pax_global_header00006660000000000000000000000064136132604510014513gustar00rootroot0000000000000052 comment=951d01162d7e38be954621fbecb76e90f1a6d58a pgFormatter-4.2/000077500000000000000000000000001361326045100136525ustar00rootroot00000000000000pgFormatter-4.2/.gitignore000066400000000000000000000000231361326045100156350ustar00rootroot00000000000000# Swap files *.swp pgFormatter-4.2/ChangeLog000066400000000000000000001263411361326045100154330ustar00rootroot00000000000000January 26 2020 - v4.2 This is a maintenance release to fix issues reported by users since the last three months. As usual there is also some improvements and more formatting supported. New option: * -U | --type-case N : Change the case of the data type name. Default is lowercase: 1. Values: 0=>unchanged, 1=>lowercase, 2=>uppercase, 3=>capitalize. also avalaible in CGI mode as a select box option. Here is the complete list of changes and acknowledgments: - Fix indentation if CASE clause when there is ; in statement. Thanks to Sergey Shepelev for the report. - Fix indentation of ORDER BY in OVER() clause. Thanks to Giorgi Modebadze for the report. - Keep CREATE STATISTICS statement on a single line. - Restore CREATE INDEX on a single line (no newline before using) to conform to pg_dump output. - Fix indentation when IF NOT EXISTS clause is present. - Add execution of regression tests through call to: make test. Thanks to Christoph Berg for the feature request. - Fix uninitialized variable error. - Fix identation of VALUES clause for INSERT statement in function code. Thanks to Sergey Shepelev for the report. - Fix indentation between comment and closed parenthesis before a column alias. Thanks to Laszlo Zsolt Nagy for the report. - Fix indentation of a FOR UPDATE clause in plpgsql function and after a LIMIT clause. Thanks to Sergey Shepelev. - Remove extra newline on inline comment. Thanks to prichardson211 for the report. - Fix indentation of VALUES in some cases of INSERT statements - Fix wrong current statement assignment with comment keywword. - Fix invalid indentation of INSERT into function. - Dump array values in _dump_var() function. - Add RAISE to keywords. - Fix indentation in function with nested BEGIN/END blocks. Thanks to prichardson211 for the reports. - Fix other bad formatting in CREATE TYPE statements. - Indent AS followed by code delimiter in CREATE PROCEDURE statement just like with CREATE FUNCTION. - Fix indentation after a function/procedure with single quote as delimiter. - Prevent newline between open and close parenthesis in create function clause when there is no parameters. - Apply same behavior than previous commit for colon followed by the variable name in double quotes - Fix formatting of psql variable used in sql file. Thanks to Girish Pasupathy for the report. - Add detection of cast in token for keyword and function formatting Do not treat TRUE/FALSE as keyword when used a column aliases. - Fix formatting with casts type after case when using ::. Thanks to Giorgi Modebadze for the report. - Fix wrong detection as keyword or column name of tokens COMMENT, INDEX and CONSTRAINT. - Add debug information for SQL keyword detection. - Fix new line in FK constrains between ON and UPDATE. - Remove newline between UPDATE and the relation name, they appears now on the same line. - Fix formatting of ON CONFLICT DO clause. Thanks to prichardson211 for the report. - Fix formatting of PREPARE ... AS UPDATE - Replace double quote in functions that has single quote as quote separator as we replace it by $$. - Fix wrong formatting with CREATE TABLE in EXPLAIN statement. - Add newline between AS and WITH keywords. Thanks to Andriy Diulin for the report. - Fix indent of RETURNS TABLE clause. Thanks to Ziyaddin Sadigov for the report. - Fix formatting of trigger with INSTEAD OF. - Fix indentation of SELECT inside INSERT statement. Thanks to Laszlo Zsolt Nagy for the report. - Fix case where formatting adds a newline before ASC or DESC. Thanks to Giorgi Modebadze for the report. - Fix formatting in CREATE TABLE statement with no indent after first foreign key. - Fix parsing of function with internal language that was breaking formatting of subsequent statements. - Fix formatting of CONSTRAINT TRIGGER. - Add more debug capabilities. - Fix wrap in compound FOREIGN KEY. Thanks to Victor Andree for the report. - Fix exception when the statement does not end with a semi colon. Thanks to Ralph R. for the report. - Allow testing installed binary in /usr/bin required for Debian - package testsuite. Thanks to Christoph Berg for the patch. - Reflect test result in exit code of regress_test.pl. Thanks to Christoph Berg for the patch. September 20 2019 - v4.1 This is a maintenance release to fix issues reported by users since the last three month. As usual there is also some improvements and new features. * Add -T, --tabs option to make pg_format use tabs instead of space characters. When this option is used spaces number is set to 1 whatever is the value set to -s, --spaces. * Allow output to override source file. * Add option -g, --no-grouping and CGI corresponding checkbox. By default pgFormatter groups all statements when they are in a transaction: BEGIN; INSERT INTO foo VALUES (1, 'text 1'); INSERT INTO foo VALUES (2, 'text 2'); ... COMMIT; By disabling grouping of statement pgFormatter will always add an extra newline characters between statements just like outside a transaction: BEGIN; INSERT INTO foo VALUES (1, 'text 1'); INSERT INTO foo VALUES (2, 'text 2'); ... COMMIT; This might add readability to not DML transactions. - Add more SQL keyword: REFRESH, MATERIALIZED, EVENT and function xmltable - Prevent uppercase of keywords when they are used as column names in a CREATE TABLE statement. Here is the complete list of changes and acknowledgments: - Keep COMMENT ON formatting unchanged when using dollar quoting. Thanks to Jeremy Palmer for the report. - Allow to disable grouping of statements in transaction. Thanks to Francisco Puga for the feature request. - Fix unwanted formatting when know keywords are used in a CREATE TABLE statement. Thanks to Jeremy Palmer for the report. - Add -T, --tabs option to make pg_format use tabs instead of space characters. Thanks to jen-Ya for the feature request. - Update regression tests. - Prevent new line between DEFAULT and VALUES in insert statements. - Fix bad formatting with single quote code separator. The separator is replaced with $$ when the code is multi-line, otherwise the code is kept untouched. - Allow regression test on specific test file passed as parameter. - Fix format when "with time out" is used. Thanks to jjangga0214 for the report. - Fix backslashes at end of string constant that produce wrong results. Thanks to jen-ya for the report. - Fix regression tests with REFRESH / MATERIALIZED. - Add REFRESH and MATERIALIZED to the list of SQL keywords. Thanks to jen-Ya for the report. - Fix tabs indentions used outside indent and add regression test for -n and -T options. - Fixes to documentation. Thanks to zach valenta for the patch. - Fix not remove comment starting with -- when the -n option is used. Thanks to Brady Holt for the report. - Fix error about STDIN reopened as STDOUT introduced in commit af63e9e. Thanks to Daniel McAuley for the report. - Fix newline wrongly forced after function parameters in CTE. - Allow output to override source file. Thanks to Daniel McAuley for the report. - Fix formatting when a token start with CALL. Thanks to Ralph R. for the report. - Fix newline in geometry type. Thanks to Sven Over for the report. - Fix incomplete previous patch. - Fix missing newline in column list after CTE. Thanks to Giorgi Modebadze for the report. - Fix OVERLAPS formatting. - CGI.pm: correct github link text in footer. Thanks to Stephan Renatus for the patch - Fix build of pg function hash. - Fix wrong formatting in CREATE TABLE statement AS WITH clause. Thanks to ImreSamu for the report. - Fix broken indent when AS has parameters. - Suppress newline in COMMENT ON FUNCTION formatting. - Fix several indentation issues related to CREATE TABLE with PARTITION and ALTER statements. - Fix an other case of bad indentation of EXECUTE in triggers and undefined value on indentation level. - Fix an other case of newline introduced in REVOKE statement. - Fix newline before FROM in REVOKE statement - Add EVENT keyword and add formatting of CREATE EVENT TRIGGER statement. - Fix indentation for an insert statement after a create table statement in the code of a function. Thanks to LaetitiaJV for the report. June 2 2019 - v4.0 This major release fixes a huge list of formatting issues which makes pgFormmater the best tool to format your plpgsql code and sql queries. pgFormatter is now able to auto-detect user defined functions to prevent adding new line between parameters. There's also a huge improvement to the CGI mode which can now apply directly a formatting option change to current code to format without having to submit again the content. Here is the complete list of changes and acknowledgments: - Change color of "Format my code" button to be more visible. - Reorder button in CGI mode for ergonomics improvement. - Allow instant change when modifying option in CGI mode. Thanks to Julien Rouhaud for the features request. - Add varying and xmlserialyze to list of functions. - Add array_position, array_positions and cardinality to function. - Add more regression test representing PostgreSQL syntax for all statements. - Improve formatting of VALUES in FROM clause. - Fix bad formatting after $$begin and end$$ since the dollar sign is allowed in object names. - Remove newline in multidimensional array - Fix indention after START WITH. - Fix indentation in TRUNCATE with multiple tables. - Fix indentation in GENERATED clause. - Remove newline before WITHIN GROUP and in parameters list. - Fix indentation in CREATE TYPE and WITH ORDINALITY AS. - Add decimal to function list to prevent newline inside parameters. - Add MATCH to PostgreSQL keywords and fix newline after MATCH. - Add newline before all ALTER, ADD DROP keywords in ALTER COMMAND unless there is a DEFAULT or NOT NULL clause. - Fix ALTER OPERATOR and ALTER AGGREGATE statements. - Fix CREATE AGGREGATE formatting with ORDER BY in parameters. - Fix indentation of CREATE AGGREGATE statements and add all related keywords. - Fix newline in function supporting ORDER BY clause. - Add percentile_cont() to the list of these functions. - Fix case where GROUP BY was not well indented after a JOIN with AND clause. - Add float8 combine functions. - Fix detection of function when they are used in cast, ex: col::numeric(10,3). - Fix indentation of FETCH in function. - Fix missing newline between ] and ) in create statement. - Keep FETCH ... FROM on the same line. Fix cases where COMMIT or ROLLBACK was not detected. - Fix newline between FOLLOWING and EXCLUDE in window functions. - Add hash functions to functions list array. - Fix some OPERATOR formatting. - Fix EXCLUDE formatting in constraint and window function call. - Fix columns/statements no longer get a newline when following a subquery. Thanks to Steve Whittaker for the report. - More speed improvement with huge file. - Improve performances when parsing file thousand of queries. Thanks to Bruce Momjian for the report. - Fix newline in function parameters in CREATE TABLE ... AS SELECT - Fix newline between BEGIN ISOLATION and AS EXECUTE. - Fix newline in parameter list of execute procedure in trigger. - Fix bad indention when there is a comment between EXCEPTION and WHEN keywords. - Fix missing indentation in DECLARE section of a DO block. - Remove newline between PASSING BY and REF in xmlexists() function. - Add query_to_xml and cursor_to_xml to PostgreSQL function list. - Fix formatting of CREATE TABLE () AS. - Fix detection of code delimiter in function resulting in infinite loop. Thanks to Bruce Momjian for the report. - Fix formatting of CREATE VIEW with colomn names. - Add COSTS and OFF to pg keywords. - Fix newline and indentation after insert statements. - Terminate bloc mode when ROLLBACK is found. - Fix indention in EXPLAIN option. Thanks to Bruce Momjian for the report. - Fix support of CREATE OPERATOR prefixed with schema and unwanted tokenisation of attributes NEGATOR/COMMUTATOR. Thanks to Bruce Momjian for the report. - Fix indentation of CREATE OPERATOR. - Prevent new line in CHECK clause of a CREATE DOMAIN statement. - Prevent newline before FROM in a CREATE CONVERSION clause. - Do not add newline in DROP list. - Fix newline between CREATE/DROP GROUP. - Fix infinite loop in detection of function code delimiter. Thanks to Bruce Momjian for the report. - Fix extra newline before ; in CREATE/DROP sequence statement. - Fix indentation of first function parameter with -t option - Fix bad indenting with CREATE FUNCTION causing following INSERT to indent. Thanks to Bruce Momjian for the report. - Fix broken indentation after CREATE TRIGGER statement. - Fix extra newline between open parenthesis and ORDER BY in WINDOW clause. Thanks to Bruce Momjian for the report. - Fix double indention before VALUES in INSERT statement. - Fix indentation in CREATE RULE statement. - Remove spaces before/after the WITH arguments if --wrap-after is used. - Fix extra indentation in WITH clause before ending parenthesis when --wrap-after is enabled. - Fix newline after DISTINCT when wrap after is enabled. Thanks to Bruce Momjian for the report. - Fix wrapping of WITH parameters when --wrap-after is enabled. Thanks to Bruce Momjian for the report. - Apply --wrap-after behavior to WITH clause. Thanks to Bruce Momjian for the report. - Apply --wrap-after behavior to SET clause of UPDATE statement. Thanks to Bruce Momjian for the report. - Fix missing new line after FROM when --wrap-after is not set. - Fix odd wrapping in the WINDOW clause. Thanks to Bruce Momjian for the report. - Fix some HTML tags and move place of footer. - Fix indentation after OVER clause without parenthesis part and indentation level of WINDOW keyword. Thanks to Bruce Momjian for the report. - Avoid new line with empty parenthesis after AS keyword. Thanks to Bruce Momjian for the report. - Add newline before WINDOW keyword as it is a top-level clause. - Fix OVER and WINDOW clause indenting. Thanks to Bruce Momjian for the report. - Apply --wrap-after behavior into ORDER BY and GROUP BY clauses. Thanks to Bruce Momjian for the report. - Fix indention after DECLARE CURSOR statement. Thanks to Bruce Momjian for the report. - Fix another crash with uninitialized variable with constant starting with --. Thanks to NisSAM for the report. - Fix crash in windows function with uninitialized variable. Thanks to Bruce Momjian for the report. April 21 2019 - v3.4 This release fixes several issues reported by users since the last six months and adds lot of improvements in code formatting: * Add -W, --wrap-after option to set number of column after which lists must be wrapped. Default is to puts every item on its own line. * Add 'Wrap after' option to CGI in the indentation section to support the -W | --wrap_atfer command line option in CGI mode. * Add -w | --wrap-limit command line option to be able to to wrap queries at a certain length. This option is not available in CGI mode. * Allow uploaded file content-type to be application/octet-stream. * Allow a single $ sign in object name. * Major optimization of function detection, speed difference is 10 time faster. * Better detection of function versus keyword with same name. * Add WORK, GENERATED and IDENTITY as PostgreSQL keyword. * Add array_remove and array_replace to the list of PostgreSQL functions. * Add detection of code separator after DO keyword if it starts with a dollar sign. * Add new option -t | --format-type to try another formatting type for some statements. At this stage it will just use a multi-line format in POLICY and PUBLICATION statements. It also introduce newline in parameter function list. * Add formatting of CREATE POLICY statements. * Add information about (Neo)vim plugin and Visual Studio use of pgFormatter to format PLPGSQL and SQL code. Here is the complete list of changes and acknowledgments: - Add regression test for --wrap_after option. - Fix for sub _indent if $self->{ '_level' } is undefined. Thanks to NisSAM for the patch. - Add information about special formatting done by -t, -w and -W. - Add -W, --wrap-after option to set number of column after which lists must be wrapped. Default: puts every item on its own line. This applies to SELECT and FROM list. For example: SELECT a, b, c, d FROM t_1, t_2, t3 WHERE a = 10 AND b = 10; is formatted into: SELECT a, b, c, d FROM t_1, t_2, t3 WHERE a = 10 AND b = 10; Note this formatting doesn't fits well with sub queries in list. Thanks to Bruce Momjian for the feature request. - Add information about the (Neo)vim plugin used to format code for many file types and that support pgFormatter to format SQL file type. Thanks to Anders Riutta for the patch to (Neo)vim. - Fix incorrect indentation of sub-select in function. Thanks to Ben Hutton for the report. - Fix indentation error on case statements in CTE. Thanks to Brent Anderson for the report. - Reset ->{'_level'} before call to new beautify request. Thanks to NisSAM for the report. - Fix indent into BEGIN WORK|TRANSACTION and remove extra new line between statement in BEGIN blocs. Thanks to Bruce Momjian for the report. - Add -w | --wrap-limit command line option to be able to to wrap queries at a certain length. Note that this could break the code formatting effort of pgFormatter but be useful in some case. - Update copyright year. - Allow a single $ sign in object name otherwise it must be quoted. Thanks to NisSAM for the report. - Fix extra newline between DELETE SET NULL, add case to regression tests. Thanks to Dmitry Chekanov for the report. - Major optimization of function detection, difference is 10 time faster. Thanks to NisSAM for the patch. - Fix formatting of first level of AND / OR - Fix unexpected extra indentation in CASE statement. Thanks to jostem for the report. - Fix newline between RAISE and EXCEPTION and extra indentation after IF statements. - Better detection of function versus keyword when they have the same name. Thanks to Huan Ruan for the report. - Fix non breaking columns out into newlines in the next CTE. Thanks to Collin Meyers for the report. - Add extra newline before a comment /* */ when the last token is ; for better reading. Thanks to Shon Frazier for the feature request - Add GENERATED and IDENTITY PostgreSQL keyword and remove newline character after GENERATED BY. Thanks to madflow for the report. - Allow uploaded file content-type to be application/octet-stream. - Add a test on uploaded file to be text file (-T). - Add information about Perl CGI module requirement. Thanks to Huan Ruan for the report. - Add array_remove() and array_replace() to the list of PostgreSQL functions. - Fix bad indentation after IF EXISTS clause. Thanks to jostem for the report. - Add detection of code separator after DO keyword if it starts with a dollar sign. - Using -t (try another format type) puts arguments for functions on multiple lines for readability. Thanks to Dan Lynch for the feature request. - Fix typo, thank to David Lynch for the report. - Update documentation about the -t option and add link to a Visual Studio extension to format PostgreSQL SQL and PLPGSQL using pgFormatter. Thanks to Brady Holt for the extension. - Add new option: -t | --format-type to try another formatting type for some statements. At this stage it will just use a multi-line format in POLICY and PUBLICATION statements. Thanks to Dan Lynch for the feature request. - Fix formatting of EXCLUDE/USING clause in constraint/indexes. Thanks to Dan Lynch for the report. - Improve trigger formatting. Thanks to Dan Lynch for the report. - Fix newline inserted after a semicolon in comment statements. Thanks to Dan Lynch for the report. - Add formatting of CREATE POLICY statements. Thanks to Dan Lynch for the report. - Preserve indentation at end of a create table statement when it called inside a block. Thanks to visciang for the report. - Fix formatting of OVER clause. - Fix missing PARTITION keyword outside main table. - Fix typo in REMAINDER keyword and move OF keyword to PG list. November 20 2018 - v3.3 This minor release fixes several issues reported by users since last release, adds and improves some code formatting: * Add formatting of GROUPING and ROLLUP clause. * Replace concat operator from some SGBD with operator ||. * Add formatting of FILTER and WITHIN GROUP clause. * Fix several wrong formatting with comment. * Improve arrays formatting. * Treat DISTINCT as a modifier of the whole select clause. * Fix formatting of ON UPDATE/DELETE in create table statements. * Add DETACH,ATTACH,LIST,HASH,MODULUS and REMINDER to keywords list. * Add formatting of CREATE PARTITION STATEMENT. Here is the complete list of changes and acknowledgments: - Add extra newline after a comment when it is not in a block or a statement. Thanks to Aaron Reisman for the report. - Fix inline comments that was wrongly moved after a new line in some case. - Fix case where comments that was not moved to next line. - Trim trailing space in comment. Thanks to Aaron Reisman for the report. - Add formatting of FILTER clause. Thanks to Leonardo Rochael Almeida for the report. - Update regression test expected on queries using ARRAY[...]. - Remove newline after comma in ARRAY[...] definition. - Remove space before and after array braket. Thanks to Peter Boromissza for the report. - Add FILTER keyword and some missing aggregate functions. Thanks to Leonardo Rochael Almeida for the report. - Fix formatting of ON UPDATE/DELETE in create table statements. Thanks to Peter Boromissza for the report. - Update regression tests with last changes. - Treat DISTINCT as a modifier of the whole select clause not only the fisrt column. Thanks to report of Leonardo Rochael Almeida. - Fix some typo in documentation. Thanks to 0xflotus for the patch. October 08 2018 - v3.2 This minor release adds support to PostgreSQL v11.0 new keywords and fixes several issues reported by users since last release. * Add new PostgreSQL v11 keywords CALL, GROUPS, INCLUDE, OTHERS, PROCEDURES, ROUTINE, ROUTINES, TIES. * Add formatting of CREATE PUBLICATION. * Add formatting of CREATE PROCEDURE code. * Add formatting of ALTER PROCEDURE/ROUTINE, all on a single line. * Add an indentation before VALUES of INSERT statements. Here is the complete list of changes and acknowledgments: - Fix weird formatting on UNIQUE and PRIMARY KEY constraints in CREATE TABLE statements. Thanks to Aaron Reisman for the report. - Add operator for name convention, ":=" was changed to "=>". Thanks to snailshell-321 for the report. - Fix missing formatting in SQL function with a single SELECT. - Remove any new line in CALL statement. - Never add newline after comma in ALTER statements. - Prevent new line between ROW LEVEL SECURITY. Thanks to madflow for the report. - Update samples with last formatting changes. September 12 2018 - v3.1 This minor release fixes several issues reported by users since six months. This release also adds some improvements in code formatting. * Function code in other language tha SQL or PLPGSQL is now kept untouched. * Make formatting output of function/procedure more compliant with the pg_dump PostgreSQL format. * Add information about atom package integration of pgFormatter. * Add a "Clear code" button to reset text area and preserve options changes. * Add a logo to the project and change title and style of the CGI part. * Add application/sql content-type support in CGI mode. Here is the complete list of changes and acknowledgments: - Add preprocessing to autodetect function language before entering the tokenizer. If the language is not SQL or PLPGSQL the code is kept untouched. Thanks to Philip Trauner for the report. - Fix whatis entry of POD manpage. Thanks to Christoph Berg for the patch. - Add more regression tests. - Remove newline between sub query and ON keyworks in JOIN clause. Thanks to Christophe Courtois for the report. - Fix indentation of multiline comments. Thanks to Philip Trauner for the report. - Fix missing newline after comma in some select statement, especially from the online example. - Fix formatting of query with multiple CTE. Thanks to Christophe Courtois for the report. - Fix plv8 syntax. Thanks to Aaron Reisman for the report. - Fix undefined value crash when formatting query not terminated by a semi colon. Thanks to David Luzar for the report. - Add newline and indentation after WITH [RECURSIVE] ... ( - Fix bad indentation on case statements with 0 value. Thanks to John Dimm for the report. - Add regression test for sub queries followed by a join. - Set the code separator for ex19.sql regression test. - Fix indentation in join when previous instruction is a sub select. Thanks to Christophe Courtois for the report. - Fix indentdation of INNER JOINS when a previous JOIN use AND/OR operator. Thanks to David Vogt for the report. - Fix indentation problem with sub-select and UNION. Thanks to Christophe Courtois for the report. - Fix handling of newline after comma in CGI mode for insert queries. - Cleanup some code with comment addition. - Prevent newline before BETWEEN operator. - Fix JOIN formatting with AND/OR operator. - Fix utf8 uncaught exception on CLI mode. - Remove requirement to Perl v5.14 minimum. - Avoid backward indentation in multiple JOIN clauses. - Set default CGI maxlength to 100KB - Fix unwanted indentation of WHEN in CASE statement. - Remove use of UTF8 Perl pragma in CGI mode, it is not recommended and it breaks CGI when not utf8 encoding. Thanks to Christophe Courtois for the report. - Fix PREPARE and INSERT|DELETE formatting. Thanks to Tom Halson for the report. - Fix formatting of WITH inside CREATE TABLE statement that was wrongly formatting TIMESTAMP WITH TIME ZONE. Thanks to James Bourne for the report. - Fix uninitialized maxlength. Thanks to Steve Wainstead for the report. - Reactivate -m | --maxlength in CLI mode that was removed in version 2.0. Thanks to Steve Wainstead for the report. Febuary 28 2018 - v3.0 This major release fixes some issues reported by users during the last six months and adds several new features. * Add colorization of comments. * Add detection and colorization of user defined function in the SQL code. Until now only PostgreSQL internal function was highlighted. * Add new option -B | --comma-break and in CGI the checkbox "New-line after comma (insert)" to force a newline after each comma in INSERT statements, columns and values parts. * Add command line option: -F | --format to be able to change the output format in CLI mode. At now there is two possible format, text the default or html. More format can be added in the future. * Add support to all psql meta commands up to v1O. There is also some improvement in SQL code formatting. Here is the complete list of changes: - Add information on how to customize CSS in CGI to change the HTML style. - Update documentation and add a pointer to the Node.js wrapper written by Gajus Kuizinas. - Remove program extention from $self->{program_name} used as title of the CGI interface. - Remove html_highlight_code() method to apply HTML formatting style at token level instead of full SQL content like before. You just have to call the $object->beautify() method, the HTML formatting will be called internally if you have set the out format to HTML with the call to $object->format('html'). This call is now done internally by pg_format when it is called with the -F html option or in CGI mode. This compatibility break justify a change of the major version number. Note that this breaks backward compatibility if you are using your own custom scripts. With a complete upgrade and the use of the pg_format script and libraries everything should work as before. - Improve the CGI interface where the footer bar overlaps. - The CGI library also support a new internal parameter/option $self->{ 'colorize' } used to disable HTML output formatting to avoid applying colorization to error messages returned by the Perl package pgFormatter::CGI.pm - Improve documentation of the Perl Package. - Add detection of user defined function in the SQL code. Until now only PostgreSQL known function was highlighted. Thanks to Gajus Kuizinas for the feature request. - Add option -B | --comma-break to documentation. - Add regression test to CASE formating. - Fix poorly formatted output when using CASE statements within function params. Thanks to Kale Davis for the report. - Add new option -B | --comma-break and in CGI the checkbox "New-line after comma (insert)" to force a newline after each comma in INSERT statements, columns and values parts. Thanks to Troy Johnson for the feature request. - Add sample of insert statement to test new -B option - Improve formatting of multiple join. - Add new regression test on JOIN formatting. - Add command line option: -F | --format to be able to change the output format in CLI mode. At now there is two possible format, text the default or html. More format can be added in the future. This allow embedded call to pg_format from any other program and send a more sophisticated format than simple text to be able to detect keywords, function and punctuation in that program. - Add Hubert depesz Lubaczewski to the main authors list. - Add simple regression test on psql meta command. - Add support to all psql meta commands, thanks to Tobias Bussmann the patch. - Remove regexp pragma for negative look backward (use re '/aa';) it is no more necessary since html_highlight_code() method is gone. The requirement of minimal Perl version 5.14 should no more needed but it is preserved for now. The demo isite is working well on Perl 5.10. September 02 2017 - v2.1 This minor release fixes several issues reported by users since four months. This release also adds some improvements in code formatting. - Add support to new psql meta-command from v10. - Remove new line before ORDER BY with string_agg and group_concat function call. Thanks to Huy Pham for the report. - Fix CREATE TYPE format. Thanks to Fabian Zeindl for the report. - Fix fix full outer join formatting. Thanks to Jakob Egger for the report. - Fix indentation in CTE. Thanks to Jakob Egger for the report. - Add two command line option to defined where comma must appears in a parameter list: -b | --comma-start: in a parameters list, start with a comma (see -e) -e | --comma-end : in a parameters list, end with a comma (default) The CGI interface adds a new checkbox to force comma at beginning. Default is lines end with a comma. Thanks to fnwiya for the feature request. - Fix use of uninitialized value in pattern match. Thanks to Gajus Kuizinas for the report. - Fix missing space after a function name especially in ALTER FUNCTION calls. - Add sample to regression test on CTE, CREATE TYPE and dynamic code formatting. - Prevent dynamic code formatting. By default pgFormatter takes all code between single quote as string constant and do not perform any formatting on this code. It is common to use a string as code separator to avoid doubling single quote in dynamic code generation, in this case pgFormatter can fail to auto detect the code separator. By default it will search for any string after the EXECUTE keyword starting with dollar sign. If it can not auto detect your code separator you can use the command line option -S or --separator to set the code separator that must be used. Thanks to Alvaro Herrera for the report. - Fix undetected case of BEGIN keyword when lowercase. - Add test case for GRANT and DISTINCT FROM formatting. - Remove newline between DISTINCT and FROM. Thanks to Sebastian Albert for the report. - Fix GRANT formatting. Thanks to Vesa Karjalainen for the report. - Make it possible to run pg_format via symlink. For example, if pgFormatter is in /opt/pgFormatter and there is a symlink like /usr/local/bin/pg_format -> /opt/pgFormatter/pg_format then previous version wouldn't work when called via symlink, as it would search for libs in /usr/local/bin/lib/, and not in /opt/pgFormatter/lib. - Move COALESCE from SQL keyword to SQL function to prevent new line after comma in the parameters. May 07 2017 - v2.0 This major release adds a plpgsql code beautifier. This makes pgFormatter the best free tool to rewrite and beautify any SQL and PLPGSQL code. This release also fixes several issues and adds lot of improvements in code formatting. - Add PLPGSQL beautifier. - Fix case sensitivity of plpgsql keywords when using the -u command line option. Thanks to Hubert Depesz Lubaczewski for the report. - Add regression tests script regress_test.pl, more test files and expected results. - Do not append newline before a concatenation operator. - Fix unitialized variable issue and remove newline in insert statement after a close parenthesis. - Remove comment before HTML beautifier and restore them after to prevent beautifying keywords in comments. - Improve formatting of UPDATE and DELETE statement. - Improve all DDL statements formatting. - Only format functions when name is followed by an open parenthesis. - Add support for AWS Redshift keywords. Thanks to cavanaug for the feature request. - Prevent new line after a comma in parameters function. - Add LATERAL keyword. - Remove new line between UNION and ALL. - Add RETURNING as far left keyword. - Fix some potential case sensitivity issues. - Replace tabulation with space for better indentation. - Prevent newline in USING clause of JOIN. - Prevent newline after a closing parenthesis but before an operator. - Prevent new line when a comma is followed by a quote January 23 2017 - v1.6 This release fixes several issues and adds lot of improvements in query formatting. Command line option --placeholder or -p has been added to be able to set regex to find code that must not be formatted. For example, in query like: SELECT * FROM projects WHERE projectnumber IN <> AND username = <>; you may want pgFormatter to not interpret << and >> as bit-shift keywords and modify your code snippsets. You can use a Perl regular expression to instruct pgFormatter to keep some part of the query untouched. For example: pg_format samples/ex9.sql -p '<<(?:.*)?>>' will not format the bit-shift like operators. Here is the complete list of changes: - Improve SQL formatting by reducing space with parenthesis and cast with ::. - Add two sample file to test placeholder feature and WITH query. - Fix missing newline after INNER JOIN. Thanks to Julien Rouhaud for the report. - Fix whitespace placed in between the E and the quote character. Thanks to clijunky for the report. - Fix lots of operators reported on issue #22. Thanks to Laurenz Albe for the report and list of missing operators. - Fix version number in lib/pgFormatter library files. - Add -p | --placeholder command line option to prevent pgFormatter to replace some part of the SQL queries. Thanks to jhelberg for the feature request. - Fix bad formatting with anonymized values in queries. Thanks to Pilat66 for the report. October 17 2015 - v1.5 This release fixes several issues and adds support to new keywords added in PostgreSQL 9.5. There's also a major code rewrite by Hubert depesz Lubaczewski, pg_format works as previously - handles both CGI and CLI interface, but you can get directly to whatever functionality you want by using pgFormatter::* modules. New beautified keywords are: BERNOULLI, CUBE, GROUPING SETS, SKIP LOCKED, LOGGED, POLICY, ROLLUP, TABLESAMPLE. And new functions are: jsonb_pretty, jsonb_set, pg_last_committed_xact, pg_xact_commit_timestamp. There is also a useful hint added to documentation to be able to format you SQL code from vi by adding a simple line to .vimrc. This hint is taken from David Fetter's blog : http://people.planetpostgresql.org/dfetter/index.php?/archives/78-Formatting!.html Here is the complete list of other changes: - Add useful hint about formating in vi. Thanks to David Fetter for the hint. - Prevent newline after open parenthesis and indent back after close when we have count(*) for example. - Add test query with a backslash before a quote in samples/ex8.sql. - Fix coloring string when the string contained a backslash before a quote. Thanks to Kevin Brannen for the patch. - Add RETURNS keywords. - Remove newline before the FOR keywords. - Add BY to the list of PG keywords to be capitalized. - Add new keywords from PostgreSQL 9.5. - Reformat queries with FOR and USING, for example: CREATE POLICY policy_even_ids_only ON log FOR ALL TO PUBLIC USING (id % 2 = 0); - Fix unescaped character in regex. Thanks to Tom Burnett for the patch. - Fix wrong indentation with function that accept FROM clause. Thanks to Adrian Klaver and Alvaro Herrera for the report. - Add more test samples about json operators. - Add JSON #>> operator and sample. Thanks to Tom Burnett for the patch. - Some more JSON operators. Thanks to Hubert depesz Lubaczewski for the patch. - JSON operators have to be before normal operators as regexp will match first possible string, so -> will match - first, if it's before in tokenizer. Changing order solves the problem reports by Tom Burnett. Thanks to Hubert depesz Lubaczewski for the patch. - Add new json/jsonb functions to list of pg functions. - Add JSON operators. Thanks to Tom Burnett for the report. - Fix anonymizing failure reported in issue #15. Thanks to Niklas Schmidtmer for the report. - Add developer documentation. Thanks to Hubert depesz Lubaczewski for the patch. - Fix Beautify.pm with perl 5.20. Thanks to Hubert depesz Lubaczewski for the patch. - In Beautify.pm we were not considering the possibility of $last being empty in one condition inside a JOIN, and that gives an exception in perl 5.20. Thanks to Pablo Alvarez de Sotomayor Posadillo for the patch. - Split into libraries and programs complete. Thanks to Hubert depesz Lubaczewski for the patch. April 18 2015 - v1.4 This release fixes several issues and adds a useful anonymization option to be able to obfuscate all values in SQL queries, see option -a | --anonymize at command line or the corresponding checkbox in the CGI interface. - Fix wrong reset position when a FROM clause is encountered in a subselect. Thanks to Alvaro Herrera for the report. - Add information on indentation to obtain a single line statement in CGI interface. - Remove unecessary first line in text mode and stdout output. Thanks to David Fetter for the report. - Automatically set default page for Reset all redirection. Thanks to rimbault for the report. - Add -a | --anonymize to obfuscate all values in SQL queries. - Add checkbox to activation anonymize feature from CGI interface. - Ran perlcritic on the code, and fixed most critical issues. Thanks to Hubert depesz Lubaczewski for the patch. - Make anonymization more useful. Thanks to Hubert depesz Lubaczewski for the patch. - Removed anonymisation of interval value (eg: 1 day) and to_char date format (eg: DD/MM/YYYY HH24:MI:SS. Add a sample into ex4.sql file. - Add REPLACE to the list of SQL keywords. Thanks to Alvaro Herrera for the report. - Prevent new line to be inserted before OR after a CREATE keyword to have CREATE OR REPLACE on a single line. - Fix additional spaces added in function body separator ($BODY$ was rewritten as $ BODY $). Thanks to Alvaro Herrera for the report. - Prevent (....)::interval to be replaced by the anonymizer. - Add sample file with create function for testing purpose. Mars 19 2015 - v1.3 This release fixes several issues and adds geometric operators. It is also possible now to format an sql script with one statement per line, which is the reverse of the standard used. - Update Copyright year. - Add geometric operators. Thanks to Devin Ben-Hur for the report. - With one statement per line format, keep comment on separate line. Thanks to Marco Schmidt for the patch. - Allow pgFormatter to format an sql script with one statement per line (no line-break, no indent, no comments) when -s | --space is set to 0. Thanks to Marco Schmidt for the patch. - Restrict regex matching in the ASCII range only with "use re /aa;". Regex that has a look-ahead or look-behind AND uses the "i" modifier need the "aa" modifiers too. This is for perl>=5.14. For perl<5.14 you will just have a warning about non existant re pragma. Thanks to Kevin Brannen for the patch. - Fix bug where capitalize makes functions disappear. Thanks to Vincent Picavet for the report. - Fix usage where obsolete -c option was still used. - Add new sample file. - Fix left joins that came out too far to the left and remove newline after OUTER. Thanks to Kevin Brannen for the report. - Applied typo patch of Felix Hummel to the POD documentation. - Fix some typo on documentation. Thanks to Felix Hummel for the patch. - Fix issue in formatting double quotes included into single quotes. Thanks to Julien Rouhaud for the report. - Fix issue with Perl 5.16 that do not allow "ss" inside look-behind assertions. Thanks to Cedric for the report. January 11 2013 - v1.2 This release add file upload capability in CGI context and better code formatting. - Add HTML break before the footer in CGI context. - Add missing SQL::Beautify paternity and update license part of the documentation. - Allow file upload in CGI context. - Fix issue in formatting that prevent left back indentation when major keywords were found. Thanks to Kevin Brannen for the patch. - Add information in code comments about external files in CGI mode. - Force the parser to not insert a new line after the SET keyword when the query begin with it. This is to preserve the single line with queries like SET client_encoding TO "utf8"; - Add new line after SET keyword to better format UPDATE queries. Thanks to Pilat66 for the report. December 27 2012 - v1.1 - Preserve SQL code formatting when copy/paste. - Removed coypright from CGI footer - Modify links at the bottom of the CGI interface. - Fix obsolete option in usage example. - Limit SQL query size to $maxlength in CGI context and non interactive usage. December 22 2012 - v1.0 This is the initial release of pgFormatter pgFormatter-4.2/LICENSE000066400000000000000000000016531361326045100146640ustar00rootroot00000000000000Copyright (c) 2012-2020, Gilles Darold Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies. IN NO EVENT SHALL Gilles Darold BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF Gilles Darold HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Gilles Darold SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND Gilles Darold HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. pgFormatter-4.2/Makefile.PL000066400000000000000000000024431361326045100156270ustar00rootroot00000000000000use ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. use strict; my @ALLOWED_ARGS = ('INSTALLDIRS','DESTDIR'); # Parse command line arguments and store them as environment variables while ($_ = shift) { my ($k,$v) = split(/=/, $_, 2); if (grep(/^$k$/, @ALLOWED_ARGS)) { $ENV{$k} = $v; } } $ENV{DESTDIR} =~ s/\/$//; # Default install path my $DESTDIR = $ENV{DESTDIR} || ''; my $INSTALLDIRS = $ENV{INSTALLDIRS} || 'site'; WriteMakefile( 'DISTNAME' => 'pgformatter', 'NAME' => 'pgFormatter', 'VERSION_FROM' => 'pg_format', 'dist' => { 'COMPRESS'=>'gzip -9f', 'SUFFIX' => 'gz', 'ZIP'=>'/usr/bin/zip','ZIPFLAGS'=>'-rl' }, 'AUTHOR' => 'Gilles Darold (gilles@darold.net)', 'ABSTRACT' => 'pgFormatter - PostgreSQL SQL and PL/PGSQL syntaxe beautifier', 'EXE_FILES' => [ qw(pg_format) ], 'MAN1PODS' => { 'doc/pg_format.pod' => 'blib/man1/pg_format.1' }, 'DESTDIR' => $DESTDIR, 'INSTALLDIRS' => $INSTALLDIRS, 'clean' => {}, 'META_MERGE' => { resources => { homepage => 'http://sqlformat.darold.net/', repository => { type => 'git', git => 'git@github.com:darold/pgformatter.git', web => 'https://github.com/darold/pgformatter', }, }, } ); pgFormatter-4.2/README000066400000000000000000000256141361326045100145420ustar00rootroot00000000000000NAME pg_format - PostgreSQL SQL syntax beautifier DESCRIPTION This SQL formatter/beautifier supports keywords from SQL-92, SQL-99, SQL-2003, SQL-2008, SQL-2011 and PostgreSQL specifics keywords. Works with any other databases too. pgFormatter can work as a console program or as a CGI. It will automatically detect its environment and format output as text or as HTML following the context. Keywords highlighting will only be available in CGI context. Terminal/console execution Usage: pg_format [options] file.sql PostgreSQL SQL queries and PL/PGSQL code beautifier. Arguments: file.sql can be a file or use - to read query from stdin. Returning the SQL formatted to stdout or into a file specified with the -o | --output option. Options: -a | --anonymize : obscure all literals in queries, useful to hide confidential data before formatting. -b | --comma-start : in a parameters list, start with the comma (see -e) -B | --comma-break : in insert statement, add a newline after each comma -d | --debug : enable debug mode. Disabled by default. -e | --comma-end : in a parameters list, end with the comma (default) -f | --function-case N: Change the case of the reserved keyword. Default is unchanged: 0. Values: 0=>unchanged, 1=>lowercase, 2=>uppercase, 3=>capitalize. -F | --format STR : output format: text or html. Default: text. -g | --nogrouping : add a newline between statements in transaction regroupement. Default is to group statements. -h | --help : show this message and exit. -m | --maxlength SIZE : maximum length of a query, it will be cutted above the given size. Default: no truncate. -n | --nocomment : remove any comment from SQL code. -o | --output file : define the filename for the output. Default: stdout. -p | --placeholder re : set regex to find code that must not be changed. -s | --spaces size : change space indent, default 4 spaces. -S | --separator STR : dynamic code separator, default to single quote. -t | --format-type : try another formatting type for some statements. -T | --tabs : use tabs instead of space characters, when used spaces is set to 1 whatever is the value set to -s. -u | --keyword-case N : Change the case of the reserved keyword. Default is uppercase: 2. Values: 0=>unchanged, 1=>lowercase, 2=>uppercase, 3=>capitalize. -U | --type-case N : Change the case of the data type name. Default is lowercase: 1. Values: 0=>unchanged, 1=>lowercase, 2=>uppercase, 3=>capitalize. -v | --version : show pg_format version and exit. -w | --wrap-limit N : wrap queries at a certain length. -W | --wrap-after N : number of column after which lists must be wrapped. Default: puts every item on its own line. Examples: cat samples/ex1.sql | pg_format - pg_format -n samples/ex1.sql pg_format -f 2 -n -o result.sql samples/ex1.sql CGI context Install pg_format into your cgi-bin folder, grant execution on it as a CGI script (maybe you should add the .cgi extension) and get it from your favorite browser. Copy files logo_pgformatter.png and icon_pgformatter.ico in the CGI directory, pg_format.cgi look for them in the same repository. You have a live example without limitation than ten thousand characters in your SQL query here: http://sqlformat.darold.net/ pg_format will automatically detected that it is running in a CGI environment and will output all html code needed to run an online code formatter site. There's nothing more to do. You need to install Perl CGI module first if it is not already the case: yum install perl-cgi or apt-get install libcgi-pm-perl following your distribution. INSTALLATION Download the tarball from github and unpack the archive as follow: tar xzf pgFormatter-x.x.tar.gz cd pgFormatter-x.x/ perl Makefile.PL make && sudo make install This will copy the Perl script pg_format in /usr/local/bin/pg_format directory by default and the man page into /usr/local/share/man/man1/pg_format.1. Those are the default installation directory for 'site' install. If you want to install all under /usr/ location, use INSTALLDIRS='perl' as argument of Makefile.PL. The script will be installed into /usr/bin/pg_format and the manpage into /usr/share/man/man1/pg_format.1. For example, to install everything just like Debian does, proceed as follow: perl Makefile.PL INSTALLDIRS=vendor By default INSTALLDIRS is set to site. Regression tests can be executed with the following command: make test SPECIAL FORMATTING Option -W, --wrap-after This option can be used to set number of column after which lists must be wrapped. By default pgFormatter puts every item on its own line. This format applies to SELECT and FROM list. For example the following query: SELECT a, b, c, d FROM t_1, t_2, t3 WHERE a = 10 AND b = 10; will be formatted into with -W 4: SELECT a, b, c, d FROM t_1, t_2, t3 WHERE a = 10 AND b = 10; Note this formatting doesn't fits well with sub queries in list. Option -w, --wrap-limit This option wraps queries at a certain length whatever is the part of the query at the limit unless it is a comment. For example if the limit is reach in a text constant the text will be wrapped. Indentation is not included in the character count. This option is applied in all cases even if other options are used. Option -t, --format-type This option activate an alternative formatting that adds: * newline in procedure/function parameter list * new line in PUBLICATION and POLICY DDL Expect this list grow following alternative thoughts. Option -g, --no-grouping By default pgFormatter groups all statements when they are in a transaction: BEGIN; INSERT INTO foo VALUES (1, 'text 1'); INSERT INTO foo VALUES (2, 'text 2'); ... COMMIT; By disabling grouping of statement pgFormatter will always add an extra newline characters between statements just like outside a transaction: BEGIN; INSERT INTO foo VALUES (1, 'text 1'); INSERT INTO foo VALUES (2, 'text 2'); ... COMMIT; This might add readability to not DML transactions. HINTS Formatting from VI With pgFormatter, you can just add the following line to your ~/.vimrc file: au FileType sql setl formatprg=/usr/local/bin/pg_format\ - This lets your gq commands use pgFormatter automagically. For example if you are on the first line, typing: ESC+gq+G will format the entire file. ESC+gq+2j will format the next two line. Thanks to David Fetter for the hint. There is also the (Neo)vim plugin for formatting code for many file types that support pg_format to format SQL file type. Thanks to Anders Riutta for the patch to (Neo)vim. Formatting from Atom If you use atom as your favorite editor you can install the pg-formatter package which is a Node.js wrapper of pgFormatter. Features: * Format selected text or a whole file via keyboard shortcut or command. * Format SQL files on save. Installation: Search for pg-formatter in Atom UI or get it via command line: apm install pg-formatter Usage: Hit Ctrl-Alt-F to format selected text (or a whole file) or define your shortcut: 'ctrl-alt-p': 'pg-formatter:format' Also, you can automatically format SQL files on save (disabled by default). You can download the package from url: https://atom.io/packages/pg-formatter the sources are available at https://github.com/gajus/pg-formatter Thanks to Alex Fedoseev for the atom package. Formatting from Visual Studio Thanks to Brady Holt a Visual Studio Code extension is available to formats PostgresSQL SQL using pgFormatter. https://marketplace.visualstudio.com/items?itemName=bradymholt.pgformatter For installation and use have a look at URL above. Prevent replacing code snippets Using -p or --placeholder command line option it is possible to keep code untouched by pgFormatter in your SQL queries. For example, in query like: SELECT * FROM projects WHERE projectnumber IN <> AND username = <>; you may want pgFormatter to not interpret << and >> as bit-shift keywords and modify your code snippets. You can use a Perl regular expression to instruct pgFormatter to keep some part of the query untouched. For example: pg_format samples/ex9.sql -p '<<(?:.*)?>>' will not format the bit-shift like operators. Prevent dynamic code formatting By default pgFormatter takes all code between single quote as string constant and do not perform any formatting on this code. It is common to use a string as code separator to avoid doubling single quote in dynamic code generation, in this case pgFormatter can fail to auto detect the code separator. By default it will search for any string after the EXECUTE keyword starting with dollar sign. If it can not auto detect your code separator you can use the command line option -S or --separator to set the code separator that must be used. Node.js thin-wrapper Gajus Kuizinas has written a Node.js wrapper for executing pgFormatter. You can find it at https://github.com/gajus/pg-formatter Customize CSS for the CGI output You can change the HTML style rendered through the default CSS style by creating a file named custom_css_file.css into the pgFormatter CGI script directory. The default CSS will be fully overridden by this custom file content. You have to look at the generated HTML output to get the default CSS code used. AUTHORS pgFormatter is an original work from Gilles Darold with major code refactoring by Hubert depesz Lubaczewski. COPYRIGHT Copyright 2012-2020 Gilles Darold. All rights reserved. LICENSE pgFormatter is free software distributed under the PostgreSQL Licence. A modified version of the SQL::Beautify Perl Module is embedded in pgFormatter with copyright (C) 2009 by Jonas Kramer and is published under the terms of the Artistic License 2.0. pgFormatter-4.2/doc/000077500000000000000000000000001361326045100144175ustar00rootroot00000000000000pgFormatter-4.2/doc/pg_format.pod000066400000000000000000000242731361326045100171110ustar00rootroot00000000000000=head1 NAME pg_format - PostgreSQL SQL syntax beautifier =head1 DESCRIPTION This SQL formatter/beautifier supports keywords from SQL-92, SQL-99, SQL-2003, SQL-2008, SQL-2011 and PostgreSQL specifics keywords. Works with any other databases too. pgFormatter can work as a console program or as a CGI. It will automatically detect its environment and format output as text or as HTML following the context. Keywords highlighting will only be available in CGI context. =head2 Terminal/console execution Usage: pg_format [options] file.sql PostgreSQL SQL queries and PL/PGSQL code beautifier. Arguments: file.sql can be a file or use - to read query from stdin. Returning the SQL formatted to stdout or into a file specified with the -o | --output option. Options: -a | --anonymize : obscure all literals in queries, useful to hide confidential data before formatting. -b | --comma-start : in a parameters list, start with the comma (see -e) -B | --comma-break : in insert statement, add a newline after each comma -d | --debug : enable debug mode. Disabled by default. -e | --comma-end : in a parameters list, end with the comma (default) -f | --function-case N: Change the case of the reserved keyword. Default is unchanged: 0. Values: 0=>unchanged, 1=>lowercase, 2=>uppercase, 3=>capitalize. -F | --format STR : output format: text or html. Default: text. -g | --nogrouping : add a newline between statements in transaction regroupement. Default is to group statements. -h | --help : show this message and exit. -m | --maxlength SIZE : maximum length of a query, it will be cutted above the given size. Default: no truncate. -n | --nocomment : remove any comment from SQL code. -o | --output file : define the filename for the output. Default: stdout. -p | --placeholder re : set regex to find code that must not be changed. -s | --spaces size : change space indent, default 4 spaces. -S | --separator STR : dynamic code separator, default to single quote. -t | --format-type : try another formatting type for some statements. -T | --tabs : use tabs instead of space characters, when used spaces is set to 1 whatever is the value set to -s. -u | --keyword-case N : Change the case of the reserved keyword. Default is uppercase: 2. Values: 0=>unchanged, 1=>lowercase, 2=>uppercase, 3=>capitalize. -U | --type-case N : Change the case of the data type name. Default is lowercase: 1. Values: 0=>unchanged, 1=>lowercase, 2=>uppercase, 3=>capitalize. -v | --version : show pg_format version and exit. -w | --wrap-limit N : wrap queries at a certain length. -W | --wrap-after N : number of column after which lists must be wrapped. Default: puts every item on its own line. Examples: cat samples/ex1.sql | pg_format - pg_format -n samples/ex1.sql pg_format -f 2 -n -o result.sql samples/ex1.sql =head2 CGI context Install pg_format into your cgi-bin folder, grant execution on it as a CGI script (maybe you should add the .cgi extension) and get it from your favorite browser. Copy files logo_pgformatter.png and icon_pgformatter.ico in the CGI directory, pg_format.cgi look for them in the same repository. You have a live example without limitation than ten thousand characters in your SQL query here: http://sqlformat.darold.net/ pg_format will automatically detected that it is running in a CGI environment and will output all html code needed to run an online code formatter site. There's nothing more to do. You need to install Perl CGI module first if it is not already the case: yum install perl-cgi or apt-get install libcgi-pm-perl following your distribution. =head1 INSTALLATION Download the tarball from github and unpack the archive as follow: tar xzf pgFormatter-x.x.tar.gz cd pgFormatter-x.x/ perl Makefile.PL make && sudo make install This will copy the Perl script pg_format in /usr/local/bin/pg_format directory by default and the man page into /usr/local/share/man/man1/pg_format.1. Those are the default installation directory for 'site' install. If you want to install all under /usr/ location, use INSTALLDIRS='perl' as argument of Makefile.PL. The script will be installed into /usr/bin/pg_format and the manpage into /usr/share/man/man1/pg_format.1. For example, to install everything just like Debian does, proceed as follow: perl Makefile.PL INSTALLDIRS=vendor By default INSTALLDIRS is set to site. Regression tests can be executed with the following command: make test =head1 SPECIAL FORMATTING =head2 Option -W, --wrap-after This option can be used to set number of column after which lists must be wrapped. By default pgFormatter puts every item on its own line. This format applies to SELECT and FROM list. For example the following query: SELECT a, b, c, d FROM t_1, t_2, t3 WHERE a = 10 AND b = 10; will be formatted into with -W 4: SELECT a, b, c, d FROM t_1, t_2, t3 WHERE a = 10 AND b = 10; Note this formatting doesn't fits well with sub queries in list. =head2 Option -w, --wrap-limit This option wraps queries at a certain length whatever is the part of the query at the limit unless it is a comment. For example if the limit is reach in a text constant the text will be wrapped. Indentation is not included in the character count. This option is applied in all cases even if other options are used. =head2 Option -t, --format-type This option activate an alternative formatting that adds: * newline in procedure/function parameter list * new line in PUBLICATION and POLICY DDL Expect this list grow following alternative thoughts. =head2 Option -g, --no-grouping By default pgFormatter groups all statements when they are in a transaction: BEGIN; INSERT INTO foo VALUES (1, 'text 1'); INSERT INTO foo VALUES (2, 'text 2'); ... COMMIT; By disabling grouping of statement pgFormatter will always add an extra newline characters between statements just like outside a transaction: BEGIN; INSERT INTO foo VALUES (1, 'text 1'); INSERT INTO foo VALUES (2, 'text 2'); ... COMMIT; This might add readability to not DML transactions. =head1 HINTS =head2 Formatting from VI With pgFormatter, you can just add the following line to your ~/.vimrc file: au FileType sql setl formatprg=/usr/local/bin/pg_format\ - This lets your gq commands use pgFormatter automagically. For example if you are on the first line, typing: ESC+gq+G will format the entire file. ESC+gq+2j will format the next two line. Thanks to David Fetter for the hint. There is also the (Neo)vim plugin for formatting code for many file types that support pg_format to format SQL file type. Thanks to Anders Riutta for the patch to (Neo)vim. =head2 Formatting from Atom If you use atom as your favorite editor you can install the pg-formatter package which is a Node.js wrapper of pgFormatter. Features: * Format selected text or a whole file via keyboard shortcut or command. * Format SQL files on save. Installation: Search for pg-formatter in Atom UI or get it via command line: apm install pg-formatter Usage: Hit Ctrl-Alt-F to format selected text (or a whole file) or define your shortcut: 'ctrl-alt-p': 'pg-formatter:format' Also, you can automatically format SQL files on save (disabled by default). You can download the package from url: https://atom.io/packages/pg-formatter the sources are available at https://github.com/gajus/pg-formatter Thanks to Alex Fedoseev for the atom package. =head2 Formatting from Visual Studio Thanks to Brady Holt a Visual Studio Code extension is available to formats PostgresSQL SQL using pgFormatter. https://marketplace.visualstudio.com/items?itemName=bradymholt.pgformatter For installation and use have a look at URL above. =head2 Prevent replacing code snippets Using -p or --placeholder command line option it is possible to keep code untouched by pgFormatter in your SQL queries. For example, in query like: SELECT * FROM projects WHERE projectnumber IN <> AND username = <>; you may want pgFormatter to not interpret << and >> as bit-shift keywords and modify your code snippets. You can use a Perl regular expression to instruct pgFormatter to keep some part of the query untouched. For example: pg_format samples/ex9.sql -p '<<(?:.*)?>>' will not format the bit-shift like operators. =head2 Prevent dynamic code formatting By default pgFormatter takes all code between single quote as string constant and do not perform any formatting on this code. It is common to use a string as code separator to avoid doubling single quote in dynamic code generation, in this case pgFormatter can fail to auto detect the code separator. By default it will search for any string after the EXECUTE keyword starting with dollar sign. If it can not auto detect your code separator you can use the command line option -S or --separator to set the code separator that must be used. =head2 Node.js thin-wrapper Gajus Kuizinas has written a Node.js wrapper for executing pgFormatter. You can find it at https://github.com/gajus/pg-formatter =head2 Customize CSS for the CGI output You can change the HTML style rendered through the default CSS style by creating a file named custom_css_file.css into the pgFormatter CGI script directory. The default CSS will be fully overridden by this custom file content. You have to look at the generated HTML output to get the default CSS code used. =head1 AUTHORS pgFormatter is an original work from Gilles Darold with major code refactoring by Hubert depesz Lubaczewski. =head1 COPYRIGHT Copyright 2012-2020 Gilles Darold. All rights reserved. =head1 LICENSE pgFormatter is free software distributed under the PostgreSQL Licence. A modified version of the SQL::Beautify Perl Module is embedded in pgFormatter with copyright (C) 2009 by Jonas Kramer and is published under the terms of the Artistic License 2.0. pgFormatter-4.2/icon_pgformatter.ico000066400000000000000000004241321361326045100177160ustar00rootroot00000000000000 D((r +)2,R*~s)++*************++)+r,R)2-x+ +};*h********************************+*h+};+ &{)~W**+**************************************+**V'@+<)+*~************************************************)D$mq +T*********************************************************+~Sq @+~M+**************************************************************,LU.+~w*******************************************************************)-",F*************************************************************************,F3f*~U****************************************************************************)+f +)j**********************************************************************************h+*~U*************************************************************************************+f@*~O*****************************************************************************************+~M-(*********************************************************************************************(}-$************************************************************************************************+'v )J***************************************************************************************************)~Q,z******************************************************************************************************+.)~E*********************************************************************************************************)~K )***********************************************************************************************************~*3 -(*************************************************3CSU^cc^UTC2*************************************************)(&+~M+*****************************************.OttO-*******************************************+~SU****************************************EwuA****************************************~)U$*************************************4ki2**************************************'v '***********************************4tr2***********************************+,{(}-*********************************+_]************************************}1)>********************************65********************************++<+~A*******************************=A********************************,F,L******************************?<*******************************,R)P*****************************76*******************************~O)D****************************,y,******************************~I,:****************************VS*****************************)>*}1***************************23***************************+,.'***************************ZX****************************,{'v **************************--***************************"w@**************************@=***************************U+*************************[Y**************************+*~O*~************************|**************************~+~S'|'************************+,*************************,|)q ************************1/************************+ )***********************32************************++H*~**********************45************************~)~K.***********************64************************$y***********************21***********************))~K**********************/-***********************+N$**********************+uaSD7u'eVjttttttttttttttmS&d5tCO`y************************'v )**********************lM|.] svtrsttttttttttttttttttttttttttttrrtvf-[L{o***********************++***********************tj1x~qqttttttttttttttttttttttttttttttttttttttttttttlr~4{hn***********************),)*********************Pk9jottttttttttttttttttttttttttttttttttttttttttttttttttttttgk6rN***********************)~Q*********************5bhttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttvX6**********************,L3f**********************vitttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt Zl***********************@+f*********************_sptttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttwQy***********************~a+*********************Gdqtttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttsk`H**********************+*~m********************-,dttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttz,**********************~g$m*********************ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt op**********************3f*b********************CivttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttqtA**********************\)********************utttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt v*********************+*~I********************a:{tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt&g^*********************)~E+*******************-attttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt,*********************'|!********************~tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttqy*********************~,|#********************/tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttq/********************)@********************tttttttttttttttttttttttttttttrprt w y z{Y*U7aAlMxNyX____XNyLwAl9d*UJ}v z x wtrptttttttttttttttttttttttttttttq}********************+U)P*******************0tttttttttttttttttttttttttc Z:uTojT9s"\]ttttttttttttttttttttttttq.********************,L********************|ttttttttttttttttttp`1{GfhI1{fttttttttttttttttttqv*********************3 ********************ttttttttttttttk {?luu?}yqtttttttttttttq*********************q )~W*******************fttttttttttqu1mxs:jxqtttttttttq`********************,R)*******************tttttttth********************$m,F*******************tttjLqPsttq********************+~A)*******************tfPtp}dqSrD@<<977778:<@EO}cpq~^vq********************+*******************U4z]G1p[\ttttttttttttttttttttttttttttnZ4rD^yBtN*******************+,{*******************_,[ gutsttttttttttttttttttttttttttttttttttttttssuj-_\********************'+~Y*******************GrjttttttttttttttttttttttttttttttttttttttttttttttttttprJt*********************~U)******************Nj0 ettttttttttttttttttttttttttttttttttttttttttttttttttttttttttttr-|l|H*******************+*******************m-utttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttp)kk*******************)'v +******************FuftttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttG}********************+ *}=******************1@o qsttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt s=.*******************,:+~k******************\msttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttjcV********************~g*******************%|tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt0l*******************)+******************xttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttb***************************************{tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt#v********************&******************9]stttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttM6*******************0+}5******************_\ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttvZ********************}1*~U******************tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttr~*******************,R)v******************tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttq*******************+r+******************ttttttttttttttttttttttttttttttttttf hmpuy{#$'(((('$#~{yuql icottttttttttttttttttttttttttttttttq********************+******************ttttttttttttttttttttttttsr u y{*VCl`xyaEn&[t y uqstttttttttttttttttttttttq***************************************ttttttttttttttttttttp];uWvwX8rTttttttttttttttttttttq*******************)******************-ttttttttttttttttl>e{d=crttttttttttttttq+******************+******************<tttttttttttsq9w|:krkttttttttttq6*************************************LttttttttoyIpG}|rtttttttqZ******************+******************VttttttoD{I Ytttttqt*************************************\tttt%wrn'npttqx*************************************cto#xvtjffffitv/jtq|*************************************hyQC5+{ pf^sttttttttttttttttttm \fn+{7CU|}"n*************************************ftM+kYtttttttttttttttttttttttttttttttttttttttpR+kKte*************************************cPeutsttttttttttttttttttttttttttttttttttttttttttttttstu gL|~*************************************_e rpttttttttttttttttttttttttttttttttttttttttttttttttttttttttrp(w_x*************************************YT%wsttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttl#vWs******************+******************K5ztttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttn0sk*************************************=ukttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttuy]*************************************.(kqttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttr#-******************+*******************X vtttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt nR*******************++******************Jztttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt-********************)v******************6ztttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt6v********************~s*V******************urttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttsk*******************,R+}5******************'httttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttp********************}1$y******************`etttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttts\*******************-x******************;tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttq7**************************************tttttttttttttttttttttttttttttttttttttttttqfffffffftttttttttttttttttttttttttttttttttttttttttq********************)******************tttttttttttttttttttttttttttp gr {(/5Ope~|zgPj9/(!}q htttttttttttttttttttttttttttq*******************+*~m******************ttttttttttttttttttttsq wl>g[|~]>gh vqrtttttttttttttttttttq*******************)~i(}?******************`tttttttttttttttttt#iRwsS&_nttttttttttttttttqV*******************+};$+*****************2ttttttttttttta5~nxl;httttttttttttq.*******************+ )******************ttttttttti$|MsP#|pttttttttq********************+******************tttttttyUQwkptttttq*********************~[******************Qttttp3i7vttttqI*******************)~W){******************+tttP|Jitq********************&{*******************kHqQo2s'#|xusq n n n n n n n nrsux{"'/yOmpEm q********************+******************ZjyD.m`lttttttttttttttttttttttttttttttttl_n.~?jP********************+~G******************+c7x ltttttttttttttttttttttttttttttttttttttttttttttttta8y]********************+B *******************_+_uttttttttttttttttttttttttttttttttttttttttttttttttttttttttj$Ua********************$m*******************ANotstttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttto|I{?********************+~Y*******************o&yrttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttk'zg********************+T.t *******************fW~jtttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt oGd********************.t *******************+VrttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttaR+*******************+)~Q*******************!ytttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttscx********************+~M@*******************2~ ktttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt wn.********************@********************n mtttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttk********************+-"*******************1utttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttth/********************~,|#+*******************5vtttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt%bz*********************)J*******************-nttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt-********************+~G********************htttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttrd*********************)d********************ltttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttts*********************+~_$m********************C%itttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttthB*********************+)~o********************rsttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttom*********************)~i$m********************-9rrttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt9,*********************$m*h********************K?etttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt-~K*********************)~c+*********************Ww stttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt g\|**********************@+T********************+${sttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt(}***********************~O+********************8{"motttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttts{z7**********************+0+********************R{ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttoO*********************+,.**********************y]#gpttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttn"m_s***********************$**********************Z,htttttttttttttttttttttttttttttttttttttttttttttttttttttttttr j.V~***********************0,L*********************+Sv&iurttttttttttttttttttttttttttttttttttttttttttttttttnu|Sv************************~O**********************/mK}ZotttttttttttttttttttttttttttttttttttttttttWFxm.**********************+++*********************2\M3u"dVpttttttttttttttttttttttfT!c4vK`3***********************.*~I*~*********************8|n~n~n~n~n~n~n~n~|5***********************~+~M***********************77************************3 ***********************63************************q -(***********************20*************************}+)X***********************-,************************~*~U)*************************************************+@*************************^a**************************U$)************************E@**************************0&{*************************-.**************************+(}3**************************a[***************************+0+};**************************25**************************+,@)~E***************************^W****************************)J)~Q***************************--****************************)P+~M****************************<7*****************************+~S*~I+****************************CA******************************+H)>******************************CC********************************~C,.*******************************97********************************(}3&{********************************,fb,*********************************+"w**********************************7{y7***********************************$@)*~**********************************7qq7*************************************3f)X**************************************J|{K****************************************~U),*****************************************1U|yT1******************************************,|)q **~**********************************************7JUVcccc[UF7************************************************+.t +~G********************************************************************************************************++~M+*******************************************************************************************************,z,L***************************************************************************************************+~S"w*+*********************************************************************************************+)$,|)*********************************************************************************************+}/+~S*****************************************************************************************)~Q3f)^+************************************************************************************)~o$m *n**********************************************************************************~m$m+)^*****************************************************************************)~oq *~I+*~**********************************************************************~+*~I,{+~w*******************************************************************),|#3f)~Q****************************************************************~O@+ )X+*******************************************************))~W.t )D)+*************************************************+~M +*~[)*+*************************************+**+Z,{$)>+~k*******************************+*)j*}='v $y+}5*~U)~u+*********+******++)~u+T,4&????????????????????pgFormatter-4.2/lib/000077500000000000000000000000001361326045100144205ustar00rootroot00000000000000pgFormatter-4.2/lib/pgFormatter/000077500000000000000000000000001361326045100167125ustar00rootroot00000000000000pgFormatter-4.2/lib/pgFormatter/Beautify.pm000077500000000000000000004533041361326045100210340ustar00rootroot00000000000000package pgFormatter::Beautify; use strict; use warnings; use warnings qw( FATAL ); use Encode qw( decode ); use Text::Wrap; our $DEBUG = 0; our $DEBUG_SP = 0; # PostgreSQL functions that use a FROM clause our @have_from_clause = qw( extract overlay substring trim ); =head1 NAME pgFormatter::Beautify - Library for pretty-printing SQL queries =head1 VERSION Version 4.2 =cut # Version of pgFormatter our $VERSION = '4.2'; # Inclusion of code from Perl package SQL::Beautify # Copyright (C) 2009 by Jonas Kramer # Published under the terms of the Artistic License 2.0. =head1 SYNOPSIS This module can be used to reformat given SQL query, optionally anonymizing parameters. Output can be either plain text, or it can be HTML with appropriate styles so that it can be displayed on a web page. Example usage: my $beautifier = pgFormatter::Beautify->new(); $beautifier->query( 'select a,b,c from d where e = f' ); $beautifier->beautify(); my $nice_txt = $beautifier->content(); $beautifier->format('html'); $beautifier->beautify(); my $nice_html = $beautifier->content(); $beautifier->format('html'); $beautifier->anonymize(); $beautifier->beautify(); my $nice_anonymized_html = $beautifier->content(); $beautifier->format(); $beautifier->beautify(); $beautifier->wrap_lines() my $wrapped_txt = $beautifier->content(); =head1 FUNCTIONS =head2 new Generic constructor - creates object, sets defaults, and reads config from given hash with options. Takes options as hash. Following options are recognized: =over =item * break - String that is used for linebreaks. Default is "\n". =item * colorize - if set to false CSS style will not be applied to html output. Used internally to display errors in CGI mode withour style. =item * comma - set comma at beginning or end of a line in a parameter list =over =item end - put comma at end of the list (default) =item start - put comma at beginning of the list =back =item * comma_break - add new-line after each comma in INSERT statements =item * format - set beautify format to apply to the content (default: text) =over =item text - output content as plain/text (command line mode default) =item html - output text/html with CSS style applied to content (CGI mode default) =back =item * functions - list (arrayref) of strings that are function names =item * keywords - list (arrayref) of strings that are keywords =item * no_comments - if set to true comments will be removed from query =item * no_grouping - if set to true statements will not be grouped in a transaction, an extra newline character will be added between statements like outside a transaction. =item * placeholder - use the specified regex to find code that must not be changed in the query. =item * query - query to beautify =item * rules - hash of rules - uses rule semantics from SQL::Beautify =item * space - character(s) to be used as space for indentation =item * spaces - how many spaces to use for indentation =item * uc_functions - what to do with function names: =over =item 0 - do not change =item 1 - change to lower case =item 2 - change to upper case =item 3 - change to Capitalized =back =item * separator - string used as dynamic code separator, default is single quote. =item * uc_keywords - what to do with keywords - meaning of value like with uc_functions =item * uc_types - what to do with data types - meaning of value like with uc_functions =item * wrap - wraps given keywords in pre- and post- markup. Specific docs in SQL::Beautify =item * format_type - try an other formatting =item * wrap_limit - wrap queries at a certain length =item * wrap_after - number of column after which lists must be wrapped =back For defaults, please check function L. =cut sub new { my $class = shift; my %options = @_; my $self = bless {}, $class; $self->set_defaults(); for my $key ( qw( query spaces space break wrap keywords functions rules uc_keywords uc_functions uc_types no_comments no_grouping placeholder separator comma comma_break format colorize format_type wrap_limit wrap_after) ) { $self->{ $key } = $options{ $key } if defined $options{ $key }; } $self->_refresh_functions_re(); # Make sure "break" is sensible $self->{ 'break' } = ' ' if $self->{ 'spaces' } == 0; # Initialize internal stuff. $self->{ '_level' } = 0; # Array to store placeholders values @{ $self->{ 'placeholder_values' } } = (); # Hash to store dynamic code %{ $self->{ 'dynamic_code' } } = (); # Check comma value, when invalid set to default: end if (lc($self->{ 'comma' }) ne 'start') { $self->{ 'comma' } = 'end'; } else { $self->{ 'comma' } = lc($self->{ 'comma' }); } $self->{ 'format' } //= 'text'; $self->{ 'colorize' } //= 1; $self->{ 'format_type' } //= 0; $self->{ 'wrap_limit' } //= 0; $self->{ 'wrap_after' } //= 0; return $self; } =head2 query Accessor to query string. Both reads: $object->query() , and writes $object->query( $something ) =cut sub query { my $self = shift; my $new_value = shift; $self->{ 'query' } = $new_value if defined $new_value; my $i = 0; my %temp_placeholder = (); my @temp_content = split(/(CREATE(?:\s+OR\s+REPLACE)?\s+(?:FUNCTION|PROCEDURE)\s+)/i, $self->{ 'query' }); if ($#temp_content > 0) { for (my $j = 0; $j <= $#temp_content; $j++) { next if ($temp_content[$j] =~ /^CREATE/i or $temp_content[$j] eq ''); # Rewrite single quote code delimiter into $$ if ($temp_content[$j] =~ s/(\s+AS\s+)'(\s+.*?;\s*)'/$1\$\$$2\$\$/is || $temp_content[$j] =~ s/(\s+AS\s+)'(\s+.*?END[;]*\s*)'/$1\$\$$2\$\$/is ) { # Fix some double quote use, this might not cover all cases $temp_content[$j] =~ s/''''/EMPTYSTRDBLQT/isg; $temp_content[$j] =~ s/''([^']+)''/'$1'/isg; $temp_content[$j] =~ s/''''/''/isg; $temp_content[$j] =~ s/EMPTYSTRDBLQT/''/isg; } # Remove any call too CREATE/DROP LANGUAGE to not break search of function code separator $temp_content[$j] =~ s/(CREATE|DROP)\s+LANGUAGE\s+[^;]+;.*//is; # Fix case where code separator with $ is associated to begin/end keywords $temp_content[$j] =~ s/([^\s]+\$)(BEGIN\s)/$1 $2/igs; $temp_content[$j] =~ s/(\sEND)(\$[^\s]+)/$1 $2/igs; $temp_content[$j] =~ s/(CREATE|DROP)\s+LANGUAGE\s+[^;]+;.*//is; my $fctname = ''; if ($temp_content[$j] =~ /^([^\s\(]+)/) { $fctname = lc($1); } next if (!$fctname); my $language = 'sql'; if ($temp_content[$j] =~ /\s+LANGUAGE\s+[']*([^'\s;]+)[']*/i) { $language = lc($1); } if ($language =~ /^internal$/i) { if ($temp_content[$j] =~ s/AS ('[^\']+')/AS CODEPARTB${i}CODEPARTB/i) { push(@{ $self->{ 'placeholder_values' } }, $1); $i++; } } # if the function language is not SQL or PLPGSQL elsif ($language !~ /^(?:plpg)?sql$/) { # Try to find the code separator my $tmp_str = $temp_content[$j]; while ($tmp_str =~ s/\s+AS\s+([^\s]+)\s+//is) { my $code_sep = quotemeta($1); foreach my $k (@{ $self->{ 'keywords' } }) { last if ($code_sep =~ s/\b$k$//i); } next if (!$code_sep); if ($tmp_str =~ /\s+$code_sep[\s;]+/) { while ( $temp_content[$j] =~ s/($code_sep(?:.+?)$code_sep)/CODEPART${i}CODEPART/s) { push(@{ $self->{ 'placeholder_values' } }, $1); $i++; } last; } } } } } $self->{ 'query' } = join('', @temp_content); # Replace any \' by %BSLH% $self->{ 'query' } =~ s/\\'/PGFBSLHQ/g; # Store values of code that must not be changed following the given placeholder if ($self->{ 'placeholder' }) { while ( $self->{ 'query' } =~ s/($self->{ 'placeholder' })/PLACEHOLDER${i}PLACEHOLDER/) { push(@{ $self->{ 'placeholder_values' } }, $1); $i++; } } # Replace dynamic code with placeholder $self->_remove_dynamic_code( \$self->{ 'query' }, $self->{ 'separator' } ); # Replace operator with placeholder $self->_quote_operator( \$self->{ 'query' } ); # Replace comment with not quote delimiter with placeholder $self->_quote_comment_stmt( \$self->{ 'query' } ); return $self->{ 'query' }; } =head2 content Accessor to content of results. Must be called after $object->beautify(). This can be either plain text or html following the format asked by the client with the $object->format() method. =cut sub content { my $self = shift; my $new_value = shift; $self->{ 'content' } = $new_value if defined $new_value; $self->{ 'content' } =~ s/\(\s+\(/\(\(/gs; # Replace placeholders with their original dynamic code $self->_restore_dynamic_code( \$self->{ 'content' } ); # Replace placeholders with their original operator $self->_restore_operator( \$self->{ 'content' } ); # Replace placeholders with their original string $self->_restore_comment_stmt( \$self->{ 'content' } ); # Replace placeholders by their original values if ($#{ $self->{ 'placeholder_values' } } >= 0) { $self->{ 'content' } =~ s/PLACEHOLDER(\d+)PLACEHOLDER/$self->{ 'placeholder_values' }[$1]/igs; $self->{ 'content' } =~ s/CODEPART[B]*(\d+)CODEPART[B]*/$self->{ 'placeholder_values' }[$1]/igs; } # Replace any %BSLH% by \' $self->{ 'content' } =~ s/PGFBSLHQ/\\'/g; return $self->{ 'content' }; } =head2 highlight_code Makes result html with styles set for highlighting. =cut sub highlight_code { my ($self, $token, $last_token, $next_token) = @_; # Do not use uninitialized variable $last_token //= ''; $next_token //= ''; # Colorize operators while ( my ( $k, $v ) = each %{ $self->{ 'dict' }->{ 'symbols' } } ) { if ($token eq $k) { $token = '' . $v . ''; return $token; } } # lowercase/uppercase keywords taking care of function with same name if ( $self->_is_keyword( $token, $next_token, $last_token ) && (!$self->_is_function( $token ) || $next_token ne '(') ) { if ( $self->{ 'uc_keywords' } == 1 ) { $token = '' . $token . ''; } elsif ( $self->{ 'uc_keywords' } == 2 ) { $token = '' . $token . ''; } elsif ( $self->{ 'uc_keywords' } == 3 ) { $token = '' . $token . ''; } else { $token = '' . $token . ''; } return $token; } # lowercase/uppercase known functions or words followed by an open parenthesis # if the token is not a keyword, an open parenthesis or a comment if (($self->_is_function( $token ) && $next_token eq '(') || (!$self->_is_keyword( $token, $next_token, $last_token ) && !$next_token eq '(' && $token ne '(' && !$self->_is_comment( $token )) ) { if ($self->{ 'uc_functions' } == 1) { $token = '' . $token . ''; } elsif ($self->{ 'uc_functions' } == 2) { $token = '' . $token . ''; } elsif ($self->{ 'uc_functions' } == 3) { $token = '' . $token . ''; } else { $token = '' . $token . ''; } return $token; } # Colorize STDIN/STDOUT in COPY statement if ( grep(/^\Q$token\E$/i, @{ $self->{ 'dict' }->{ 'copy_keywords' } }) ) { if ($self->{ 'uc_keywords' } == 1) { $token = '' . $token . ''; } elsif ($self->{ 'uc_keywords' } == 2) { $token = '' . $token . ''; } elsif ($self->{ 'uc_keywords' } == 3) { $token = '' . $token . ''; } else { $token = '' . $token . ''; } return $token; } # Colorize parenthesis if ( grep(/^\Q$token\E$/i, @{ $self->{ 'dict' }->{ 'brackets' } }) ) { $token = '' . $token . ''; return $token; } # Colorize comment if ( $self->_is_comment( $token ) ) { $token = '' . $token . ''; return $token; } # Colorize numbers $token =~ s/\b(\d+)\b/$1<\/span>/igs; # Colorize string $token =~ s/('.*?(?$1<\/span>/gs; $token =~ s/(`[^`]*`)/$1<\/span>/gs; return $token; } =head2 tokenize_sql Splits input SQL into tokens Code lifted from SQL::Beautify =cut sub tokenize_sql { my $self = shift; my $query = $self->query(); my $re = qr{ ( (?:\\(?:copyright|errverbose|g|gx|gexec|gset|q|crosstabview|watch|\?|h|e|ef|ev|p|r|s|w|copy|echo|i|ir|o|qecho|if|elif|else|endif|d(?:[aAbcCdDfFgilLmnoOpstTuvExy]|dp|et|es|eu|ew|fa|fn|ft|fw|Fd|Fp|Ft|rds|Rp|Rs)?S?\+?|l\+?|sf\+?|sv\+?|z|a|C|f|H|pset|t|T|x|c|connect|encoding|password|conninfo|cd|setenv|timing|\!|prompt|set|unset|lo_export|lo_import|lo_list|lo_unlink))(?:$|[\n]|[\ \t](?:(?!\\\\)[\ \t\S])*) # psql meta-command | (?:\s*--)[\ \t\S]* # single line comments | (?:\-\|\-) # range operator "is adjacent to" | (?:\->>|\->|\#>>|\#>|\?\&|\?) # Json Operators | (?:\#<=|\#>=|\#<>|\#<|\#=) # compares tinterval and reltime | (?:>>=|<<=) # inet operators | (?:!!|\@\@\@) # deprecated factorial and full text search operators | (?:\|\|\/|\|\/) # square root and cube root | (?:\@\-\@|\@\@|\#\#|<\->|<<\||\|>>|\&<\||\&<|\|\&>|\&>|<\^|>\^|\?\#|\#|\?<\||\?\-\||\?\-|\?\|\||\?\||\@>|<\@|\~=) # Geometric Operators | (?:~<=~|~>=~|~>~|~<~) # string comparison for pattern matching operator families | (?:!~~|!~~\*|~~\*|~~) # LIKE operators | (?:!~\*|!~|~\*) # regular expression operators | (?:\*=|\*<>|\*<=|\*>=|\*<|\*>) # composite type comparison operators | (?:<>|<=>|>=|<=|=>|==|!=|:=|=|!|<<|>>|<|>|\|\||\||&&|&|-|\+|\*(?!/)|/(?!\*)|\%|~|\^|\?) # operators and tests | [\[\]\(\),;.] # punctuation (parenthesis, comma) | E\'\'(?!\') # empty single escaped quoted string | \'\'(?!\') # empty single quoted string | \"\"(?!\"") # empty double quoted string | '[^']+'(?!') # anyhing into single quoted string | "(?>(?:(?>[^"\\]+)|""|\\.)*)+" # anything inside double quotes, ungreedy | `(?>(?:(?>[^`\\]+)|``|\\.)*)+` # anything inside backticks quotes, ungreedy | E'(?>(?:(?>[^'\\]+)|''|\\.)*)+' # anything escaped inside single quotes, ungreedy. | '(?>(?:(?>[^'\\]+)|''|\\.)*)+' # anything inside single quotes, ungreedy. | /\*[\ \t\r\n\S]*?\*/ # C style comments | (?:[\w:\@]+[\$]*[\w:\@]*(?:\.(?:\w+|\*)?)*) # words, standard named placeholders, db.table.*, db.* | (?:\$\w+\$) | (?: \$_\$ | \$\d+ | \${1,2} | \$\w+\$ ) # dollar expressions - eg $_$ $3 $$ $BODY$ | \n # newline | [\t\ ]+ # any kind of white spaces ) }smx; my @query = (); @query = grep { /\S/ } $query =~ m{$re}smxg; map { s/(.*PGFBSLHQ)$/'$1/; } @query; $self->{ '_tokens' } = \@query; return @query; } sub _pop_level { my ($self, $token, $last_token) = @_; if ($DEBUG) { my ($package, $filename, $line) = caller; print STDERR "DEBUG_POP: line: $line => last=", ($last_token||''), ", token=$token\n"; } return pop( @{ $self->{ '_level_stack' } } ) || 0; } sub _reset_level { my ($self, $token, $last_token) = @_; if ($DEBUG) { my ($package, $filename, $line) = caller; print STDERR "DEBUG_RESET: line: $line => last=", ($last_token||''), ", token=$token\n"; } @{ $self->{ '_level_stack' } } = (); $self->{ '_level' } = 0; $self->{ 'break' } = ' ' unless ( $self->{ 'spaces' } != 0 ); } sub _set_level { my ($self, $position, $token, $last_token) = @_; return 0 if (not defined $position); if ($DEBUG) { my ($package, $filename, $line) = caller; print STDERR "DEBUG_SET: line: $line => position=$position, last=", ($last_token||''), ", token=$token\n"; } $self->{ '_level' } = ($position >= 0) ? $position : 0; } sub _push_level { my ($self, $position, $token, $last_token) = @_; if ($DEBUG) { my ($package, $filename, $line) = caller; print STDERR "DEBUG_PUSH: line: $line => position=$position, last=", ($last_token||''), ", token=$token\n"; } push(@{ $self->{ '_level_stack' } }, (($position >= 0) ? $position : 0)); } sub _set_last { my ($self, $token, $last_token) = @_; if ($DEBUG) { my ($package, $filename, $line) = caller; print STDERR "DEBUG_LAST: line: $line => last=", ($last_token||''), ", token=$token\n"; } return $token; } =head2 beautify Beautify SQL. After calling this function, $object->content() will contain nicely indented result. Code lifted from SQL::Beautify =cut sub beautify { my $self = shift; # Use to store the token position in the array my $pos = 0; # Main variables used to store differents state $self->content( '' ); $self->{ '_level' } = 0; $self->{ '_level_stack' } = []; $self->{ '_level_parenthesis' } = []; $self->{ '_new_line' } = 1; $self->{ '_current_sql_stmt' } = ''; $self->{ '_is_meta_command' } = 0; $self->{ '_fct_code_delimiter' } = ''; $self->{ '_first_when_in_case' } = 0; $self->{ '_is_in_if' } = 0; $self->{ '_is_in_conversion' } = 0; $self->{ '_is_in_case' } = 0; $self->{ '_is_in_where' } = 0; $self->{ '_is_in_from' } = 0; $self->{ '_is_in_join' } = 0; $self->{ '_is_in_create' } = 0; $self->{ '_is_in_rule' } = 0; $self->{ '_is_in_create_function' } = 0; $self->{ '_is_in_alter' } = 0; $self->{ '_is_in_trigger' } = 0; $self->{ '_is_in_publication' } = 0; $self->{ '_is_in_call' } = 0; $self->{ '_is_in_type' } = 0; $self->{ '_is_in_declare' } = 0; $self->{ '_is_in_block' } = -1; $self->{ '_is_in_work' } = 0; $self->{ '_is_in_function' } = 0; $self->{ '_is_in_statistics' } = 0; $self->{ '_is_in_procedure' } = 0; $self->{ '_is_in_index' } = 0; $self->{ '_is_in_with' } = 0; $self->{ '_is_in_explain' } = 0; $self->{ '_is_in_overlaps' } = 0; $self->{ '_parenthesis_level' } = 0; $self->{ '_parenthesis_function_level' } = 0; $self->{ '_has_order_by' } = 0; $self->{ '_has_over_in_join' } = 0; $self->{ '_insert_values' } = 0; $self->{ '_is_in_constraint' } = 0; $self->{ '_is_in_distinct' } = 0; $self->{ '_is_in_array' } = 0; $self->{ '_is_in_filter' } = 0; $self->{ '_parenthesis_filter_level' } = 0; $self->{ '_is_in_within' } = 0; $self->{ '_is_in_grouping' } = 0; $self->{ '_is_in_partition' } = 0; $self->{ '_is_in_over' } = 0; $self->{ '_is_in_policy' } = 0; $self->{ '_is_in_using' } = 0; $self->{ '_and_level' } = 0; $self->{ '_col_count' } = 0; $self->{ '_is_in_drop' } = 0; $self->{ '_is_in_operator' } = 0; $self->{ '_is_in_exception' } = 0; $self->{ '_is_in_sub_query' } = 0; $self->{ '_is_in_fetch' } = 0; $self->{ '_is_in_aggregate' } = 0; $self->{ '_is_in_value' } = 0; $self->{ '_parenthesis_level_value' } = 0; $self->{ '_parenthesis_with_level' } = 0; $self->{ '_is_in_returns_table' } = 0; $self->{ '_has_limit' } = 0; $self->{ '_not_a_type' } = 0; my $last = ''; my @token_array = $self->tokenize_sql(); while ( defined( my $token = $self->_token ) ) { my $rule = $self->_get_rule( $token ); # Replace concat operator found in some SGBD into || for normalization if (lc($token) eq 'concat' && defined $self->_next_token() && $self->_next_token ne '(') { $token = '||'; } # Case where a keyword is used as a column name. if ( $self->{ '_is_in_create' } > 1 and $self->_is_keyword( $token, $self->_next_token(), $last ) and defined $self->_next_token and $self->_is_type($self->_next_token)) { $self->_add_token($token, $last); $last = $self->_set_last($token, $last); next; } #### # Find if the current keyword is a known function name #### if (defined $last && $last && defined $self->_next_token and $self->_next_token eq '(') { my $word = lc($token); $word =~ s/^[^\.]+\.//; $word =~ s/^:://; if (uc($last) eq 'FUNCTION' and $token =~ /^\d+$/) { $self->{ '_is_in_function' }++; } elsif ($word && exists $self->{ 'dict' }->{ 'pg_functions' }{$word}) { $self->{ '_is_in_function' }++; # Try to detect user defined functions } elsif ($last ne '*' and !$self->_is_keyword($token, $self->_next_token(), $last) and (exists $self->{ 'dict' }->{ 'symbols' }{ $last } or $last =~ /^\d+$/) ) { $self->{ '_is_in_function' }++; } } #### # Set open parenthesis position to know if we # are in subqueries or function parameters #### if ( $token eq ')') { $self->{ '_parenthesis_filter_level' }-- if ($self->{ '_parenthesis_filter_level' }); $self->{ '_parenthesis_with_level' }-- if ($self->{ '_parenthesis_with_level' }); $self->{ '_is_in_filter' } = 0 if (!$self->{ '_parenthesis_filter_level' }); if (!$self->{ '_is_in_function' }) { $self->{ '_parenthesis_level' }-- if ($self->{ '_parenthesis_level' } > 0); } else { $self->{ '_parenthesis_function_level' }-- if ($self->{ '_parenthesis_function_level' } > 0); if (!$self->{ '_parenthesis_function_level' }) { $self->_set_level(pop(@{ $self->{ '_level_parenthesis_function' } }) || 0, $token, $last); $self->_over($token,$last) if (!$self->{ '_is_in_create' } && !$self->{ '_is_in_operator' } && !$self->{ '_is_in_alter' }); } } $self->{ '_is_in_function' } = 0 if (!$self->{ '_parenthesis_function_level' }); if (!$self->{ '_parenthesis_level' } && $self->{ '_is_in_sub_query' }) { $self->{ '_is_in_sub_query' }--; $self->_back($token, $last); } if ($self->{ '_is_in_value' }) { $self->{ '_parenthesis_level_value' }-- if ($self->{ '_parenthesis_level_value' }); } } elsif ( $token eq '(') { $self->{ '_parenthesis_filter_level' }++ if ($self->{ '_is_in_filter' }); $self->{ '_parenthesis_with_level' }++ if ($self->{ '_is_in_with' }); if ($self->{ '_is_in_function' }) { $self->{ '_parenthesis_function_level' }++; push(@{ $self->{ '_level_parenthesis_function' } } , $self->{ '_level' }) if ($self->{ '_parenthesis_function_level' } == 1); } else { if (!$self->{ '_parenthesis_level' } && $self->{ '_is_in_from' }) { push(@{ $self->{ '_level_parenthesis' } } , $self->{ '_level' }); } $self->{ '_parenthesis_level' }++; if ($self->{ '_is_in_value' }) { $self->{ '_parenthesis_level_value' }++; } } if (defined $self->_next_token and $self->_next_token =~ /^(SELECT|WITH)$/i) { $self->{ '_is_in_sub_query' }++ if (defined $last and uc($last) ne 'AS'); } } #### # Control case where we have to add a newline, go back and # reset indentation after the last ) in the WITH statement #### if ($token =~ /^WITH$/i && (!defined $last || $last ne ')') && !$self->{ '_is_in_partition' } && !$self->{ '_is_in_publication' } && !$self->{ '_is_in_policy' } && uc($self->_next_token) ne 'TIME') { $self->{ '_is_in_with' } = 1 if (!$self->{ '_is_in_using' } && uc($self->_next_token) ne 'ORDINALITY' && uc($last) ne 'START'); $self->{ 'no_break' } = 1 if (uc($self->_next_token) eq 'ORDINALITY'); } elsif ($token =~ /^WITH$/i && uc($self->_next_token) eq 'ORDINALITY') { $self->{ 'no_break' } = 1; } elsif ($token =~ /^(AS|IS)$/i && defined $self->_next_token && $self->_next_token eq '(') { $self->{ '_is_in_with' }++ if ($self->{ '_is_in_with' } == 1); } elsif ($self->{ '_is_in_create' } && $token =~ /^AS$/i && defined $self->_next_token && uc($self->_next_token) eq 'SELECT') { $self->{ '_is_in_create' } = 0; } elsif ( $token eq '[' ) { $self->{ '_is_in_array' }++; } elsif ( $token eq ']' ) { $self->{ '_is_in_array' }-- if ($self->{ '_is_in_array' }); } elsif ( $token eq ')' ) { $self->{ '_has_order_by' } = 0; if ($self->{ '_is_in_distinct' }) { $self->_add_token( $token ); $self->_new_line($token,$last); $self->{ '_is_in_distinct' } = 0; $last = $self->_set_last($token, $last); next; } $self->{ '_is_in_using' } = 0 if ($self->{ '_is_in_using' } and !$self->{ '_parenthesis_level' }); $self->{ '_is_in_with' } = 0 if (defined $self->_next_token and $self->_next_token !~ /^AS|WITH|,$/i and !$self->{ '_parenthesis_with_level' }); if ($self->{ '_is_in_create' } > 1 and defined $self->_next_token && uc($self->_next_token) eq 'AS' && !$self->{ '_is_in_with'}) { $self->_new_line($token,$last) if ($last ne '('); if ($self->{ '_is_in_returns_table' } and !$self->{ '_parenthesis_level' }) { $self->{ '_is_in_returns_table' } = 0; $self->_back($token, $last); $self->_add_token( $token, $last ); $last = $self->_set_last($token, $last); next; } else { $self->_over($token, $last) if ($self->{ '_is_in_procedure' }); } } if (($self->{ '_is_in_with' } > 1 || $self->{ '_is_in_operator' }) && !$self->{ '_parenthesis_level' } && !$self->{ '_parenthesis_with_level' } && !$self->{ '_is_in_alter' } && !$self->{ '_is_in_policy' }) { $self->_new_line($token,$last) if (!$self->{ '_is_in_operator' } || (!$self->{ '_is_in_drop' } and $self->_next_token eq ';')); if (!$self->{ '_is_in_operator' }) { $self->_set_level($self->_pop_level($token, $last), $token, $last); $self->_back($token, $last); } $self->_add_token( $token ); if (!$self->{ '_is_in_operator' }) { $self->_reset_level($token, $last); } if ($self->{ '_is_in_with' }) { if (defined $self->_next_token && $self->_next_token eq ',') { $self->{ '_is_in_with' } = 1; } else { $self->{ '_is_in_with' } = 0; } } $last = $self->_set_last($token, $last); next; } } elsif (defined $self->_next_token && $self->_next_token eq '(') { $self->{ '_is_in_filter' } = 1 if (uc($token) eq 'FILTER'); $self->{ '_is_in_grouping' } = 1 if ($token =~ /^GROUPING|ROLLUP$/i); } elsif ( uc($token) eq 'PASSING' and defined $self->_next_token && uc($self->_next_token) eq 'BY') { $self->{ '_has_order_by' } = 1; } # Explain need indentation in option list if ( uc($token) eq 'EXPLAIN' ) { $self->{ '_is_in_explain' } = 1; } elsif ( uc($token) eq 'OVERLAPS' ) { $self->{ '_is_in_overlaps' } = 1; } #### # Set the current kind of statement parsed #### if ($token =~ /^(FUNCTION|PROCEDURE|SEQUENCE|INSERT|DELETE|UPDATE|SELECT|RAISE|ALTER|GRANT|REVOKE|COMMENT|DROP|RULE|COMMENT|LOCK)$/i) { my $k_stmt = uc($1); $self->{ '_is_in_explain' } = 0; # Set current statement with taking care to exclude of SELECT ... FOR UPDATE # statement and ON CONFLICT DO UPDATE. if ($k_stmt ne 'UPDATE' or (defined $self->_next_token and $self->_next_token ne ';' and $self->_next_token ne ')' and (not defined $last or $last !~ /^DO|SHARE$/i))) { if ($k_stmt !~ /^UPDATE|DELETE$/i || !$self->{ '_is_in_create' }) { if ($self->{ '_current_sql_stmt' } !~ /^(GRANT|REVOKE)$/i and !$self->{ '_is_in_trigger' } and !$self->{ '_is_in_operator' } and !$self->{ '_is_in_alter' }) { if ($k_stmt ne 'COMMENT' or $self->_next_token =~ /^ON|IS$/i) { $self->{ '_current_sql_stmt' } = $k_stmt; } } } } } #### # Mark that we are in CREATE statement that need newline # after a comma in the parameter, declare or column lists. #### if ($token =~ /^(FUNCTION|PROCEDURE)$/i and $self->{ '_is_in_create' } and !$self->{'_is_in_trigger'}) { $self->{ '_is_in_create_function' } = 1; } elsif ($token =~ /^(FUNCTION|PROCEDURE)$/i and $self->{'_is_in_trigger'}) { $self->{ '_is_in_index' } = 1; } if ($token =~ /^CREATE$/i && $self->_next_token !~ /^(EVENT|UNIQUE|INDEX|EXTENSION|TYPE|PUBLICATION|OPERATOR|RULE|CONVERSION|DOMAIN)$/i) { $self->{ '_is_in_create' } = 1; } elsif ($token =~ /^CREATE$/i && $self->_next_token =~ /^RULE$/i) { $self->{ '_is_in_rule' } = 1; } elsif ($token =~ /^CREATE$/i && $self->_next_token =~ /^EVENT$/i) { $self->{ '_is_in_trigger' } = 1; } elsif ($token =~ /^CREATE$/i && $self->_next_token =~ /^TYPE$/i) { $self->{ '_is_in_type' } = 1; } elsif ($token =~ /^CREATE$/i && $self->_next_token =~ /^PUBLICATION$/i) { $self->{ '_is_in_publication' } = 1; } elsif ($token =~ /^CREATE$/i && $self->_next_token =~ /^CONVERSION$/i) { $self->{ '_is_in_conversion' } = 1; } elsif ($token =~ /^CREATE|DROP$/i && $self->_next_token =~ /^OPERATOR$/i) { $self->{ '_is_in_operator' } = 1; $self->{ '_is_in_drop' } = 1 if ($token =~ /^DROP$/i); } elsif ($token =~ /^ALTER$/i) { $self->{ '_is_in_alter' }++; } elsif ($token =~ /^DROP$/i){ $self->{ '_is_in_drop' } = 1; } elsif ($token =~ /^VIEW$/i and $self->{ '_is_in_create' }) { $self->{ '_is_in_index' } = 1; $self->{ '_is_in_create' } = 0; } elsif ($token =~ /^STATISTICS$/i and $self->{ '_is_in_create' }) { $self->{ '_is_in_statistics' } = 1; $self->{ '_is_in_create' } = 0; } elsif ($token =~ /^AGGREGATE$/i and $self->{ '_is_in_create' }) { $self->{ '_is_in_aggregate' } = 1; $self->{ '_has_order_by' } = 1; } elsif ($token =~ /^EVENT$/i && $self->_next_token =~ /^TRIGGER$/i) { $self->_over($token, $last); $self->{ '_is_in_index' } = 1; } if ($self->{ '_is_in_using' } and defined $self->_next_token and $self->_next_token =~ /^OPERATOR|AS$/i) { $self->{ '_is_in_using' } = 0; } if ($token =~ /^ALTER$/i and $self->{ '_is_in_alter' } > 1) { $self->_new_line($token,$last); $self->_over($token, $last) if ($last ne ','); $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } #### # Mark that we are in a CALL statement to remove any new line #### if ($token =~ /^CALL$/i) { $self->{ '_is_in_call' } = 1; } # Increment operator tag to add newline in alter operator statement if (($self->{ '_is_in_alter' } or uc($last) eq 'AS') and uc($token) eq 'OPERATOR') { $self->_new_line($token,$last) if (uc($last) eq 'AS' and uc($token) eq 'OPERATOR'); $self->{ '_is_in_operator' }++; } #### # Mark that we are in index/constraint creation statement to # avoid inserting a newline after comma and AND/OR keywords. # This also used in SET statement taking care that we are not # in update statement. CREATE statement are not subject to this rule #### if (! $self->{ '_is_in_create' } and $token =~ /^(INDEX|PRIMARY|CONSTRAINT)$/i) { $self->{ '_is_in_index' } = 1; } elsif (! $self->{ '_is_in_create' } and uc($token) eq 'SET') { $self->{ '_is_in_index' } = 1 if ($self->{ '_current_sql_stmt' } ne 'UPDATE'); } elsif ($self->{ '_is_in_create' } and (uc($token) eq 'UNIQUE' or ($token =~ /^PRIMARY|FOREIGN$/i and uc($self->_next_token) eq 'KEY'))) { $self->{ '_is_in_constraint' } = 1; } # Same as above but for ALTER FUNCTION/PROCEDURE/SEQUENCE or when # we are in a CREATE FUNCTION/PROCEDURE statement elsif ($token =~ /^(FUNCTION|PROCEDURE|SEQUENCE)$/i and !$self->{'_is_in_trigger'}) { $self->{ '_is_in_index' } = 1 if (uc($last) eq 'ALTER' and !$self->{ '_is_in_operator' } and !$self->{ '_is_in_alter' }); if ($token =~ /^FUNCTION$/i && ($self->{ '_is_in_create' } || $self->{ '_current_sql_stmt' } eq 'COMMENT')) { $self->{ '_is_in_index' } = 1 if (!$self->{ '_is_in_operator' }); } elsif ($token =~ /^PROCEDURE$/i && $self->{ '_is_in_create' }) { $self->{ '_is_in_index' } = 1; $self->{ '_is_in_procedure' } = 1; } } # Desactivate index like formatting when RETURN(S) keyword is found elsif ($token =~ /^(RETURN|RETURNS)$/i) { $self->{ '_is_in_index' } = 0; if (uc($token) eq 'RETURNS' and uc ($self->_next_token()) eq 'TABLE') { $self->{ '_is_in_returns_table' } = 1; } } elsif ($token =~ /^AS$/i) { if ( !$self->{ '_is_in_index' } and $self->{ '_is_in_from' } and $last eq ')' and uc($token) eq 'AS' and $self->_next_token() eq '(') { $self->{ '_is_in_index' } = 1; } else { $self->{ '_is_in_index' } = 0; } $self->{ '_is_in_block' } = 1 if ($self->{ '_is_in_procedure' }); $self->{ '_is_in_over' } = 0; } if ($token =~ /^BEGIN|DECLARE$/i) { $self->{ '_is_in_create' }-- if ($self->{ '_is_in_create' }); } #### # Mark statements that use string_agg() or group_concat() function # as statement that can have an ORDER BY clause inside the call to # prevent applying order by formatting. #### if ($token =~ /^(string_agg|group_concat|array_agg|percentile_cont)$/i) { $self->{ '_has_order_by' } = 1; } elsif ( $token =~ /^(?:GENERATED)$/i and $self->_next_token =~ /^ALWAYS|BY$/i ) { $self->{ 'no_break' } = 1; } elsif ( $token =~ /^(?:TRUNCATE)$/i ) { $self->{ 'no_break' } = 1; } elsif ( uc($token) eq 'IDENTITY' ) { $self->{ '_has_order_by' } = 0; $self->{ 'no_break' } = 0; } elsif ( $self->{ '_has_order_by' } and uc($token) eq 'ORDER' and $self->_next_token =~ /^BY$/i) { $self->_add_token( $token, $last ); $last = $self->_set_last($token, $last); next; } elsif ($self->{ '_has_order_by' } and uc($token) eq 'BY') { $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } elsif ($token =~ /^OVER$/i) { $self->_add_token( $token ); $self->{ '_is_in_over' } = 1; $self->{ '_has_order_by' } = 1; $last = $self->_set_last($token, $last); next; } # Fix case where we don't knwon if we are outside a SQL function if (defined $last and uc($last) eq 'AS'and defined $self->_next_token and $self->_next_token eq ';' and $self->{ '_is_in_create_function' }) { $self->{ '_is_in_create_function' } = 0; } #### # Set function code delimiter, it can be any string found after # the AS keyword in function or procedure creation code #### # Toogle _fct_code_delimiter to force next token to be stored as the function code delimiter if (uc($token) eq 'AS' and (!$self->{ '_fct_code_delimiter' } || $self->_next_token =~ /CODEPART/) and $self->{ '_current_sql_stmt' } =~ /^(FUNCTION|PROCEDURE)$/i) { if ($self->{ '_is_in_create' }) { $self->_new_line($token,$last); $self->_add_token( $token ); $self->_reset_level($token, $last) if ($self->_next_token !~ /CODEPARTB/); $self->{ '_is_in_create' } = 0; } else { $self->_add_token( $token ); } if ($self->_next_token !~ /CODEPART/ || $self->_next_token =~ /^'/) { $self->{ '_fct_code_delimiter' } = '1'; } $self->{ '_is_in_create' } = 0; $last = $self->_set_last($token, $last); next; } elsif ($token =~ /^INSTEAD$/i and defined $last and uc($last) eq 'DO') { $self->_add_token( $token ); $self->_new_line($token,$last); $last = $self->_set_last($token, $last); next; } elsif ($token =~ /^DO$/i and defined $self->_next_token and $self->_next_token =~ /^INSTEAD|UPDATE|NOTHING$/i) { $self->_new_line($token,$last); $self->_over($token,$last); $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } elsif ($token =~ /^DO$/i and !$self->{ '_fct_code_delimiter' } and $self->_next_token =~ /^\$[^\s]*/) { $self->{ '_fct_code_delimiter' } = '1'; $self->{ '_is_in_create_function' } = 1; $self->_new_line($token,$last) if ($self->{ 'content' } !~ /\n$/s); $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } # Store function code delimiter if ($self->{ '_fct_code_delimiter' } eq '1') { if ($self->_next_token =~ /CODEPART/) { $self->{ '_fct_code_delimiter' } = '0'; } elsif ($token =~ /^'.*'$/) { $self->{ '_fct_code_delimiter' } = "'"; } else { $self->{ '_fct_code_delimiter' } = $token; } $self->_add_token( $token ); $last = $self->_set_last($token, $last); $self->_new_line($token,$last); $self->_over($token,$last) if (defined $self->_next_token && $self->_next_token !~ /^(DECLARE|BEGIN)$/i); if ($self->{ '_fct_code_delimiter' } eq "'") { $self->{ '_is_in_block' } = -1; $self->{ '_is_in_exception' } = 0; $self->_reset_level($token, $last) if ($self->_next_token eq ';'); $self->{ '_fct_code_delimiter' } = ''; $self->{ '_current_sql_stmt' } = ''; $self->{ '_is_in_procedure' } = 0; $self->{ '_is_in_function' } = 0; $self->{ '_is_in_create_function' } = 0; } next; } # Desactivate the block mode when code delimiter is found for the second time if ($self->{ '_fct_code_delimiter' } && $token eq $self->{ '_fct_code_delimiter' }) { $self->{ '_is_in_block' } = -1; $self->{ '_is_in_exception' } = 0; $self->_reset_level($token, $last); $self->{ '_fct_code_delimiter' } = ''; $self->{ '_current_sql_stmt' } = ''; $self->_new_line($token,$last); $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } #### # Mark when we are parsing a DECLARE or a BLOCK section. When # entering a BLOCK section store the current indentation level #### if (uc($token) eq 'DECLARE' and $self->{ '_is_in_create_function' }) { $self->{ '_is_in_block' } = -1; $self->{ '_is_in_exception' } = 0; $self->{ '_is_in_declare' } = 1; $self->_reset_level($token, $last); $self->_new_line($token,$last); $self->_add_token( $token ); $self->_new_line($token,$last); $self->_over($token,$last); $last = $self->_set_last($token, $last); $self->{ '_is_in_create_function' } = 0; next; } elsif ( uc($token) eq 'BEGIN' ) { $self->{ '_is_in_declare' } = 0; if ($self->{ '_is_in_block' } == -1) { $self->_reset_level($token, $last); } $self->_new_line($token,$last); $self->_add_token( $token ); if (defined $self->_next_token && $self->_next_token !~ /^(WORK|TRANSACTION|ISOLATION|;)$/i) { $self->_new_line($token,$last); $self->_over($token,$last); $self->{ '_is_in_block' }++; # Store current indent position to print END at the right level $self->_push_level($self->{ '_level' }, $token, $last); } $self->{ '_is_in_work' }++ if (!$self->{ 'no_grouping' } and defined $self->_next_token && $self->_next_token =~ /^(WORK|TRANSACTION|ISOLATION|;)$/i); #$self->{ '_is_in_work' } = 1 if (!$self->{ 'no_grouping' } and defined $self->_next_token && $self->_next_token =~ /^(WORK|TRANSACTION|ISOLATION|;)$/i); $last = $self->_set_last($token, $last); next; } elsif ( $token =~ /^(COMMIT|ROLLBACK)$/i and (not defined $last or uc($last) ne 'ON') and !$self->{ '_is_in_procedure' } ) { $self->{ '_is_in_work' } = 0; $self->{ '_is_in_declare' } = 0; $self->_new_line($token,$last); $self->_set_level($self->_pop_level($token, $last), $token, $last); $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } elsif ( $token =~ /^(COMMIT|ROLLBACK)$/i and defined $self->_next_token and $self->_next_token eq ';' and $self->{ '_is_in_procedure' } ) { $self->_new_line($token,$last); $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } elsif ( $token =~ /^FETCH$/i and defined $last and $last eq ';') { $self->_new_line($token,$last); $self->_back($token, $last) if ($self->{ '_is_in_block' } == -1); $self->_add_token( $token ); $last = $self->_set_last($token, $last); $self->{ '_is_in_fetch' } = 1; next; } #### # Special case where we want to add a newline into ) AS ( #### if (uc($token) eq 'AS' and $last eq ')' and $self->_next_token eq '(') { $self->_new_line($token,$last); } # and before RETURNS with increasing indent level elsif (uc($token) eq 'RETURNS') { $self->_new_line($token,$last); $self->_over($token,$last); } # and before WINDOW elsif (uc($token) eq 'WINDOW') { $self->_new_line($token,$last); $self->_set_level($self->_pop_level($token, $last), $token, $last); $self->_add_token( $token ); $last = $self->_set_last($token, $last); $self->{ '_has_order_by' } = 1; next; } # Treated DISTINCT as a modifier of the whole select clause, not only the first column only if (uc($token) eq 'ON' && defined $last && uc($last) eq 'DISTINCT') { $self->{ '_is_in_distinct' } = 1; $self->_over($token,$last); } elsif (uc($token) eq 'DISTINCT' && defined $last && uc($last) eq 'SELECT' && defined $self->_next_token && $self->_next_token !~ /^ON$/i) { $self->_add_token( $token ); $self->_new_line($token,$last) if (!$self->{'wrap_after'}); $self->_over($token,$last); $last = $self->_set_last($token, $last); next; } if ( $rule ) { $self->_process_rule( $rule, $token ); } elsif ($token =~ /^(LANGUAGE|SECURITY|COST)$/i && !$self->{ '_is_in_alter' } && !$self->{ '_is_in_drop' } ) { $self->_new_line($token,$last) if (uc($token) ne 'SECURITY' or (defined $last and uc($last) ne 'LEVEL')); $self->_add_token( $token ); } elsif ($token =~ /^PARTITION$/i && !$self->{ '_is_in_over' } && defined $last && $last ne '(') { $self->{ '_is_in_partition' } = 1; if ($self->{ '_is_in_create' } && defined $last and $last eq ')') { $self->_new_line($token,$last); $self->_set_level($self->_pop_level($token, $last), $token, $last); $self->_add_token( $token ); } else { $self->_add_token( $token ); } } elsif ($token =~ /^POLICY$/i) { $self->{ '_is_in_policy' } = 1; $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } elsif ($token =~ /^TRIGGER$/i and defined $last and $last =~ /CREATE|CONSTRAINT/i) { $self->{ '_is_in_trigger' } = 1; $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } elsif ($token =~ /^(BEFORE|AFTER|INSTEAD)$/i and $self->{ '_is_in_trigger' }) { $self->_new_line($token,$last); $self->_over($token,$last); $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } elsif ($token =~ /^EXECUTE$/i and ($self->{ '_is_in_trigger' } or (defined $last and uc($last) eq 'AS'))) { $self->_new_line($token,$last); $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } elsif ( $token eq '(' ) { if ($self->{ '_is_in_aggregate' } && defined $self->_next_token and ($self->_is_keyword($self->_next_token) or $self->_is_sql_keyword($self->_next_token)) and uc($self->_next_token) ne 'VARIADIC') { $self->{ '_is_in_aggregate' } = 0; $self->{ '_has_order_by' } = 0; } $self->{ '_is_in_create' }++ if ($self->{ '_is_in_create' }); $self->{ '_is_in_constraint' }++ if ($self->{ '_is_in_constraint' }); $self->_add_token( $token, $last ); if (defined $self->_next_token and $self->_next_token eq ')' and !$self->{ '_is_in_create' }) { $last = $self->_set_last($token, $last); next; } if ( !$self->{ '_is_in_index' } && !$self->{ '_is_in_publication' } && !$self->{ '_is_in_distinct' } && !$self->{ '_is_in_filter' } && !$self->{ '_is_in_grouping' } && !$self->{ '_is_in_partition' } && !$self->{ '_is_in_over' } && !$self->{ '_is_in_trigger' } && !$self->{ '_is_in_policy' } && !$self->{ '_is_in_aggregate' } && !$self->{ 'no_break' } ) { if (uc($last) eq 'AS' || $self->{ '_is_in_create' } == 2 || uc($self->_next_token) eq 'CASE') { $self->_new_line($token,$last) if ((!$self->{'_is_in_function'} or $self->_next_token =~ /^CASE$/i) and $self->_next_token ne ')' and $self->_next_token !~ /^(PARTITION|ORDER)$/i); } if ($self->{ '_is_in_with' } == 1 or $self->{ '_is_in_explain' }) { $self->_over($token,$last); $self->_new_line($token,$last) if (!$self->{ 'wrap_after' }); $last = $self->_set_last($token, $last) if (!$self->{ '_is_in_explain' } || $self->{ 'wrap_after' }); next; } if (!$self->{ '_is_in_if' } and !$self->{ '_is_in_alter' } and (!$self->{ '_is_in_function' } or $last ne '(')) { $self->_over($token,$last) if ($self->{ '_is_in_operator' } <= 2 && $self->{ '_is_in_create' } <= 2); if (!$self->{ '_is_in_function' } and !$self->_is_type($self->_next_token)) { if ($self->{ '_is_in_operator' } == 1) { $self->_new_line($token,$last); $self->{ '_is_in_operator' }++; } elsif ($self->{ '_is_in_type' }) { $self->_new_line($token,$last); } } $last = $self->_set_last($token, $last); } if ($self->{ '_is_in_type' } == 1) { $last = $self->_set_last($token, $last); next; } } if ($self->{ 'format_type' } && $self->{ '_current_sql_stmt' } =~ /FUNCTION|PROCEDURE/i && $self->{ '_is_in_create' } == 2 && (not defined $self->_next_token or $self->_next_token ne ')') ) { $self->_over($token,$last) if ($self->{ '_is_in_block' } < 0); $self->_new_line($token,$last); next; } } elsif ( $token eq ')' ) { if ($self->{ '_is_in_constraint' } and defined $self->_next_token and ($self->_next_token eq ',' or $self->_next_token eq ')')) { $self->{ '_is_in_constraint' } = 0; } elsif ($self->{ '_is_in_constraint' }) { $self->{ '_is_in_constraint' }--; } if ($self->{ '_is_in_with' } == 1 || $self->{ '_is_in_explain' }) { $self->_back($token, $last); $self->_new_line($token,$last) if (!$self->{ 'wrap_after' } && !$self->{ '_is_in_overlaps' }); $self->_add_token( $token ); $last = $self->_set_last($token, $last) if ($token ne ')' or uc($self->_next_token) ne 'AS'); $self->{ '_is_in_explain' } = 0; next; } if ( ($self->{ 'format_type' } && $self->{ '_current_sql_stmt' } =~ /FUNCTION|PROCEDURE/i && $self->{ '_is_in_create' } == 2) || (defined $self->_next_token and uc($self->_next_token) eq 'INHERITS') ) { $self->_back($token, $last) if ($self->{ '_is_in_block' } < 0); $self->_new_line($token,$last) if (defined $last && $last ne '('); } if ($self->{ '_is_in_index' } || $self->{ '_is_in_alter' } || $self->{ '_is_in_partition' } || $self->{ '_is_in_policy' } || (defined $self->_next_token and $self->_next_token =~ /^OVER$/i) ) { $self->_add_token( '' ); $self->_add_token( $token ); $self->{ '_is_in_over' } = 0 if (!$self->{ '_parenthesis_level' }); $last = $self->_set_last($token, $last); $self->{ '_is_in_create' }-- if ($self->{ '_is_in_create' }); next; } if (defined $self->_next_token && $self->_next_token !~ /FILTER/i) { my $add_nl = 0; $add_nl = 1 if ($self->{ '_is_in_create' } > 1 and defined $last and $last ne '(' and (not defined $self->_next_token or $self->_next_token =~ /^PARTITION|;$/i or ($self->_next_token =~ /^ON$/i and !$self->{ '_parenthesis_level' })) ); $add_nl = 1 if ($self->{ '_is_in_type' } == 1 and $self->_next_token !~ /^AS$/i and (not defined $self->_next_token or $self->_next_token eq ';') ); $add_nl = 1 if ($self->{ '_current_sql_stmt' } ne 'INSERT' and !$self->{ '_is_in_function' } and (defined $self->_next_token and $self->_next_token =~ /^(SELECT|WITH)$/i) and ($self->{ '_is_in_create' } or $last ne ')' and $last ne ']') ); $self->_new_line($token,$last) if ($add_nl); if (!$self->{ '_is_in_grouping' } && !$self->{ '_is_in_trigger' } && !$self->{ 'no_break' } && $self->{ '_is_in_create' } <= 2 ) { $self->_back($token, $last); } $self->{ '_is_in_create' }-- if ($self->{ '_is_in_create' }); if ($self->{ '_is_in_type' }) { $self->_reset_level($token, $last) if ($self->{ '_is_in_block' } == -1 && !$self->{ '_parenthesis_level' }); $self->{ '_is_in_type' }--; } } if (!$self->{ '_parenthesis_level' }) { $self->{ '_is_in_filter' } = 0; $self->{ '_is_in_within' } = 0; $self->{ '_is_in_grouping' } = 0; $self->{ '_is_in_over' } = 0; $self->{ '_has_order_by' } = 0; $self->{ '_is_in_policy' } = 0; $self->{ '_is_in_where' } = 0; $self->{ '_is_in_aggregate' } = 0; } $self->_add_token( $token ); # Do not go further if this is the last token if (not defined $self->_next_token) { $last = $self->_set_last($token, $last); next; } # When closing CTE statement go back again if ($self->_next_token =~ /^SELECT|INSERT|UPDATE|DELETE$/i && !$self->{ '_is_in_policy' }) { $self->_back($token, $last) if ($self->{ '_current_sql_stmt' } ne 'INSERT'); } if ($self->{ '_is_in_create' } <= 1) { my $next_tok = quotemeta($self->_next_token); $self->_new_line($token,$last) if (defined $self->_next_token and $self->_next_token !~ /^AS|IS|THEN|INTO|BETWEEN|ON|FILTER|WITHIN|DESC|ASC$/i and ($self->_next_token !~ /^AND|OR$/i or !$self->{ '_is_in_if' }) and $self->_next_token ne ')' and $self->_next_token !~ /^:/ and $self->_next_token ne ';' and $self->_next_token ne ',' and $self->_next_token ne '||' and ($self->_is_keyword($self->_next_token) or $self->_is_function($self->_next_token)) and $self->{ '_current_sql_stmt' } !~ /^(GRANT|REVOKE)$/ and !exists $self->{ 'dict' }->{ 'symbols' }{ $next_tok } and !$self->{ '_is_in_over' } ); } } elsif ( $token eq ',' ) { my $add_newline = 0; $self->{ '_is_in_constraint' } = 0 if ($self->{ '_is_in_constraint' } == 1); $self->{ '_col_count' }++ if (!$self->{ '_is_in_function' }); if (($self->{ '_is_in_over' } or $self->{ '_has_order_by' }) and !$self->{ '_parenthesis_level' } and !$self->{ '_parenthesis_function_level' }) { $self->{ '_is_in_over' } = 0; $self->{ '_has_order_by' } = 0; $self->_back($token, $last); } $add_newline = 1 if ( !$self->{ 'no_break' } && !$self->{ '_is_in_function' } && !$self->{ '_is_in_distinct' } && !$self->{ '_is_in_array' } && ($self->{ 'comma_break' } || $self->{ '_current_sql_stmt' } ne 'INSERT') && ($self->{ '_current_sql_stmt' } ne 'RAISE') && ($self->{ '_current_sql_stmt' } !~ /^(FUNCTION|PROCEDURE)$/ || $self->{ '_fct_code_delimiter' } ne '') && !$self->{ '_is_in_where' } && !$self->{ '_is_in_drop' } && !$self->{ '_is_in_index' } && !$self->{ '_is_in_aggregate' } && !$self->{ '_is_in_alter' } && !$self->{ '_is_in_publication' } && !$self->{ '_is_in_call' } && !$self->{ '_is_in_policy' } && !$self->{ '_is_in_grouping' } && !$self->{ '_is_in_partition' } && ($self->{ '_is_in_constraint' } <= 1) && ($self->{ '_is_in_create' } <= 2) && $self->{ '_is_in_operator' } != 1 && !$self->{ '_has_order_by' } && $self->{ '_current_sql_stmt' } !~ /^(GRANT|REVOKE)$/ && $self->_next_token !~ /^('$|\s*\-\-)/i && !$self->{ '_parenthesis_function_level' } && (!$self->{ '_col_count' } or $self->{ '_col_count' } > ($self->{ 'wrap_after' } - 1)) || ($self->{ '_is_in_with' } and !$self->{ 'wrap_after' }) ); $self->{ '_col_count' } = 0 if ($self->{ '_col_count' } > ($self->{ 'wrap_after' } - 1)); $add_newline = 0 if ($self->{ '_is_in_using' } and $self->{ '_parenthesis_level' }); $add_newline = 0 if ($self->{ 'no_break' }); if ($self->{ '_is_in_with' } >= 1 && !$self->{ '_parenthesis_level' }) { $add_newline = 1 if (!$self->{ 'wrap_after' }); } if ($self->{ 'format_type' } && $self->{ '_current_sql_stmt' } =~ /FUNCTION|PROCEDURE/i && $self->{ '_is_in_create' } == 2) { $add_newline = 1; } if ($self->{ '_is_in_alter' } && $self->{ '_is_in_operator' } >= 2) { $add_newline = 1 if (defined $self->_next_token and $self->_next_token =~ /^OPERATOR|FUNCTION$/i); } $add_newline = 1 if ($self->{ '_is_in_returns_table' }); $self->_new_line($token,$last) if ($add_newline && $self->{ 'comma' } eq 'start'); $self->_add_token( $token ); $add_newline = 0 if ($self->{ '_is_in_value' } and $self->{ '_parenthesis_level_value' }); $add_newline = 0 if ($self->{ '_is_in_function' } or $self->{ '_is_in_statistics' }); $add_newline = 0 if (defined $self->_next_token and $self->_is_comment($self->_next_token)); $self->_new_line($token,$last) if ($add_newline and $self->{ 'comma' } eq 'end' and $self->{ '_current_sql_stmt' } ne 'INSERT'); } elsif ( $token eq ';' or $token =~ /^\\(?:g|crosstabview|watch)/ ) { # statement separator or executing psql meta command (prefix 'g' includes all its variants) $self->_add_token($token); next if ($token eq ';' and $self->{ '_is_in_case' }); if ($self->{ '_is_in_rule' }) { $self->_back($token, $last); } elsif ($self->{ '_is_in_create' } && $self->{ '_is_in_block' } > -1) { $self->_pop_level($token, $last); } # Initialize most of statement related variables $self->{ 'no_break' } = 0; $self->{ '_is_in_where' } = 0; $self->{ '_is_in_from' } = 0; $self->{ '_is_in_join' } = 0; $self->{ '_is_in_create' } = 0; $self->{ '_is_in_alter' } = 0; $self->{ '_is_in_rule' } = 0; $self->{ '_is_in_publication' } = 0; $self->{ '_is_in_call' } = 0; $self->{ '_is_in_type' } = 0; $self->{ '_is_in_function' } = 0; $self->{ '_is_in_prodedure' } = 0; $self->{ '_is_in_index' } = 0; $self->{ '_is_in_statistics' } = 0; $self->{ '_is_in_if' } = 0; $self->{ '_is_in_with' } = 0; $self->{ '_is_in_overlaps' } = 0; $self->{ '_has_order_by' } = 0; $self->{ '_has_over_in_join' } = 0; $self->{ '_parenthesis_level' } = 0; $self->{ '_parenthesis_function_level' } = 0; $self->{ '_is_in_constraint' } = 0; $self->{ '_is_in_distinct' } = 0; $self->{ '_is_in_array' } = 0; $self->{ '_is_in_filter' } = 0; $self->{ '_parenthesis_filter_level' } = 0; $self->{ '_is_in_partition' } = 0; $self->{ '_is_in_over' } = 0; $self->{ '_is_in_policy' } = 0; $self->{ '_is_in_trigger' } = 0; $self->{ '_is_in_using' } = 0; $self->{ '_and_level' } = 0; $self->{ '_col_count' } = 0; $self->{ '_is_in_drop' } = 0; $self->{ '_is_in_conversion' } = 0; $self->{ '_is_in_operator' } = 0; $self->{ '_is_in_explain' } = 0; $self->{ '_is_in_sub_query' } = 0; $self->{ '_is_in_fetch' } = 0; $self->{ '_is_in_aggregate' } = 0; $self->{ '_is_in_value' } = 0; $self->{ '_parenthesis_level_value' } = 0; $self->{ '_parenthesis_with_level' } = 0; $self->{ '_is_in_returns_table' } = 0; $self->{ '_has_limit' } = 0; $self->{ '_not_a_type' } = 0; if ( $self->{ '_insert_values' } ) { if ($self->{ '_is_in_block' } == -1 and !$self->{ '_is_in_declare' } and !$self->{ '_fct_code_delimiter' }) { $self->_reset_level($token, $last); } elsif ($self->{ '_is_in_block' } == -1 and $self->{ '_current_sql_stmt' } eq 'INSERT' and !$self->{ '_is_in_create' } and !$self->{ '_is_in_create_function' }) { $self->_back($token, $last); $self->_pop_level($token, $last); } else { $self->_set_level($self->_pop_level($token, $last), $token, $last); } $self->{ '_insert_values' } = 0; } $self->{ '_current_sql_stmt' } = ''; $self->{ 'break' } = "\n" unless ( $self->{ 'spaces' } != 0 ); $self->_new_line($token,$last) if (uc($last) ne 'VALUES'); # Add an additional newline after ; when we are not in a function if ($self->{ '_is_in_block' } == -1 and !$self->{ '_is_in_work' } and !$self->{ '_is_in_declare' } and uc($last) ne 'VALUES') { $self->{ '_new_line' } = 0; $self->_new_line($token,$last); } # End of statement; remove all indentation when we are not in a BEGIN/END block if (!$self->{ '_is_in_declare' } && $self->{ '_is_in_block' } == -1) { $self->_reset_level($token, $last); } elsif (not defined $self->_next_token or $self->_next_token !~ /^INSERT$/) { if ($#{ $self->{ '_level_stack' } } == -1) { $self->_set_level(($self->{ '_is_in_declare' }) ? 1 : ($self->{ '_is_in_block' }+1), $token, $last); } else { $self->_set_level($self->{ '_level_stack' }[-1], $token, $last); } } $last = $self->_set_last($token, $last); } elsif ($token =~ /^FOR$/i && (!$self->{ '_is_in_policy' } || $self->{ 'format_type' })) { if ($self->_next_token =~ /^(UPDATE|KEY|NO|VALUES)$/i) { $self->_back($token, $last) if (!$self->{ '_has_limit' } and ($#{$self->{ '_level_stack' }} == -1 or $self->{ '_level' } > $self->{ '_level_stack' }[-1])); $self->_new_line($token,$last); $self->{ '_has_limit' } = 0; } elsif ($self->_next_token =~ /^EACH$/ and $self->{ '_is_in_trigger' }) { $self->_new_line($token,$last); } if (!$self->{ 'format_type' }) { $self->_add_token( $token ); # cover FOR in cursor $self->_over($token,$last) if (uc($self->_next_token) eq 'SELECT' && !$self->{ '_is_in_policy' } && !$self->{ '_is_in_publication' }); $last = $self->_set_last($token, $last); next; } if ($self->_next_token =~ /^SELECT|UPDATE|DELETE|INSERT|TABLE|ALL$/i) { # cover FOR in cursor and in policy or publication statements $self->_over($token,$last) if (uc($self->_next_token) eq 'SELECT' || $self->{ '_is_in_policy' } || $self->{ '_is_in_publication' }); } if ($self->{ 'format_type' } && $self->{ '_is_in_policy' } || $self->{ '_is_in_publication' }) { $self->_new_line($token,$last); } $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } elsif ( $token =~ /^(?:FROM|WHERE|SET|RETURNING|HAVING|VALUES)$/i ) { if (uc($token) eq 'FROM' and $self->{ '_has_order_by' } and !$self->{ '_parenthesis_level' }) { $self->_back($token, $last) if ($self->{ '_has_order_by' }); } $self->{ 'no_break' } = 0; $self->{ '_col_count' } = 0; # special cases for create partition statement if ($token =~ /^VALUES$/i && defined $last and $last =~ /^FOR|IN$/i) { $self->_add_token( $token ); $self->{ 'no_break' } = 1; $last = $self->_set_last($token, $last); next; } elsif ($token =~ /^FROM$/i && defined $last and uc($last) eq 'VALUES') { $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } # Case of DISTINCT FROM clause if ($token =~ /^FROM$/i) { if (uc($last) eq 'DISTINCT' || $self->{ '_is_in_fetch' } || $self->{ '_is_in_alter' } || $self->{ '_is_in_conversion' }) { $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } } if ($token =~ /^FROM$/i) { $self->{ '_is_in_from' }++ if (!$self->{ '_is_in_function' } && !$self->{ '_is_in_partition' }); } if ($token =~ /^WHERE$/i && !$self->{ '_is_in_filter' }) { $self->_back($token, $last) if ($self->{ '_has_over_in_join' }); $self->{ '_is_in_where' }++; $self->{ '_is_in_from' }-- if ($self->{ '_is_in_from' }); $self->{ '_is_in_join' } = 0; $self->{ '_has_over_in_join' } = 0; } elsif (!$self->{ '_is_in_function' }) { $self->{ '_is_in_where' }-- if ($self->{ '_is_in_where' }); } if ($token =~ /^SET$/i and $self->{ '_is_in_create' }) { # Add newline before SET statement in function header $self->_new_line($token,$last) if (not defined $last or $last !~ /DELETE|UPDATE/i); } elsif ($token =~ /^WHERE$/i and $self->{ '_current_sql_stmt' } eq 'DELETE') { $self->_new_line($token,$last); $self->_add_token( $token ); $self->_over($token,$last); $last = $self->_set_last($token, $last); $self->{ '_is_in_join' } = 0; $last = $self->_set_last($token, $last); next; } elsif ($token =~ /^SET$/i and defined $last and uc($last) eq 'UPDATE' and !$self->_is_keyword($self->_next_token())) { $self->{ '_is_in_index' } = 0; $self->{ '_is_in_from' } = 0; $self->_add_token( $token ); $self->_new_line($token,$last); $self->_over($token,$last); $last = $self->_set_last($token, $last); next; } elsif ($token !~ /^FROM$/i or (!$self->{ '_is_in_function' } and !$self->{ '_is_in_statistics' } and $self->{ '_current_sql_stmt' } !~ /DELETE|REVOKE/)) { if (!$self->{ '_is_in_filter' } and ($token !~ /^SET$/i or !$self->{ '_is_in_index' })) { $self->_back($token, $last) if (uc($token) ne 'VALUES' or $self->{ '_current_sql_stmt' } ne 'INSERT'); $self->_new_line($token,$last) if (!$self->{ '_is_in_rule' } and ($last !~ /^DEFAULT$/i or $self->_next_token() ne ';')); } } else { $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } if ($token =~ /^VALUES$/i and !$self->{ '_is_in_rule' } and ($self->{ '_current_sql_stmt' } eq 'INSERT' or $last eq '(')) { $self->_over($token,$last); if ($self->{ '_current_sql_stmt' } eq 'INSERT' or $last eq '(') { $self->{ '_insert_values' } = 1; $self->_push_level($self->{ '_level' }, $token, $last); } } if ($token =~ /^VALUES$/i and $last eq '(') { $self->{ '_is_in_value' } = 1; } if (uc($token) eq 'WHERE') { $self->_add_token( $token, $last ); $self->{ '_is_in_value' } = 0; $self->{ '_parenthesis_level_value' } = 0; } else { $self->_add_token( $token ); } if ($token =~ /^VALUES$/i and $last eq '(') { $self->_over($token,$last); } elsif ( $token =~ /^SET$/i && $self->{ '_current_sql_stmt' } eq 'UPDATE' ) { $self->_new_line($token,$last) if (!$self->{ 'wrap_after' }); $self->_over($token,$last); } elsif ( !$self->{ '_is_in_over' } and !$self->{ '_is_in_filter' } and ($token !~ /^SET$/i or $self->{ '_current_sql_stmt' } eq 'UPDATE') ) { if (defined $self->_next_token and $self->_next_token !~ /\(|;/ and ($self->_next_token !~ /^(UPDATE|KEY|NO)$/i || uc($token) eq 'WHERE')) { $self->_new_line($token,$last) if (!$self->{ 'wrap_after' }); $self->_over($token,$last); } } } # Add newline before INSERT and DELETE if last token was AS (prepared statement) elsif (defined $last and $token =~ /^INSERT|DELETE|UPDATE$/i and uc($last) eq 'AS') { $self->_new_line($token,$last); $self->_add_token( $token ); } elsif ( $self->{ '_current_sql_stmt' } !~ /^(GRANT|REVOKE)$/ and $token =~ /^(?:SELECT|PERFORM|UPDATE|DELETE)$/i and (!$self->{ '_is_in_policy' } || $self->{ 'format_type' }) ) { $self->{ 'no_break' } = 0; if ($token =~ /^SELECT|UPDATE|DELETE|INSERT$/i && $self->{ '_is_in_policy' } && $self->{ 'format_type' }) { $self->_over($token,$last); } # case of ON DELETE/UPDATE clause in create table statements if ($token =~ /^UPDATE|DELETE$/i && $self->{ '_is_in_create' }) { $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } if ($token =~ /^UPDATE$/i and $last =~ /^(FOR|KEY|DO)$/i) { $self->_add_token( $token ); } elsif (!$self->{ '_is_in_policy' } && $token !~ /^DELETE|UPDATE$/i && (!defined $self->_next_token || $self->_next_token !~ /^DISTINCT$/i)) { $self->_new_line($token,$last); $self->_add_token( $token ); $self->_new_line($token,$last) if (!$self->{ 'wrap_after' }); $self->_over($token,$last); } else { $self->_add_token( $token ); } } elsif ( $self->{ '_current_sql_stmt' } !~ /^(GRANT|REVOKE)$/ and uc($token) eq 'INSERT' and $self->{ '_is_in_policy' } && $self->{ 'format_type' }) { $self->_add_token( $token ); $self->_new_line($token,$last); $self->_over($token,$last); } elsif ( $token =~ /^(?:WITHIN)$/i ) { $self->{ '_is_in_within' } = 1; $self->{ '_has_order_by' } = 1; $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } elsif ( $token =~ /^(?:GROUP|ORDER|LIMIT|EXCEPTION)$/i or (uc($token) eq 'ON' and uc($self->_next_token()) eq 'CONFLICT')) { $self->{ '_is_in_value' } = 0; $self->{ '_parenthesis_level_value' } = 0; if (uc($token) eq 'GROUP' and !defined $last or uc($last) eq 'EXCLUDE') { $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } if (($self->{ '_is_in_within' } && uc($token) eq 'GROUP') || ($self->{ '_is_in_over' } && uc($token) eq 'ORDER')) { $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } if ($self->{ '_has_over_in_join' } and uc($token) eq 'GROUP') { $self->_back($token, $last); $self->{ '_has_over_in_join' } = 0; } $self->{ '_is_in_join' } = 0; $self->{ '_has_limit' } = 1 if (uc($token) eq 'LIMIT'); if ($token !~ /^EXCEPTION/i) { $self->_back($token, $last); } else { $self->_set_level($self->_pop_level($token, $last), $token, $last); } if (uc($token) ne 'EXCEPTION' or not defined $last or uc($last) ne 'RAISE') { # Excluding CREATE/DROP GROUP $self->_new_line($token,$last) if (not defined $last or $last !~ /^(CREATE|DROP)$/); } # Store current indent position to print END at the right level if (uc($last) ne 'RAISE' and $token =~ /^EXCEPTION$/i) { $self->{ '_is_in_exception' } = 1; if ($self->{ '_level' } != 1) { $self->_push_level($self->{ '_level' }, $token, $last); } else { $self->_reset_level($token, $last); $last = $self->_set_last($token, $last); } } elsif (uc($last) eq 'RAISE' and $token =~ /^EXCEPTION$/i) { $self->_push_level($self->{ '_level' }, $token, $last); $self->_over($token,$last); } $self->{ '_is_in_where' }-- if ($self->{ '_is_in_where' }); $self->_add_token( $token ); if ($token =~ /^EXCEPTION$/i && $self->{ '_level' } == 0) { $self->_over($token,$last); } } elsif ( $token =~ /^(?:BY)$/i and $last !~ /^(?:INCREMENT|OWNED|PARTITION)$/i) { $self->_add_token( $token ); $self->{ '_col_count' } = 0 if (defined $last && $last =~ /^(?:GROUP|ORDER)/i); if (!$self->{ '_has_order_by' } and !$self->{ '_is_in_over' }) { $self->_new_line($token,$last) if (!$self->{ 'wrap_after' }); $self->_over($token,$last); } } elsif ( $token =~ /^(?:CASE)$/i and uc($last) ne 'END') { $self->_add_token( $token ); # Store current indent position to print END at the right level $self->_push_level($self->{ '_level' }, $token, $last); # Mark next WHEN statement as first element of a case # to force indentation only after this element $self->{ '_first_when_in_case' } = 1; $self->{ '_is_in_case' }++; } elsif ( $token =~ /^(?:WHEN)$/i) { if (!$self->{ '_first_when_in_case' } and !$self->{'_is_in_trigger'} and defined $last and uc($last) ne 'CASE' # handle case where there is a comment between EXCEPTION and WHEN keywords and !$self->{ '_is_in_exception' } ) { $self->_set_level($self->{ '_level_stack' }[-1], $token, $last); $self->{ '_is_in_exception' } = 0; } $self->_new_line($token,$last) if (not defined $last or $last !~ /^CASE|,|\($/i ); $self->_add_token( $token ); if (!$self->{ '_is_in_case' } && !$self->{ '_is_in_trigger' }) { $self->_over($token,$last); } $self->{ '_first_when_in_case' } = 0; } elsif ( $token =~ /^(?:IF|LOOP)$/i && $self->{ '_current_sql_stmt' } ne 'GRANT') { $self->_add_token( $token ); $self->{ 'no_break' } = 0; if (defined $self->_next_token and $self->_next_token !~ /^(EXISTS|;)$/i) { if (uc($self->_next_token) ne 'NOT' || uc($self->{ '_tokens' }->[ 1 ]) ne 'EXISTS') { $self->_new_line($token,$last) if ($token =~ /^LOOP$/i); $self->_over($token,$last); $self->_push_level($self->{ '_level' }, $token, $last); if ($token =~ /^IF$/i) { $self->{ '_is_in_if' } = 1; } } } } elsif ($token =~ /^THEN$/i) { $self->_add_token( $token ); $self->_new_line($token,$last); $self->_set_level($self->{ '_level_stack' }[-1], $token, $last) if ($self->{ '_is_in_if' }); if ($self->{ '_is_in_case' } && defined $self->_next_token() and $self->_next_token() !~ /^(\(|RAISE)$/i) { $self->_set_level($self->{ '_level_stack' }[-1], $token, $last); $self->_over($token,$last); } $self->{ '_is_in_if' } = 0; } elsif ( $token =~ /^(?:ELSE|ELSIF)$/i ) { $self->_back($token, $last); $self->_new_line($token,$last); $self->_add_token( $token ); $self->_new_line($token,$last) if ($token !~ /^ELSIF$/i); $self->_over($token,$last); } elsif ( $token =~ /^(?:END)$/i ) { $self->{ '_first_when_in_case' } = 0; if ($self->{ '_is_in_case' }) { $self->{ '_is_in_case' }--; $self->_back($token, $last); $self->_set_level($self->_pop_level($token, $last), $token, $last); } # When we are not in a function code block (0 is the main begin/end block of a function) elsif ($self->{ '_is_in_block' } == -1 && $last ne ',') { # END is closing a create function statement so reset position to begining if ($self->_next_token !~ /^(IF|LOOP|CASE|INTO|FROM|END|ELSE|AND|OR|WHEN|AS|,)$/i) { $self->_reset_level($token, $last); } else { # otherwise back to last level stored at CASE keyword $self->_set_level($self->_pop_level($token, $last), $token, $last); } } # We reach the last end of the code elsif ($self->{ '_is_in_block' } > -1 and $self->_next_token =~/^(;|\$.*\$)$/ and !$self->{ '_is_in_exception' }) { if ($self->{ '_is_in_block' } == 0) { $self->_reset_level($token, $last); } else { $self->_set_level($self->_pop_level($token, $last) - 1, $token, $last); $self->{ '_is_in_block' }--; } } # We are in code block elsif ($last ne ',') { # decrease the block level if this is a END closing a BEGIN block if ($self->_next_token !~ /^(IF|LOOP|CASE|INTO|FROM|END|ELSE|AND|OR|WHEN|AS|,)$/i) { $self->{ '_is_in_block' }--; } # Go back to level stored with IF/LOOP/BEGIN/EXCEPTION block if ($self->{ '_is_in_block' } > -1) { $self->_set_level($self->_pop_level($token, $last), $token, $last); } else { $self->_reset_level($token, $last); } $self->_back($token, $last) if ($self->_next_token =~ /^(IF|LOOP|CASE|INTO|FROM|END|ELSE|AND|OR|WHEN|AS|,)$/i); } $self->_new_line($token,$last); $self->_add_token( $token ); } elsif ( $token =~ /^(?:END::[^\s]+)$/i and $self->{ '_is_in_case' } ) { $self->{ '_first_when_in_case' } = 0; if ($self->{ '_is_in_case' }) { $self->{ '_is_in_case' }--; $self->_back($token, $last); $self->_set_level($self->_pop_level($token, $last), $token, $last); } $self->_new_line($token,$last); $self->_add_token( $token ); } elsif ( $token =~ /^(?:UNION|INTERSECT|EXCEPT)$/i ) { $self->{ 'no_break' } = 0; if ($self->{ '_is_in_join' }) { $self->_back($token, $last); $self->{ '_is_in_join' } = 0; } $self->_back($token, $last) unless defined $last and $last eq '('; $self->_new_line($token,$last); $self->_add_token( $token ); $self->_new_line($token,$last) if ( defined $self->_next_token and $self->_next_token ne '(' and $self->_next_token !~ /^ALL$/i ); $self->{ '_is_in_where' }-- if ($self->{ '_is_in_where' }); $self->{ '_is_in_from' } = 0; } elsif ( $token =~ /^(?:LEFT|RIGHT|FULL|INNER|OUTER|CROSS|NATURAL)$/i and (not defined $last or uc($last) ne 'MATCH') ) { $self->{ 'no_break' } = 0; if (!$self->{ '_is_in_join' } and ($last and $last ne ')') ) { $self->_back($token, $last); } if ($self->{ '_has_over_in_join' }) { $self->{ '_has_over_in_join' } = 0; $self->_back($token, $last); } if ( $token =~ /(?:LEFT|RIGHT|FULL|CROSS|NATURAL)$/i ) { $self->_new_line($token,$last); $self->_over($token,$last) if ( $self->{ '_level' } == 0 ); } if ( ($token =~ /(?:INNER|OUTER)$/i) && ($last !~ /(?:LEFT|RIGHT|CROSS|NATURAL|FULL)$/i) ) { $self->_new_line($token,$last); $self->_over($token,$last) if (!$self->{ '_is_in_join' }); } $self->_add_token( $token ); } elsif ( $token =~ /^(?:JOIN)$/i and !$self->{ '_is_in_operator' }) { $self->{ 'no_break' } = 0; if ( not defined $last or $last !~ /^(?:LEFT|RIGHT|FULL|INNER|OUTER|CROSS|NATURAL)$/i ) { $self->_new_line($token,$last); $self->_back($token, $last) if ($self->{ '_has_over_in_join' }); $self->{ '_has_over_in_join' } = 0; } $self->_add_token( $token ); $self->{ '_is_in_join' } = 1; } elsif ( $token =~ /^(?:AND|OR)$/i ) { # Try to detect AND in BETWEEN clause to prevent newline insert if (uc($token) eq 'AND' and ($self->_next_token() =~ /^\d+$/ || (defined $last && $last =~ /^(PRECEDING|FOLLOWING|ROW)$/i))) { $self->_add_token( $token ); $last = $self->_set_last($token, $last); next; } $self->{ 'no_break' } = 0; if ($self->{ '_is_in_join' }) { $self->_over($token,$last); $self->{ '_has_over_in_join' } = 1; } $self->{ '_is_in_join' } = 0; if ( !$self->{ '_is_in_if' } and !$self->{ '_is_in_index' } and (!$last or $last !~ /^(?:CREATE)$/i) and ($self->{ '_is_in_create' } <= 2) and !$self->{ '_is_in_trigger' } ) { $self->_new_line($token,$last); if (!$self->{'_and_level'} and (!$self->{ '_level' } || $self->{ '_is_in_alter' })) { $self->_over($token,$last); } elsif ($self->{'_and_level'} and !$self->{ '_level' } and uc($token) eq 'OR') { $self->_over($token,$last); } elsif ($#{$self->{ '_level_stack' }} >= 0 and $self->{ '_level' } == $self->{ '_level_stack' }[-1]) { $self->_over($token,$last); } } $self->_add_token( $token ); $self->{'_and_level'}++; } elsif ( $token =~ /^\/\*.*\*\/$/s ) { if ( !$self->{ 'no_comments' } ) { $token =~ s/\n[\s\t]+\*/\n\*/gs; $self->_new_line($token,$last), $self->_add_token('') if (defined $last and $last eq ';'); $self->_new_line($token,$last); $self->_add_token( $token ); $self->{ 'break' } = "\n" unless ( $self->{ 'spaces' } != 0 ); $self->_new_line($token,$last) if (!$self->_is_comment($token) or !defined $self->_next_token or $self->_next_token ne ')'); $self->{ 'break' } = " " unless ( $self->{ 'spaces' } != 0 ); } } elsif ($token =~ /^USING$/i and (!$self->{ '_is_in_policy' } || $self->{ 'format_type' })) { $self->{ '_is_in_using' } = 1; if (!$self->{ '_is_in_from' }) { $self->_over($token,$last) if ($self->{ '_is_in_operator' }); $self->_new_line($token,$last) if (uc($last) ne 'EXCLUDE' and !$self->{ '_is_in_index' }); } else { # USING from join clause disable line break #$self->{ 'no_break' } = 1; $self->{ '_is_in_function' }++; } $self->_add_token($token); } elsif ($token =~ /^EXCLUDE$/i) { if ($last !~ /^(FOLLOWING|ADD)$/i or $self->_next_token !~ /^USING$/i) { $self->_new_line($token,$last) if ($last !~ /^(FOLLOWING|ADD)$/i); } $self->_add_token( $token ); $self->{ '_is_in_using' } = 1; } elsif ($token =~ /^\\\S/) { # treat everything starting with a \ and at least one character as psql meta command. $self->_add_token( $token ); $self->_new_line($token,$last); } elsif ($token =~ /^ADD|DROP$/i && ($self->{ '_current_sql_stmt' } eq 'SEQUENCE' || $self->{ '_current_sql_stmt' } eq 'ALTER')) { if ($self->_next_token !~ /^NOT|NULL|DEFAULT$/i and (not defined $last or !$self->{ '_is_in_alter' } or $last ne '(')) { $self->_new_line($token,$last); if ($self->{ '_is_in_alter' } < 2) { $self->_over($token,$last); } } $self->_add_token($token, $last); $self->{ '_is_in_alter' }++ if ($self->{ '_is_in_alter' } == 1); } elsif ($token =~ /^INCREMENT$/i && $self->{ '_current_sql_stmt' } eq 'SEQUENCE') { $self->_new_line($token,$last); $self->_add_token($token); } elsif ($token =~ /^NO$/i and $self->_next_token =~ /^(MINVALUE|MAXVALUE)$/i) { $self->_new_line($token,$last); $self->_add_token($token); } elsif ($last !~ /^(\(|NO)$/i and $token =~ /^(MINVALUE|MAXVALUE)$/i) { $self->_new_line($token,$last); $self->_add_token($token); } elsif ($token =~ /^CACHE$/i) { $self->_new_line($token,$last); $self->_add_token($token); } else { if ($self->{ '_fct_code_delimiter' } and $self->{ '_fct_code_delimiter' } =~ /^'.*'$/) { $self->{ '_fct_code_delimiter' } = ""; } if ($self->{ '_is_in_block' } != -1 and !$self->{ '_fct_code_delimiter' }) { $self->{ '_is_in_block' } = -1; $self->{ '_is_in_procedure' } = 0; $self->{ '_is_in_function' } = 0; } # special case with comment if ($token =~ /(?:\s*--)[\ \t\S]*/s) { if ( !$self->{ 'no_comments' } ) { $token =~ s/^(\s*)(--.*)/$2/s; my $start = $1 || ''; if ($start =~ /\n/s) { $self->_new_line($token,$last), $self->_add_token('') if (defined $last and $last eq ';' and $self->{ 'content' } !~ /\n$/s); $self->_new_line($token,$last); } $token =~ s/\s+$//s; $token =~ s/^\s+//s; $self->_add_token( $token ); $self->_new_line($token,$last) if ($start || $self->{ 'content' } !~ /\n/s); # Add extra newline after the last comment if we are not in a block or a statement if (defined $self->_next_token and $self->_next_token !~ /^\s*--/) { $self->{ 'content' } .= "\n" if ($self->{ '_is_in_block' } == -1 and !$self->{ '_is_in_declare' } and !$self->{ '_fct_code_delimiter' } and !$self->{ '_current_sql_stmt' } and defined $last and $self->_is_comment($last) ); } $last = $self->_set_last($token, $last); } next; } if ($last =~ /^(?:SEQUENCE)$/i and $self->_next_token !~ /^(OWNED|;)$/i) { $self->_add_token( $token ); $self->_new_line($token,$last); $self->_over($token,$last); } else { if (defined $last && $last eq ')' && (!defined $self->_next_token || $self->_next_token ne ';')) { if (!$self->{ '_parenthesis_level' } && $self->{ '_is_in_from' }) { $self->_set_level(pop(@{ $self->{ '_level_parenthesis' } }) || 1, $token, $last); } } if (defined $last and uc($last) eq 'UPDATE' and $self->{ '_current_sql_stmt' } eq 'UPDATE') { $self->_new_line($token,$last); $self->_over($token,$last); } if (defined $last and uc($last) eq 'AS' and uc($token) eq 'WITH') { $self->_new_line($token,$last); } if (uc($token) eq 'INSERT' and defined $last and $last eq ';') { if ($#{ $self->{ '_level_stack' } } >= 0) { $self->_set_level($self->{ '_level_stack' }[-1], $token, $last); } else { $self->_back($token,$last); } } $self->_add_token( $token, $last ); if (defined $last && uc($last) eq 'LANGUAGE' && (!defined $self->_next_token || $self->_next_token ne ';')) { $self->_new_line($token,$last); } } } $last = $self->_set_last($token, $last); $pos++; } $self->_new_line(); return; } =head2 _add_token Add a token to the beautified string. Code lifted from SQL::Beautify =cut sub _add_token { my ( $self, $token, $last_token ) = @_; if ($DEBUG) { my ($package, $filename, $line) = caller; print STDERR "DEBUG_ADD: line: $line => last=", ($last_token||''), ", token=$token\n"; } if ( $self->{ 'wrap' } ) { my $wrap; if ( $self->_is_keyword( $token, $self->_next_token(), $last_token ) ) { $wrap = $self->{ 'wrap' }->{ 'keywords' }; } elsif ( $self->_is_constant( $token ) ) { $wrap = $self->{ 'wrap' }->{ 'constants' }; } if ( $wrap ) { $token = $wrap->[ 0 ] . $token . $wrap->[ 1 ]; } } my $last_is_dot = defined( $last_token ) && $last_token eq '.'; my $sp = $self->_indent; if ( !$self->_is_punctuation( $token ) and !$last_is_dot) { if ( (!defined($last_token) || $last_token ne '(') && $token ne ')' && $token !~ /^::/ ) { if ($token ne ')' && defined($last_token) && $last_token ne '::' && $last_token ne '[' && ($token ne '(' || !$self->_is_function( $last_token ) || $self->{ '_is_in_type' }) ) { print STDERR "DEBUG_SPC: 1) last=", ($last_token||''), ", token=$token\n" if ($DEBUG_SP); $self->{ 'content' } .= $sp if ($token !~ /^['"].*['"]$/ || $last_token ne ':'); } if (!defined($last_token) && $token) { print STDERR "DEBUG_SPC: 2) last=", ($last_token||''), ", token=$token\n" if ($DEBUG_SP); $self->{ 'content' } .= $sp; } } elsif ( defined $last_token && $last_token eq '(' && $token ne ')' && $token !~ /^::/ && !$self->{'wrap_after'} && $self->{ '_is_in_with' } == 1) { print STDERR "DEBUG_SPC: 3) last=", ($last_token||''), ", token=$token\n" if ($DEBUG_SP); $self->{ 'content' } .= $sp; } elsif ( $self->{ '_is_in_create' } == 2 && defined($last_token)) { if ($last_token ne '::' and !$self->{ '_is_in_partition' } and !$self->{ '_is_in_policy' } and !$self->{ '_is_in_trigger' } and !$self->{ '_is_in_aggregate' } and ($last_token ne '(' || !$self->{ '_is_in_index' })) { print STDERR "DEBUG_SPC: 4) last=", ($last_token||''), ", token=$token\n" if ($DEBUG_SP); $self->{ 'content' } .= $sp; } } elsif (defined $last_token and (!$self->{ '_is_in_operator' } or !$self->{ '_is_in_alter' })) { if ($last_token eq '(' && ($self->{ '_is_in_type' } or ($self->{ '_is_in_operator' } and !$self->_is_type($token)))) { print STDERR "DEBUG_SPC: 5) last=", ($last_token||''), ", token=$token\n" if ($DEBUG_SP); $self->{ 'content' } .= $sp; } } elsif ($token eq ')' and $self->{ '_is_in_block' } >= 0 && $self->{ '_is_in_create' }) { print STDERR "DEBUG_SPC: 6) last=", ($last_token||''), ", token=$token\n" if ($DEBUG_SP); $self->{ 'content' } .= $sp; } else { print STDERR "DEBUG_SPC: 7) last=", ($last_token||''), ", token=$token\n" if ($DEBUG_SP); } if ($self->_is_comment($token)) { my @lines = split(/\n/, $token); for (my $i = 1; $i <= $#lines; $i++) { if ($lines[$i] =~ /^\s*\*/) { $lines[$i] =~ s/^\s*\*/$sp */; } elsif ($lines[$i] =~ /^\s+[^\*]/) { $lines[$i] =~ s/^\s+/$sp /; } } $token = join("\n", @lines); } else { $token =~ s/\n/\n$sp/gs; } } my $next_token = $self->_next_token || ''; my @cast = split(/::/, $token); $token = shift(@cast) if ($#cast >= 0); # lowercase/uppercase keywords taking care of function with same name if ($self->_is_keyword( $token, $next_token, $last_token ) and (!$self->_is_type($self->_next_token) or $self->{ '_is_in_create' } < 2 or $self->{ '_is_in_create_function' }) and (!$self->_is_function( $token ) || $next_token ne '(') ) { $token = lc( $token ) if ( $self->{ 'uc_keywords' } == 1 ); $token = uc( $token ) if ( $self->{ 'uc_keywords' } == 2 ); $token = ucfirst( lc( $token ) ) if ( $self->{ 'uc_keywords' } == 3 ); } else { # lowercase/uppercase known functions or words followed by an open parenthesis # if the token is not a keyword, an open parenthesis or a comment my $fct = $self->_is_function( $token ) || ''; if (($fct && $next_token eq '(') || (!$self->_is_keyword( $token, $next_token, $last_token ) && !$next_token eq '(' && $token ne '(' && !$self->_is_comment( $token )) ) { $token =~ s/$fct/\L$fct\E/i if ( $self->{ 'uc_functions' } == 1 ); $token =~ s/$fct/\U$fct\E/i if ( $self->{ 'uc_functions' } == 2 ); $fct = ucfirst( lc( $fct ) ); $token =~ s/$fct/$fct/i if ( $self->{ 'uc_functions' } == 3 ); } } if ($token =~ /^(AT|SET)$/i) { $self->{ '_not_a_type' } = 1; } elsif (!$self->_is_type($token)) { $self->{ '_not_a_type' } = 0; } # Type are always lowercase if (!$self->{ '_not_a_type' }) { if ($self->_is_type($token) and defined $last_token) { if ((!$self->_is_keyword($last_token) or $self->_is_type($last_token) or $self->_is_type($next_token)) and (not defined $next_token or $next_token !~ /^(SEARCH)$/i)) { $token = lc( $token ) if ( $self->{ 'uc_types' } == 1 ); $token = uc( $token ) if ( $self->{ 'uc_types' } == 2 ); $token = ucfirst( lc( $token ) ) if ( $self->{ 'uc_types' } == 3 ); } } } #$self->{ '_not_a_type' } = 0 if ($token =~ /^(ZONE|TO|=|;)$/i); # Add formatting for HTML output if ( $self->{ 'colorize' } && $self->{ 'format' } eq 'html' ) { $token = $self->highlight_code($token, $last_token, $next_token); } foreach my $c (@cast) { $c = lc($c) if ( $self->{ 'uc_functions' } == 1 ); $c = uc($c) if ( $self->{ 'uc_functions' } == 2 ); $c = ucfirst( lc( $c ) ) if ( $self->{ 'uc_functions' } == 3 ); $token .= '::' . $c; } $self->{ 'content' } .= $token; # This can't be the beginning of a new line anymore. $self->{ '_new_line' } = 0; } =head2 _over Increase the indentation level. Code lifted from SQL::Beautify =cut sub _over { my ( $self, $token, $last ) = @_; if ($DEBUG) { my ($package, $filename, $line) = caller; print STDERR "DEBUG_OVER: line: $line => last=$last, token=$token\n"; } ++$self->{ '_level' }; } =head2 _back Decrease the indentation level. Code lifted from SQL::Beautify =cut sub _back { my ( $self, $token, $last ) = @_; if ($DEBUG) { my ($package, $filename, $line) = caller; print STDERR "DEBUG_BACK: line: $line => last=$last, token=$token\n"; } --$self->{ '_level' } if ( $self->{ '_level' } > 0 ); } =head2 _indent Return a string of spaces according to the current indentation level and the spaces setting for indenting. Code lifted from SQL::Beautify =cut sub _indent { my ( $self ) = @_; if ( $self->{ '_new_line' } ) { return $self->{ 'space' } x ( $self->{ 'spaces' } * ( $self->{ '_level' } // 0 ) ); } # When this is not for identation force using space else { return ' '; } } =head2 _new_line Add a line break, but make sure there are no empty lines. Code lifted from SQL::Beautify =cut sub _new_line { my ( $self, $token, $last ) = @_; if ($DEBUG and defined $token) { my ($package, $filename, $line) = caller; print STDERR "DEBUG_NL: line: $line => last=", ($last||''), ", token=$token\n"; } $self->{ 'content' } .= $self->{ 'break' } unless ( $self->{ '_new_line' } ); $self->{ '_new_line' } = 1; } =head2 _next_token Have a look at the token that's coming up next. Code lifted from SQL::Beautify =cut sub _next_token { my ( $self ) = @_; return @{ $self->{ '_tokens' } } ? $self->{ '_tokens' }->[ 0 ] : undef; } =head2 _token Get the next token, removing it from the list of remaining tokens. Code lifted from SQL::Beautify =cut sub _token { my ( $self ) = @_; return shift @{ $self->{ '_tokens' } }; } =head2 _is_keyword Check if a token is a known SQL keyword. Code lifted from SQL::Beautify =cut sub _is_keyword { my ( $self, $token, $next_token, $last_token ) = @_; return 0 if (!$token); # Remove cast if any $token =~ s/::[^:]+$//; # Fix some false positive if (defined $next_token) { return 0 if (uc($token) eq 'EVENT' and uc($next_token) ne 'TRIGGER'); } return 0 if (uc($token) eq 'COMMENT' and (not defined $next_token or $next_token) !~ /^ON|IS$/i); if (defined $last_token) { return 0 if ( ($self->{ '_is_in_type' } or $self->{ '_is_in_create' }) and $last_token =~ /^OF|FROM$/i); return 0 if ($token =~ /^CONSTRAINT|INDEX|TRUE|FALSE$/i and uc($last_token) eq 'AS'); } if ($DEBUG and defined $token and grep { $_ eq uc( $token ) } @{ $self->{ 'keywords' } }) { my ($package, $filename, $line) = caller; print STDERR "DEBUG_KEYWORD: line: $line => last=", ($last_token||''), ", token=$token, next=", ($next_token||''), "\n"; } return ~~ grep { $_ eq uc( $token ) } @{ $self->{ 'keywords' } }; } =head2 _is_type Check if a token is a known SQL type =cut sub _is_type { my ( $self, $token ) = @_; return if (!defined $token); $token =~ s/\s*\(.*//; # remove any parameter to the type return ~~ grep { $_ eq uc( $token ) } @{ $self->{ 'types' } }; } sub _is_sql_keyword { my ( $self, $token ) = @_; return ~~ grep { $_ eq uc( $token ) } @{ $self->{ 'sql_keywords' } }; } =head2 _is_comment Check if a token is a SQL or C style comment =cut sub _is_comment { my ( $self, $token ) = @_; return 1 if ( $token =~ m#^\s*((?:--)[\ \t\S]*|/\*[\ \t\r\n\S]*?\*/)$#s ); return 0; } =head2 _is_function Check if a token is a known SQL function. Code lifted from SQL::Beautify and rewritten to check one long regexp instead of a lot of small ones. =cut sub _is_function { my ( $self, $token ) = @_; return undef if (!$token); if ( $token =~ $self->{ 'functions_re' } ) { return $1; } else { return undef; } } =head2 add_keywords Add new keywords to highlight. Code lifted from SQL::Beautify =cut sub add_keywords { my $self = shift; for my $keyword ( @_ ) { push @{ $self->{ 'keywords' } }, ref( $keyword ) ? @{ $keyword } : $keyword; } } =head2 _re_from_list Create compiled regexp from prefix, suffix and and a list of values to match. =cut sub _re_from_list { my $prefix = shift; my $suffix = shift; my (@joined_list, $ret_re); for my $list_item ( @_ ) { push @joined_list, ref( $list_item ) ? @{ $list_item } : $list_item; } $ret_re = "$prefix(" . join('|', @joined_list) . ")$suffix"; return qr/$ret_re/i; } =head2 _refresh_functions_re Refresh compiled regexp for functions. =cut sub _refresh_functions_re { my $self = shift; $self->{ 'functions_re' } = _re_from_list( '\b[\.]*', '$', @{ $self->{ 'functions' } }); } =head2 add_functions Add new functions to highlight. Code lifted from SQL::Beautify =cut sub add_functions { my $self = shift; for my $function ( @_ ) { push @{ $self->{ 'functions' } }, ref( $function ) ? @{ $function } : $function; } $self->_refresh_functions_re(); } =head2 add_rule Add new rules. Code lifted from SQL::Beautify =cut sub add_rule { my ( $self, $format, $token ) = @_; my $rules = $self->{ 'rules' } ||= {}; my $group = $rules->{ $format } ||= []; push @{ $group }, ref( $token ) ? @{ $token } : $token; } =head2 _get_rule Find custom rule for a token. Code lifted from SQL::Beautify =cut sub _get_rule { my ( $self, $token ) = @_; values %{ $self->{ 'rules' } }; # Reset iterator. while ( my ( $rule, $list ) = each %{ $self->{ 'rules' } } ) { return $rule if ( grep { uc( $token ) eq uc( $_ ) } @$list ); } return; } =head2 _process_rule Applies defined rule. Code lifted from SQL::Beautify =cut sub _process_rule { my ( $self, $rule, $token ) = @_; my $format = { break => sub { $self->_new_line() }, over => sub { $self->_over() }, back => sub { $self->_back() }, token => sub { $self->_add_token( $token ) }, push => sub { push @{ $self->{ '_level_stack' } }, $self->{ '_level' } }, pop => sub { $self->{ '_level' } = $self->_pop_level($token, '') }, reset => sub { $self->{ '_level' } = 0; @{ $self->{ '_level_stack' } } = (); }, }; for ( split /-/, lc $rule ) { &{ $format->{ $_ } } if ( $format->{ $_ } ); } } =head2 _is_constant Check if a token is a constant. Code lifted from SQL::Beautify =cut sub _is_constant { my ( $self, $token ) = @_; return ( $token =~ /^\d+$/ or $token =~ /^(['"`]).*\1$/ ); } =head2 _is_punctuation Check if a token is punctuation. Code lifted from SQL::Beautify =cut sub _is_punctuation { my ( $self, $token ) = @_; if ($self->{ 'comma' } eq 'start' and $token eq ',') { return 0; } return ( $token =~ /^[,;.\[\]]$/ ); } =head2 _generate_anonymized_string Simply generate a random string, thanks to Perlmonks. Returns original in certain cases which don't require anonymization, like timestamps, or intervals. =cut sub _generate_anonymized_string { my $self = shift; my ( $before, $original, $after ) = @_; # Prevent dates from being anonymized return $original if $original =~ m{\A\d\d\d\d[/:-]\d\d[/:-]\d\d\z}; return $original if $original =~ m{\A\d\d[/:-]\d\d[/:-]\d\d\d\d\z}; # Prevent dates format like DD/MM/YYYY HH24:MI:SS from being anonymized return $original if $original =~ m{ \A (?:FM|FX|TM)? (?: HH | HH12 | HH24 | MI | SS | MS | US | SSSS | AM | A\.M\. | am | a\.m\. | PM | P\.M\. | pm | p\.m\. | Y,YYY | YYYY | YYY | YY | Y | IYYY | IYY | IY | I | BC | B\.C\. | bc | b\.c\. | AD | A\.D\. | ad | a\.d\. | MONTH | Month | month | MON | Mon | mon | MM | DAY | Day | day | DY | Dy | dy | DDD | DD | D | W | WW | IW | CC | J | Q | RM | rm | TZ | tz | [\s/:-] )+ (?:TH|th|SP)? \z }; # Prevent interval from being anonymized return $original if ($before && ($before =~ /interval/i)); return $original if ($after && ($after =~ /^\)*::interval/i)); # Shortcut my $cache = $self->{ '_anonymization_cache' }; # Range of characters to use in anonymized strings my @chars = ( 'A' .. 'Z', 0 .. 9, 'a' .. 'z', '-', '_', '.' ); unless ( $cache->{ $original } ) { # Actual anonymized version generation $cache->{ $original } = join( '', map { $chars[ rand @chars ] } 1 .. 10 ); } return $cache->{ $original }; } =head2 anonymize Anonymize litteral in SQL queries by replacing parameters with fake values =cut sub anonymize { my $self = shift; my $query = $self->query; return if ( !$query ); # Variable to hold anonymized versions, so we can provide the same value # for the same input, within single query. $self->{ '_anonymization_cache' } = {}; # Remove comments $query =~ s/\/\*(.*?)\*\///gs; # Clean query $query =~ s/\\'//gs; $query =~ s/('')+/\$EMPTYSTRING\$/gs; # Anonymize each values $query =~ s{ ([^\s\']+[\s\(]*) # before '([^']*)' # original ([\)]*::\w+)? # after }{$1 . "'" . $self->_generate_anonymized_string($1, $2, $3) . "'" . ($3||'')}xeg; $query =~ s/\$EMPTYSTRING\$/''/gs; $self->query( $query ); } =head2 set_defaults Sets defaults for newly created objects. Currently defined defaults: =over =item spaces => 4 =item space => ' ' =item break => "\n" =item uc_keywords => 2 =item uc_functions => 0 =item uc_types => 1 =item no_comments => 0 =item no_grouping => 0 =item placeholder => '' =item separator => '' =item comma => 'end' =item format => 'text' =item colorize => 1 =item format_type => 0 =item wrap_limit => 0 =item wrap_after => 0 =back =cut sub set_defaults { my $self = shift; $self->set_dicts(); # Set some defaults. $self->{ 'query' } = ''; $self->{ 'spaces' } = 4; $self->{ 'space' } = ' '; $self->{ 'break' } = "\n"; $self->{ 'wrap' } = {}; $self->{ 'rules' } = {}; $self->{ 'uc_keywords' } = 2; $self->{ 'uc_functions' } = 0; $self->{ 'uc_types' } = 1; $self->{ 'no_comments' } = 0; $self->{ 'no_grouping' } = 0; $self->{ 'placeholder' } = ''; $self->{ 'keywords' } = $self->{ 'dict' }->{ 'pg_keywords' }; $self->{ 'types' } = $self->{ 'dict' }->{ 'pg_types' }; $self->{ 'functions' } = (); push(@{ $self->{ 'functions' } }, keys %{ $self->{ 'dict' }->{ 'pg_functions' } }); $self->_refresh_functions_re(); $self->{ 'separator' } = ''; $self->{ 'comma' } = 'end'; $self->{ 'format' } = 'text'; $self->{ 'colorize' } = 1; $self->{ 'format_type' } = 0; $self->{ 'wrap_limit' } = 0; $self->{ 'wrap_after' } = 0; return; } =head2 format Set output format - possible values: 'text' and 'html' Default is text output. Returns 0 in case or wrong format and use default. =cut sub format { my $self = shift; my $format = shift; if ( grep(/^$format$/i, 'text', 'html') ) { $self->{ 'format' } = lc($format); return 1; } return 0; } =head2 set_dicts Sets various dictionaries (lists of keywords, functions, symbols, and the like) This was moved to separate function, so it can be put at the very end of module so it will be easier to read the rest of the code. =cut sub set_dicts { my $self = shift; # First load it all as "my" variables, to make it simpler to modify/map/grep/add # Afterwards, when everything is ready, put it in $self->{'dict'}->{...} my @pg_keywords = map { uc } qw( ADD AFTER AGGREGATE ALL ALTER ANALYSE ANALYZE AND ANY ARRAY AS ASC ASYMMETRIC AUTHORIZATION ATTACH AUTO_INCREMENT BACKWARD BEFORE BEGIN BERNOULLI BETWEEN BINARY BOTH BY CACHE CASCADE CASE CAST CHECK CHECKPOINT CLOSE CLUSTER COLLATE COLLATION COLUMN COMMENT COMMIT COMMITTED CONCURRENTLY CONFLICT CONSTRAINT CONSTRAINT CONTINUE COPY COST COSTS CREATE CROSS CUBE CURRENT CURRENT_DATE CURRENT_ROLE CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE DATABASE DEALLOCATE DECLARE DEFAULT DEFERRABLE DEFERRED DEFINER DELETE DELIMITER DESC DETACH EVENT DISTINCT DO DOMAIN DROP EACH ELSE ENCODING END EXCEPT EXCLUDE EXCLUDING EXECUTE EXISTS EXPLAIN EXTENSION FALSE FETCH FILTER FIRST FOLLOWING FOR FOREIGN FORWARD FREEZE FROM FULL FUNCTION GENERATED GRANT GROUP GROUPING HAVING HASHES HASH IDENTITY IF ILIKE IMMUTABLE IN INCLUDING INCREMENT INDEX INHERITS INITIALLY INNER INOUT INSERT INSTEAD INTERSECT INTO INVOKER IS ISNULL ISOLATION JOIN KEY LANGUAGE LAST LATERAL LC_COLLATE LC_CTYPE LEADING LEAKPROOF LEFT LEFTARG LIKE LIMIT LIST LISTEN LOAD LOCALTIME LOCALTIMESTAMP LOCATION LOCK LOCKED LOGGED LOGIN LOOP MAPPING MATCH MAXVALUE MERGES MINVALUE MODULUS MOVE NATURAL NEXT NOTHING ORDINALITY NO NOCREATEDB NOCREATEROLE NOSUPERUSER NOT NOTIFY NOTNULL NOWAIT NULL OFF OF OIDS ON ONLY OPEN OPERATOR OR ORDER OUTER OVER OVERLAPS OWNER PARTITION PASSWORD PERFORM PLACING POLICY PRECEDING PREPARE PRIMARY PROCEDURE RANGE REASSIGN RECURSIVE REFERENCES REINDEX REMAINDER RENAME REPEATABLE REPLACE REPLICA RESET RESTART RESTRICT RETURN RETURNING RETURNS REVOKE RIGHT RIGHTARG ROLE ROLLBACK ROLLUP ROWS ROW RULE SAVEPOINT SCHEMA SCROLL SECURITY SELECT SEQUENCE SEQUENCE SERIALIZABLE SERVER SESSION_USER SET SETOF SETS SHOW SIMILAR SKIP SNAPSHOT SOME STABLE START STRICT SYMMETRIC SYSTEM TABLE TABLESAMPLE TABLESPACE TEMPLATE TEMPORARY THEN TO TRAILING TRANSACTION TRIGGER TRUE TRUNCATE TYPE UNBOUNDED UNCOMMITTED UNION UNIQUE UNLISTEN UNLOCK UNLOGGED UPDATE USER USING VACUUM VALUES VARIADIC VERBOSE VIEW VOLATILE WHEN WHERE WINDOW WITH WITHIN WORK XOR ZEROFILL CALL GROUPS INCLUDE OTHERS PROCEDURES ROUTINE ROUTINES TIES READ_ONLY SHAREABLE READ_WRITE BASETYPE SFUNC STYPE SFUNC1 STYPE1 SSPACE FINALFUNC FINALFUNC_EXTRA FINALFUNC_MODIFY COMBINEFUNC SERIALFUNC DESERIALFUNC INITCOND MSFUNC MINVFUNC MSTYPE MSSPACE MFINALFUNC MFINALFUNC_EXTRA MFINALFUNC_MODIFY MINITCOND SORTOP REFRESH MATERIALIZED RAISE ); my @pg_types = qw( BIGINT BIGSERIAL BIT BOOLEAN BOOL BOX BYTEA CHARACTER CHAR CIDR CIRCLE DATE DOUBLE INET INT INTEGER INTERVAL JSON JSONB LINE LSEG MACADDR MACADDR8 MONEY NUMERIC PG_LSN POINT POLYGON REAL SMALLINT SMALLSERIAL SERIAL TEXT TIME TIMESTAMP TIMESTAMPTZ TSQUERY TSVECTOR TXID_SNAPSHOT UUID XML INT2 INT4 INT8 VARYING VARCHAR ZONE ); my @sql_keywords = map { uc } qw( ABORT ABSOLUTE ACCESS ACTION ADMIN ALSO ALWAYS ASSERTION ASSIGNMENT AT ATTRIBUTE BIGINT BOOLEAN CALLED CASCADED CATALOG CHAIN CHANGE CHARACTER CHARACTERISTICS COLUMNS COMMENTS CONFIGURATION CONNECTION CONSTRAINTS CONTENT CONVERSION CSV CURRENT DATA DATABASES DAY DEC DECIMAL DEFAULTS DELAYED DELIMITERS DESCRIBE DICTIONARY DISABLE DISCARD DOCUMENT DOUBLE ENABLE ENCLOSED ENCRYPTED ENUM ESCAPE ESCAPED EXCLUSIVE EXTERNAL FIELD FIELDS FLOAT FLUSH FOLLOWING FORCE FUNCTIONS GLOBAL GRANTED GREATEST HANDLER HEADER HOLD HOUR IDENTIFIED IGNORE IMMEDIATE IMPLICIT INDEXES INFILE INHERIT INLINE INPUT INSENSITIVE INT INTEGER KEYS KILL LABEL LARGE LEAST LEVEL LINES LOCAL LOW_PRIORITY MATCH MINUTE MODE MODIFY MONTH NAMES NATIONAL NCHAR NONE NOTHING NULLIF NULLS OBJECT OFF OPERATOR OPTIMIZE OPTION OPTIONALLY OPTIONS OUT OUTFILE OWNED PARSER PARTIAL PASSING PLANS PRECISION PREPARED PRESERVE PRIOR PRIVILEGES PROCEDURAL QUOTE READ REAL RECHECK REF REGEXP RELATIVE RELEASE RLIKE ROW SEARCH SECOND SEQUENCES SESSION SHARE SIMPLE SMALLINT SONAME STANDALONE STATEMENT STATISTICS STATUS STORAGE STRAIGHT_JOIN SYSID TABLES TEMP TERMINATED TREAT TRUSTED TYPES UNENCRYPTED UNKNOWN UNSIGNED UNTIL USE VALID VALIDATE VALIDATOR VALUE VARIABLES VARYING WHITESPACE WITHOUT WORK WRAPPER WRITE XMLATTRIBUTES YEAR YES ZONE ); my @redshift_keywords = map { uc } qw( AES128 AES256 ALLOWOVERWRITE BACKUP BLANKSASNULL BYTEDICT BZIP2 CREDENTIALS CURRENT_USER_ID DEFLATE DEFRAG DELTA DELTA32K DISABLE DISTKEY EMPTYASNULL ENABLE ENCODE ENCRYPT ENCRYPTION EXPLICIT GLOBALDICT256 GLOBALDICT64K GZIP INTERLEAVED LUN LUNS LZO LZOP MINUS MOSTLY13 MOSTLY32 MOSTLY8 NEW OFFLINE OFFSET OID OLD PARALLEL PERCENT PERMISSIONS RAW READRATIO RECOVER REJECTLOG RESORT RESPECT RESTORE SORTKEY SYSDATE TAG TDES TEXT255 TEXT32K TIMESTAMP TOP TRUNCATECOLUMNS WALLET ); for my $k ( @pg_keywords ) { next if grep { $k eq $_ } @sql_keywords; push @sql_keywords, $k; } for my $k ( @redshift_keywords ) { next if grep { $k eq $_ } @sql_keywords; push @sql_keywords, $k; } my @pg_functions = map { lc } qw( ascii age bit_length btrim cardinality cast char_length character_length coalesce convert chr current_date current_time current_timestamp count decode date_part date_trunc encode extract get_byte get_bit initcap isfinite interval justify_hours justify_days lower length lpad ltrim localtime localtimestamp md5 now octet_length overlay position pg_client_encoding quote_ident quote_literal repeat replace rpad rtrim substring split_part strpos substr set_byte set_bit trim to_ascii to_hex translate to_char to_date to_timestamp to_number timeofday upper abbrev abs abstime abstimeeq abstimege abstimegt abstimein abstimele abstimelt abstimene abstimeout abstimerecv abstimesend aclcontains acldefault aclexplode aclinsert aclitemeq aclitemin aclitemout aclremove acos any_in any_out anyarray_in anyarray_out anyarray_recv anyarray_send anyelement_in anyelement_out anyenum_in anyenum_out anynonarray_in anynonarray_out anyrange_in anyrange_out anytextcat area areajoinsel areasel armor array_agg array_agg_finalfn array_agg_transfn array_append array_cat array_dims array_eq array_fill array_ge array_positions array_gt array_in array_larger array_le array_length array_lower array_lt array_position array_ndims array_ne array_out array_prepend array_recv array_remove array_replace array_send array_smaller array_to_json array_to_string array_typanalyze array_upper arraycontained arraycontains arraycontjoinsel arraycontsel arrayoverlap ascii_to_mic ascii_to_utf8 asin atan atan2 avg big5_to_euc_tw big5_to_mic big5_to_utf8 bit bit_and bit_in bit_or bit_out bit_recv bit_send bitand bitcat bitcmp biteq bitge bitgt bitle bitlt bitne bitnot bitor bitshiftleft bitshiftright bittypmodin bittypmodout bitxor bool bool_and bool_or booland_statefunc boolean booleq boolge boolgt boolin boolle boollt boolne boolor_statefunc boolout boolrecv boolsend box box_above box_above_eq box_add box_below box_below_eq box_center box_contain box_contain_pt box_contained box_distance box_div box_eq box_ge box_gt box_in box_intersect box_le box_left box_lt box_mul box_out box_overabove box_overbelow box_overlap box_overleft box_overright box_recv box_right box_same box_send box_sub bpchar bpchar_larger bpchar_pattern_ge bpchar_pattern_gt bpchar_pattern_le bpchar_pattern_lt bpchar_smaller bpcharcmp bpchareq bpcharge bpchargt bpchariclike bpcharicnlike bpcharicregexeq bpcharicregexne bpcharin bpcharle bpcharlike bpcharlt bpcharne bpcharnlike bpcharout bpcharrecv bpcharregexeq bpcharregexne bpcharsend bpchartypmodin bpchartypmodout broadcast btabstimecmp btarraycmp btbeginscan btboolcmp btbpchar_pattern_cmp btbuild btbuildempty btbulkdelete btcanreturn btcharcmp btcostestimate btendscan btfloat48cmp btfloat4cmp btfloat4sortsupport btfloat84cmp btfloat8cmp btfloat8sortsupport btgetbitmap btgettuple btinsert btint24cmp btint28cmp btint2cmp btint2sortsupport btint42cmp btint48cmp btint4cmp btint4sortsupport btint82cmp btint84cmp btint8cmp btint8sortsupport btmarkpos btnamecmp btnamesortsupport btoidcmp btoidsortsupport btoidvectorcmp btoptions btrecordcmp btreltimecmp btrescan btrestrpos bttext_pattern_cmp bttextcmp bttidcmp bttintervalcmp btvacuumcleanup bytea_string_agg_finalfn bytea_string_agg_transfn byteacat byteacmp byteaeq byteage byteagt byteain byteale bytealike bytealt byteane byteanlike byteaout bytearecv byteasend cash_cmp cash_div_cash cash_div_flt4 cash_div_flt8 cash_div_int2 cash_div_int4 cash_eq cash_ge cash_gt cash_in cash_le cash_lt cash_mi cash_mul_flt4 cash_mul_flt8 cash_mul_int2 cash_mul_int4 cash_ne cash_out cash_pl cash_recv cash_send cash_words cashlarger cashsmaller cbrt ceil ceiling center char chareq charge chargt charin charle charlt charne charout charrecv charsend cideq cidin cidout cidr cidr_in cidr_out cidr_recv cidr_send cidrecv cidsend circle circle_above circle_add_pt circle_below circle_center circle_contain circle_contain_pt circle_contained circle_distance circle_div_pt circle_eq circle_ge circle_gt circle_in circle_le circle_left circle_lt circle_mul_pt circle_ne circle_out circle_overabove circle_overbelow circle_overlap circle_overleft circle_overright circle_recv circle_right circle_same circle_send circle_sub_pt clock_timestamp close_lb close_ls close_lseg close_pb close_pl close_ps close_sb close_sl col_description concat concat_ws contjoinsel contsel convert_from convert_to corr cos cot covar_pop covar_samp crypt cstring_in cstring_out cstring_recv cstring_send cume_dist current_database current_query current_schema current_schemas current_setting current_user currtid currtid2 currval date date_cmp date_cmp_timestamp date_cmp_timestamptz date_eq date_eq_timestamp date_eq_timestamptz date_ge date_ge_timestamp date_ge_timestamptz date_gt date_gt_timestamp date_gt_timestamptz date_in date_larger date_le date_le_timestamp date_le_timestamptz date_lt date_lt_timestamp date_lt_timestamptz date_mi date_mi_interval date_mii date_ne date_ne_timestamp date_ne_timestamptz date_out date_pl_interval date_pli date_recv date_send date_smaller date_sortsupport daterange daterange_canonical daterange_subdiff datetime_pl datetimetz_pl dblink_connect_u dblink_connect dblink_disconnect dblink_exec dblink_open dblink_fetch dblink_close dblink_get_connections dblink_error_message dblink_send_query dblink_is_busy dblink_get_notify dblink_get_result dblink_cancel_query dblink_get_pkey dblink_build_sql_insert dblink_build_sql_delete dblink_build_sql_update dblink dcbrt dearmor decrypt decrypt_iv degrees dense_rank dexp diagonal decimal diameter digest dispell_init dispell_lexize dist_cpoly dist_lb dist_pb dist_pc dist_pl dist_ppath dist_ps dist_sb dist_sl div dlog1 dlog10 domain_in domain_recv dpow dround dsimple_init dsimple_lexize dsnowball_init dsnowball_lexize dsqrt dsynonym_init dsynonym_lexize dtrunc elem_contained_by_range encrypt encrypt_iv enum_cmp enum_eq enum_first enum_ge enum_gt enum_in enum_larger enum_last enum_le enum_lt enum_ne enum_out enum_range enum_recv enum_send enum_smaller eqjoinsel eqsel euc_cn_to_mic euc_cn_to_utf8 euc_jis_2004_to_shift_jis_2004 euc_jis_2004_to_utf8 euc_jp_to_mic euc_jp_to_sjis euc_jp_to_utf8 euc_kr_to_mic euc_kr_to_utf8 euc_tw_to_big5 euc_tw_to_mic euc_tw_to_utf8 every exp factorial family fdw_handler_in fdw_handler_out first_value float4 float48div float48eq float48ge float48gt float48le float48lt float48mi float48mul float48ne float48pl float4_accum float4abs float4div float4eq float4ge float4gt float4in float4larger float4le float4lt float4mi float4mul float4ne float4out float4pl float4recv float4send float4smaller float4um float4up float8 float84div float84eq float84ge float84gt float84le float84lt float84mi float84mul float84ne float84pl float8_accum float8_avg float8_combine float8_regr_combine float8_corr float8_covar_pop float8_covar_samp float8_regr_accum float8_regr_avgx float8_regr_avgy float8_regr_intercept float8_regr_r2 float8_regr_slope float8_regr_sxx float8_regr_sxy float8_regr_syy float8_stddev_pop float8_stddev_samp float8_var_pop float8_var_samp float8abs float8div float8eq float8ge float8gt float8in float8larger float8le float8lt float8mi float8mul float8ne float8out float8pl float8recv float8send float8smaller float8um float8up floor flt4_mul_cash flt8_mul_cash fmgr_c_validator fmgr_internal_validator fmgr_sql_validator format format_type gb18030_to_utf8 gbk_to_utf8 gen_random_bytes gen_salt generate_series generate_subscripts geometry get_current_ts_config getdatabaseencoding getpgusername gin_cmp_prefix gin_cmp_tslexeme gin_extract_tsquery gin_extract_tsvector gin_tsquery_consistent ginarrayconsistent ginarrayextract ginbeginscan ginbuild ginbuildempty ginbulkdelete gincostestimate ginendscan gingetbitmap gininsert ginmarkpos ginoptions ginqueryarrayextract ginrescan ginrestrpos ginvacuumcleanup gist_box_compress gist_box_consistent gist_box_decompress gist_box_penalty gist_box_picksplit gist_box_same gist_box_union gist_circle_compress gist_circle_consistent gist_point_compress gist_point_consistent gist_point_distance gist_poly_compress gist_poly_consistent gistbeginscan gistbuild gistbuildempty gistbulkdelete gistcostestimate gistendscan gistgetbitmap gistgettuple gistinsert gistmarkpos gistoptions gistrescan gistrestrpos gistvacuumcleanup gtsquery_compress gtsquery_consistent gtsquery_decompress gtsquery_penalty gtsquery_picksplit gtsquery_same gtsquery_union gtsvector_compress gtsvector_consistent gtsvector_decompress gtsvector_penalty gtsvector_picksplit gtsvector_same gtsvector_union gtsvectorin gtsvectorout has_any_column_privilege has_column_privilege has_database_privilege has_foreign_data_wrapper_privilege has_function_privilege has_language_privilege has_schema_privilege has_sequence_privilege has_server_privilege has_table_privilege has_tablespace_privilege has_type_privilege hash_aclitem hash_array hash_numeric hash_range hashbeginscan hashbpchar hashbuild hashbuildempty hashbulkdelete hashchar hashcostestimate hash_aclitem_extended hashendscan hashenum hashfloat4 hashfloat8 hashgetbitmap hashgettuple hashinet hashinsert hashint2 hashint2extended hashint2vector hashint4 hashint4extended hashint8 hashint8extended hashmacaddr hashfloat4extended hashfloat8extended hashcharextended hashoidextended hashnameextended hashmarkpos hashoidvectorextended hashmacaddrextended hashinetextended hashname hashoid hashoidvector hashoptions hash_numeric_extended hashmacaddr8extended hash_array_extended hashrescan hashrestrpos hashtext hashbpcharextended time_hash_extended timetz_hash_extended interval_hash_extended timestamp_hash_extended uuid_hash_extended pg_lsn_hash_extended hashenumextended jsonb_hash_extended hash_range_extended hashtextextended hashvacuumcleanup hashvarlena height hmac host hostmask iclikejoinsel iclikesel icnlikejoinsel icnlikesel icregexeqjoinsel icregexeqsel icregexnejoinsel icregexnesel inet_client_addr inet_client_port inet_in inet_out inet_recv inet_send inet_server_addr inet_server_port inetand inetmi inetmi_int8 inetnot inetor inetpl int int2 int24div int24eq int24ge int24gt int24le int24lt integer int24mi int24mul int24ne int24pl int28div int28eq int28ge int28gt int28le int28lt int28mi int28mul int28ne int28pl int2_accum int2_avg_accum int2_mul_cash int2_sum int2abs int2and int2div int2eq int2ge int2gt int2in int2larger int2le int2lt int2mi int2mod int2mul int2ne int2not int2or int2out int2pl int2recv int2send int2shl int2shr int2smaller int2um int2up int2vectoreq int2vectorin int2vectorout int2vectorrecv int2vectorsend int2xor int4 int42div int42eq int42ge int42gt int42le int42lt int42mi int42mul int42ne int42pl int48div int48eq int48ge int48gt int48le int48lt int48mi int48mul int48ne int48pl int4_accum int4_avg_accum int4_mul_cash int4_sum int4abs int4and int4div int4eq int4ge int4gt int4in int4inc int4larger int4le int4lt int4mi int4mod int4mul int4ne int4not int4or int4out int4pl int4range int4range_canonical int4range_subdiff int4recv int4send int4shl int4shr int4smaller int4um int4up int4xor int8 int82div int82eq int82ge int82gt int82le int82lt int82mi int82mul int82ne int82pl int84div int84eq int84ge int84gt int84le int84lt int84mi int84mul int84ne int84pl int8_accum int8_avg int8_avg_accum int8_sum int8abs int8and int8div int8eq int8ge int8gt int8in int8inc int8inc_any int8inc_float8_float8 int8larger int8le int8lt int8mi int8mod int8mul int8ne int8not int8or int8out int8pl int8pl_inet int8range int8range_canonical int8range_subdiff int8recv int8send int8shl int8shr int8smaller int8um int8up int8xor integer_pl_date inter_lb inter_sb inter_sl internal_in internal_out interval_accum interval_avg interval_cmp interval_div interval_eq interval_ge interval_gt interval_hash interval_in interval_larger interval_le interval_lt interval_mi interval_mul interval_ne interval_out interval_pl interval_pl_date interval_pl_time interval_pl_timestamp interval_pl_timestamptz interval_pl_timetz interval_recv interval_send interval_smaller interval_transform interval_um intervaltypmodin intervaltypmodout intinterval isclosed isempty ishorizontal iso8859_1_to_utf8 iso8859_to_utf8 iso_to_koi8r iso_to_mic iso_to_win1251 iso_to_win866 isopen isparallel isperp isvertical johab_to_utf8 json_agg jsonb_agg json_array_elements jsonb_array_elements json_array_elements_text jsonb_array_elements_text json_array_length jsonb_array_length json_build_array json_build_object json_each jsonb_each json_each_text jsonb_each_text json_extract_path jsonb_extract_path json_extract_path_text jsonb_extract_path_text json_in json_object json_object_agg jsonb_object_agg json_object_keys jsonb_object_keys json_out json_populate_record jsonb_populate_record json_populate_recordset jsonb_pretty jsonb_populate_recordset json_recv json_send jsonb_set json_typeof jsonb_typeof json_to_record jsonb_to_record json_to_recordset jsonb_to_recordset justify_interval koi8r_to_iso koi8r_to_mic koi8r_to_utf8 koi8r_to_win1251 koi8r_to_win866 koi8u_to_utf8 jsonb_path_query jsonb_build_object lag language_handler_in language_handler_out last_value lastval latin1_to_mic latin2_to_mic latin2_to_win1250 latin3_to_mic latin4_to_mic lead like_escape likejoinsel likesel line line_distance line_eq line_horizontal line_in line_interpt line_intersect line_out line_parallel line_perp line_recv line_send line_vertical ln lo_close lo_creat lo_create lo_export lo_import lo_lseek lo_open lo_tell lo_truncate lo_unlink log loread lower_inc lower_inf lowrite lseg lseg_center lseg_distance lseg_eq lseg_ge lseg_gt lseg_horizontal lseg_in lseg_interpt lseg_intersect lseg_le lseg_length lseg_lt lseg_ne lseg_out lseg_parallel lseg_perp lseg_recv lseg_send lseg_vertical macaddr_and macaddr_cmp macaddr_eq macaddr_ge macaddr_gt macaddr_in macaddr_le macaddr_lt macaddr_ne macaddr_not macaddr_or macaddr_out macaddr_recv macaddr_send makeaclitem make_interval make_tsrange masklen max mic_to_ascii mic_to_big5 mic_to_euc_cn mic_to_euc_jp mic_to_euc_kr mic_to_euc_tw mic_to_iso mic_to_koi8r mic_to_latin1 mic_to_latin2 mic_to_latin3 mic_to_latin4 mic_to_sjis mic_to_win1250 mic_to_win1251 mic_to_win866 min mktinterval mode mod money mul_d_interval name nameeq namege namegt nameiclike nameicnlike nameicregexeq nameicregexne namein namele namelike namelt namene namenlike nameout namerecv nameregexeq nameregexne namesend neqjoinsel neqsel netmask network network_cmp network_eq network_ge network_gt network_le network_lt network_ne network_sub network_subeq network_sup network_supeq nextval nlikejoinsel nlikesel notlike npoints nth_value ntile numeric numeric_abs numeric_accum numeric_add numeric_avg numeric_avg_accum numeric_cmp numeric_div numeric_div_trunc numeric_eq numeric_exp numeric_fac numeric_ge numeric_gt numeric_in numeric_inc numeric_larger numeric_le numeric_ln numeric_log numeric_lt numeric_mod numeric_mul numeric_ne numeric_out numeric_power numeric_recv numeric_send numeric_smaller numeric_sqrt numeric_stddev_pop numeric_stddev_samp numeric_sub numeric_transform numeric_uminus numeric_uplus numeric_var_pop numeric_var_samp numerictypmodin numerictypmodout numnode numrange numrange_subdiff obj_description oid oideq oidge oidgt oidin oidlarger oidle oidlt oidne oidout oidrecv oidsend oidsmaller oidvectoreq oidvectorge oidvectorgt oidvectorin oidvectorle oidvectorlt oidvectorne oidvectorout oidvectorrecv oidvectorsend oidvectortypes on_pb on_pl on_ppath on_ps on_sb on_sl opaque_in opaque_out overlaps path path_add path_add_pt path_center path_contain_pt path_distance path_div_pt path_in path_inter path_length path_mul_pt path_n_eq path_n_ge path_n_gt path_n_le path_n_lt path_npoints path_out path_recv path_send path_sub_pt pclose percent_rank percentile_cont percentile_disc pg_advisory_lock pg_advisory_lock_shared pg_advisory_unlock pg_advisory_unlock_all pg_advisory_unlock_shared pg_advisory_xact_lock pg_advisory_xact_lock_shared pg_available_extension_versions pg_available_extensions pg_backend_pid pg_cancel_backend pg_char_to_encoding pg_collation_for pg_collation_is_visible pg_column_size pg_conf_load_time pg_conversion_is_visible pg_create_restore_point pg_current_xlog_insert_location pg_current_xlog_location pg_cursor pg_database_size pg_describe_object pg_encoding_max_length pg_encoding_to_char pg_export_snapshot pg_extension_config_dump pg_extension_update_paths pg_function_is_visible pg_get_constraintdef pg_get_expr pg_get_function_arguments pg_filenode_relation pg_indexam_has_property pg_get_function_identity_arguments pg_get_function_result pg_get_functiondef pg_get_indexdef pg_get_keywords pg_get_ruledef pg_get_serial_sequence pg_get_triggerdef pg_get_userbyid pg_get_viewdef pg_has_role pg_indexes_size pg_is_in_recovery pg_is_other_temp_schema pg_is_xlog_replay_paused pg_last_xact_replay_timestamp pg_last_xlog_receive_location pg_last_xlog_replay_location pg_listening_channels pg_lock_status pg_ls_dir pg_my_temp_schema pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send pg_notify pg_opclass_is_visible pg_operator_is_visible pg_opfamily_is_visible pg_options_to_table pg_index_has_property pg_postmaster_start_time pg_prepared_statement pg_prepared_xact pg_read_binary_file pg_read_file pg_relation_filenode pg_relation_filepath pg_relation_size pg_reload_conf pg_rotate_logfile pg_sequence_parameters pg_show_all_settings pg_size_pretty pg_sleep pg_start_backup pg_index_column_has_property pg_stat_clear_snapshot pg_stat_file pg_stat_get_activity pg_stat_get_analyze_count pg_stat_get_autoanalyze_count pg_stat_get_autovacuum_count pg_get_object_address pg_identify_object_as_address pg_stat_get_backend_activity pg_stat_get_backend_activity_start pg_stat_get_backend_client_addr pg_stat_get_backend_client_port pg_stat_get_backend_dbid pg_stat_get_backend_idset pg_stat_get_backend_pid pg_stat_get_backend_start pg_stat_get_backend_userid pg_stat_get_backend_waiting pg_stat_get_backend_xact_start pg_stat_get_bgwriter_buf_written_checkpoints pg_stat_get_bgwriter_buf_written_clean pg_stat_get_bgwriter_maxwritten_clean pg_stat_get_bgwriter_requested_checkpoints pg_stat_get_bgwriter_stat_reset_time pg_stat_get_bgwriter_timed_checkpoints pg_stat_get_blocks_fetched pg_stat_get_blocks_hit pg_stat_get_buf_alloc pg_stat_get_buf_fsync_backend pg_stat_get_buf_written_backend pg_stat_get_checkpoint_sync_time pg_stat_get_checkpoint_write_time pg_stat_get_db_blk_read_time pg_stat_get_db_blk_write_time pg_stat_get_db_blocks_fetched pg_stat_get_db_blocks_hit pg_stat_get_db_conflict_all pg_stat_get_db_conflict_bufferpin pg_stat_get_db_conflict_lock pg_stat_get_db_conflict_snapshot pg_stat_get_db_conflict_startup_deadlock pg_stat_get_db_conflict_tablespace pg_stat_get_db_deadlocks pg_stat_get_db_numbackends pg_stat_get_db_stat_reset_time pg_stat_get_db_temp_bytes pg_stat_get_db_temp_files pg_stat_get_db_tuples_deleted pg_stat_get_db_tuples_fetched pg_stat_get_db_tuples_inserted pg_stat_get_db_tuples_returned pg_stat_get_db_tuples_updated pg_stat_get_db_xact_commit pg_stat_get_db_xact_rollback pg_stat_get_dead_tuples pg_stat_get_function_calls pg_stat_get_function_self_time pg_stat_get_function_total_time pg_stat_get_last_analyze_time pg_stat_get_last_autoanalyze_time pg_stat_get_last_autovacuum_time pg_stat_get_last_vacuum_time pg_stat_get_live_tuples pg_stat_get_numscans pg_stat_get_tuples_deleted pg_stat_get_tuples_fetched pg_stat_get_tuples_hot_updated pg_stat_get_tuples_inserted pg_stat_get_tuples_returned pg_stat_get_tuples_updated pg_stat_get_vacuum_count pg_stat_get_wal_senders pg_stat_get_xact_blocks_fetched pg_stat_get_xact_blocks_hit pg_stat_get_xact_function_calls pg_stat_get_xact_function_self_time pg_stat_get_xact_function_total_time pg_stat_get_xact_numscans pg_stat_get_xact_tuples_deleted pg_stat_get_xact_tuples_fetched pg_stat_get_xact_tuples_hot_updated pg_stat_get_xact_tuples_inserted pg_stat_get_xact_tuples_returned pg_stat_get_xact_tuples_updated pg_stat_reset pg_stat_reset_shared pg_stat_reset_single_function_counters pg_stat_reset_single_table_counters pg_stop_backup pg_switch_xlog pg_table_is_visible pg_table_size pg_tablespace_databases pg_tablespace_location pg_tablespace_size pg_terminate_backend pg_timezone_abbrevs pg_timezone_names pg_total_relation_size pg_trigger_depth pg_try_advisory_lock pg_try_advisory_lock_shared pg_try_advisory_xact_lock pg_try_advisory_xact_lock_shared pg_ts_config_is_visible pg_ts_dict_is_visible pg_ts_parser_is_visible pg_ts_template_is_visible pg_type_is_visible pg_typeof pg_xact_commit_timestamp pg_last_committed_xact pg_xlog_location_diff pg_xlog_replay_pause pg_xlog_replay_resume pg_xlogfile_name pg_xlogfile_name_offset pgp_key_id pgp_pub_decrypt pgp_pub_decrypt_bytea pgp_pub_encrypt pgp_pub_encrypt_bytea pgp_sym_decrypt pgp_sym_decrypt_bytea pgp_sym_encrypt pgp_sym_encrypt_bytea pi plainto_tsquery plpgsql_call_handler plpgsql_inline_handler plpgsql_validator point point_above point_add point_below point_distance point_div point_eq point_horiz point_in point_left point_mul point_ne point_out point_recv point_right point_send point_sub point_vert poly_above poly_below poly_center poly_contain poly_contain_pt poly_contained poly_distance poly_in poly_left poly_npoints poly_out poly_overabove poly_overbelow poly_overlap poly_overleft poly_overright poly_recv poly_right poly_same poly_send polygon popen positionjoinsel positionsel postgresql_fdw_validator pow power prsd_end prsd_headline prsd_lextype prsd_nexttoken prsd_start pt_contained_circle pt_contained_poly querytree quote_nullable radians radius random range_adjacent range_after range_before range_cmp range_contained_by range_contains range_contains_elem range_eq range_ge range_gist_compress range_gist_consistent range_gist_decompress range_gist_penalty range_gist_picksplit range_gist_same range_gist_union range_gt range_in range_intersect range_le range_lt range_minus range_ne range_out range_overlaps range_overleft range_overright range_recv range_send range_typanalyze range_union rank record_eq record_ge record_gt record_in record_le record_lt record_ne record_out record_recv record_send regclass regclassin regclassout regclassrecv regclasssend regconfigin regconfigout regconfigrecv regconfigsend regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend regexeqjoinsel regexeqsel regexnejoinsel regexnesel regexp_matches regexp_replace regexp_split_to_array regexp_split_to_table regoperatorin regoperatorout regoperatorrecv regoperatorsend regoperin regoperout regoperrecv regopersend regprocedurein regprocedureout regprocedurerecv regproceduresend regprocin regprocout regprocrecv regprocsend regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy regtypein regtypeout regtyperecv regtypesend reltime reltimeeq reltimege reltimegt reltimein reltimele reltimelt reltimene reltimeout reltimerecv reltimesend reverse round row_number row_to_json scalargtjoinsel scalargtsel scalarltjoinsel scalarltsel session_user set_config set_masklen setseed setval setweight shell_in shell_out shift_jis_2004_to_euc_jis_2004 shift_jis_2004_to_utf8 shobj_description sign similar_escape sin sjis_to_euc_jp sjis_to_mic sjis_to_utf8 slope smgreq smgrin smgrne smgrout spg_kd_choose spg_kd_config spg_kd_inner_consistent spg_kd_picksplit spg_quad_choose spg_quad_config spg_quad_inner_consistent spg_quad_leaf_consistent spg_quad_picksplit spg_text_choose spg_text_config spg_text_inner_consistent spg_text_leaf_consistent spg_text_picksplit spgbeginscan spgbuild spgbuildempty spgbulkdelete spgcanreturn spgcostestimate spgendscan spggetbitmap spggettuple spginsert spgmarkpos spgoptions spgrescan spgrestrpos spgvacuumcleanup sqrt statement_timestamp stddev stddev_pop stddev_samp string_agg string_agg_finalfn string_agg_transfn string_to_array strip sum tan text text_ge text_gt text_larger text_le text_lt text_pattern_ge text_pattern_gt text_pattern_le text_pattern_lt text_smaller textanycat textcat texteq texticlike texticnlike texticregexeq texticregexne textin textlen textlike textne textnlike textout textrecv textregexeq textregexne textsend thesaurus_init thesaurus_lexize tideq tidge tidgt tidin tidlarger tidle tidlt tidne tidout tidrecv tidsend tidsmaller time time_cmp time_eq time_ge time_gt time_hash time_in time_larger time_le time_lt time_mi_interval time_mi_time time_ne time_out time_pl_interval time_recv time_send time_smaller time_transform timedate_pl timemi timenow timepl timestamp timestamp_cmp timestamp_cmp_date timestamp_cmp_timestamptz timestamp_eq timestamp_eq_date timestamp_eq_timestamptz timestamp_ge timestamp_ge_date timestamp_ge_timestamptz timestamp_gt timestamp_gt_date timestamp_gt_timestamptz timestamp_hash timestamp_in timestamp_larger timestamp_le timestamp_le_date timestamp_le_timestamptz timestamp_lt timestamp_lt_date timestamp_lt_timestamptz timestamp_mi timestamp_mi_interval timestamp_ne timestamp_ne_date timestamp_ne_timestamptz timestamp_out timestamp_pl_interval timestamp_recv timestamp_send timestamp_smaller timestamp_sortsupport timestamp_transform timestamptypmodin timestamptypmodout timestamptz timestamptz_cmp timestamptz_cmp_date timestamptz_cmp_timestamp timestamptz_eq timestamptz_eq_date timestamptz_eq_timestamp timestamptz_ge timestamptz_ge_date timestamptz_ge_timestamp timestamptz_gt timestamptz_gt_date timestamptz_gt_timestamp timestamptz_in timestamptz_larger timestamptz_le timestamptz_le_date timestamptz_le_timestamp timestamptz_lt timestamptz_lt_date timestamptz_lt_timestamp timestamptz_mi timestamptz_mi_interval timestamptz_ne timestamptz_ne_date timestamptz_ne_timestamp timestamptz_out timestamptz_pl_interval timestamptz_recv timestamptz_send timestamptz_smaller timestamptztypmodin timestamptztypmodout timetypmodin timetypmodout timetz timetz_cmp timetz_eq timetz_ge timetz_gt timetz_hash timetz_in timetz_larger timetz_le timetz_lt timetz_mi_interval timetz_ne timetz_out timetz_pl_interval timetz_recv timetz_send timetz_smaller timetzdate_pl timetztypmodin timetztypmodout timezone tinterval tintervalct tintervalend tintervaleq tintervalge tintervalgt tintervalin tintervalle tintervalleneq tintervallenge tintervallengt tintervallenle tintervallenlt tintervallenne tintervallt tintervalne tintervalout tintervalov tintervalrecv tintervalrel tintervalsame tintervalsend tintervalstart to_json to_tsquery to_tsvector transaction_timestamp trigger_out trunc ts_debug ts_headline ts_lexize ts_match_qv ts_match_tq ts_match_tt ts_match_vq ts_parse ts_rank ts_rank_cd ts_rewrite ts_stat ts_token_type ts_typanalyze tsmatchjoinsel tsmatchsel tsq_mcontained tsq_mcontains tsquery_and tsquery_cmp tsquery_eq tsquery_ge tsquery_gt tsquery_le tsquery_lt tsquery_ne tsquery_not tsquery_or tsqueryin tsqueryout tsqueryrecv tsquerysend tsrange tsrange_subdiff tstzrange tstzrange_subdiff tsvector_cmp tsvector_concat tsvector_eq tsvector_ge tsvector_gt tsvector_le tsvector_lt tsvector_ne tsvectorin tsvectorout tsvectorrecv tsvectorsend txid_current txid_current_snapshot txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send txid_snapshot_xip txid_snapshot_xmax txid_snapshot_xmin txid_visible_in_snapshot uhc_to_utf8 unknownin unknownout unknownrecv unknownsend unnest upper_inc upper_inf utf8_to_ascii utf8_to_big5 utf8_to_euc_cn utf8_to_euc_jis_2004 utf8_to_euc_jp utf8_to_euc_kr utf8_to_euc_tw utf8_to_gb18030 utf8_to_gbk utf8_to_iso8859 utf8_to_iso8859_1 utf8_to_johab utf8_to_koi8r utf8_to_koi8u utf8_to_shift_jis_2004 utf8_to_sjis utf8_to_uhc utf8_to_win uuid_cmp uuid_eq uuid_ge uuid_gt uuid_hash uuid_in uuid_le uuid_lt uuid_ne uuid_out uuid_recv uuid_send var_pop var_samp varbit varbit_in varbit_out varbit_recv varbit_send varbit_transform varbitcmp varbiteq varbitge varbitgt varbitle varbitlt varbitne varbittypmodin varbittypmodout varchar varying varchar_transform varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout variance version void_in void_out void_recv void_send width width_bucket win1250_to_latin2 win1250_to_mic win1251_to_iso win1251_to_koi8r win1251_to_mic win1251_to_win866 win866_to_iso win866_to_koi8r win866_to_mic win866_to_win1251 win_to_utf8 xideq xideqint4 xidin xidout xidrecv xidsend xml xml_in xmlcomment xpath xpath_exists table_to_xmlschema query_to_xmlschema cursor_to_xmlschema table_to_xml_and_xmlschema query_to_xml_and_xmlschema schema_to_xml schema_to_xmlschema schema_to_xml_and_xmlschema database_to_xml database_to_xmlschema xmlroot database_to_xml_and_xmlschema table_to_xml query_to_xmlcursor_to_xml xmlcomment xmlconcat xmlelement xmlforest xml_is_well_formed_content xml_is_well_formed_document xml_is_well_formed xml_out xml_recv xml_send xmlagg xmlpi query_to_xml cursor_to_xml xmlserialize xmltable ); my @copy_keywords = ( 'STDIN', 'STDOUT' ); my %symbols = ( '=' => '=', '<' => '<', '>' => '>', '|' => '|', ',' => ',', '.' => '.', '+' => '+', '-' => '-', '*' => '*', '/' => '/', '!=' => '!=', '%' => '%', '<=' => '<=', '>=' => '>=', '<>' => '<>' ); my @brackets = ( '(', ')' ); # All setting and modification of dicts is done, can set them now to $self->{'dict'}->{...} $self->{ 'dict' }->{ 'pg_keywords' } = \@pg_keywords; $self->{ 'dict' }->{ 'pg_types' } = \@pg_types; $self->{ 'dict' }->{ 'sql_keywords' } = \@sql_keywords; $self->{ 'dict' }->{ 'pg_functions' } = (); map { $self->{ 'dict' }->{ 'pg_functions' }{$_} = ''; } @pg_functions; $self->{ 'dict' }->{ 'copy_keywords' } = \@copy_keywords; $self->{ 'dict' }->{ 'symbols' } = \%symbols; $self->{ 'dict' }->{ 'brackets' } = \@brackets; return; } =head2 _remove_dynamic_code Internal function used to hide dynamic code in plpgsql to the parser. The original values are restored with function _restore_dynamic_code(). =cut sub _remove_dynamic_code { my ($self, $str, $code_sep) = @_; my @dynsep = (); push(@dynsep, $code_sep) if ($code_sep && $code_sep ne "'"); # Try to auto detect the string separator if none are provided. # Note that default single quote separtor is natively supported. if ($#dynsep == -1) { # if a dollar sign is found after EXECUTE then the following string # until an other dollar is found will be understand as a text delimiter @dynsep = $$str =~ /EXECUTE\s+(\$[^\$\s]*\$)/igs; } my $idx = 0; foreach my $sep (@dynsep) { while ($$str =~ s/(\Q$sep\E.*?\Q$sep\E)/TEXTVALUE$idx/s) { $self->{dynamic_code}{$idx} = $1; $idx++; } } # Replace any COMMENT constant between single quote while ($$str =~ s/IS\s+('(?:.*?)')\s*;/IS TEXTVALUE$idx;/s) { $self->{dynamic_code}{$idx} = $1; $idx++; } } =head2 _restore_dynamic_code Internal function used to restore plpgsql dynamic code in plpgsql that was removed by the _remove_dynamic_code() method. =cut sub _restore_dynamic_code { my ($self, $str) = @_; $$str =~ s/TEXTVALUE(\d+)/$self->{dynamic_code}{$1}/gs; } =head2 _quote_operator Internal function used to quote operator with multiple character to be tokenized as a single word. The original values are restored with function _restore_operator(). =cut sub _quote_operator { my ($self, $str) = @_; while ($$str =~ s/((?:CREATE|DROP|ALTER)\s+OPERATOR\s+(?:IF\s+EXISTS)?)\s*((:?[a-z0-9]+\.)?[\+\-\*\/<>=\~\!\@\#\%\^\&\|\`\?]+)\s*/$1 "$2" /is) { push(@{ $self->{operator} }, $2) if (!grep(/^\Q$2\E$/, @{ $self->{operator} })); } my $idx = 0; while ($$str =~ s/(NEGATOR|COMMUTATOR)\s*=\s*([^,\)\s]+)/\U$1\E$idx/is) { $self->{uc($1)}{$idx} = "$1 = $2"; $idx++; } } =head2 _restore_operator Internal function used to restore operator that was removed by the _quote_operator() method. =cut sub _restore_operator { my ($self, $str) = @_; foreach my $op (@{ $self->{operator} }) { $$str =~ s/"$op"/$op/gs; } if (exists $self->{COMMUTATOR}) { $$str =~ s/COMMUTATOR(\d+)/$self->{COMMUTATOR}{$1}/igs; } if (exists $self->{NEGATOR}) { $$str =~ s/NEGATOR(\d+)/$self->{NEGATOR}{$1}/igs; } } =head2 _quote_comment_stmt Internal function used to replace constant in a COMMENT statement to be tokenized as a single word. The original values are restored with function _restore_comment_stmt(). =cut sub _quote_comment_stmt { my ($self, $str) = @_; my $idx = 0; while ($$str =~ s/(COMMENT\s+ON\s+(?:.*?)\s+IS)\s+(\$[^;]+?\$)\s*;/$1 PGF_CMTSTR$idx;/is) { $self->{comment_str}{$idx} = $2; $idx++; } } =head2 _restore_comment_stmt Internal function used to restore comment string that was removed by the _quote_comment_stmt() method. =cut sub _restore_comment_stmt { my ($self, $str) = @_; if (exists $self->{comment_str}) { $$str =~ s/PGF_CMTSTR(\d+)/$self->{comment_str}{$1}/igs; } } =head2 _remove_comments Internal function used to remove comments in SQL code to simplify the work of the wrap_lines. Comments must be restored with the _restore_comments() method. =cut sub _remove_comments { my $self = shift; my $idx = 0; while ($self->{ 'content' } =~ s/(\/\*(.*?)\*\/)/PGF_COMMENT${idx}A/s) { $self->{'comments'}{"PGF_COMMENT${idx}A"} = $1; $idx++; } my @lines = split(/\n/, $self->{ 'content' }); for (my $j = 0; $j <= $#lines; $j++) { $lines[$j] //= ''; # Extract multiline comments as a single placeholder my $old_j = $j; my $cmt = ''; while ($j <= $#lines && $lines[$j] =~ /^(\s*\-\-.*)$/) { $cmt .= "$1\n"; $j++; } if ( $j > $old_j ) { chomp($cmt); $lines[$old_j] =~ s/^(\s*\-\-.*)$/PGF_COMMENT${idx}A/; $self->{'comments'}{"PGF_COMMENT${idx}A"} = $cmt; $idx++; $j--; while ($j > $old_j) { delete $lines[$j]; $j--; } } if ($lines[$j] =~ s/(\s*\-\-.*)$/PGF_COMMENT${idx}A/) { $self->{'comments'}{"PGF_COMMENT${idx}A"} = $1; chomp($self->{'comments'}{"PGF_COMMENT${idx}A"}); $idx++; } # Mysql supports differents kinds of comment's starter if ( ($lines[$j] =~ s/(\s*COMMENT\s+'.*)$/PGF_COMMENT${idx}A/) || ($lines[$j] =~ s/(\s*\# .*)$/PGF_COMMENT${idx}A/) ) { $self->{'comments'}{"PGF_COMMENT${idx}A"} = $1; chomp($self->{'comments'}{"PGF_COMMENT${idx}A"}); # Normalize start of comment $self->{'comments'}{"PGF_COMMENT${idx}A"} =~ s/^(\s*)COMMENT/$1\-\- /; $self->{'comments'}{"PGF_COMMENT${idx}A"} =~ s/^(\s*)\#/$1\-\- /; $idx++; } } $self->{ 'content' } = join("\n", @lines); # Replace subsequent comment by a single one while ($self->{ 'content' } =~ s/(PGF_COMMENT\d+A\s*PGF_COMMENT\d+A)/PGF_COMMENT${idx}A/s) { $self->{'comments'}{"PGF_COMMENT${idx}A"} = $1; $idx++; } } =head2 _restore_comments (OBSOLETE) Internal function used to restore comments in SQL code that was removed by the _remove_comments() method. =cut sub _restore_comments { my $self = shift; while ($self->{ 'content' } =~ s/(PGF_COMMENT\d+A)[\n]*/$self->{'comments'}{$1}\n/s) { delete $self->{'comments'}{$1}; }; } =head2 wrap_lines Internal function used to wrap line at a certain length. =cut sub wrap_lines { my $self = shift; return if (!$self->{'wrap_limit'} || !$self->{ 'content' }); $self->_remove_comments(); my @lines = split(/\n/, $self->{ 'content' }); $self->{ 'content' } = ''; foreach my $l (@lines) { # Remove and store the indentation of the line my $indent = ''; if ($l =~ s/^(\s+)//) { $indent = $1; } if (length($l) > $self->{'wrap_limit'} + ($self->{'wrap_limit'}*10/100)) { $Text::Wrap::columns = $self->{'wrap_limit'}; my $t = wrap($indent, " "x$self->{ 'spaces' } . $indent, $l); $self->{ 'content' } .= "$t\n"; } else { $self->{ 'content' } .= $indent . "$l\n"; } } $self->_restore_comments() if ($self->{ 'content' }); return; } sub _dump_var { my $self = shift; foreach my $v (sort keys %{$self}) { next if ($v !~ /^_/); if ($self->{$v} =~ /ARRAY/) { print STDERR "$v => (", join(',', @{$self->{$v}}), ")\n"; } else { print STDERR "$v => $self->{$v}\n"; } } } =head1 AUTHOR pgFormatter is an original work from Gilles Darold =head1 BUGS Please report any bugs or feature requests to: https://github.com/darold/pgFormatter/issues =head1 COPYRIGHT Copyright 2012-2020 Gilles Darold. All rights reserved. =head1 LICENSE pgFormatter is free software distributed under the PostgreSQL Licence. A modified version of the SQL::Beautify Perl Module is embedded in pgFormatter with copyright (C) 2009 by Jonas Kramer and is published under the terms of the Artistic License 2.0. =cut 1; pgFormatter-4.2/lib/pgFormatter/CGI.pm000077500000000000000000000627611361326045100176710ustar00rootroot00000000000000package pgFormatter::CGI; use strict; use warnings; use warnings qw( FATAL ); use Encode qw( decode ); =head1 NAME pgFormatter::CGI - Implementation of CGI-BIN script to format SQL queries. =head1 VERSION Version 4.2 =cut # Version of pgFormatter our $VERSION = '4.2'; use pgFormatter::Beautify; use File::Basename; use CGI; =head1 SYNOPSIS This module is called by pg_format program, when it detects it is run in CGI environment. In such case all control over flow is passed to this module by calling: my $program = pgFormatter::CGI->new(); $program->run() =head1 FUNCTIONS =head2 new Object constructor, calls L method to set default values for various parameters. =cut sub new { my $class = shift; my $self = bless {}, $class; $self->set_config(); return $self; } =head2 run Wraps all work related to generating html page. It calls set of functions to get CGI object, receive parameters from request, sanitize them, beautify query (if provided) and print ghtml. =cut sub run { my $self = shift; $self->get_cgi(); $self->get_params(); $self->sanitize_params(); $self->print_headers(); $self->beautify_query(); $self->print_body(); $self->print_footer(); return; } =head2 get_cgi Creates CGI object, and sets POST size limit =cut sub get_cgi { my $self = shift; $self->{ 'cgi' } = CGI->new(); $CGI::POST_MAX = $self->{ 'maxlength' }; return; } =head2 Sets config for the page and beautifier. URLs, names of auxiliary files, configuration for pgFormatter::Beautify module. =cut sub set_config { my $self = shift; $self->{ 'program_name' } = 'pgFormatter'; $self->{ 'program_name' } =~ s/\.[^\.]+$//; $self->{ 'maxlength' } = 100000; $self->{ 'spaces' } = 4; $self->{ 'outfile' } = ''; $self->{ 'outdir' } = ''; $self->{ 'help' } = ''; $self->{ 'version' } = ''; $self->{ 'nocomment' } = 0; $self->{ 'nogrouping' } = 0; $self->{ 'colorize' } = 1; $self->{ 'uc_keyword' } = 2; $self->{ 'uc_function' } = 0; $self->{ 'uc_type' } = 1; $self->{ 'debug' } = 0; $self->{ 'content' } = ''; $self->{ 'original_content' } = ''; $self->{ 'show_example' } = 0; $self->{ 'project_url' } = 'https://github.com/darold/pgFormatter'; $self->{ 'service_url' } = ''; $self->{ 'download_url' } = 'http://sourceforge.net/projects/pgformatter/'; $self->{ 'anonymize' } = 0; $self->{ 'separator' } = ''; $self->{ 'comma' } = 'end'; $self->{ 'format' } = 'html'; $self->{ 'comma_break' } = 0; $self->{ 'format_type' } = 0; $self->{ 'wrap_after' } = 0; # Filename to load tracker and ad to be included respectively in the # HTML head and the bottom of the HTML page. $self->{ 'bottom_ad_file' } = 'bottom_ad_file.txt'; $self->{ 'head_track_file' } = 'head_track_file.txt'; # CSS file to load if exists to override default style $self->{ 'css_file' } = 'custom_css_file.css'; return; } =head2 Loads, and returns, default CSS from __DATA__. =cut sub default_styles { my $shift; local $/; my $style = ; return $style; } =head2 Gets values of parameters, and possibly uploaded file from CGI object to structures in $self. =cut sub get_params { my $self = shift; return unless $self->{ 'cgi' }->param; # shortcut my $cgi = $self->{ 'cgi' }; for my $param_name ( qw( colorize spaces uc_keyword uc_function uc_type content nocomment nogrouping show_example anonymize separator comma comma_break format_type wrap_after original_content) ) { $self->{ $param_name } = $cgi->param( $param_name ) if defined $cgi->param( $param_name ); } my $filename = $cgi->param( 'filetoload' ); return unless $filename; my $type = $cgi->uploadInfo( $filename )->{ 'Content-Type' }; if ( $type eq 'text/plain' || $type eq 'text/x-sql' || $type eq 'application/sql' || $type eq 'application/octet-stream') { local $/ = undef; my $fh = $cgi->upload( 'filetoload' ); my $tmpfilename = $cgi->tmpFileName( $fh ); if (!-T $tmpfilename) { $self->{ 'colorize' } = 0; $self->{ 'uc_keyword' } = 0; $self->{ 'uc_function' }= 0; $self->{ 'uc_type' } = 0; $self->{ 'content' } = "FATAL: Only text files are supported! Found content-type: $type"; } else { binmode $fh; $self->{ 'content' } = <$fh>; } } else { $self->{ 'colorize' } = 0; $self->{ 'uc_keyword' } = 0; $self->{ 'uc_function' }= 0; $self->{ 'uc_type' } = 0; $self->{ 'content' } = "FATAL: Only text files are supported! Found content-type: $type"; } return; } =head2 sanitize_params Overrides parameter values if given values were not within acceptable ranges. =cut sub sanitize_params { my $self = shift; $self->{ 'colorize' } = 1 if $self->{ 'colorize' } !~ /^(0|1)$/; $self->{ 'spaces' } = 4 if $self->{ 'spaces' } !~ /^\d{1,2}$/; $self->{ 'uc_keyword' } = 2 if $self->{ 'uc_keyword' } && ( $self->{ 'uc_keyword' } !~ /^(0|1|2|3)$/ ); $self->{ 'uc_function' } = 0 if $self->{ 'uc_function' } && ( $self->{ 'uc_function' } !~ /^(0|1|2|3)$/ ); $self->{ 'uc_type' } = 1 if $self->{ 'uc_type' } && ( $self->{ 'uc_type' } !~ /^(0|1|2|3)$/ ); $self->{ 'nocomment' } = 0 if $self->{ 'nocomment' } !~ /^(0|1)$/; $self->{ 'nogrouping' } = 0 if $self->{ 'nogrouping' } !~ /^(0|1)$/; $self->{ 'show_example' } = 0 if $self->{ 'show_example' } !~ /^(0|1)$/; $self->{ 'separator' } = '' if ($self->{ 'separator' } eq "'" or length($self->{ 'separator' }) > 6); $self->{ 'comma' } = 'end' if ($self->{ 'comma' } ne 'start'); $self->{ 'comma_break' } = 0 if ($self->{ 'comma_break' } !~ /^(0|1)$/); $self->{ 'format_type' } = 0 if ($self->{ 'format_type' } !~ /^(0|1)$/); $self->{ 'wrap_after' } = 0 if ($self->{ 'wrap_after' } !~ /^\d{1,2}$/); if ( $self->{ 'show_example' } ) { $self->{ 'content' } = q{ SELECT DISTINCT (current_database())::information_schema.sql_identifier AS view_catalog, (nv.nspname)::information_schema.sql_identifier AS view_schema, (v.relname)::information_schema.sql_identifier AS view_name, (current_database())::information_schema.sql_identifier AS table_catalog, (nt.nspname)::information_schema.sql_identifier AS table_schema, (t.relname)::information_schema.sql_identifier AS table_name FROM pg_namespace nv, pg_class v, pg_depend dv, pg_depend dt, pg_class t, pg_namespace nt WHERE ((((((((((((((nv.oid = v.relnamespace) AND (v.relkind = 'v'::"char")) AND (v.oid = dv.refobjid)) AND (dv.refclassid = ('pg_class'::regclass)::oid)) AND (dv.classid = ('pg_rewrite'::regclass)::oid)) AND (dv.deptype = 'i'::"char")) AND (dv.objid = dt.objid)) AND (dv.refobjid <> dt.refobjid)) AND (dt.classid = ('pg_rewrite'::regclass)::oid)) AND (dt.refclassid = ('pg_class'::regclass)::oid)) AND (dt.refobjid = t.oid)) AND (t.relnamespace = nt.oid)) AND (t.relkind = ANY (ARRAY['r'::"char", 'v'::"char"]))) AND pg_has_role(t.relowner, 'USAGE'::text)) ORDER BY (current_database())::information_schema.sql_identifier, (nv.nspname)::information_schema.sql_identifier, (v.relname)::information_schema.sql_identifier, (current_database())::information_schema.sql_identifier, (nt.nspname)::information_schema.sql_identifier, (t.relname)::information_schema.sql_identifier; }; $self->{ 'original_content' } = $self->{ 'content' }; } if (!$self->{ 'original_content' }) { $self->{ 'original_content' } = $self->{ 'content' }; } else { $self->{ 'content' } = $self->{ 'original_content' }; } $self->{ 'content' } = substr( $self->{ 'content' }, 0, $self->{ 'maxlength' } ); return; } =head2 beautify_query Runs beautification on provided query, and stores new version in $self->{'content'} =cut sub beautify_query { my $self = shift; return if $self->{ 'show_example' }; return unless $self->{ 'content' }; my %args; $args{ 'no_comments' } = 1 if $self->{ 'nocomment' }; $args{ 'spaces' } = $self->{ 'spaces' }; $args{ 'uc_keywords' } = $self->{ 'uc_keyword' }; $args{ 'uc_functions' } = $self->{ 'uc_function' }; $args{ 'uc_types' } = $self->{ 'uc_type' }; $args{ 'separator' } = $self->{ 'separator' }; $args{ 'comma' } = $self->{ 'comma' }; $args{ 'format' } = $self->{ 'format' }; $args{ 'colorize' } = $self->{ 'colorize' }; $args{ 'comma_break' } = $self->{ 'comma_break' }; $args{ 'format_type' } = 1 if ($self->{ 'format_type' }); $args{ 'wrap_after' } = $self->{ 'wrap_after' }; $args{ 'no_grouping' } = 1 if $self->{ 'nogrouping' }; $self->{ 'content' } = &remove_extra_parenthesis($self->{ 'content' } ) if ($self->{ 'content' } ); my $beautifier = pgFormatter::Beautify->new( %args ); $beautifier->query( $self->{ 'content' } ); $beautifier->anonymize() if $self->{ 'anonymize' }; $beautifier->beautify(); $self->{ 'content' } = $beautifier->content(); return; } sub remove_extra_parenthesis { my $str = shift; while ($str =~ s/\(\s*\(([^\(\)]+)\)\s*\)/($1)/gs) {}; while ($str =~ s/\(\s*\(([^\(\)]+)\)\s*AND\s*\(([^\(\)]+)\)\s*\)/($1 AND $2)/igs) {}; while ($str =~ s/\(\s*\(\s*\(([^\(\)]+\)[^\(\)]+\([^\(\)]+)\)\s*\)\s*\)/(($1))/gs) {}; return $str; } =head2 Outputs body of the page. =cut sub print_body { my $self = shift; my $chk_nocomment = $self->{ 'nocomment' } ? 'checked="checked" ' : ''; my $chk_colorize = $self->{ 'colorize' } ? 'checked="checked" ' : ''; my $chk_anonymize = $self->{ 'anonymize' } ? 'checked="checked" ' : ''; my $chk_comma = $self->{ 'comma' } eq 'start' ? 'checked="checked" ' : ''; my $chk_comma_break = $self->{ 'comma_break' } ? 'checked="checked" ' : ''; my $chk_format_type = $self->{ 'format_type' } ? 'checked="checked" ' : ''; my $chk_nogrouping = $self->{ 'nogrouping' } ? 'checked="checked" ' : ''; my %kw_toggle = ( 0 => '', 1 => '', 2 => '', 3 => '' ); $kw_toggle{ $self->{ 'uc_keyword' } } = ' selected="selected"'; my %fct_toggle = ( 0 => '', 1 => '', 2 => '', 3 => '' ); $fct_toggle{ $self->{ 'uc_function' } } = ' selected="selected"'; my %typ_toggle = ( 0 => '', 1 => '', 2 => '', 3 => '' ); $typ_toggle{ $self->{ 'uc_type' } } = ' selected="selected"'; my $service_url = $self->{ 'service_url' } || $self->{ 'cgi' }->url; print <<_END_OF_HTML_;
General







Keywords & functions
Keywords:
Functions:
Datatypes:

Indentation
Indentation: spaces
Wrap after:   cols

     

_END_OF_HTML_ if ( ( $self->{ 'show_example' } ) || ( !$self->{ 'content' } ) ) { $self->{ 'content' } = 'Enter your SQL code here...' unless $self->{ 'content' }; print qq{"; } else { print qq{
$self->{ 'content' }
}; } print qq{}; print qq{
}; return; } =head2 print_footer Outputs footer of the page =cut sub print_footer { my $self = shift; # Add external file with html code at bottom of the page # used to display ads or anything else below the text area my $ad_content = $self->_load_optional_file( $self->{ 'bottom_ad_file' } ); $ad_content ||= '
'; print $ad_content; print qq{

 

}; print " \n"; return; } =head2 _load_optional_file Helper function to try to load file. If it succeeds, it returns file content, it not, it returns empty string (instead of dying). =cut sub _load_optional_file { my $self = shift; my $filename = shift; return '' unless -f $filename; return '' if -z $filename; open my $in, '<', $filename or return ''; local $/ = undef; my $content = <$in>; close $in; return $content; } =head2 print_headers Outputs page headers - both HTTP level headers, and HTML. =cut sub print_headers { my $self = shift; print $self->{ 'cgi' }->header(-charset => 'utf-8'); my $date = localtime( time ); # Add external file content into the HTML header, used to add tracker # information or anything else between the tags. my $track_content = $self->_load_optional_file( $self->{ 'head_track_file' } ); my $style_content = $self->_load_optional_file( $self->{ 'css_file' } ); $style_content = $self->default_styles if '' eq $style_content; print <<_END_OF_HTML_HEADER_; $self->{ 'program_name' } $track_content

pgFormatter

Free Online version of $self->{ 'program_name' } a PostgreSQL SQL syntax beautifier (no line limit here up to $self->{ 'maxlength' } characters). This SQL formatter/beautifier supports keywords from SQL-92, SQL-99, SQL-2003, SQL-2008, SQL-2011 and PostgreSQL specifics keywords. May works with any other databases too.
_END_OF_HTML_HEADER_ return; } =head1 DATA __DATA__ section (at the end of this file) is used to store default CSS Style, so that it will not "pollute" Perl code, and will not need to be indented. =head1 AUTHOR pgFormatter is an original work from Gilles Darold =head1 BUGS Please report any bugs or feature requests to: https://github.com/darold/pgFormatter/issues =head1 COPYRIGHT Copyright 2012-2020 Gilles Darold. All rights reserved. =head1 LICENSE pgFormatter is free software distributed under the PostgreSQL Licence. A modified version of the SQL::Beautify Perl Module is embedded in pgFormatter with copyright (C) 2009 by Jonas Kramer and is published under the terms of the Artistic License 2.0. =cut __DATA__ body { background-color:#262626; margin-top:0px; font-family: Lucida Sans, Arial, Helvetica, sans-serif; font-size: 18px; color: #888888; height: 100% !important; background-position:top center; background-attachment:fixed; } a { text-decoration: none; color: #000000; } a:hover { text-decoration:underline; color: #000000; } h1 { font-family: Lucida Sans, sans-serif; font-size: 38px; color:#ff7400; font-weight: bold; padding:5px; margin:3px 3px 3px 3px; border-radius:6px; -moz-border-radius:10px; -webkit-border-radius:10px; box-shadow:3px 3px 6px 2px #A9A9A9; -moz-box-shadow:3px 3px 6px 2px #A9A9A9; -webkit-box-shadow:3px 3px 6px #A9A9A9; } textarea#sqlcontent { width: 1000px; height: 400px; border: 3px solid #cccccc; padding: 5px; font-family: Tahoma, sans-serif; font-size: 14px; background-position: bottom right; background-repeat: no-repeat; background: #f5f3de; border-radius:6px; -moz-border-radius:10px; -webkit-border-radius:10px; box-shadow:3px 3px 6px 2px #A9A9A9; -moz-box-shadow:3px 3px 6px 2px #A9A9A9; -webkit-box-shadow:3px 3px 6px #A9A9A9; } div#sql { width: 1000px; height: 400px; border: 3px solid #cccccc; padding: 5px; overflow: auto; font-family:monospace; font-size: 14px; float: left; text-align: left; background-position: bottom right; background-repeat: no-repeat; background: #f5f3de; white-space: pre; border-radius:6px; -moz-border-radius:10px; -webkit-border-radius:10px; box-shadow:3px 3px 6px 2px #A9A9A9; -moz-box-shadow:3px 3px 6px 2px #A9A9A9; -webkit-box-shadow:3px 3px 6px #A9A9A9; } .sql .kw1 {color: #993333; font-weight: bold;} .sql .kw1_u {color: #993333; font-weight: bold; text-transform: uppercase;} .sql .kw1_l {color: #993333; font-weight: bold; text-transform: lowercase;} .sql .kw1_c {color: #993333; font-weight: bold; text-transform: capitalize;} .sql .kw2 {color: #993333; font-style: italic;} .sql .kw2_u {color: #993333; font-style: italic; text-transform: uppercase;} .sql .kw2_l {color: #993333; font-style: italic; text-transform: lowercase;} .sql .kw2_c {color: #993333; font-style: italic; text-transform: capitalize;} .sql .kw3 {color: #993333;} .sql .kw3_u {color: #993333; text-transform: uppercase;} .sql .kw3_l {color: #993333; text-transform: lowercase;} .sql .kw3_c {color: #993333; text-transform: capitalize;} .sql .br0 {color: #66cc66;} .sql .br1 {color: #3b3ba2;} .sql .sy0 {color: #000000;} .sql .st0 {color: #ff0000;} .sql .nu0 {color: #cc66cc;} div.footer { width: 1020px; font: 14px Helvetica, Arial, sans-serif;clear: both; height:40px; color: #000000; padding: 13px 0px 0 0; margin-left: auto; margin-right: auto; text-align: center; background-color: #ff7400; } div.footer a strong { color: #eeeeee; font-weight: bold;} div.footer a, #footer a:visited { color: #eeeeee; } div.footer a:hover { color: #eeeeee; } div.smaller { font: 11px Helvetica, Arial, sans-serif;clear: both; color: #000000; padding:13px 0px 0 0;margin-left: auto; margin-right: auto; text-align: center; background-color: #ff7400; } #options { width: 250px; height: 400px; margin:3px 3px 3px 3px; padding:2 2px; font-size: 14px; float: left; text-align: left; color: #2e3436; border-radius:6px; } #options fieldset { border: 1px solid #dddddd; margin:3px 3px 3px 3px; background: #ff7400; border-radius:6px; -moz-border-radius:10px; -webkit-border-radius:10px; box-shadow:3px 3px 6px 2px #A9A9A9; -moz-box-shadow:3px 3px 6px 2px #A9A9A9; -webkit-box-shadow:3px 3px 6px #A9A9A9; } #options fieldset legend { border: 1px solid #dddddd; margin-bottom: .6em; background: #ff7400; border-radius:6px; -moz-border-radius:10px; -webkit-border-radius:10px; box-shadow:3px 3px 6px 2px #A9A9A9; -moz-box-shadow:3px 3px 6px 2px #A9A9A9; -webkit-box-shadow:3px 3px 6px #A9A9A9; } #options input, select, button { border: 1px solid #dddddd; background: #f5f3de; border-radius:6px; -moz-border-radius:10px; -webkit-border-radius:10px; box-shadow:3px 3px 6px 2px #A9A9A9; -moz-box-shadow:3px 3px 6px 2px #A9A9A9; -webkit-box-shadow:3px 3px 6px #A9A9A9; } #format_code { border: 1px solid #dddddd; background: #f5f3de; padding: 5px; margin: 5px; border-radius:6px; -moz-border-radius:10px; -webkit-border-radius:10px; box-shadow:3px 3px 6px 2px #A9A9A9; -moz-box-shadow:3px 3px 6px 2px #A9A9A9; -webkit-box-shadow:3px 3px 6px #A9A9A9; font-weight: normal; font-size: 16px; } pgFormatter-4.2/lib/pgFormatter/CLI.pm000077500000000000000000000252561361326045100176740ustar00rootroot00000000000000package pgFormatter::CLI; use strict; use warnings; # UTF8 boilerplace, per http://stackoverflow.com/questions/6162484/why-does-modern-perl-avoid-utf-8-by-default/ use warnings qw( FATAL ); use utf8; use open qw( :std ); use Encode qw( decode ); =head1 NAME pgFormatter::CLI - Implementation of command line program to format SQL queries. =head1 VERSION Version 4.2 =cut # Version of pgFormatter our $VERSION = '4.2'; use autodie; use pgFormatter::Beautify; use Getopt::Long qw(:config no_ignore_case bundling); use File::Basename; =head1 SYNOPSIS This module is called by pg_format program, when it detects it is not being run in CGI environment. In such case all control over flow is passed to this module by calling: my $program = pgFormatter::CLI->new(); $program->run() =head1 FUNCTIONS =head2 new Object constructor, nothing fancy in here. =cut sub new { my $class = shift; return bless {}, $class; } =head2 run Wraps all work related to pg_format CLI program. This includes calling methods to read command line parameters, validate them, read query, beautify it, and output. =cut sub run { my $self = shift; $self->get_command_line_args(); $self->validate_args(); $self->logmsg( 'DEBUG', 'Starting to parse SQL file: %s', $self->{ 'cfg' }->{ 'input' } ); $self->load_sql(); $self->logmsg( 'DEBUG', 'Beautifying' ); $self->beautify(); if ($self->{'wrap_limit'}) { $self->logmsg( 'DEBUG', 'Wrap query' ); $self->wrap_lines(); } $self->logmsg( 'DEBUG', 'Writing output' ); $self->save_output(); return; } =head2 beautify Actually formats loaded query using pgFormatter::Beautify library. If necessary runs anonymization. =cut sub beautify { my $self = shift; my %args; $args{ 'no_comments' } = 1 if $self->{ 'cfg' }->{ 'nocomment' }; $args{ 'spaces' } = $self->{ 'cfg' }->{ 'spaces' }; $args{ 'uc_keywords' } = $self->{ 'cfg' }->{ 'keyword-case' }; $args{ 'uc_functions' } = $self->{ 'cfg' }->{ 'function-case' }; $args{ 'uc_types' } = $self->{ 'cfg' }->{ 'type-case' }; $args{ 'placeholder' } = $self->{ 'cfg' }->{ 'placeholder' }; $args{ 'separator' } = $self->{ 'cfg' }->{ 'separator' }; $args{ 'comma' } = $self->{ 'cfg' }->{ 'comma' }; $args{ 'comma_break' } = $self->{ 'cfg' }->{ 'comma-break' }; $args{ 'format' } = $self->{ 'cfg' }->{ 'format' }; $args{ 'maxlength' } = $self->{ 'cfg' }->{ 'maxlength' }; $args{ 'format_type' } = $self->{ 'cfg' }->{ 'format-type' }; $args{ 'wrap_limit' } = $self->{ 'cfg' }->{ 'wrap-limit' }; $args{ 'wrap_after' } = $self->{ 'cfg' }->{ 'wrap-after' }; $args{ 'space' } = $self->{ 'cfg' }->{ 'space' }; $args{ 'no_grouping' } = $self->{ 'cfg' }->{ 'nogrouping' }; if ($self->{ 'query' } && ($args{ 'maxlength' } && length($self->{ 'query' }) > $args{ 'maxlength' })) { $self->{ 'query' } = substr($self->{ 'query' }, 0, $args{ 'maxlength' }) } my $beautifier = pgFormatter::Beautify->new( %args ); $beautifier->query( $self->{ 'query' } ); $beautifier->anonymize() if $self->{ 'cfg' }->{ 'anonymize' }; $beautifier->beautify(); if ($self->{ 'cfg' }->{ 'wrap-limit' }) { $self->logmsg( 'DEBUG', 'Wrap query' ); $beautifier->wrap_lines(); } $self->{ 'ready' } = $beautifier->content(); return; } =head2 save_output Saves beautified query to whatever is output filehandle =cut sub save_output { my $self = shift; my $fh; # Thanks to "autodie" I don't have to check if open() worked. if ( $self->{ 'cfg' }->{ 'output' } ne '-' ) { $self->logmsg( 'DEBUG', 'Formatted SQL queries will be written to stdout' ); open $fh, '>', $self->{ 'cfg' }->{ 'output' }; } else { $fh = \*STDOUT; } print $fh $self->{ 'ready' }; close $fh; return; } =head2 logmsg Display message following the log level =cut sub logmsg { my $self = shift; my ( $level, $str, @args ) = @_; return if ( !$self->{ 'cfg' }->{ 'debug' } && ( $level eq 'DEBUG' ) ); printf STDERR "%s: $str\n", $level, @args; return; } =head2 show_help_and_die As name suggests - shows help page, with optional error message, and ends program. =cut sub show_help_and_die { my $self = shift; my ( $status, $format, @args ) = @_; if ( $format ) { $format =~ s/\s*$//; printf STDERR "Error: $format\n\n", @args; } my $program_name = basename( $0 ); my $help = qq{ Usage: $program_name [options] file.sql PostgreSQL SQL queries and PL/PGSQL code beautifier. Arguments: file.sql can be a file or use - to read query from stdin. Returning the SQL formatted to stdout or into a file specified with the -o | --output option. Options: -a | --anonymize : obscure all literals in queries, useful to hide confidential data before formatting. -b | --comma-start : in a parameters list, start with the comma (see -e) -B | --comma-break : in insert statement, add a newline after each comma -d | --debug : enable debug mode. Disabled by default. -e | --comma-end : in a parameters list, end with the comma (default) -f | --function-case N: Change the case of the reserved keyword. Default is unchanged: 0. Values: 0=>unchanged, 1=>lowercase, 2=>uppercase, 3=>capitalize. -F | --format STR : output format: text or html. Default: text. -g | --nogrouping : add a newline between statements in transaction regroupement. Default is to group statements. -h | --help : show this message and exit. -m | --maxlength SIZE : maximum length of a query, it will be cutted above the given size. Default: no truncate. -n | --nocomment : remove any comment from SQL code. -o | --output file : define the filename for the output. Default: stdout. -p | --placeholder re : set regex to find code that must not be changed. -s | --spaces size : change space indent, default 4 spaces. -S | --separator STR : dynamic code separator, default to single quote. -t | --format-type : try another formatting type for some statements. -T | --tabs : use tabs instead of space characters, when used spaces is set to 1 whatever is the value set to -s. -u | --keyword-case N : Change the case of the reserved keyword. Default is uppercase: 2. Values: 0=>unchanged, 1=>lowercase, 2=>uppercase, 3=>capitalize. -U | --type-case N : Change the case of the data type name. Default is lowercase: 1. Values: 0=>unchanged, 1=>lowercase, 2=>uppercase, 3=>capitalize. -v | --version : show pg_format version and exit. -w | --wrap-limit N : wrap queries at a certain length. -W | --wrap-after N : number of column after which lists must be wrapped. Default: puts every item on its own line. Examples: cat samples/ex1.sql | $0 - $0 -n samples/ex1.sql $0 -f 2 -n -o result.sql samples/ex1.sql }; if ( $status ) { print STDERR $help; } else { print $help; } exit $status; } =head2 load_sql Loads SQL from input file or stdin. =cut sub load_sql { my $self = shift; local $/ = undef; my $fh; if ( $self->{ 'cfg' }->{ 'input' } ne '-' ) { open $fh, '<', $self->{ 'cfg' }->{ 'input' }; } else { $fh = \*STDIN; } $self->{ 'query' } = <$fh>; close $fh; return; } =head2 get_command_line_args Parses command line options into $self->{'cfg'}. =cut sub get_command_line_args { my $self = shift; my %cfg; my @options = ( 'anonymize|a!', 'comma-start|b!', 'comma-break|B!', 'comma-end|e!', 'debug|d!', 'format|F=s', 'function-case|f=i', 'nogrouping|g!', 'help|h!', 'maxlength|m=i', 'nocomment|n!', 'output|o=s', 'placeholder|p=s', 'separator|S=s', 'spaces|s=i', 'format-type|t!', 'tabs|T!', 'keyword-case|u=i', 'type-case|U=i', 'version|v!', 'wrap-limit|w=i', 'wrap-after|W=i', ); $self->show_help_and_die( 1 ) unless GetOptions( \%cfg, @options ); $self->show_help_and_die( 0 ) if $cfg{ 'help' }; if ( $cfg{ 'version' } ) { printf '%s version %s%s', basename( $0 ), $VERSION, "\n"; exit 0; } $cfg{ 'spaces' } //= 4; $cfg{ 'output' } //= '-'; $cfg{ 'function-case' } //= 0; $cfg{ 'keyword-case' } //= 2; $cfg{ 'type-case' } //= 1; $cfg{ 'comma' } = 'end'; $cfg{ 'format' } //= 'text'; $cfg{ 'comma-break' } //= 0; $cfg{ 'maxlength' } //= 0; $cfg{ 'format-type' } //= 0; $cfg{ 'wrap-limit' } //= 0; $cfg{ 'wrap-after' } //= 0; $cfg{ 'space' } //= ' '; if ($cfg{ 'tabs' }) { $cfg{ 'spaces' } = 1; $cfg{ 'space' } = "\t"; } if (!grep(/^$cfg{ 'format' }$/i, 'text', 'html')) { printf 'FATAL: unknow output format: %s%s', $cfg{ 'format' } , "\n"; exit 0; } $cfg{ 'input' } = $ARGV[ 0 ] // '-'; $self->{ 'cfg' } = \%cfg; return; } =head2 validate_args Validates that options parsed from command line have sensible values, opens input and output files. =cut sub validate_args { my $self = shift; $self->show_help_and_die( 2, 'function-case can be only one of: 0, 1, 2, or 3.' ) unless $self->{ 'cfg' }->{ 'function-case' } =~ m{\A[0123]\z}; $self->show_help_and_die( 2, 'keyword-case can be only one of: 0, 1, 2, or 3.' ) unless $self->{ 'cfg' }->{ 'keyword-case' } =~ m{\A[0123]\z}; $self->show_help_and_die( 2, 'type-case can be only one of: 0, 1, 2, or 3.' ) unless $self->{ 'cfg' }->{ 'type-case' } =~ m{\A[0123]\z}; if ($self->{ 'cfg' }->{ 'comma-end' }) { $self->{ 'cfg' }->{ 'comma' } = 'end'; } elsif ($self->{ 'cfg' }->{ 'comma-start' }) { $self->{ 'cfg' }->{ 'comma' } = 'start'; } return; } =head1 AUTHOR pgFormatter is an original work from Gilles Darold =head1 BUGS Please report any bugs or feature requests to: https://github.com/darold/pgFormatter/issues =head1 COPYRIGHT Copyright 2012-2020 Gilles Darold. All rights reserved. =head1 LICENSE pgFormatter is free software distributed under the PostgreSQL Licence. A modified version of the SQL::Beautify Perl Module is embedded in pgFormatter with copyright (C) 2009 by Jonas Kramer and is published under the terms of the Artistic License 2.0. =cut 1; pgFormatter-4.2/logo_pgformatter.png000066400000000000000000000201011361326045100177240ustar00rootroot00000000000000PNG  IHDRZZ8AbKGD pHYs+tIME {`3IDATx՝wx\?w4jbl B6n eC eR(6ni\Rd i?HHB z %1Q-˶li㎚-iF}=޷|y{=1,I0yKoc@EFS @))`D0@v}`3Gߣ$@IkmwA] 7~//, xT\|f7Q6!$hL[RD+֊1HE[,bx3P `|U# cfqt[I0]%VKfh6{!G`/V*$;p4ˏtsX Wrۗ1$PQ ]nrtA)0ݶq- ,NQ>3ADE1!P+8c]I:Q "j>"u(OXξB*q``:p` Z:;@( 7 *7aw: )Xtp!PXpDc;3YV"- W߲PXܟ{:ቍZt*2m>up|~z2N2TB:p?Lhm20I,>MՐ03T7uUo;|ϴ,@OT'^\#=1 M|`90)緓Fd v Y21yuXI5]C=~., @ ~o*[Zzff%ԏ5!e ;˴HF 2ROAa%R`})|N]Gy|KV.?KP.G5pDcr€TTѱ6@ lfH4֡$WzߺOo39{') ~d`TXJ>=5 ;5UʟiqU4_shN_qX2 `L8oLH:{r\tt T_NWRq~Ge؊=gΰɚMFՓ( (US'*]}8aVsChy ۱RéՖ;udf,,`qV~؏b*pT?=,+Ƈ[hqeډX5Mi²}왿iYֳٯ '|x7}O$SNeD歭ՅځZCʄwNX"u[&?Jlߙ_Yʬ.ҳ}ꢬ=к`t@@Iivyk^tݿLa+I_G }FEJ t^DͣNbIk~B]G~M^Uu/ |oPZʫw Aw=8@Z{!_8`zLj`Umkג )3n;+~͖j7%'JĺT*6t_]邽U'xj;(/^@vf~*LKDzpI M*4!'\PDSN:{ t nw>0->=`ܻe;gږ7aDZ"&m'^gǧwn"Ҏyc$ Vuk+p?#yP&pUn[L,CcenO$>ғwr4sexpoG|FgHzN@C.~^} RYhm[|ܹS6~1XK&uEqHf{`“ ыew*>a%z"xB)kՂAE3&yzcYu&Zwz0v7آcMhy MULt r\ɂN'Pb.@g(kGK|N,6AϠK iѼEv+'.固s@`jXW[Nv7B:p*© hTc{ 7樭@Wve!M>}RM`_GaC@ՂgNOGW0ͨny, ֨_%pgҩpŃ}j2^m%Pd{WZGJ-:vBGr٥ǁY&\GGxq|*?OeʾDjWxvwg֭cjz*DXN^swEԦȈk{>h7D<;jU\3`jCYB>_Z+ۯ<VK~{i[=B:yV. SP_S;7_܋p%)KH-Vp llV&6*Ȯ {HAp۲}\=RdxC {&0NI9pB;5a(0J\OZManTdT:Z{r6 s% X(7B !vRY£yJqI-SqJ\tKvaD;Cs@aBouFԁ:| \bYX. ߷tڈf!.= 3"*&hl~9Qo7G:GVn[%ds=%VbُCt7n+$ VOUyb-3N8ip~ 8 %kZ. ͺgbؙZPCzH1N[R{U{ml['2n{7tdht C<7P Lm<4ߒh,Wh6=.bμ}ͿW * `뭓jӇ%/ √@P@|II+j}~OrLd0깶C5 0ƽN 7`;~N^S%Skthn*Wuv>,bdd_dujGjwhZ`.ژU&so(~k槗ZxfZMZŽ iOL Sm7^㨦㮛Z6bVڎ|+޾oKA, M t`9C ?hu<_>MxZF֚CŻҞ4vGj,!m`CE7~J>'$QoC n?[馫nXs$9Z <^DcDTr3.ykB?M[=g(ijkdeZ1 v?h$6#p{X@;k57TZ16B(Ddȕ7?q[\- 1(<}a)z=> "-=Nz/4hyݛgޭ@3 﫜vLҶF-٥څt{poP]ֺsޗh6 N7-K\"k['x@9׉5d*}sw[p%9TJ4o[ޑ{t<>)Cގ7/:1_l}>3$,i.ۻB@sBn]1ڄ>%%/ \j|Z\zOΉ.=bȂo@TĪ|)<cos)4YXUBo`nooI ,);Ww/ ytCJJFw ];;diYN3O-okēnˍI}rtQ\%mIsʖnhM=,m,5gIj / K$[ yH}S}3} 맄fWN]埦#o%>&T='2M`aKN*K#'6nsJwp޽l2񸗞tip/=E EmVSۧ`+,~0uu/›dk?|7qݯ7J.,X.;s\AeNCxLrs/=[K&e^Nvުu~%gK}ND; Bd(QHmtAi!0 a^7 V:|=aF$Ash슬SM,c #T,A]6y?KZrKKߖ1 {7@=/߅t:LD*bks<絢ևIQO॓[ dhI5${dmWJ4֤-":زul)-iV:-ڍ>ջ 9p 3̗ +?LBC]e=uL**_5ni"Z2 b.;C+0փ ۼd{Rd\cfiP{PN*ƞړ{w#[6 \adD+SGÜ&{ v&OI~o-V>K=6xy9tQUV[1Oc rӫG@.>un7} wYf'D{2{2v= 8ze #bIL0TS#qr<~=|acG<;.@yyBCǷ.7$={=H)`G|B|D+|bI D>$v#Fb'𰃂A:1cNC,s4vI%=eqȂ)~\#K7 8_c GgsȮ~Vm0Y{/MQOG9sc{x/G#oTQ9 ,2M|TW:T_NGyNMu mY(K9OYot!XU[j~-q?ƁӀ,Y|J`JF4~+${#'縔I[iӁg u'pu,?Z8 dpV Ycw!p~Rw#;_C[(i @ARGV ) { @ARGV = map { decode( 'UTF-8', $_ ) } @ARGV; } # UTF8 boilerplace, per http://stackoverflow.com/questions/6162484/why-does-modern-perl-avoid-utf-8-by-default/ # Improve warning/exception handling use Carp qw( carp croak confess cluck ); # give a full stack dump on any untrapped exceptions local $SIG{ __DIE__ } = sub { confess "Uncaught exception: @_" unless $^S; }; # now promote run-time warnings into stackdumped exceptions # *unless* we're in an try block, in which # case just generate a clucking stackdump instead local $SIG{ __WARN__ } = sub { if ( $^S ) { cluck "Trapped warning: @_" } else { confess "Deadly warning: @_" } }; # Improve warning/exception handling # Find libraries path (should be in lib/ directory of where binary is, or on system perl library directory use FindBin; use lib "$FindBin::RealBin/lib"; our $VERSION = '4.2'; # Find out whether current run should be treated as CGI or CLI my $program; if ( $ENV{ 'GATEWAY_INTERFACE' } ) { require pgFormatter::CGI; $program = pgFormatter::CGI->new(); } else { require pgFormatter::CLI; $program = pgFormatter::CLI->new(); } $program->run(); exit 0; pgFormatter-4.2/t/000077500000000000000000000000001361326045100141155ustar00rootroot00000000000000pgFormatter-4.2/t/01_lint.t000077500000000000000000000002621361326045100155530ustar00rootroot00000000000000use Test::Simple tests => 2; my $ret = `perl -I. -wc pg_format 2>&1`; ok( $? == 0, "PERL syntax check"); $ret = `podchecker doc/*.pod 2>&1`; ok( $? == 0, "pod syntax check"); pgFormatter-4.2/t/02_regress.t000077500000000000000000000017431361326045100162650ustar00rootroot00000000000000use Test::Simple tests => 57; my $ret = `perl -I. -wc pg_format 2>&1`; my @files = `find t/test-files/ -maxdepth 1 -name '*.sql' | sort`; chomp(@files); my $pg_format = $ENV{PG_FORMAT} // './pg_format'; # set to 'pg_format' to test installed binary in /usr/bin my $exit = 0; foreach my $f (@files) { next if ( $#ARGV >= 0 and lc($ARGV[0]) ne 'update' and !grep(m#^$f$#, @ARGV) ); my $opt = ''; $opt = "-S '\$f\$'" if ($f =~ m#/ex19.sql$#); $opt = "-W 4" if ($f =~ m#/ex46.sql$#); #$opt .= ' -t' if (grep(/^-t/, @ARGV) or $f =~ /float4\.sql/); $opt .= ' -t' if (grep(/^-t/, @ARGV)); $opt = "-T -n " if ($f =~ m#/ex51.sql$#); my $cmd = "./pg_format $opt -u 2 $f >/tmp/output.sql"; `$cmd`; $f =~ s/test-files\//test-files\/expected\//; if (lc($ARGV[0]) eq 'update') { `cp -f /tmp/output.sql $f`; } else { my @diff = `diff -u /tmp/output.sql $f | grep "^[+-]" | grep -v "^[+-]\t\$" | grep -v "^[+-][+-][+-]"`; ok( $#diff < 0, "Test file $f"); } unlink("/tmp/output.sql"); } pgFormatter-4.2/t/pg-test-files/000077500000000000000000000000001361326045100166005ustar00rootroot00000000000000pgFormatter-4.2/t/pg-test-files/expected/000077500000000000000000000000001361326045100204015ustar00rootroot00000000000000pgFormatter-4.2/t/pg-test-files/expected/advisory_lock.sql000066400000000000000000000111661361326045100237770ustar00rootroot00000000000000-- -- ADVISORY LOCKS -- BEGIN; SELECT pg_advisory_xact_lock(1), pg_advisory_xact_lock_shared(2), pg_advisory_xact_lock(1, 1), pg_advisory_xact_lock_shared(2, 2); SELECT locktype, classid, objid, objsubid, mode, granted FROM pg_locks WHERE locktype = 'advisory' ORDER BY classid, objid, objsubid; -- pg_advisory_unlock_all() shouldn't release xact locks SELECT pg_advisory_unlock_all(); SELECT count(*) FROM pg_locks WHERE locktype = 'advisory'; -- can't unlock xact locks SELECT pg_advisory_unlock(1), pg_advisory_unlock_shared(2), pg_advisory_unlock(1, 1), pg_advisory_unlock_shared(2, 2); -- automatically release xact locks at commit COMMIT; SELECT count(*) FROM pg_locks WHERE locktype = 'advisory'; BEGIN; -- holding both session and xact locks on the same objects, xact first SELECT pg_advisory_xact_lock(1), pg_advisory_xact_lock_shared(2), pg_advisory_xact_lock(1, 1), pg_advisory_xact_lock_shared(2, 2); SELECT locktype, classid, objid, objsubid, mode, granted FROM pg_locks WHERE locktype = 'advisory' ORDER BY classid, objid, objsubid; SELECT pg_advisory_lock(1), pg_advisory_lock_shared(2), pg_advisory_lock(1, 1), pg_advisory_lock_shared(2, 2); ROLLBACK; SELECT locktype, classid, objid, objsubid, mode, granted FROM pg_locks WHERE locktype = 'advisory' ORDER BY classid, objid, objsubid; -- unlocking session locks SELECT pg_advisory_unlock(1), pg_advisory_unlock(1), pg_advisory_unlock_shared(2), pg_advisory_unlock_shared(2), pg_advisory_unlock(1, 1), pg_advisory_unlock(1, 1), pg_advisory_unlock_shared(2, 2), pg_advisory_unlock_shared(2, 2); SELECT count(*) FROM pg_locks WHERE locktype = 'advisory'; BEGIN; -- holding both session and xact locks on the same objects, session first SELECT pg_advisory_lock(1), pg_advisory_lock_shared(2), pg_advisory_lock(1, 1), pg_advisory_lock_shared(2, 2); SELECT locktype, classid, objid, objsubid, mode, granted FROM pg_locks WHERE locktype = 'advisory' ORDER BY classid, objid, objsubid; SELECT pg_advisory_xact_lock(1), pg_advisory_xact_lock_shared(2), pg_advisory_xact_lock(1, 1), pg_advisory_xact_lock_shared(2, 2); ROLLBACK; SELECT locktype, classid, objid, objsubid, mode, granted FROM pg_locks WHERE locktype = 'advisory' ORDER BY classid, objid, objsubid; -- releasing all session locks SELECT pg_advisory_unlock_all(); SELECT count(*) FROM pg_locks WHERE locktype = 'advisory'; BEGIN; -- grabbing txn locks multiple times SELECT pg_advisory_xact_lock(1), pg_advisory_xact_lock(1), pg_advisory_xact_lock_shared(2), pg_advisory_xact_lock_shared(2), pg_advisory_xact_lock(1, 1), pg_advisory_xact_lock(1, 1), pg_advisory_xact_lock_shared(2, 2), pg_advisory_xact_lock_shared(2, 2); SELECT locktype, classid, objid, objsubid, mode, granted FROM pg_locks WHERE locktype = 'advisory' ORDER BY classid, objid, objsubid; COMMIT; SELECT count(*) FROM pg_locks WHERE locktype = 'advisory'; -- grabbing session locks multiple times SELECT pg_advisory_lock(1), pg_advisory_lock(1), pg_advisory_lock_shared(2), pg_advisory_lock_shared(2), pg_advisory_lock(1, 1), pg_advisory_lock(1, 1), pg_advisory_lock_shared(2, 2), pg_advisory_lock_shared(2, 2); SELECT locktype, classid, objid, objsubid, mode, granted FROM pg_locks WHERE locktype = 'advisory' ORDER BY classid, objid, objsubid; SELECT pg_advisory_unlock(1), pg_advisory_unlock(1), pg_advisory_unlock_shared(2), pg_advisory_unlock_shared(2), pg_advisory_unlock(1, 1), pg_advisory_unlock(1, 1), pg_advisory_unlock_shared(2, 2), pg_advisory_unlock_shared(2, 2); SELECT count(*) FROM pg_locks WHERE locktype = 'advisory'; -- .. and releasing them all at once SELECT pg_advisory_lock(1), pg_advisory_lock(1), pg_advisory_lock_shared(2), pg_advisory_lock_shared(2), pg_advisory_lock(1, 1), pg_advisory_lock(1, 1), pg_advisory_lock_shared(2, 2), pg_advisory_lock_shared(2, 2); SELECT locktype, classid, objid, objsubid, mode, granted FROM pg_locks WHERE locktype = 'advisory' ORDER BY classid, objid, objsubid; SELECT pg_advisory_unlock_all(); SELECT count(*) FROM pg_locks WHERE locktype = 'advisory'; pgFormatter-4.2/t/pg-test-files/expected/aggregates.sql000066400000000000000000001136731361326045100232460ustar00rootroot00000000000000-- -- AGGREGATES -- -- avoid bit-exact output here because operations may not be bit-exact. SET extra_float_digits = 0; SELECT avg(four) AS avg_1 FROM onek; SELECT avg(a) AS avg_32 FROM aggtest WHERE a < 100; -- In 7.1, avg(float4) is computed using float8 arithmetic. -- Round the result to 3 digits to avoid platform-specific results. SELECT avg(b)::numeric(10, 3) AS avg_107_943 FROM aggtest; SELECT avg(gpa) AS avg_3_4 FROM ONLY student; SELECT sum(four) AS sum_1500 FROM onek; SELECT sum(a) AS sum_198 FROM aggtest; SELECT sum(b) AS avg_431_773 FROM aggtest; SELECT sum(gpa) AS avg_6_8 FROM ONLY student; SELECT max(four) AS max_3 FROM onek; SELECT max(a) AS max_100 FROM aggtest; SELECT max(aggtest.b) AS max_324_78 FROM aggtest; SELECT max(student.gpa) AS max_3_7 FROM student; SELECT stddev_pop(b) FROM aggtest; SELECT stddev_samp(b) FROM aggtest; SELECT var_pop(b) FROM aggtest; SELECT var_samp(b) FROM aggtest; SELECT stddev_pop(b::numeric) FROM aggtest; SELECT stddev_samp(b::numeric) FROM aggtest; SELECT var_pop(b::numeric) FROM aggtest; SELECT var_samp(b::numeric) FROM aggtest; -- population variance is defined for a single tuple, sample variance -- is not SELECT var_pop(1.0), var_samp(2.0); SELECT stddev_pop(3.0::numeric), stddev_samp(4.0::numeric); -- verify correct results for null and NaN inputs SELECT sum(NULL::int4) FROM generate_series(1, 3); SELECT sum(NULL::int8) FROM generate_series(1, 3); SELECT sum(NULL::numeric) FROM generate_series(1, 3); SELECT sum(NULL::float8) FROM generate_series(1, 3); SELECT avg(NULL::int4) FROM generate_series(1, 3); SELECT avg(NULL::int8) FROM generate_series(1, 3); SELECT avg(NULL::numeric) FROM generate_series(1, 3); SELECT avg(NULL::float8) FROM generate_series(1, 3); SELECT sum('NaN'::numeric) FROM generate_series(1, 3); SELECT avg('NaN'::numeric) FROM generate_series(1, 3); -- verify correct results for infinite inputs SELECT avg(x::float8), var_pop(x::float8) FROM ( VALUES ('1'), ('infinity')) v (x); SELECT avg(x::float8), var_pop(x::float8) FROM ( VALUES ('infinity'), ('1')) v (x); SELECT avg(x::float8), var_pop(x::float8) FROM ( VALUES ('infinity'), ('infinity')) v (x); SELECT avg(x::float8), var_pop(x::float8) FROM ( VALUES ('-infinity'), ('infinity')) v (x); -- test accuracy with a large input offset SELECT avg(x::float8), var_pop(x::float8) FROM ( VALUES (100000003), (100000004), (100000006), (100000007)) v (x); SELECT avg(x::float8), var_pop(x::float8) FROM ( VALUES (7000000000005), (7000000000007)) v (x); -- SQL2003 binary aggregates SELECT regr_count(b, a) FROM aggtest; SELECT regr_sxx(b, a) FROM aggtest; SELECT regr_syy(b, a) FROM aggtest; SELECT regr_sxy(b, a) FROM aggtest; SELECT regr_avgx(b, a), regr_avgy(b, a) FROM aggtest; SELECT regr_r2(b, a) FROM aggtest; SELECT regr_slope(b, a), regr_intercept(b, a) FROM aggtest; SELECT covar_pop(b, a), covar_samp(b, a) FROM aggtest; SELECT corr(b, a) FROM aggtest; -- test accum and combine functions directly CREATE TABLE regr_test ( x float8, y float8 ); INSERT INTO regr_test VALUES (10, 150), (20, 250), (30, 350), (80, 540), (100, 200); SELECT count(*), sum(x), regr_sxx(y, x), sum(y), regr_syy(y, x), regr_sxy(y, x) FROM regr_test WHERE x IN (10, 20, 30, 80); SELECT count(*), sum(x), regr_sxx(y, x), sum(y), regr_syy(y, x), regr_sxy(y, x) FROM regr_test; SELECT float8_accum('{4,140,2900}'::float8[], 100); SELECT float8_regr_accum('{4,140,2900,1290,83075,15050}'::float8[], 200, 100); SELECT count(*), sum(x), regr_sxx(y, x), sum(y), regr_syy(y, x), regr_sxy(y, x) FROM regr_test WHERE x IN (10, 20, 30); SELECT count(*), sum(x), regr_sxx(y, x), sum(y), regr_syy(y, x), regr_sxy(y, x) FROM regr_test WHERE x IN (80, 100); SELECT float8_combine('{3,60,200}'::float8[], '{0,0,0}'::float8[]); SELECT float8_combine('{0,0,0}'::float8[], '{2,180,200}'::float8[]); SELECT float8_combine('{3,60,200}'::float8[], '{2,180,200}'::float8[]); SELECT float8_regr_combine('{3,60,200,750,20000,2000}'::float8[], '{0,0,0,0,0,0}'::float8[]); SELECT float8_regr_combine('{0,0,0,0,0,0}'::float8[], '{2,180,200,740,57800,-3400}'::float8[]); SELECT float8_regr_combine('{3,60,200,750,20000,2000}'::float8[], '{2,180,200,740,57800,-3400}'::float8[]); DROP TABLE regr_test; -- test count, distinct SELECT count(four) AS cnt_1000 FROM onek; SELECT count(DISTINCT four) AS cnt_4 FROM onek; SELECT ten, count(*), sum(four) FROM onek GROUP BY ten ORDER BY ten; SELECT ten, count(four), sum(DISTINCT four) FROM onek GROUP BY ten ORDER BY ten; -- user-defined aggregates SELECT newavg (four) AS avg_1 FROM onek; SELECT newsum (four) AS sum_1500 FROM onek; SELECT newcnt (four) AS cnt_1000 FROM onek; SELECT newcnt (*) AS cnt_1000 FROM onek; SELECT oldcnt (*) AS cnt_1000 FROM onek; SELECT sum2 (q1, q2) FROM int8_tbl; -- test for outer-level aggregates -- this should work SELECT ten, sum(DISTINCT four) FROM onek a GROUP BY ten HAVING EXISTS ( SELECT 1 FROM onek b WHERE sum(DISTINCT a.four) = b.four); -- this should fail because subquery has an agg of its own in WHERE SELECT ten, sum(DISTINCT four) FROM onek a GROUP BY ten HAVING EXISTS ( SELECT 1 FROM onek b WHERE sum(DISTINCT a.four + b.four) = b.four); -- Test handling of sublinks within outer-level aggregates. -- Per bug report from Daniel Grace. SELECT ( SELECT max(( SELECT i.unique2 FROM tenk1 i WHERE i.unique1 = o.unique1))) FROM tenk1 o; -- Test handling of Params within aggregate arguments in hashed aggregation. -- Per bug report from Jeevan Chalke. EXPLAIN ( VERBOSE, COSTS OFF ) SELECT s1, s2, sm FROM generate_series(1, 3) s1, LATERAL ( SELECT s2, sum(s1 + s2) sm FROM generate_series(1, 3) s2 GROUP BY s2) ss ORDER BY 1, 2; SELECT s1, s2, sm FROM generate_series(1, 3) s1, LATERAL ( SELECT s2, sum(s1 + s2) sm FROM generate_series(1, 3) s2 GROUP BY s2) ss ORDER BY 1, 2; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT ARRAY ( SELECT sum(x + y) s FROM generate_series(1, 3) y GROUP BY y ORDER BY s) FROM generate_series(1, 3) x; SELECT ARRAY ( SELECT sum(x + y) s FROM generate_series(1, 3) y GROUP BY y ORDER BY s) FROM generate_series(1, 3) x; -- -- test for bitwise integer aggregates -- CREATE TEMPORARY TABLE bitwise_test ( i2 int2, i4 int4, i8 int8, i integer, x int2, y bit(4) ); -- empty case SELECT BIT_AND(i2) AS "?", BIT_OR(i4) AS "?" FROM bitwise_test; SELECT BIT_AND(i2) AS "1", BIT_AND(i4) AS "1", BIT_AND(i8) AS "1", BIT_AND(i) AS "?", BIT_AND(x) AS "0", BIT_AND(y) AS "0100", BIT_OR(i2) AS "7", BIT_OR(i4) AS "7", BIT_OR(i8) AS "7", BIT_OR(i) AS "?", BIT_OR(x) AS "7", BIT_OR(y) AS "1101" FROM bitwise_test; -- -- test boolean aggregates -- -- first test all possible transition and final states SELECT -- boolean and transitions -- null because strict booland_statefunc(NULL, NULL) IS NULL AS "t", booland_statefunc(TRUE, NULL) IS NULL AS "t", booland_statefunc(FALSE, NULL) IS NULL AS "t", booland_statefunc(NULL, TRUE) IS NULL AS "t", booland_statefunc(NULL, FALSE) IS NULL AS "t", -- and actual computations booland_statefunc(TRUE, TRUE) AS "t", NOT booland_statefunc(TRUE, FALSE) AS "t", NOT booland_statefunc(FALSE, TRUE) AS "t", NOT booland_statefunc(FALSE, FALSE) AS "t"; SELECT -- boolean or transitions -- null because strict boolor_statefunc(NULL, NULL) IS NULL AS "t", boolor_statefunc(TRUE, NULL) IS NULL AS "t", boolor_statefunc(FALSE, NULL) IS NULL AS "t", boolor_statefunc(NULL, TRUE) IS NULL AS "t", boolor_statefunc(NULL, FALSE) IS NULL AS "t", -- actual computations boolor_statefunc(TRUE, TRUE) AS "t", boolor_statefunc(TRUE, FALSE) AS "t", boolor_statefunc(FALSE, TRUE) AS "t", NOT boolor_statefunc(FALSE, FALSE) AS "t"; CREATE TEMPORARY TABLE bool_test ( b1 bool, b2 bool, b3 bool, b4 bool ); -- empty case SELECT BOOL_AND(b1) AS "n", BOOL_OR(b3) AS "n" FROM bool_test; SELECT BOOL_AND(b1) AS "f", BOOL_AND(b2) AS "t", BOOL_AND(b3) AS "f", BOOL_AND(b4) AS "n", BOOL_AND(NOT b2) AS "f", BOOL_AND(NOT b3) AS "t" FROM bool_test; SELECT EVERY(b1) AS "f", EVERY(b2) AS "t", EVERY(b3) AS "f", EVERY(b4) AS "n", EVERY(NOT b2) AS "f", EVERY(NOT b3) AS "t" FROM bool_test; SELECT BOOL_OR(b1) AS "t", BOOL_OR(b2) AS "t", BOOL_OR(b3) AS "f", BOOL_OR(b4) AS "n", BOOL_OR(NOT b2) AS "f", BOOL_OR(NOT b3) AS "t" FROM bool_test; -- -- Test cases that should be optimized into indexscans instead of -- the generic aggregate implementation. -- -- Basic cases EXPLAIN ( COSTS OFF ) SELECT min(unique1) FROM tenk1; SELECT min(unique1) FROM tenk1; EXPLAIN ( COSTS OFF ) SELECT max(unique1) FROM tenk1; SELECT max(unique1) FROM tenk1; EXPLAIN ( COSTS OFF ) SELECT max(unique1) FROM tenk1 WHERE unique1 < 42; SELECT max(unique1) FROM tenk1 WHERE unique1 < 42; EXPLAIN ( COSTS OFF ) SELECT max(unique1) FROM tenk1 WHERE unique1 > 42; SELECT max(unique1) FROM tenk1 WHERE unique1 > 42; -- the planner may choose a generic aggregate here if parallel query is -- enabled, since that plan will be parallel safe and the "optimized" -- plan, which has almost identical cost, will not be. we want to test -- the optimized plan, so temporarily disable parallel query. BEGIN; SET local max_parallel_workers_per_gather = 0; EXPLAIN ( COSTS OFF ) SELECT max(unique1) FROM tenk1 WHERE unique1 > 42000; SELECT max(unique1) FROM tenk1 WHERE unique1 > 42000; ROLLBACK; -- multi-column index (uses tenk1_thous_tenthous) EXPLAIN ( COSTS OFF ) SELECT max(tenthous) FROM tenk1 WHERE thousand = 33; SELECT max(tenthous) FROM tenk1 WHERE thousand = 33; EXPLAIN ( COSTS OFF ) SELECT min(tenthous) FROM tenk1 WHERE thousand = 33; SELECT min(tenthous) FROM tenk1 WHERE thousand = 33; -- check parameter propagation into an indexscan subquery EXPLAIN ( COSTS OFF ) SELECT f1, ( SELECT min(unique1) FROM tenk1 WHERE unique1 > f1) AS gt FROM int4_tbl; SELECT f1, ( SELECT min(unique1) FROM tenk1 WHERE unique1 > f1) AS gt FROM int4_tbl; -- check some cases that were handled incorrectly in 8.3.0 EXPLAIN ( COSTS OFF ) SELECT DISTINCT max(unique2) FROM tenk1; SELECT DISTINCT max(unique2) FROM tenk1; EXPLAIN ( COSTS OFF ) SELECT max(unique2) FROM tenk1 ORDER BY 1; SELECT max(unique2) FROM tenk1 ORDER BY 1; EXPLAIN ( COSTS OFF ) SELECT max(unique2) FROM tenk1 ORDER BY max(unique2); SELECT max(unique2) FROM tenk1 ORDER BY max(unique2); EXPLAIN ( COSTS OFF ) SELECT max(unique2) FROM tenk1 ORDER BY max(unique2) + 1; SELECT max(unique2) FROM tenk1 ORDER BY max(unique2) + 1; EXPLAIN ( COSTS OFF ) SELECT max(unique2), generate_series(1, 3) AS g FROM tenk1 ORDER BY g DESC; SELECT max(unique2), generate_series(1, 3) AS g FROM tenk1 ORDER BY g DESC; -- interesting corner case: constant gets optimized into a seqscan EXPLAIN ( COSTS OFF ) SELECT max(100) FROM tenk1; SELECT max(100) FROM tenk1; -- try it on an inheritance tree CREATE TABLE minmaxtest ( f1 int ); CREATE TABLE minmaxtest1 () INHERITS ( minmaxtest ); CREATE TABLE minmaxtest2 () INHERITS ( minmaxtest ); CREATE TABLE minmaxtest3 () INHERITS ( minmaxtest ); CREATE INDEX minmaxtesti ON minmaxtest (f1); CREATE INDEX minmaxtest1i ON minmaxtest1 (f1); CREATE INDEX minmaxtest2i ON minmaxtest2 (f1 DESC); CREATE INDEX minmaxtest3i ON minmaxtest3 (f1) WHERE f1 IS NOT NULL; INSERT INTO minmaxtest VALUES (11), (12); INSERT INTO minmaxtest1 VALUES (13), (14); INSERT INTO minmaxtest2 VALUES (15), (16); INSERT INTO minmaxtest3 VALUES (17), (18); EXPLAIN ( COSTS OFF ) SELECT min(f1), max(f1) FROM minmaxtest; SELECT min(f1), max(f1) FROM minmaxtest; -- DISTINCT doesn't do anything useful here, but it shouldn't fail EXPLAIN ( COSTS OFF ) SELECT DISTINCT min(f1), max(f1) FROM minmaxtest; SELECT DISTINCT min(f1), max(f1) FROM minmaxtest; DROP TABLE minmaxtest CASCADE; -- check for correct detection of nested-aggregate errors SELECT max(min(unique1)) FROM tenk1; SELECT ( SELECT max(min(unique1)) FROM int8_tbl) FROM tenk1; -- -- Test removal of redundant GROUP BY columns -- CREATE temp TABLE t1 ( a int, b int, c int, d int, PRIMARY KEY (a, b) ); CREATE temp TABLE t2 ( x int, y int, z int, PRIMARY KEY (x, y) ); CREATE temp TABLE t3 ( a int, b int, c int, PRIMARY KEY (a, b) DEFERRABLE ); -- Non-primary-key columns can be removed from GROUP BY EXPLAIN ( COSTS OFF ) SELECT * FROM t1 GROUP BY a, b, c, d; -- No removal can happen if the complete PK is not present in GROUP BY EXPLAIN ( COSTS OFF ) SELECT a, c FROM t1 GROUP BY a, c, d; -- Test removal across multiple relations EXPLAIN ( COSTS OFF ) SELECT * FROM t1 INNER JOIN t2 ON t1.a = t2.x AND t1.b = t2.y GROUP BY t1.a, t1.b, t1.c, t1.d, t2.x, t2.y, t2.z; -- Test case where t1 can be optimized but not t2 EXPLAIN ( COSTS OFF ) SELECT t1.*, t2.x, t2.z FROM t1 INNER JOIN t2 ON t1.a = t2.x AND t1.b = t2.y GROUP BY t1.a, t1.b, t1.c, t1.d, t2.x, t2.z; -- Cannot optimize when PK is deferrable EXPLAIN ( COSTS OFF ) SELECT * FROM t3 GROUP BY a, b, c; DROP TABLE t1; DROP TABLE t2; DROP TABLE t3; -- -- Test combinations of DISTINCT and/or ORDER BY -- SELECT array_agg(a ORDER BY b) FROM ( VALUES (1, 4), (2, 3), (3, 1), (4, 2)) v (a, b); SELECT array_agg(a ORDER BY a) FROM ( VALUES (1, 4), (2, 3), (3, 1), (4, 2)) v (a, b); SELECT array_agg(a ORDER BY a DESC) FROM ( VALUES (1, 4), (2, 3), (3, 1), (4, 2)) v (a, b); SELECT array_agg(b ORDER BY a DESC) FROM ( VALUES (1, 4), (2, 3), (3, 1), (4, 2)) v (a, b); SELECT array_agg(DISTINCT a) FROM ( VALUES (1), (2), (1), (3), (NULL), (2)) v (a); SELECT array_agg(DISTINCT a ORDER BY a) FROM ( VALUES (1), (2), (1), (3), (NULL), (2)) v (a); SELECT array_agg(DISTINCT a ORDER BY a DESC) FROM ( VALUES (1), (2), (1), (3), (NULL), (2)) v (a); SELECT array_agg(DISTINCT a ORDER BY a DESC nulls LAST) FROM ( VALUES (1), (2), (1), (3), (NULL), (2)) v (a); -- multi-arg aggs, strict/nonstrict, distinct/order by SELECT aggfstr (a, b, c) FROM ( VALUES (1, 3, 'foo'), (0, NULL, NULL), (2, 2, 'bar'), (3, 1, 'baz')) v (a, b, c); SELECT aggfns (a, b, c) FROM ( VALUES (1, 3, 'foo'), (0, NULL, NULL), (2, 2, 'bar'), (3, 1, 'baz')) v (a, b, c); SELECT aggfstr (DISTINCT a, b, c) FROM ( VALUES (1, 3, 'foo'), (0, NULL, NULL), (2, 2, 'bar'), (3, 1, 'baz')) v (a, b, c), generate_series(1, 3) i; SELECT aggfns (DISTINCT a, b, c) FROM ( VALUES (1, 3, 'foo'), (0, NULL, NULL), (2, 2, 'bar'), (3, 1, 'baz')) v (a, b, c), generate_series(1, 3) i; SELECT aggfstr (DISTINCT a, b, c ORDER BY b) FROM ( VALUES (1, 3, 'foo'), (0, NULL, NULL), (2, 2, 'bar'), (3, 1, 'baz')) v (a, b, c), generate_series(1, 3) i; SELECT aggfns (DISTINCT a, b, c ORDER BY b) FROM ( VALUES (1, 3, 'foo'), (0, NULL, NULL), (2, 2, 'bar'), (3, 1, 'baz')) v (a, b, c), generate_series(1, 3) i; -- test specific code paths SELECT aggfns (DISTINCT a, a, c ORDER BY c USING ~<~, a) FROM ( VALUES (1, 3, 'foo'), (0, NULL, NULL), (2, 2, 'bar'), (3, 1, 'baz')) v (a, b, c), generate_series(1, 2) i; SELECT aggfns (DISTINCT a, a, c ORDER BY c USING ~<~) FROM ( VALUES (1, 3, 'foo'), (0, NULL, NULL), (2, 2, 'bar'), (3, 1, 'baz')) v (a, b, c), generate_series(1, 2) i; SELECT aggfns (DISTINCT a, a, c ORDER BY a) FROM ( VALUES (1, 3, 'foo'), (0, NULL, NULL), (2, 2, 'bar'), (3, 1, 'baz')) v (a, b, c), generate_series(1, 2) i; SELECT aggfns (DISTINCT a, b, c ORDER BY a, c USING ~<~, b) FROM ( VALUES (1, 3, 'foo'), (0, NULL, NULL), (2, 2, 'bar'), (3, 1, 'baz')) v (a, b, c), generate_series(1, 2) i; -- check node I/O via view creation and usage, also deparsing logic CREATE VIEW agg_view1 AS SELECT aggfns (a, b, c) FROM ( VALUES (1, 3, 'foo'), (0, NULL, NULL), (2, 2, 'bar'), (3, 1, 'baz')) v (a, b, c); SELECT * FROM agg_view1; SELECT pg_get_viewdef('agg_view1'::regclass); CREATE OR REPLACE VIEW agg_view1 AS SELECT aggfns (DISTINCT a, b, c) FROM ( VALUES (1, 3, 'foo'), (0, NULL, NULL), (2, 2, 'bar'), (3, 1, 'baz')) v (a, b, c), generate_series(1, 3) i; SELECT * FROM agg_view1; SELECT pg_get_viewdef('agg_view1'::regclass); CREATE OR REPLACE VIEW agg_view1 AS SELECT aggfns (DISTINCT a, b, c ORDER BY b) FROM ( VALUES (1, 3, 'foo'), (0, NULL, NULL), (2, 2, 'bar'), (3, 1, 'baz')) v (a, b, c), generate_series(1, 3) i; SELECT * FROM agg_view1; SELECT pg_get_viewdef('agg_view1'::regclass); CREATE OR REPLACE VIEW agg_view1 AS SELECT aggfns (a, b, c ORDER BY b + 1) FROM ( VALUES (1, 3, 'foo'), (0, NULL, NULL), (2, 2, 'bar'), (3, 1, 'baz')) v (a, b, c); SELECT * FROM agg_view1; SELECT pg_get_viewdef('agg_view1'::regclass); CREATE OR REPLACE VIEW agg_view1 AS SELECT aggfns (a, a, c ORDER BY b) FROM ( VALUES (1, 3, 'foo'), (0, NULL, NULL), (2, 2, 'bar'), (3, 1, 'baz')) v (a, b, c); SELECT * FROM agg_view1; SELECT pg_get_viewdef('agg_view1'::regclass); CREATE OR REPLACE VIEW agg_view1 AS SELECT aggfns (a, b, c ORDER BY c USING ~<~) FROM ( VALUES (1, 3, 'foo'), (0, NULL, NULL), (2, 2, 'bar'), (3, 1, 'baz')) v (a, b, c); SELECT * FROM agg_view1; SELECT pg_get_viewdef('agg_view1'::regclass); CREATE OR REPLACE VIEW agg_view1 AS SELECT aggfns (DISTINCT a, b, c ORDER BY a, c USING ~<~, b) FROM ( VALUES (1, 3, 'foo'), (0, NULL, NULL), (2, 2, 'bar'), (3, 1, 'baz')) v (a, b, c), generate_series(1, 2) i; SELECT * FROM agg_view1; SELECT pg_get_viewdef('agg_view1'::regclass); DROP VIEW agg_view1; -- incorrect DISTINCT usage errors SELECT aggfns (DISTINCT a, b, c ORDER BY i) FROM ( VALUES (1, 1, 'foo')) v (a, b, c), generate_series(1, 2) i; SELECT aggfns (DISTINCT a, b, c ORDER BY a, b + 1) FROM ( VALUES (1, 1, 'foo')) v (a, b, c), generate_series(1, 2) i; SELECT aggfns (DISTINCT a, b, c ORDER BY a, b, i, c) FROM ( VALUES (1, 1, 'foo')) v (a, b, c), generate_series(1, 2) i; SELECT aggfns (DISTINCT a, a, c ORDER BY a, b) FROM ( VALUES (1, 1, 'foo')) v (a, b, c), generate_series(1, 2) i; -- string_agg tests SELECT string_agg(a, ',') FROM ( VALUES ('aaaa'), ('bbbb'), ('cccc')) g (a); SELECT string_agg(a, ',') FROM ( VALUES ('aaaa'), (NULL), ('bbbb'), ('cccc')) g (a); SELECT string_agg(a, 'AB') FROM ( VALUES (NULL), (NULL), ('bbbb'), ('cccc')) g (a); SELECT string_agg(a, ',') FROM ( VALUES (NULL), (NULL)) g (a); -- check some implicit casting cases, as per bug #5564 SELECT string_agg(DISTINCT f1, ',' ORDER BY f1) FROM varchar_tbl; -- ok SELECT string_agg(DISTINCT f1::text, ',' ORDER BY f1) FROM varchar_tbl; -- not ok SELECT string_agg(DISTINCT f1, ',' ORDER BY f1::text) FROM varchar_tbl; -- not ok SELECT string_agg(DISTINCT f1::text, ',' ORDER BY f1::text) FROM varchar_tbl; -- ok -- string_agg bytea tests CREATE TABLE bytea_test_table ( v bytea ); SELECT string_agg(v, '') FROM bytea_test_table; INSERT INTO bytea_test_table VALUES (decode('ff', 'hex')); SELECT string_agg(v, '') FROM bytea_test_table; INSERT INTO bytea_test_table VALUES (decode('aa', 'hex')); SELECT string_agg(v, '') FROM bytea_test_table; SELECT string_agg(v, NULL) FROM bytea_test_table; SELECT string_agg(v, decode('ee', 'hex')) FROM bytea_test_table; DROP TABLE bytea_test_table; -- FILTER tests SELECT min(unique1) FILTER (WHERE unique1 > 100) FROM tenk1; SELECT sum(1 / ten) FILTER (WHERE ten > 0) FROM tenk1; SELECT ten, sum(DISTINCT four) FILTER (WHERE four::text ~ '123') FROM onek a GROUP BY ten; SELECT ten, sum(DISTINCT four) FILTER (WHERE four > 10) FROM onek a GROUP BY ten HAVING EXISTS ( SELECT 1 FROM onek b WHERE sum(DISTINCT a.four) = b.four); SELECT max(foo COLLATE "C") FILTER (WHERE (bar COLLATE "POSIX") > '0') FROM ( VALUES ('a', 'b')) AS v (foo, bar); -- outer reference in FILTER (PostgreSQL extension) SELECT ( SELECT count(*) FROM ( VALUES (1)) t0 (inner_c)) FROM ( VALUES (2), (3)) t1 (outer_c); -- inner query is aggregation query SELECT ( SELECT count(*) FILTER (WHERE outer_c <> 0) FROM ( VALUES (1)) t0 (inner_c)) FROM ( VALUES (2), (3)) t1 (outer_c); -- outer query is aggregation query SELECT ( SELECT count(inner_c) FILTER (WHERE outer_c <> 0) FROM ( VALUES (1)) t0 (inner_c)) FROM ( VALUES (2), (3)) t1 (outer_c); -- inner query is aggregation query SELECT ( SELECT max(( SELECT i.unique2 FROM tenk1 i WHERE i.unique1 = o.unique1)) FILTER (WHERE o.unique1 < 10)) FROM tenk1 o; -- outer query is aggregation query -- subquery in FILTER clause (PostgreSQL extension) SELECT sum(unique1) FILTER (WHERE unique1 IN ( SELECT unique1 FROM onek WHERE unique1 < 100)) FROM tenk1; -- exercise lots of aggregate parts with FILTER SELECT aggfns (DISTINCT a, b, c ORDER BY a, c USING ~<~, b) FILTER (WHERE a > 1) FROM ( VALUES (1, 3, 'foo'), (0, NULL, NULL), (2, 2, 'bar'), (3, 1, 'baz')) v (a, b, c), generate_series(1, 2) i; -- ordered-set aggregates SELECT p, percentile_cont(p) WITHIN GROUP (ORDER BY x::float8) FROM generate_series(1, 5) x, ( VALUES (0::float8), (0.1), (0.25), (0.4), (0.5), (0.6), (0.75), (0.9), (1)) v (p) GROUP BY p ORDER BY p; SELECT p, percentile_cont(p ORDER BY p) WITHIN GROUP (ORDER BY x) -- error FROM generate_series(1, 5) x, ( VALUES (0::float8), (0.1), (0.25), (0.4), (0.5), (0.6), (0.75), (0.9), (1)) v (p) GROUP BY p ORDER BY p; SELECT p, sum() WITHIN GROUP (ORDER BY x::float8) -- error FROM generate_series(1, 5) x, ( VALUES (0::float8), (0.1), (0.25), (0.4), (0.5), (0.6), (0.75), (0.9), (1)) v (p) GROUP BY p ORDER BY p; SELECT p, percentile_cont(p, p) -- error FROM generate_series(1, 5) x, ( VALUES (0::float8), (0.1), (0.25), (0.4), (0.5), (0.6), (0.75), (0.9), (1)) v (p) GROUP BY p ORDER BY p; SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY b) FROM aggtest; SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY b), sum(b) FROM aggtest; SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY thousand) FROM tenk1; SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY thousand) FROM tenk1; SELECT rank(3) WITHIN GROUP (ORDER BY x) FROM ( VALUES (1), (1), (2), (2), (3), (3), (4)) v (x); SELECT cume_dist(3) WITHIN GROUP (ORDER BY x) FROM ( VALUES (1), (1), (2), (2), (3), (3), (4)) v (x); SELECT percent_rank(3) WITHIN GROUP (ORDER BY x) FROM ( VALUES (1), (1), (2), (2), (3), (3), (4), (5)) v (x); SELECT dense_rank(3) WITHIN GROUP (ORDER BY x) FROM ( VALUES (1), (1), (2), (2), (3), (3), (4)) v (x); SELECT percentile_disc(ARRAY[0, 0.1, 0.25, 0.5, 0.75, 0.9, 1]) WITHIN GROUP (ORDER BY thousand) FROM tenk1; SELECT percentile_cont(ARRAY[0, 0.25, 0.5, 0.75, 1]) WITHIN GROUP (ORDER BY thousand) FROM tenk1; SELECT percentile_disc(ARRAY[[NULL, 1, 0.5],[0.75, 0.25, NULL]]) WITHIN GROUP (ORDER BY thousand) FROM tenk1; SELECT percentile_cont(ARRAY[0, 1, 0.25, 0.75, 0.5, 1, 0.3, 0.32, 0.35, 0.38, 0.4]) WITHIN GROUP (ORDER BY x) FROM generate_series(1, 6) x; SELECT ten, mode() WITHIN GROUP (ORDER BY string4) FROM tenk1 GROUP BY ten; SELECT percentile_disc(ARRAY[0.25, 0.5, 0.75]) WITHIN GROUP (ORDER BY x) FROM unnest('{fred,jim,fred,jack,jill,fred,jill,jim,jim,sheila,jim,sheila}'::text[]) u (x); -- check collation propagates up in suitable cases: SELECT pg_collation_for(percentile_disc(1) WITHIN GROUP (ORDER BY x COLLATE "POSIX")) FROM ( VALUES ('fred'), ('jim')) v (x); -- ordered-set aggs created with CREATE AGGREGATE SELECT test_rank (3) WITHIN GROUP (ORDER BY x) FROM ( VALUES (1), (1), (2), (2), (3), (3), (4)) v (x); SELECT test_percentile_disc (0.5) WITHIN GROUP (ORDER BY thousand) FROM tenk1; -- ordered-set aggs can't use ungrouped vars in direct args: SELECT rank(x) WITHIN GROUP (ORDER BY x) FROM generate_series(1, 5) x; -- outer-level agg can't use a grouped arg of a lower level, either: SELECT ARRAY ( SELECT percentile_disc(a) WITHIN GROUP (ORDER BY x) FROM ( VALUES (0.3), (0.7)) v (a) GROUP BY a) FROM generate_series(1, 5) g (x); -- agg in the direct args is a grouping violation, too: SELECT rank(sum(x)) WITHIN GROUP (ORDER BY x) FROM generate_series(1, 5) x; -- hypothetical-set type unification and argument-count failures: SELECT rank(3) WITHIN GROUP (ORDER BY x) FROM ( VALUES ('fred'), ('jim')) v (x); SELECT rank(3) WITHIN GROUP (ORDER BY stringu1, stringu2) FROM tenk1; SELECT rank('fred') WITHIN GROUP (ORDER BY x) FROM generate_series(1, 5) x; SELECT rank('adam'::text COLLATE "C") WITHIN GROUP (ORDER BY x COLLATE "POSIX") FROM ( VALUES ('fred'), ('jim')) v (x); -- hypothetical-set type unification successes: SELECT rank('adam'::varchar) WITHIN GROUP (ORDER BY x) FROM ( VALUES ('fred'), ('jim')) v (x); SELECT rank('3') WITHIN GROUP (ORDER BY x) FROM generate_series(1, 5) x; -- divide by zero check SELECT percent_rank(0) WITHIN GROUP (ORDER BY x) FROM generate_series(1, 0) x; -- deparse and multiple features: CREATE VIEW aggordview1 AS SELECT ten, percentile_disc(0.5) WITHIN GROUP (ORDER BY thousand) AS p50, percentile_disc(0.5) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE hundred = 1) AS px, rank(5, 'AZZZZ', 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) FROM tenk1 GROUP BY ten ORDER BY ten; SELECT pg_get_viewdef('aggordview1'); SELECT * FROM aggordview1 ORDER BY ten; DROP VIEW aggordview1; -- variadic aggregates SELECT least_agg (q1, q2) FROM int8_tbl; SELECT least_agg (VARIADIC ARRAY[q1, q2]) FROM int8_tbl; -- test aggregates with common transition functions share the same states BEGIN WORK; CREATE TYPE avg_state AS ( total bigint, count bigint ); CREATE OR REPLACE FUNCTION avg_transfn (state avg_state, n int) RETURNS avg_state AS $$ DECLARE new_state avg_state; BEGIN RAISE notice 'avg_transfn called with %', n; IF state IS NULL THEN IF n IS NOT NULL THEN new_state.total := n; new_state.count := 1; RETURN new_state; END IF; RETURN NULL; elsif n IS NOT NULL THEN state.total := state.total + n; state.count := state.count + 1; RETURN state; END IF; RETURN NULL; END $$ LANGUAGE plpgsql; CREATE FUNCTION avg_finalfn (state avg_state) RETURNS int4 AS $$ BEGIN IF state IS NULL THEN RETURN NULL; ELSE RETURN state.total / state.count; END IF; END $$ LANGUAGE plpgsql; CREATE FUNCTION sum_finalfn (state avg_state) RETURNS int4 AS $$ BEGIN IF state IS NULL THEN RETURN NULL; ELSE RETURN state.total; END IF; END $$ LANGUAGE plpgsql; CREATE AGGREGATE my_avg (int4) ( STYPE = avg_state, SFUNC = avg_transfn, FINALFUNC = avg_finalfn ); CREATE AGGREGATE my_sum (int4) ( STYPE = avg_state, SFUNC = avg_transfn, FINALFUNC = sum_finalfn ); -- aggregate state should be shared as aggs are the same. SELECT my_avg (one), my_avg (one) FROM ( VALUES (1), (3)) t (one); -- aggregate state should be shared as transfn is the same for both aggs. SELECT my_avg (one), my_sum (one) FROM ( VALUES (1), (3)) t (one); -- same as previous one, but with DISTINCT, which requires sorting the input. SELECT my_avg (DISTINCT one), my_sum (DISTINCT one) FROM ( VALUES (1), (3), (1)) t (one); -- shouldn't share states due to the distinctness not matching. SELECT my_avg (DISTINCT one), my_sum (one) FROM ( VALUES (1), (3)) t (one); -- shouldn't share states due to the filter clause not matching. SELECT my_avg (one) FILTER (WHERE one > 1), my_sum (one) FROM ( VALUES (1), (3)) t (one); -- this should not share the state due to different input columns. SELECT my_avg (one), my_sum (two) FROM ( VALUES (1, 2), (3, 4)) t (one, two); -- exercise cases where OSAs share state SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY a), percentile_disc(0.5) WITHIN GROUP (ORDER BY a) FROM ( VALUES (1::float8), (3), (5), (7)) t (a); SELECT percentile_cont(0.25) WITHIN GROUP (ORDER BY a), percentile_disc(0.5) WITHIN GROUP (ORDER BY a) FROM ( VALUES (1::float8), (3), (5), (7)) t (a); -- these can't share state currently SELECT rank(4) WITHIN GROUP (ORDER BY a), dense_rank(4) WITHIN GROUP (ORDER BY a) FROM ( VALUES (1), (3), (5), (7)) t (a); -- test that aggs with the same sfunc and initcond share the same agg state CREATE AGGREGATE my_sum_init (int4) ( STYPE = avg_state, SFUNC = avg_transfn, FINALFUNC = sum_finalfn, INITCOND = '(10,0)' ); CREATE AGGREGATE my_avg_init (int4) ( STYPE = avg_state, SFUNC = avg_transfn, FINALFUNC = avg_finalfn, INITCOND = '(10,0)' ); CREATE AGGREGATE my_avg_init2 (int4) ( STYPE = avg_state, SFUNC = avg_transfn, FINALFUNC = avg_finalfn, INITCOND = '(4,0)' ); -- state should be shared if INITCONDs are matching SELECT my_sum_init (one), my_avg_init (one) FROM ( VALUES (1), (3)) t (one); -- Varying INITCONDs should cause the states not to be shared. SELECT my_sum_init (one), my_avg_init2 (one) FROM ( VALUES (1), (3)) t (one); ROLLBACK; -- test aggregate state sharing to ensure it works if one aggregate has a -- finalfn and the other one has none. BEGIN WORK; CREATE OR REPLACE FUNCTION sum_transfn (state int4, n int4) RETURNS int4 AS $$ DECLARE new_state int4; BEGIN RAISE notice 'sum_transfn called with %', n; IF state IS NULL THEN IF n IS NOT NULL THEN new_state := n; RETURN new_state; END IF; RETURN NULL; elsif n IS NOT NULL THEN state := state + n; RETURN state; END IF; RETURN NULL; END $$ LANGUAGE plpgsql; CREATE FUNCTION halfsum_finalfn (state int4) RETURNS int4 AS $$ BEGIN IF state IS NULL THEN RETURN NULL; ELSE RETURN state / 2; END IF; END $$ LANGUAGE plpgsql; CREATE AGGREGATE my_sum (int4) ( STYPE = int4, SFUNC = sum_transfn ); CREATE AGGREGATE my_half_sum (int4) ( STYPE = int4, SFUNC = sum_transfn, FINALFUNC = halfsum_finalfn ); -- Agg state should be shared even though my_sum has no finalfn SELECT my_sum (one), my_half_sum (one) FROM ( VALUES (1), (2), (3), (4)) t (one); ROLLBACK; -- test that the aggregate transition logic correctly handles -- transition / combine functions returning NULL -- First test the case of a normal transition function returning NULL BEGIN; CREATE FUNCTION balkifnull (int8, int4) RETURNS int8 STRICT LANGUAGE plpgsql AS $$ BEGIN IF $1 IS NULL THEN RAISE 'erroneously called with NULL argument'; END IF; RETURN NULL; END $$; CREATE AGGREGATE balk (int4) ( SFUNC = balkifnull (int8, int4), STYPE = int8, PARALLEL = SAFE, INITCOND = '0' ); SELECT balk (hundred) FROM tenk1; ROLLBACK; -- Secondly test the case of a parallel aggregate combiner function -- returning NULL. For that use normal transition function, but a -- combiner function returning NULL. BEGIN ISOLATION LEVEL REPEATABLE READ; CREATE FUNCTION balkifnull (int8, int8) RETURNS int8 PARALLEL SAFE STRICT LANGUAGE plpgsql AS $$ BEGIN IF $1 IS NULL THEN RAISE 'erroneously called with NULL argument'; END IF; RETURN NULL; END $$; CREATE AGGREGATE balk (int4) ( SFUNC = int4_sum(int8, int4), STYPE = int8, COMBINEFUNC = balkifnull (int8, int8), PARALLEL = SAFE, INITCOND = '0' ); -- force use of parallelism ALTER TABLE tenk1 SET (parallel_workers = 4); SET LOCAL parallel_setup_cost = 0; SET LOCAL max_parallel_workers_per_gather = 4; EXPLAIN ( COSTS OFF ) SELECT balk (hundred) FROM tenk1; SELECT balk (hundred) FROM tenk1; ROLLBACK; -- test coverage for aggregate combine/serial/deserial functions BEGIN ISOLATION LEVEL REPEATABLE READ; SET parallel_setup_cost = 0; SET parallel_tuple_cost = 0; SET min_parallel_table_scan_size = 0; SET max_parallel_workers_per_gather = 4; SET enable_indexonlyscan = OFF; -- variance(int4) covers numeric_poly_combine -- sum(int8) covers int8_avg_combine EXPLAIN ( COSTS OFF ) SELECT variance(unique1::int4), sum(unique1::int8) FROM tenk1; SELECT variance(unique1::int4), sum(unique1::int8) FROM tenk1; ROLLBACK; -- test coverage for dense_rank SELECT dense_rank(x) WITHIN GROUP (ORDER BY x) FROM ( VALUES (1), (1), (2), (2), (3), (3)) v (x) GROUP BY (x) ORDER BY 1; -- Ensure that the STRICT checks for aggregates does not take NULLness -- of ORDER BY columns into account. See bug report around -- 2a505161-2727-2473-7c46-591ed108ac52@email.cz SELECT min(x ORDER BY y) FROM ( VALUES (1, NULL)) AS d (x, y); SELECT min(x ORDER BY y) FROM ( VALUES (1, 2)) AS d (x, y); -- check collation-sensitive matching between grouping expressions SELECT v || 'a', CASE v || 'a' WHEN 'aa' THEN 1 ELSE 0 END, count(*) FROM unnest(ARRAY['a', 'b']) u (v) GROUP BY v || 'a' ORDER BY 1; SELECT v || 'a', CASE WHEN v || 'a' = 'aa' THEN 1 ELSE 0 END, count(*) FROM unnest(ARRAY['a', 'b']) u (v) GROUP BY v || 'a' ORDER BY 1; pgFormatter-4.2/t/pg-test-files/expected/alter_generic.sql000066400000000000000000000423571361326045100237400ustar00rootroot00000000000000-- -- Test for ALTER some_object {RENAME TO, OWNER TO, SET SCHEMA} -- -- Clean up in case a prior regression run failed SET client_min_messages TO 'warning'; DROP ROLE IF EXISTS regress_alter_generic_user1; DROP ROLE IF EXISTS regress_alter_generic_user2; DROP ROLE IF EXISTS regress_alter_generic_user3; RESET client_min_messages; CREATE USER regress_alter_generic_user3; CREATE USER regress_alter_generic_user2; CREATE USER regress_alter_generic_user1 IN ROLE regress_alter_generic_user3; CREATE SCHEMA alt_nsp1; CREATE SCHEMA alt_nsp2; GRANT ALL ON SCHEMA alt_nsp1, alt_nsp2 TO public; SET search_path = alt_nsp1, public; -- -- Function and Aggregate -- SET SESSION AUTHORIZATION regress_alter_generic_user1; CREATE FUNCTION alt_func1 (int) RETURNS int LANGUAGE sql AS 'SELECT $1 + 1' ; CREATE FUNCTION alt_func2 (int) RETURNS int LANGUAGE sql AS 'SELECT $1 - 1' ; CREATE AGGREGATE alt_agg1 ( SFUNC1 = int4pl, BASETYPE = int4, STYPE1 = int4, INITCOND = 0 ); CREATE AGGREGATE alt_agg2 ( SFUNC1 = int4mi, BASETYPE = int4, STYPE1 = int4, INITCOND = 0 ); ALTER AGGREGATE alt_func1 (int) RENAME TO alt_func3; -- failed (not aggregate) ALTER AGGREGATE alt_func1 (int) OWNER TO regress_alter_generic_user3; -- failed (not aggregate) ALTER AGGREGATE alt_func1 (int) SET SCHEMA alt_nsp2; -- failed (not aggregate) ALTER FUNCTION alt_func1 (int) RENAME TO alt_func2; -- failed (name conflict) ALTER FUNCTION alt_func1 (int) RENAME TO alt_func3; -- OK ALTER FUNCTION alt_func2 (int) OWNER TO regress_alter_generic_user2; -- failed (no role membership) ALTER FUNCTION alt_func2 (int) OWNER TO regress_alter_generic_user3; -- OK ALTER FUNCTION alt_func2 (int) SET SCHEMA alt_nsp1; -- OK, already there ALTER FUNCTION alt_func2 (int) SET SCHEMA alt_nsp2; -- OK ALTER AGGREGATE alt_agg1 (int) RENAME TO alt_agg2; -- failed (name conflict) ALTER AGGREGATE alt_agg1 (int) RENAME TO alt_agg3; -- OK ALTER AGGREGATE alt_agg2 (int) OWNER TO regress_alter_generic_user2; -- failed (no role membership) ALTER AGGREGATE alt_agg2 (int) OWNER TO regress_alter_generic_user3; -- OK ALTER AGGREGATE alt_agg2 (int) SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_generic_user2; CREATE FUNCTION alt_func1 (int) RETURNS int LANGUAGE sql AS 'SELECT $1 + 2' ; CREATE FUNCTION alt_func2 (int) RETURNS int LANGUAGE sql AS 'SELECT $1 - 2' ; CREATE AGGREGATE alt_agg1 ( SFUNC1 = int4pl, BASETYPE = int4, STYPE1 = int4, INITCOND = 100 ); CREATE AGGREGATE alt_agg2 ( SFUNC1 = int4mi, BASETYPE = int4, STYPE1 = int4, INITCOND = - 100 ); ALTER FUNCTION alt_func3 (int) RENAME TO alt_func4; -- failed (not owner) ALTER FUNCTION alt_func1 (int) RENAME TO alt_func4; -- OK ALTER FUNCTION alt_func3 (int) OWNER TO regress_alter_generic_user2; -- failed (not owner) ALTER FUNCTION alt_func2 (int) OWNER TO regress_alter_generic_user3; -- failed (no role membership) ALTER FUNCTION alt_func3 (int) SET SCHEMA alt_nsp2; -- failed (not owner) ALTER FUNCTION alt_func2 (int) SET SCHEMA alt_nsp2; -- failed (name conflicts) ALTER AGGREGATE alt_agg3 (int) RENAME TO alt_agg4; -- failed (not owner) ALTER AGGREGATE alt_agg1 (int) RENAME TO alt_agg4; -- OK ALTER AGGREGATE alt_agg3 (int) OWNER TO regress_alter_generic_user2; -- failed (not owner) ALTER AGGREGATE alt_agg2 (int) OWNER TO regress_alter_generic_user3; -- failed (no role membership) ALTER AGGREGATE alt_agg3 (int) SET SCHEMA alt_nsp2; -- failed (not owner) ALTER AGGREGATE alt_agg2 (int) SET SCHEMA alt_nsp2; -- failed (name conflict) RESET SESSION AUTHORIZATION; SELECT n.nspname, proname, prorettype::regtype, prokind, a.rolname FROM pg_proc p, pg_namespace n, pg_authid a WHERE p.pronamespace = n.oid AND p.proowner = a.oid AND n.nspname IN ('alt_nsp1', 'alt_nsp2') ORDER BY nspname, proname; -- -- We would test collations here, but it's not possible because the error -- messages tend to be nonportable. -- -- -- Conversion -- SET SESSION AUTHORIZATION regress_alter_generic_user1; CREATE CONVERSION alt_conv1 FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; CREATE CONVERSION alt_conv2 FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; ALTER CONVERSION alt_conv1 RENAME TO alt_conv2; -- failed (name conflict) ALTER CONVERSION alt_conv1 RENAME TO alt_conv3; -- OK ALTER CONVERSION alt_conv2 OWNER TO regress_alter_generic_user2; -- failed (no role membership) ALTER CONVERSION alt_conv2 OWNER TO regress_alter_generic_user3; -- OK ALTER CONVERSION alt_conv2 SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_generic_user2; CREATE CONVERSION alt_conv1 FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; CREATE CONVERSION alt_conv2 FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; ALTER CONVERSION alt_conv3 RENAME TO alt_conv4; -- failed (not owner) ALTER CONVERSION alt_conv1 RENAME TO alt_conv4; -- OK ALTER CONVERSION alt_conv3 OWNER TO regress_alter_generic_user2; -- failed (not owner) ALTER CONVERSION alt_conv2 OWNER TO regress_alter_generic_user3; -- failed (no role membership) ALTER CONVERSION alt_conv3 SET SCHEMA alt_nsp2; -- failed (not owner) ALTER CONVERSION alt_conv2 SET SCHEMA alt_nsp2; -- failed (name conflict) RESET SESSION AUTHORIZATION; SELECT n.nspname, c.conname, a.rolname FROM pg_conversion c, pg_namespace n, pg_authid a WHERE c.connamespace = n.oid AND c.conowner = a.oid AND n.nspname IN ('alt_nsp1', 'alt_nsp2') ORDER BY nspname, conname; -- -- Foreign Data Wrapper and Foreign Server -- CREATE FOREIGN DATA WRAPPER alt_fdw1; CREATE FOREIGN DATA WRAPPER alt_fdw2; CREATE SERVER alt_fserv1 FOREIGN DATA WRAPPER alt_fdw1; CREATE SERVER alt_fserv2 FOREIGN DATA WRAPPER alt_fdw2; ALTER FOREIGN DATA WRAPPER alt_fdw1 RENAME TO alt_fdw2; -- failed (name conflict) ALTER FOREIGN DATA WRAPPER alt_fdw1 RENAME TO alt_fdw3; -- OK ALTER SERVER alt_fserv1 RENAME TO alt_fserv2; -- failed (name conflict) ALTER SERVER alt_fserv1 RENAME TO alt_fserv3; -- OK SELECT fdwname FROM pg_foreign_data_wrapper WHERE fdwname LIKE 'alt_fdw%'; SELECT srvname FROM pg_foreign_server WHERE srvname LIKE 'alt_fserv%'; -- -- Procedural Language -- CREATE FUNCTION fn_opf12 (int4, int2) RETURNS BIGINT AS 'SELECT NULL::BIGINT;' LANGUAGE SQL; ALTER OPERATOR FAMILY alt_opf12 USING btree ADD FUNCTION 1 fn_opf12 (int4, int2); DROP OPERATOR FAMILY alt_opf12 USING btree; ROLLBACK; -- Should fail. hash comparison functions should return INTEGER in ALTER OPERATOR FAMILY ... ADD FUNCTION BEGIN TRANSACTION; CREATE OPERATOR FAMILY alt_opf13 USING HASH; CREATE FUNCTION fn_opf13 (int4) RETURNS BIGINT AS 'SELECT NULL::BIGINT;' LANGUAGE SQL; ALTER OPERATOR FAMILY alt_opf13 USING HASH ADD FUNCTION 1 fn_opf13 (int4); DROP OPERATOR FAMILY alt_opf13 USING HASH; ROLLBACK; -- Should fail. btree comparison functions should have two arguments in ALTER OPERATOR FAMILY ... ADD FUNCTION BEGIN TRANSACTION; CREATE OPERATOR FAMILY alt_opf14 USING btree; CREATE FUNCTION fn_opf14 (int4) RETURNS BIGINT AS 'SELECT NULL::BIGINT;' LANGUAGE SQL; ALTER OPERATOR FAMILY alt_opf14 USING btree ADD FUNCTION 1 fn_opf14 (int4); DROP OPERATOR FAMILY alt_opf14 USING btree; ROLLBACK; -- Should fail. hash comparison functions should have one argument in ALTER OPERATOR FAMILY ... ADD FUNCTION BEGIN TRANSACTION; CREATE OPERATOR FAMILY alt_opf15 USING HASH; CREATE FUNCTION fn_opf15 (int4, int2) RETURNS BIGINT AS 'SELECT NULL::BIGINT;' LANGUAGE SQL; ALTER OPERATOR FAMILY alt_opf15 USING HASH ADD FUNCTION 1 fn_opf15 (int4, int2); DROP OPERATOR FAMILY alt_opf15 USING HASH; ROLLBACK; -- Should fail. In gist throw an error when giving different data types for function argument -- without defining left / right type in ALTER OPERATOR FAMILY ... ADD FUNCTION CREATE OPERATOR FAMILY alt_opf16 USING gist; ALTER OPERATOR FAMILY alt_opf16 USING gist ADD FUNCTION 1 btint42cmp(int4, int2); DROP OPERATOR FAMILY alt_opf16 USING gist; -- Should fail. duplicate operator number / function number in ALTER OPERATOR FAMILY ... ADD FUNCTION CREATE OPERATOR FAMILY alt_opf17 USING btree; ALTER OPERATOR FAMILY alt_opf17 USING btree ADD OPERATOR 1 < (int4, int4), OPERATOR 1 < (int4, int4); -- operator # appears twice in same statement ALTER OPERATOR FAMILY alt_opf17 USING btree ADD OPERATOR 1 < (int4, int4); -- operator 1 requested first-time ALTER OPERATOR FAMILY alt_opf17 USING btree ADD OPERATOR 1 < (int4, int4); -- operator 1 requested again in separate statement ALTER OPERATOR FAMILY alt_opf17 USING btree ADD OPERATOR 1 < (int4, int2), OPERATOR 2 <= (int4, int2), OPERATOR 3 = (int4, int2), OPERATOR 4 >= (int4, int2), OPERATOR 5 > (int4, int2), FUNCTION 1 btint42cmp(int4, int2), FUNCTION 1 btint42cmp(int4, int2); -- procedure 1 appears twice in same statement ALTER OPERATOR FAMILY alt_opf17 USING btree ADD OPERATOR 1 < (int4, int2), OPERATOR 2 <= (int4, int2), OPERATOR 3 = (int4, int2), OPERATOR 4 >= (int4, int2), OPERATOR 5 > (int4, int2), FUNCTION 1 btint42cmp(int4, int2); -- procedure 1 appears first time ALTER OPERATOR FAMILY alt_opf17 USING btree ADD OPERATOR 1 < (int4, int2), OPERATOR 2 <= (int4, int2), OPERATOR 3 = (int4, int2), OPERATOR 4 >= (int4, int2), OPERATOR 5 > (int4, int2), FUNCTION 1 btint42cmp(int4, int2); -- procedure 1 requested again in separate statement DROP OPERATOR FAMILY alt_opf17 USING btree; -- Should fail. Ensure that DROP requests for missing OPERATOR / FUNCTIONS -- return appropriate message in ALTER OPERATOR FAMILY ... DROP OPERATOR / FUNCTION CREATE OPERATOR FAMILY alt_opf18 USING btree; ALTER OPERATOR FAMILY alt_opf18 USING btree DROP OPERATOR 1 (int4, int4); ALTER OPERATOR FAMILY alt_opf18 USING btree ADD OPERATOR 1 < (int4, int2), OPERATOR 2 <= (int4, int2), OPERATOR 3 = (int4, int2), OPERATOR 4 >= (int4, int2), OPERATOR 5 > (int4, int2), FUNCTION 1 btint42cmp(int4, int2); ALTER OPERATOR FAMILY alt_opf18 USING btree DROP FUNCTION 2 (int4, int4); DROP OPERATOR FAMILY alt_opf18 USING btree; -- -- Statistics -- SET SESSION AUTHORIZATION regress_alter_generic_user1; CREATE TABLE alt_regress_1 ( a integer, b integer ); CREATE STATISTICS alt_stat1 ON a, b FROM alt_regress_1; CREATE STATISTICS alt_stat2 ON a, b FROM alt_regress_1; ALTER STATISTICS alt_stat1 RENAME TO alt_stat2; -- failed (name conflict) ALTER STATISTICS alt_stat1 RENAME TO alt_stat3; -- failed (name conflict) ALTER STATISTICS alt_stat2 OWNER TO regress_alter_generic_user2; -- failed (no role membership) ALTER STATISTICS alt_stat2 OWNER TO regress_alter_generic_user3; -- OK ALTER STATISTICS alt_stat2 SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_generic_user2; CREATE TABLE alt_regress_2 ( a integer, b integer ); CREATE STATISTICS alt_stat1 ON a, b FROM alt_regress_2; CREATE STATISTICS alt_stat2 ON a, b FROM alt_regress_2; ALTER STATISTICS alt_stat3 RENAME TO alt_stat4; -- failed (not owner) ALTER STATISTICS alt_stat1 RENAME TO alt_stat4; -- OK ALTER STATISTICS alt_stat3 OWNER TO regress_alter_generic_user2; -- failed (not owner) ALTER STATISTICS alt_stat2 OWNER TO regress_alter_generic_user3; -- failed (no role membership) ALTER STATISTICS alt_stat3 SET SCHEMA alt_nsp2; -- failed (not owner) ALTER STATISTICS alt_stat2 SET SCHEMA alt_nsp2; -- failed (name conflict) RESET SESSION AUTHORIZATION; SELECT nspname, stxname, rolname FROM pg_statistic_ext s, pg_namespace n, pg_authid a WHERE s.stxnamespace = n.oid AND s.stxowner = a.oid AND n.nspname IN ('alt_nsp1', 'alt_nsp2') ORDER BY nspname, stxname; -- -- Text Search Dictionary -- SET SESSION AUTHORIZATION regress_alter_generic_user1; CREATE TEXT SEARCH DICTIONARY alt_ts_dict1 ( TEMPLATE = simple ); CREATE TEXT SEARCH DICTIONARY alt_ts_dict2 ( TEMPLATE = simple ); ALTER TEXT SEARCH DICTIONARY alt_ts_dict1 RENAME TO alt_ts_dict2; -- failed (name conflict) ALTER TEXT SEARCH DICTIONARY alt_ts_dict1 RENAME TO alt_ts_dict3; -- OK ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 OWNER TO regress_alter_generic_user2; -- failed (no role membership) ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 OWNER TO regress_alter_generic_user3; -- OK ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_generic_user2; CREATE TEXT SEARCH DICTIONARY alt_ts_dict1 ( TEMPLATE = simple ); CREATE TEXT SEARCH DICTIONARY alt_ts_dict2 ( TEMPLATE = simple ); ALTER TEXT SEARCH DICTIONARY alt_ts_dict3 RENAME TO alt_ts_dict4; -- failed (not owner) ALTER TEXT SEARCH DICTIONARY alt_ts_dict1 RENAME TO alt_ts_dict4; -- OK ALTER TEXT SEARCH DICTIONARY alt_ts_dict3 OWNER TO regress_alter_generic_user2; -- failed (not owner) ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 OWNER TO regress_alter_generic_user3; -- failed (no role membership) ALTER TEXT SEARCH DICTIONARY alt_ts_dict3 SET SCHEMA alt_nsp2; -- failed (not owner) ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 SET SCHEMA alt_nsp2; -- failed (name conflict) RESET SESSION AUTHORIZATION; SELECT nspname, dictname, rolname FROM pg_ts_dict t, pg_namespace n, pg_authid a WHERE t.dictnamespace = n.oid AND t.dictowner = a.oid AND n.nspname IN ('alt_nsp1', 'alt_nsp2') ORDER BY nspname, dictname; -- -- Text Search Configuration -- SET SESSION AUTHORIZATION regress_alter_generic_user1; CREATE TEXT SEARCH CONFIGURATION alt_ts_conf1 ( COPY = english ); CREATE TEXT SEARCH CONFIGURATION alt_ts_conf2 ( COPY = english ); ALTER TEXT SEARCH CONFIGURATION alt_ts_conf1 RENAME TO alt_ts_conf2; -- failed (name conflict) ALTER TEXT SEARCH CONFIGURATION alt_ts_conf1 RENAME TO alt_ts_conf3; -- OK ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 OWNER TO regress_alter_generic_user2; -- failed (no role membership) ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 OWNER TO regress_alter_generic_user3; -- OK ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_generic_user2; CREATE TEXT SEARCH CONFIGURATION alt_ts_conf1 ( COPY = english ); CREATE TEXT SEARCH CONFIGURATION alt_ts_conf2 ( COPY = english ); ALTER TEXT SEARCH CONFIGURATION alt_ts_conf3 RENAME TO alt_ts_conf4; -- failed (not owner) ALTER TEXT SEARCH CONFIGURATION alt_ts_conf1 RENAME TO alt_ts_conf4; -- OK ALTER TEXT SEARCH CONFIGURATION alt_ts_conf3 OWNER TO regress_alter_generic_user2; -- failed (not owner) ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 OWNER TO regress_alter_generic_user3; -- failed (no role membership) ALTER TEXT SEARCH CONFIGURATION alt_ts_conf3 SET SCHEMA alt_nsp2; -- failed (not owner) ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 SET SCHEMA alt_nsp2; -- failed (name conflict) RESET SESSION AUTHORIZATION; SELECT nspname, cfgname, rolname FROM pg_ts_config t, pg_namespace n, pg_authid a WHERE t.cfgnamespace = n.oid AND t.cfgowner = a.oid AND n.nspname IN ('alt_nsp1', 'alt_nsp2') ORDER BY nspname, cfgname; -- -- Text Search Template -- CREATE TEXT SEARCH TEMPLATE alt_ts_temp1 ( lexize = dsimple_lexize ); CREATE TEXT SEARCH TEMPLATE alt_ts_temp2 ( lexize = dsimple_lexize ); ALTER TEXT SEARCH TEMPLATE alt_ts_temp1 RENAME TO alt_ts_temp2; -- failed (name conflict) ALTER TEXT SEARCH TEMPLATE alt_ts_temp1 RENAME TO alt_ts_temp3; -- OK ALTER TEXT SEARCH TEMPLATE alt_ts_temp2 SET SCHEMA alt_nsp2; -- OK CREATE TEXT SEARCH TEMPLATE alt_ts_temp2 ( lexize = dsimple_lexize ); ALTER TEXT SEARCH TEMPLATE alt_ts_temp2 SET SCHEMA alt_nsp2; -- failed (name conflict) -- invalid: non-lowercase quoted identifiers CREATE TEXT SEARCH TEMPLATE tstemp_case ( "Init" = init_function ); SELECT nspname, tmplname FROM pg_ts_template t, pg_namespace n WHERE t.tmplnamespace = n.oid AND nspname LIKE 'alt_nsp%' ORDER BY nspname, tmplname; -- -- Text Search Parser -- CREATE TEXT SEARCH PARSER alt_ts_prs1 ( START = prsd_start, gettoken = prsd_nexttoken, END = prsd_end, lextypes = prsd_lextype ); CREATE TEXT SEARCH PARSER alt_ts_prs2 ( START = prsd_start, gettoken = prsd_nexttoken, END = prsd_end, lextypes = prsd_lextype ); ALTER TEXT SEARCH PARSER alt_ts_prs1 RENAME TO alt_ts_prs2; -- failed (name conflict) ALTER TEXT SEARCH PARSER alt_ts_prs1 RENAME TO alt_ts_prs3; -- OK ALTER TEXT SEARCH PARSER alt_ts_prs2 SET SCHEMA alt_nsp2; -- OK CREATE TEXT SEARCH PARSER alt_ts_prs2 ( START = prsd_start, gettoken = prsd_nexttoken, END = prsd_end, lextypes = prsd_lextype ); ALTER TEXT SEARCH PARSER alt_ts_prs2 SET SCHEMA alt_nsp2; -- failed (name conflict) -- invalid: non-lowercase quoted identifiers CREATE TEXT SEARCH PARSER tspars_case ( "Start" = start_function ); SELECT nspname, prsname FROM pg_ts_parser t, pg_namespace n WHERE t.prsnamespace = n.oid AND nspname LIKE 'alt_nsp%' ORDER BY nspname, prsname; --- --- Cleanup resources --- DROP FOREIGN DATA WRAPPER alt_fdw2 CASCADE; DROP FOREIGN DATA WRAPPER alt_fdw3 CASCADE; pgFormatter-4.2/t/pg-test-files/expected/alter_operator.sql000066400000000000000000000070751361326045100241550ustar00rootroot00000000000000CREATE FUNCTION alter_op_test_fn (boolean, boolean) RETURNS boolean AS $$ SELECT NULL::BOOLEAN; $$ LANGUAGE sql IMMUTABLE; CREATE FUNCTION customcontsel (internal, oid, internal, integer) RETURNS float8 AS 'contsel' LANGUAGE internal STABLE STRICT; CREATE OPERATOR === ( LEFTARG = boolean, RIGHTARG = boolean, PROCEDURE = alter_op_test_fn, COMMUTATOR = ===, NEGATOR = !==, RESTRICT = customcontsel, JOIN = contjoinsel, HASHES, MERGES ); SELECT pg_describe_object(refclassid, refobjid, refobjsubid) AS ref, deptype FROM pg_depend WHERE classid = 'pg_operator'::regclass AND objid = '===(bool,bool)'::regoperator ORDER BY 1; -- -- Reset and set params -- ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NONE); ALTER OPERATOR === (boolean, boolean) SET (JOIN = NONE); SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '===' AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype; SELECT pg_describe_object(refclassid, refobjid, refobjsubid) AS ref, deptype FROM pg_depend WHERE classid = 'pg_operator'::regclass AND objid = '===(bool,bool)'::regoperator ORDER BY 1; ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = contsel); ALTER OPERATOR === (boolean, boolean) SET (JOIN = contjoinsel); SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '===' AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype; SELECT pg_describe_object(refclassid, refobjid, refobjsubid) AS ref, deptype FROM pg_depend WHERE classid = 'pg_operator'::regclass AND objid = '===(bool,bool)'::regoperator ORDER BY 1; ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NONE, JOIN = NONE); SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '===' AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype; SELECT pg_describe_object(refclassid, refobjid, refobjsubid) AS ref, deptype FROM pg_depend WHERE classid = 'pg_operator'::regclass AND objid = '===(bool,bool)'::regoperator ORDER BY 1; ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = customcontsel, JOIN = contjoinsel); SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '===' AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype; SELECT pg_describe_object(refclassid, refobjid, refobjsubid) AS ref, deptype FROM pg_depend WHERE classid = 'pg_operator'::regclass AND objid = '===(bool,bool)'::regoperator ORDER BY 1; -- -- Test invalid options. -- ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = ====); ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = ====); ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = non_existent_func); ALTER OPERATOR === (boolean, boolean) SET (JOIN = non_existent_func); ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = !==); ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = !==); -- invalid: non-lowercase quoted identifiers ALTER OPERATOR & (bit, bit) SET ("Restrict" = _int_contsel, "Join" = _int_contjoinsel); -- -- Test permission check. Must be owner to ALTER OPERATOR. -- CREATE USER regress_alter_op_user; SET SESSION AUTHORIZATION regress_alter_op_user; ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NONE); -- Clean up RESET SESSION AUTHORIZATION; DROP USER regress_alter_op_user; DROP OPERATOR === (boolean, boolean); DROP FUNCTION customcontsel (internal, oid, internal, integer); DROP FUNCTION alter_op_test_fn (boolean, boolean); pgFormatter-4.2/t/pg-test-files/expected/alter_table.sql000066400000000000000000003254421361326045100234120ustar00rootroot00000000000000-- -- ALTER_TABLE -- -- Clean up in case a prior regression run failed SET client_min_messages TO 'warning'; DROP ROLE IF EXISTS regress_alter_table_user1; RESET client_min_messages; CREATE USER regress_alter_table_user1; -- -- add attribute -- CREATE TABLE attmp ( initial int4 ); COMMENT ON TABLE attmp_wrong IS 'table comment'; COMMENT ON TABLE attmp IS 'table comment'; COMMENT ON TABLE attmp IS NULL; ALTER TABLE attmp ADD COLUMN xmin integer; -- fails ALTER TABLE attmp ADD COLUMN a int4 DEFAULT 3; ALTER TABLE attmp ADD COLUMN b name; ALTER TABLE attmp ADD COLUMN c text; ALTER TABLE attmp ADD COLUMN d float8; ALTER TABLE attmp ADD COLUMN e float4; ALTER TABLE attmp ADD COLUMN f int2; ALTER TABLE attmp ADD COLUMN g polygon; ALTER TABLE attmp ADD COLUMN i char; ALTER TABLE attmp ADD COLUMN k int4; ALTER TABLE attmp ADD COLUMN l tid; ALTER TABLE attmp ADD COLUMN m xid; ALTER TABLE attmp ADD COLUMN n oidvector; --ALTER TABLE attmp ADD COLUMN o lock; ALTER TABLE attmp ADD COLUMN p boolean; ALTER TABLE attmp ADD COLUMN q point; ALTER TABLE attmp ADD COLUMN r lseg; ALTER TABLE attmp ADD COLUMN s path; ALTER TABLE attmp ADD COLUMN t box; ALTER TABLE attmp ADD COLUMN v timestamp; ALTER TABLE attmp ADD COLUMN w interval; ALTER TABLE attmp ADD COLUMN x float8[]; ALTER TABLE attmp ADD COLUMN y float4[]; ALTER TABLE attmp ADD COLUMN z int2[]; INSERT INTO attmp (a, b, c, d, e, f, g, i, k, l, m, n, p, q, r, s, t, v, w, x, y, z) VALUES (4, 'name', 'text', 4.1, 4.1, 2, '(4.1,4.1,3.1,3.1)', 'c', 314159, '(1,1)', '512', '1 2 3 4 5 6 7 8', TRUE, '(1.1,1.1)', '(4.1,4.1,3.1,3.1)', '(0,2,4.1,4.1,3.1,3.1)', '(4.1,4.1,3.1,3.1)', 'epoch', '01:00:10', '{1.0,2.0,3.0,4.0}', '{1.0,2.0,3.0,4.0}', '{1,2,3,4}'); SELECT * FROM attmp; DROP TABLE attmp; -- the wolf bug - schema mods caused inconsistent row descriptors CREATE TABLE attmp ( initial int4 ); ALTER TABLE attmp ADD COLUMN a int4; ALTER TABLE attmp ADD COLUMN b name; ALTER TABLE attmp ADD COLUMN c text; ALTER TABLE attmp ADD COLUMN d float8; ALTER TABLE attmp ADD COLUMN e float4; ALTER TABLE attmp ADD COLUMN f int2; ALTER TABLE attmp ADD COLUMN g polygon; ALTER TABLE attmp ADD COLUMN i char; ALTER TABLE attmp ADD COLUMN k int4; ALTER TABLE attmp ADD COLUMN l tid; ALTER TABLE attmp ADD COLUMN m xid; ALTER TABLE attmp ADD COLUMN n oidvector; --ALTER TABLE attmp ADD COLUMN o lock; ALTER TABLE attmp ADD COLUMN p boolean; ALTER TABLE attmp ADD COLUMN q point; ALTER TABLE attmp ADD COLUMN r lseg; ALTER TABLE attmp ADD COLUMN s path; ALTER TABLE attmp ADD COLUMN t box; ALTER TABLE attmp ADD COLUMN v timestamp; ALTER TABLE attmp ADD COLUMN w interval; ALTER TABLE attmp ADD COLUMN x float8[]; ALTER TABLE attmp ADD COLUMN y float4[]; ALTER TABLE attmp ADD COLUMN z int2[]; INSERT INTO attmp (a, b, c, d, e, f, g, i, k, l, m, n, p, q, r, s, t, v, w, x, y, z) VALUES (4, 'name', 'text', 4.1, 4.1, 2, '(4.1,4.1,3.1,3.1)', 'c', 314159, '(1,1)', '512', '1 2 3 4 5 6 7 8', TRUE, '(1.1,1.1)', '(4.1,4.1,3.1,3.1)', '(0,2,4.1,4.1,3.1,3.1)', '(4.1,4.1,3.1,3.1)', 'epoch', '01:00:10', '{1.0,2.0,3.0,4.0}', '{1.0,2.0,3.0,4.0}', '{1,2,3,4}'); SELECT * FROM attmp; CREATE INDEX attmp_idx ON attmp (a, (d + e), b); ALTER INDEX attmp_idx ALTER COLUMN 0 SET STATISTICS 1000; ALTER INDEX attmp_idx ALTER COLUMN 1 SET STATISTICS 1000; ALTER INDEX attmp_idx ALTER COLUMN 2 SET STATISTICS 1000; \d+ attmp_idx ALTER INDEX attmp_idx ALTER COLUMN 3 SET STATISTICS 1000; ALTER INDEX attmp_idx ALTER COLUMN 4 SET STATISTICS 1000; ALTER INDEX attmp_idx ALTER COLUMN 2 SET STATISTICS - 1; DROP TABLE attmp; -- -- rename - check on both non-temp and temp tables -- CREATE TABLE attmp ( regtable int ); CREATE TEMP TABLE attmp ( attmptable int ); ALTER TABLE attmp RENAME TO attmp_new; SELECT * FROM attmp; SELECT * FROM attmp_new; ALTER TABLE attmp RENAME TO attmp_new2; SELECT * FROM attmp; -- should fail SELECT * FROM attmp_new; SELECT * FROM attmp_new2; DROP TABLE attmp_new; DROP TABLE attmp_new2; -- check rename of partitioned tables and indexes also CREATE TABLE part_attmp ( a int PRIMARY KEY ) PARTITION BY RANGE (a); CREATE TABLE part_attmp1 PARTITION OF part_attmp FOR VALUES FROM (0) TO (100); ALTER INDEX part_attmp_pkey RENAME TO part_attmp_index; ALTER INDEX part_attmp1_pkey RENAME TO part_attmp1_index; ALTER TABLE part_attmp RENAME TO part_at2tmp; ALTER TABLE part_attmp1 RENAME TO part_at2tmp1; SET ROLE regress_alter_table_user1; ALTER INDEX part_attmp_index RENAME TO fail; ALTER INDEX part_attmp1_index RENAME TO fail; ALTER TABLE part_at2tmp RENAME TO fail; ALTER TABLE part_at2tmp1 RENAME TO fail; RESET ROLE; DROP TABLE part_at2tmp; -- -- check renaming to a table's array type's autogenerated name -- (the array type's name should get out of the way) -- CREATE TABLE attmp_array ( id int ); CREATE TABLE attmp_array2 ( id int ); SELECT typname FROM pg_type WHERE oid = 'attmp_array[]'::regtype; SELECT typname FROM pg_type WHERE oid = 'attmp_array2[]'::regtype; ALTER TABLE attmp_array2 RENAME TO _attmp_array; SELECT typname FROM pg_type WHERE oid = 'attmp_array[]'::regtype; SELECT typname FROM pg_type WHERE oid = '_attmp_array[]'::regtype; DROP TABLE _attmp_array; DROP TABLE attmp_array; -- renaming to table's own array type's name is an interesting corner case CREATE TABLE attmp_array ( id int ); SELECT typname FROM pg_type WHERE oid = 'attmp_array[]'::regtype; ALTER TABLE attmp_array RENAME TO _attmp_array; SELECT typname FROM pg_type WHERE oid = '_attmp_array[]'::regtype; DROP TABLE _attmp_array; -- ALTER TABLE ... RENAME on non-table relations -- renaming indexes (FIXME: this should probably test the index's functionality) ALTER INDEX IF EXISTS __onek_unique1 RENAME TO attmp_onek_unique1; ALTER INDEX IF EXISTS __attmp_onek_unique1 RENAME TO onek_unique1; ALTER INDEX onek_unique1 RENAME TO attmp_onek_unique1; ALTER INDEX attmp_onek_unique1 RENAME TO onek_unique1; SET ROLE regress_alter_table_user1; ALTER INDEX onek_unique1 RENAME TO fail; -- permission denied RESET ROLE; -- renaming views CREATE VIEW attmp_view (unique1) AS SELECT unique1 FROM tenk1; ALTER TABLE attmp_view RENAME TO attmp_view_new; SET ROLE regress_alter_table_user1; ALTER VIEW attmp_view_new RENAME TO fail; -- permission denied RESET ROLE; -- hack to ensure we get an indexscan here SET enable_seqscan TO OFF; SET enable_bitmapscan TO OFF; -- 5 values, sorted SELECT unique1 FROM tenk1 WHERE unique1 < 5; RESET enable_seqscan; RESET enable_bitmapscan; DROP VIEW attmp_view_new; -- toast-like relation name ALTER TABLE stud_emp RENAME TO pg_toast_stud_emp; ALTER TABLE pg_toast_stud_emp RENAME TO stud_emp; -- renaming index should rename constraint as well ALTER TABLE onek ADD CONSTRAINT onek_unique1_constraint UNIQUE (unique1); ALTER INDEX onek_unique1_constraint RENAME TO onek_unique1_constraint_foo; ALTER TABLE onek DROP CONSTRAINT onek_unique1_constraint_foo; -- renaming constraint ALTER TABLE onek ADD CONSTRAINT onek_check_constraint CHECK (unique1 >= 0); ALTER TABLE onek RENAME CONSTRAINT onek_check_constraint TO onek_check_constraint_foo; ALTER TABLE onek DROP CONSTRAINT onek_check_constraint_foo; -- renaming constraint should rename index as well ALTER TABLE onek ADD CONSTRAINT onek_unique1_constraint UNIQUE (unique1); DROP INDEX onek_unique1_constraint; -- to see whether it's there ALTER TABLE onek RENAME CONSTRAINT onek_unique1_constraint TO onek_unique1_constraint_foo; DROP INDEX onek_unique1_constraint_foo; -- to see whether it's there ALTER TABLE onek DROP CONSTRAINT onek_unique1_constraint_foo; -- renaming constraints vs. inheritance CREATE TABLE constraint_rename_test ( a int CONSTRAINT con1 CHECK (a > 0), b int, c int ); \d constraint_rename_test CREATE TABLE constraint_rename_test2 ( a int CONSTRAINT con1 CHECK (a > 0), d int ) INHERITS ( constraint_rename_test ); \d constraint_rename_test2 ALTER TABLE constraint_rename_test2 RENAME CONSTRAINT con1 TO con1foo; -- fail ALTER TABLE ONLY constraint_rename_test RENAME CONSTRAINT con1 TO con1foo; -- fail ALTER TABLE constraint_rename_test RENAME CONSTRAINT con1 TO con1foo; -- ok \d constraint_rename_test \d constraint_rename_test2 ALTER TABLE constraint_rename_test ADD CONSTRAINT con2 CHECK (b > 0) NO INHERIT; ALTER TABLE ONLY constraint_rename_test RENAME CONSTRAINT con2 TO con2foo; -- ok ALTER TABLE constraint_rename_test RENAME CONSTRAINT con2foo TO con2bar; -- ok \d constraint_rename_test \d constraint_rename_test2 ALTER TABLE constraint_rename_test ADD CONSTRAINT con3 PRIMARY KEY (a); ALTER TABLE constraint_rename_test RENAME CONSTRAINT con3 TO con3foo; -- ok \d constraint_rename_test \d constraint_rename_test2 DROP TABLE constraint_rename_test2; DROP TABLE constraint_rename_test; ALTER TABLE IF EXISTS constraint_not_exist RENAME CONSTRAINT con3 TO con3foo; -- ok ALTER TABLE IF EXISTS constraint_rename_test ADD CONSTRAINT con4 UNIQUE (a); -- renaming constraints with cache reset of target relation CREATE TABLE constraint_rename_cache ( a int, CONSTRAINT chk_a CHECK (a > 0), PRIMARY KEY (a) ); ALTER TABLE constraint_rename_cache RENAME CONSTRAINT chk_a TO chk_a_new; ALTER TABLE constraint_rename_cache RENAME CONSTRAINT constraint_rename_cache_pkey TO constraint_rename_pkey_new; CREATE TABLE like_constraint_rename_cache ( LIKE constraint_rename_cache INCLUDING ALL ); \d like_constraint_rename_cache DROP TABLE constraint_rename_cache; DROP TABLE like_constraint_rename_cache; -- FOREIGN KEY CONSTRAINT adding TEST CREATE TABLE attmp2 ( a int PRIMARY KEY ); CREATE TABLE attmp3 ( a int, b int ); CREATE TABLE attmp4 ( a int, b int, UNIQUE (a, b) ); CREATE TABLE attmp5 ( a int, b int ); -- Insert rows into attmp2 (pktable) INSERT INTO attmp2 VALUES (1); INSERT INTO attmp2 VALUES (2); INSERT INTO attmp2 VALUES (3); INSERT INTO attmp2 VALUES (4); -- Insert rows into attmp3 INSERT INTO attmp3 VALUES (1, 10); INSERT INTO attmp3 VALUES (1, 20); INSERT INTO attmp3 VALUES (5, 50); -- Try (and fail) to add constraint due to invalid source columns ALTER TABLE attmp3 ADD CONSTRAINT attmpconstr FOREIGN KEY (c) REFERENCES attmp2 MATCH FULL; -- Try (and fail) to add constraint due to invalid destination columns explicitly given ALTER TABLE attmp3 ADD CONSTRAINT attmpconstr FOREIGN KEY (a) REFERENCES attmp2 (b) MATCH FULL; -- Try (and fail) to add constraint due to invalid data ALTER TABLE attmp3 ADD CONSTRAINT attmpconstr FOREIGN KEY (a) REFERENCES attmp2 MATCH FULL; -- Delete failing row DELETE FROM attmp3 WHERE a = 5; -- Try (and succeed) ALTER TABLE attmp3 ADD CONSTRAINT attmpconstr FOREIGN KEY (a) REFERENCES attmp2 MATCH FULL; ALTER TABLE attmp3 DROP CONSTRAINT attmpconstr; INSERT INTO attmp3 VALUES (5, 50); -- Try NOT VALID and then VALIDATE CONSTRAINT, but fails. Delete failure then re-validate ALTER TABLE attmp3 ADD CONSTRAINT attmpconstr FOREIGN KEY (a) REFERENCES attmp2 MATCH FULL NOT VALID; ALTER TABLE attmp3 validate CONSTRAINT attmpconstr; -- Delete failing row DELETE FROM attmp3 WHERE a = 5; -- Try (and succeed) and repeat to show it works on already valid constraint ALTER TABLE attmp3 validate CONSTRAINT attmpconstr; ALTER TABLE attmp3 validate CONSTRAINT attmpconstr; -- Try a non-verified CHECK constraint ALTER TABLE attmp3 ADD CONSTRAINT b_greater_than_ten CHECK (b > 10); -- fail ALTER TABLE attmp3 ADD CONSTRAINT b_greater_than_ten CHECK (b > 10) NOT VALID; -- succeeds ALTER TABLE attmp3 VALIDATE CONSTRAINT b_greater_than_ten; -- fails DELETE FROM attmp3 WHERE NOT b > 10; ALTER TABLE attmp3 VALIDATE CONSTRAINT b_greater_than_ten; -- succeeds ALTER TABLE attmp3 VALIDATE CONSTRAINT b_greater_than_ten; -- succeeds -- Test inherited NOT VALID CHECK constraints SELECT * FROM attmp3; CREATE TABLE attmp6 () INHERITS ( attmp3 ); CREATE TABLE attmp7 () INHERITS ( attmp3 ); INSERT INTO attmp6 VALUES (6, 30), (7, 16); ALTER TABLE attmp3 ADD CONSTRAINT b_le_20 CHECK (b <= 20) NOT VALID; ALTER TABLE attmp3 VALIDATE CONSTRAINT b_le_20; -- fails DELETE FROM attmp6 WHERE b > 20; ALTER TABLE attmp3 VALIDATE CONSTRAINT b_le_20; -- succeeds -- An already validated constraint must not be revalidated CREATE FUNCTION boo (int) RETURNS int IMMUTABLE STRICT LANGUAGE plpgsql AS $$ BEGIN RAISE NOTICE 'boo: %', $1; RETURN $1; END; $$; INSERT INTO attmp7 VALUES (8, 18); ALTER TABLE attmp7 ADD CONSTRAINT IDENTITY CHECK (b = boo (b)); ALTER TABLE attmp3 ADD CONSTRAINT IDENTITY CHECK (b = boo (b)) NOT VALID; ALTER TABLE attmp3 VALIDATE CONSTRAINT IDENTITY; -- A NO INHERIT constraint should not be looked for in children during VALIDATE CONSTRAINT CREATE TABLE parent_noinh_convalid ( a int ); CREATE TABLE child_noinh_convalid () INHERITS ( parent_noinh_convalid ); INSERT INTO parent_noinh_convalid VALUES (1); INSERT INTO child_noinh_convalid VALUES (1); ALTER TABLE parent_noinh_convalid ADD CONSTRAINT check_a_is_2 CHECK (a = 2) NO inherit NOT valid; -- fail, because of the row in parent ALTER TABLE parent_noinh_convalid validate CONSTRAINT check_a_is_2; DELETE FROM ONLY parent_noinh_convalid; -- ok (parent itself contains no violating rows) ALTER TABLE parent_noinh_convalid validate CONSTRAINT check_a_is_2; SELECT convalidated FROM pg_constraint WHERE conrelid = 'parent_noinh_convalid'::regclass AND conname = 'check_a_is_2'; -- cleanup DROP TABLE parent_noinh_convalid, child_noinh_convalid; -- Try (and fail) to create constraint from attmp5(a) to attmp4(a) - unique constraint on -- attmp4 is a,b ALTER TABLE attmp5 ADD CONSTRAINT attmpconstr FOREIGN KEY (a) REFERENCES attmp4 (a) MATCH FULL; DROP TABLE attmp7; DROP TABLE attmp6; DROP TABLE attmp5; DROP TABLE attmp4; DROP TABLE attmp3; DROP TABLE attmp2; -- NOT VALID with plan invalidation -- ensure we don't use a constraint for -- exclusion until validated SET constraint_exclusion TO 'partition'; CREATE TABLE nv_parent ( d date, CHECK (FALSE) NO inherit NOT valid ); -- not valid constraint added at creation time should automatically become valid \d nv_parent CREATE TABLE nv_child_2010 () INHERITS ( nv_parent ); CREATE TABLE nv_child_2011 () INHERITS ( nv_parent ); ALTER TABLE nv_child_2010 ADD CHECK (d BETWEEN '2010-01-01'::date AND '2010-12-31'::date) NOT valid; ALTER TABLE nv_child_2011 ADD CHECK (d BETWEEN '2011-01-01'::date AND '2011-12-31'::date) NOT valid; EXPLAIN ( COSTS OFF ) SELECT * FROM nv_parent WHERE d BETWEEN '2011-08-01' AND '2011-08-31'; CREATE TABLE nv_child_2009 ( CHECK (d BETWEEN '2009-01-01'::date AND '2009-12-31'::date) ) INHERITS ( nv_parent ); EXPLAIN ( COSTS OFF ) SELECT * FROM nv_parent WHERE d BETWEEN '2011-08-01'::date AND '2011-08-31'::date; EXPLAIN ( COSTS OFF ) SELECT * FROM nv_parent WHERE d BETWEEN '2009-08-01'::date AND '2009-08-31'::date; -- after validation, the constraint should be used ALTER TABLE nv_child_2011 VALIDATE CONSTRAINT nv_child_2011_d_check; EXPLAIN ( COSTS OFF ) SELECT * FROM nv_parent WHERE d BETWEEN '2009-08-01'::date AND '2009-08-31'::date; -- add an inherited NOT VALID constraint ALTER TABLE nv_parent ADD CHECK (d BETWEEN '2001-01-01'::date AND '2099-12-31'::date) NOT valid; \d nv_child_2009 -- we leave nv_parent and children around to help test pg_dump logic -- Foreign key adding test with mixed types -- Note: these tables are TEMP to avoid name conflicts when this test -- is run in parallel with foreign_key.sql. CREATE TEMP TABLE PKTABLE ( ptest1 int PRIMARY KEY ); INSERT INTO PKTABLE VALUES (42); CREATE TEMP TABLE FKTABLE ( ftest1 inet ); -- This next should fail, because int=inet does not exist ALTER TABLE FKTABLE ADD FOREIGN KEY (ftest1) REFERENCES pktable; -- This should also fail for the same reason, but here we -- give the column name ALTER TABLE FKTABLE ADD FOREIGN KEY (ftest1) REFERENCES pktable (ptest1); DROP TABLE FKTABLE; -- This should succeed, even though they are different types, -- because int=int8 exists and is a member of the integer opfamily CREATE TEMP TABLE FKTABLE ( ftest1 int8 ); ALTER TABLE FKTABLE ADD FOREIGN KEY (ftest1) REFERENCES pktable; -- Check it actually works INSERT INTO FKTABLE VALUES (42); -- should succeed INSERT INTO FKTABLE VALUES (43); -- should fail DROP TABLE FKTABLE; -- This should fail, because we'd have to cast numeric to int which is -- not an implicit coercion (or use numeric=numeric, but that's not part -- of the integer opfamily) CREATE TEMP TABLE FKTABLE ( ftest1 numeric ); ALTER TABLE FKTABLE ADD FOREIGN KEY (ftest1) REFERENCES pktable; DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- On the other hand, this should work because int implicitly promotes to -- numeric, and we allow promotion on the FK side CREATE TEMP TABLE PKTABLE ( ptest1 numeric PRIMARY KEY ); INSERT INTO PKTABLE VALUES (42); CREATE TEMP TABLE FKTABLE ( ftest1 int ); ALTER TABLE FKTABLE ADD FOREIGN KEY (ftest1) REFERENCES pktable; -- Check it actually works INSERT INTO FKTABLE VALUES (42); -- should succeed INSERT INTO FKTABLE VALUES (43); -- should fail DROP TABLE FKTABLE; DROP TABLE PKTABLE; CREATE TEMP TABLE PKTABLE ( ptest1 int, ptest2 inet, PRIMARY KEY (ptest1, ptest2) ); -- This should fail, because we just chose really odd types CREATE TEMP TABLE FKTABLE ( ftest1 cidr, ftest2 timestamp ); ALTER TABLE FKTABLE ADD FOREIGN KEY (ftest1, ftest2) REFERENCES pktable; DROP TABLE FKTABLE; -- Again, so should this... CREATE TEMP TABLE FKTABLE ( ftest1 cidr, ftest2 timestamp ); ALTER TABLE FKTABLE ADD FOREIGN KEY (ftest1, ftest2) REFERENCES pktable (ptest1, ptest2); DROP TABLE FKTABLE; -- This fails because we mixed up the column ordering CREATE TEMP TABLE FKTABLE ( ftest1 int, ftest2 inet ); ALTER TABLE FKTABLE ADD FOREIGN KEY (ftest1, ftest2) REFERENCES pktable (ptest2, ptest1); -- As does this... ALTER TABLE FKTABLE ADD FOREIGN KEY (ftest2, ftest1) REFERENCES pktable (ptest1, ptest2); DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- Test that ALTER CONSTRAINT updates trigger deferrability properly CREATE TEMP TABLE PKTABLE ( ptest1 int PRIMARY KEY ); CREATE TEMP TABLE FKTABLE ( ftest1 int ); ALTER TABLE FKTABLE ADD CONSTRAINT fknd FOREIGN KEY (ftest1) REFERENCES pktable ON DELETE CASCADE ON UPDATE NO ACTION NOT DEFERRABLE; ALTER TABLE FKTABLE ADD CONSTRAINT fkdd FOREIGN KEY (ftest1) REFERENCES pktable ON DELETE CASCADE ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED; ALTER TABLE FKTABLE ADD CONSTRAINT fkdi FOREIGN KEY (ftest1) REFERENCES pktable ON DELETE CASCADE ON UPDATE NO ACTION DEFERRABLE INITIALLY IMMEDIATE; ALTER TABLE FKTABLE ADD CONSTRAINT fknd2 FOREIGN KEY (ftest1) REFERENCES pktable ON DELETE CASCADE ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED; ALTER TABLE FKTABLE ALTER CONSTRAINT fknd2 NOT DEFERRABLE; ALTER TABLE FKTABLE ADD CONSTRAINT fkdd2 FOREIGN KEY (ftest1) REFERENCES pktable ON DELETE CASCADE ON UPDATE NO ACTION NOT DEFERRABLE; ALTER TABLE FKTABLE ALTER CONSTRAINT fkdd2 DEFERRABLE INITIALLY DEFERRED; ALTER TABLE FKTABLE ADD CONSTRAINT fkdi2 FOREIGN KEY (ftest1) REFERENCES pktable ON DELETE CASCADE ON UPDATE NO ACTION NOT DEFERRABLE; ALTER TABLE FKTABLE ALTER CONSTRAINT fkdi2 DEFERRABLE INITIALLY IMMEDIATE; SELECT conname, tgfoid::regproc, tgtype, tgdeferrable, tginitdeferred FROM pg_trigger JOIN pg_constraint con ON con.oid = tgconstraint WHERE tgrelid = 'pktable'::regclass ORDER BY 1, 2, 3; SELECT conname, tgfoid::regproc, tgtype, tgdeferrable, tginitdeferred FROM pg_trigger JOIN pg_constraint con ON con.oid = tgconstraint WHERE tgrelid = 'fktable'::regclass ORDER BY 1, 2, 3; -- temp tables should go away by themselves, need not drop them. -- test check constraint adding CREATE TABLE atacc1 ( test int ); -- add a check constraint ALTER TABLE atacc1 ADD CONSTRAINT atacc_test1 CHECK (test > 3); -- should fail INSERT INTO atacc1 (test) VALUES (2); -- should succeed INSERT INTO atacc1 (test) VALUES (4); DROP TABLE atacc1; -- let's do one where the check fails when added CREATE TABLE atacc1 ( test int ); -- insert a soon to be failing row INSERT INTO atacc1 (test) VALUES (2); -- add a check constraint (fails) ALTER TABLE atacc1 ADD CONSTRAINT atacc_test1 CHECK (test > 3); INSERT INTO atacc1 (test) VALUES (4); DROP TABLE atacc1; -- let's do one where the check fails because the column doesn't exist CREATE TABLE atacc1 ( test int ); -- add a check constraint (fails) ALTER TABLE atacc1 ADD CONSTRAINT atacc_test1 CHECK (test1 > 3); DROP TABLE atacc1; -- something a little more complicated CREATE TABLE atacc1 ( test int, test2 int, test3 int ); -- add a check constraint (fails) ALTER TABLE atacc1 ADD CONSTRAINT atacc_test1 CHECK (test + test2 < test3 * 4); -- should fail INSERT INTO atacc1 (test, test2, test3) VALUES (4, 4, 2); -- should succeed INSERT INTO atacc1 (test, test2, test3) VALUES (4, 4, 5); DROP TABLE atacc1; -- lets do some naming tests CREATE TABLE atacc1 ( test int CHECK (test > 3), test2 int ); ALTER TABLE atacc1 ADD CHECK (test2 > test); -- should fail for $2 INSERT INTO atacc1 (test2, test) VALUES (3, 4); DROP TABLE atacc1; -- inheritance related tests CREATE TABLE atacc1 ( test int ); CREATE TABLE atacc2 ( test2 int ); CREATE TABLE atacc3 ( test3 int ) INHERITS ( atacc1, atacc2 ); ALTER TABLE atacc2 ADD CONSTRAINT foo CHECK (test2 > 0); -- fail and then succeed on atacc2 INSERT INTO atacc2 (test2) VALUES (- 3); INSERT INTO atacc2 (test2) VALUES (3); -- fail and then succeed on atacc3 INSERT INTO atacc3 (test2) VALUES (- 3); INSERT INTO atacc3 (test2) VALUES (3); DROP TABLE atacc3; DROP TABLE atacc2; DROP TABLE atacc1; -- same things with one created with INHERIT CREATE TABLE atacc1 ( test int ); CREATE TABLE atacc2 ( test2 int ); CREATE TABLE atacc3 ( test3 int ) INHERITS ( atacc1, atacc2 ); ALTER TABLE atacc3 NO inherit atacc2; -- fail ALTER TABLE atacc3 NO inherit atacc2; -- make sure it really isn't a child INSERT INTO atacc3 (test2) VALUES (3); SELECT test2 FROM atacc2; -- fail due to missing constraint ALTER TABLE atacc2 ADD CONSTRAINT foo CHECK (test2 > 0); ALTER TABLE atacc3 inherit atacc2; -- fail due to missing column ALTER TABLE atacc3 RENAME test2 TO testx; ALTER TABLE atacc3 inherit atacc2; -- fail due to mismatched data type ALTER TABLE atacc3 ADD test2 bool; ALTER TABLE atacc3 inherit atacc2; ALTER TABLE atacc3 DROP test2; -- succeed ALTER TABLE atacc3 ADD test2 int; UPDATE atacc3 SET test2 = 4 WHERE test2 IS NULL; ALTER TABLE atacc3 ADD CONSTRAINT foo CHECK (test2 > 0); ALTER TABLE atacc3 inherit atacc2; -- fail due to duplicates and circular inheritance ALTER TABLE atacc3 inherit atacc2; ALTER TABLE atacc2 inherit atacc3; ALTER TABLE atacc2 inherit atacc2; -- test that we really are a child now (should see 4 not 3 and cascade should go through) SELECT test2 FROM atacc2; DROP TABLE atacc2 CASCADE; DROP TABLE atacc1; -- adding only to a parent is allowed as of 9.2 CREATE TABLE atacc1 ( test int ); CREATE TABLE atacc2 ( test2 int ) INHERITS ( atacc1 ); -- ok: ALTER TABLE atacc1 ADD CONSTRAINT foo CHECK (test > 0) NO inherit; -- check constraint is not there on child INSERT INTO atacc2 (test) VALUES (- 3); -- check constraint is there on parent INSERT INTO atacc1 (test) VALUES (- 3); INSERT INTO atacc1 (test) VALUES (3); -- fail, violating row: ALTER TABLE atacc2 ADD CONSTRAINT foo CHECK (test > 0) NO inherit; DROP TABLE atacc2; DROP TABLE atacc1; -- test unique constraint adding CREATE TABLE atacc1 ( test int ); -- add a unique constraint ALTER TABLE atacc1 ADD CONSTRAINT atacc_test1 UNIQUE (test); -- insert first value INSERT INTO atacc1 (test) VALUES (2); -- should fail INSERT INTO atacc1 (test) VALUES (2); -- should succeed INSERT INTO atacc1 (test) VALUES (4); -- try to create duplicates via alter table using - should fail ALTER TABLE atacc1 ALTER COLUMN test TYPE integer USING 0; DROP TABLE atacc1; -- let's do one where the unique constraint fails when added CREATE TABLE atacc1 ( test int ); -- insert soon to be failing rows INSERT INTO atacc1 (test) VALUES (2); INSERT INTO atacc1 (test) VALUES (2); -- add a unique constraint (fails) ALTER TABLE atacc1 ADD CONSTRAINT atacc_test1 UNIQUE (test); INSERT INTO atacc1 (test) VALUES (3); DROP TABLE atacc1; -- let's do one where the unique constraint fails -- because the column doesn't exist CREATE TABLE atacc1 ( test int ); -- add a unique constraint (fails) ALTER TABLE atacc1 ADD CONSTRAINT atacc_test1 UNIQUE (test1); DROP TABLE atacc1; -- something a little more complicated CREATE TABLE atacc1 ( test int, test2 int ); -- add a unique constraint ALTER TABLE atacc1 ADD CONSTRAINT atacc_test1 UNIQUE (test, test2); -- insert initial value INSERT INTO atacc1 (test, test2) VALUES (4, 4); -- should fail INSERT INTO atacc1 (test, test2) VALUES (4, 4); -- should all succeed INSERT INTO atacc1 (test, test2) VALUES (4, 5); INSERT INTO atacc1 (test, test2) VALUES (5, 4); INSERT INTO atacc1 (test, test2) VALUES (5, 5); DROP TABLE atacc1; -- lets do some naming tests CREATE TABLE atacc1 ( test int, test2 int, UNIQUE (test) ); ALTER TABLE atacc1 ADD UNIQUE (test2); -- should fail for @@ second one @@ INSERT INTO atacc1 (test2, test) VALUES (3, 3); INSERT INTO atacc1 (test2, test) VALUES (2, 3); DROP TABLE atacc1; -- test primary key constraint adding CREATE TABLE atacc1 ( id serial, test int ); -- add a primary key constraint ALTER TABLE atacc1 ADD CONSTRAINT atacc_test1 PRIMARY KEY (test); -- insert first value INSERT INTO atacc1 (test) VALUES (2); -- should fail INSERT INTO atacc1 (test) VALUES (2); -- should succeed INSERT INTO atacc1 (test) VALUES (4); -- inserting NULL should fail INSERT INTO atacc1 (test) VALUES (NULL); -- try adding a second primary key (should fail) ALTER TABLE atacc1 ADD CONSTRAINT atacc_oid1 PRIMARY KEY (id); -- drop first primary key constraint ALTER TABLE atacc1 DROP CONSTRAINT atacc_test1 RESTRICT; -- try adding a primary key on oid (should succeed) ALTER TABLE atacc1 ADD CONSTRAINT atacc_oid1 PRIMARY KEY (id); DROP TABLE atacc1; -- let's do one where the primary key constraint fails when added CREATE TABLE atacc1 ( test int ); -- insert soon to be failing rows INSERT INTO atacc1 (test) VALUES (2); INSERT INTO atacc1 (test) VALUES (2); -- add a primary key (fails) ALTER TABLE atacc1 ADD CONSTRAINT atacc_test1 PRIMARY KEY (test); INSERT INTO atacc1 (test) VALUES (3); DROP TABLE atacc1; -- let's do another one where the primary key constraint fails when added CREATE TABLE atacc1 ( test int ); -- insert soon to be failing row INSERT INTO atacc1 (test) VALUES (NULL); -- add a primary key (fails) ALTER TABLE atacc1 ADD CONSTRAINT atacc_test1 PRIMARY KEY (test); INSERT INTO atacc1 (test) VALUES (3); DROP TABLE atacc1; -- let's do one where the primary key constraint fails -- because the column doesn't exist CREATE TABLE atacc1 ( test int ); -- add a primary key constraint (fails) ALTER TABLE atacc1 ADD CONSTRAINT atacc_test1 PRIMARY KEY (test1); DROP TABLE atacc1; -- adding a new column as primary key to a non-empty table. -- should fail unless the column has a non-null default value. CREATE TABLE atacc1 ( test int ); INSERT INTO atacc1 (test) VALUES (0); -- add a primary key column without a default (fails). ALTER TABLE atacc1 ADD COLUMN test2 int PRIMARY KEY; -- now add a primary key column with a default (succeeds). ALTER TABLE atacc1 ADD COLUMN test2 int DEFAULT 0 PRIMARY KEY; DROP TABLE atacc1; -- this combination used to have order-of-execution problems (bug #15580) CREATE TABLE atacc1 ( a int ); INSERT INTO atacc1 VALUES (1); ALTER TABLE atacc1 ADD COLUMN b float8 NOT NULL DEFAULT random(), ADD PRIMARY KEY (a); DROP TABLE atacc1; -- something a little more complicated CREATE TABLE atacc1 ( test int, test2 int ); -- add a primary key constraint ALTER TABLE atacc1 ADD CONSTRAINT atacc_test1 PRIMARY KEY (test, test2); -- try adding a second primary key - should fail ALTER TABLE atacc1 ADD CONSTRAINT atacc_test2 PRIMARY KEY (test); -- insert initial value INSERT INTO atacc1 (test, test2) VALUES (4, 4); -- should fail INSERT INTO atacc1 (test, test2) VALUES (4, 4); INSERT INTO atacc1 (test, test2) VALUES (NULL, 3); INSERT INTO atacc1 (test, test2) VALUES (3, NULL); INSERT INTO atacc1 (test, test2) VALUES (NULL, NULL); -- should all succeed INSERT INTO atacc1 (test, test2) VALUES (4, 5); INSERT INTO atacc1 (test, test2) VALUES (5, 4); INSERT INTO atacc1 (test, test2) VALUES (5, 5); DROP TABLE atacc1; -- lets do some naming tests CREATE TABLE atacc1 ( test int, test2 int, PRIMARY KEY (test) ); -- only first should succeed INSERT INTO atacc1 (test2, test) VALUES (3, 3); INSERT INTO atacc1 (test2, test) VALUES (2, 3); INSERT INTO atacc1 (test2, test) VALUES (1, NULL); DROP TABLE atacc1; -- alter table / alter column [set/drop] not null tests -- try altering system catalogs, should fail ALTER TABLE pg_class ALTER COLUMN relname DROP NOT NULL; ALTER TABLE pg_class ALTER relname SET NOT NULL; -- try altering non-existent table, should fail ALTER TABLE non_existent ALTER COLUMN bar SET NOT NULL; ALTER TABLE non_existent ALTER COLUMN bar DROP NOT NULL; -- test setting columns to null and not null and vice versa -- test checking for null values and primary key CREATE TABLE atacc1 ( test int NOT NULL ); ALTER TABLE atacc1 ADD CONSTRAINT "atacc1_pkey" PRIMARY KEY (test); ALTER TABLE atacc1 ALTER COLUMN test DROP NOT NULL; ALTER TABLE atacc1 DROP CONSTRAINT "atacc1_pkey"; ALTER TABLE atacc1 ALTER COLUMN test DROP NOT NULL; INSERT INTO atacc1 VALUES (NULL); ALTER TABLE atacc1 ALTER test SET NOT NULL; DELETE FROM atacc1; ALTER TABLE atacc1 ALTER test SET NOT NULL; -- try altering a non-existent column, should fail ALTER TABLE atacc1 ALTER bar SET NOT NULL; ALTER TABLE atacc1 ALTER bar DROP NOT NULL; -- try creating a view and altering that, should fail CREATE VIEW myview AS SELECT * FROM atacc1; ALTER TABLE myview ALTER COLUMN test DROP NOT NULL; ALTER TABLE myview ALTER COLUMN test SET NOT NULL; DROP VIEW myview; DROP TABLE atacc1; -- set not null verified by constraints CREATE TABLE atacc1 ( test_a int, test_b int ); INSERT INTO atacc1 VALUES (NULL, 1); -- constraint not cover all values, should fail ALTER TABLE atacc1 ADD CONSTRAINT atacc1_constr_or CHECK (test_a IS NOT NULL OR test_b < 10); ALTER TABLE atacc1 ALTER test_a SET NOT NULL; ALTER TABLE atacc1 DROP CONSTRAINT atacc1_constr_or; -- not valid constraint, should fail ALTER TABLE atacc1 ADD CONSTRAINT atacc1_constr_invalid CHECK (test_a IS NOT NULL) NOT valid; ALTER TABLE atacc1 ALTER test_a SET NOT NULL; ALTER TABLE atacc1 DROP CONSTRAINT atacc1_constr_invalid; -- with valid constraint UPDATE atacc1 SET test_a = 1; ALTER TABLE atacc1 ADD CONSTRAINT atacc1_constr_a_valid CHECK (test_a IS NOT NULL); ALTER TABLE atacc1 ALTER test_a SET NOT NULL; DELETE FROM atacc1; INSERT INTO atacc1 VALUES (2, NULL); ALTER TABLE atacc1 ALTER test_a DROP NOT NULL; -- test multiple set not null at same time -- test_a checked by atacc1_constr_a_valid, test_b should fail by table scan ALTER TABLE atacc1 ALTER test_a SET NOT NULL, ALTER test_b SET NOT NULL; -- commands order has no importance ALTER TABLE atacc1 ALTER test_b SET NOT NULL, ALTER test_a SET NOT NULL; -- valid one by table scan, one by check constraints UPDATE atacc1 SET test_b = 1; ALTER TABLE atacc1 ALTER test_b SET NOT NULL, ALTER test_a SET NOT NULL; ALTER TABLE atacc1 ALTER test_a DROP NOT NULL, ALTER test_b DROP NOT NULL; -- both column has check constraints ALTER TABLE atacc1 ADD CONSTRAINT atacc1_constr_b_valid CHECK (test_b IS NOT NULL); ALTER TABLE atacc1 ALTER test_b SET NOT NULL, ALTER test_a SET NOT NULL; DROP TABLE atacc1; -- test inheritance CREATE TABLE parent ( a int ); CREATE TABLE child ( b varchar(255) ) INHERITS ( parent ); ALTER TABLE parent ALTER a SET NOT NULL; INSERT INTO parent VALUES (NULL); INSERT INTO child (a, b) VALUES (NULL, 'foo'); ALTER TABLE parent ALTER a DROP NOT NULL; INSERT INTO parent VALUES (NULL); INSERT INTO child (a, b) VALUES (NULL, 'foo'); ALTER TABLE ONLY parent ALTER a SET NOT NULL; ALTER TABLE child ALTER a SET NOT NULL; DELETE FROM parent; ALTER TABLE ONLY parent ALTER a SET NOT NULL; INSERT INTO parent VALUES (NULL); ALTER TABLE child ALTER a SET NOT NULL; INSERT INTO child (a, b) VALUES (NULL, 'foo'); DELETE FROM child; ALTER TABLE child ALTER a SET NOT NULL; INSERT INTO child (a, b) VALUES (NULL, 'foo'); DROP TABLE child; DROP TABLE parent; -- test setting and removing default values CREATE TABLE def_test ( c1 int4 DEFAULT 5, c2 text DEFAULT 'initial_default' ); INSERT INTO def_test DEFAULT VALUES; ALTER TABLE def_test ALTER COLUMN c1 DROP DEFAULT; INSERT INTO def_test DEFAULT VALUES; ALTER TABLE def_test ALTER COLUMN c2 DROP DEFAULT; INSERT INTO def_test DEFAULT VALUES; ALTER TABLE def_test ALTER COLUMN c1 SET DEFAULT 10; ALTER TABLE def_test ALTER COLUMN c2 SET DEFAULT 'new_default'; INSERT INTO def_test DEFAULT VALUES; SELECT * FROM def_test; -- set defaults to an incorrect type: this should fail ALTER TABLE def_test ALTER COLUMN c1 SET DEFAULT 'wrong_datatype'; ALTER TABLE def_test ALTER COLUMN c2 SET DEFAULT 20; -- set defaults on a non-existent column: this should fail ALTER TABLE def_test ALTER COLUMN c3 SET DEFAULT 30; -- set defaults on views: we need to create a view, add a rule -- to allow insertions into it, and then alter the view to add -- a default CREATE VIEW def_view_test AS SELECT * FROM def_test; CREATE RULE def_view_test_ins AS ON INSERT TO def_view_test DO INSTEAD INSERT INTO def_test SELECT new.*; INSERT INTO def_view_test DEFAULT VALUES; ALTER TABLE def_view_test ALTER COLUMN c1 SET DEFAULT 45; INSERT INTO def_view_test DEFAULT VALUES; ALTER TABLE def_view_test ALTER COLUMN c2 SET DEFAULT 'view_default'; INSERT INTO def_view_test DEFAULT VALUES; SELECT * FROM def_view_test; DROP RULE def_view_test_ins ON def_view_test; DROP VIEW def_view_test; DROP TABLE def_test; -- alter table / drop column tests -- try altering system catalogs, should fail ALTER TABLE pg_class DROP COLUMN relname; -- try altering non-existent table, should fail ALTER TABLE nosuchtable DROP COLUMN bar; -- test dropping columns CREATE TABLE atacc1 ( a int4 NOT NULL, b int4, c int4 NOT NULL, d int4 ); INSERT INTO atacc1 VALUES (1, 2, 3, 4); ALTER TABLE atacc1 DROP a; ALTER TABLE atacc1 DROP a; -- SELECTs SELECT * FROM atacc1; SELECT * FROM atacc1 ORDER BY a; SELECT * FROM atacc1 ORDER BY "........pg.dropped.1........"; SELECT * FROM atacc1 GROUP BY a; SELECT * FROM atacc1 GROUP BY "........pg.dropped.1........"; SELECT atacc1.* FROM atacc1; SELECT a FROM atacc1; SELECT atacc1.a FROM atacc1; SELECT b, c, d FROM atacc1; SELECT a, b, c, d FROM atacc1; SELECT * FROM atacc1 WHERE a = 1; SELECT "........pg.dropped.1........" FROM atacc1; SELECT atacc1. "........pg.dropped.1........" FROM atacc1; SELECT "........pg.dropped.1........", b, c, d FROM atacc1; SELECT * FROM atacc1 WHERE "........pg.dropped.1........" = 1; -- UPDATEs UPDATE atacc1 SET a = 3; UPDATE atacc1 SET b = 2 WHERE a = 3; UPDATE atacc1 SET "........pg.dropped.1........" = 3; UPDATE atacc1 SET b = 2 WHERE "........pg.dropped.1........" = 3; -- INSERTs INSERT INTO atacc1 VALUES (10, 11, 12, 13); INSERT INTO atacc1 VALUES (DEFAULT, 11, 12, 13); INSERT INTO atacc1 VALUES (11, 12, 13); INSERT INTO atacc1 (a) VALUES (10); INSERT INTO atacc1 (a) VALUES (DEFAULT); INSERT INTO atacc1 (a, b, c, d) VALUES (10, 11, 12, 13); INSERT INTO atacc1 (a, b, c, d) VALUES (DEFAULT, 11, 12, 13); INSERT INTO atacc1 (b, c, d) VALUES (11, 12, 13); INSERT INTO atacc1 ("........pg.dropped.1........") VALUES (10); INSERT INTO atacc1 ("........pg.dropped.1........") VALUES (DEFAULT); INSERT INTO atacc1 ("........pg.dropped.1........", b, c, d) VALUES (10, 11, 12, 13); INSERT INTO atacc1 ("........pg.dropped.1........", b, c, d) VALUES (DEFAULT, 11, 12, 13); -- DELETEs DELETE FROM atacc1 WHERE a = 3; DELETE FROM atacc1 WHERE "........pg.dropped.1........" = 3; DELETE FROM atacc1; -- try dropping a non-existent column, should fail ALTER TABLE atacc1 DROP bar; -- try removing an oid column, should succeed (as it's nonexistant) ALTER TABLE atacc1 SET WITHOUT OIDS; -- try adding an oid column, should fail (not supported) ALTER TABLE atacc1 SET WITH OIDS; -- try dropping the xmin column, should fail ALTER TABLE atacc1 DROP xmin; -- try creating a view and altering that, should fail CREATE VIEW myview AS SELECT * FROM atacc1; SELECT * FROM myview; ALTER TABLE myview DROP d; DROP VIEW myview; -- test some commands to make sure they fail on the dropped column ANALYZE atacc1 (a); ANALYZE atacc1 ("........pg.dropped.1........"); VACUUM ANALYZE atacc1 (a); VACUUM ANALYZE atacc1 ("........pg.dropped.1........"); COMMENT ON COLUMN atacc1.a IS 'testing'; COMMENT ON COLUMN atacc1. "........pg.dropped.1........" IS 'testing'; ALTER TABLE atacc1 ALTER a SET storage plain; ALTER TABLE atacc1 ALTER "........pg.dropped.1........" SET storage plain; ALTER TABLE atacc1 ALTER a SET statistics 0; ALTER TABLE atacc1 ALTER "........pg.dropped.1........" SET statistics 0; ALTER TABLE atacc1 ALTER a SET DEFAULT 3; ALTER TABLE atacc1 ALTER "........pg.dropped.1........" SET DEFAULT 3; ALTER TABLE atacc1 ALTER a DROP DEFAULT; ALTER TABLE atacc1 ALTER "........pg.dropped.1........" DROP DEFAULT; ALTER TABLE atacc1 ALTER a SET NOT NULL; ALTER TABLE atacc1 ALTER "........pg.dropped.1........" SET NOT NULL; ALTER TABLE atacc1 ALTER a DROP NOT NULL; ALTER TABLE atacc1 ALTER "........pg.dropped.1........" DROP NOT NULL; ALTER TABLE atacc1 RENAME a TO x; ALTER TABLE atacc1 RENAME "........pg.dropped.1........" TO x; ALTER TABLE atacc1 ADD PRIMARY KEY (a); ALTER TABLE atacc1 ADD PRIMARY KEY ("........pg.dropped.1........"); ALTER TABLE atacc1 ADD UNIQUE (a); ALTER TABLE atacc1 ADD UNIQUE ("........pg.dropped.1........"); ALTER TABLE atacc1 ADD CHECK (a > 3); ALTER TABLE atacc1 ADD CHECK ("........pg.dropped.1........" > 3); CREATE TABLE atacc2 ( id int4 UNIQUE ); ALTER TABLE atacc1 ADD FOREIGN KEY (a) REFERENCES atacc2 (id); ALTER TABLE atacc1 ADD FOREIGN KEY ("........pg.dropped.1........") REFERENCES atacc2 (id); ALTER TABLE atacc2 ADD FOREIGN KEY (id) REFERENCES atacc1 (a); ALTER TABLE atacc2 ADD FOREIGN KEY (id) REFERENCES atacc1 ("........pg.dropped.1........"); DROP TABLE atacc2; CREATE INDEX "testing_idx" ON atacc1 (a); CREATE INDEX "testing_idx" ON atacc1 ("........pg.dropped.1........"); -- test create as and select into INSERT INTO atacc1 VALUES (21, 22, 23); CREATE TABLE attest1 AS SELECT * FROM atacc1; SELECT * FROM attest1; DROP TABLE attest1; SELECT * INTO attest2 FROM atacc1; SELECT * FROM attest2; DROP TABLE attest2; -- try dropping all columns ALTER TABLE atacc1 DROP c; ALTER TABLE atacc1 DROP d; ALTER TABLE atacc1 DROP b; SELECT * FROM atacc1; DROP TABLE atacc1; -- test constraint error reporting in presence of dropped columns CREATE TABLE atacc1 ( id serial PRIMARY KEY, value int CHECK (value < 10) ); INSERT INTO atacc1 (value) VALUES (100); ALTER TABLE atacc1 DROP COLUMN value; ALTER TABLE atacc1 ADD COLUMN value int CHECK (value < 10); INSERT INTO atacc1 (value) VALUES (100); INSERT INTO atacc1 (id, value) VALUES (NULL, 0); DROP TABLE atacc1; -- test inheritance CREATE TABLE parent ( a int, b int, c int ); INSERT INTO parent VALUES (1, 2, 3); ALTER TABLE parent DROP a; CREATE TABLE child ( d varchar(255) ) INHERITS ( parent ); INSERT INTO child VALUES (12, 13, 'testing'); SELECT * FROM parent; SELECT * FROM child; ALTER TABLE parent DROP c; SELECT * FROM parent; SELECT * FROM child; DROP TABLE child; DROP TABLE parent; -- check error cases for inheritance column merging CREATE TABLE parent ( a float8, b numeric(10, 4), c text COLLATE "C" ); CREATE TABLE child ( a float4 ) INHERITS ( parent ); -- fail CREATE TABLE child ( b decimal(10, 7) ) INHERITS ( parent ); -- fail CREATE TABLE child ( c text COLLATE "POSIX" ) INHERITS ( parent ); -- fail CREATE TABLE child ( a double precision, b decimal(10, 4) ) INHERITS ( parent ); DROP TABLE child; DROP TABLE parent; -- test copy in/out CREATE TABLE attest ( a int4, b int4, c int4 ); INSERT INTO attest VALUES (1, 2, 3); ALTER TABLE attest DROP a; SELECT * FROM attest; SELECT * FROM attest; SELECT * FROM attest; DROP TABLE attest; -- test inheritance CREATE TABLE dropColumn ( a int, b int, e int ); CREATE TABLE dropColumnChild ( c int ) INHERITS ( dropColumn ); CREATE TABLE dropColumnAnother ( d int ) INHERITS ( dropColumnChild ); -- these two should fail ALTER TABLE dropColumnchild DROP COLUMN a; ALTER TABLE ONLY dropColumnChild DROP COLUMN b; -- these three should work ALTER TABLE ONLY dropColumn DROP COLUMN e; ALTER TABLE dropColumnChild DROP COLUMN c; ALTER TABLE dropColumn DROP COLUMN a; CREATE TABLE renameColumn ( a int ); CREATE TABLE renameColumnChild ( b int ) INHERITS ( renameColumn ); CREATE TABLE renameColumnAnother ( c int ) INHERITS ( renameColumnChild ); -- these three should fail ALTER TABLE renameColumnChild RENAME COLUMN a TO d; ALTER TABLE ONLY renameColumnChild RENAME COLUMN a TO d; ALTER TABLE ONLY renameColumn RENAME COLUMN a TO d; -- these should work ALTER TABLE renameColumn RENAME COLUMN a TO d; ALTER TABLE renameColumnChild RENAME COLUMN b TO a; -- these should work ALTER TABLE IF EXISTS doesnt_exist_tab RENAME COLUMN a TO d; ALTER TABLE IF EXISTS doesnt_exist_tab RENAME COLUMN b TO a; -- this should work ALTER TABLE renameColumn ADD COLUMN w int; -- this should fail ALTER TABLE ONLY renameColumn ADD COLUMN x int; -- Test corner cases in dropping of inherited columns CREATE TABLE p1 ( f1 int, f2 int ); CREATE TABLE c1 ( f1 int NOT NULL ) INHERITS ( p1 ); -- should be rejected since c1.f1 is inherited ALTER TABLE c1 DROP COLUMN f1; -- should work ALTER TABLE p1 DROP COLUMN f1; -- c1.f1 is still there, but no longer inherited SELECT f1 FROM c1; ALTER TABLE c1 DROP COLUMN f1; SELECT f1 FROM c1; DROP TABLE p1 CASCADE; CREATE TABLE p1 ( f1 int, f2 int ); CREATE TABLE c1 () INHERITS ( p1 ); -- should be rejected since c1.f1 is inherited ALTER TABLE c1 DROP COLUMN f1; ALTER TABLE p1 DROP COLUMN f1; -- c1.f1 is dropped now, since there is no local definition for it SELECT f1 FROM c1; DROP TABLE p1 CASCADE; CREATE TABLE p1 ( f1 int, f2 int ); CREATE TABLE c1 () INHERITS ( p1 ); -- should be rejected since c1.f1 is inherited ALTER TABLE c1 DROP COLUMN f1; ALTER TABLE ONLY p1 DROP COLUMN f1; -- c1.f1 is NOT dropped, but must now be considered non-inherited ALTER TABLE c1 DROP COLUMN f1; DROP TABLE p1 CASCADE; CREATE TABLE p1 ( f1 int, f2 int ); CREATE TABLE c1 ( f1 int NOT NULL ) INHERITS ( p1 ); -- should be rejected since c1.f1 is inherited ALTER TABLE c1 DROP COLUMN f1; ALTER TABLE ONLY p1 DROP COLUMN f1; -- c1.f1 is still there, but no longer inherited ALTER TABLE c1 DROP COLUMN f1; DROP TABLE p1 CASCADE; CREATE TABLE p1 ( id int, name text ); CREATE TABLE p2 ( id2 int, name text, height int ); CREATE TABLE c1 ( age int ) INHERITS ( p1, p2 ); CREATE TABLE gc1 () INHERITS ( c1 ); SELECT relname, attname, attinhcount, attislocal FROM pg_class JOIN pg_attribute ON (pg_class.oid = pg_attribute.attrelid) WHERE relname IN ('p1', 'p2', 'c1', 'gc1') AND attnum > 0 AND NOT attisdropped ORDER BY relname, attnum; -- should work ALTER TABLE ONLY p1 DROP COLUMN name; -- should work. Now c1.name is local and inhcount is 0. ALTER TABLE p2 DROP COLUMN name; -- should be rejected since its inherited ALTER TABLE gc1 DROP COLUMN name; -- should work, and drop gc1.name along ALTER TABLE c1 DROP COLUMN name; -- should fail: column does not exist ALTER TABLE gc1 DROP COLUMN name; -- should work and drop the attribute in all tables ALTER TABLE p2 DROP COLUMN height; -- IF EXISTS test CREATE TABLE dropColumnExists (); ALTER TABLE dropColumnExists DROP COLUMN non_existing; --fail ALTER TABLE dropColumnExists DROP COLUMN IF EXISTS non_existing; --succeed SELECT relname, attname, attinhcount, attislocal FROM pg_class JOIN pg_attribute ON (pg_class.oid = pg_attribute.attrelid) WHERE relname IN ('p1', 'p2', 'c1', 'gc1') AND attnum > 0 AND NOT attisdropped ORDER BY relname, attnum; DROP TABLE p1, p2 CASCADE; -- test attinhcount tracking with merged columns CREATE TABLE depth0 (); CREATE TABLE depth1 ( c text ) INHERITS ( depth0 ); CREATE TABLE depth2 () INHERITS ( depth1 ); ALTER TABLE depth0 ADD c text; SELECT attrelid::regclass, attname, attinhcount, attislocal FROM pg_attribute WHERE attnum > 0 AND attrelid::regclass IN ('depth0', 'depth1', 'depth2') ORDER BY attrelid::regclass::text, attnum; -- test renumbering of child-table columns in inherited operations CREATE TABLE p1 ( f1 int ); CREATE TABLE c1 ( f2 text, f3 int ) INHERITS ( p1 ); ALTER TABLE p1 ADD COLUMN a1 int CHECK (a1 > 0); ALTER TABLE p1 ADD COLUMN f2 text; INSERT INTO p1 VALUES (1, 2, 'abc'); INSERT INTO c1 VALUES (11, 'xyz', 33, 0); -- should fail INSERT INTO c1 VALUES (11, 'xyz', 33, 22); SELECT * FROM p1; UPDATE p1 SET a1 = a1 + 1, f2 = upper(f2); SELECT * FROM p1; DROP TABLE p1 CASCADE; -- test that operations with a dropped column do not try to reference -- its datatype CREATE DOMAIN mytype AS text; CREATE temp TABLE foo ( f1 text, f2 mytype, f3 text ); INSERT INTO foo VALUES ('bb', 'cc', 'dd'); SELECT * FROM foo; DROP DOMAIN mytype CASCADE; SELECT * FROM foo; INSERT INTO foo VALUES ('qq', 'rr'); SELECT * FROM foo; UPDATE foo SET f3 = 'zz'; SELECT * FROM foo; SELECT f3, max(f1) FROM foo GROUP BY f3; -- Simple tests for alter table column type ALTER TABLE foo ALTER f1 TYPE integer; -- fails ALTER TABLE foo ALTER f1 TYPE varchar(10); CREATE TABLE anothertab ( atcol1 serial8, atcol2 boolean, CONSTRAINT anothertab_chk CHECK (atcol1 <= 3) ); INSERT INTO anothertab (atcol1, atcol2) VALUES (DEFAULT, TRUE); INSERT INTO anothertab (atcol1, atcol2) VALUES (DEFAULT, FALSE); SELECT * FROM anothertab; ALTER TABLE anothertab ALTER COLUMN atcol1 TYPE boolean; -- fails ALTER TABLE anothertab ALTER COLUMN atcol1 TYPE boolean USING atcol1::int; -- fails ALTER TABLE anothertab ALTER COLUMN atcol1 TYPE integer; SELECT * FROM anothertab; INSERT INTO anothertab (atcol1, atcol2) VALUES (45, NULL); -- fails INSERT INTO anothertab (atcol1, atcol2) VALUES (DEFAULT, NULL); SELECT * FROM anothertab; ALTER TABLE anothertab ALTER COLUMN atcol2 TYPE text USING CASE WHEN atcol2 IS TRUE THEN 'IT WAS TRUE' WHEN atcol2 IS FALSE THEN 'IT WAS FALSE' ELSE 'IT WAS NULL!' END; SELECT * FROM anothertab; ALTER TABLE anothertab ALTER COLUMN atcol1 TYPE boolean USING CASE WHEN atcol1 % 2 = 0 THEN TRUE ELSE FALSE END; -- fails ALTER TABLE anothertab ALTER COLUMN atcol1 DROP DEFAULT; ALTER TABLE anothertab ALTER COLUMN atcol1 TYPE boolean USING CASE WHEN atcol1 % 2 = 0 THEN TRUE ELSE FALSE END; -- fails ALTER TABLE anothertab DROP CONSTRAINT anothertab_chk; ALTER TABLE anothertab DROP CONSTRAINT anothertab_chk; -- fails ALTER TABLE anothertab DROP CONSTRAINT IF EXISTS anothertab_chk; -- succeeds ALTER TABLE anothertab ALTER COLUMN atcol1 TYPE boolean USING CASE WHEN atcol1 % 2 = 0 THEN TRUE ELSE FALSE END; SELECT * FROM anothertab; DROP TABLE anothertab; CREATE TABLE another ( f1 int, f2 text ); INSERT INTO another VALUES (1, 'one'); INSERT INTO another VALUES (2, 'two'); INSERT INTO another VALUES (3, 'three'); SELECT * FROM another; ALTER TABLE another ALTER f1 TYPE text USING f2 || ' more', ALTER f2 TYPE bigint USING f1 * 10; SELECT * FROM another; DROP TABLE another; -- table's row type CREATE TABLE tab1 ( a int, b text ); CREATE TABLE tab2 ( x int, y tab1 ); ALTER TABLE tab1 ALTER COLUMN b TYPE varchar; -- fails -- Alter column type that's part of a partitioned index CREATE TABLE at_partitioned ( a int, b text ) PARTITION BY RANGE (a); CREATE TABLE at_part_1 PARTITION OF at_partitioned FOR VALUES FROM (0) TO (1000); INSERT INTO at_partitioned VALUES (512, '0.123'); CREATE TABLE at_part_2 ( b text, a int ); INSERT INTO at_part_2 VALUES ('1.234', 1024); CREATE INDEX ON at_partitioned (b); CREATE INDEX ON at_partitioned (a); \d at_part_1 \d at_part_2 ALTER TABLE at_partitioned ATTACH PARTITION at_part_2 FOR VALUES FROM (1000) TO (2000); \d at_part_2 ALTER TABLE at_partitioned ALTER COLUMN b TYPE numeric USING b::numeric; \d at_part_1 \d at_part_2 DROP TABLE at_partitioned; -- Alter column type when no table rewrite is required -- Also check that comments are preserved CREATE TABLE at_partitioned ( id int, name varchar(64), UNIQUE (id, name) ) PARTITION BY HASH (id); COMMENT ON CONSTRAINT at_partitioned_id_name_key ON at_partitioned IS 'parent constraint'; COMMENT ON INDEX at_partitioned_id_name_key IS 'parent index'; CREATE TABLE at_partitioned_0 PARTITION OF at_partitioned FOR VALUES WITH (MODULUS 2, REMAINDER 0); COMMENT ON CONSTRAINT at_partitioned_0_id_name_key ON at_partitioned_0 IS 'child 0 constraint'; COMMENT ON INDEX at_partitioned_0_id_name_key IS 'child 0 index'; CREATE TABLE at_partitioned_1 PARTITION OF at_partitioned FOR VALUES WITH (MODULUS 2, REMAINDER 1); COMMENT ON CONSTRAINT at_partitioned_1_id_name_key ON at_partitioned_1 IS 'child 1 constraint'; COMMENT ON INDEX at_partitioned_1_id_name_key IS 'child 1 index'; INSERT INTO at_partitioned VALUES (1, 'foo'); INSERT INTO at_partitioned VALUES (3, 'bar'); CREATE temp TABLE old_oids AS SELECT relname, oid AS oldoid, relfilenode AS oldfilenode FROM pg_class WHERE relname LIKE 'at_partitioned%'; SELECT relname, c.oid = oldoid AS orig_oid, CASE relfilenode WHEN 0 THEN 'none' WHEN c.oid THEN 'own' WHEN oldfilenode THEN 'orig' ELSE 'OTHER' END AS storage, obj_description(c.oid, 'pg_class') AS DESC FROM pg_class c LEFT JOIN old_oids USING (relname) WHERE relname LIKE 'at_partitioned%' ORDER BY relname; SELECT conname, obj_description(oid, 'pg_constraint') AS DESC FROM pg_constraint WHERE conname LIKE 'at_partitioned%' ORDER BY conname; ALTER TABLE at_partitioned ALTER COLUMN name TYPE varchar(127); -- Note: these tests currently show the wrong behavior for comments :-( SELECT relname, c.oid = oldoid AS orig_oid, CASE relfilenode WHEN 0 THEN 'none' WHEN c.oid THEN 'own' WHEN oldfilenode THEN 'orig' ELSE 'OTHER' END AS storage, obj_description(c.oid, 'pg_class') AS DESC FROM pg_class c LEFT JOIN old_oids USING (relname) WHERE relname LIKE 'at_partitioned%' ORDER BY relname; SELECT conname, obj_description(oid, 'pg_constraint') AS DESC FROM pg_constraint WHERE conname LIKE 'at_partitioned%' ORDER BY conname; -- Don't remove this DROP, it exposes bug #15672 DROP TABLE at_partitioned; -- disallow recursive containment of row types CREATE temp TABLE recur1 ( f1 int ); ALTER TABLE recur1 ADD COLUMN f2 recur1; -- fails ALTER TABLE recur1 ADD COLUMN f2 recur1[]; -- fails CREATE DOMAIN array_of_recur1 AS recur1[]; ALTER TABLE recur1 ADD COLUMN f2 array_of_recur1; -- fails CREATE temp TABLE recur2 ( f1 int, f2 recur1 ); ALTER TABLE recur1 ADD COLUMN f2 recur2; -- fails ALTER TABLE recur1 ADD COLUMN f2 int; ALTER TABLE recur1 ALTER COLUMN f2 TYPE recur2; -- fails -- SET STORAGE may need to add a TOAST table CREATE TABLE test_storage ( a text ); ALTER TABLE test_storage ALTER a SET storage plain; ALTER TABLE test_storage ADD b int DEFAULT 0; -- rewrite table to remove its TOAST table ALTER TABLE test_storage ALTER a SET storage extended; -- re-add TOAST table SELECT reltoastrelid <> 0 AS has_toast_table FROM pg_class WHERE oid = 'test_storage'::regclass; -- ALTER COLUMN TYPE with a check constraint and a child table (bug #13779) CREATE TABLE test_inh_check ( a float CHECK (a > 10.2), b float ); CREATE TABLE test_inh_check_child () INHERITS ( test_inh_check ); \d test_inh_check \d test_inh_check_child SELECT relname, conname, coninhcount, conislocal, connoinherit FROM pg_constraint c, pg_class r WHERE relname LIKE 'test_inh_check%' AND c.conrelid = r.oid ORDER BY 1, 2; ALTER TABLE test_inh_check ALTER COLUMN a TYPE numeric; \d test_inh_check \d test_inh_check_child SELECT relname, conname, coninhcount, conislocal, connoinherit FROM pg_constraint c, pg_class r WHERE relname LIKE 'test_inh_check%' AND c.conrelid = r.oid ORDER BY 1, 2; -- also try noinherit, local, and local+inherited cases ALTER TABLE test_inh_check ADD CONSTRAINT bnoinherit CHECK (b > 100) NO INHERIT; ALTER TABLE test_inh_check_child ADD CONSTRAINT blocal CHECK (b < 1000); ALTER TABLE test_inh_check_child ADD CONSTRAINT bmerged CHECK (b > 1); ALTER TABLE test_inh_check ADD CONSTRAINT bmerged CHECK (b > 1); \d test_inh_check \d test_inh_check_child SELECT relname, conname, coninhcount, conislocal, connoinherit FROM pg_constraint c, pg_class r WHERE relname LIKE 'test_inh_check%' AND c.conrelid = r.oid ORDER BY 1, 2; ALTER TABLE test_inh_check ALTER COLUMN b TYPE numeric; \d test_inh_check \d test_inh_check_child SELECT relname, conname, coninhcount, conislocal, connoinherit FROM pg_constraint c, pg_class r WHERE relname LIKE 'test_inh_check%' AND c.conrelid = r.oid ORDER BY 1, 2; -- ALTER COLUMN TYPE with different schema in children -- Bug at https://postgr.es/m/20170102225618.GA10071@telsasoft.com CREATE TABLE test_type_diff ( f1 int ); CREATE TABLE test_type_diff_c ( extra smallint ) INHERITS ( test_type_diff ); ALTER TABLE test_type_diff ADD COLUMN f2 int; INSERT INTO test_type_diff_c VALUES (1, 2, 3); ALTER TABLE test_type_diff ALTER COLUMN f2 TYPE bigint USING f2::bigint; CREATE TABLE test_type_diff2 ( int_two int2, int_four int4, int_eight int8 ); CREATE TABLE test_type_diff2_c1 ( int_four int4, int_eight int8, int_two int2 ); CREATE TABLE test_type_diff2_c2 ( int_eight int8, int_two int2, int_four int4 ); CREATE TABLE test_type_diff2_c3 ( int_two int2, int_four int4, int_eight int8 ); ALTER TABLE test_type_diff2_c1 INHERIT test_type_diff2; ALTER TABLE test_type_diff2_c2 INHERIT test_type_diff2; ALTER TABLE test_type_diff2_c3 INHERIT test_type_diff2; INSERT INTO test_type_diff2_c1 VALUES (1, 2, 3); INSERT INTO test_type_diff2_c2 VALUES (4, 5, 6); INSERT INTO test_type_diff2_c3 VALUES (7, 8, 9); ALTER TABLE test_type_diff2 ALTER COLUMN int_four TYPE int8 USING int_four::int8; -- whole-row references are disallowed ALTER TABLE test_type_diff2 ALTER COLUMN int_four TYPE int4 USING (pg_column_size(test_type_diff2)); -- check for rollback of ANALYZE corrupting table property flags (bug #11638) CREATE TABLE check_fk_presence_1 ( id int PRIMARY KEY, t text ); CREATE TABLE check_fk_presence_2 ( id int REFERENCES check_fk_presence_1, t text ); BEGIN; ALTER TABLE check_fk_presence_2 DROP CONSTRAINT check_fk_presence_2_id_fkey; ANALYZE check_fk_presence_2; ROLLBACK; \d check_fk_presence_2 DROP TABLE check_fk_presence_1, check_fk_presence_2; -- check column addition within a view (bug #14876) CREATE TABLE at_base_table ( id int, stuff text ); INSERT INTO at_base_table VALUES (23, 'skidoo'); CREATE VIEW at_view_1 AS SELECT * FROM at_base_table bt; CREATE VIEW at_view_2 AS SELECT *, to_json(v1) AS j FROM at_view_1 v1; \d+ at_view_1 \d+ at_view_2 EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM at_view_2; SELECT * FROM at_view_2; CREATE OR REPLACE VIEW at_view_1 AS SELECT *, 2 + 2 AS more FROM at_base_table bt; \d+ at_view_1 \d+ at_view_2 EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM at_view_2; SELECT * FROM at_view_2; DROP VIEW at_view_2; DROP VIEW at_view_1; DROP TABLE at_base_table; -- -- lock levels -- DROP TYPE lockmodes; CREATE TYPE lockmodes AS enum ( 'SIReadLock', 'AccessShareLock', 'RowShareLock', 'RowExclusiveLock', 'ShareUpdateExclusiveLock', 'ShareLock', 'ShareRowExclusiveLock', 'ExclusiveLock', 'AccessExclusiveLock' ); DROP VIEW my_locks; CREATE OR REPLACE VIEW my_locks AS SELECT CASE WHEN c.relname LIKE 'pg_toast%' THEN 'pg_toast' ELSE c.relname END, max(mode::lockmodes) AS max_lockmode FROM pg_locks l JOIN pg_class c ON l.relation = c.oid WHERE virtualtransaction = ( SELECT virtualtransaction FROM pg_locks WHERE transactionid = txid_current()::integer) AND locktype = 'relation' AND relnamespace != ( SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog') AND c.relname != 'my_locks' GROUP BY c.relname; CREATE TABLE alterlock ( f1 int PRIMARY KEY, f2 text ); INSERT INTO alterlock VALUES (1, 'foo'); CREATE TABLE alterlock2 ( f3 int PRIMARY KEY, f1 int ); INSERT INTO alterlock2 VALUES (1, 1); BEGIN; ALTER TABLE alterlock ALTER COLUMN f2 SET statistics 150; SELECT * FROM my_locks ORDER BY 1; ROLLBACK; BEGIN; ALTER TABLE alterlock CLUSTER ON alterlock_pkey; SELECT * FROM my_locks ORDER BY 1; COMMIT; BEGIN; ALTER TABLE alterlock SET without CLUSTER; SELECT * FROM my_locks ORDER BY 1; COMMIT; BEGIN; ALTER TABLE alterlock SET (fillfactor = 100); SELECT * FROM my_locks ORDER BY 1; COMMIT; BEGIN; ALTER TABLE alterlock RESET (fillfactor); SELECT * FROM my_locks ORDER BY 1; COMMIT; BEGIN; ALTER TABLE alterlock SET (toast.autovacuum_enabled = OFF); SELECT * FROM my_locks ORDER BY 1; COMMIT; BEGIN; ALTER TABLE alterlock SET (autovacuum_enabled = OFF); SELECT * FROM my_locks ORDER BY 1; COMMIT; BEGIN; ALTER TABLE alterlock ALTER COLUMN f2 SET (n_distinct = 1); SELECT * FROM my_locks ORDER BY 1; ROLLBACK; -- test that mixing options with different lock levels works as expected BEGIN; ALTER TABLE alterlock SET (autovacuum_enabled = OFF, fillfactor = 80); SELECT * FROM my_locks ORDER BY 1; COMMIT; BEGIN; ALTER TABLE alterlock ALTER COLUMN f2 SET storage extended; SELECT * FROM my_locks ORDER BY 1; ROLLBACK; BEGIN; ALTER TABLE alterlock ALTER COLUMN f2 SET DEFAULT 'x'; SELECT * FROM my_locks ORDER BY 1; ROLLBACK; BEGIN; CREATE TRIGGER ttdummy BEFORE DELETE OR UPDATE ON alterlock FOR EACH ROW EXECUTE PROCEDURE ttdummy (1, 1); SELECT * FROM my_locks ORDER BY 1; ROLLBACK; BEGIN; SELECT * FROM my_locks ORDER BY 1; ALTER TABLE alterlock2 ADD FOREIGN KEY (f1) REFERENCES alterlock (f1); SELECT * FROM my_locks ORDER BY 1; ROLLBACK; BEGIN; ALTER TABLE alterlock2 ADD CONSTRAINT alterlock2nv FOREIGN KEY (f1) REFERENCES alterlock (f1) NOT VALID; SELECT * FROM my_locks ORDER BY 1; COMMIT; BEGIN; ALTER TABLE alterlock2 validate CONSTRAINT alterlock2nv; SELECT * FROM my_locks ORDER BY 1; ROLLBACK; CREATE OR REPLACE VIEW my_locks AS SELECT CASE WHEN c.relname LIKE 'pg_toast%' THEN 'pg_toast' ELSE c.relname END, max(mode::lockmodes) AS max_lockmode FROM pg_locks l JOIN pg_class c ON l.relation = c.oid WHERE virtualtransaction = ( SELECT virtualtransaction FROM pg_locks WHERE transactionid = txid_current()::integer) AND locktype = 'relation' AND relnamespace != ( SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog') AND c.relname = 'my_locks' GROUP BY c.relname; -- raise exception ALTER TABLE my_locks SET (autovacuum_enabled = FALSE); ALTER VIEW my_locks SET (autovacuum_enabled = FALSE); ALTER TABLE my_locks RESET (autovacuum_enabled); ALTER VIEW my_locks RESET (autovacuum_enabled); BEGIN; ALTER VIEW my_locks SET (security_barrier = OFF); SELECT * FROM my_locks ORDER BY 1; ALTER VIEW my_locks RESET (security_barrier); ROLLBACK; -- this test intentionally applies the ALTER TABLE command against a view, but -- uses a view option so we expect this to succeed. This form of SQL is -- accepted for historical reasons, as shown in the docs for ALTER VIEW BEGIN; ALTER TABLE my_locks SET (security_barrier = OFF); SELECT * FROM my_locks ORDER BY 1; ALTER TABLE my_locks RESET (security_barrier); ROLLBACK; -- cleanup DROP TABLE alterlock2; DROP TABLE alterlock; DROP VIEW my_locks; DROP TYPE lockmodes; -- -- alter function -- CREATE FUNCTION test_strict (text) RETURNS text AS 'select coalesce($1, ''got passed a null'');' LANGUAGE sql RETURNS NULL ON NULL input; SELECT test_strict (NULL); ALTER FUNCTION test_strict (text) called ON NULL input; SELECT test_strict (NULL); CREATE FUNCTION non_strict (text) RETURNS text AS 'select coalesce($1, ''got passed a null'');' LANGUAGE sql called ON NULL input; SELECT non_strict (NULL); ALTER FUNCTION non_strict (text) RETURNS NULL ON NULL input; SELECT non_strict (NULL); -- -- alter object set schema -- CREATE SCHEMA alter1; CREATE SCHEMA alter2; CREATE TABLE alter1.t1 ( f1 serial PRIMARY KEY, f2 int CHECK (f2 > 0) ); CREATE VIEW alter1.v1 AS SELECT * FROM alter1.t1; CREATE FUNCTION alter1.plus1 (int) RETURNS int AS 'select $1+1' LANGUAGE sql; CREATE DOMAIN alter1.posint integer CHECK (value > 0); CREATE TYPE alter1.ctype AS ( f1 int, f2 text ); CREATE FUNCTION alter1.same (alter1.ctype, alter1.ctype) RETURNS boolean LANGUAGE sql AS 'select $1.f1 is not distinct from $2.f1 and $1.f2 is not distinct from $2.f2' ; CREATE OPERATOR alter1.= ( PROCEDURE = alter1.same, LEFTARG = alter1.ctype, RIGHTARG = alter1.ctype ); CREATE OPERATOR class alter1.ctype_hash_ops DEFAULT FOR TYPE alter1.ctype USING HASH AS OPERATOR 1 alter1. = ( alter1.ctype, alter1.ctype ); CREATE conversion alter1.ascii_to_utf8 FOR 'sql_ascii' TO 'utf8' FROM ascii_to_utf8; CREATE text search parser alter1.prs ( START = prsd_start, gettoken = prsd_nexttoken, END = prsd_end, lextypes = prsd_lextype ); CREATE text search configuration alter1.cfg ( parser = alter1.prs ); CREATE text search TEMPLATE alter1.tmpl ( init = dsimple_init, lexize = dsimple_lexize ); CREATE text search dictionary alter1.dict ( TEMPLATE = alter1.tmpl ); INSERT INTO alter1.t1 (f2) VALUES (11); INSERT INTO alter1.t1 (f2) VALUES (12); ALTER TABLE alter1.t1 SET SCHEMA alter1; -- no-op, same schema ALTER TABLE alter1.t1 SET SCHEMA alter2; ALTER TABLE alter1.v1 SET SCHEMA alter2; ALTER FUNCTION alter1.plus1 (int) SET SCHEMA alter2; ALTER DOMAIN alter1.posint SET SCHEMA alter2; ALTER OPERATOR class alter1.ctype_hash_ops USING HASH SET SCHEMA alter2; ALTER OPERATOR family alter1.ctype_hash_ops USING HASH SET SCHEMA alter2; ALTER OPERATOR alter1.= (alter1.ctype, alter1.ctype) SET SCHEMA alter2; ALTER FUNCTION alter1.same (alter1.ctype, alter1.ctype) SET SCHEMA alter2; ALTER TYPE alter1.ctype SET SCHEMA alter1; -- no-op, same schema ALTER TYPE alter1.ctype SET SCHEMA alter2; ALTER conversion alter1.ascii_to_utf8 SET SCHEMA alter2; ALTER text search parser alter1.prs SET SCHEMA alter2; ALTER text search configuration alter1.cfg SET SCHEMA alter2; ALTER text search TEMPLATE alter1.tmpl SET SCHEMA alter2; ALTER text search dictionary alter1.dict SET SCHEMA alter2; -- this should succeed because nothing is left in alter1 DROP SCHEMA alter1; INSERT INTO alter2.t1 (f2) VALUES (13); INSERT INTO alter2.t1 (f2) VALUES (14); SELECT * FROM alter2.t1; SELECT * FROM alter2.v1; SELECT alter2.plus1 (41); -- clean up DROP SCHEMA alter2 CASCADE; -- -- composite types -- CREATE TYPE test_type AS ( a int ); \d test_type ALTER TYPE nosuchtype ADD ATTRIBUTE b text; -- fails ALTER TYPE test_type ADD ATTRIBUTE b text; \d test_type ALTER TYPE test_type ADD ATTRIBUTE b text; -- fails ALTER TYPE test_type ALTER ATTRIBUTE b SET DATA TYPE varchar; \d test_type ALTER TYPE test_type ALTER ATTRIBUTE b SET DATA TYPE integer; \d test_type ALTER TYPE test_type DROP ATTRIBUTE b; \d test_type ALTER TYPE test_type DROP ATTRIBUTE c; -- fails ALTER TYPE test_type DROP ATTRIBUTE IF EXISTS c; ALTER TYPE test_type DROP ATTRIBUTE a, ADD ATTRIBUTE d boolean; \d test_type ALTER TYPE test_type RENAME ATTRIBUTE a TO aa; ALTER TYPE test_type RENAME ATTRIBUTE d TO dd; \d test_type DROP TYPE test_type; CREATE TYPE test_type1 AS ( a int, b text ); CREATE TABLE test_tbl1 ( x int, y test_type1 ); ALTER TYPE test_type1 ALTER ATTRIBUTE b TYPE varchar; -- fails CREATE TYPE test_type2 AS ( a int, b text ); CREATE TABLE test_tbl2 OF test_type2; CREATE TABLE test_tbl2_subclass () INHERITS ( test_tbl2 ); \d test_type2 \d test_tbl2 ALTER TYPE test_type2 ADD ATTRIBUTE c text; -- fails ALTER TYPE test_type2 ADD ATTRIBUTE c text CASCADE; \d test_type2 \d test_tbl2 ALTER TYPE test_type2 ALTER ATTRIBUTE b TYPE varchar; -- fails ALTER TYPE test_type2 ALTER ATTRIBUTE b TYPE varchar CASCADE; \d test_type2 \d test_tbl2 ALTER TYPE test_type2 DROP ATTRIBUTE b; -- fails ALTER TYPE test_type2 DROP ATTRIBUTE b CASCADE; \d test_type2 \d test_tbl2 ALTER TYPE test_type2 RENAME ATTRIBUTE a TO aa; -- fails ALTER TYPE test_type2 RENAME ATTRIBUTE a TO aa CASCADE; \d test_type2 \d test_tbl2 \d test_tbl2_subclass DROP TABLE test_tbl2_subclass; CREATE TYPE test_typex AS ( a int, b text ); CREATE TABLE test_tblx ( x int, y test_typex CHECK ((y).a > 0) ); ALTER TYPE test_typex DROP ATTRIBUTE a; -- fails ALTER TYPE test_typex DROP ATTRIBUTE a CASCADE; \d test_tblx DROP TABLE test_tblx; DROP TYPE test_typex; -- This test isn't that interesting on its own, but the purpose is to leave -- behind a table to test pg_upgrade with. The table has a composite type -- column in it, and the composite type has a dropped attribute. CREATE TYPE test_type3 AS ( a int ); CREATE TABLE test_tbl3 ( c ) AS SELECT '(1)'::test_type3; ALTER TYPE test_type3 DROP ATTRIBUTE a, ADD ATTRIBUTE b int; CREATE TYPE test_type_empty AS ( ); DROP TYPE test_type_empty; -- -- typed tables: OF / NOT OF -- CREATE TYPE tt_t0 AS ( z inet, x int, y numeric ( 8, 2)); ALTER TYPE tt_t0 DROP ATTRIBUTE z; CREATE TABLE tt0 ( x int NOT NULL, y numeric(8, 2) ); -- OK CREATE TABLE tt1 ( x int, y bigint ); -- wrong base type CREATE TABLE tt2 ( x int, y numeric(9, 2) ); -- wrong typmod CREATE TABLE tt3 ( y numeric(8, 2), x int ); -- wrong column order CREATE TABLE tt4 ( x int ); -- too few columns CREATE TABLE tt5 ( x int, y numeric(8, 2), z int ); -- too few columns CREATE TABLE tt6 () INHERITS ( tt0 ); -- can't have a parent CREATE TABLE tt7 ( x int, q text, y numeric(8, 2) ); ALTER TABLE tt7 DROP q; -- OK ALTER TABLE tt0 OF tt_t0; ALTER TABLE tt1 OF tt_t0; ALTER TABLE tt2 OF tt_t0; ALTER TABLE tt3 OF tt_t0; ALTER TABLE tt4 OF tt_t0; ALTER TABLE tt5 OF tt_t0; ALTER TABLE tt6 OF tt_t0; ALTER TABLE tt7 OF tt_t0; CREATE TYPE tt_t1 AS ( x int, y numeric ( 8, 2)); ALTER TABLE tt7 OF tt_t1; -- reassign an already-typed table ALTER TABLE tt7 NOT OF; \d tt7 -- make sure we can drop a constraint on the parent but it remains on the child CREATE TABLE test_drop_constr_parent ( c text CHECK (c IS NOT NULL) ); CREATE TABLE test_drop_constr_child () INHERITS ( test_drop_constr_parent ); ALTER TABLE ONLY test_drop_constr_parent DROP CONSTRAINT "test_drop_constr_parent_c_check"; -- should fail INSERT INTO test_drop_constr_child (c) VALUES (NULL); DROP TABLE test_drop_constr_parent CASCADE; -- -- IF EXISTS test -- ALTER TABLE IF EXISTS tt8 ADD COLUMN f int; ALTER TABLE IF EXISTS tt8 ADD CONSTRAINT xxx PRIMARY KEY (f); ALTER TABLE IF EXISTS tt8 ADD CHECK (f BETWEEN 0 AND 10); ALTER TABLE IF EXISTS tt8 ALTER COLUMN f SET DEFAULT 0; ALTER TABLE IF EXISTS tt8 RENAME COLUMN f TO f1; ALTER TABLE IF EXISTS tt8 SET SCHEMA alter2; CREATE TABLE tt8 ( a int ); CREATE SCHEMA alter2; ALTER TABLE IF EXISTS tt8 ADD COLUMN f int; ALTER TABLE IF EXISTS tt8 ADD CONSTRAINT xxx PRIMARY KEY (f); ALTER TABLE IF EXISTS tt8 ADD CHECK (f BETWEEN 0 AND 10); ALTER TABLE IF EXISTS tt8 ALTER COLUMN f SET DEFAULT 0; ALTER TABLE IF EXISTS tt8 RENAME COLUMN f TO f1; ALTER TABLE IF EXISTS tt8 SET SCHEMA alter2; \d alter2.tt8 DROP TABLE alter2.tt8; DROP SCHEMA alter2; -- -- Check conflicts between index and CHECK constraint names -- CREATE TABLE tt9 ( c integer ); ALTER TABLE tt9 ADD CHECK (c > 1); ALTER TABLE tt9 ADD CHECK (c > 2); -- picks nonconflicting name ALTER TABLE tt9 ADD CONSTRAINT foo CHECK (c > 3); ALTER TABLE tt9 ADD CONSTRAINT foo CHECK (c > 4); -- fail, dup name ALTER TABLE tt9 ADD UNIQUE (c); ALTER TABLE tt9 ADD UNIQUE (c); -- picks nonconflicting name ALTER TABLE tt9 ADD CONSTRAINT tt9_c_key UNIQUE (c); -- fail, dup name ALTER TABLE tt9 ADD CONSTRAINT foo UNIQUE (c); -- fail, dup name ALTER TABLE tt9 ADD CONSTRAINT tt9_c_key CHECK (c > 5); -- fail, dup name ALTER TABLE tt9 ADD CONSTRAINT tt9_c_key2 CHECK (c > 6); ALTER TABLE tt9 ADD UNIQUE (c); -- picks nonconflicting name \d tt9 DROP TABLE tt9; -- Check that comments on constraints and indexes are not lost at ALTER TABLE. CREATE TABLE comment_test ( id int, positive_col int CHECK (positive_col > 0), indexed_col int, CONSTRAINT comment_test_pk PRIMARY KEY (id) ); CREATE INDEX comment_test_index ON comment_test (indexed_col); COMMENT ON COLUMN comment_test.id IS 'Column ''id'' on comment_test'; COMMENT ON INDEX comment_test_index IS 'Simple index on comment_test'; COMMENT ON CONSTRAINT comment_test_positive_col_check ON comment_test IS 'CHECK constraint on comment_test.positive_col'; COMMENT ON CONSTRAINT comment_test_pk ON comment_test IS 'PRIMARY KEY constraint of comment_test'; COMMENT ON INDEX comment_test_pk IS 'Index backing the PRIMARY KEY of comment_test'; SELECT col_description('comment_test'::regclass, 1) AS comment; SELECT indexrelid::regclass::text AS index, obj_description(indexrelid, 'pg_class') AS comment FROM pg_index WHERE indrelid = 'comment_test'::regclass ORDER BY 1, 2; SELECT conname AS constraint, obj_description(oid, 'pg_constraint') AS comment FROM pg_constraint WHERE conrelid = 'comment_test'::regclass ORDER BY 1, 2; -- Change the datatype of all the columns. ALTER TABLE is optimized to not -- rebuild an index if the new data type is binary compatible with the old -- one. Check do a dummy ALTER TABLE that doesn't change the datatype -- first, to test that no-op codepath, and another one that does. ALTER TABLE comment_test ALTER COLUMN indexed_col SET DATA TYPE int; ALTER TABLE comment_test ALTER COLUMN indexed_col SET DATA TYPE text; ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int; ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text; ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int; ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint; -- Check that the comments are intact. SELECT col_description('comment_test'::regclass, 1) AS comment; SELECT indexrelid::regclass::text AS index, obj_description(indexrelid, 'pg_class') AS comment FROM pg_index WHERE indrelid = 'comment_test'::regclass ORDER BY 1, 2; SELECT conname AS constraint, obj_description(oid, 'pg_constraint') AS comment FROM pg_constraint WHERE conrelid = 'comment_test'::regclass ORDER BY 1, 2; -- Check compatibility for foreign keys and comments. This is done -- separately as rebuilding the column type of the parent leads -- to an error and would reduce the test scope. CREATE TABLE comment_test_child ( id text CONSTRAINT comment_test_child_fk REFERENCES comment_test ); CREATE INDEX comment_test_child_fk ON comment_test_child (id); COMMENT ON COLUMN comment_test_child.id IS 'Column ''id'' on comment_test_child'; COMMENT ON INDEX comment_test_child_fk IS 'Index backing the FOREIGN KEY of comment_test_child'; COMMENT ON CONSTRAINT comment_test_child_fk ON comment_test_child IS 'FOREIGN KEY constraint of comment_test_child'; -- Change column type of parent ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text; ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int USING id::integer; -- Comments should be intact SELECT col_description('comment_test_child'::regclass, 1) AS comment; SELECT indexrelid::regclass::text AS index, obj_description(indexrelid, 'pg_class') AS comment FROM pg_index WHERE indrelid = 'comment_test_child'::regclass ORDER BY 1, 2; SELECT conname AS constraint, obj_description(oid, 'pg_constraint') AS comment FROM pg_constraint WHERE conrelid = 'comment_test_child'::regclass ORDER BY 1, 2; -- Check that we map relation oids to filenodes and back correctly. Only -- display bad mappings so the test output doesn't change all the time. A -- filenode function call can return NULL for a relation dropped concurrently -- with the call's surrounding query, so ignore a NULL mapped_oid for -- relations that no longer exist after all calls finish. CREATE TEMP TABLE filenode_mapping AS SELECT oid, mapped_oid, reltablespace, relfilenode, relname FROM pg_class, pg_filenode_relation(reltablespace, pg_relation_filenode(oid)) AS mapped_oid WHERE relkind IN ('r', 'i', 'S', 't', 'm') AND mapped_oid IS DISTINCT FROM oid; SELECT m.* FROM filenode_mapping m LEFT JOIN pg_class c ON c.oid = m.oid WHERE c.oid IS NOT NULL OR m.mapped_oid IS NOT NULL; -- Checks on creating and manipulation of user defined relations in -- pg_catalog. -- -- XXX: It would be useful to add checks around trying to manipulate -- catalog tables, but that might have ugly consequences when run -- against an existing server with allow_system_table_mods = on. SHOW allow_system_table_mods; -- disallowed because of search_path issues with pg_dump CREATE TABLE pg_catalog.new_system_table (); -- instead create in public first, move to catalog CREATE TABLE new_system_table ( id serial PRIMARY KEY, othercol text ); ALTER TABLE new_system_table SET SCHEMA pg_catalog; ALTER TABLE new_system_table SET SCHEMA public; ALTER TABLE new_system_table SET SCHEMA pg_catalog; -- will be ignored -- already there: ALTER TABLE new_system_table SET SCHEMA pg_catalog; ALTER TABLE new_system_table RENAME TO old_system_table; CREATE INDEX old_system_table__othercol ON old_system_table (othercol); INSERT INTO old_system_table (othercol) VALUES ('somedata'), ('otherdata'); UPDATE old_system_table SET id = - id; DELETE FROM old_system_table WHERE othercol = 'somedata'; TRUNCATE old_system_table; ALTER TABLE old_system_table DROP CONSTRAINT new_system_table_pkey; ALTER TABLE old_system_table DROP COLUMN othercol; DROP TABLE old_system_table; -- set logged CREATE UNLOGGED TABLE unlogged1 ( f1 serial PRIMARY KEY, f2 text ); -- check relpersistence of an unlogged table SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged1' UNION ALL SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged1' UNION ALL SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged1' ORDER BY relname; CREATE UNLOGGED TABLE unlogged2 ( f1 serial PRIMARY KEY, f2 integer REFERENCES unlogged1 ); -- foreign key CREATE UNLOGGED TABLE unlogged3 ( f1 serial PRIMARY KEY, f2 integer REFERENCES unlogged3 ); -- self-referencing foreign key ALTER TABLE unlogged3 SET LOGGED; -- skip self-referencing foreign key ALTER TABLE unlogged2 SET LOGGED; -- fails because a foreign key to an unlogged table exists ALTER TABLE unlogged1 SET LOGGED; -- check relpersistence of an unlogged table after changing to permanent SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged1' UNION ALL SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged1' UNION ALL SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged1' ORDER BY relname; ALTER TABLE unlogged1 SET LOGGED; -- silently do nothing DROP TABLE unlogged3; DROP TABLE unlogged2; DROP TABLE unlogged1; -- set unlogged CREATE TABLE logged1 ( f1 serial PRIMARY KEY, f2 text ); -- check relpersistence of a permanent table SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged1' UNION ALL SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged1' UNION ALL SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged1' ORDER BY relname; CREATE TABLE logged2 ( f1 serial PRIMARY KEY, f2 integer REFERENCES logged1 ); -- foreign key CREATE TABLE logged3 ( f1 serial PRIMARY KEY, f2 integer REFERENCES logged3 ); -- self-referencing foreign key ALTER TABLE logged1 SET UNLOGGED; -- fails because a foreign key from a permanent table exists ALTER TABLE logged3 SET UNLOGGED; -- skip self-referencing foreign key ALTER TABLE logged2 SET UNLOGGED; ALTER TABLE logged1 SET UNLOGGED; -- check relpersistence of a permanent table after changing to unlogged SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged1' UNION ALL SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged1' UNION ALL SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged1' ORDER BY relname; ALTER TABLE logged1 SET UNLOGGED; -- silently do nothing DROP TABLE logged3; DROP TABLE logged2; DROP TABLE logged1; -- test ADD COLUMN IF NOT EXISTS CREATE TABLE test_add_column ( c1 integer ); \d test_add_column ALTER TABLE test_add_column ADD COLUMN c2 integer; \d test_add_column ALTER TABLE test_add_column ADD COLUMN c2 integer; -- fail because c2 already exists ALTER TABLE ONLY test_add_column ADD COLUMN c2 integer; -- fail because c2 already exists \d test_add_column ALTER TABLE test_add_column ADD COLUMN IF NOT EXISTS c2 integer; -- skipping because c2 already exists ALTER TABLE ONLY test_add_column ADD COLUMN IF NOT EXISTS c2 integer; -- skipping because c2 already exists \d test_add_column ALTER TABLE test_add_column ADD COLUMN c2 integer, -- fail because c2 already exists ADD COLUMN c3 integer; \d test_add_column ALTER TABLE test_add_column ADD COLUMN IF NOT EXISTS c2 integer, -- skipping because c2 already exists ADD COLUMN c3 integer; -- fail because c3 already exists \d test_add_column ALTER TABLE test_add_column ADD COLUMN IF NOT EXISTS c2 integer, -- skipping because c2 already exists ADD COLUMN IF NOT EXISTS c3 integer; -- skipping because c3 already exists \d test_add_column ALTER TABLE test_add_column ADD COLUMN IF NOT EXISTS c2 integer, -- skipping because c2 already exists ADD COLUMN IF NOT EXISTS c3 integer, -- skipping because c3 already exists ADD COLUMN c4 integer; \d test_add_column DROP TABLE test_add_column; -- unsupported constraint types for partitioned tables CREATE TABLE partitioned ( a int, b int ) PARTITION BY RANGE (a, (a + b + 1)); ALTER TABLE partitioned ADD EXCLUDE USING gist (a WITH &&); -- cannot drop column that is part of the partition key ALTER TABLE partitioned DROP COLUMN a; ALTER TABLE partitioned ALTER COLUMN a TYPE char(5); ALTER TABLE partitioned DROP COLUMN b; ALTER TABLE partitioned ALTER COLUMN b TYPE char(5); -- partitioned table cannot participate in regular inheritance CREATE TABLE nonpartitioned ( a int, b int ); ALTER TABLE partitioned INHERIT nonpartitioned; ALTER TABLE nonpartitioned INHERIT partitioned; -- cannot add NO INHERIT constraint to partitioned tables ALTER TABLE partitioned ADD CONSTRAINT chk_a CHECK (a > 0) NO INHERIT; DROP TABLE partitioned, nonpartitioned; -- -- ATTACH PARTITION -- -- check that target table is partitioned CREATE TABLE unparted ( a int ); CREATE TABLE fail_part ( LIKE unparted ); ALTER TABLE unparted ATTACH PARTITION fail_part FOR VALUES IN ('a'); DROP TABLE unparted, fail_part; -- check that partition bound is compatible CREATE TABLE list_parted ( a int NOT NULL, b char(2) COLLATE "C", CONSTRAINT check_a CHECK (a > 0) ) PARTITION BY LIST (a); CREATE TABLE fail_part ( LIKE list_parted ); ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES FROM (1) TO (10); DROP TABLE fail_part; -- check that the table being attached exists ALTER TABLE list_parted ATTACH PARTITION nonexistant FOR VALUES IN (1); -- check ownership of the source table CREATE ROLE regress_test_me; CREATE ROLE regress_test_not_me; CREATE TABLE not_owned_by_me ( LIKE list_parted ); ALTER TABLE not_owned_by_me OWNER TO regress_test_not_me; SET SESSION AUTHORIZATION regress_test_me; CREATE TABLE owned_by_me ( a int ) PARTITION BY LIST (a); ALTER TABLE owned_by_me ATTACH PARTITION not_owned_by_me FOR VALUES IN (1); RESET SESSION AUTHORIZATION; DROP TABLE owned_by_me, not_owned_by_me; DROP ROLE regress_test_not_me; DROP ROLE regress_test_me; -- check that the table being attached is not part of regular inheritance CREATE TABLE parent ( LIKE list_parted ); CREATE TABLE child () INHERITS ( parent ); ALTER TABLE list_parted ATTACH PARTITION child FOR VALUES IN (1); ALTER TABLE list_parted ATTACH PARTITION parent FOR VALUES IN (1); DROP TABLE parent CASCADE; -- check any TEMP-ness CREATE TEMP TABLE temp_parted ( a int ) PARTITION BY LIST (a); CREATE TABLE perm_part ( a int ); ALTER TABLE temp_parted ATTACH PARTITION perm_part FOR VALUES IN (1); DROP TABLE temp_parted, perm_part; -- check that the table being attached is not a typed table CREATE TYPE mytype AS ( a int ); CREATE TABLE fail_part OF mytype; ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); DROP TYPE mytype CASCADE; -- check that the table being attached has only columns present in the parent CREATE TABLE fail_part ( LIKE list_parted, c int ); ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); DROP TABLE fail_part; -- check that the table being attached has every column of the parent CREATE TABLE fail_part ( a int NOT NULL ); ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); DROP TABLE fail_part; -- check that columns match in type, collation and NOT NULL status CREATE TABLE fail_part ( b char(3), a int NOT NULL ); ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); ALTER TABLE fail_part ALTER b TYPE char(2) COLLATE "POSIX"; ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); DROP TABLE fail_part; -- check that the table being attached has all constraints of the parent CREATE TABLE fail_part ( b char(2) COLLATE "C", a int NOT NULL ); ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); -- check that the constraint matches in definition with parent's constraint ALTER TABLE fail_part ADD CONSTRAINT check_a CHECK (a >= 0); ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); DROP TABLE fail_part; -- check the attributes and constraints after partition is attached CREATE TABLE part_1 ( a int NOT NULL, b char(2) COLLATE "C", CONSTRAINT check_a CHECK (a > 0) ); ALTER TABLE list_parted ATTACH PARTITION part_1 FOR VALUES IN (1); -- attislocal and conislocal are always false for merged attributes and constraints respectively. SELECT attislocal, attinhcount FROM pg_attribute WHERE attrelid = 'part_1'::regclass AND attnum > 0; SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_1'::regclass AND conname = 'check_a'; -- check that the new partition won't overlap with an existing partition CREATE TABLE fail_part ( LIKE part_1 INCLUDING CONSTRAINTS ); ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); DROP TABLE fail_part; -- check that an existing table can be attached as a default partition CREATE TABLE def_part ( LIKE list_parted INCLUDING CONSTRAINTS ); ALTER TABLE list_parted ATTACH PARTITION def_part DEFAULT; -- check attaching default partition fails if a default partition already -- exists CREATE TABLE fail_def_part ( LIKE part_1 INCLUDING CONSTRAINTS ); ALTER TABLE list_parted ATTACH PARTITION fail_def_part DEFAULT; -- check validation when attaching list partitions CREATE TABLE list_parted2 ( a int, b char ) PARTITION BY LIST (a); -- check that violating rows are correctly reported CREATE TABLE part_2 ( LIKE list_parted2 ); INSERT INTO part_2 VALUES (3, 'a'); ALTER TABLE list_parted2 ATTACH PARTITION part_2 FOR VALUES IN (2); -- should be ok after deleting the bad row DELETE FROM part_2; ALTER TABLE list_parted2 ATTACH PARTITION part_2 FOR VALUES IN (2); -- check partition cannot be attached if default has some row for its values CREATE TABLE list_parted2_def PARTITION OF list_parted2 DEFAULT; INSERT INTO list_parted2_def VALUES (11, 'z'); CREATE TABLE part_3 ( LIKE list_parted2 ); ALTER TABLE list_parted2 ATTACH PARTITION part_3 FOR VALUES IN (11); -- should be ok after deleting the bad row DELETE FROM list_parted2_def WHERE a = 11; ALTER TABLE list_parted2 ATTACH PARTITION part_3 FOR VALUES IN (11); -- adding constraints that describe the desired partition constraint -- (or more restrictive) will help skip the validation scan CREATE TABLE part_3_4 ( LIKE list_parted2, CONSTRAINT check_a CHECK (a IN (3)) ); -- however, if a list partition does not accept nulls, there should be -- an explicit NOT NULL constraint on the partition key column for the -- validation scan to be skipped; ALTER TABLE list_parted2 ATTACH PARTITION part_3_4 FOR VALUES IN (3, 4); -- adding a NOT NULL constraint will cause the scan to be skipped ALTER TABLE list_parted2 DETACH PARTITION part_3_4; ALTER TABLE part_3_4 ALTER a SET NOT NULL; ALTER TABLE list_parted2 ATTACH PARTITION part_3_4 FOR VALUES IN (3, 4); -- check if default partition scan skipped ALTER TABLE list_parted2_def ADD CONSTRAINT check_a CHECK (a IN (5, 6)); CREATE TABLE part_55_66 PARTITION OF list_parted2 FOR VALUES IN (55, 66); -- check validation when attaching range partitions CREATE TABLE range_parted ( a int, b int ) PARTITION BY RANGE (a, b); -- check that violating rows are correctly reported CREATE TABLE part1 ( a int NOT NULL CHECK (a = 1), b int NOT NULL CHECK (b >= 1 AND b <= 10) ); INSERT INTO part1 VALUES (1, 10); -- Remember the TO bound is exclusive ALTER TABLE range_parted ATTACH PARTITION part1 FOR VALUES FROM (1, 1) TO (1, 10); -- should be ok after deleting the bad row DELETE FROM part1; ALTER TABLE range_parted ATTACH PARTITION part1 FOR VALUES FROM (1, 1) TO (1, 10); -- adding constraints that describe the desired partition constraint -- (or more restrictive) will help skip the validation scan CREATE TABLE part2 ( a int NOT NULL CHECK (a = 1), b int NOT NULL CHECK (b >= 10 AND b < 18) ); ALTER TABLE range_parted ATTACH PARTITION part2 FOR VALUES FROM (1, 10) TO (1, 20); -- Create default partition CREATE TABLE partr_def1 PARTITION OF range_parted DEFAULT; -- Only one default partition is allowed, hence, following should give error CREATE TABLE partr_def2 ( LIKE part1 INCLUDING CONSTRAINTS ); ALTER TABLE range_parted ATTACH PARTITION partr_def2 DEFAULT; -- Overlapping partitions cannot be attached, hence, following should give error INSERT INTO partr_def1 VALUES (2, 10); CREATE TABLE part3 ( LIKE range_parted ); ALTER TABLE range_parted ATTACH PARTITION part3 FOR VALUES FROM (2, 10) TO (2, 20); -- Attaching partitions should be successful when there are no overlapping rows ALTER TABLE range_parted ATTACH PARTITION part3 FOR VALUES FROM (3, 10) TO (3, 20); -- check that leaf partitions are scanned when attaching a partitioned -- table CREATE TABLE part_5 ( LIKE list_parted2 ) PARTITION BY LIST (b); -- check that violating rows are correctly reported CREATE TABLE part_5_a PARTITION OF part_5 FOR VALUES IN ('a'); INSERT INTO part_5_a (a, b) VALUES (6, 'a'); ALTER TABLE list_parted2 ATTACH PARTITION part_5 FOR VALUES IN (5); -- delete the faulting row and also add a constraint to skip the scan DELETE FROM part_5_a WHERE a NOT IN (3); ALTER TABLE part_5 ADD CONSTRAINT check_a CHECK (a IS NOT NULL AND a = 5); ALTER TABLE list_parted2 ATTACH PARTITION part_5 FOR VALUES IN (5); ALTER TABLE list_parted2 DETACH PARTITION part_5; ALTER TABLE part_5 DROP CONSTRAINT check_a; -- scan should again be skipped, even though NOT NULL is now a column property ALTER TABLE part_5 ADD CONSTRAINT check_a CHECK (a IN (5)), ALTER a SET NOT NULL; ALTER TABLE list_parted2 ATTACH PARTITION part_5 FOR VALUES IN (5); -- Check the case where attnos of the partitioning columns in the table being -- attached differs from the parent. It should not affect the constraint- -- checking logic that allows to skip the scan. CREATE TABLE part_6 ( c int, LIKE list_parted2, CONSTRAINT check_a CHECK (a IS NOT NULL AND a = 6) ); ALTER TABLE part_6 DROP c; ALTER TABLE list_parted2 ATTACH PARTITION part_6 FOR VALUES IN (6); -- Similar to above, but the table being attached is a partitioned table -- whose partition has still different attnos for the root partitioning -- columns. CREATE TABLE part_7 ( LIKE list_parted2, CONSTRAINT check_a CHECK (a IS NOT NULL AND a = 7) ) PARTITION BY LIST (b); CREATE TABLE part_7_a_null ( c int, d int, e int, LIKE list_parted2, -- 'a' will have attnum = 4 CONSTRAINT check_b CHECK (b IS NULL OR b = 'a'), CONSTRAINT check_a CHECK (a IS NOT NULL AND a = 7) ); ALTER TABLE part_7_a_null DROP c, DROP d, DROP e; ALTER TABLE part_7 ATTACH PARTITION part_7_a_null FOR VALUES IN ('a', NULL); ALTER TABLE list_parted2 ATTACH PARTITION part_7 FOR VALUES IN (7); -- Same example, but check this time that the constraint correctly detects -- violating rows ALTER TABLE list_parted2 DETACH PARTITION part_7; ALTER TABLE part_7 DROP CONSTRAINT check_a; -- thusly, scan won't be skipped INSERT INTO part_7 (a, b) VALUES (8, NULL), (9, 'a'); SELECT tableoid::regclass, a, b FROM part_7 ORDER BY a; ALTER TABLE list_parted2 ATTACH PARTITION part_7 FOR VALUES IN (7); -- check that leaf partitions of default partition are scanned when -- attaching a partitioned table. ALTER TABLE part_5 DROP CONSTRAINT check_a; CREATE TABLE part5_def PARTITION OF part_5 DEFAULT PARTITION BY LIST (a); CREATE TABLE part5_def_p1 PARTITION OF part5_def FOR VALUES IN (5); INSERT INTO part5_def_p1 VALUES (5, 'y'); CREATE TABLE part5_p1 ( LIKE part_5 ); ALTER TABLE part_5 ATTACH PARTITION part5_p1 FOR VALUES IN ('y'); -- should be ok after deleting the bad row DELETE FROM part5_def_p1 WHERE b = 'y'; ALTER TABLE part_5 ATTACH PARTITION part5_p1 FOR VALUES IN ('y'); -- check that the table being attached is not already a partition ALTER TABLE list_parted2 ATTACH PARTITION part_2 FOR VALUES IN (2); -- check that circular inheritance is not allowed ALTER TABLE part_5 ATTACH PARTITION list_parted2 FOR VALUES IN ('b'); ALTER TABLE list_parted2 ATTACH PARTITION list_parted2 FOR VALUES IN (0); -- If a partitioned table being created or an existing table being attached -- as a partition does not have a constraint that would allow validation scan -- to be skipped, but an individual partition does, then the partition's -- validation scan is skipped. CREATE TABLE quuux ( a int, b text ) PARTITION BY LIST (a); CREATE TABLE quuux_default PARTITION OF quuux DEFAULT PARTITION BY LIST (b); CREATE TABLE quuux_default1 PARTITION OF quuux_default (CONSTRAINT check_1 CHECK (a IS NOT NULL AND a = 1)) FOR VALUES IN ('b'); CREATE TABLE quuux1 ( a int, b text ); ALTER TABLE quuux ATTACH PARTITION quuux1 FOR VALUES IN (1); -- validate! CREATE TABLE quuux2 ( a int, b text ); ALTER TABLE quuux ATTACH PARTITION quuux2 FOR VALUES IN (2); -- skip validation DROP TABLE quuux1, quuux2; -- should validate for quuux1, but not for quuux2 CREATE TABLE quuux1 PARTITION OF quuux FOR VALUES IN (1); CREATE TABLE quuux2 PARTITION OF quuux FOR VALUES IN (2); DROP TABLE quuux; -- check validation when attaching hash partitions -- Use hand-rolled hash functions and operator class to get predictable result -- on different matchines. part_test_int4_ops is defined in insert.sql. -- check that the new partition won't overlap with an existing partition CREATE TABLE hash_parted ( a int, b int ) PARTITION BY HASH (a part_test_int4_ops); CREATE TABLE hpart_1 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 4, REMAINDER 0); CREATE TABLE fail_part ( LIKE hpart_1 ); ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 8, REMAINDER 4); ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 8, REMAINDER 0); DROP TABLE fail_part; -- check validation when attaching hash partitions -- check that violating rows are correctly reported CREATE TABLE hpart_2 ( LIKE hash_parted ); INSERT INTO hpart_2 VALUES (3, 0); ALTER TABLE hash_parted ATTACH PARTITION hpart_2 FOR VALUES WITH (MODULUS 4, REMAINDER 1); -- should be ok after deleting the bad row DELETE FROM hpart_2; ALTER TABLE hash_parted ATTACH PARTITION hpart_2 FOR VALUES WITH (MODULUS 4, REMAINDER 1); -- check that leaf partitions are scanned when attaching a partitioned -- table CREATE TABLE hpart_5 ( LIKE hash_parted ) PARTITION BY LIST (b); -- check that violating rows are correctly reported CREATE TABLE hpart_5_a PARTITION OF hpart_5 FOR VALUES IN ('1', '2', '3'); INSERT INTO hpart_5_a (a, b) VALUES (7, 1); ALTER TABLE hash_parted ATTACH PARTITION hpart_5 FOR VALUES WITH (MODULUS 4, REMAINDER 2); -- should be ok after deleting the bad row DELETE FROM hpart_5_a; ALTER TABLE hash_parted ATTACH PARTITION hpart_5 FOR VALUES WITH (MODULUS 4, REMAINDER 2); -- check that the table being attach is with valid modulus and remainder value CREATE TABLE fail_part ( LIKE hash_parted ); ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 0, REMAINDER 1); ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 8, REMAINDER 8); ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 3, REMAINDER 2); DROP TABLE fail_part; -- -- DETACH PARTITION -- -- check that the table is partitioned at all CREATE TABLE regular_table ( a int ); ALTER TABLE regular_table DETACH PARTITION any_name; DROP TABLE regular_table; -- check that the partition being detached exists at all ALTER TABLE list_parted2 DETACH PARTITION part_4; ALTER TABLE hash_parted DETACH PARTITION hpart_4; -- check that the partition being detached is actually a partition of the parent CREATE TABLE not_a_part ( a int ); ALTER TABLE list_parted2 DETACH PARTITION not_a_part; ALTER TABLE list_parted2 DETACH PARTITION part_1; ALTER TABLE hash_parted DETACH PARTITION not_a_part; DROP TABLE not_a_part; -- check that, after being detached, attinhcount/coninhcount is dropped to 0 and -- attislocal/conislocal is set to true ALTER TABLE list_parted2 DETACH PARTITION part_3_4; SELECT attinhcount, attislocal FROM pg_attribute WHERE attrelid = 'part_3_4'::regclass AND attnum > 0; SELECT coninhcount, conislocal FROM pg_constraint WHERE conrelid = 'part_3_4'::regclass AND conname = 'check_a'; DROP TABLE part_3_4; -- check that a detached partition is not dropped on dropping a partitioned table CREATE TABLE range_parted2 ( a int ) PARTITION BY RANGE (a); CREATE TABLE part_rp PARTITION OF range_parted2 FOR VALUES FROM (0) TO (100); ALTER TABLE range_parted2 DETACH PARTITION part_rp; DROP TABLE range_parted2; SELECT * FROM part_rp; DROP TABLE part_rp; -- Check ALTER TABLE commands for partitioned tables and partitions -- cannot add/drop column to/from *only* the parent ALTER TABLE ONLY list_parted2 ADD COLUMN c int; ALTER TABLE ONLY list_parted2 DROP COLUMN b; -- cannot add a column to partition or drop an inherited one ALTER TABLE part_2 ADD COLUMN c text; ALTER TABLE part_2 DROP COLUMN b; -- Nor rename, alter type ALTER TABLE part_2 RENAME COLUMN b TO c; ALTER TABLE part_2 ALTER COLUMN b TYPE text; -- cannot add/drop NOT NULL or check constraints to *only* the parent, when -- partitions exist ALTER TABLE ONLY list_parted2 ALTER b SET NOT NULL; ALTER TABLE ONLY list_parted2 ADD CONSTRAINT check_b CHECK (b <> 'zz'); ALTER TABLE list_parted2 ALTER b SET NOT NULL; ALTER TABLE ONLY list_parted2 ALTER b DROP NOT NULL; ALTER TABLE list_parted2 ADD CONSTRAINT check_b CHECK (b <> 'zz'); ALTER TABLE ONLY list_parted2 DROP CONSTRAINT check_b; -- It's alright though, if no partitions are yet created CREATE TABLE parted_no_parts ( a int ) PARTITION BY LIST (a); ALTER TABLE ONLY parted_no_parts ALTER a SET NOT NULL; ALTER TABLE ONLY parted_no_parts ADD CONSTRAINT check_a CHECK (a > 0); ALTER TABLE ONLY parted_no_parts ALTER a DROP NOT NULL; ALTER TABLE ONLY parted_no_parts DROP CONSTRAINT check_a; DROP TABLE parted_no_parts; -- cannot drop inherited NOT NULL or check constraints from partition ALTER TABLE list_parted2 ALTER b SET NOT NULL, ADD CONSTRAINT check_a2 CHECK (a > 0); ALTER TABLE part_2 ALTER b DROP NOT NULL; ALTER TABLE part_2 DROP CONSTRAINT check_a2; -- Doesn't make sense to add NO INHERIT constraints on partitioned tables ALTER TABLE list_parted2 ADD CONSTRAINT check_b2 CHECK (b <> 'zz') NO INHERIT; -- check that a partition cannot participate in regular inheritance CREATE TABLE inh_test () INHERITS ( part_2 ); CREATE TABLE inh_test ( LIKE part_2 ); ALTER TABLE inh_test INHERIT part_2; ALTER TABLE part_2 INHERIT inh_test; -- cannot drop or alter type of partition key columns of lower level -- partitioned tables; for example, part_5, which is list_parted2's -- partition, is partitioned on b; ALTER TABLE list_parted2 DROP COLUMN b; ALTER TABLE list_parted2 ALTER COLUMN b TYPE text; -- dropping non-partition key columns should be allowed on the parent table. ALTER TABLE list_parted DROP COLUMN b; SELECT * FROM list_parted; -- cleanup DROP TABLE list_parted, list_parted2, range_parted; DROP TABLE fail_def_part; DROP TABLE hash_parted; -- more tests for certain multi-level partitioning scenarios CREATE TABLE p ( a int, b int ) PARTITION BY RANGE (a, b); CREATE TABLE p1 ( b int, a int NOT NULL ) PARTITION BY RANGE (b); CREATE TABLE p11 ( LIKE p1 ); ALTER TABLE p11 DROP a; ALTER TABLE p11 ADD a int; ALTER TABLE p11 DROP a; ALTER TABLE p11 ADD a int NOT NULL; -- attnum for key attribute 'a' is different in p, p1, and p11 SELECT attrelid::regclass, attname, attnum FROM pg_attribute WHERE attname = 'a' AND (attrelid = 'p'::regclass OR attrelid = 'p1'::regclass OR attrelid = 'p11'::regclass) ORDER BY attrelid::regclass::text; ALTER TABLE p1 ATTACH PARTITION p11 FOR VALUES FROM (2) TO (5); INSERT INTO p1 (a, b) VALUES (2, 3); -- check that partition validation scan correctly detects violating rows ALTER TABLE p ATTACH PARTITION p1 FOR VALUES FROM (1, 2) TO (1, 10); -- cleanup DROP TABLE p; DROP TABLE p1; -- validate constraint on partitioned tables should only scan leaf partitions CREATE TABLE parted_validate_test ( a int ) PARTITION BY LIST (a); CREATE TABLE parted_validate_test_1 PARTITION OF parted_validate_test FOR VALUES IN (0, 1); ALTER TABLE parted_validate_test ADD CONSTRAINT parted_validate_test_chka CHECK (a > 0) NOT valid; ALTER TABLE parted_validate_test validate CONSTRAINT parted_validate_test_chka; DROP TABLE parted_validate_test; -- test alter column options CREATE TABLE attmp ( i integer ); INSERT INTO attmp VALUES (1); ALTER TABLE attmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2); ALTER TABLE attmp ALTER COLUMN i RESET (n_distinct_inherited); ANALYZE attmp; DROP TABLE attmp; DROP USER regress_alter_table_user1; -- check that violating rows are correctly reported when attaching as the -- default partition CREATE TABLE defpart_attach_test ( a int ) PARTITION BY LIST (a); CREATE TABLE defpart_attach_test1 PARTITION OF defpart_attach_test FOR VALUES IN (1); CREATE TABLE defpart_attach_test_d ( LIKE defpart_attach_test ); INSERT INTO defpart_attach_test_d VALUES (1), (2); -- error because its constraint as the default partition would be violated -- by the row containing 1 ALTER TABLE defpart_attach_test ATTACH PARTITION defpart_attach_test_d DEFAULT; DELETE FROM defpart_attach_test_d WHERE a = 1; ALTER TABLE defpart_attach_test_d ADD CHECK (a > 1); -- should be attached successfully and without needing to be scanned ALTER TABLE defpart_attach_test ATTACH PARTITION defpart_attach_test_d DEFAULT; DROP TABLE defpart_attach_test; -- check combinations of temporary and permanent relations when attaching -- partitions. CREATE TABLE perm_part_parent ( a int ) PARTITION BY LIST (a); CREATE temp TABLE temp_part_parent ( a int ) PARTITION BY LIST (a); CREATE TABLE perm_part_child ( a int ); CREATE temp TABLE temp_part_child ( a int ); ALTER TABLE temp_part_parent ATTACH PARTITION perm_part_child DEFAULT; -- error ALTER TABLE perm_part_parent ATTACH PARTITION temp_part_child DEFAULT; -- error ALTER TABLE temp_part_parent ATTACH PARTITION temp_part_child DEFAULT; -- ok DROP TABLE perm_part_parent CASCADE; DROP TABLE temp_part_parent CASCADE; -- check that attaching partitions to a table while it is being used is -- prevented CREATE TABLE tab_part_attach ( a int ) PARTITION BY LIST (a); CREATE OR REPLACE FUNCTION func_part_attach () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN EXECUTE 'create table tab_part_attach_1 (a int)'; EXECUTE 'alter table tab_part_attach attach partition tab_part_attach_1 for values in (1)'; RETURN NULL; END $$; CREATE TRIGGER trig_part_attach BEFORE INSERT ON tab_part_attach FOR EACH statement EXECUTE PROCEDURE func_part_attach (); INSERT INTO tab_part_attach VALUES (1); DROP TABLE tab_part_attach; DROP FUNCTION func_part_attach (); -- test case where the partitioning operator is a SQL function whose -- evaluation results in the table's relcache being rebuilt partway through -- the execution of an ATTACH PARTITION command CREATE FUNCTION at_test_sql_partop (int4, int4) RETURNS int LANGUAGE sql AS $$ SELECT CASE WHEN $1 = $2 THEN 0 WHEN $1 > $2 THEN 1 ELSE - 1 END; $$; CREATE OPERATOR class at_test_sql_partop FOR TYPE int4 USING btree AS OPERATOR 1 < (int4, int4), OPERATOR 2 <= (int4, int4), OPERATOR 3 = (int4, int4), OPERATOR 4 >= (int4, int4), OPERATOR 5 > (int4, int4), FUNCTION 1 at_test_sql_partop (int4, int4 ); CREATE TABLE at_test_sql_partop ( a int ) PARTITION BY RANGE (a at_test_sql_partop); CREATE TABLE at_test_sql_partop_1 ( a int ); ALTER TABLE at_test_sql_partop ATTACH PARTITION at_test_sql_partop_1 FOR VALUES FROM (0) TO (10); DROP TABLE at_test_sql_partop; DROP OPERATOR class at_test_sql_partop USING btree; DROP FUNCTION at_test_sql_partop; pgFormatter-4.2/t/pg-test-files/expected/amutils.sql000066400000000000000000000074351361326045100226110ustar00rootroot00000000000000-- -- Test index AM property-reporting functions -- SELECT prop, pg_indexam_has_property(a.oid, prop) AS "AM", pg_index_has_property('onek_hundred'::regclass, prop) AS "Index", pg_index_column_has_property('onek_hundred'::regclass, 1, prop) AS "Column" FROM pg_am a, unnest(ARRAY['asc', 'desc', 'nulls_first', 'nulls_last', 'orderable', 'distance_orderable', 'returnable', 'search_array', 'search_nulls', 'clusterable', 'index_scan', 'bitmap_scan', 'backward_scan', 'can_order', 'can_unique', 'can_multi_col', 'can_exclude', 'can_include', 'bogus']::text[]) WITH ORDINALITY AS u (prop, ord) WHERE a.amname = 'btree' ORDER BY ord; SELECT prop, pg_indexam_has_property(a.oid, prop) AS "AM", pg_index_has_property('gcircleind'::regclass, prop) AS "Index", pg_index_column_has_property('gcircleind'::regclass, 1, prop) AS "Column" FROM pg_am a, unnest(ARRAY['asc', 'desc', 'nulls_first', 'nulls_last', 'orderable', 'distance_orderable', 'returnable', 'search_array', 'search_nulls', 'clusterable', 'index_scan', 'bitmap_scan', 'backward_scan', 'can_order', 'can_unique', 'can_multi_col', 'can_exclude', 'can_include', 'bogus']::text[]) WITH ORDINALITY AS u (prop, ord) WHERE a.amname = 'gist' ORDER BY ord; SELECT prop, pg_index_column_has_property('onek_hundred'::regclass, 1, prop) AS btree, pg_index_column_has_property('hash_i4_index'::regclass, 1, prop) AS HASH, pg_index_column_has_property('gcircleind'::regclass, 1, prop) AS gist, pg_index_column_has_property('sp_radix_ind'::regclass, 1, prop) AS spgist_radix, pg_index_column_has_property('sp_quad_ind'::regclass, 1, prop) AS spgist_quad, pg_index_column_has_property('botharrayidx'::regclass, 1, prop) AS gin, pg_index_column_has_property('brinidx'::regclass, 1, prop) AS brin FROM unnest(ARRAY['asc', 'desc', 'nulls_first', 'nulls_last', 'orderable', 'distance_orderable', 'returnable', 'search_array', 'search_nulls', 'bogus']::text[]) WITH ORDINALITY AS u (prop, ord) ORDER BY ord; SELECT prop, pg_index_has_property('onek_hundred'::regclass, prop) AS btree, pg_index_has_property('hash_i4_index'::regclass, prop) AS HASH, pg_index_has_property('gcircleind'::regclass, prop) AS gist, pg_index_has_property('sp_radix_ind'::regclass, prop) AS spgist, pg_index_has_property('botharrayidx'::regclass, prop) AS gin, pg_index_has_property('brinidx'::regclass, prop) AS brin FROM unnest(ARRAY['clusterable', 'index_scan', 'bitmap_scan', 'backward_scan', 'bogus']::text[]) WITH ORDINALITY AS u (prop, ord) ORDER BY ord; SELECT amname, prop, pg_indexam_has_property(a.oid, prop) AS p FROM pg_am a, unnest(ARRAY['can_order', 'can_unique', 'can_multi_col', 'can_exclude', 'can_include', 'bogus']::text[]) WITH ORDINALITY AS u (prop, ord) WHERE amtype = 'i' ORDER BY amname, ord; -- -- additional checks for pg_index_column_has_property -- CREATE TEMP TABLE foo ( f1 int, f2 int, f3 int, f4 int ); CREATE INDEX fooindex ON foo (f1 DESC, f2 ASC, f3 nulls FIRST, f4 nulls LAST); SELECT col, prop, pg_index_column_has_property(o, col, prop) FROM ( VALUES ('fooindex'::regclass)) v1 (o), ( VALUES (1, 'orderable'), (2, 'asc'), (3, 'desc'), (4, 'nulls_first'), (5, 'nulls_last'), (6, 'bogus')) v2 (idx, prop), generate_series(1, 4) col ORDER BY col, idx; CREATE INDEX foocover ON foo (f1) INCLUDE (f2, f3); SELECT col, prop, pg_index_column_has_property(o, col, prop) FROM ( VALUES ('foocover'::regclass)) v1 (o), ( VALUES (1, 'orderable'), (2, 'asc'), (3, 'desc'), (4, 'nulls_first'), (5, 'nulls_last'), (6, 'distance_orderable'), (7, 'returnable'), (8, 'bogus')) v2 (idx, prop), generate_series(1, 3) col ORDER BY col, idx; pgFormatter-4.2/t/pg-test-files/expected/arrays.sql000066400000000000000000000702451361326045100224330ustar00rootroot00000000000000-- -- ARRAYS -- CREATE TABLE arrtest ( a int2[], b int4[][][], c name[], d text[][], e float8[], f char(5)[], g varchar(5)[] ); -- -- only the 'e' array is 0-based, the others are 1-based. -- INSERT INTO arrtest (a[1:5], b[1:1][1:2][1:2], c, d, f, g) VALUES ('{1,2,3,4,5}', '{{{0,0},{1,2}}}', '{}', '{}', '{}', '{}'); UPDATE arrtest SET e[0] = '1.1'; UPDATE arrtest SET e[1] = '2.2'; INSERT INTO arrtest (f) VALUES ('{"too long"}'); INSERT INTO arrtest (a, b[1:2][1:2], c, d, e, f, g) VALUES ('{11,12,23}', '{{3,4},{4,5}}', '{"foobar"}', '{{"elt1", "elt2"}}', '{"3.4", "6.7"}', '{"abc","abcde"}', '{"abc","abcde"}'); INSERT INTO arrtest (a, b[1:2], c, d[1:2]) VALUES ('{}', '{3,4}', '{foo,bar}', '{bar,foo}'); SELECT * FROM arrtest; SELECT arrtest.a[1], arrtest.b[1][1][1], arrtest.c[1], arrtest.d[1][1], arrtest.e[0] FROM arrtest; SELECT a[1], b[1][1][1], c[1], d[1][1], e[0] FROM arrtest; SELECT a[1:3], b[1:1][1:2][1:2], c[1:2], d[1:1][1:2] FROM arrtest; SELECT array_ndims(a) AS a, array_ndims(b) AS b, array_ndims(c) AS c FROM arrtest; SELECT array_dims(a) AS a, array_dims(b) AS b, array_dims(c) AS c FROM arrtest; -- returns nothing SELECT * FROM arrtest WHERE a[1] < 5 AND c = '{"foobar"}'::_name; UPDATE arrtest SET a[1:2] = '{16,25}' WHERE NOT a = '{}'::_int2; UPDATE arrtest SET b[1:1][1:1][1:2] = '{113, 117}', b[1:1][1:2][2:2] = '{142, 147}' WHERE array_dims(b) = '[1:1][1:2][1:2]'; UPDATE arrtest SET c[2:2] = '{"new_word"}' WHERE array_dims(c) IS NOT NULL; SELECT a, b, c FROM arrtest; SELECT a[1:3], b[1:1][1:2][1:2], c[1:2], d[1:1][2:2] FROM arrtest; SELECT b[1:1][2][2], d[1:1][2] FROM arrtest; INSERT INTO arrtest (a) VALUES ('{1,null,3}'); SELECT a FROM arrtest; UPDATE arrtest SET a[4] = NULL WHERE a[2] IS NULL; SELECT a FROM arrtest WHERE a[2] IS NULL; DELETE FROM arrtest WHERE a[2] IS NULL AND b IS NULL; SELECT a, b, c FROM arrtest; -- test mixed slice/scalar subscripting SELECT '{{1,2,3},{4,5,6},{7,8,9}}'::int[]; SELECT ('{{1,2,3},{4,5,6},{7,8,9}}'::int[])[1:2][2]; SELECT '[0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}}'::int[]; SELECT ('[0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}}'::int[])[1:2][2]; -- -- check subscription corner cases -- -- More subscripts than MAXDIMS(6) SELECT ('{}'::int[])[1][2][3][4][5][6][7]; -- NULL index yields NULL when selecting SELECT ('{{{1},{2},{3}},{{4},{5},{6}}}'::int[])[1][NULL][1]; SELECT ('{{{1},{2},{3}},{{4},{5},{6}}}'::int[])[1][NULL:1][1]; SELECT ('{{{1},{2},{3}},{{4},{5},{6}}}'::int[])[1][1:NULL][1]; -- NULL index in assignment is an error UPDATE arrtest SET c[NULL] = '{"can''t assign"}' WHERE array_dims(c) IS NOT NULL; UPDATE arrtest SET c[NULL:1] = '{"can''t assign"}' WHERE array_dims(c) IS NOT NULL; UPDATE arrtest SET c[1:NULL] = '{"can''t assign"}' WHERE array_dims(c) IS NOT NULL; -- test slices with empty lower and/or upper index CREATE TEMP TABLE arrtest_s ( a int2[], b int2[][] ); INSERT INTO arrtest_s VALUES ('{1,2,3,4,5}', '{{1,2,3}, {4,5,6}, {7,8,9}}'); INSERT INTO arrtest_s VALUES ('[0:4]={1,2,3,4,5}', '[0:2][0:2]={{1,2,3}, {4,5,6}, {7,8,9}}'); SELECT * FROM arrtest_s; SELECT a[:3], b[:2][:2] FROM arrtest_s; SELECT a[2:], b[2:][2:] FROM arrtest_s; SELECT a[:], b[:] FROM arrtest_s; -- updates UPDATE arrtest_s SET a[:3] = '{11, 12, 13}', b[:2][:2] = '{{11,12}, {14,15}}' WHERE array_lower(a, 1) = 1; SELECT * FROM arrtest_s; UPDATE arrtest_s SET a[3:] = '{23, 24, 25}', b[2:][2:] = '{{25,26}, {28,29}}'; SELECT * FROM arrtest_s; UPDATE arrtest_s SET a[:] = '{11, 12, 13, 14, 15}'; SELECT * FROM arrtest_s; UPDATE arrtest_s SET a[:] = '{23, 24, 25}'; -- fail, too small INSERT INTO arrtest_s VALUES (NULL, NULL); UPDATE arrtest_s SET a[:] = '{11, 12, 13, 14, 15}'; -- fail, no good with null -- check with fixed-length-array type, such as point SELECT f1[0:1] FROM POINT_TBL; SELECT f1[0:] FROM POINT_TBL; SELECT f1[:1] FROM POINT_TBL; SELECT f1[:] FROM POINT_TBL; -- subscript assignments to fixed-width result in NULL if previous value is NULL UPDATE point_tbl SET f1[0] = 10 WHERE f1 IS NULL RETURNING *; INSERT INTO point_tbl (f1[0]) VALUES (0) RETURNING *; -- NULL assignments get ignored UPDATE point_tbl SET f1[0] = NULL WHERE f1::text = '(10,10)'::point::text RETURNING *; -- but non-NULL subscript assignments work UPDATE point_tbl SET f1[0] = - 10, f1[1] = - 10 WHERE f1::text = '(10,10)'::point::text RETURNING *; -- but not to expand the range UPDATE point_tbl SET f1[3] = 10 WHERE f1::text = '(-10,-10)'::point::text RETURNING *; -- -- test array extension -- CREATE TEMP TABLE arrtest1 ( i int[], t text[] ); INSERT INTO arrtest1 VALUES (ARRAY[1, 2, NULL, 4], ARRAY['one', 'two', NULL, 'four']); SELECT * FROM arrtest1; UPDATE arrtest1 SET i[2] = 22, t[2] = 'twenty-two'; SELECT * FROM arrtest1; UPDATE arrtest1 SET i[5] = 5, t[5] = 'five'; SELECT * FROM arrtest1; UPDATE arrtest1 SET i[8] = 8, t[8] = 'eight'; SELECT * FROM arrtest1; UPDATE arrtest1 SET i[0] = 0, t[0] = 'zero'; SELECT * FROM arrtest1; UPDATE arrtest1 SET i[- 3] = - 3, t[- 3] = 'minus-three'; SELECT * FROM arrtest1; UPDATE arrtest1 SET i[0:2] = ARRAY[10, 11, 12], t[0:2] = ARRAY['ten', 'eleven', 'twelve']; SELECT * FROM arrtest1; UPDATE arrtest1 SET i[8:10] = ARRAY[18, NULL, 20], t[8:10] = ARRAY['p18', NULL, 'p20']; SELECT * FROM arrtest1; UPDATE arrtest1 SET i[11:12] = ARRAY[NULL, 22], t[11:12] = ARRAY[NULL, 'p22']; SELECT * FROM arrtest1; UPDATE arrtest1 SET i[15:16] = ARRAY[NULL, 26], t[15:16] = ARRAY[NULL, 'p26']; SELECT * FROM arrtest1; UPDATE arrtest1 SET i[- 5: - 3] = ARRAY[- 15, - 14, - 13], t[- 5: - 3] = ARRAY['m15', 'm14', 'm13']; SELECT * FROM arrtest1; UPDATE arrtest1 SET i[- 7: - 6] = ARRAY[- 17, NULL], t[- 7: - 6] = ARRAY['m17', NULL]; SELECT * FROM arrtest1; UPDATE arrtest1 SET i[- 12: - 10] = ARRAY[- 22, NULL, - 20], t[- 12: - 10] = ARRAY['m22', NULL, 'm20']; SELECT * FROM arrtest1; DELETE FROM arrtest1; INSERT INTO arrtest1 VALUES (ARRAY[1, 2, NULL, 4], ARRAY['one', 'two', NULL, 'four']); SELECT * FROM arrtest1; UPDATE arrtest1 SET i[0:5] = ARRAY[0, 1, 2, NULL, 4, 5], t[0:5] = ARRAY['z', 'p1', 'p2', NULL, 'p4', 'p5']; SELECT * FROM arrtest1; -- -- array expressions and operators -- -- table creation and INSERTs CREATE TEMP TABLE arrtest2 ( i integer ARRAY[4], f float8[], n numeric[], t text[], d timestamp[] ); INSERT INTO arrtest2 VALUES (ARRAY[[[113, 142],[1, 147]]], ARRAY[1.1, 1.2, 1.3]::float8[], ARRAY[1.1, 1.2, 1.3], ARRAY[[['aaa', 'aab'],['aba', 'abb'],['aca', 'acb']],[['baa', 'bab'],['bba', 'bbb'],['bca', 'bcb']]], ARRAY['19620326', '19931223', '19970117']::timestamp[]); -- some more test data CREATE TEMP TABLE arrtest_f ( f0 int, f1 text, f2 float8 ); INSERT INTO arrtest_f VALUES (1, 'cat1', 1.21); INSERT INTO arrtest_f VALUES (2, 'cat1', 1.24); INSERT INTO arrtest_f VALUES (3, 'cat1', 1.18); INSERT INTO arrtest_f VALUES (4, 'cat1', 1.26); INSERT INTO arrtest_f VALUES (5, 'cat1', 1.15); INSERT INTO arrtest_f VALUES (6, 'cat2', 1.15); INSERT INTO arrtest_f VALUES (7, 'cat2', 1.26); INSERT INTO arrtest_f VALUES (8, 'cat2', 1.32); INSERT INTO arrtest_f VALUES (9, 'cat2', 1.30); CREATE TEMP TABLE arrtest_i ( f0 int, f1 text, f2 int ); INSERT INTO arrtest_i VALUES (1, 'cat1', 21); INSERT INTO arrtest_i VALUES (2, 'cat1', 24); INSERT INTO arrtest_i VALUES (3, 'cat1', 18); INSERT INTO arrtest_i VALUES (4, 'cat1', 26); INSERT INTO arrtest_i VALUES (5, 'cat1', 15); INSERT INTO arrtest_i VALUES (6, 'cat2', 15); INSERT INTO arrtest_i VALUES (7, 'cat2', 26); INSERT INTO arrtest_i VALUES (8, 'cat2', 32); INSERT INTO arrtest_i VALUES (9, 'cat2', 30); -- expressions SELECT t.f[1][3][1] AS "131", t.f[2][2][1] AS "221" FROM ( SELECT ARRAY[[[111, 112],[121, 122],[131, 132]],[[211, 212],[221, 122],[231, 232]]] AS f) AS t; SELECT ARRAY[[[[[['hello'],['world']]]]]]; SELECT ARRAY[ARRAY['hello'], ARRAY['world']]; SELECT ARRAY ( SELECT f2 FROM arrtest_f ORDER BY f2) AS "ARRAY"; -- with nulls SELECT '{1,null,3}'::int[]; SELECT ARRAY[1, NULL, 3]; -- functions SELECT array_append(ARRAY[42], 6) AS "{42,6}"; SELECT array_prepend(6, ARRAY[42]) AS "{6,42}"; SELECT array_cat(ARRAY[1, 2], ARRAY[3, 4]) AS "{1,2,3,4}"; SELECT array_cat(ARRAY[1, 2], ARRAY[[3, 4],[5, 6]]) AS "{{1,2},{3,4},{5,6}}"; SELECT array_cat(ARRAY[[3, 4],[5, 6]], ARRAY[1, 2]) AS "{{3,4},{5,6},{1,2}}"; SELECT array_position(ARRAY[1, 2, 3, 4, 5], 4); SELECT array_position(ARRAY[5, 3, 4, 2, 1], 4); SELECT array_position(ARRAY[[1, 2],[3, 4]], 3); SELECT array_position(ARRAY['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'], 'mon'); SELECT array_position(ARRAY['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'], 'sat'); SELECT array_position(ARRAY['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'], NULL); SELECT array_position(ARRAY['sun', 'mon', 'tue', 'wed', 'thu', NULL, 'fri', 'sat'], NULL); SELECT array_position(ARRAY['sun', 'mon', 'tue', 'wed', 'thu', NULL, 'fri', 'sat'], 'sat'); SELECT array_positions(NULL, 10); SELECT array_positions(NULL, NULL::int); SELECT array_positions(ARRAY[1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6], 4); SELECT array_positions(ARRAY[[1, 2],[3, 4]], 4); SELECT array_positions(ARRAY[1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6], NULL); SELECT array_positions(ARRAY[1, 2, 3, NULL, 5, 6, 1, 2, 3, NULL, 5, 6], NULL); SELECT array_length(array_positions(ARRAY ( SELECT 'AAAAAAAAAAAAAAAAAAAAAAAAA'::text || i % 10 FROM generate_series(1, 100) g (i)), 'AAAAAAAAAAAAAAAAAAAAAAAAA5'), 1); DO $$ DECLARE o int; a int[] := ARRAY[1, 2, 3, 2, 3, 1, 2]; BEGIN o := array_position(a, 2); WHILE o IS NOT NULL LOOP RAISE NOTICE '%', o; o := array_position(a, 2, o + 1); END LOOP; END $$ LANGUAGE plpgsql; SELECT array_position('[2:4]={1,2,3}'::int[], 1); SELECT array_positions('[2:4]={1,2,3}'::int[], 1); SELECT array_position(ids, (1, 1)), array_positions(ids, (1, 1)) FROM ( VALUES (ARRAY[(0, 0), (1, 1)]), (ARRAY[(1, 1)])) AS f (ids); -- operators SELECT a FROM arrtest WHERE b = ARRAY[[[113, 142],[1, 147]]]; SELECT NOT ARRAY[1.1, 1.2, 1.3] = ARRAY[1.1, 1.2, 1.3] AS "FALSE"; SELECT ARRAY[1, 2] || 3 AS "{1,2,3}"; SELECT 0 || ARRAY[1, 2] AS "{0,1,2}"; SELECT ARRAY[1, 2] || ARRAY[3, 4] AS "{1,2,3,4}"; SELECT ARRAY[[['hello', 'world']]] || ARRAY[[['happy', 'birthday']]] AS "ARRAY"; SELECT ARRAY[[1, 2],[3, 4]] || ARRAY[5, 6] AS "{{1,2},{3,4},{5,6}}"; SELECT ARRAY[0, 0] || ARRAY[1, 1] || ARRAY[2, 2] AS "{0,0,1,1,2,2}"; SELECT 0 || ARRAY[1, 2] || 3 AS "{0,1,2,3}"; SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i @> '{32,17}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i && '{32,17}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i <@ '{38,34,32,89}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i = '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i @> '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i && '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i <@ '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t && '{AAAAAAAAAA646}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t <@ '{AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t = '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t @> '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t && '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t <@ '{}' ORDER BY seqno; -- array casts SELECT ARRAY[1, 2, 3]::text[]::int[]::float8[] AS "{1,2,3}"; SELECT ARRAY[1, 2, 3]::text[]::int[]::float8[] IS OF (float8[]) AS "TRUE"; SELECT ARRAY[['a', 'bc'],['def', 'hijk']]::text[]::varchar[] AS "{{a,bc},{def,hijk}}"; SELECT ARRAY[['a', 'bc'],['def', 'hijk']]::text[]::varchar[] IS OF (varchar[]) AS "TRUE"; SELECT CAST(ARRAY[[[[[['a', 'bb', 'ccc']]]]]] AS text[]) AS "{{{{{{a,bb,ccc}}}}}}"; SELECT NULL::text[]::int[] AS "NULL"; -- scalar op any/all (array) SELECT 33 = ANY ('{1,2,3}'); SELECT 33 = ANY ('{1,2,33}'); SELECT 33 = ALL ('{1,2,33}'); SELECT 33 >= ALL ('{1,2,33}'); -- boundary cases SELECT NULL::int >= ALL ('{1,2,33}'); SELECT NULL::int >= ALL ('{}'); SELECT NULL::int >= ANY ('{}'); -- cross-datatype SELECT 33.4 = ANY (ARRAY[1, 2, 3]); SELECT 33.4 > ALL (ARRAY[1, 2, 3]); -- errors SELECT 33 * ANY ('{1,2,3}'); SELECT 33 * ANY (44); -- nulls SELECT 33 = ANY (NULL::int[]); SELECT NULL::int = ANY ('{1,2,3}'); SELECT 33 = ANY ('{1,null,3}'); SELECT 33 = ANY ('{1,null,33}'); SELECT 33 = ALL (NULL::int[]); SELECT NULL::int = ALL ('{1,2,3}'); SELECT 33 = ALL ('{1,null,3}'); SELECT 33 = ALL ('{33,null,33}'); -- nulls later in the bitmap SELECT - 1 != ALL (ARRAY ( SELECT NULLIF (g.i, 900) FROM generate_series(1, 1000) g (i))); -- test indexes on arrays CREATE temp TABLE arr_tbl ( f1 int[] UNIQUE ); INSERT INTO arr_tbl VALUES ('{1,2,3}'); INSERT INTO arr_tbl VALUES ('{1,2}'); -- failure expected: INSERT INTO arr_tbl VALUES ('{1,2,3}'); INSERT INTO arr_tbl VALUES ('{2,3,4}'); INSERT INTO arr_tbl VALUES ('{1,5,3}'); INSERT INTO arr_tbl VALUES ('{1,2,10}'); SET enable_seqscan TO OFF; SET enable_bitmapscan TO OFF; SELECT * FROM arr_tbl WHERE f1 > '{1,2,3}' AND f1 <= '{1,5,3}'; SELECT * FROM arr_tbl WHERE f1 >= '{1,2,3}' AND f1 < '{1,5,3}'; -- test ON CONFLICT DO UPDATE with arrays CREATE temp TABLE arr_pk_tbl ( pk int4 PRIMARY KEY, f1 int[] ); INSERT INTO arr_pk_tbl VALUES (1, '{1,2,3}'); INSERT INTO arr_pk_tbl VALUES (1, '{3,4,5}') ON CONFLICT (pk) DO UPDATE SET f1[1] = excluded.f1[1], f1[3] = excluded.f1[3] RETURNING pk, f1; INSERT INTO arr_pk_tbl (pk, f1[1:2]) VALUES (1, '{6,7,8}') ON CONFLICT (pk) DO UPDATE SET f1[1] = excluded.f1[1], f1[2] = excluded.f1[2], f1[3] = excluded.f1[3] RETURNING pk, f1; -- note: if above selects don't produce the expected tuple order, -- then you didn't get an indexscan plan, and something is busted. RESET enable_seqscan; RESET enable_bitmapscan; -- test [not] (like|ilike) (any|all) (...) SELECT 'foo' LIKE ANY (ARRAY['%a', '%o']); -- t SELECT 'foo' LIKE ANY (ARRAY['%a', '%b']); -- f SELECT 'foo' LIKE ALL (ARRAY['f%', '%o']); -- t SELECT 'foo' LIKE ALL (ARRAY['f%', '%b']); -- f SELECT 'foo' NOT LIKE ANY (ARRAY['%a', '%b']); -- t SELECT 'foo' NOT LIKE ALL (ARRAY['%a', '%o']); -- f SELECT 'foo' ILIKE ANY (ARRAY['%A', '%O']); -- t SELECT 'foo' ILIKE ALL (ARRAY['F%', '%O']); -- t -- -- General array parser tests -- -- none of the following should be accepted SELECT '{{1,{2}},{2,3}}'::text[]; SELECT '{{},{}}'::text[]; SELECT E'{{1,2},\\{2,3}}'::text[]; SELECT '{{"1 2" x},{3}}'::text[]; SELECT '{}}'::text[]; SELECT '{ }}'::text[]; SELECT ARRAY[]; -- none of the above should be accepted -- all of the following should be accepted SELECT '{}'::text[]; SELECT '{{{1,2,3,4},{2,3,4,5}},{{3,4,5,6},{4,5,6,7}}}'::text[]; SELECT '{0 second ,0 second}'::interval[]; SELECT '{ { "," } , { 3 } }'::text[]; SELECT ' { { " 0 second " , 0 second } }'::text[]; SELECT '{ 0 second, @ 1 hour @ 42 minutes @ 20 seconds }'::interval[]; SELECT ARRAY[]::text[]; SELECT '[0:1]={1.1,2.2}'::float8[]; -- all of the above should be accepted -- tests for array aggregates CREATE TEMP TABLE arraggtest ( f1 int[], f2 text[][], f3 FLOAT[] ); INSERT INTO arraggtest (f1, f2, f3) VALUES ('{1,2,3,4}', '{{grey,red},{blue,blue}}', '{1.6, 0.0}'); INSERT INTO arraggtest (f1, f2, f3) VALUES ('{1,2,3}', '{{grey,red},{grey,blue}}', '{1.6}'); SELECT max(f1), min(f1), max(f2), min(f2), max(f3), min(f3) FROM arraggtest; INSERT INTO arraggtest (f1, f2, f3) VALUES ('{3,3,2,4,5,6}', '{{white,yellow},{pink,orange}}', '{2.1,3.3,1.8,1.7,1.6}'); SELECT max(f1), min(f1), max(f2), min(f2), max(f3), min(f3) FROM arraggtest; INSERT INTO arraggtest (f1, f2, f3) VALUES ('{2}', '{{black,red},{green,orange}}', '{1.6,2.2,2.6,0.4}'); SELECT max(f1), min(f1), max(f2), min(f2), max(f3), min(f3) FROM arraggtest; INSERT INTO arraggtest (f1, f2, f3) VALUES ('{4,2,6,7,8,1}', '{{red},{black},{purple},{blue},{blue}}', NULL); SELECT max(f1), min(f1), max(f2), min(f2), max(f3), min(f3) FROM arraggtest; INSERT INTO arraggtest (f1, f2, f3) VALUES ('{}', '{{pink,white,blue,red,grey,orange}}', '{2.1,1.87,1.4,2.2}'); SELECT max(f1), min(f1), max(f2), min(f2), max(f3), min(f3) FROM arraggtest; -- A few simple tests for arrays of composite types CREATE TYPE comptype AS ( f1 int, f2 text ); CREATE TABLE comptable ( c1 comptype, c2 comptype[] ); -- XXX would like to not have to specify row() construct types here ... INSERT INTO comptable VALUES (ROW (1, 'foo'), ARRAY[ROW (2, 'bar')::comptype, ROW (3, 'baz')::comptype]); -- check that implicitly named array type _comptype isn't a problem CREATE TYPE _comptype AS enum ( 'fooey' ); SELECT * FROM comptable; SELECT c2[2].f2 FROM comptable; DROP TYPE _comptype; DROP TABLE comptable; DROP TYPE comptype; CREATE OR REPLACE FUNCTION unnest1 (anyarray) RETURNS SETOF anyelement AS $$ SELECT $1[s] FROM generate_subscripts($1, 1) g (s); $$ LANGUAGE sql IMMUTABLE; CREATE OR REPLACE FUNCTION unnest2 (anyarray) RETURNS SETOF anyelement AS $$ SELECT $1[s1][s2] FROM generate_subscripts($1, 1) g1 (s1), generate_subscripts($1, 2) g2 (s2); $$ LANGUAGE sql IMMUTABLE; SELECT * FROM unnest1 (ARRAY[1, 2, 3]); SELECT * FROM unnest2 (ARRAY[[1, 2, 3],[4, 5, 6]]); DROP FUNCTION unnest1 (anyarray); DROP FUNCTION unnest2 (anyarray); SELECT array_fill(NULL::integer, ARRAY[3, 3], ARRAY[2, 2]); SELECT array_fill(NULL::integer, ARRAY[3, 3]); SELECT array_fill(NULL::text, ARRAY[3, 3], ARRAY[2, 2]); SELECT array_fill(NULL::text, ARRAY[3, 3]); SELECT array_fill(7, ARRAY[3, 3], ARRAY[2, 2]); SELECT array_fill(7, ARRAY[3, 3]); SELECT array_fill('juhu'::text, ARRAY[3, 3], ARRAY[2, 2]); SELECT array_fill('juhu'::text, ARRAY[3, 3]); SELECT a, a = '{}' AS is_eq, array_dims(a) FROM ( SELECT array_fill(42, ARRAY[0]) AS a) ss; SELECT a, a = '{}' AS is_eq, array_dims(a) FROM ( SELECT array_fill(42, '{}') AS a) ss; SELECT a, a = '{}' AS is_eq, array_dims(a) FROM ( SELECT array_fill(42, '{}', '{}') AS a) ss; -- raise exception SELECT array_fill(1, NULL, ARRAY[2, 2]); SELECT array_fill(1, ARRAY[2, 2], NULL); SELECT array_fill(1, ARRAY[2, 2], '{}'); SELECT array_fill(1, ARRAY[3, 3], ARRAY[1, 1, 1]); SELECT array_fill(1, ARRAY[1, 2, NULL]); SELECT array_fill(1, ARRAY[[1, 2],[3, 4]]); SELECT string_to_array('1|2|3', '|'); SELECT string_to_array('1|2|3|', '|'); SELECT string_to_array('1||2|3||', '||'); SELECT string_to_array('1|2|3', ''); SELECT string_to_array('', '|'); SELECT string_to_array('1|2|3', NULL); SELECT string_to_array(NULL, '|') IS NULL; SELECT string_to_array('abc', ''); SELECT string_to_array('abc', '', 'abc'); SELECT string_to_array('abc', ','); SELECT string_to_array('abc', ',', 'abc'); SELECT string_to_array('1,2,3,4,,6', ','); SELECT string_to_array('1,2,3,4,,6', ',', ''); SELECT string_to_array('1,2,3,4,*,6', ',', '*'); SELECT array_to_string(NULL::int4[], ',') IS NULL; SELECT array_to_string('{}'::int4[], ','); SELECT array_to_string(ARRAY[1, 2, 3, 4, NULL, 6], ','); SELECT array_to_string(ARRAY[1, 2, 3, 4, NULL, 6], ',', '*'); SELECT array_to_string(ARRAY[1, 2, 3, 4, NULL, 6], NULL); SELECT array_to_string(ARRAY[1, 2, 3, 4, NULL, 6], ',', NULL); SELECT array_to_string(string_to_array('1|2|3', '|'), '|'); SELECT array_length(ARRAY[1, 2, 3], 1); SELECT array_length(ARRAY[[1, 2, 3],[4, 5, 6]], 0); SELECT array_length(ARRAY[[1, 2, 3],[4, 5, 6]], 1); SELECT array_length(ARRAY[[1, 2, 3],[4, 5, 6]], 2); SELECT array_length(ARRAY[[1, 2, 3],[4, 5, 6]], 3); SELECT cardinality(NULL::int[]); SELECT cardinality('{}'::int[]); SELECT cardinality(ARRAY[1, 2, 3]); SELECT cardinality('[2:4]={5,6,7}'::int[]); SELECT cardinality('{{1,2}}'::int[]); SELECT cardinality('{{1,2},{3,4},{5,6}}'::int[]); SELECT cardinality('{{{1,9},{5,6}},{{2,3},{3,4}}}'::int[]); -- array_agg(anynonarray) SELECT array_agg(unique1) FROM ( SELECT unique1 FROM tenk1 WHERE unique1 < 15 ORDER BY unique1) ss; SELECT array_agg(ten) FROM ( SELECT ten FROM tenk1 WHERE unique1 < 15 ORDER BY unique1) ss; SELECT array_agg(nullif (ten, 4)) FROM ( SELECT ten FROM tenk1 WHERE unique1 < 15 ORDER BY unique1) ss; SELECT array_agg(unique1) FROM tenk1 WHERE unique1 < - 15; -- array_agg(anyarray) SELECT array_agg(ar) FROM ( VALUES ('{1,2}'::int[]), ('{3,4}'::int[])) v (ar); SELECT array_agg(DISTINCT ar ORDER BY ar DESC) FROM ( SELECT ARRAY[i / 2] FROM generate_series(1, 10) a (i)) b (ar); SELECT array_agg(ar) FROM ( SELECT array_agg(ARRAY[i, i + 1, i - 1]) FROM generate_series(1, 2) a (i)) b (ar); SELECT array_agg(ARRAY[i + 1.2, i + 1.3, i + 1.4]) FROM generate_series(1, 3) g (i); SELECT array_agg(ARRAY['Hello', i::text]) FROM generate_series(9, 11) g (i); SELECT array_agg(ARRAY[i, nullif (i, 3), i + 1]) FROM generate_series(1, 4) g (i); -- errors SELECT array_agg('{}'::int[]) FROM generate_series(1, 2); SELECT array_agg(NULL::int[]) FROM generate_series(1, 2); SELECT array_agg(ar) FROM ( VALUES ('{1,2}'::int[]), ('{3}'::int[])) v (ar); SELECT unnest(ARRAY[1, 2, 3]); SELECT * FROM unnest(ARRAY[1, 2, 3]); SELECT unnest(ARRAY[1, 2, 3, 4.5]::float8[]); SELECT unnest(ARRAY[1, 2, 3, 4.5]::numeric[]); SELECT unnest(ARRAY[1, 2, 3, NULL, 4, NULL, NULL, 5, 6]); SELECT unnest(ARRAY[1, 2, 3, NULL, 4, NULL, NULL, 5, 6]::text[]); SELECT abs(unnest(ARRAY[1, 2, NULL, - 3])); SELECT array_remove(ARRAY[1, 2, 2, 3], 2); SELECT array_remove(ARRAY[1, 2, 2, 3], 5); SELECT array_remove(ARRAY[1, NULL, NULL, 3], NULL); SELECT array_remove(ARRAY['A', 'CC', 'D', 'C', 'RR'], 'RR'); SELECT array_remove('{{1,2,2},{1,4,3}}', 2); -- not allowed SELECT array_remove(ARRAY['X', 'X', 'X'], 'X') = '{}'; SELECT array_replace(ARRAY[1, 2, 5, 4], 5, 3); SELECT array_replace(ARRAY[1, 2, 5, 4], 5, NULL); SELECT array_replace(ARRAY[1, 2, NULL, 4, NULL], NULL, 5); SELECT array_replace(ARRAY['A', 'B', 'DD', 'B'], 'B', 'CC'); SELECT array_replace(ARRAY[1, NULL, 3], NULL, NULL); SELECT array_replace(ARRAY['AB', NULL, 'CDE'], NULL, '12'); -- array(select array-value ...) SELECT ARRAY ( SELECT ARRAY[i, i / 2] FROM generate_series(1, 5) i); SELECT ARRAY ( SELECT ARRAY['Hello', i::text] FROM generate_series(9, 11) i); -- Insert/update on a column that is array of composite CREATE temp TABLE t1 ( f1 int8_tbl[] ); INSERT INTO t1 (f1[5].q1) VALUES (42); SELECT * FROM t1; UPDATE t1 SET f1[5].q2 = 43; SELECT * FROM t1; -- Check that arrays of composites are safely detoasted when needed CREATE temp TABLE src ( f1 text ); INSERT INTO src SELECT string_agg(random()::text, '') FROM generate_series(1, 10000); CREATE TYPE textandtext AS ( c1 text, c2 text ); CREATE temp TABLE dest ( f1 textandtext[] ); INSERT INTO dest SELECT ARRAY[ROW (f1, f1)::textandtext] FROM src; SELECT length(md5((f1[1]).c2)) FROM dest; DELETE FROM src; SELECT length(md5((f1[1]).c2)) FROM dest; TRUNCATE TABLE src; DROP TABLE src; SELECT length(md5((f1[1]).c2)) FROM dest; DROP TABLE dest; DROP TYPE textandtext; -- Tests for polymorphic-array form of width_bucket() -- this exercises the varwidth and float8 code paths SELECT op, width_bucket(op::numeric, ARRAY[1, 3, 5, 10.0]::numeric[]) AS wb_n1, width_bucket(op::numeric, ARRAY[0, 5.5, 9.99]::numeric[]) AS wb_n2, width_bucket(op::numeric, ARRAY[- 6, - 5, 2.0]::numeric[]) AS wb_n3, width_bucket(op::float8, ARRAY[1, 3, 5, 10.0]::float8[]) AS wb_f1, width_bucket(op::float8, ARRAY[0, 5.5, 9.99]::float8[]) AS wb_f2, width_bucket(op::float8, ARRAY[- 6, - 5, 2.0]::float8[]) AS wb_f3 FROM ( VALUES (- 5.2), (- 0.0000000001), (0.000000000001), (1), (1.99999999999999), (2), (2.00000000000001), (3), (4), (4.5), (5), (5.5), (6), (7), (8), (9), (9.99999999999999), (10), (10.0000000000001)) v (op); -- ensure float8 path handles NaN properly SELECT op, width_bucket(op, ARRAY[1, 3, 9, 'NaN', 'NaN']::float8[]) AS wb FROM ( VALUES (- 5.2::float8), (4::float8), (77::float8), ('NaN'::float8)) v (op); -- these exercise the generic fixed-width code path SELECT op, width_bucket(op, ARRAY[1, 3, 5, 10]) AS wb_1 FROM generate_series(0, 11) AS op; SELECT width_bucket(now(), ARRAY['yesterday', 'today', 'tomorrow']::timestamptz[]); -- corner cases SELECT width_bucket(5, ARRAY[3]); SELECT width_bucket(5, '{}'); -- error cases SELECT width_bucket('5'::text, ARRAY[3, 4]::integer[]); SELECT width_bucket(5, ARRAY[3, 4, NULL]); SELECT width_bucket(5, ARRAY[ARRAY[1, 2], ARRAY[3, 4]]); pgFormatter-4.2/t/pg-test-files/expected/async.sql000066400000000000000000000014531361326045100222420ustar00rootroot00000000000000-- -- ASYNC -- --Should work. Send a valid message via a valid channel name SELECT pg_notify('notify_async1', 'sample message1'); SELECT pg_notify('notify_async1', ''); SELECT pg_notify('notify_async1', NULL); -- Should fail. Send a valid message via an invalid channel name SELECT pg_notify('', 'sample message1'); SELECT pg_notify(NULL, 'sample message1'); SELECT pg_notify('notify_async_channel_name_too_long______________________________', 'sample_message1'); --Should work. Valid NOTIFY/LISTEN/UNLISTEN commands NOTIFY notify_async2; LISTEN notify_async2; UNLISTEN notify_async2; UNLISTEN *; -- Should return zero while there are no pending notifications. -- src/test/isolation/specs/async-notify.spec tests for actual usage. SELECT pg_notification_queue_usage (); pgFormatter-4.2/t/pg-test-files/expected/bit.sql000066400000000000000000000160101361326045100216760ustar00rootroot00000000000000-- -- BIT types -- -- -- Build tables for testing -- CREATE TABLE BIT_TABLE ( b bit(11) ); INSERT INTO BIT_TABLE VALUES (B '10'); -- too short INSERT INTO BIT_TABLE VALUES (B '00000000000'); INSERT INTO BIT_TABLE VALUES (B '11011000000'); INSERT INTO BIT_TABLE VALUES (B '01010101010'); INSERT INTO BIT_TABLE VALUES (B '101011111010'); -- too long --INSERT INTO BIT_TABLE VALUES ('X554'); --INSERT INTO BIT_TABLE VALUES ('X555'); SELECT * FROM BIT_TABLE; CREATE TABLE VARBIT_TABLE ( v bit varying(11) ); INSERT INTO VARBIT_TABLE VALUES (B ''); INSERT INTO VARBIT_TABLE VALUES (B '0'); INSERT INTO VARBIT_TABLE VALUES (B '010101'); INSERT INTO VARBIT_TABLE VALUES (B '01010101010'); INSERT INTO VARBIT_TABLE VALUES (B '101011111010'); -- too long --INSERT INTO VARBIT_TABLE VALUES ('X554'); --INSERT INTO VARBIT_TABLE VALUES ('X555'); SELECT * FROM VARBIT_TABLE; -- Concatenation SELECT v, b, (v || b) AS || FROM BIT_TABLE, VARBIT_TABLE ORDER BY 3; -- Length SELECT b, length(b) AS lb FROM BIT_TABLE; SELECT v, length(v) AS lv FROM VARBIT_TABLE; -- Substring SELECT b, SUBSTRING(b FROM 2 FOR 4) AS sub_2_4, SUBSTRING(b FROM 7 FOR 13) AS sub_7_13, SUBSTRING(b FROM 6) AS sub_6 FROM BIT_TABLE; SELECT v, SUBSTRING(v FROM 2 FOR 4) AS sub_2_4, SUBSTRING(v FROM 7 FOR 13) AS sub_7_13, SUBSTRING(v FROM 6) AS sub_6 FROM VARBIT_TABLE; --- Bit operations DROP TABLE varbit_table; CREATE TABLE varbit_table ( a bit varying(16), b bit varying(16) ); SELECT a, b, ~ a AS "~ a", a & b AS "a & b", a | b AS "a | b", a # b AS "a # b" FROM varbit_table; SELECT a, b, a < b AS "a= b AS "a>=b", a > b AS "a>b", a <> b AS "a<>b" FROM varbit_table; SELECT a, a << 4 AS "a<<4", b, b >> 2 AS "b>>2" FROM varbit_table; DROP TABLE varbit_table; --- Bit operations DROP TABLE bit_table; CREATE TABLE bit_table ( a bit(16), b bit(16) ); SELECT a, b, ~ a AS "~ a", a & b AS "a & b", a | b AS "a | b", a # b AS "a # b" FROM bit_table; SELECT a, b, a < b AS "a= b AS "a>=b", a > b AS "a>b", a <> b AS "a<>b" FROM bit_table; SELECT a, a << 4 AS "a<<4", b, b >> 2 AS "b>>2" FROM bit_table; DROP TABLE bit_table; -- The following should fail SELECT B '001' & B '10'; SELECT B '0111' | B '011'; SELECT B '0010' # B '011101'; -- More position tests, checking all the boundary cases SELECT POSITION(B '1010' IN B '0000101'); -- 0 SELECT POSITION(B '1010' IN B '00001010'); -- 5 SELECT POSITION(B '1010' IN B '00000101'); -- 0 SELECT POSITION(B '1010' IN B '000001010'); -- 6 SELECT POSITION(B '' IN B '00001010'); -- 1 SELECT POSITION(B '0' IN B ''); -- 0 SELECT POSITION(B '' IN B ''); -- 0 SELECT POSITION(B '101101' IN B '001011011011011000'); -- 3 SELECT POSITION(B '10110110' IN B '001011011011010'); -- 3 SELECT POSITION(B '1011011011011' IN B '001011011011011'); -- 3 SELECT POSITION(B '1011011011011' IN B '00001011011011011'); -- 5 SELECT POSITION(B '11101011' IN B '11101011'); -- 1 SELECT POSITION(B '11101011' IN B '011101011'); -- 2 SELECT POSITION(B '11101011' IN B '00011101011'); -- 4 SELECT POSITION(B '11101011' IN B '0000011101011'); -- 6 SELECT POSITION(B '111010110' IN B '111010110'); -- 1 SELECT POSITION(B '111010110' IN B '0111010110'); -- 2 SELECT POSITION(B '111010110' IN B '000111010110'); -- 4 SELECT POSITION(B '111010110' IN B '00000111010110'); -- 6 SELECT POSITION(B '111010110' IN B '11101011'); -- 0 SELECT POSITION(B '111010110' IN B '011101011'); -- 0 SELECT POSITION(B '111010110' IN B '00011101011'); -- 0 SELECT POSITION(B '111010110' IN B '0000011101011'); -- 0 SELECT POSITION(B '111010110' IN B '111010110'); -- 1 SELECT POSITION(B '111010110' IN B '0111010110'); -- 2 SELECT POSITION(B '111010110' IN B '000111010110'); -- 4 SELECT POSITION(B '111010110' IN B '00000111010110'); -- 6 SELECT POSITION(B '111010110' IN B '000001110101111101011'); -- 0 SELECT POSITION(B '111010110' IN B '0000001110101111101011'); -- 0 SELECT POSITION(B '111010110' IN B '000000001110101111101011'); -- 0 SELECT POSITION(B '111010110' IN B '00000000001110101111101011'); -- 0 SELECT POSITION(B '111010110' IN B '0000011101011111010110'); -- 14 SELECT POSITION(B '111010110' IN B '00000011101011111010110'); -- 15 SELECT POSITION(B '111010110' IN B '0000000011101011111010110'); -- 17 SELECT POSITION(B '111010110' IN B '000000000011101011111010110'); -- 19 SELECT POSITION(B '000000000011101011111010110' IN B '000000000011101011111010110'); -- 1 SELECT POSITION(B '00000000011101011111010110' IN B '000000000011101011111010110'); -- 2 SELECT POSITION(B '0000000000011101011111010110' IN B '000000000011101011111010110'); -- 0 -- Shifting CREATE TABLE BIT_SHIFT_TABLE ( b bit(16) ); INSERT INTO BIT_SHIFT_TABLE VALUES (B '1101100000000000'); INSERT INTO BIT_SHIFT_TABLE SELECT b >> 1 FROM BIT_SHIFT_TABLE; INSERT INTO BIT_SHIFT_TABLE SELECT b >> 2 FROM BIT_SHIFT_TABLE; INSERT INTO BIT_SHIFT_TABLE SELECT b >> 4 FROM BIT_SHIFT_TABLE; INSERT INTO BIT_SHIFT_TABLE SELECT b >> 8 FROM BIT_SHIFT_TABLE; SELECT POSITION(B '1101' IN b), POSITION(B '11011' IN b), b FROM BIT_SHIFT_TABLE; CREATE TABLE VARBIT_SHIFT_TABLE ( v bit varying(20) ); INSERT INTO VARBIT_SHIFT_TABLE VALUES (B '11011'); INSERT INTO VARBIT_SHIFT_TABLE SELECT CAST(v || B '0' AS bit varying(6)) >> 1 FROM VARBIT_SHIFT_TABLE; INSERT INTO VARBIT_SHIFT_TABLE SELECT CAST(v || B '00' AS bit varying(8)) >> 2 FROM VARBIT_SHIFT_TABLE; INSERT INTO VARBIT_SHIFT_TABLE SELECT CAST(v || B '0000' AS bit varying(12)) >> 4 FROM VARBIT_SHIFT_TABLE; INSERT INTO VARBIT_SHIFT_TABLE SELECT CAST(v || B '00000000' AS bit varying(20)) >> 8 FROM VARBIT_SHIFT_TABLE; SELECT POSITION(B '1101' IN v), POSITION(B '11011' IN v), v FROM VARBIT_SHIFT_TABLE; DROP TABLE BIT_SHIFT_TABLE; DROP TABLE VARBIT_SHIFT_TABLE; -- Get/Set bit SELECT get_bit(B '0101011000100', 10); SELECT set_bit(B '0101011000100100', 15, 1); SELECT set_bit(B '0101011000100100', 16, 1); -- fail -- Overlay SELECT overlay(B '0101011100' PLACING '001' FROM 2 FOR 3); SELECT overlay(B '0101011100' PLACING '101' FROM 6); SELECT overlay(B '0101011100' PLACING '001' FROM 11); SELECT overlay(B '0101011100' PLACING '001' FROM 20); -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults ( b1 bit(4) DEFAULT '1001', b2 bit(4) DEFAULT B '0101', b3 bit varying(5) DEFAULT '1001', b4 bit varying(5) DEFAULT B '0101' ); \d bit_defaults INSERT INTO bit_defaults DEFAULT VALUES; TABLE bit_defaults; pgFormatter-4.2/t/pg-test-files/expected/bitmapops.sql000066400000000000000000000026311361326045100231220ustar00rootroot00000000000000-- Test bitmap AND and OR -- Generate enough data that we can test the lossy bitmaps. -- There's 55 tuples per page in the table. 53 is just -- below 55, so that an index scan with qual a = constant -- will return at least one hit per page. 59 is just above -- 55, so that an index scan with qual b = constant will return -- hits on most but not all pages. 53 and 59 are prime, so that -- there's a maximum number of a,b combinations in the table. -- That allows us to test all the different combinations of -- lossy and non-lossy pages with the minimum amount of data CREATE TABLE bmscantest ( a int, b int, t text ); INSERT INTO bmscantest SELECT (r % 53), (r % 59), 'foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo' FROM generate_series(1, 70000) r; CREATE INDEX i_bmtest_a ON bmscantest (a); CREATE INDEX i_bmtest_b ON bmscantest (b); -- We want to use bitmapscans. With default settings, the planner currently -- chooses a bitmap scan for the queries below anyway, but let's make sure. SET enable_indexscan = FALSE; SET enable_seqscan = FALSE; -- Lower work_mem to trigger use of lossy bitmaps SET work_mem = 64; -- Test bitmap-and. SELECT count(*) FROM bmscantest WHERE a = 1 AND b = 1; -- Test bitmap-or. SELECT count(*) FROM bmscantest WHERE a = 1 OR b = 1; -- clean up DROP TABLE bmscantest; pgFormatter-4.2/t/pg-test-files/expected/boolean.sql000066400000000000000000000144511361326045100225460ustar00rootroot00000000000000-- -- BOOLEAN -- -- -- sanity check - if this fails go insane! -- SELECT 1 AS one; -- ******************testing built-in type bool******************** -- check bool input syntax SELECT TRUE AS true; SELECT FALSE AS false; SELECT bool 't' AS true; SELECT bool ' f ' AS false; SELECT bool 'true' AS true; SELECT bool 'test' AS error; SELECT bool 'false' AS false; SELECT bool 'foo' AS error; SELECT bool 'y' AS true; SELECT bool 'yes' AS true; SELECT bool 'yeah' AS error; SELECT bool 'n' AS false; SELECT bool 'no' AS false; SELECT bool 'nay' AS error; SELECT bool 'on' AS true; SELECT bool 'off' AS false; SELECT bool 'of' AS false; SELECT bool 'o' AS error; SELECT bool 'on_' AS error; SELECT bool 'off_' AS error; SELECT bool '1' AS true; SELECT bool '11' AS error; SELECT bool '0' AS false; SELECT bool '000' AS error; SELECT bool '' AS error; -- and, or, not in qualifications SELECT bool 't' OR bool 'f' AS true; SELECT bool 't' AND bool 'f' AS false; SELECT NOT bool 'f' AS true; SELECT bool 't' = bool 'f' AS false; SELECT bool 't' <> bool 'f' AS true; SELECT bool 't' > bool 'f' AS true; SELECT bool 't' >= bool 'f' AS true; SELECT bool 'f' < bool 't' AS true; SELECT bool 'f' <= bool 't' AS true; -- explicit casts to/from text SELECT 'TrUe'::text::boolean AS true, 'fAlse'::text::boolean AS false; SELECT ' true '::text::boolean AS true, ' FALSE'::text::boolean AS false; SELECT TRUE::boolean::text AS true, FALSE::boolean::text AS false; SELECT ' tru e '::text::boolean AS invalid; -- error SELECT ''::text::boolean AS invalid; -- error CREATE TABLE BOOLTBL1 ( f1 bool ); INSERT INTO BOOLTBL1 (f1) VALUES (bool 't'); INSERT INTO BOOLTBL1 (f1) VALUES (bool 'True'); INSERT INTO BOOLTBL1 (f1) VALUES (bool 'true'); -- BOOLTBL1 should be full of true's at this point SELECT '' AS t_3, BOOLTBL1.* FROM BOOLTBL1; SELECT '' AS t_3, BOOLTBL1.* FROM BOOLTBL1 WHERE f1 = bool 'true'; SELECT '' AS t_3, BOOLTBL1.* FROM BOOLTBL1 WHERE f1 <> bool 'false'; SELECT '' AS zero, BOOLTBL1.* FROM BOOLTBL1 WHERE booleq(bool 'false', f1); INSERT INTO BOOLTBL1 (f1) VALUES (bool 'f'); SELECT '' AS f_1, BOOLTBL1.* FROM BOOLTBL1 WHERE f1 = bool 'false'; CREATE TABLE BOOLTBL2 ( f1 bool ); INSERT INTO BOOLTBL2 (f1) VALUES (bool 'f'); INSERT INTO BOOLTBL2 (f1) VALUES (bool 'false'); INSERT INTO BOOLTBL2 (f1) VALUES (bool 'False'); INSERT INTO BOOLTBL2 (f1) VALUES (bool 'FALSE'); -- This is now an invalid expression -- For pre-v6.3 this evaluated to false - thomas 1997-10-23 INSERT INTO BOOLTBL2 (f1) VALUES (bool 'XXX'); -- BOOLTBL2 should be full of false's at this point SELECT '' AS f_4, BOOLTBL2.* FROM BOOLTBL2; SELECT '' AS tf_12, BOOLTBL1.*, BOOLTBL2.* FROM BOOLTBL1, BOOLTBL2 WHERE BOOLTBL2.f1 <> BOOLTBL1.f1; SELECT '' AS tf_12, BOOLTBL1.*, BOOLTBL2.* FROM BOOLTBL1, BOOLTBL2 WHERE boolne(BOOLTBL2.f1, BOOLTBL1.f1); SELECT '' AS ff_4, BOOLTBL1.*, BOOLTBL2.* FROM BOOLTBL1, BOOLTBL2 WHERE BOOLTBL2.f1 = BOOLTBL1.f1 AND BOOLTBL1.f1 = bool 'false'; SELECT '' AS tf_12_ff_4, BOOLTBL1.*, BOOLTBL2.* FROM BOOLTBL1, BOOLTBL2 WHERE BOOLTBL2.f1 = BOOLTBL1.f1 OR BOOLTBL1.f1 = bool 'true' ORDER BY BOOLTBL1.f1, BOOLTBL2.f1; -- -- SQL syntax -- Try all combinations to ensure that we get nothing when we expect nothing -- - thomas 2000-01-04 -- SELECT '' AS "True", f1 FROM BOOLTBL1 WHERE f1 IS TRUE; SELECT '' AS "Not False", f1 FROM BOOLTBL1 WHERE f1 IS NOT FALSE; SELECT '' AS "False", f1 FROM BOOLTBL1 WHERE f1 IS FALSE; SELECT '' AS "Not True", f1 FROM BOOLTBL1 WHERE f1 IS NOT TRUE; SELECT '' AS "True", f1 FROM BOOLTBL2 WHERE f1 IS TRUE; SELECT '' AS "Not False", f1 FROM BOOLTBL2 WHERE f1 IS NOT FALSE; SELECT '' AS "False", f1 FROM BOOLTBL2 WHERE f1 IS FALSE; SELECT '' AS "Not True", f1 FROM BOOLTBL2 WHERE f1 IS NOT TRUE; -- -- Tests for BooleanTest -- CREATE TABLE BOOLTBL3 ( d text, b bool, o int ); INSERT INTO BOOLTBL3 (d, b, o) VALUES ('true', TRUE, 1); INSERT INTO BOOLTBL3 (d, b, o) VALUES ('false', FALSE, 2); INSERT INTO BOOLTBL3 (d, b, o) VALUES ('null', NULL, 3); SELECT d, b IS TRUE AS istrue, b IS NOT TRUE AS isnottrue, b IS FALSE AS isfalse, b IS NOT FALSE AS isnotfalse, b IS UNKNOWN AS isunknown, b IS NOT UNKNOWN AS isnotunknown FROM booltbl3 ORDER BY o; -- Test to make sure short-circuiting and NULL handling is -- correct. Use a table as source to prevent constant simplification -- to interfer. CREATE TABLE booltbl4 ( isfalse bool, istrue bool, isnul bool ); INSERT INTO booltbl4 VALUES (FALSE, TRUE, NULL); \pset null '(null)' -- AND expression need to return null if there's any nulls and not all -- of the value are true SELECT istrue AND isnul AND istrue FROM booltbl4; SELECT istrue AND istrue AND isnul FROM booltbl4; SELECT isnul AND istrue AND istrue FROM booltbl4; SELECT isfalse AND isnul AND istrue FROM booltbl4; SELECT istrue AND isfalse AND isnul FROM booltbl4; SELECT isnul AND istrue AND isfalse FROM booltbl4; -- OR expression need to return null if there's any nulls and none -- of the value is true SELECT isfalse OR isnul OR isfalse FROM booltbl4; SELECT isfalse OR isfalse OR isnul FROM booltbl4; SELECT isnul OR isfalse OR isfalse FROM booltbl4; SELECT isfalse OR isnul OR istrue FROM booltbl4; SELECT istrue OR isfalse OR isnul FROM booltbl4; SELECT isnul OR istrue OR isfalse FROM booltbl4; -- -- Clean up -- Many tables are retained by the regression test, but these do not seem -- particularly useful so just get rid of them for now. -- - thomas 1997-11-30 -- DROP TABLE BOOLTBL1; DROP TABLE BOOLTBL2; DROP TABLE BOOLTBL3; DROP TABLE BOOLTBL4; pgFormatter-4.2/t/pg-test-files/expected/box.sql000066400000000000000000000161671361326045100217250ustar00rootroot00000000000000-- -- BOX -- -- -- box logic -- o -- 3 o--|X -- | o| -- 2 +-+-+ | -- | | | | -- 1 | o-+-o -- | | -- 0 +---+ -- -- 0 1 2 3 -- -- boxes are specified by two points, given by four floats x1,y1,x2,y2 CREATE TABLE BOX_TBL ( f1 box ); INSERT INTO BOX_TBL (f1) VALUES ('(2.0,2.0,0.0,0.0)'); INSERT INTO BOX_TBL (f1) VALUES ('(1.0,1.0,3.0,3.0)'); INSERT INTO BOX_TBL (f1) VALUES ('((-8, 2), (-2, -10))'); -- degenerate cases where the box is a line or a point -- note that lines and points boxes all have zero area INSERT INTO BOX_TBL (f1) VALUES ('(2.5, 2.5, 2.5,3.5)'); INSERT INTO BOX_TBL (f1) VALUES ('(3.0, 3.0,3.0,3.0)'); -- badly formatted box inputs INSERT INTO BOX_TBL (f1) VALUES ('(2.3, 4.5)'); INSERT INTO BOX_TBL (f1) VALUES ('[1, 2, 3, 4)'); INSERT INTO BOX_TBL (f1) VALUES ('(1, 2, 3, 4]'); INSERT INTO BOX_TBL (f1) VALUES ('(1, 2, 3, 4) x'); INSERT INTO BOX_TBL (f1) VALUES ('asdfasdf(ad'); SELECT '' AS four, * FROM BOX_TBL; SELECT '' AS four, b.*, area(b.f1) AS barea FROM BOX_TBL b; -- overlap SELECT '' AS three, b.f1 FROM BOX_TBL b WHERE b.f1 && box '(2.5,2.5,1.0,1.0)'; -- left-or-overlap (x only) SELECT '' AS two, b1.* FROM BOX_TBL b1 WHERE b1.f1 &< box '(2.0,2.0,2.5,2.5)'; -- right-or-overlap (x only) SELECT '' AS two, b1.* FROM BOX_TBL b1 WHERE b1.f1 &> box '(2.0,2.0,2.5,2.5)'; -- left of SELECT '' AS two, b.f1 FROM BOX_TBL b WHERE b.f1 << box '(3.0,3.0,5.0,5.0)'; -- area <= SELECT '' AS four, b.f1 FROM BOX_TBL b WHERE b.f1 <= box '(3.0,3.0,5.0,5.0)'; -- area < SELECT '' AS two, b.f1 FROM BOX_TBL b WHERE b.f1 < box '(3.0,3.0,5.0,5.0)'; -- area = SELECT '' AS two, b.f1 FROM BOX_TBL b WHERE b.f1 = box '(3.0,3.0,5.0,5.0)'; -- area > SELECT '' AS two, b.f1 FROM BOX_TBL b -- zero area WHERE b.f1 > box '(3.5,3.0,4.5,3.0)'; -- area >= SELECT '' AS four, b.f1 FROM BOX_TBL b -- zero area WHERE b.f1 >= box '(3.5,3.0,4.5,3.0)'; -- right of SELECT '' AS two, b.f1 FROM BOX_TBL b WHERE box '(3.0,3.0,5.0,5.0)' >> b.f1; -- contained in SELECT '' AS three, b.f1 FROM BOX_TBL b WHERE b.f1 <@ box '(0,0,3,3)'; -- contains SELECT '' AS three, b.f1 FROM BOX_TBL b WHERE box '(0,0,3,3)' @> b.f1; -- box equality SELECT '' AS one, b.f1 FROM BOX_TBL b WHERE box '(1,1,3,3)' ~= b.f1; -- center of box, left unary operator SELECT '' AS four, @@ (b1.f1) AS p FROM BOX_TBL b1; -- wholly-contained SELECT '' AS one, b1.*, b2.* FROM BOX_TBL b1, BOX_TBL b2 WHERE b1.f1 @> b2.f1 AND NOT b1.f1 ~= b2.f1; SELECT '' AS four, height(f1), width(f1) FROM BOX_TBL; -- -- Test the SP-GiST index -- CREATE TEMPORARY TABLE box_temp ( f1 box ); INSERT INTO box_temp SELECT box(point(i, i), point(i * 2, i * 2)) FROM generate_series(1, 50) AS i; CREATE INDEX box_spgist ON box_temp USING spgist (f1); INSERT INTO box_temp VALUES (NULL), ('(0,0)(0,100)'), ('(-3,4.3333333333)(40,1)'), ('(0,100)(0,infinity)'), ('(-infinity,0)(0,infinity)'), ('(-infinity,-infinity)(infinity,infinity)'); SET enable_seqscan = FALSE; SELECT * FROM box_temp WHERE f1 << '(10,20),(30,40)'; EXPLAIN ( COSTS OFF ) SELECT * FROM box_temp WHERE f1 << '(10,20),(30,40)'; SELECT * FROM box_temp WHERE f1 &< '(10,4.333334),(5,100)'; EXPLAIN ( COSTS OFF ) SELECT * FROM box_temp WHERE f1 &< '(10,4.333334),(5,100)'; SELECT * FROM box_temp WHERE f1 && '(15,20),(25,30)'; EXPLAIN ( COSTS OFF ) SELECT * FROM box_temp WHERE f1 && '(15,20),(25,30)'; SELECT * FROM box_temp WHERE f1 &> '(40,30),(45,50)'; EXPLAIN ( COSTS OFF ) SELECT * FROM box_temp WHERE f1 &> '(40,30),(45,50)'; SELECT * FROM box_temp WHERE f1 >> '(30,40),(40,30)'; EXPLAIN ( COSTS OFF ) SELECT * FROM box_temp WHERE f1 >> '(30,40),(40,30)'; SELECT * FROM box_temp WHERE f1 <<| '(10,4.33334),(5,100)'; EXPLAIN ( COSTS OFF ) SELECT * FROM box_temp WHERE f1 <<| '(10,4.33334),(5,100)'; SELECT * FROM box_temp WHERE f1 &<| '(10,4.3333334),(5,1)'; EXPLAIN ( COSTS OFF ) SELECT * FROM box_temp WHERE f1 &<| '(10,4.3333334),(5,1)'; SELECT * FROM box_temp WHERE f1 |&> '(49.99,49.99),(49.99,49.99)'; EXPLAIN ( COSTS OFF ) SELECT * FROM box_temp WHERE f1 |&> '(49.99,49.99),(49.99,49.99)'; SELECT * FROM box_temp WHERE f1 |>> '(37,38),(39,40)'; EXPLAIN ( COSTS OFF ) SELECT * FROM box_temp WHERE f1 |>> '(37,38),(39,40)'; SELECT * FROM box_temp WHERE f1 @> '(10,11),(15,16)'; EXPLAIN ( COSTS OFF ) SELECT * FROM box_temp WHERE f1 @> '(10,11),(15,15)'; SELECT * FROM box_temp WHERE f1 <@ '(10,15),(30,35)'; EXPLAIN ( COSTS OFF ) SELECT * FROM box_temp WHERE f1 <@ '(10,15),(30,35)'; SELECT * FROM box_temp WHERE f1 ~= '(20,20),(40,40)'; EXPLAIN ( COSTS OFF ) SELECT * FROM box_temp WHERE f1 ~= '(20,20),(40,40)'; RESET enable_seqscan; DROP INDEX box_spgist; -- -- Test the SP-GiST index on the larger volume of data -- CREATE TABLE quad_box_tbl ( b box ); INSERT INTO quad_box_tbl SELECT box(point(x * 10, y * 10), point(x * 10 + 5, y * 10 + 5)) FROM generate_series(1, 100) x, generate_series(1, 100) y; -- insert repeating data to test allTheSame INSERT INTO quad_box_tbl SELECT '((200, 300),(210, 310))' FROM generate_series(1, 1000); INSERT INTO quad_box_tbl VALUES (NULL), (NULL), ('((-infinity,-infinity),(infinity,infinity))'), ('((-infinity,100),(-infinity,500))'), ('((-infinity,-infinity),(700,infinity))'); CREATE INDEX quad_box_tbl_idx ON quad_box_tbl USING spgist (b); SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = ON; SELECT count(*) FROM quad_box_tbl WHERE b << box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b &< box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b && box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b &> box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b >> box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b >> box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b <<| box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b &<| box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b |&> box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b |>> box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b @> box '((201,301),(202,303))'; SELECT count(*) FROM quad_box_tbl WHERE b <@ box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b ~= box '((200,300),(205,305))'; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; pgFormatter-4.2/t/pg-test-files/expected/brin.sql000066400000000000000000000413221361326045100220560ustar00rootroot00000000000000CREATE TABLE brintest ( byteacol bytea, charcol "char", namecol name, int8col bigint, int2col smallint, int4col integer, textcol text, oidcol oid, tidcol tid, float4col real, float8col double precision, macaddrcol macaddr, inetcol inet, cidrcol cidr, bpcharcol character, datecol date, timecol time without time zone, timestampcol timestamp without time zone, timestamptzcol timestamp with time zone, intervalcol interval, timetzcol time with time zone, bitcol bit(10), varbitcol bit varying(16), numericcol numeric, uuidcol uuid, int4rangecol int4range, lsncol pg_lsn, boxcol box ) WITH ( fillfactor = 10 ); INSERT INTO brintest SELECT repeat(stringu1, 8)::bytea, substr(stringu1, 1, 1)::"char", stringu1::name, 142857 * tenthous, thousand, twothousand, repeat(stringu1, 8), unique1::oid, format('(%s,%s)', tenthous, twenty)::tid, (four + 1.0) / (hundred + 1), odd::float8 / (tenthous + 1), format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, inet '10.2.3.4/24' + tenthous, cidr '10.2.3/24' + tenthous, substr(stringu1, 1, 1)::bpchar, date '1995-08-15' + tenthous, time '01:20:30' + thousand * interval '18.5 second', timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', justify_days(justify_hours(tenthous * interval '12 minutes')), timetz '01:30:20+02' + hundred * interval '15 seconds', thousand::bit(10), tenthous::bit(16)::varbit, tenthous::numeric(36, 30) * fivethous * even / (hundred + 1), format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, int4range(thousand, twothousand), format('%s/%s%s', odd, even, tenthous)::pg_lsn, box(point(odd, even), point(thousand, twothousand)) FROM tenk1 ORDER BY unique2 LIMIT 100; -- throw in some NULL's and different values INSERT INTO brintest (inetcol, cidrcol, int4rangecol) SELECT inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous, 'empty'::int4range FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; CREATE INDEX brinidx ON brintest USING brin (byteacol, charcol, namecol, int8col, int2col, int4col, textcol, oidcol, tidcol, float4col, float8col, macaddrcol, inetcol inet_inclusion_ops, inetcol inet_minmax_ops, cidrcol inet_inclusion_ops, cidrcol inet_minmax_ops, bpcharcol, datecol, timecol, timestampcol, timestamptzcol, intervalcol, timetzcol, bitcol, varbitcol, numericcol, uuidcol, int4rangecol, lsncol, boxcol) WITH (pages_per_range = 1); CREATE TABLE brinopers ( colname name, typ text, op text[], value text[], matches int[], CHECK (cardinality(op) = cardinality(value)), CHECK (cardinality(op) = cardinality(matches)) ); INSERT INTO brinopers VALUES ('byteacol', 'bytea', '{>, >=, =, <=, <}', '{AAAAAA, AAAAAA, BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA, ZZZZZZ, ZZZZZZ}', '{100, 100, 1, 100, 100}'), ('charcol', '"char"', '{>, >=, =, <=, <}', '{A, A, M, Z, Z}', '{97, 100, 6, 100, 98}'), ('namecol', 'name', '{>, >=, =, <=, <}', '{AAAAAA, AAAAAA, MAAAAA, ZZAAAA, ZZAAAA}', '{100, 100, 2, 100, 100}'), ('int2col', 'int2', '{>, >=, =, <=, <}', '{0, 0, 800, 999, 999}', '{100, 100, 1, 100, 100}'), ('int2col', 'int4', '{>, >=, =, <=, <}', '{0, 0, 800, 999, 1999}', '{100, 100, 1, 100, 100}'), ('int2col', 'int8', '{>, >=, =, <=, <}', '{0, 0, 800, 999, 1428427143}', '{100, 100, 1, 100, 100}'), ('int4col', 'int2', '{>, >=, =, <=, <}', '{0, 0, 800, 1999, 1999}', '{100, 100, 1, 100, 100}'), ('int4col', 'int4', '{>, >=, =, <=, <}', '{0, 0, 800, 1999, 1999}', '{100, 100, 1, 100, 100}'), ('int4col', 'int8', '{>, >=, =, <=, <}', '{0, 0, 800, 1999, 1428427143}', '{100, 100, 1, 100, 100}'), ('int8col', 'int2', '{>, >=}', '{0, 0}', '{100, 100}'), ('int8col', 'int4', '{>, >=}', '{0, 0}', '{100, 100}'), ('int8col', 'int8', '{>, >=, =, <=, <}', '{0, 0, 1257141600, 1428427143, 1428427143}', '{100, 100, 1, 100, 100}'), ('textcol', 'text', '{>, >=, =, <=, <}', '{ABABAB, ABABAB, BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA, ZZAAAA, ZZAAAA}', '{100, 100, 1, 100, 100}'), ('oidcol', 'oid', '{>, >=, =, <=, <}', '{0, 0, 8800, 9999, 9999}', '{100, 100, 1, 100, 100}'), ('tidcol', 'tid', '{>, >=, =, <=, <}', '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', '{100, 100, 1, 100, 100}'), ('float4col', 'float4', '{>, >=, =, <=, <}', '{0.0103093, 0.0103093, 1, 1, 1}', '{100, 100, 4, 100, 96}'), ('float4col', 'float8', '{>, >=, =, <=, <}', '{0.0103093, 0.0103093, 1, 1, 1}', '{100, 100, 4, 100, 96}'), ('float8col', 'float4', '{>, >=, =, <=, <}', '{0, 0, 0, 1.98, 1.98}', '{99, 100, 1, 100, 100}'), ('float8col', 'float8', '{>, >=, =, <=, <}', '{0, 0, 0, 1.98, 1.98}', '{99, 100, 1, 100, 100}'), ('macaddrcol', 'macaddr', '{>, >=, =, <=, <}', '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', '{99, 100, 2, 100, 100}'), ('inetcol', 'inet', '{&&, =, <, <=, >, >=, >>=, >>, <<=, <<}', '{10/8, 10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0, 10.2.14.231/24, 10.2.14.231/25, 10.2.14.231/8, 0/0}', '{100, 1, 100, 100, 125, 125, 2, 2, 100, 100}'), ('inetcol', 'inet', '{&&, >>=, <<=, =}', '{fe80::6e40:8ff:fea9:a673/32, fe80::6e40:8ff:fea9:8c46, fe80::6e40:8ff:fea9:a673/32, fe80::6e40:8ff:fea9:8c46}', '{25, 1, 25, 1}'), ('inetcol', 'cidr', '{&&, <, <=, >, >=, >>=, >>, <<=, <<}', '{10/8, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0, 10.2.14/24, 10.2.14/25, 10/8, 0/0}', '{100, 100, 100, 125, 125, 2, 2, 100, 100}'), ('inetcol', 'cidr', '{&&, >>=, <<=, =}', '{fe80::/32, fe80::6e40:8ff:fea9:8c46, fe80::/32, fe80::6e40:8ff:fea9:8c46}', '{25, 1, 25, 1}'), ('cidrcol', 'inet', '{&&, =, <, <=, >, >=, >>=, >>, <<=, <<}', '{10/8, 10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0, 10.2.14.231/24, 10.2.14.231/25, 10.2.14.231/8, 0/0}', '{100, 2, 100, 100, 125, 125, 2, 2, 100, 100}'), ('cidrcol', 'inet', '{&&, >>=, <<=, =}', '{fe80::6e40:8ff:fea9:a673/32, fe80::6e40:8ff:fea9:8c46, fe80::6e40:8ff:fea9:a673/32, fe80::6e40:8ff:fea9:8c46}', '{25, 1, 25, 1}'), ('cidrcol', 'cidr', '{&&, =, <, <=, >, >=, >>=, >>, <<=, <<}', '{10/8, 10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0, 10.2.14/24, 10.2.14/25, 10/8, 0/0}', '{100, 2, 100, 100, 125, 125, 2, 2, 100, 100}'), ('cidrcol', 'cidr', '{&&, >>=, <<=, =}', '{fe80::/32, fe80::6e40:8ff:fea9:8c46, fe80::/32, fe80::6e40:8ff:fea9:8c46}', '{25, 1, 25, 1}'), ('bpcharcol', 'bpchar', '{>, >=, =, <=, <}', '{A, A, W, Z, Z}', '{97, 100, 6, 100, 98}'), ('datecol', 'date', '{>, >=, =, <=, <}', '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', '{100, 100, 1, 100, 100}'), ('timecol', 'time', '{>, >=, =, <=, <}', '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', '{100, 100, 1, 100, 100}'), ('timestampcol', 'timestamp', '{>, >=, =, <=, <}', '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', '{100, 100, 1, 100, 100}'), ('timestampcol', 'timestamptz', '{>, >=, =, <=, <}', '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', '{100, 100, 1, 100, 100}'), ('timestamptzcol', 'timestamptz', '{>, >=, =, <=, <}', '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', '{100, 100, 1, 100, 100}'), ('intervalcol', 'interval', '{>, >=, =, <=, <}', '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', '{100, 100, 1, 100, 100}'), ('timetzcol', 'timetz', '{>, >=, =, <=, <}', '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', '{99, 100, 2, 100, 100}'), ('bitcol', 'bit(10)', '{>, >=, =, <=, <}', '{0000000010, 0000000010, 0011011110, 1111111000, 1111111000}', '{100, 100, 1, 100, 100}'), ('varbitcol', 'varbit(16)', '{>, >=, =, <=, <}', '{0000000000000100, 0000000000000100, 0001010001100110, 1111111111111000, 1111111111111000}', '{100, 100, 1, 100, 100}'), ('numericcol', 'numeric', '{>, >=, =, <=, <}', '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', '{100, 100, 1, 100, 100}'), ('uuidcol', 'uuid', '{>, >=, =, <=, <}', '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', '{100, 100, 1, 100, 100}'), ('int4rangecol', 'int4range', '{<<, &<, &&, &>, >>, @>, <@, =, <, <=, >, >=}', '{"[10000,)","[10000,)","(,]","[3,4)","[36,44)","(1500,1501]","[3,4)","[222,1222)","[36,44)","[43,1043)","[367,4466)","[519,)"}', '{53, 53, 53, 53, 50, 22, 72, 1, 74, 75, 34, 21}'), ('int4rangecol', 'int4range', '{@>, <@, =, <=, >, >=}', '{empty, empty, empty, empty, empty, empty}', '{125, 72, 72, 72, 53, 125}'), ('int4rangecol', 'int4', '{@>}', '{1500}', '{22}'), ('lsncol', 'pg_lsn', '{>, >=, =, <=, <, IS, IS NOT}', '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', '{100, 100, 1, 100, 100, 25, 100}'), ('boxcol', 'point', '{@>}', '{"(500,43)"}', '{11}'), ('boxcol', 'box', '{<<, &<, &&, &>, >>, <<|, &<|, |&>, |>>, @>, <@, ~=}', '{"((1000,2000),(3000,4000))","((1,2),(3000,4000))","((1,2),(3000,4000))","((1,2),(3000,4000))","((1,2),(3,4))","((1000,2000),(3000,4000))","((1,2000),(3,4000))","((1000,2),(3000,4))","((1,2),(3,4))","((1,2),(300,400))","((1,2),(3000,4000))","((222,1222),(44,45))"}', '{100, 100, 100, 99, 96, 100, 100, 99, 96, 1, 99, 1}'); DO $x$ DECLARE r record; r2 record; cond text; idx_ctids tid[]; ss_ctids tid[]; count int; plan_ok bool; plan_line text; BEGIN FOR r IN SELECT colname, oper, typ, value[ORDINALITY], matches[ORDINALITY] FROM brinopers, unnest(op ) WITH ORDINALITY AS oper LOOP -- prepare the condition IF r.value IS NULL THEN cond := format('%I %s %L', r.colname, r.oper, r.value); ELSE cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); END IF; -- run the query using the brin index SET enable_seqscan = 0; SET enable_bitmapscan = 1; plan_ok := FALSE; FOR plan_line IN EXECUTE format($y$ EXPLAIN SELECT array_agg(ctid) FROM brintest WHERE % s $y$, cond) LOOP IF plan_line LIKE '%Bitmap Heap Scan on brintest%' THEN plan_ok := TRUE; END IF; END LOOP; IF NOT plan_ok THEN RAISE WARNING 'did not get bitmap indexscan plan for %', r; END IF; EXECUTE format($y$ SELECT array_agg(ctid) FROM brintest WHERE % s $y$, cond) INTO idx_ctids; -- run the query using a seqscan SET enable_seqscan = 1; SET enable_bitmapscan = 0; plan_ok := FALSE; FOR plan_line IN EXECUTE format($y$ EXPLAIN SELECT array_agg(ctid) FROM brintest WHERE % s $y$, cond) LOOP IF plan_line LIKE '%Seq Scan on brintest%' THEN plan_ok := TRUE; END IF; END LOOP; IF NOT plan_ok THEN RAISE WARNING 'did not get seqscan plan for %', r; END IF; EXECUTE format($y$ SELECT array_agg(ctid) FROM brintest WHERE % s $y$, cond) INTO ss_ctids; -- make sure both return the same results count := array_length(idx_ctids, 1); IF NOT (count = array_length(ss_ctids, 1) AND idx_ctids @> ss_ctids AND idx_ctids <@ ss_ctids) THEN -- report the results of each scan to make the differences obvious RAISE WARNING 'something not right in %: count %', r, count; SET enable_seqscan = 1; SET enable_bitmapscan = 0; FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest WHERE ' || cond LOOP RAISE NOTICE 'seqscan: %', r2; END LOOP; SET enable_seqscan = 0; SET enable_bitmapscan = 1; FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest WHERE ' || cond LOOP RAISE NOTICE 'bitmapscan: %', r2; END LOOP; END IF; -- make sure we found expected number of matches IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; END LOOP; END; $x$; RESET enable_seqscan; RESET enable_bitmapscan; INSERT INTO brintest SELECT repeat(stringu1, 42)::bytea, substr(stringu1, 1, 1)::"char", stringu1::name, 142857 * tenthous, thousand, twothousand, repeat(stringu1, 42), unique1::oid, format('(%s,%s)', tenthous, twenty)::tid, (four + 1.0) / (hundred + 1), odd::float8 / (tenthous + 1), format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, inet '10.2.3.4' + tenthous, cidr '10.2.3/24' + tenthous, substr(stringu1, 1, 1)::bpchar, date '1995-08-15' + tenthous, time '01:20:30' + thousand * interval '18.5 second', timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', justify_days(justify_hours(tenthous * interval '12 minutes')), timetz '01:30:20' + hundred * interval '15 seconds', thousand::bit(10), tenthous::bit(16)::varbit, tenthous::numeric(36, 30) * fivethous * even / (hundred + 1), format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, int4range(thousand, twothousand), format('%s/%s%s', odd, even, tenthous)::pg_lsn, box(point(odd, even), point(thousand, twothousand)) FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; SELECT brin_desummarize_range ('brinidx', 0); VACUUM brintest; -- force a summarization cycle in brinidx UPDATE brintest SET int8col = int8col * int4col; UPDATE brintest SET textcol = '' WHERE textcol IS NOT NULL; -- Tests for brin_summarize_new_values SELECT brin_summarize_new_values ('brintest'); -- error, not an index SELECT brin_summarize_new_values ('tenk1_unique1'); -- error, not a BRIN index SELECT brin_summarize_new_values ('brinidx'); -- ok, no change expected -- Tests for brin_desummarize_range SELECT brin_desummarize_range ('brinidx', - 1); -- error, invalid range SELECT brin_desummarize_range ('brinidx', 0); SELECT brin_desummarize_range ('brinidx', 0); SELECT brin_desummarize_range ('brinidx', 100000000); -- Test brin_summarize_range CREATE TABLE brin_summarize ( value int ) WITH ( fillfactor = 10, autovacuum_enabled = FALSE ); CREATE INDEX brin_summarize_idx ON brin_summarize USING brin (value) WITH (pages_per_range = 2); -- Fill a few pages DO $$ DECLARE curtid tid; BEGIN LOOP INSERT INTO brin_summarize VALUES (1) RETURNING ctid INTO curtid; EXIT WHEN curtid > tid '(2, 0)'; END LOOP; END; $$; -- summarize one range SELECT brin_summarize_range ('brin_summarize_idx', 0); -- nothing: already summarized SELECT brin_summarize_range ('brin_summarize_idx', 1); -- summarize one range SELECT brin_summarize_range ('brin_summarize_idx', 2); -- nothing: page doesn't exist in table SELECT brin_summarize_range ('brin_summarize_idx', 4294967295); -- invalid block number values SELECT brin_summarize_range ('brin_summarize_idx', - 1); SELECT brin_summarize_range ('brin_summarize_idx', 4294967296); -- test brin cost estimates behave sanely based on correlation of values CREATE TABLE brin_test ( a int, b int ); INSERT INTO brin_test SELECT x / 100, x % 100 FROM generate_series(1, 10000) x (x); CREATE INDEX brin_test_a_idx ON brin_test USING brin (a) WITH (pages_per_range = 2); CREATE INDEX brin_test_b_idx ON brin_test USING brin (b) WITH (pages_per_range = 2); VACUUM ANALYZE brin_test; -- Ensure brin index is used when columns are perfectly correlated EXPLAIN ( COSTS OFF ) SELECT * FROM brin_test WHERE a = 1; -- Ensure brin index is not used when values are not correlated EXPLAIN ( COSTS OFF ) SELECT * FROM brin_test WHERE b = 1; pgFormatter-4.2/t/pg-test-files/expected/btree_index.sql000066400000000000000000000116461361326045100234220ustar00rootroot00000000000000-- -- BTREE_INDEX -- test retrieval of min/max keys for each index -- SELECT b.* FROM bt_i4_heap b WHERE b.seqno < 1; SELECT b.* FROM bt_i4_heap b WHERE b.seqno >= 9999; SELECT b.* FROM bt_i4_heap b WHERE b.seqno = 4500; SELECT b.* FROM bt_name_heap b WHERE b.seqno < '1'::name; SELECT b.* FROM bt_name_heap b WHERE b.seqno >= '9999'::name; SELECT b.* FROM bt_name_heap b WHERE b.seqno = '4500'::name; SELECT b.* FROM bt_txt_heap b WHERE b.seqno < '1'::text; SELECT b.* FROM bt_txt_heap b WHERE b.seqno >= '9999'::text; SELECT b.* FROM bt_txt_heap b WHERE b.seqno = '4500'::text; SELECT b.* FROM bt_f8_heap b WHERE b.seqno < '1'::float8; SELECT b.* FROM bt_f8_heap b WHERE b.seqno >= '9999'::float8; SELECT b.* FROM bt_f8_heap b WHERE b.seqno = '4500'::float8; -- -- Check correct optimization of LIKE (special index operator support) -- for both indexscan and bitmapscan cases -- SET enable_seqscan TO FALSE; SET enable_indexscan TO TRUE; SET enable_bitmapscan TO FALSE; EXPLAIN ( COSTS OFF ) SELECT proname FROM pg_proc WHERE proname LIKE E'RI\\_FKey%del' ORDER BY 1; SELECT proname FROM pg_proc WHERE proname LIKE E'RI\\_FKey%del' ORDER BY 1; EXPLAIN ( COSTS OFF ) SELECT proname FROM pg_proc WHERE proname ILIKE '00%foo' ORDER BY 1; SELECT proname FROM pg_proc WHERE proname ILIKE '00%foo' ORDER BY 1; EXPLAIN ( COSTS OFF ) SELECT proname FROM pg_proc WHERE proname ILIKE 'ri%foo' ORDER BY 1; SET enable_indexscan TO FALSE; SET enable_bitmapscan TO TRUE; EXPLAIN ( COSTS OFF ) SELECT proname FROM pg_proc WHERE proname LIKE E'RI\\_FKey%del' ORDER BY 1; SELECT proname FROM pg_proc WHERE proname LIKE E'RI\\_FKey%del' ORDER BY 1; EXPLAIN ( COSTS OFF ) SELECT proname FROM pg_proc WHERE proname ILIKE '00%foo' ORDER BY 1; SELECT proname FROM pg_proc WHERE proname ILIKE '00%foo' ORDER BY 1; EXPLAIN ( COSTS OFF ) SELECT proname FROM pg_proc WHERE proname ILIKE 'ri%foo' ORDER BY 1; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; -- -- Test B-tree fast path (cache rightmost leaf page) optimization. -- -- First create a tree that's at least three levels deep (i.e. has one level -- between the root and leaf levels). The text inserted is long. It won't be -- compressed because we use plain storage in the table. Only a few index -- tuples fit on each internal page, allowing us to get a tall tree with few -- pages. (A tall tree is required to trigger caching.) -- -- The text column must be the leading column in the index, since suffix -- truncation would otherwise truncate tuples on internal pages, leaving us -- with a short tree. CREATE TABLE btree_tall_tbl ( id int4, t text ); ALTER TABLE btree_tall_tbl ALTER COLUMN t SET storage plain; CREATE INDEX btree_tall_idx ON btree_tall_tbl (t, id) WITH (fillfactor = 10); INSERT INTO btree_tall_tbl SELECT g, repeat('x', 250) FROM generate_series(1, 130) g; -- -- Test vacuum_cleanup_index_scale_factor -- -- Simple create CREATE TABLE btree_test ( a int ); CREATE INDEX btree_idx1 ON btree_test (a) WITH (vacuum_cleanup_index_scale_factor = 40.0); SELECT reloptions FROM pg_class WHERE oid = 'btree_idx1'::regclass; -- Fail while setting improper values CREATE INDEX btree_idx_err ON btree_test (a) WITH (vacuum_cleanup_index_scale_factor = - 10.0); CREATE INDEX btree_idx_err ON btree_test (a) WITH (vacuum_cleanup_index_scale_factor = 100.0); CREATE INDEX btree_idx_err ON btree_test (a) WITH (vacuum_cleanup_index_scale_factor = 'string'); CREATE INDEX btree_idx_err ON btree_test (a) WITH (vacuum_cleanup_index_scale_factor = TRUE); -- Simple ALTER INDEX ALTER INDEX btree_idx1 SET (vacuum_cleanup_index_scale_factor = 70.0); SELECT reloptions FROM pg_class WHERE oid = 'btree_idx1'::regclass; -- -- Test for multilevel page deletion -- CREATE TABLE delete_test_table ( a bigint, b bigint, c bigint, d bigint ); INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1, 80000) i; ALTER TABLE delete_test_table ADD PRIMARY KEY (a, b, c, d); -- Delete most entries, and vacuum, deleting internal pages and creating "fast -- root" DELETE FROM delete_test_table WHERE a < 79990; VACUUM delete_test_table; -- -- Test B-tree insertion with a metapage update (XLOG_BTREE_INSERT_META -- WAL record type). This happens when a "fast root" page is split. This -- also creates coverage for nbtree FSM page recycling. -- -- The vacuum above should've turned the leaf page into a fast root. We just -- need to insert some rows to cause the fast root page to split. INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1, 1000) i; pgFormatter-4.2/t/pg-test-files/expected/case.sql000066400000000000000000000153551361326045100220460ustar00rootroot00000000000000-- -- CASE -- Test the case statement -- CREATE TABLE CASE_TBL ( i integer, f double precision ); CREATE TABLE CASE2_TBL ( i integer, j integer ); INSERT INTO CASE_TBL VALUES (1, 10.1); INSERT INTO CASE_TBL VALUES (2, 20.2); INSERT INTO CASE_TBL VALUES (3, - 30.3); INSERT INTO CASE_TBL VALUES (4, NULL); INSERT INTO CASE2_TBL VALUES (1, - 1); INSERT INTO CASE2_TBL VALUES (2, - 2); INSERT INTO CASE2_TBL VALUES (3, - 3); INSERT INTO CASE2_TBL VALUES (2, - 4); INSERT INTO CASE2_TBL VALUES (1, NULL); INSERT INTO CASE2_TBL VALUES (NULL, - 6); -- -- Simplest examples without tables -- SELECT '3' AS "One", CASE WHEN 1 < 2 THEN 3 END AS "Simple WHEN"; SELECT '' AS "One", CASE WHEN 1 > 2 THEN 3 END AS "Simple default"; SELECT '3' AS "One", CASE WHEN 1 < 2 THEN 3 ELSE 4 END AS "Simple ELSE"; SELECT '4' AS "One", CASE WHEN 1 > 2 THEN 3 ELSE 4 END AS "ELSE default"; SELECT '6' AS "One", CASE WHEN 1 > 2 THEN 3 WHEN 4 < 5 THEN 6 ELSE 7 END AS "Two WHEN with default"; SELECT '7' AS "None", CASE WHEN random() < 0 THEN 1 END AS "NULL on no matches"; -- Constant-expression folding shouldn't evaluate unreachable subexpressions SELECT CASE WHEN 1 = 0 THEN 1 / 0 WHEN 1 = 1 THEN 1 ELSE 2 / 0 END; SELECT CASE 1 WHEN 0 THEN 1 / 0 WHEN 1 THEN 1 ELSE 2 / 0 END; -- However we do not currently suppress folding of potentially -- reachable subexpressions SELECT CASE WHEN i > 100 THEN 1 / 0 ELSE 0 END FROM case_tbl; -- Test for cases involving untyped literals in test expression SELECT CASE 'a' WHEN 'a' THEN 1 ELSE 2 END; -- -- Examples of targets involving tables -- SELECT '' AS "Five", CASE WHEN i >= 3 THEN i END AS ">= 3 or Null" FROM CASE_TBL; SELECT '' AS "Five", CASE WHEN i >= 3 THEN (i + i) ELSE i END AS "Simplest Math" FROM CASE_TBL; SELECT '' AS "Five", i AS "Value", CASE WHEN (i < 0) THEN 'small' WHEN (i = 0) THEN 'zero' WHEN (i = 1) THEN 'one' WHEN (i = 2) THEN 'two' ELSE 'big' END AS "Category" FROM CASE_TBL; SELECT '' AS "Five", CASE WHEN ((i < 0) OR (i < 0)) THEN 'small' WHEN ((i = 0) OR (i = 0)) THEN 'zero' WHEN ((i = 1) OR (i = 1)) THEN 'one' WHEN ((i = 2) OR (i = 2)) THEN 'two' ELSE 'big' END AS "Category" FROM CASE_TBL; -- -- Examples of qualifications involving tables -- -- -- NULLIF() and COALESCE() -- Shorthand forms for typical CASE constructs -- defined in the SQL standard. -- SELECT * FROM CASE_TBL WHERE COALESCE(f, i) = 4; SELECT * FROM CASE_TBL WHERE NULLIF (f, i) = 2; SELECT COALESCE(a.f, b.i, b.j) FROM CASE_TBL a, CASE2_TBL b; SELECT * FROM CASE_TBL a, CASE2_TBL b WHERE COALESCE(a.f, b.i, b.j) = 2; SELECT '' AS Five, NULLIF (a.i, b.i) AS "NULLIF(a.i,b.i)", NULLIF (b.i, 4) AS "NULLIF(b.i,4)" FROM CASE_TBL a, CASE2_TBL b; SELECT '' AS "Two", * FROM CASE_TBL a, CASE2_TBL b WHERE COALESCE(f, b.i) = 2; -- -- Examples of updates involving tables -- UPDATE CASE_TBL SET i = CASE WHEN i >= 3 THEN (- i) ELSE (2 * i) END; SELECT * FROM CASE_TBL; UPDATE CASE_TBL SET i = CASE WHEN i >= 2 THEN (2 * i) ELSE (3 * i) END; SELECT * FROM CASE_TBL; UPDATE CASE_TBL SET i = CASE WHEN b.i >= 2 THEN (2 * j) ELSE (3 * j) END FROM CASE2_TBL b WHERE j = - CASE_TBL.i; SELECT * FROM CASE_TBL; -- -- Nested CASE expressions -- -- This test exercises a bug caused by aliasing econtext->caseValue_isNull -- with the isNull argument of the inner CASE's CaseExpr evaluation. After -- evaluating the vol(null) expression in the inner CASE's second WHEN-clause, -- the isNull flag for the case test value incorrectly became true, causing -- the third WHEN-clause not to match. The volatile function calls are needed -- to prevent constant-folding in the planner, which would hide the bug. -- Wrap this in a single transaction so the transient '=' operator doesn't -- cause problems in concurrent sessions BEGIN; CREATE FUNCTION vol (text) RETURNS text AS 'begin return $1; end' LANGUAGE plpgsql VOLATILE; SELECT CASE ( CASE vol ('bar') WHEN 'foo' THEN 'it was foo!' WHEN vol (NULL) THEN 'null input' WHEN 'bar' THEN 'it was bar!' END) WHEN 'it was foo!' THEN 'foo recognized' WHEN 'it was bar!' THEN 'bar recognized' ELSE 'unrecognized' END; -- In this case, we can't inline the SQL function without confusing things. CREATE DOMAIN foodomain AS text; CREATE FUNCTION volfoo (text) RETURNS foodomain AS 'begin return $1::foodomain; end' LANGUAGE plpgsql VOLATILE; CREATE FUNCTION inline_eq (foodomain, foodomain) RETURNS boolean AS 'SELECT CASE $2::text WHEN $1::text THEN true ELSE false END' LANGUAGE sql; CREATE OPERATOR = ( PROCEDURE = inline_eq, LEFTARG = foodomain, RIGHTARG = foodomain ); SELECT CASE volfoo ('bar') WHEN 'foo'::foodomain THEN 'is foo' ELSE 'is not foo' END; ROLLBACK; -- Test multiple evaluation of a CASE arg that is a read/write object (#14472) -- Wrap this in a single transaction so the transient '=' operator doesn't -- cause problems in concurrent sessions BEGIN; CREATE DOMAIN arrdomain AS int[]; CREATE FUNCTION make_ad (int, int) RETURNS arrdomain AS 'declare x arrdomain; begin x := array[$1,$2]; return x; end' LANGUAGE plpgsql VOLATILE; CREATE FUNCTION ad_eq (arrdomain, arrdomain) RETURNS boolean AS 'begin return array_eq($1, $2); end' LANGUAGE plpgsql; CREATE OPERATOR = ( PROCEDURE = ad_eq, LEFTARG = arrdomain, RIGHTARG = arrdomain ); SELECT CASE make_ad (1, 2) WHEN ARRAY[2, 4]::arrdomain THEN 'wrong' WHEN ARRAY[2, 5]::arrdomain THEN 'still wrong' WHEN ARRAY[1, 2]::arrdomain THEN 'right' END; ROLLBACK; -- Test interaction of CASE with ArrayCoerceExpr (bug #15471) BEGIN; CREATE TYPE casetestenum AS ENUM ( 'e', 'f', 'g' ); SELECT CASE 'foo'::text WHEN 'foo' THEN ARRAY['a', 'b', 'c', 'd'] || enum_range(NULL::casetestenum)::text[] ELSE ARRAY['x', 'y'] END; ROLLBACK; -- -- Clean up -- DROP TABLE CASE_TBL; DROP TABLE CASE2_TBL; pgFormatter-4.2/t/pg-test-files/expected/char.sql000066400000000000000000000030541361326045100220410ustar00rootroot00000000000000-- -- CHAR -- -- fixed-length by value -- internally passed by value if <= 4 bytes in storage SELECT char 'c' = char 'c' AS true; -- -- Build a table for testing -- CREATE TABLE CHAR_TBL ( f1 char ); INSERT INTO CHAR_TBL (f1) VALUES ('a'); INSERT INTO CHAR_TBL (f1) VALUES ('A'); -- any of the following three input formats are acceptable INSERT INTO CHAR_TBL (f1) VALUES ('1'); INSERT INTO CHAR_TBL (f1) VALUES (2); INSERT INTO CHAR_TBL (f1) VALUES ('3'); -- zero-length char INSERT INTO CHAR_TBL (f1) VALUES (''); -- try char's of greater than 1 length INSERT INTO CHAR_TBL (f1) VALUES ('cd'); INSERT INTO CHAR_TBL (f1) VALUES ('c '); SELECT '' AS seven, * FROM CHAR_TBL; SELECT '' AS six, c.* FROM CHAR_TBL c WHERE c.f1 <> 'a'; SELECT '' AS one, c.* FROM CHAR_TBL c WHERE c.f1 = 'a'; SELECT '' AS five, c.* FROM CHAR_TBL c WHERE c.f1 < 'a'; SELECT '' AS six, c.* FROM CHAR_TBL c WHERE c.f1 <= 'a'; SELECT '' AS one, c.* FROM CHAR_TBL c WHERE c.f1 > 'a'; SELECT '' AS two, c.* FROM CHAR_TBL c WHERE c.f1 >= 'a'; DROP TABLE CHAR_TBL; -- -- Now test longer arrays of char -- CREATE TABLE CHAR_TBL ( f1 char(4) ); INSERT INTO CHAR_TBL (f1) VALUES ('a'); INSERT INTO CHAR_TBL (f1) VALUES ('ab'); INSERT INTO CHAR_TBL (f1) VALUES ('abcd'); INSERT INTO CHAR_TBL (f1) VALUES ('abcde'); INSERT INTO CHAR_TBL (f1) VALUES ('abcd '); SELECT '' AS four, * FROM CHAR_TBL; pgFormatter-4.2/t/pg-test-files/expected/circle.sql000066400000000000000000000030071361326045100223630ustar00rootroot00000000000000-- -- CIRCLE -- -- avoid bit-exact output here because operations may not be bit-exact. SET extra_float_digits = 0; CREATE TABLE CIRCLE_TBL ( f1 circle ); INSERT INTO CIRCLE_TBL VALUES ('<(5,1),3>'); INSERT INTO CIRCLE_TBL VALUES ('<(1,2),100>'); INSERT INTO CIRCLE_TBL VALUES ('1,3,5'); INSERT INTO CIRCLE_TBL VALUES ('((1,2),3)'); INSERT INTO CIRCLE_TBL VALUES ('<(100,200),10>'); INSERT INTO CIRCLE_TBL VALUES (' < ( 100 , 1 ) , 115 > '); INSERT INTO CIRCLE_TBL VALUES ('<(3,5),0>'); -- Zero radius INSERT INTO CIRCLE_TBL VALUES ('<(3,5),NaN>'); -- NaN radius -- bad values INSERT INTO CIRCLE_TBL VALUES ('<(-100,0),-100>'); INSERT INTO CIRCLE_TBL VALUES ('<(100,200),10'); INSERT INTO CIRCLE_TBL VALUES ('<(100,200),10> x'); INSERT INTO CIRCLE_TBL VALUES ('1abc,3,5'); INSERT INTO CIRCLE_TBL VALUES ('(3,(1,2),3)'); SELECT * FROM CIRCLE_TBL; SELECT '' AS six, center(f1) AS center FROM CIRCLE_TBL; SELECT '' AS six, radius(f1) AS radius FROM CIRCLE_TBL; SELECT '' AS six, diameter(f1) AS diameter FROM CIRCLE_TBL; SELECT '' AS two, f1 FROM CIRCLE_TBL WHERE radius(f1) < 5; SELECT '' AS four, f1 FROM CIRCLE_TBL WHERE diameter(f1) >= 10; SELECT '' AS five, c1.f1 AS one, c2.f1 AS two, (c1.f1 <-> c2.f1) AS distance FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE (c1.f1 < c2.f1) AND ((c1.f1 <-> c2.f1) > 0) ORDER BY distance, area(c1.f1), area(c2.f1); pgFormatter-4.2/t/pg-test-files/expected/cluster.sql000066400000000000000000000215271361326045100226120ustar00rootroot00000000000000-- -- CLUSTER -- CREATE TABLE clstr_tst_s ( rf_a serial PRIMARY KEY, b int ); CREATE TABLE clstr_tst ( a serial PRIMARY KEY, b int, c text, d text, CONSTRAINT clstr_tst_con FOREIGN KEY (b) REFERENCES clstr_tst_s ); CREATE INDEX clstr_tst_b ON clstr_tst (b); CREATE INDEX clstr_tst_c ON clstr_tst (c); CREATE INDEX clstr_tst_c_b ON clstr_tst (c, b); CREATE INDEX clstr_tst_b_c ON clstr_tst (b, c); INSERT INTO clstr_tst_s (b) VALUES (0); INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; CREATE TABLE clstr_tst_inh () INHERITS ( clstr_tst ); INSERT INTO clstr_tst (b, c) VALUES (11, 'once'); INSERT INTO clstr_tst (b, c) VALUES (10, 'diez'); INSERT INTO clstr_tst (b, c) VALUES (31, 'treinta y uno'); INSERT INTO clstr_tst (b, c) VALUES (22, 'veintidos'); INSERT INTO clstr_tst (b, c) VALUES (3, 'tres'); INSERT INTO clstr_tst (b, c) VALUES (20, 'veinte'); INSERT INTO clstr_tst (b, c) VALUES (23, 'veintitres'); INSERT INTO clstr_tst (b, c) VALUES (21, 'veintiuno'); INSERT INTO clstr_tst (b, c) VALUES (4, 'cuatro'); INSERT INTO clstr_tst (b, c) VALUES (14, 'catorce'); INSERT INTO clstr_tst (b, c) VALUES (2, 'dos'); INSERT INTO clstr_tst (b, c) VALUES (18, 'dieciocho'); INSERT INTO clstr_tst (b, c) VALUES (27, 'veintisiete'); INSERT INTO clstr_tst (b, c) VALUES (25, 'veinticinco'); INSERT INTO clstr_tst (b, c) VALUES (13, 'trece'); INSERT INTO clstr_tst (b, c) VALUES (28, 'veintiocho'); INSERT INTO clstr_tst (b, c) VALUES (32, 'treinta y dos'); INSERT INTO clstr_tst (b, c) VALUES (5, 'cinco'); INSERT INTO clstr_tst (b, c) VALUES (29, 'veintinueve'); INSERT INTO clstr_tst (b, c) VALUES (1, 'uno'); INSERT INTO clstr_tst (b, c) VALUES (24, 'veinticuatro'); INSERT INTO clstr_tst (b, c) VALUES (30, 'treinta'); INSERT INTO clstr_tst (b, c) VALUES (12, 'doce'); INSERT INTO clstr_tst (b, c) VALUES (17, 'diecisiete'); INSERT INTO clstr_tst (b, c) VALUES (9, 'nueve'); INSERT INTO clstr_tst (b, c) VALUES (19, 'diecinueve'); INSERT INTO clstr_tst (b, c) VALUES (26, 'veintiseis'); INSERT INTO clstr_tst (b, c) VALUES (15, 'quince'); INSERT INTO clstr_tst (b, c) VALUES (7, 'siete'); INSERT INTO clstr_tst (b, c) VALUES (16, 'dieciseis'); INSERT INTO clstr_tst (b, c) VALUES (8, 'ocho'); -- This entry is needed to test that TOASTED values are copied correctly. INSERT INTO clstr_tst (b, c, d) VALUES (6, 'seis', repeat('xyzzy', 100000)); CLUSTER clstr_tst_c ON clstr_tst; SELECT a, b, c, substring(d FOR 30), length(d) FROM clstr_tst; SELECT a, b, c, substring(d FOR 30), length(d) FROM clstr_tst ORDER BY a; SELECT a, b, c, substring(d FOR 30), length(d) FROM clstr_tst ORDER BY b; SELECT a, b, c, substring(d FOR 30), length(d) FROM clstr_tst ORDER BY c; -- Verify that inheritance link still works INSERT INTO clstr_tst_inh VALUES (0, 100, 'in child table'); SELECT a, b, c, substring(d FOR 30), length(d) FROM clstr_tst; -- Verify that foreign key link still works INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should fail'); SELECT conname FROM pg_constraint WHERE conrelid = 'clstr_tst'::regclass ORDER BY 1; SELECT relname, relkind, EXISTS ( SELECT 1 FROM pg_class WHERE oid = c.reltoastrelid) AS hastoast FROM pg_class c WHERE relname LIKE 'clstr_tst%' ORDER BY relname; -- Verify that indisclustered is correctly set SELECT pg_class.relname FROM pg_index, pg_class, pg_class AS pg_class_2 WHERE pg_class.oid = indexrelid AND indrelid = pg_class_2.oid AND pg_class_2.relname = 'clstr_tst' AND indisclustered; -- Try changing indisclustered ALTER TABLE clstr_tst CLUSTER ON clstr_tst_b_c; SELECT pg_class.relname FROM pg_index, pg_class, pg_class AS pg_class_2 WHERE pg_class.oid = indexrelid AND indrelid = pg_class_2.oid AND pg_class_2.relname = 'clstr_tst' AND indisclustered; -- Try turning off all clustering ALTER TABLE clstr_tst SET WITHOUT CLUSTER; SELECT pg_class.relname FROM pg_index, pg_class, pg_class AS pg_class_2 WHERE pg_class.oid = indexrelid AND indrelid = pg_class_2.oid AND pg_class_2.relname = 'clstr_tst' AND indisclustered; -- Verify that clustering all tables does in fact cluster the right ones CREATE USER regress_clstr_user; CREATE TABLE clstr_1 ( a int PRIMARY KEY ); CREATE TABLE clstr_2 ( a int PRIMARY KEY ); CREATE TABLE clstr_3 ( a int PRIMARY KEY ); ALTER TABLE clstr_1 OWNER TO regress_clstr_user; ALTER TABLE clstr_3 OWNER TO regress_clstr_user; GRANT SELECT ON clstr_2 TO regress_clstr_user; INSERT INTO clstr_1 VALUES (2); INSERT INTO clstr_1 VALUES (1); INSERT INTO clstr_2 VALUES (2); INSERT INTO clstr_2 VALUES (1); INSERT INTO clstr_3 VALUES (2); INSERT INTO clstr_3 VALUES (1); -- "CLUSTER " on a table that hasn't been clustered CLUSTER clstr_2; CLUSTER clstr_1_pkey ON clstr_1; CLUSTER clstr_2 USING clstr_2_pkey; SELECT * FROM clstr_1 UNION ALL SELECT * FROM clstr_2 UNION ALL SELECT * FROM clstr_3; -- revert to the original state DELETE FROM clstr_1; DELETE FROM clstr_2; DELETE FROM clstr_3; INSERT INTO clstr_1 VALUES (2); INSERT INTO clstr_1 VALUES (1); INSERT INTO clstr_2 VALUES (2); INSERT INTO clstr_2 VALUES (1); INSERT INTO clstr_3 VALUES (2); INSERT INTO clstr_3 VALUES (1); -- this user can only cluster clstr_1 and clstr_3, but the latter -- has not been clustered SET SESSION AUTHORIZATION regress_clstr_user; CLUSTER; SELECT * FROM clstr_1 UNION ALL SELECT * FROM clstr_2 UNION ALL SELECT * FROM clstr_3; -- cluster a single table using the indisclustered bit previously set DELETE FROM clstr_1; INSERT INTO clstr_1 VALUES (2); INSERT INTO clstr_1 VALUES (1); CLUSTER clstr_1; SELECT * FROM clstr_1; -- Test MVCC-safety of cluster. There isn't much we can do to verify the -- results with a single backend... CREATE TABLE clustertest ( key int PRIMARY KEY ); INSERT INTO clustertest VALUES (10); INSERT INTO clustertest VALUES (20); INSERT INTO clustertest VALUES (30); INSERT INTO clustertest VALUES (40); INSERT INTO clustertest VALUES (50); -- Use a transaction so that updates are not committed when CLUSTER sees 'em BEGIN; -- Test update where the old row version is found first in the scan UPDATE clustertest SET KEY = 100 WHERE KEY = 10; -- Test update where the new row version is found first in the scan UPDATE clustertest SET KEY = 35 WHERE KEY = 40; -- Test longer update chain UPDATE clustertest SET KEY = 60 WHERE KEY = 50; UPDATE clustertest SET KEY = 70 WHERE KEY = 60; UPDATE clustertest SET KEY = 80 WHERE KEY = 70; SELECT * FROM clustertest; CLUSTER clustertest_pkey ON clustertest; SELECT * FROM clustertest; COMMIT; SELECT * FROM clustertest; -- check that temp tables can be clustered CREATE temp TABLE clstr_temp ( col1 int PRIMARY KEY, col2 text ); INSERT INTO clstr_temp VALUES (2, 'two'), (1, 'one'); CLUSTER clstr_temp USING clstr_temp_pkey; SELECT * FROM clstr_temp; DROP TABLE clstr_temp; RESET SESSION AUTHORIZATION; -- Check that partitioned tables cannot be clustered CREATE TABLE clstrpart ( a int ) PARTITION BY RANGE (a); CREATE INDEX clstrpart_idx ON clstrpart (a); ALTER TABLE clstrpart CLUSTER ON clstrpart_idx; CLUSTER clstrpart USING clstrpart_idx; DROP TABLE clstrpart; -- Test CLUSTER with external tuplesorting CREATE TABLE clstr_4 AS SELECT * FROM tenk1; CREATE INDEX cluster_sort ON clstr_4 (hundred, thousand, tenthous); -- ensure we don't use the index in CLUSTER nor the checking SELECTs SET enable_indexscan = OFF; -- Use external sort: SET maintenance_work_mem = '1MB'; CLUSTER clstr_4 USING cluster_sort; SELECT * FROM ( SELECT hundred, lag(hundred) OVER () AS lhundred, thousand, lag(thousand) OVER () AS lthousand, tenthous, lag(tenthous) OVER () AS ltenthous FROM clstr_4) ss WHERE ROW (hundred, thousand, tenthous) <= ROW (lhundred, lthousand, ltenthous); RESET enable_indexscan; RESET maintenance_work_mem; -- clean up DROP TABLE clustertest; DROP TABLE clstr_1; DROP TABLE clstr_2; DROP TABLE clstr_3; DROP TABLE clstr_4; DROP USER regress_clstr_user; pgFormatter-4.2/t/pg-test-files/expected/collate.icu.utf8.sql000066400000000000000000000771471361326045100242310ustar00rootroot00000000000000/* * This test is for ICU collations. */ SET client_encoding TO UTF8; CREATE SCHEMA collate_tests; SET search_path = collate_tests; CREATE TABLE collate_test1 ( a int, b text COLLATE "en-x-icu" NOT NULL ); \d collate_test1 CREATE TABLE collate_test_fail ( a int, b text COLLATE "ja_JP.eucjp-x-icu" ); CREATE TABLE collate_test_fail ( a int, b text COLLATE "foo-x-icu" ); CREATE TABLE collate_test_fail ( a int COLLATE "en-x-icu", b text ); CREATE TABLE collate_test_like ( LIKE collate_test1 ); \d collate_test_like CREATE TABLE collate_test2 ( a int, b text COLLATE "sv-x-icu" ); CREATE TABLE collate_test3 ( a int, b text COLLATE "C" ); INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); INSERT INTO collate_test2 SELECT * FROM collate_test1; INSERT INTO collate_test3 SELECT * FROM collate_test1; SELECT * FROM collate_test1 WHERE b >= 'bbc'; SELECT * FROM collate_test2 WHERE b >= 'bbc'; SELECT * FROM collate_test3 WHERE b >= 'bbc'; SELECT * FROM collate_test3 WHERE b >= 'BBC'; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en-x-icu"; CREATE DOMAIN testdomain_sv AS text COLLATE "sv-x-icu"; CREATE DOMAIN testdomain_i AS int COLLATE "sv-x-icu"; -- fails CREATE TABLE collate_test4 ( a int, b testdomain_sv ); INSERT INTO collate_test4 SELECT * FROM collate_test1; SELECT a, b FROM collate_test4 ORDER BY b; CREATE TABLE collate_test5 ( a int, b testdomain_sv COLLATE "en-x-icu" ); INSERT INTO collate_test5 SELECT * FROM collate_test1; SELECT a, b FROM collate_test5 ORDER BY b; SELECT a, b FROM collate_test1 ORDER BY b; SELECT a, b FROM collate_test2 ORDER BY b; SELECT a, b FROM collate_test3 ORDER BY b; SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; -- star expansion SELECT * FROM collate_test1 ORDER BY b; SELECT * FROM collate_test2 ORDER BY b; SELECT * FROM collate_test3 ORDER BY b; -- constant expression folding SELECT 'bbc' COLLATE "en-x-icu" > 'äbc' COLLATE "en-x-icu" AS "true"; SELECT 'bbc' COLLATE "sv-x-icu" > 'äbc' COLLATE "sv-x-icu" AS "false"; -- upper/lower CREATE TABLE collate_test10 ( a int, x text COLLATE "en-x-icu", y text COLLATE "tr-x-icu" ); INSERT INTO collate_test10 VALUES (1, 'hij', 'hij'), (2, 'HIJ', 'HIJ'); SELECT a, lower(x), lower(y), upper(x), upper(y), initcap(x), initcap(y) FROM collate_test10; SELECT a, lower(x COLLATE "C"), lower(y COLLATE "C") FROM collate_test10; SELECT a, x, y FROM collate_test10 ORDER BY lower(y), a; -- LIKE/ILIKE SELECT * FROM collate_test1 WHERE b LIKE 'abc'; SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; SELECT 'Türkiye' COLLATE "en-x-icu" ILIKE '%KI%' AS "true"; SELECT 'Türkiye' COLLATE "tr-x-icu" ILIKE '%KI%' AS "false"; SELECT 'bıt' ILIKE 'BIT' COLLATE "en-x-icu" AS "false"; SELECT 'bıt' ILIKE 'BIT' COLLATE "tr-x-icu" AS "true"; -- The following actually exercises the selectivity estimation for ILIKE. SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; -- regular expressions SELECT * FROM collate_test1 WHERE b ~ '^abc$'; SELECT * FROM collate_test1 WHERE b ~ '^abc'; SELECT * FROM collate_test1 WHERE b ~ 'bc'; SELECT * FROM collate_test1 WHERE b ~* '^abc$'; SELECT * FROM collate_test1 WHERE b ~* '^abc'; SELECT * FROM collate_test1 WHERE b ~* 'bc'; CREATE TABLE collate_test6 ( a int, b text COLLATE "en-x-icu" ); INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), (9, 'äbç'), (10, 'ÄBÇ'); SELECT b, b ~ '^[[:alpha:]]+$' AS is_alpha, b ~ '^[[:upper:]]+$' AS is_upper, b ~ '^[[:lower:]]+$' AS is_lower, b ~ '^[[:digit:]]+$' AS is_digit, b ~ '^[[:alnum:]]+$' AS is_alnum, b ~ '^[[:graph:]]+$' AS is_graph, b ~ '^[[:print:]]+$' AS is_print, b ~ '^[[:punct:]]+$' AS is_punct, b ~ '^[[:space:]]+$' AS is_space FROM collate_test6; SELECT 'Türkiye' COLLATE "en-x-icu" ~* 'KI' AS "true"; SELECT 'Türkiye' COLLATE "tr-x-icu" ~* 'KI' AS "true"; -- true with ICU SELECT 'bıt' ~* 'BIT' COLLATE "en-x-icu" AS "false"; SELECT 'bıt' ~* 'BIT' COLLATE "tr-x-icu" AS "false"; -- false with ICU -- The following actually exercises the selectivity estimation for ~*. SELECT relname FROM pg_class WHERE relname ~* '^abc'; /* not run by default because it requires tr_TR system locale -- to_char SET lc_time TO 'tr_TR'; SELECT to_char(date '2010-04-01', 'DD TMMON YYYY'); SELECT to_char(date '2010-04-01', 'DD TMMON YYYY' COLLATE "tr-x-icu"); */ -- backwards parsing CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; -- collation propagation in various expression types SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; SELECT a, lower(coalesce(x, 'foo')), lower(coalesce(y, 'foo')) FROM collate_test10; SELECT a, b, greatest (b, 'CCC') FROM collate_test1 ORDER BY 3; SELECT a, b, greatest (b, 'CCC') FROM collate_test2 ORDER BY 3; SELECT a, b, greatest (b, 'CCC') FROM collate_test3 ORDER BY 3; SELECT a, x, y, lower(greatest (x, 'foo')), lower(greatest (y, 'foo')) FROM collate_test10; SELECT a, nullif (b, 'abc') FROM collate_test1 ORDER BY 2; SELECT a, nullif (b, 'abc') FROM collate_test2 ORDER BY 2; SELECT a, nullif (b, 'abc') FROM collate_test3 ORDER BY 2; SELECT a, lower(nullif (x, 'foo')), lower(nullif (y, 'foo')) FROM collate_test10; SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; CREATE DOMAIN testdomain AS text; SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; SELECT a, lower(x::testdomain), lower(y::testdomain) FROM collate_test10; SELECT min(b), max(b) FROM collate_test1; SELECT min(b), max(b) FROM collate_test2; SELECT min(b), max(b) FROM collate_test3; SELECT array_agg(b ORDER BY b) FROM collate_test1; SELECT array_agg(b ORDER BY b) FROM collate_test2; SELECT array_agg(b ORDER BY b) FROM collate_test3; SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail -- ideally this would be a parse-time error, but for now it must be run-time: SELECT x < y FROM collate_test10; -- fail SELECT x || y FROM collate_test10; -- ok, because || is not collation aware SELECT x, y FROM collate_test10 ORDER BY x || y; -- not so ok -- collation mismatch between recursive and non-recursive term WITH RECURSIVE foo ( x ) AS ( SELECT x FROM ( VALUES ('a' COLLATE "en-x-icu"), ('b')) t (x) UNION ALL SELECT (x || 'c') COLLATE "de-x-icu" FROM foo WHERE length(x) < 10 ) SELECT * FROM foo; -- casting SELECT CAST('42' AS text COLLATE "C"); SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; -- propagation of collation in SQL functions (inlined and non-inlined cases) -- and plpgsql functions too CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql AS $$ SELECT $1 < $2 $$; CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql AS $$ SELECT $1 < $2 LIMIT 1 $$; CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql AS $$ BEGIN RETURN $1 < $2; END $$; SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, mylt (a.b, b.b), mylt_noninline (a.b, b.b), mylt_plpgsql (a.b, b.b) FROM collate_test1 a, collate_test1 b ORDER BY a.b, b.b; SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, mylt (a.b, b.b COLLATE "C"), mylt_noninline (a.b, b.b COLLATE "C"), mylt_plpgsql (a.b, b.b COLLATE "C") FROM collate_test1 a, collate_test1 b ORDER BY a.b, b.b; -- collation override in plpgsql CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ DECLARE xx text := x; yy text := y; BEGIN RETURN xx < yy; END $$; SELECT mylt2 ('a', 'B' COLLATE "en-x-icu") AS t, mylt2 ('a', 'B' COLLATE "C") AS f; CREATE OR REPLACE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ DECLARE xx text COLLATE "POSIX" := x; yy text := y; BEGIN RETURN xx < yy; END $$; SELECT mylt2 ('a', 'B') AS f; SELECT mylt2 ('a', 'B' COLLATE "C") AS fail; -- conflicting collations SELECT mylt2 ('a', 'B' COLLATE "POSIX") AS f; -- polymorphism SELECT * FROM unnest(( SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; SELECT * FROM unnest(( SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; SELECT * FROM unnest(( SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; CREATE FUNCTION dup (anyelement) RETURNS anyelement AS 'select $1' LANGUAGE sql; SELECT a, dup (b) FROM collate_test1 ORDER BY 2; SELECT a, dup (b) FROM collate_test2 ORDER BY 2; SELECT a, dup (b) FROM collate_test3 ORDER BY 2; -- indexes CREATE INDEX collate_test1_idx1 ON collate_test1 (b); CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically CREATE INDEX collate_test1_idx4 ON collate_test1 (((b || 'foo') COLLATE "POSIX")); CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; -- schema manipulation commands CREATE ROLE regress_test_role; CREATE SCHEMA test_schema; -- We need to do this this way to cope with varying names for encodings: DO $$ BEGIN EXECUTE 'CREATE COLLATION test0 (provider = icu, locale = ' || quote_literal(current_setting('lc_collate')) || ');'; END $$; CREATE COLLATION test0 FROM "C"; -- fail, duplicate name DO $$ BEGIN EXECUTE 'CREATE COLLATION test1 (provider = icu, lc_collate = ' || quote_literal(current_setting('lc_collate')) || ', lc_ctype = ' || quote_literal(current_setting('lc_ctype')) || ');'; END $$; CREATE COLLATION test3 ( provider = icu, LC_COLLATE = 'en_US.utf8' ); -- fail, need lc_ctype CREATE COLLATION testx ( provider = icu, locale = 'nonsense' ); /* never fails with ICU */ DROP COLLATION testx; CREATE COLLATION test4 FROM nonsense; CREATE COLLATION test5 FROM test0; SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; ALTER COLLATION test1 RENAME TO test11; ALTER COLLATION test0 RENAME TO test11; -- fail ALTER COLLATION test1 RENAME TO test22; -- fail ALTER COLLATION test11 OWNER TO regress_test_role; ALTER COLLATION test11 OWNER TO nonsense; ALTER COLLATION test11 SET SCHEMA test_schema; COMMENT ON COLLATION test0 IS 'US English'; SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) WHERE collname LIKE 'test%' ORDER BY 1; DROP COLLATION test0, test_schema.test11, test5; DROP COLLATION test0; -- fail DROP COLLATION IF EXISTS test0; SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; DROP SCHEMA test_schema; DROP ROLE regress_test_role; -- ALTER ALTER COLLATION "en-x-icu" REFRESH VERSION; -- dependencies CREATE COLLATION test0 FROM "C"; CREATE TABLE collate_dep_test1 ( a int, b text COLLATE test0 ); CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; CREATE TYPE collate_dep_test2 AS ( x int, y text COLLATE test0 ); CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; CREATE TABLE collate_dep_test4t ( a int, b text ); CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); DROP COLLATION test0 RESTRICT; -- fail DROP COLLATION test0 CASCADE; \d collate_dep_test1 \d collate_dep_test2 DROP TABLE collate_dep_test1, collate_dep_test4t; DROP TYPE collate_dep_test2; -- test range types and collations CREATE TYPE textrange_c AS RANGE ( subtype = text, COLLATION = "C" ); CREATE TYPE textrange_en_us AS RANGE ( subtype = text, COLLATION = "en-x-icu" ); SELECT textrange_c ('A', 'Z') @> 'b'::text; SELECT textrange_en_us ('A', 'Z') @> 'b'::text; DROP TYPE textrange_c; DROP TYPE textrange_en_us; -- test ICU collation customization -- test the attributes handled by icu_set_collation_attributes() CREATE COLLATION testcoll_ignore_accents ( provider = icu, locale = '@colStrength=primary;colCaseLevel=yes' ); SELECT 'aaá' > 'AAA' COLLATE "und-x-icu", 'aaá' < 'AAA' COLLATE testcoll_ignore_accents; CREATE COLLATION testcoll_backwards ( provider = icu, locale = '@colBackwards=yes' ); SELECT 'coté' < 'côte' COLLATE "und-x-icu", 'coté' > 'côte' COLLATE testcoll_backwards; CREATE COLLATION testcoll_lower_first ( provider = icu, locale = '@colCaseFirst=lower' ); CREATE COLLATION testcoll_upper_first ( provider = icu, locale = '@colCaseFirst=upper' ); SELECT 'aaa' < 'AAA' COLLATE testcoll_lower_first, 'aaa' > 'AAA' COLLATE testcoll_upper_first; CREATE COLLATION testcoll_shifted ( provider = icu, locale = '@colAlternate=shifted' ); SELECT 'de-luge' < 'deanza' COLLATE "und-x-icu", 'de-luge' > 'deanza' COLLATE testcoll_shifted; CREATE COLLATION testcoll_numeric ( provider = icu, locale = '@colNumeric=yes' ); SELECT 'A-21' > 'A-123' COLLATE "und-x-icu", 'A-21' < 'A-123' COLLATE testcoll_numeric; CREATE COLLATION testcoll_error1 ( provider = icu, locale = '@colNumeric=lower' ); -- test that attributes not handled by icu_set_collation_attributes() -- (handled by ucol_open() directly) also work CREATE COLLATION testcoll_de_phonebook ( provider = icu, locale = 'de@collation=phonebook' ); SELECT 'Goldmann' < 'Götz' COLLATE "de-x-icu", 'Goldmann' > 'Götz' COLLATE testcoll_de_phonebook; -- nondeterministic collations CREATE COLLATION ctest_det ( provider = icu, locale = '', deterministic = TRUE ); CREATE COLLATION ctest_nondet ( provider = icu, locale = '', deterministic = FALSE ); CREATE TABLE test6 ( a int, b text ); -- same string in different normal forms INSERT INTO test6 VALUES (1, U & '\00E4bc'); INSERT INTO test6 VALUES (2, U & '\0061\0308bc'); SELECT * FROM test6; SELECT * FROM test6 WHERE b = 'äbc' COLLATE ctest_det; SELECT * FROM test6 WHERE b = 'äbc' COLLATE ctest_nondet; CREATE COLLATION case_sensitive ( provider = icu, locale = '' ); CREATE COLLATION case_insensitive ( provider = icu, locale = '@colStrength=secondary', deterministic = FALSE ); SELECT 'abc' <= 'ABC' COLLATE case_sensitive, 'abc' >= 'ABC' COLLATE case_sensitive; SELECT 'abc' <= 'ABC' COLLATE case_insensitive, 'abc' >= 'ABC' COLLATE case_insensitive; CREATE TABLE test1cs ( x text COLLATE case_sensitive ); CREATE TABLE test2cs ( x text COLLATE case_sensitive ); CREATE TABLE test3cs ( x text COLLATE case_sensitive ); INSERT INTO test1cs VALUES ('abc'), ('def'), ('ghi'); INSERT INTO test2cs VALUES ('ABC'), ('ghi'); INSERT INTO test3cs VALUES ('abc'), ('ABC'), ('def'), ('ghi'); SELECT x FROM test3cs WHERE x = 'abc'; SELECT x FROM test3cs WHERE x <> 'abc'; SELECT x FROM test3cs WHERE x LIKE 'a%'; SELECT x FROM test3cs WHERE x ILIKE 'a%'; SELECT x FROM test3cs WHERE x SIMILAR TO 'a%'; SELECT x FROM test3cs WHERE x ~ 'a'; SELECT x FROM test1cs UNION SELECT x FROM test2cs ORDER BY x; SELECT x FROM test2cs UNION SELECT x FROM test1cs ORDER BY x; SELECT x FROM test1cs INTERSECT SELECT x FROM test2cs; SELECT x FROM test2cs INTERSECT SELECT x FROM test1cs; SELECT x FROM test1cs EXCEPT SELECT x FROM test2cs; SELECT x FROM test2cs EXCEPT SELECT x FROM test1cs; SELECT DISTINCT x FROM test3cs ORDER BY x; SELECT count(DISTINCT x) FROM test3cs; SELECT x, count(*) FROM test3cs GROUP BY x ORDER BY x; SELECT x, row_number() OVER (ORDER BY x), rank() OVER (ORDER BY x) FROM test3cs ORDER BY x; CREATE UNIQUE INDEX ON test1cs (x); -- ok INSERT INTO test1cs VALUES ('ABC'); -- ok CREATE UNIQUE INDEX ON test3cs (x); -- ok SELECT string_to_array('ABC,DEF,GHI' COLLATE case_sensitive, ',', 'abc'); SELECT string_to_array('ABCDEFGHI' COLLATE case_sensitive, NULL, 'b'); CREATE TABLE test1ci ( x text COLLATE case_insensitive ); CREATE TABLE test2ci ( x text COLLATE case_insensitive ); CREATE TABLE test3ci ( x text COLLATE case_insensitive ); CREATE INDEX ON test3ci (x text_pattern_ops); -- error INSERT INTO test1ci VALUES ('abc'), ('def'), ('ghi'); INSERT INTO test2ci VALUES ('ABC'), ('ghi'); INSERT INTO test3ci VALUES ('abc'), ('ABC'), ('def'), ('ghi'); SELECT x FROM test3ci WHERE x = 'abc'; SELECT x FROM test3ci WHERE x <> 'abc'; SELECT x FROM test3ci WHERE x LIKE 'a%'; SELECT x FROM test3ci WHERE x ILIKE 'a%'; SELECT x FROM test3ci WHERE x SIMILAR TO 'a%'; SELECT x FROM test3ci WHERE x ~ 'a'; SELECT x FROM test1ci UNION SELECT x FROM test2ci ORDER BY x; SELECT x FROM test2ci UNION SELECT x FROM test1ci ORDER BY x; SELECT x FROM test1ci INTERSECT SELECT x FROM test2ci ORDER BY x; SELECT x FROM test2ci INTERSECT SELECT x FROM test1ci ORDER BY x; SELECT x FROM test1ci EXCEPT SELECT x FROM test2ci; SELECT x FROM test2ci EXCEPT SELECT x FROM test1ci; SELECT DISTINCT x FROM test3ci ORDER BY x; SELECT count(DISTINCT x) FROM test3ci; SELECT x, count(*) FROM test3ci GROUP BY x ORDER BY x; SELECT x, row_number() OVER (ORDER BY x), rank() OVER (ORDER BY x) FROM test3ci ORDER BY x; CREATE UNIQUE INDEX ON test1ci (x); -- ok INSERT INTO test1ci VALUES ('ABC'); -- error CREATE UNIQUE INDEX ON test3ci (x); -- error SELECT string_to_array('ABC,DEF,GHI' COLLATE case_insensitive, ',', 'abc'); SELECT string_to_array('ABCDEFGHI' COLLATE case_insensitive, NULL, 'b'); -- bpchar CREATE TABLE test1bpci ( x char(3) COLLATE case_insensitive ); CREATE TABLE test2bpci ( x char(3) COLLATE case_insensitive ); CREATE TABLE test3bpci ( x char(3) COLLATE case_insensitive ); CREATE INDEX ON test3bpci (x bpchar_pattern_ops); -- error INSERT INTO test1bpci VALUES ('abc'), ('def'), ('ghi'); INSERT INTO test2bpci VALUES ('ABC'), ('ghi'); INSERT INTO test3bpci VALUES ('abc'), ('ABC'), ('def'), ('ghi'); SELECT x FROM test3bpci WHERE x = 'abc'; SELECT x FROM test3bpci WHERE x <> 'abc'; SELECT x FROM test3bpci WHERE x LIKE 'a%'; SELECT x FROM test3bpci WHERE x ILIKE 'a%'; SELECT x FROM test3bpci WHERE x SIMILAR TO 'a%'; SELECT x FROM test3bpci WHERE x ~ 'a'; SELECT x FROM test1bpci UNION SELECT x FROM test2bpci ORDER BY x; SELECT x FROM test2bpci UNION SELECT x FROM test1bpci ORDER BY x; SELECT x FROM test1bpci INTERSECT SELECT x FROM test2bpci ORDER BY x; SELECT x FROM test2bpci INTERSECT SELECT x FROM test1bpci ORDER BY x; SELECT x FROM test1bpci EXCEPT SELECT x FROM test2bpci; SELECT x FROM test2bpci EXCEPT SELECT x FROM test1bpci; SELECT DISTINCT x FROM test3bpci ORDER BY x; SELECT count(DISTINCT x) FROM test3bpci; SELECT x, count(*) FROM test3bpci GROUP BY x ORDER BY x; SELECT x, row_number() OVER (ORDER BY x), rank() OVER (ORDER BY x) FROM test3bpci ORDER BY x; CREATE UNIQUE INDEX ON test1bpci (x); -- ok INSERT INTO test1bpci VALUES ('ABC'); -- error CREATE UNIQUE INDEX ON test3bpci (x); -- error SELECT string_to_array('ABC,DEF,GHI'::char(11) COLLATE case_insensitive, ',', 'abc'); SELECT string_to_array('ABCDEFGHI'::char(9) COLLATE case_insensitive, NULL, 'b'); -- This tests the issue described in match_pattern_prefix(). In the -- absence of that check, the case_insensitive tests below would -- return no rows where they should logically return one. CREATE TABLE test4c ( x text COLLATE "C" ); INSERT INTO test4c VALUES ('abc'); CREATE INDEX ON test4c (x); SET enable_seqscan = OFF; SELECT x FROM test4c WHERE x LIKE 'ABC' COLLATE case_sensitive; -- ok, no rows SELECT x FROM test4c WHERE x LIKE 'ABC%' COLLATE case_sensitive; -- ok, no rows SELECT x FROM test4c WHERE x LIKE 'ABC' COLLATE case_insensitive; -- error SELECT x FROM test4c WHERE x LIKE 'ABC%' COLLATE case_insensitive; -- error RESET enable_seqscan; -- Unicode special case: different variants of Greek lower case sigma. -- A naive implementation like citext that just does lower(x) = -- lower(y) will do the wrong thing here, because lower('Σ') is 'σ' -- but upper('ς') is 'Σ'. SELECT 'ὀδυσσεύς' = 'ὈΔΥΣΣΕΎΣ' COLLATE case_sensitive; SELECT 'ὀδυσσεύς' = 'ὈΔΥΣΣΕΎΣ' COLLATE case_insensitive; -- name vs. text comparison operators SELECT relname FROM pg_class WHERE relname = 'PG_CLASS'::text COLLATE case_insensitive; SELECT relname FROM pg_class WHERE 'PG_CLASS'::text = relname COLLATE case_insensitive; SELECT typname FROM pg_type WHERE typname LIKE 'int_' AND typname <> 'INT2'::text COLLATE case_insensitive; SELECT typname FROM pg_type WHERE typname LIKE 'int_' AND 'INT2'::text <> typname COLLATE case_insensitive; ; -- test case adapted from subselect.sql CREATE TEMP TABLE outer_text ( f1 text COLLATE case_insensitive, f2 text ); INSERT INTO outer_text VALUES ('a', 'a'); INSERT INTO outer_text VALUES ('b', 'a'); INSERT INTO outer_text VALUES ('A', NULL); INSERT INTO outer_text VALUES ('B', NULL); CREATE TEMP TABLE inner_text ( c1 text COLLATE case_insensitive, c2 text ); INSERT INTO inner_text VALUES ('a', NULL); SELECT * FROM outer_text WHERE (f1, f2) NOT IN ( SELECT * FROM inner_text); -- accents CREATE COLLATION ignore_accents ( provider = icu, locale = '@colStrength=primary;colCaseLevel=yes', deterministic = FALSE ); CREATE TABLE test4 ( a int, b text ); INSERT INTO test4 VALUES (1, 'cote'), (2, 'côte'), (3, 'coté'), (4, 'côté'); SELECT * FROM test4 WHERE b = 'cote'; SELECT * FROM test4 WHERE b = 'cote' COLLATE ignore_accents; SELECT * FROM test4 WHERE b = 'Cote' COLLATE ignore_accents; -- still case-sensitive SELECT * FROM test4 WHERE b = 'Cote' COLLATE case_insensitive; -- foreign keys (should use collation of primary key) -- PK is case-sensitive, FK is case-insensitive CREATE TABLE test10pk ( x text COLLATE case_sensitive PRIMARY KEY ); INSERT INTO test10pk VALUES ('abc'), ('def'), ('ghi'); CREATE TABLE test10fk ( x text COLLATE case_insensitive REFERENCES test10pk (x) ON UPDATE CASCADE ON DELETE CASCADE ); INSERT INTO test10fk VALUES ('abc'); -- ok INSERT INTO test10fk VALUES ('ABC'); -- error INSERT INTO test10fk VALUES ('xyz'); -- error SELECT * FROM test10pk; SELECT * FROM test10fk; -- restrict update even though the values are "equal" in the FK table UPDATE test10fk SET x = 'ABC' WHERE x = 'abc'; -- error SELECT * FROM test10fk; DELETE FROM test10pk WHERE x = 'abc'; SELECT * FROM test10pk; SELECT * FROM test10fk; -- PK is case-insensitive, FK is case-sensitive CREATE TABLE test11pk ( x text COLLATE case_insensitive PRIMARY KEY ); INSERT INTO test11pk VALUES ('abc'), ('def'), ('ghi'); CREATE TABLE test11fk ( x text COLLATE case_sensitive REFERENCES test11pk (x) ON UPDATE CASCADE ON DELETE CASCADE ); INSERT INTO test11fk VALUES ('abc'); -- ok INSERT INTO test11fk VALUES ('ABC'); -- ok INSERT INTO test11fk VALUES ('xyz'); -- error SELECT * FROM test11pk; SELECT * FROM test11fk; -- cascade update even though the values are "equal" in the PK table UPDATE test11pk SET x = 'ABC' WHERE x = 'abc'; SELECT * FROM test11fk; DELETE FROM test11pk WHERE x = 'abc'; SELECT * FROM test11pk; SELECT * FROM test11fk; -- partitioning CREATE TABLE test20 ( a int, b text COLLATE case_insensitive ) PARTITION BY LIST (b); CREATE TABLE test20_1 PARTITION OF test20 FOR VALUES IN ('abc'); INSERT INTO test20 VALUES (1, 'abc'); INSERT INTO test20 VALUES (2, 'ABC'); SELECT * FROM test20_1; CREATE TABLE test21 ( a int, b text COLLATE case_insensitive ) PARTITION BY RANGE (b); CREATE TABLE test21_1 PARTITION OF test21 FOR VALUES FROM ('ABC') TO ('DEF'); INSERT INTO test21 VALUES (1, 'abc'); INSERT INTO test21 VALUES (2, 'ABC'); SELECT * FROM test21_1; CREATE TABLE test22 ( a int, b text COLLATE case_sensitive ) PARTITION BY HASH (b); CREATE TABLE test22_0 PARTITION OF test22 FOR VALUES WITH (MODULUS 2, REMAINDER 0); CREATE TABLE test22_1 PARTITION OF test22 FOR VALUES WITH (MODULUS 2, REMAINDER 1); INSERT INTO test22 VALUES (1, 'def'); INSERT INTO test22 VALUES (2, 'DEF'); -- they end up in different partitions SELECT ( SELECT count(*) FROM test22_0) = ( SELECT count(*) FROM test22_1); CREATE TABLE test23 ( a int, b text COLLATE case_insensitive ) PARTITION BY HASH (b); CREATE TABLE test23_0 PARTITION OF test23 FOR VALUES WITH (MODULUS 2, REMAINDER 0); CREATE TABLE test23_1 PARTITION OF test23 FOR VALUES WITH (MODULUS 2, REMAINDER 1); INSERT INTO test23 VALUES (1, 'def'); INSERT INTO test23 VALUES (2, 'DEF'); -- they end up in the same partition (but it's platform-dependent which one) SELECT ( SELECT count(*) FROM test23_0) <> ( SELECT count(*) FROM test23_1); CREATE TABLE test30 ( a int, b char(3) COLLATE case_insensitive ) PARTITION BY LIST (b); CREATE TABLE test30_1 PARTITION OF test30 FOR VALUES IN ('abc'); INSERT INTO test30 VALUES (1, 'abc'); INSERT INTO test30 VALUES (2, 'ABC'); SELECT * FROM test30_1; CREATE TABLE test31 ( a int, b char(3) COLLATE case_insensitive ) PARTITION BY RANGE (b); CREATE TABLE test31_1 PARTITION OF test31 FOR VALUES FROM ('ABC') TO ('DEF'); INSERT INTO test31 VALUES (1, 'abc'); INSERT INTO test31 VALUES (2, 'ABC'); SELECT * FROM test31_1; CREATE TABLE test32 ( a int, b char(3) COLLATE case_sensitive ) PARTITION BY HASH (b); CREATE TABLE test32_0 PARTITION OF test32 FOR VALUES WITH (MODULUS 2, REMAINDER 0); CREATE TABLE test32_1 PARTITION OF test32 FOR VALUES WITH (MODULUS 2, REMAINDER 1); INSERT INTO test32 VALUES (1, 'def'); INSERT INTO test32 VALUES (2, 'DEF'); -- they end up in different partitions SELECT ( SELECT count(*) FROM test32_0) = ( SELECT count(*) FROM test32_1); CREATE TABLE test33 ( a int, b char(3) COLLATE case_insensitive ) PARTITION BY HASH (b); CREATE TABLE test33_0 PARTITION OF test33 FOR VALUES WITH (MODULUS 2, REMAINDER 0); CREATE TABLE test33_1 PARTITION OF test33 FOR VALUES WITH (MODULUS 2, REMAINDER 1); INSERT INTO test33 VALUES (1, 'def'); INSERT INTO test33 VALUES (2, 'DEF'); -- they end up in the same partition (but it's platform-dependent which one) SELECT ( SELECT count(*) FROM test33_0) <> ( SELECT count(*) FROM test33_1); -- cleanup SET client_min_messages TO warning; DROP SCHEMA collate_tests CASCADE; RESET search_path; -- leave a collation for pg_upgrade test CREATE COLLATION coll_icu_upgrade FROM "und-x-icu"; pgFormatter-4.2/t/pg-test-files/expected/collate.linux.utf8.sql000066400000000000000000000406661361326045100246040ustar00rootroot00000000000000/* * This test is for Linux/glibc systems and assumes that a full set of * locales is installed. It must be run in a database with UTF-8 encoding, * because other encodings don't support all the characters used. */ SET client_encoding TO UTF8; CREATE SCHEMA collate_tests; SET search_path = collate_tests; CREATE TABLE collate_test1 ( a int, b text COLLATE "en_US" NOT NULL ); \d collate_test1 CREATE TABLE collate_test_fail ( a int, b text COLLATE "ja_JP.eucjp" ); CREATE TABLE collate_test_fail ( a int, b text COLLATE "foo" ); CREATE TABLE collate_test_fail ( a int COLLATE "en_US", b text ); CREATE TABLE collate_test_like ( LIKE collate_test1 ); \d collate_test_like CREATE TABLE collate_test2 ( a int, b text COLLATE "sv_SE" ); CREATE TABLE collate_test3 ( a int, b text COLLATE "C" ); INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); INSERT INTO collate_test2 SELECT * FROM collate_test1; INSERT INTO collate_test3 SELECT * FROM collate_test1; SELECT * FROM collate_test1 WHERE b >= 'bbc'; SELECT * FROM collate_test2 WHERE b >= 'bbc'; SELECT * FROM collate_test3 WHERE b >= 'bbc'; SELECT * FROM collate_test3 WHERE b >= 'BBC'; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails CREATE TABLE collate_test4 ( a int, b testdomain_sv ); INSERT INTO collate_test4 SELECT * FROM collate_test1; SELECT a, b FROM collate_test4 ORDER BY b; CREATE TABLE collate_test5 ( a int, b testdomain_sv COLLATE "en_US" ); INSERT INTO collate_test5 SELECT * FROM collate_test1; SELECT a, b FROM collate_test5 ORDER BY b; SELECT a, b FROM collate_test1 ORDER BY b; SELECT a, b FROM collate_test2 ORDER BY b; SELECT a, b FROM collate_test3 ORDER BY b; SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; -- star expansion SELECT * FROM collate_test1 ORDER BY b; SELECT * FROM collate_test2 ORDER BY b; SELECT * FROM collate_test3 ORDER BY b; -- constant expression folding SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; -- upper/lower CREATE TABLE collate_test10 ( a int, x text COLLATE "en_US", y text COLLATE "tr_TR" ); INSERT INTO collate_test10 VALUES (1, 'hij', 'hij'), (2, 'HIJ', 'HIJ'); SELECT a, lower(x), lower(y), upper(x), upper(y), initcap(x), initcap(y) FROM collate_test10; SELECT a, lower(x COLLATE "C"), lower(y COLLATE "C") FROM collate_test10; SELECT a, x, y FROM collate_test10 ORDER BY lower(y), a; -- LIKE/ILIKE SELECT * FROM collate_test1 WHERE b LIKE 'abc'; SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; SELECT 'Türkiye' COLLATE "en_US" ILIKE '%KI%' AS "true"; SELECT 'Türkiye' COLLATE "tr_TR" ILIKE '%KI%' AS "false"; SELECT 'bıt' ILIKE 'BIT' COLLATE "en_US" AS "false"; SELECT 'bıt' ILIKE 'BIT' COLLATE "tr_TR" AS "true"; -- The following actually exercises the selectivity estimation for ILIKE. SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; -- regular expressions SELECT * FROM collate_test1 WHERE b ~ '^abc$'; SELECT * FROM collate_test1 WHERE b ~ '^abc'; SELECT * FROM collate_test1 WHERE b ~ 'bc'; SELECT * FROM collate_test1 WHERE b ~* '^abc$'; SELECT * FROM collate_test1 WHERE b ~* '^abc'; SELECT * FROM collate_test1 WHERE b ~* 'bc'; CREATE TABLE collate_test6 ( a int, b text COLLATE "en_US" ); INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), (9, 'äbç'), (10, 'ÄBÇ'); SELECT b, b ~ '^[[:alpha:]]+$' AS is_alpha, b ~ '^[[:upper:]]+$' AS is_upper, b ~ '^[[:lower:]]+$' AS is_lower, b ~ '^[[:digit:]]+$' AS is_digit, b ~ '^[[:alnum:]]+$' AS is_alnum, b ~ '^[[:graph:]]+$' AS is_graph, b ~ '^[[:print:]]+$' AS is_print, b ~ '^[[:punct:]]+$' AS is_punct, b ~ '^[[:space:]]+$' AS is_space FROM collate_test6; SELECT 'Türkiye' COLLATE "en_US" ~* 'KI' AS "true"; SELECT 'Türkiye' COLLATE "tr_TR" ~* 'KI' AS "false"; SELECT 'bıt' ~* 'BIT' COLLATE "en_US" AS "false"; SELECT 'bıt' ~* 'BIT' COLLATE "tr_TR" AS "true"; -- The following actually exercises the selectivity estimation for ~*. SELECT relname FROM pg_class WHERE relname ~* '^abc'; -- to_char SET lc_time TO 'tr_TR'; SELECT to_char(date '2010-02-01', 'DD TMMON YYYY'); SELECT to_char(date '2010-02-01', 'DD TMMON YYYY' COLLATE "tr_TR"); SELECT to_char(date '2010-04-01', 'DD TMMON YYYY'); SELECT to_char(date '2010-04-01', 'DD TMMON YYYY' COLLATE "tr_TR"); -- backwards parsing CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; -- collation propagation in various expression types SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; SELECT a, lower(coalesce(x, 'foo')), lower(coalesce(y, 'foo')) FROM collate_test10; SELECT a, b, greatest (b, 'CCC') FROM collate_test1 ORDER BY 3; SELECT a, b, greatest (b, 'CCC') FROM collate_test2 ORDER BY 3; SELECT a, b, greatest (b, 'CCC') FROM collate_test3 ORDER BY 3; SELECT a, x, y, lower(greatest (x, 'foo')), lower(greatest (y, 'foo')) FROM collate_test10; SELECT a, nullif (b, 'abc') FROM collate_test1 ORDER BY 2; SELECT a, nullif (b, 'abc') FROM collate_test2 ORDER BY 2; SELECT a, nullif (b, 'abc') FROM collate_test3 ORDER BY 2; SELECT a, lower(nullif (x, 'foo')), lower(nullif (y, 'foo')) FROM collate_test10; SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; CREATE DOMAIN testdomain AS text; SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; SELECT a, lower(x::testdomain), lower(y::testdomain) FROM collate_test10; SELECT min(b), max(b) FROM collate_test1; SELECT min(b), max(b) FROM collate_test2; SELECT min(b), max(b) FROM collate_test3; SELECT array_agg(b ORDER BY b) FROM collate_test1; SELECT array_agg(b ORDER BY b) FROM collate_test2; SELECT array_agg(b ORDER BY b) FROM collate_test3; SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail -- ideally this would be a parse-time error, but for now it must be run-time: SELECT x < y FROM collate_test10; -- fail SELECT x || y FROM collate_test10; -- ok, because || is not collation aware SELECT x, y FROM collate_test10 ORDER BY x || y; -- not so ok -- collation mismatch between recursive and non-recursive term WITH RECURSIVE foo ( x ) AS ( SELECT x FROM ( VALUES ('a' COLLATE "en_US"), ('b')) t (x) UNION ALL SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10 ) SELECT * FROM foo; -- casting SELECT CAST('42' AS text COLLATE "C"); SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; -- propagation of collation in SQL functions (inlined and non-inlined cases) -- and plpgsql functions too CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql AS $$ SELECT $1 < $2 $$; CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql AS $$ SELECT $1 < $2 LIMIT 1 $$; CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql AS $$ BEGIN RETURN $1 < $2; END $$; SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, mylt (a.b, b.b), mylt_noninline (a.b, b.b), mylt_plpgsql (a.b, b.b) FROM collate_test1 a, collate_test1 b ORDER BY a.b, b.b; SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, mylt (a.b, b.b COLLATE "C"), mylt_noninline (a.b, b.b COLLATE "C"), mylt_plpgsql (a.b, b.b COLLATE "C") FROM collate_test1 a, collate_test1 b ORDER BY a.b, b.b; -- collation override in plpgsql CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ DECLARE xx text := x; yy text := y; BEGIN RETURN xx < yy; END $$; SELECT mylt2 ('a', 'B' COLLATE "en_US") AS t, mylt2 ('a', 'B' COLLATE "C") AS f; CREATE OR REPLACE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ DECLARE xx text COLLATE "POSIX" := x; yy text := y; BEGIN RETURN xx < yy; END $$; SELECT mylt2 ('a', 'B') AS f; SELECT mylt2 ('a', 'B' COLLATE "C") AS fail; -- conflicting collations SELECT mylt2 ('a', 'B' COLLATE "POSIX") AS f; -- polymorphism SELECT * FROM unnest(( SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; SELECT * FROM unnest(( SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; SELECT * FROM unnest(( SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; CREATE FUNCTION dup (anyelement) RETURNS anyelement AS 'select $1' LANGUAGE sql; SELECT a, dup (b) FROM collate_test1 ORDER BY 2; SELECT a, dup (b) FROM collate_test2 ORDER BY 2; SELECT a, dup (b) FROM collate_test3 ORDER BY 2; -- indexes CREATE INDEX collate_test1_idx1 ON collate_test1 (b); CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically CREATE INDEX collate_test1_idx4 ON collate_test1 (((b || 'foo') COLLATE "POSIX")); CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; -- schema manipulation commands CREATE ROLE regress_test_role; CREATE SCHEMA test_schema; -- We need to do this this way to cope with varying names for encodings: DO $$ BEGIN EXECUTE 'CREATE COLLATION test0 (locale = ' || quote_literal(current_setting('lc_collate')) || ');'; END $$; CREATE COLLATION test0 FROM "C"; -- fail, duplicate name CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped CREATE COLLATION IF NOT EXISTS test0 ( locale = 'foo' ); -- ok, skipped DO $$ BEGIN EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || quote_literal(current_setting('lc_collate')) || ', lc_ctype = ' || quote_literal(current_setting('lc_ctype')) || ');'; END $$; CREATE COLLATION test3 ( LC_COLLATE = 'en_US.utf8' ); -- fail, need lc_ctype CREATE COLLATION testx ( locale = 'nonsense' ); -- fail CREATE COLLATION testy ( locale = 'en_US.utf8', version = 'foo' ); -- fail, no versions for libc CREATE COLLATION test4 FROM nonsense; CREATE COLLATION test5 FROM test0; SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; ALTER COLLATION test1 RENAME TO test11; ALTER COLLATION test0 RENAME TO test11; -- fail ALTER COLLATION test1 RENAME TO test22; -- fail ALTER COLLATION test11 OWNER TO regress_test_role; ALTER COLLATION test11 OWNER TO nonsense; ALTER COLLATION test11 SET SCHEMA test_schema; COMMENT ON COLLATION test0 IS 'US English'; SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) WHERE collname LIKE 'test%' ORDER BY 1; DROP COLLATION test0, test_schema.test11, test5; DROP COLLATION test0; -- fail DROP COLLATION IF EXISTS test0; SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; DROP SCHEMA test_schema; DROP ROLE regress_test_role; -- ALTER ALTER COLLATION "en_US" REFRESH VERSION; -- dependencies CREATE COLLATION test0 FROM "C"; CREATE TABLE collate_dep_test1 ( a int, b text COLLATE test0 ); CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; CREATE TYPE collate_dep_test2 AS ( x int, y text COLLATE test0 ); CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; CREATE TABLE collate_dep_test4t ( a int, b text ); CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); DROP COLLATION test0 RESTRICT; -- fail DROP COLLATION test0 CASCADE; \d collate_dep_test1 \d collate_dep_test2 DROP TABLE collate_dep_test1, collate_dep_test4t; DROP TYPE collate_dep_test2; -- test range types and collations CREATE TYPE textrange_c AS RANGE ( subtype = text, COLLATION = "C" ); CREATE TYPE textrange_en_us AS RANGE ( subtype = text, COLLATION = "en_US" ); SELECT textrange_c ('A', 'Z') @> 'b'::text; SELECT textrange_en_us ('A', 'Z') @> 'b'::text; DROP TYPE textrange_c; DROP TYPE textrange_en_us; -- nondeterministic collations -- (not supported with libc provider) CREATE COLLATION ctest_det ( locale = 'en_US.utf8', deterministic = TRUE ); CREATE COLLATION ctest_nondet ( locale = 'en_US.utf8', deterministic = FALSE ); -- cleanup SET client_min_messages TO warning; DROP SCHEMA collate_tests CASCADE; pgFormatter-4.2/t/pg-test-files/expected/collate.sql000066400000000000000000000271171361326045100225550ustar00rootroot00000000000000/* * This test is intended to pass on all platforms supported by Postgres. * We can therefore only assume that the default, C, and POSIX collations * are available --- and since the regression tests are often run in a * C-locale database, these may well all have the same behavior. But * fortunately, the system doesn't know that and will treat them as * incompatible collations. It is therefore at least possible to test * parser behaviors such as collation conflict resolution. This test will, * however, be more revealing when run in a database with non-C locale, * since any departure from C sorting behavior will show as a failure. */ CREATE SCHEMA collate_tests; SET search_path = collate_tests; CREATE TABLE collate_test1 ( a int, b text COLLATE "C" NOT NULL ); \d collate_test1 CREATE TABLE collate_test_fail ( a int COLLATE "C", b text ); CREATE TABLE collate_test_like ( LIKE collate_test1 ); \d collate_test_like CREATE TABLE collate_test2 ( a int, b text COLLATE "POSIX" ); INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'Abc'), (3, 'bbc'), (4, 'ABD'); INSERT INTO collate_test2 SELECT * FROM collate_test1; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'abc'; SELECT * FROM collate_test1 WHERE b >= 'abc' COLLATE "C"; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'abc' COLLATE "C"; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "POSIX"; -- fail CREATE DOMAIN testdomain_p AS text COLLATE "POSIX"; CREATE DOMAIN testdomain_i AS int COLLATE "POSIX"; -- fail CREATE TABLE collate_test4 ( a int, b testdomain_p ); INSERT INTO collate_test4 SELECT * FROM collate_test1; SELECT a, b FROM collate_test4 ORDER BY b; CREATE TABLE collate_test5 ( a int, b testdomain_p COLLATE "C" ); INSERT INTO collate_test5 SELECT * FROM collate_test1; SELECT a, b FROM collate_test5 ORDER BY b; SELECT a, b FROM collate_test1 ORDER BY b; SELECT a, b FROM collate_test2 ORDER BY b; SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; -- star expansion SELECT * FROM collate_test1 ORDER BY b; SELECT * FROM collate_test2 ORDER BY b; -- constant expression folding SELECT 'bbc' COLLATE "C" > 'Abc' COLLATE "C" AS "true"; SELECT 'bbc' COLLATE "POSIX" < 'Abc' COLLATE "POSIX" AS "false"; -- upper/lower CREATE TABLE collate_test10 ( a int, x text COLLATE "C", y text COLLATE "POSIX" ); INSERT INTO collate_test10 VALUES (1, 'hij', 'hij'), (2, 'HIJ', 'HIJ'); SELECT a, lower(x), lower(y), upper(x), upper(y), initcap(x), initcap(y) FROM collate_test10; SELECT a, lower(x COLLATE "C"), lower(y COLLATE "C") FROM collate_test10; SELECT a, x, y FROM collate_test10 ORDER BY lower(y), a; -- backwards parsing CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; -- collation propagation in various expression types SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; SELECT a, lower(coalesce(x, 'foo')), lower(coalesce(y, 'foo')) FROM collate_test10; SELECT a, b, greatest (b, 'CCC') FROM collate_test1 ORDER BY 3; SELECT a, b, greatest (b, 'CCC') FROM collate_test2 ORDER BY 3; SELECT a, x, y, lower(greatest (x, 'foo')), lower(greatest (y, 'foo')) FROM collate_test10; SELECT a, nullif (b, 'abc') FROM collate_test1 ORDER BY 2; SELECT a, nullif (b, 'abc') FROM collate_test2 ORDER BY 2; SELECT a, lower(nullif (x, 'foo')), lower(nullif (y, 'foo')) FROM collate_test10; SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; CREATE DOMAIN testdomain AS text; SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; SELECT a, b::testdomain_p FROM collate_test2 ORDER BY 2; SELECT a, lower(x::testdomain), lower(y::testdomain) FROM collate_test10; SELECT min(b), max(b) FROM collate_test1; SELECT min(b), max(b) FROM collate_test2; SELECT array_agg(b ORDER BY b) FROM collate_test1; SELECT array_agg(b ORDER BY b) FROM collate_test2; -- In aggregates, ORDER BY expressions don't affect aggregate's collation SELECT string_agg(x COLLATE "C", y COLLATE "POSIX") FROM collate_test10; -- fail SELECT array_agg(x COLLATE "C" ORDER BY y COLLATE "POSIX") FROM collate_test10; SELECT array_agg(a ORDER BY x COLLATE "C", y COLLATE "POSIX") FROM collate_test10; SELECT array_agg(a ORDER BY x || y) FROM collate_test10; -- fail SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; SELECT a, b FROM collate_test2 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test2 WHERE a > 1 ORDER BY 2; SELECT a, b FROM collate_test2 EXCEPT SELECT a, b FROM collate_test2 WHERE a < 2 ORDER BY 2; SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test2 ORDER BY 2; -- fail SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test2; -- ok SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test2 ORDER BY 2; -- fail SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test2 ORDER BY 2; -- ok SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test2 ORDER BY 2; -- fail SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test2 ORDER BY 2; -- fail CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test2; -- fail -- ideally this would be a parse-time error, but for now it must be run-time: SELECT x < y FROM collate_test10; -- fail SELECT x || y FROM collate_test10; -- ok, because || is not collation aware SELECT x, y FROM collate_test10 ORDER BY x || y; -- not so ok -- collation mismatch between recursive and non-recursive term WITH RECURSIVE foo ( x ) AS ( SELECT x FROM ( VALUES ('a' COLLATE "C"), ('b')) t (x) UNION ALL SELECT (x || 'c') COLLATE "POSIX" FROM foo WHERE length(x) < 10 ) SELECT * FROM foo; SELECT a, b, a < b AS lt FROM ( VALUES ('a', 'B'), ('A', 'b' COLLATE "C")) v (a, b); -- collation mismatch in subselects SELECT * FROM collate_test10 WHERE (x, y) NOT IN ( SELECT y, x FROM collate_test10); -- now it works with overrides SELECT * FROM collate_test10 WHERE (x COLLATE "POSIX", y COLLATE "C") NOT IN ( SELECT y, x FROM collate_test10); SELECT * FROM collate_test10 WHERE (x, y) NOT IN ( SELECT y COLLATE "C", x COLLATE "POSIX" FROM collate_test10); -- casting SELECT CAST('42' AS text COLLATE "C"); SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; -- polymorphism SELECT * FROM unnest(( SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; SELECT * FROM unnest(( SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; CREATE FUNCTION dup (anyelement) RETURNS anyelement AS 'select $1' LANGUAGE sql; SELECT a, dup (b) FROM collate_test1 ORDER BY 2; SELECT a, dup (b) FROM collate_test2 ORDER BY 2; -- indexes CREATE INDEX collate_test1_idx1 ON collate_test1 (b); CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "POSIX"); CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "POSIX")); -- this is different grammatically CREATE INDEX collate_test1_idx4 ON collate_test1 (((b || 'foo') COLLATE "POSIX")); CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "POSIX"); -- fail CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "POSIX")); -- fail SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; -- foreign keys -- force indexes and mergejoins to be used for FK checking queries, -- else they might not exercise collation-dependent operators SET enable_seqscan TO 0; SET enable_hashjoin TO 0; SET enable_nestloop TO 0; CREATE TABLE collate_test20 ( f1 text COLLATE "C" PRIMARY KEY ); INSERT INTO collate_test20 VALUES ('foo'), ('bar'); CREATE TABLE collate_test21 ( f2 text COLLATE "POSIX" REFERENCES collate_test20 ); INSERT INTO collate_test21 VALUES ('foo'), ('bar'); INSERT INTO collate_test21 VALUES ('baz'); -- fail CREATE TABLE collate_test22 ( f2 text COLLATE "POSIX" ); INSERT INTO collate_test22 VALUES ('foo'), ('bar'), ('baz'); ALTER TABLE collate_test22 ADD FOREIGN KEY (f2) REFERENCES collate_test20; -- fail DELETE FROM collate_test22 WHERE f2 = 'baz'; ALTER TABLE collate_test22 ADD FOREIGN KEY (f2) REFERENCES collate_test20; RESET enable_seqscan; RESET enable_hashjoin; RESET enable_nestloop; -- EXPLAIN EXPLAIN ( COSTS OFF ) SELECT * FROM collate_test10 ORDER BY x, y; EXPLAIN ( COSTS OFF ) SELECT * FROM collate_test10 ORDER BY x DESC, y COLLATE "C" ASC NULLS FIRST; -- CREATE/DROP COLLATION CREATE COLLATION mycoll1 FROM "C"; CREATE COLLATION mycoll2 ( LC_COLLATE = "POSIX", LC_CTYPE = "POSIX" ); CREATE COLLATION mycoll3 FROM "default"; -- intentionally unsupported DROP COLLATION mycoll1; CREATE TABLE collate_test23 ( f1 text COLLATE mycoll2 ); DROP COLLATION mycoll2; -- fail -- invalid: non-lowercase quoted identifiers CREATE COLLATION case_coll ( "Lc_Collate" = "POSIX", "Lc_Ctype" = "POSIX" ); -- 9.1 bug with useless COLLATE in an expression subject to length coercion CREATE TEMP TABLE vctable ( f1 varchar(25) ); INSERT INTO vctable VALUES ('foo' COLLATE "C"); SELECT COLLATION FOR ('foo'); -- unknown type - null SELECT COLLATION FOR ('foo'::text); SELECT COLLATION FOR (( SELECT a FROM collate_test1 LIMIT 1)); -- non-collatable type - error SELECT COLLATION FOR (( SELECT b FROM collate_test1 LIMIT 1)); -- -- Clean up. Many of these table names will be re-used if the user is -- trying to run any platform-specific collation tests later, so we -- must get rid of them. -- DROP SCHEMA collate_tests CASCADE; pgFormatter-4.2/t/pg-test-files/expected/combocid.sql000066400000000000000000000062471361326045100227120ustar00rootroot00000000000000-- -- Tests for some likely failure cases with combo cmin/cmax mechanism -- CREATE TEMP TABLE combocidtest ( foobar int ); BEGIN; -- a few dummy ops to push up the CommandId counter INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest VALUES (1); INSERT INTO combocidtest VALUES (2); SELECT ctid, cmin, * FROM combocidtest; SAVEPOINT s1; UPDATE combocidtest SET foobar = foobar + 10; -- here we should see only updated tuples SELECT ctid, cmin, * FROM combocidtest; ROLLBACK TO s1; -- now we should see old tuples, but with combo CIDs starting at 0 SELECT ctid, cmin, * FROM combocidtest; COMMIT; -- combo data is not there anymore, but should still see tuples SELECT ctid, cmin, * FROM combocidtest; -- Test combo cids with portals BEGIN; INSERT INTO combocidtest VALUES (333); DECLARE c CURSOR FOR SELECT ctid, cmin, * FROM combocidtest; DELETE FROM combocidtest; FETCH ALL FROM c; ROLLBACK; SELECT ctid, cmin, * FROM combocidtest; -- check behavior with locked tuples BEGIN; -- a few dummy ops to push up the CommandId counter INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest VALUES (444); SELECT ctid, cmin, * FROM combocidtest; SAVEPOINT s1; -- this doesn't affect cmin SELECT ctid, cmin, * FROM combocidtest FOR UPDATE; SELECT ctid, cmin, * FROM combocidtest; -- but this does UPDATE combocidtest SET foobar = foobar + 10; SELECT ctid, cmin, * FROM combocidtest; ROLLBACK TO s1; SELECT ctid, cmin, * FROM combocidtest; COMMIT; SELECT ctid, cmin, * FROM combocidtest; -- test for bug reported in -- CABRT9RC81YUf1=jsmWopcKJEro=VoeG2ou6sPwyOUTx_qteRsg@mail.gmail.com CREATE TABLE IF NOT EXISTS testcase ( id int PRIMARY KEY, balance numeric ); INSERT INTO testcase VALUES (1, 0); BEGIN; SELECT * FROM testcase WHERE testcase.id = 1 FOR UPDATE; UPDATE testcase SET balance = balance + 400 WHERE id = 1; SAVEPOINT subxact; UPDATE testcase SET balance = balance - 100 WHERE id = 1; ROLLBACK TO SAVEPOINT subxact; -- should return one tuple SELECT * FROM testcase WHERE id = 1 FOR UPDATE; ROLLBACK; DROP TABLE testcase; pgFormatter-4.2/t/pg-test-files/expected/comments.sql000066400000000000000000000021021361326045100227420ustar00rootroot00000000000000-- -- COMMENTS -- SELECT 'trailing' AS FIRST; -- trailing single line SELECT /* embedded single line */ 'embedded' AS second; SELECT /* both embedded and trailing single line */ 'both' AS third; -- trailing single line SELECT 'before multi-line' AS fourth; /* This is an example of SQL which should not execute: * select 'multi-line'; */ SELECT 'after multi-line' AS fifth; -- -- Nested comments -- /* SELECT 'trailing' as x1; -- inside block comment */ /* This block comment surrounds a query which itself has a block comment... SELECT /* embedded single line */ 'embedded' AS x2; / SELECT -- continued after the following block comments... /* Deeply nested comment. This includes a single apostrophe to make sure we aren't decoding this part as a string. SELECT 'deep nest' AS n1; /* Second level of nesting... SELECT 'deeper nest' as n2; /* Third level of nesting... SELECT 'deepest nest' as n3; */ Hoo boy. Still two deep... / Now just one deep... / 'deeply nested example' AS sixth; /* and this is the end of the file */ pgFormatter-4.2/t/pg-test-files/expected/conversion.sql000066400000000000000000000021471361326045100233130ustar00rootroot00000000000000-- -- create user defined conversion -- CREATE USER regress_conversion_user WITH NOCREATEDB NOCREATEROLE; SET SESSION AUTHORIZATION regress_conversion_user; CREATE CONVERSION myconv FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; -- -- cannot make same name conversion in same schema -- CREATE CONVERSION myconv FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; -- -- create default conversion with qualified name -- CREATE DEFAULT CONVERSION public.mydef FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; -- -- cannot make default conversion with same schema/for_encoding/to_encoding -- CREATE DEFAULT CONVERSION public.mydef2 FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; -- test comments COMMENT ON CONVERSION myconv_bad IS 'foo'; COMMENT ON CONVERSION myconv IS 'bar'; COMMENT ON CONVERSION myconv IS NULL; -- -- drop user defined conversion -- DROP CONVERSION myconv; DROP CONVERSION mydef; -- -- Note: the built-in conversions are exercised in opr_sanity.sql, -- so there's no need to do that here. -- -- -- return to the super user -- RESET SESSION AUTHORIZATION; DROP USER regress_conversion_user; pgFormatter-4.2/t/pg-test-files/expected/create_aggregate.sql000066400000000000000000000173341361326045100244030ustar00rootroot00000000000000-- -- CREATE_AGGREGATE -- -- all functions CREATEd CREATE AGGREGATE newavg ( SFUNC = int4_avg_accum, BASETYPE = int4, STYPE = _int8, FINALFUNC = int8_avg, initcond1 = '{0,0}' ); -- test comments COMMENT ON AGGREGATE newavg_wrong (int4) IS 'an agg comment'; COMMENT ON AGGREGATE newavg (int4) IS 'an agg comment'; COMMENT ON AGGREGATE newavg (int4) IS NULL; -- without finalfunc; test obsolete spellings 'sfunc1' etc CREATE AGGREGATE newsum ( SFUNC1 = int4pl, BASETYPE = int4, STYPE1 = int4, initcond1 = '0' ); -- zero-argument aggregate CREATE AGGREGATE newcnt (*) ( SFUNC = int8inc, STYPE = int8, INITCOND = '0', parallel = safe ); -- old-style spelling of same (except without parallel-safe; that's too new) CREATE AGGREGATE oldcnt ( SFUNC = int8inc, BASETYPE = 'ANY', STYPE = int8, INITCOND = '0' ); -- aggregate that only cares about null/nonnull input CREATE AGGREGATE newcnt ("any") ( SFUNC = int8inc_any, STYPE = int8, INITCOND = '0' ); COMMENT ON AGGREGATE nosuchagg (*) IS 'should fail'; COMMENT ON AGGREGATE newcnt (*) IS 'an agg(*) comment'; COMMENT ON AGGREGATE newcnt ("any") IS 'an agg(any) comment'; -- multi-argument aggregate CREATE FUNCTION sum3 (int8, int8, int8) RETURNS int8 AS 'select $1 + $2 + $3' LANGUAGE sql STRICT IMMUTABLE; CREATE AGGREGATE sum2 (int8, int8) ( SFUNC = sum3, STYPE = int8, INITCOND = '0' ); -- multi-argument aggregates sensitive to distinct/order, strict/nonstrict CREATE TYPE aggtype AS ( a integer, b integer, c text ); CREATE FUNCTION aggf_trans (aggtype[], integer, integer, text) RETURNS aggtype[] AS 'select array_append($1,ROW($2,$3,$4)::aggtype)' LANGUAGE sql STRICT IMMUTABLE; CREATE FUNCTION aggfns_trans (aggtype[], integer, integer, text) RETURNS aggtype[] AS 'select array_append($1,ROW($2,$3,$4)::aggtype)' LANGUAGE sql IMMUTABLE; CREATE AGGREGATE aggfstr (integer, integer, text) ( SFUNC = aggf_trans, STYPE = aggtype[], INITCOND = '{}' ); CREATE AGGREGATE aggfns (integer, integer, text) ( SFUNC = aggfns_trans, STYPE = aggtype[], SSPACE = 10000, INITCOND = '{}' ); -- variadic aggregate CREATE FUNCTION least_accum (anyelement, VARIADIC anyarray) RETURNS anyelement LANGUAGE sql AS 'select least($1, min($2[i])) from generate_subscripts($2,1) g(i)' ; CREATE AGGREGATE least_agg (VARIADIC items anyarray) ( STYPE = anyelement, SFUNC = least_accum ); -- test ordered-set aggs using built-in support functions CREATE AGGREGATE my_percentile_disc (float8 ORDER BY anyelement) ( STYPE = internal, SFUNC = ordered_set_transition, FINALFUNC = percentile_disc_final, FINALFUNC_EXTRA = TRUE, FINALFUNC_MODIFY = READ_WRITE ); CREATE AGGREGATE my_rank (VARIADIC "any" ORDER BY VARIADIC "any") ( STYPE = internal, SFUNC = ordered_set_transition_multi, FINALFUNC = rank_final, FINALFUNC_EXTRA = TRUE, hypothetical ); ALTER AGGREGATE my_percentile_disc (float8 ORDER BY anyelement) RENAME TO test_percentile_disc; ALTER AGGREGATE my_rank (VARIADIC "any" ORDER BY VARIADIC "any") RENAME TO test_rank; \da test_* -- moving-aggregate options CREATE AGGREGATE sumdouble (float8) ( STYPE = float8, SFUNC = float8pl, MSTYPE = float8, MSFUNC = float8pl, MINVFUNC = float8mi ); -- aggregate combine and serialization functions -- can't specify just one of serialfunc and deserialfunc CREATE AGGREGATE myavg (numeric) ( STYPE = internal, SFUNC = numeric_avg_accum, SERIALFUNC = numeric_avg_serialize ); -- serialfunc must have correct parameters CREATE AGGREGATE myavg (numeric) ( STYPE = internal, SFUNC = numeric_avg_accum, SERIALFUNC = numeric_avg_deserialize, DESERIALFUNC = numeric_avg_deserialize ); -- deserialfunc must have correct parameters CREATE AGGREGATE myavg (numeric) ( STYPE = internal, SFUNC = numeric_avg_accum, SERIALFUNC = numeric_avg_serialize, DESERIALFUNC = numeric_avg_serialize ); -- ensure combine function parameters are checked CREATE AGGREGATE myavg (numeric) ( STYPE = internal, SFUNC = numeric_avg_accum, SERIALFUNC = numeric_avg_serialize, DESERIALFUNC = numeric_avg_deserialize, COMBINEFUNC = int4larger ); -- ensure create aggregate works. CREATE AGGREGATE myavg (numeric) ( STYPE = internal, SFUNC = numeric_avg_accum, FINALFUNC = numeric_avg, SERIALFUNC = numeric_avg_serialize, DESERIALFUNC = numeric_avg_deserialize, COMBINEFUNC = numeric_avg_combine, FINALFUNC_MODIFY = SHAREABLE -- just to test a non-default setting ); -- Ensure all these functions made it into the catalog SELECT aggfnoid, aggtransfn, aggcombinefn, aggtranstype::regtype, aggserialfn, aggdeserialfn, aggfinalmodify FROM pg_aggregate WHERE aggfnoid = 'myavg'::REGPROC; DROP AGGREGATE myavg (numeric); -- create or replace aggregate CREATE AGGREGATE myavg (numeric) ( STYPE = internal, SFUNC = numeric_avg_accum, FINALFUNC = numeric_avg ); CREATE OR REPLACE AGGREGATE myavg (numeric) ( STYPE = internal, SFUNC = numeric_avg_accum, FINALFUNC = numeric_avg, SERIALFUNC = numeric_avg_serialize, DESERIALFUNC = numeric_avg_deserialize, COMBINEFUNC = numeric_avg_combine, FINALFUNC_MODIFY = SHAREABLE -- just to test a non-default setting ); -- Ensure all these functions made it into the catalog again SELECT aggfnoid, aggtransfn, aggcombinefn, aggtranstype::regtype, aggserialfn, aggdeserialfn, aggfinalmodify FROM pg_aggregate WHERE aggfnoid = 'myavg'::REGPROC; -- can change stype: CREATE OR REPLACE AGGREGATE myavg (numeric) ( STYPE = numeric, SFUNC = numeric_add ); SELECT aggfnoid, aggtransfn, aggcombinefn, aggtranstype::regtype, aggserialfn, aggdeserialfn, aggfinalmodify FROM pg_aggregate WHERE aggfnoid = 'myavg'::REGPROC; -- can't change return type: CREATE OR REPLACE AGGREGATE myavg (numeric) ( STYPE = numeric, SFUNC = numeric_add, FINALFUNC = numeric_out ); -- can't change to a different kind: CREATE OR REPLACE AGGREGATE myavg ( ORDER by numeric) ( STYPE = numeric, SFUNC = numeric_add ); -- can't change plain function to aggregate: CREATE FUNCTION sum4 (int8, int8, int8, int8) RETURNS int8 AS 'select $1 + $2 + $3 + $4' LANGUAGE sql STRICT IMMUTABLE; CREATE OR REPLACE AGGREGATE sum3 (int8, int8, int8) ( STYPE = int8, SFUNC = sum4 ); DROP FUNCTION sum4 (int8, int8, int8, int8); DROP AGGREGATE myavg (numeric); -- invalid: bad parallel-safety marking CREATE AGGREGATE mysum (int) ( STYPE = int, SFUNC = int4pl, parallel = pear ); -- invalid: nonstrict inverse with strict forward function CREATE FUNCTION float8mi_n (float8, float8) RETURNS float8 AS $$ SELECT $1 - $2; $$ LANGUAGE SQL; CREATE AGGREGATE invalidsumdouble (float8) ( STYPE = float8, SFUNC = float8pl, MSTYPE = float8, MSFUNC = float8pl, MINVFUNC = float8mi_n ); -- invalid: non-matching result types CREATE FUNCTION float8mi_int (float8, float8) RETURNS int AS $$ SELECT CAST($1 - $2 AS INT); $$ LANGUAGE SQL; CREATE AGGREGATE wrongreturntype (float8) ( STYPE = float8, SFUNC = float8pl, MSTYPE = float8, MSFUNC = float8pl, MINVFUNC = float8mi_int ); -- invalid: non-lowercase quoted identifiers CREATE AGGREGATE case_agg ( -- old syntax "Sfunc1" = int4pl, "Basetype" = int4, "Stype1" = int4, "Initcond1" = '0', "Parallel" = safe ); CREATE AGGREGATE case_agg (float8) ( "Stype" = internal, "Sfunc" = ordered_set_transition, "Finalfunc" = percentile_disc_final, "Finalfunc_extra" = TRUE, "Finalfunc_modify" = READ_WRITE, "Parallel" = safe ); pgFormatter-4.2/t/pg-test-files/expected/create_am.sql000066400000000000000000000173211361326045100230460ustar00rootroot00000000000000-- -- Create access method tests -- -- Make gist2 over gisthandler. In fact, it would be a synonym to gist. CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler; -- Verify return type checks for handlers CREATE ACCESS METHOD bogus TYPE INDEX HANDLER int4in; CREATE ACCESS METHOD bogus TYPE INDEX HANDLER heap_tableam_handler; -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist CREATE INDEX grect2ind2 ON fast_emp4000 USING gist2 (home_base); -- Make operator class for boxes using gist2 CREATE OPERATOR CLASS box_ops DEFAULT FOR TYPE box USING gist2 AS OPERATOR 1 <<, OPERATOR 2 &<, OPERATOR 3 &&, OPERATOR 4 &>, OPERATOR 5 >>, OPERATOR 6 ~=, OPERATOR 7 @>, OPERATOR 8 <@, OPERATOR 9 &<|, OPERATOR 10 <<|, OPERATOR 11 |>>, OPERATOR 12 |&>, OPERATOR 13 ~, OPERATOR 14 @, FUNCTION 1 gist_box_consistent( internal, box, smallint, oid, internal), FUNCTION 2 gist_box_union( internal, internal), -- don't need compress, decompress, or fetch functions FUNCTION 5 gist_box_penalty( internal, internal, internal), FUNCTION 6 gist_box_picksplit( internal, internal), FUNCTION 7 gist_box_same(box, box, internal ); -- Create gist2 index on fast_emp4000 CREATE INDEX grect2ind2 ON fast_emp4000 USING gist2 (home_base); -- Now check the results from plain indexscan; temporarily drop existing -- index grect2ind to ensure it doesn't capture the plan BEGIN; DROP INDEX grect2ind; SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = OFF; EXPLAIN ( COSTS OFF ) SELECT * FROM fast_emp4000 WHERE home_base @ '(200,200),(2000,1000)'::box ORDER BY (home_base[0])[0]; SELECT * FROM fast_emp4000 WHERE home_base @ '(200,200),(2000,1000)'::box ORDER BY (home_base[0])[0]; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box; SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; ROLLBACK; -- Try to drop access method: fail because of dependent objects DROP ACCESS METHOD gist2; -- Drop access method cascade DROP ACCESS METHOD gist2 CASCADE; -- -- Test table access methods -- -- prevent empty values SET default_table_access_method = ''; -- prevent nonexistant values SET default_table_access_method = 'I do not exist AM'; -- prevent setting it to an index AM SET default_table_access_method = 'btree'; -- Create a heap2 table am handler with heapam handler CREATE ACCESS METHOD heap2 TYPE TABLE HANDLER heap_tableam_handler; -- Verify return type checks for handlers CREATE ACCESS METHOD bogus TYPE TABLE HANDLER int4in; CREATE ACCESS METHOD bogus TYPE TABLE HANDLER bthandler; SELECT amname, amhandler, amtype FROM pg_am WHERE amtype = 't' ORDER BY 1, 2; -- First create tables employing the new AM using USING -- plain CREATE TABLE CREATE TABLE tableam_tbl_heap2 ( f1 int) USING heap2; INSERT INTO tableam_tbl_heap2 VALUES (1); SELECT f1 FROM tableam_tbl_heap2 ORDER BY f1; -- CREATE TABLE AS CREATE TABLE tableam_tblas_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2; SELECT f1 FROM tableam_tbl_heap2 ORDER BY f1; -- SELECT INTO doesn't support USING SELECT INTO tableam_tblselectinto_heap2 USING heap2 FROM tableam_tbl_heap2; -- CREATE VIEW doesn't support USING CREATE VIEW tableam_view_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2; -- CREATE SEQUENCE doesn't support USING CREATE SEQUENCE tableam_seq_heap2 USING heap2; -- CREATE MATERIALIZED VIEW does support USING CREATE MATERIALIZED VIEW tableam_tblmv_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2; SELECT f1 FROM tableam_tblmv_heap2 ORDER BY f1; -- CREATE TABLE .. PARTITION BY doesn't not support USING CREATE TABLE tableam_parted_heap2 ( a text, b int ) PARTITION BY LIST (a) USING heap2; CREATE TABLE tableam_parted_heap2 ( a text, b int ) PARTITION BY LIST (a); -- new partitions will inherit from the current default, rather the partition root SET default_table_access_method = 'heap'; CREATE TABLE tableam_parted_a_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('a'); SET default_table_access_method = 'heap2'; CREATE TABLE tableam_parted_b_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('b'); RESET default_table_access_method; -- but the method can be explicitly specified CREATE TABLE tableam_parted_c_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('c') USING heap; CREATE TABLE tableam_parted_d_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('d') USING heap2; -- List all objects in AM SELECT pc.relkind, pa.amname, CASE WHEN relkind = 't' THEN ( SELECT 'toast for ' || relname::regclass FROM pg_class pcm WHERE pcm.reltoastrelid = pc.oid) ELSE relname::regclass::text END COLLATE "C" AS relname FROM pg_class AS pc, pg_am AS pa WHERE pa.oid = pc.relam AND pa.amname = 'heap2' ORDER BY 3, 1, 2; -- Show dependencies onto AM - there shouldn't be any for toast SELECT pg_describe_object(classid, objid, objsubid) AS obj FROM pg_depend, pg_am WHERE pg_depend.refclassid = 'pg_am'::regclass AND pg_am.oid = pg_depend.refobjid AND pg_am.amname = 'heap2' ORDER BY classid, objid, objsubid; -- Second, create objects in the new AM by changing the default AM BEGIN; SET LOCAL default_table_access_method = 'heap2'; -- following tests should all respect the default AM CREATE TABLE tableam_tbl_heapx ( f1 int ); CREATE TABLE tableam_tblas_heapx AS SELECT * FROM tableam_tbl_heapx; SELECT INTO tableam_tblselectinto_heapx FROM tableam_tbl_heapx; CREATE MATERIALIZED VIEW tableam_tblmv_heapx USING heap2 AS SELECT * FROM tableam_tbl_heapx; CREATE TABLE tableam_parted_heapx ( a text, b int ) PARTITION BY LIST (a); CREATE TABLE tableam_parted_1_heapx PARTITION OF tableam_parted_heapx FOR VALUES IN ('a', 'b'); -- but an explicitly set AM overrides it CREATE TABLE tableam_parted_2_heapx PARTITION OF tableam_parted_heapx FOR VALUES IN ('c', 'd') USING heap; -- sequences, views and foreign servers shouldn't have an AM CREATE VIEW tableam_view_heapx AS SELECT * FROM tableam_tbl_heapx; CREATE SEQUENCE tableam_seq_heapx; CREATE FOREIGN DATA WRAPPER fdw_heap2 VALIDATOR postgresql_fdw_validator; CREATE SERVER fs_heap2 FOREIGN DATA WRAPPER fdw_heap2; CREATE FOREIGN TABLE tableam_fdw_heapx () SERVER fs_heap2; -- Verify that new AM was used for tables, matviews, but not for sequences, views and fdws SELECT pc.relkind, pa.amname, CASE WHEN relkind = 't' THEN ( SELECT 'toast for ' || relname::regclass FROM pg_class pcm WHERE pcm.reltoastrelid = pc.oid) ELSE relname::regclass::text END COLLATE "C" AS relname FROM pg_class AS pc LEFT JOIN pg_am AS pa ON (pa.oid = pc.relam) WHERE pc.relname LIKE 'tableam_%_heapx' ORDER BY 3, 1, 2; -- don't want to keep those tables, nor the default ROLLBACK; -- Third, check that we can neither create a table using a nonexistant -- AM, nor using an index AM CREATE TABLE i_am_a_failure () USING ""; CREATE TABLE i_am_a_failure () USING i_do_not_exist_am; CREATE TABLE i_am_a_failure () USING "I do not exist AM"; CREATE TABLE i_am_a_failure () USING "btree"; -- Drop table access method, which fails as objects depends on it DROP ACCESS METHOD heap2; -- we intentionally leave the objects created above alive, to verify pg_dump support pgFormatter-4.2/t/pg-test-files/expected/create_cast.sql000066400000000000000000000032771361326045100234100ustar00rootroot00000000000000-- -- CREATE_CAST -- -- Create some types to test with CREATE TYPE casttesttype; CREATE FUNCTION casttesttype_in (cstring) RETURNS casttesttype AS 'textin' LANGUAGE internal STRICT IMMUTABLE; CREATE FUNCTION casttesttype_out (casttesttype) RETURNS cstring AS 'textout' LANGUAGE internal STRICT IMMUTABLE; CREATE TYPE casttesttype ( internallength = variable, input = casttesttype_in, output = casttesttype_out, alignment = int4 ); -- a dummy function to test with CREATE FUNCTION casttestfunc (casttesttype) RETURNS int4 LANGUAGE SQL AS $$ SELECT 1; $$; SELECT casttestfunc ('foo'::text); -- fails, as there's no cast -- Try binary coercion cast CREATE CAST( text AS casttesttype) WITHOUT FUNCTION; SELECT casttestfunc ('foo'::text); -- doesn't work, as the cast is explicit SELECT casttestfunc ('foo'::text::casttesttype); -- should work DROP CAST(text AS casttesttype); -- cleanup -- Try IMPLICIT binary coercion cast CREATE CAST( text AS casttesttype) WITHOUT FUNCTION AS IMPLICIT ; SELECT casttestfunc ('foo'::text); -- Should work now -- Try I/O conversion cast. SELECT 1234::int4::casttesttype; -- No cast yet, should fail CREATE CAST( int4 AS casttesttype ) WITH INOUT; SELECT 1234::int4::casttesttype; -- Should work now DROP CAST(int4 AS casttesttype); -- Try cast with a function CREATE FUNCTION int4_casttesttype (int4) RETURNS casttesttype LANGUAGE SQL AS $$ SELECT ( 'foo' ::text || $1 ::text)::casttesttype; $$; CREATE CAST( int4 AS casttesttype ) WITH FUNCTION int4_casttesttype (int4 ) AS IMPLICIT; SELECT 1234::int4::casttesttype; -- Should work now pgFormatter-4.2/t/pg-test-files/expected/create_function_3.sql000066400000000000000000000176711361326045100245300ustar00rootroot00000000000000-- -- CREATE FUNCTION -- -- Assorted tests using SQL-language functions -- -- All objects made in this test are in temp_func_test schema CREATE USER regress_unpriv_user; CREATE SCHEMA temp_func_test; GRANT ALL ON SCHEMA temp_func_test TO public; SET search_path TO temp_func_test, public; -- -- Make sanity checks on the pg_proc entries created by CREATE FUNCTION -- -- -- ARGUMENT and RETURN TYPES -- CREATE FUNCTION functest_A_1 (text, date) RETURNS bool LANGUAGE 'sql' AS 'SELECT $1 = ''abcd'' AND $2 > ''2001-01-01''' ; CREATE FUNCTION functest_A_2 (text[]) RETURNS int LANGUAGE 'sql' AS 'SELECT $1[0]::int' ; CREATE FUNCTION functest_A_3 () RETURNS bool LANGUAGE 'sql' AS 'SELECT false' ; SELECT proname, prorettype::regtype, proargtypes::regtype[] FROM pg_proc WHERE oid IN ('functest_A_1'::regproc, 'functest_A_2'::regproc, 'functest_A_3'::regproc) ORDER BY proname; -- -- IMMUTABLE | STABLE | VOLATILE -- CREATE FUNCTION functest_B_1 (int) RETURNS bool LANGUAGE 'sql' AS 'SELECT $1 > 0' ; CREATE FUNCTION functest_B_2 (int) RETURNS bool LANGUAGE 'sql' IMMUTABLE AS 'SELECT $1 > 0' ; CREATE FUNCTION functest_B_3 (int) RETURNS bool LANGUAGE 'sql' STABLE AS 'SELECT $1 = 0' ; CREATE FUNCTION functest_B_4 (int) RETURNS bool LANGUAGE 'sql' VOLATILE AS 'SELECT $1 < 0' ; SELECT proname, provolatile FROM pg_proc WHERE oid IN ('functest_B_1'::regproc, 'functest_B_2'::regproc, 'functest_B_3'::regproc, 'functest_B_4'::regproc) ORDER BY proname; ALTER FUNCTION functest_B_2 (int) VOLATILE; ALTER FUNCTION functest_B_3 (int) COST 100; -- unrelated change, no effect SELECT proname, provolatile FROM pg_proc WHERE oid IN ('functest_B_1'::regproc, 'functest_B_2'::regproc, 'functest_B_3'::regproc, 'functest_B_4'::regproc) ORDER BY proname; -- -- SECURITY DEFINER | INVOKER -- CREATE FUNCTION functest_C_1 (int) RETURNS bool LANGUAGE 'sql' AS 'SELECT $1 > 0' ; CREATE FUNCTION functest_C_2 (int) RETURNS bool LANGUAGE 'sql' SECURITY DEFINER AS 'SELECT $1 = 0' ; CREATE FUNCTION functest_C_3 (int) RETURNS bool LANGUAGE 'sql' SECURITY INVOKER AS 'SELECT $1 < 0' ; SELECT proname, prosecdef FROM pg_proc WHERE oid IN ('functest_C_1'::regproc, 'functest_C_2'::regproc, 'functest_C_3'::regproc) ORDER BY proname; ALTER FUNCTION functest_C_1 (int) IMMUTABLE; -- unrelated change, no effect ALTER FUNCTION functest_C_2 (int) SECURITY INVOKER; ALTER FUNCTION functest_C_3 (int) SECURITY DEFINER; SELECT proname, prosecdef FROM pg_proc WHERE oid IN ('functest_C_1'::regproc, 'functest_C_2'::regproc, 'functest_C_3'::regproc) ORDER BY proname; -- -- LEAKPROOF -- CREATE FUNCTION functest_E_1 (int) RETURNS bool LANGUAGE 'sql' AS 'SELECT $1 > 100' ; CREATE FUNCTION functest_E_2 (int) RETURNS bool LANGUAGE 'sql' LEAKPROOF AS 'SELECT $1 > 100' ; SELECT proname, proleakproof FROM pg_proc WHERE oid IN ('functest_E_1'::regproc, 'functest_E_2'::regproc) ORDER BY proname; ALTER FUNCTION functest_E_1 (int) LEAKPROOF; ALTER FUNCTION functest_E_2 (int) STABLE; -- unrelated change, no effect SELECT proname, proleakproof FROM pg_proc WHERE oid IN ('functest_E_1'::regproc, 'functest_E_2'::regproc) ORDER BY proname; ALTER FUNCTION functest_E_2 (int) NOT LEAKPROOF; -- remove leakproof attribute SELECT proname, proleakproof FROM pg_proc WHERE oid IN ('functest_E_1'::regproc, 'functest_E_2'::regproc) ORDER BY proname; -- it takes superuser privilege to turn on leakproof, but not to turn off ALTER FUNCTION functest_E_1 (int) OWNER TO regress_unpriv_user; ALTER FUNCTION functest_E_2 (int) OWNER TO regress_unpriv_user; SET SESSION AUTHORIZATION regress_unpriv_user; SET search_path TO temp_func_test, public; ALTER FUNCTION functest_E_1 (int) NOT LEAKPROOF; ALTER FUNCTION functest_E_2 (int) LEAKPROOF; CREATE FUNCTION functest_E_3 (int) RETURNS bool LANGUAGE 'sql' LEAKPROOF AS 'SELECT $1 < 200' ; -- fail RESET SESSION AUTHORIZATION; -- -- CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT -- CREATE FUNCTION functest_F_1 (int) RETURNS bool LANGUAGE 'sql' AS 'SELECT $1 > 50' ; CREATE FUNCTION functest_F_2 (int) RETURNS bool LANGUAGE 'sql' CALLED ON NULL INPUT AS 'SELECT $1 = 50' ; CREATE FUNCTION functest_F_3 (int) RETURNS bool LANGUAGE 'sql' RETURNS NULL ON NULL INPUT AS 'SELECT $1 < 50' ; CREATE FUNCTION functest_F_4 (int) RETURNS bool LANGUAGE 'sql' STRICT AS 'SELECT $1 = 50' ; SELECT proname, proisstrict FROM pg_proc WHERE oid IN ('functest_F_1'::regproc, 'functest_F_2'::regproc, 'functest_F_3'::regproc, 'functest_F_4'::regproc) ORDER BY proname; ALTER FUNCTION functest_F_1 (int) IMMUTABLE; -- unrelated change, no effect ALTER FUNCTION functest_F_2 (int) STRICT; ALTER FUNCTION functest_F_3 (int) CALLED ON NULL INPUT; SELECT proname, proisstrict FROM pg_proc WHERE oid IN ('functest_F_1'::regproc, 'functest_F_2'::regproc, 'functest_F_3'::regproc, 'functest_F_4'::regproc) ORDER BY proname; -- pg_get_functiondef tests SELECT pg_get_functiondef('functest_A_1'::regproc); SELECT pg_get_functiondef('functest_B_3'::regproc); SELECT pg_get_functiondef('functest_C_3'::regproc); SELECT pg_get_functiondef('functest_F_2'::regproc); -- information_schema tests CREATE FUNCTION functest_IS_1 (a int, b int DEFAULT 1, c text DEFAULT 'foo') RETURNS int LANGUAGE SQL AS 'SELECT $1 + $2' ; CREATE FUNCTION functest_IS_2 (out a int, b int DEFAULT 1) RETURNS int LANGUAGE SQL AS 'SELECT $1' ; CREATE FUNCTION functest_IS_3 (a int DEFAULT 1, out b int) RETURNS int LANGUAGE SQL AS 'SELECT $1' ; SELECT routine_name, ordinal_position, parameter_name, parameter_default FROM information_schema.parameters JOIN information_schema.routines USING (specific_schema, specific_name) WHERE routine_schema = 'temp_func_test' AND routine_name ~ '^functest_is_' ORDER BY 1, 2; DROP FUNCTION functest_IS_1 (int, int, text), functest_IS_2 (int), functest_IS_3 (int); -- overload CREATE FUNCTION functest_B_2 (bigint) RETURNS bool LANGUAGE 'sql' IMMUTABLE AS 'SELECT $1 > 0' ; DROP FUNCTION functest_b_1; DROP FUNCTION functest_b_1; -- error, not found DROP FUNCTION functest_b_2; -- error, ambiguous -- CREATE OR REPLACE tests CREATE FUNCTION functest1 (a int) RETURNS int LANGUAGE SQL AS 'SELECT $1' ; CREATE OR REPLACE FUNCTION functest1 (a int) RETURNS int LANGUAGE SQL WINDOW AS 'SELECT $1' ; CREATE OR REPLACE PROCEDURE functest1 (a int) LANGUAGE SQL AS 'SELECT $1' ; DROP FUNCTION functest1 (a int); -- Check behavior of VOID-returning SQL functions CREATE FUNCTION voidtest1 (a int) RETURNS VOID LANGUAGE SQL AS $$ SELECT a + 1 $$; SELECT voidtest1 (42); CREATE FUNCTION voidtest2 (a int, b int) RETURNS VOID LANGUAGE SQL AS $$ SELECT voidtest1 (a + b) $$; SELECT voidtest2 (11, 22); -- currently, we can inline voidtest2 but not voidtest1 EXPLAIN ( VERBOSE, COSTS OFF ) SELECT voidtest2 (11, 22); CREATE TEMP TABLE sometable ( f1 int ); CREATE FUNCTION voidtest3 (a int) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO sometable VALUES (a + 1) $$; SELECT voidtest3 (17); CREATE FUNCTION voidtest4 (a int) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO sometable VALUES (a - 1) RETURNING f1 $$; SELECT voidtest4 (39); TABLE sometable; CREATE FUNCTION voidtest5 (a int) RETURNS SETOF VOID LANGUAGE SQL AS $$ SELECT generate_series(1, a) $$ STABLE; SELECT * FROM voidtest5 (3); -- Cleanup DROP SCHEMA temp_func_test CASCADE; DROP USER regress_unpriv_user; RESET search_path; pgFormatter-4.2/t/pg-test-files/expected/create_index.sql000066400000000000000000001114621361326045100235610ustar00rootroot00000000000000-- -- CREATE_INDEX -- Create ancillary data structures (i.e. indices) -- -- -- BTREE -- CREATE INDEX onek_unique1 ON onek USING btree (unique1 int4_ops); CREATE INDEX IF NOT EXISTS onek_unique1 ON onek USING btree (unique1 int4_ops); CREATE INDEX IF NOT EXISTS ON onek USING btree (unique1 int4_ops); CREATE INDEX onek_unique2 ON onek USING btree (unique2 int4_ops); CREATE INDEX onek_hundred ON onek USING btree (hundred int4_ops); CREATE INDEX onek_stringu1 ON onek USING btree (stringu1 name_ops); CREATE INDEX tenk1_unique1 ON tenk1 USING btree (unique1 int4_ops); CREATE INDEX tenk1_unique2 ON tenk1 USING btree (unique2 int4_ops); CREATE INDEX tenk1_hundred ON tenk1 USING btree (hundred int4_ops); CREATE INDEX tenk1_thous_tenthous ON tenk1 (thousand, tenthous); CREATE INDEX tenk2_unique1 ON tenk2 USING btree (unique1 int4_ops); CREATE INDEX tenk2_unique2 ON tenk2 USING btree (unique2 int4_ops); CREATE INDEX tenk2_hundred ON tenk2 USING btree (hundred int4_ops); CREATE INDEX rix ON road USING btree (name text_ops); CREATE INDEX iix ON ihighway USING btree (name text_ops); CREATE INDEX six ON shighway USING btree (name text_ops); -- test comments COMMENT ON INDEX six_wrong IS 'bad index'; COMMENT ON INDEX six IS 'good index'; COMMENT ON INDEX six IS NULL; -- -- BTREE ascending/descending cases -- -- we load int4/text from pure descending data (each key is a new -- low key) and name/f8 from pure ascending data (each key is a new -- high key). we had a bug where new low keys would sometimes be -- "lost". -- CREATE INDEX bt_i4_index ON bt_i4_heap USING btree (seqno int4_ops); CREATE INDEX bt_name_index ON bt_name_heap USING btree (seqno name_ops); CREATE INDEX bt_txt_index ON bt_txt_heap USING btree (seqno text_ops); CREATE INDEX bt_f8_index ON bt_f8_heap USING btree (seqno float8_ops); -- -- BTREE partial indices -- CREATE INDEX onek2_u1_prtl ON onek2 USING btree (unique1 int4_ops) WHERE unique1 < 20 OR unique1 > 980; CREATE INDEX onek2_u2_prtl ON onek2 USING btree (unique2 int4_ops) WHERE stringu1 < 'B'; CREATE INDEX onek2_stu1_prtl ON onek2 USING btree (stringu1 name_ops) WHERE onek2.stringu1 >= 'J' AND onek2.stringu1 < 'K'; -- -- GiST (rtree-equivalent opclasses only) -- CREATE INDEX grect2ind ON fast_emp4000 USING gist (home_base); CREATE INDEX gpolygonind ON polygon_tbl USING gist (f1); CREATE INDEX gcircleind ON circle_tbl USING gist (f1); INSERT INTO POINT_TBL (f1) VALUES (NULL); CREATE INDEX gpointind ON point_tbl USING gist (f1); CREATE TEMP TABLE gpolygon_tbl AS SELECT polygon(home_base) AS f1 FROM slow_emp4000; INSERT INTO gpolygon_tbl VALUES ('(1000,0,0,1000)'); INSERT INTO gpolygon_tbl VALUES ('(0,1000,1000,1000)'); CREATE TEMP TABLE gcircle_tbl AS SELECT circle(home_base) AS f1 FROM slow_emp4000; CREATE INDEX ggpolygonind ON gpolygon_tbl USING gist (f1); CREATE INDEX ggcircleind ON gcircle_tbl USING gist (f1); -- -- Test GiST indexes -- -- get non-indexed results for comparison purposes SET enable_seqscan = ON; SET enable_indexscan = OFF; SET enable_bitmapscan = OFF; SELECT * FROM fast_emp4000 WHERE home_base @ '(200,200),(2000,1000)'::box ORDER BY (home_base[0])[0]; SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box; SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon ORDER BY (poly_center(f1))[0]; SELECT * FROM circle_tbl WHERE f1 && circle(point(1, - 2), 1) ORDER BY area(f1); SELECT count(*) FROM gpolygon_tbl WHERE f1 && '(1000,1000,0,0)'::polygon; SELECT count(*) FROM gcircle_tbl WHERE f1 && '<(500,500),500>'::circle; SELECT count(*) FROM point_tbl WHERE f1 <@ box '(0,0,100,100)'; SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1; SELECT count(*) FROM point_tbl WHERE f1 <@ polygon '(0,0),(0,100),(100,100),(50,50),(100,0),(0,0)'; SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>'; SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)'; SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)'; SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)'; SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)'; SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)'; SELECT * FROM point_tbl ORDER BY f1 <-> '0,1'; SELECT * FROM point_tbl WHERE f1 IS NULL; SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1'; SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)'::box ORDER BY f1 <-> '0,1'; SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10; SELECT circle_center(f1), round(radius(f1)) AS radius FROM gcircle_tbl ORDER BY f1 <-> '(200,300)'::point LIMIT 10; -- Now check the results from plain indexscan SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = OFF; EXPLAIN ( COSTS OFF ) SELECT * FROM fast_emp4000 WHERE home_base @ '(200,200),(2000,1000)'::box ORDER BY (home_base[0])[0]; SELECT * FROM fast_emp4000 WHERE home_base @ '(200,200),(2000,1000)'::box ORDER BY (home_base[0])[0]; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box; SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon ORDER BY (poly_center(f1))[0]; SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon ORDER BY (poly_center(f1))[0]; EXPLAIN ( COSTS OFF ) SELECT * FROM circle_tbl WHERE f1 && circle(point(1, - 2), 1) ORDER BY area(f1); SELECT * FROM circle_tbl WHERE f1 && circle(point(1, - 2), 1) ORDER BY area(f1); EXPLAIN ( COSTS OFF ) SELECT count(*) FROM gpolygon_tbl WHERE f1 && '(1000,1000,0,0)'::polygon; SELECT count(*) FROM gpolygon_tbl WHERE f1 && '(1000,1000,0,0)'::polygon; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM gcircle_tbl WHERE f1 && '<(500,500),500>'::circle; SELECT count(*) FROM gcircle_tbl WHERE f1 && '<(500,500),500>'::circle; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM point_tbl WHERE f1 <@ box '(0,0,100,100)'; SELECT count(*) FROM point_tbl WHERE f1 <@ box '(0,0,100,100)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1; SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM point_tbl WHERE f1 <@ polygon '(0,0),(0,100),(100,100),(50,50),(100,0),(0,0)'; SELECT count(*) FROM point_tbl WHERE f1 <@ polygon '(0,0),(0,100),(100,100),(50,50),(100,0),(0,0)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>'; SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)'; SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)'; SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)'; SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)'; SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)'; SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)'; EXPLAIN ( COSTS OFF ) SELECT * FROM point_tbl ORDER BY f1 <-> '0,1'; SELECT * FROM point_tbl ORDER BY f1 <-> '0,1'; EXPLAIN ( COSTS OFF ) SELECT * FROM point_tbl WHERE f1 IS NULL; SELECT * FROM point_tbl WHERE f1 IS NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1'; SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1'; EXPLAIN ( COSTS OFF ) SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)'::box ORDER BY f1 <-> '0,1'; SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)'::box ORDER BY f1 <-> '0,1'; EXPLAIN ( COSTS OFF ) SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10; SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10; EXPLAIN ( COSTS OFF ) SELECT circle_center(f1), round(radius(f1)) AS radius FROM gcircle_tbl ORDER BY f1 <-> '(200,300)'::point LIMIT 10; SELECT circle_center(f1), round(radius(f1)) AS radius FROM gcircle_tbl ORDER BY f1 <-> '(200,300)'::point LIMIT 10; -- Now check the results from bitmap indexscan SET enable_seqscan = OFF; SET enable_indexscan = OFF; SET enable_bitmapscan = ON; EXPLAIN ( COSTS OFF ) SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)'::box ORDER BY f1 <-> '0,1'; SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)'::box ORDER BY f1 <-> '0,1'; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; -- -- GIN over int[] and text[] -- -- Note: GIN currently supports only bitmap scans, not plain indexscans -- SET enable_seqscan = OFF; SET enable_indexscan = OFF; SET enable_bitmapscan = ON; CREATE INDEX intarrayidx ON array_index_op_test USING gin (i); EXPLAIN ( COSTS OFF ) SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i && '{32}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i @> '{17}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i && '{17}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i @> '{32,17}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i && '{32,17}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i <@ '{38,34,32,89}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i = '{47,77}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i = '{}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i @> '{}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i && '{}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i <@ '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno; CREATE INDEX textarrayidx ON array_index_op_test USING gin (t); EXPLAIN ( COSTS OFF ) SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t && '{AAAAAAAAAA646}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t && '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t <@ '{AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t = '{AAAAAAAAAA646,A87088}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t = '{}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t @> '{}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t && '{}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t <@ '{}' ORDER BY seqno; -- And try it with a multicolumn GIN index DROP INDEX intarrayidx, textarrayidx; CREATE INDEX botharrayidx ON array_index_op_test USING gin (i, t); SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i && '{32}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAA80240}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t && '{AAAAAAA80240}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i @> '{32}' AND t && '{AAAAAAA80240}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i && '{32}' AND t @> '{AAAAAAA80240}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t = '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; -- -- Try a GIN index with a lot of items with same key. (GIN creates a posting -- tree when there are enough duplicates) -- CREATE TABLE array_gin_test ( a int[] ); INSERT INTO array_gin_test SELECT ARRAY[1, g % 5, g] FROM generate_series(1, 10000) g; CREATE INDEX array_gin_test_idx ON array_gin_test USING gin (a); SELECT COUNT(*) FROM array_gin_test WHERE a @> '{2}'; DROP TABLE array_gin_test; -- -- Test GIN index's reloptions -- CREATE INDEX gin_relopts_test ON array_index_op_test USING gin (i) WITH (FASTUPDATE = ON, GIN_PENDING_LIST_LIMIT = 128); \d+ gin_relopts_test -- -- HASH -- CREATE INDEX hash_i4_index ON hash_i4_heap USING HASH (random int4_ops); CREATE INDEX hash_name_index ON hash_name_heap USING HASH (random name_ops); CREATE INDEX hash_txt_index ON hash_txt_heap USING HASH (random text_ops); CREATE INDEX hash_f8_index ON hash_f8_heap USING HASH (random float8_ops) WITH (fillfactor = 60); CREATE UNLOGGED TABLE unlogged_hash_table ( id int4 ); CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING HASH (id int4_ops); DROP TABLE unlogged_hash_table; -- CREATE INDEX hash_ovfl_index ON hash_ovfl_heap USING hash (x int4_ops); -- Test hash index build tuplesorting. Force hash tuplesort using low -- maintenance_work_mem setting and fillfactor: SET maintenance_work_mem = '1MB'; CREATE INDEX hash_tuplesort_idx ON tenk1 USING HASH (stringu1 name_ops) WITH (fillfactor = 10); EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1 WHERE stringu1 = 'TVAAAA'; SELECT count(*) FROM tenk1 WHERE stringu1 = 'TVAAAA'; DROP INDEX hash_tuplesort_idx; RESET maintenance_work_mem; -- -- Test functional index -- CREATE TABLE func_index_heap ( f1 text, f2 text ); CREATE UNIQUE INDEX func_index_index ON func_index_heap (textcat(f1, f2)); INSERT INTO func_index_heap VALUES ('ABC', 'DEF'); INSERT INTO func_index_heap VALUES ('AB', 'CDEFG'); INSERT INTO func_index_heap VALUES ('QWE', 'RTY'); -- this should fail because of unique index: INSERT INTO func_index_heap VALUES ('ABCD', 'EF'); -- but this shouldn't: INSERT INTO func_index_heap VALUES ('QWERTY'); -- -- Same test, expressional index -- DROP TABLE func_index_heap; CREATE TABLE func_index_heap ( f1 text, f2 text ); CREATE UNIQUE INDEX func_index_index ON func_index_heap ((f1 || f2) text_ops); INSERT INTO func_index_heap VALUES ('ABC', 'DEF'); INSERT INTO func_index_heap VALUES ('AB', 'CDEFG'); INSERT INTO func_index_heap VALUES ('QWE', 'RTY'); -- this should fail because of unique index: INSERT INTO func_index_heap VALUES ('ABCD', 'EF'); -- but this shouldn't: INSERT INTO func_index_heap VALUES ('QWERTY'); -- -- Test unique index with included columns -- CREATE TABLE covering_index_heap ( f1 int, f2 int, f3 text ); CREATE UNIQUE INDEX covering_index_index ON covering_index_heap (f1, f2) INCLUDE (f3); INSERT INTO covering_index_heap VALUES (1, 1, 'AAA'); INSERT INTO covering_index_heap VALUES (1, 2, 'AAA'); -- this should fail because of unique index on f1,f2: INSERT INTO covering_index_heap VALUES (1, 2, 'BBB'); -- and this shouldn't: INSERT INTO covering_index_heap VALUES (1, 4, 'AAA'); -- Try to build index on table that already contains data CREATE UNIQUE INDEX covering_pkey ON covering_index_heap (f1, f2) INCLUDE (f3); -- Try to use existing covering index as primary key ALTER TABLE covering_index_heap ADD CONSTRAINT covering_pkey PRIMARY KEY USING INDEX covering_pkey; DROP TABLE covering_index_heap; -- -- Also try building functional, expressional, and partial indexes on -- tables that already contain data. -- CREATE UNIQUE INDEX hash_f8_index_1 ON hash_f8_heap (abs(random)); CREATE UNIQUE INDEX hash_f8_index_2 ON hash_f8_heap ((seqno + 1), random); CREATE UNIQUE INDEX hash_f8_index_3 ON hash_f8_heap (random) WHERE seqno > 1000; -- -- Try some concurrent index builds -- -- Unfortunately this only tests about half the code paths because there are -- no concurrent updates happening to the table at the same time. CREATE TABLE concur_heap ( f1 text, f2 text ); -- empty table CREATE INDEX CONCURRENTLY concur_index1 ON concur_heap (f2, f1); CREATE INDEX CONCURRENTLY IF NOT EXISTS concur_index1 ON concur_heap (f2, f1); INSERT INTO concur_heap VALUES ('a', 'b'); INSERT INTO concur_heap VALUES ('b', 'b'); -- unique index CREATE UNIQUE INDEX CONCURRENTLY concur_index2 ON concur_heap (f1); CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS concur_index2 ON concur_heap (f1); -- check if constraint is set up properly to be enforced INSERT INTO concur_heap VALUES ('b', 'x'); -- check if constraint is enforced properly at build time CREATE UNIQUE INDEX CONCURRENTLY concur_index3 ON concur_heap (f2); -- test that expression indexes and partial indexes work concurrently CREATE INDEX CONCURRENTLY concur_index4 ON concur_heap (f2) WHERE f1 = 'a'; CREATE INDEX CONCURRENTLY concur_index5 ON concur_heap (f2) WHERE f1 = 'x'; -- here we also check that you can default the index name CREATE INDEX CONCURRENTLY ON concur_heap ((f2 || f1)); -- You can't do a concurrent index build in a transaction BEGIN; CREATE INDEX CONCURRENTLY concur_index7 ON concur_heap (f1); COMMIT; -- But you can do a regular index build in a transaction BEGIN; CREATE INDEX std_index ON concur_heap (f2); COMMIT; -- Failed builds are left invalid by VACUUM FULL, fixed by REINDEX VACUUM FULL concur_heap; REINDEX TABLE concur_heap; DELETE FROM concur_heap WHERE f1 = 'b'; VACUUM FULL concur_heap; \d concur_heap REINDEX TABLE concur_heap; \d concur_heap -- -- Try some concurrent index drops -- DROP INDEX CONCURRENTLY "concur_index2"; -- works DROP INDEX CONCURRENTLY IF EXISTS "concur_index2"; -- notice -- failures DROP INDEX CONCURRENTLY "concur_index2", "concur_index3"; BEGIN; DROP INDEX CONCURRENTLY "concur_index5"; ROLLBACK; -- successes DROP INDEX CONCURRENTLY IF EXISTS "concur_index3"; DROP INDEX CONCURRENTLY "concur_index4"; DROP INDEX CONCURRENTLY "concur_index5"; DROP INDEX CONCURRENTLY "concur_index1"; DROP INDEX CONCURRENTLY "concur_heap_expr_idx"; \d concur_heap DROP TABLE concur_heap; -- -- Test ADD CONSTRAINT USING INDEX -- CREATE TABLE cwi_test ( a int, b varchar(10), c char ); -- add some data so that all tests have something to work with. INSERT INTO cwi_test VALUES (1, 2), (3, 4), (5, 6); CREATE UNIQUE INDEX cwi_uniq_idx ON cwi_test (a, b); ALTER TABLE cwi_test ADD PRIMARY KEY USING INDEX cwi_uniq_idx; \d cwi_test \d cwi_uniq_idx CREATE UNIQUE INDEX cwi_uniq2_idx ON cwi_test (b, a); ALTER TABLE cwi_test DROP CONSTRAINT cwi_uniq_idx, ADD CONSTRAINT cwi_replaced_pkey PRIMARY KEY USING INDEX cwi_uniq2_idx; \d cwi_test \d cwi_replaced_pkey DROP INDEX cwi_replaced_pkey; -- Should fail; a constraint depends on it DROP TABLE cwi_test; -- ADD CONSTRAINT USING INDEX is forbidden on partitioned tables CREATE TABLE cwi_test ( a int ) PARTITION BY HASH (a); CREATE UNIQUE INDEX ON cwi_test (a); ALTER TABLE cwi_test ADD PRIMARY KEY USING INDEX cwi_test_a_idx; DROP TABLE cwi_test; -- -- Check handling of indexes on system columns -- CREATE TABLE syscol_table ( a int ); -- System columns cannot be indexed CREATE INDEX ON syscolcol_table (ctid); -- nor used in expressions CREATE INDEX ON syscol_table ((ctid >= '(1000,0)')); -- nor used in predicates CREATE INDEX ON syscol_table (a) WHERE ctid >= '(1000,0)'; DROP TABLE syscol_table; -- -- Tests for IS NULL/IS NOT NULL with b-tree indexes -- SELECT unique1, unique2 INTO onek_with_null FROM onek; INSERT INTO onek_with_null (unique1, unique2) VALUES (NULL, - 1), (NULL, NULL); CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2, unique1); SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = ON; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NOT NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL AND unique1 > 500; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique1 > 500; DROP INDEX onek_nulltest; CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2 DESC, unique1); SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NOT NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL AND unique1 > 500; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique1 > 500; DROP INDEX onek_nulltest; CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2 DESC nulls LAST, unique1); SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NOT NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL AND unique1 > 500; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique1 > 500; DROP INDEX onek_nulltest; CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2 nulls FIRST, unique1); SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NOT NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL AND unique1 > 500; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique1 > 500; DROP INDEX onek_nulltest; -- Check initial-positioning logic too CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2); SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = OFF; SELECT unique1, unique2 FROM onek_with_null ORDER BY unique2 LIMIT 2; SELECT unique1, unique2 FROM onek_with_null WHERE unique2 >= - 1 ORDER BY unique2 LIMIT 2; SELECT unique1, unique2 FROM onek_with_null WHERE unique2 >= 0 ORDER BY unique2 LIMIT 2; SELECT unique1, unique2 FROM onek_with_null ORDER BY unique2 DESC LIMIT 2; SELECT unique1, unique2 FROM onek_with_null WHERE unique2 >= - 1 ORDER BY unique2 DESC LIMIT 2; SELECT unique1, unique2 FROM onek_with_null WHERE unique2 < 999 ORDER BY unique2 DESC LIMIT 2; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; DROP TABLE onek_with_null; -- -- Check bitmap index path planning -- EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1 WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42); SELECT * FROM tenk1 WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42); EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1 WHERE hundred = 42 AND (thousand = 42 OR thousand = 99); SELECT count(*) FROM tenk1 WHERE hundred = 42 AND (thousand = 42 OR thousand = 99); -- -- Check behavior with duplicate index column contents -- CREATE TABLE dupindexcols AS SELECT unique1 AS id, stringu2::text AS f1 FROM tenk1; CREATE INDEX dupindexcols_i ON dupindexcols (f1, id, f1 text_pattern_ops); ANALYZE dupindexcols; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM dupindexcols WHERE f1 BETWEEN 'WA' AND 'ZZZ' AND id < 1000 AND f1 ~<~ 'YX'; SELECT count(*) FROM dupindexcols WHERE f1 BETWEEN 'WA' AND 'ZZZ' AND id < 1000 AND f1 ~<~ 'YX'; -- -- Check ordering of =ANY indexqual results (bug in 9.2.0) -- VACUUM tenk1; -- ensure we get consistent plans here EXPLAIN ( COSTS OFF ) SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) ORDER BY unique1; SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) ORDER BY unique1; EXPLAIN ( COSTS OFF ) SELECT thousand, tenthous FROM tenk1 WHERE thousand < 2 AND tenthous IN (1001, 3000) ORDER BY thousand; SELECT thousand, tenthous FROM tenk1 WHERE thousand < 2 AND tenthous IN (1001, 3000) ORDER BY thousand; SET enable_indexonlyscan = OFF; EXPLAIN ( COSTS OFF ) SELECT thousand, tenthous FROM tenk1 WHERE thousand < 2 AND tenthous IN (1001, 3000) ORDER BY thousand; SELECT thousand, tenthous FROM tenk1 WHERE thousand < 2 AND tenthous IN (1001, 3000) ORDER BY thousand; RESET enable_indexonlyscan; -- -- Check elimination of constant-NULL subexpressions -- EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1 WHERE (thousand, tenthous) IN ((1, 1001), (NULL, NULL)); -- -- Check matching of boolean index columns to WHERE conditions and sort keys -- CREATE temp TABLE boolindex ( b bool, i int, UNIQUE (b, i), junk float ); EXPLAIN ( COSTS OFF ) SELECT * FROM boolindex ORDER BY b, i LIMIT 10; EXPLAIN ( COSTS OFF ) SELECT * FROM boolindex WHERE b ORDER BY i LIMIT 10; EXPLAIN ( COSTS OFF ) SELECT * FROM boolindex WHERE b = TRUE ORDER BY i DESC LIMIT 10; EXPLAIN ( COSTS OFF ) SELECT * FROM boolindex WHERE NOT b ORDER BY i LIMIT 10; EXPLAIN ( COSTS OFF ) SELECT * FROM boolindex WHERE b IS TRUE ORDER BY i DESC LIMIT 10; EXPLAIN ( COSTS OFF ) SELECT * FROM boolindex WHERE b IS FALSE ORDER BY i DESC LIMIT 10; -- -- REINDEX (VERBOSE) -- CREATE TABLE reindex_verbose ( id integer PRIMARY KEY ); \set VERBOSITY terse -- suppress machine-dependent details REINDEX (VERBOSE) TABLE reindex_verbose; \set VERBOSITY default DROP TABLE reindex_verbose; -- -- REINDEX CONCURRENTLY -- CREATE TABLE concur_reindex_tab ( c1 int ); -- REINDEX REINDEX TABLE concur_reindex_tab; -- notice REINDEX TABLE CONCURRENTLY concur_reindex_tab; -- notice ALTER TABLE concur_reindex_tab ADD COLUMN c2 text; -- add toast index -- Normal index with integer column CREATE UNIQUE INDEX concur_reindex_ind1 ON concur_reindex_tab (c1); -- Normal index with text column CREATE INDEX concur_reindex_ind2 ON concur_reindex_tab (c2); -- UNIQUE index with expression CREATE UNIQUE INDEX concur_reindex_ind3 ON concur_reindex_tab (abs(c1)); -- Duplicate column names CREATE INDEX concur_reindex_ind4 ON concur_reindex_tab (c1, c1, c2); -- Create table for check on foreign key dependence switch with indexes swapped ALTER TABLE concur_reindex_tab ADD PRIMARY KEY USING INDEX concur_reindex_ind1; CREATE TABLE concur_reindex_tab2 ( c1 int REFERENCES concur_reindex_tab ); INSERT INTO concur_reindex_tab VALUES (1, 'a'); INSERT INTO concur_reindex_tab VALUES (2, 'a'); -- Reindex concurrently of exclusion constraint currently not supported CREATE TABLE concur_reindex_tab3 ( c1 int, c2 int4range, EXCLUDE USING gist (c2 WITH &&) ); INSERT INTO concur_reindex_tab3 VALUES (3, '[1,2]'); REINDEX INDEX CONCURRENTLY concur_reindex_tab3_c2_excl; -- error REINDEX TABLE CONCURRENTLY concur_reindex_tab3; -- succeeds with warning INSERT INTO concur_reindex_tab3 VALUES (4, '[2,4]'); -- Check materialized views CREATE MATERIALIZED VIEW concur_reindex_matview AS SELECT * FROM concur_reindex_tab; REINDEX INDEX CONCURRENTLY concur_reindex_ind1; REINDEX TABLE CONCURRENTLY concur_reindex_tab; REINDEX TABLE CONCURRENTLY concur_reindex_matview; -- Check that comments are preserved CREATE TABLE testcomment ( i int ); CREATE INDEX testcomment_idx1 ON testcomment (i); COMMENT ON INDEX testcomment_idx1 IS 'test comment'; SELECT obj_description('testcomment_idx1'::regclass, 'pg_class'); REINDEX TABLE testcomment; SELECT obj_description('testcomment_idx1'::regclass, 'pg_class'); REINDEX TABLE CONCURRENTLY testcomment; SELECT obj_description('testcomment_idx1'::regclass, 'pg_class'); DROP TABLE testcomment; -- Partitions -- Create some partitioned tables CREATE TABLE concur_reindex_part ( c1 int, c2 int ) PARTITION BY RANGE (c1); CREATE TABLE concur_reindex_part_0 PARTITION OF concur_reindex_part FOR VALUES FROM (0) TO (10) PARTITION BY LIST (c2); CREATE TABLE concur_reindex_part_0_1 PARTITION OF concur_reindex_part_0 FOR VALUES IN (1); CREATE TABLE concur_reindex_part_0_2 PARTITION OF concur_reindex_part_0 FOR VALUES IN (2); -- This partitioned table will have no partitions. CREATE TABLE concur_reindex_part_10 PARTITION OF concur_reindex_part FOR VALUES FROM (10) TO (20) PARTITION BY LIST (c2); -- Create some partitioned indexes CREATE INDEX concur_reindex_part_index ON ONLY concur_reindex_part (c1); CREATE INDEX concur_reindex_part_index_0 ON ONLY concur_reindex_part_0 (c1); ALTER INDEX concur_reindex_part_index ATTACH PARTITION concur_reindex_part_index_0; -- This partitioned index will have no partitions. CREATE INDEX concur_reindex_part_index_10 ON ONLY concur_reindex_part_10 (c1); ALTER INDEX concur_reindex_part_index ATTACH PARTITION concur_reindex_part_index_10; CREATE INDEX concur_reindex_part_index_0_1 ON ONLY concur_reindex_part_0_1 (c1); ALTER INDEX concur_reindex_part_index_0 ATTACH PARTITION concur_reindex_part_index_0_1; CREATE INDEX concur_reindex_part_index_0_2 ON ONLY concur_reindex_part_0_2 (c1); ALTER INDEX concur_reindex_part_index_0 ATTACH PARTITION concur_reindex_part_index_0_2; SELECT relid, parentrelid, level FROM pg_partition_tree ('concur_reindex_part_index') ORDER BY relid, level; -- REINDEX fails for partitioned indexes REINDEX INDEX concur_reindex_part_index_10; REINDEX INDEX CONCURRENTLY concur_reindex_part_index_10; -- REINDEX is a no-op for partitioned tables REINDEX TABLE concur_reindex_part_10; REINDEX TABLE CONCURRENTLY concur_reindex_part_10; SELECT relid, parentrelid, level FROM pg_partition_tree ('concur_reindex_part_index') ORDER BY relid, level; -- REINDEX should preserve dependencies of partition tree. REINDEX INDEX CONCURRENTLY concur_reindex_part_index_0_1; REINDEX INDEX CONCURRENTLY concur_reindex_part_index_0_2; SELECT relid, parentrelid, level FROM pg_partition_tree ('concur_reindex_part_index') ORDER BY relid, level; REINDEX TABLE CONCURRENTLY concur_reindex_part_0_1; REINDEX TABLE CONCURRENTLY concur_reindex_part_0_2; SELECT relid, parentrelid, level FROM pg_partition_tree ('concur_reindex_part_index') ORDER BY relid, level; DROP TABLE concur_reindex_part; -- Check errors -- Cannot run inside a transaction block BEGIN; REINDEX TABLE CONCURRENTLY concur_reindex_tab; COMMIT; REINDEX TABLE CONCURRENTLY pg_database; -- no shared relation REINDEX TABLE CONCURRENTLY pg_class; -- no catalog relations REINDEX SYSTEM CONCURRENTLY postgres; -- not allowed for SYSTEM -- Warns about catalog relations REINDEX SCHEMA CONCURRENTLY pg_catalog; -- Check the relation status, there should not be invalid indexes \d concur_reindex_tab DROP MATERIALIZED VIEW concur_reindex_matview; DROP TABLE concur_reindex_tab, concur_reindex_tab2, concur_reindex_tab3; -- Check handling of invalid indexes CREATE TABLE concur_reindex_tab4 ( c1 int ); INSERT INTO concur_reindex_tab4 VALUES (1), (1), (2); -- This trick creates an invalid index. CREATE UNIQUE INDEX CONCURRENTLY concur_reindex_ind5 ON concur_reindex_tab4 (c1); -- Reindexing concurrently this index fails with the same failure. -- The extra index created is itself invalid, and can be dropped. REINDEX INDEX CONCURRENTLY concur_reindex_ind5; \d concur_reindex_tab4 DROP INDEX concur_reindex_ind5_ccnew; -- This makes the previous failure go away, so the index can become valid. DELETE FROM concur_reindex_tab4 WHERE c1 = 1; -- The invalid index is not processed when running REINDEX TABLE. REINDEX TABLE CONCURRENTLY concur_reindex_tab4; \d concur_reindex_tab4 -- But it is fixed with REINDEX INDEX. REINDEX INDEX CONCURRENTLY concur_reindex_ind5; \d concur_reindex_tab4 DROP TABLE concur_reindex_tab4; -- -- REINDEX SCHEMA -- REINDEX SCHEMA schema_to_reindex; -- failure, schema does not exist CREATE SCHEMA schema_to_reindex; SET search_path = 'schema_to_reindex'; CREATE TABLE table1 ( col1 serial PRIMARY KEY ); INSERT INTO table1 SELECT generate_series(1, 400); CREATE TABLE table2 ( col1 serial PRIMARY KEY, col2 text NOT NULL ); INSERT INTO table2 SELECT generate_series(1, 400), 'abc'; CREATE INDEX ON table2 (col2); CREATE MATERIALIZED VIEW matview AS SELECT col1 FROM table2; CREATE INDEX ON matview (col1); CREATE VIEW VIEW AS SELECT col2 FROM table2; CREATE TABLE reindex_before AS SELECT oid, relname, relfilenode, relkind, reltoastrelid FROM pg_class WHERE relnamespace = ( SELECT oid FROM pg_namespace WHERE nspname = 'schema_to_reindex'); INSERT INTO reindex_before SELECT oid, 'pg_toast_TABLE', relfilenode, relkind, reltoastrelid FROM pg_class WHERE oid IN ( SELECT reltoastrelid FROM reindex_before WHERE reltoastrelid > 0); INSERT INTO reindex_before SELECT oid, 'pg_toast_TABLE_index', relfilenode, relkind, reltoastrelid FROM pg_class WHERE oid IN ( SELECT indexrelid FROM pg_index WHERE indrelid IN ( SELECT reltoastrelid FROM reindex_before WHERE reltoastrelid > 0)); REINDEX SCHEMA schema_to_reindex; CREATE TABLE reindex_after AS SELECT oid, relname, relfilenode, relkind FROM pg_class WHERE relnamespace = ( SELECT oid FROM pg_namespace WHERE nspname = 'schema_to_reindex'); SELECT b.relname, b.relkind, CASE WHEN a.relfilenode = b.relfilenode THEN 'relfilenode is unchanged' ELSE 'relfilenode has changed' END FROM reindex_before b JOIN pg_class a ON b.oid = a.oid ORDER BY 1; REINDEX SCHEMA schema_to_reindex; BEGIN; REINDEX SCHEMA schema_to_reindex; -- failure, cannot run in a transaction END; -- concurrently REINDEX SCHEMA CONCURRENTLY schema_to_reindex; -- Failure for unauthorized user CREATE ROLE regress_reindexuser NOLOGIN; SET SESSION ROLE regress_reindexuser; REINDEX SCHEMA schema_to_reindex; -- Clean up RESET ROLE; DROP ROLE regress_reindexuser; DROP SCHEMA schema_to_reindex CASCADE; pgFormatter-4.2/t/pg-test-files/expected/create_index_spgist.sql000066400000000000000000000444201361326045100251510ustar00rootroot00000000000000-- -- SP-GiST index tests -- CREATE TABLE quad_point_tbl AS SELECT point(unique1, unique2) AS p FROM tenk1; INSERT INTO quad_point_tbl SELECT '(333.0,400.0)'::point FROM generate_series(1, 1000); INSERT INTO quad_point_tbl VALUES (NULL), (NULL), (NULL); CREATE INDEX sp_quad_ind ON quad_point_tbl USING spgist (p); CREATE TABLE kd_point_tbl AS SELECT * FROM quad_point_tbl; CREATE INDEX sp_kd_ind ON kd_point_tbl USING spgist (p kd_point_ops); CREATE TABLE radix_text_tbl AS SELECT name AS t FROM road WHERE name !~ '^[0-9]'; INSERT INTO radix_text_tbl SELECT 'P0123456789abcdef' FROM generate_series(1, 1000); INSERT INTO radix_text_tbl VALUES ('P0123456789abcde'); INSERT INTO radix_text_tbl VALUES ('P0123456789abcdefF'); CREATE INDEX sp_radix_ind ON radix_text_tbl USING spgist (t); -- get non-indexed results for comparison purposes SET enable_seqscan = ON; SET enable_indexscan = OFF; SET enable_bitmapscan = OFF; SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; SELECT count(*) FROM quad_point_tbl; SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; CREATE TEMP TABLE quad_point_tbl_ord_seq1 AS SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM quad_point_tbl; CREATE TEMP TABLE quad_point_tbl_ord_seq2 AS SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; CREATE TEMP TABLE quad_point_tbl_ord_seq3 AS SELECT row_number() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p FROM quad_point_tbl WHERE p IS NOT NULL; SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t ^ @ 'Worth'; -- Now check the results from plain indexscan SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = OFF; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl; SELECT count(*) FROM quad_point_tbl; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; EXPLAIN ( COSTS OFF ) SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM quad_point_tbl; CREATE TEMP TABLE quad_point_tbl_ord_idx1 AS SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM quad_point_tbl; SELECT * FROM quad_point_tbl_ord_seq1 seq FULL JOIN quad_point_tbl_ord_idx1 idx ON seq.n = idx.n WHERE seq.dist IS DISTINCT FROM idx.dist; EXPLAIN ( COSTS OFF ) SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; CREATE TEMP TABLE quad_point_tbl_ord_idx2 AS SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; SELECT * FROM quad_point_tbl_ord_seq2 seq FULL JOIN quad_point_tbl_ord_idx2 idx ON seq.n = idx.n WHERE seq.dist IS DISTINCT FROM idx.dist; EXPLAIN ( COSTS OFF ) SELECT row_number() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p FROM quad_point_tbl WHERE p IS NOT NULL; CREATE TEMP TABLE quad_point_tbl_ord_idx3 AS SELECT row_number() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p FROM quad_point_tbl WHERE p IS NOT NULL; SELECT * FROM quad_point_tbl_ord_seq3 seq FULL JOIN quad_point_tbl_ord_idx3 idx ON seq.n = idx.n WHERE seq.dist IS DISTINCT FROM idx.dist; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; EXPLAIN ( COSTS OFF ) SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM kd_point_tbl; CREATE TEMP TABLE kd_point_tbl_ord_idx1 AS SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM kd_point_tbl; SELECT * FROM quad_point_tbl_ord_seq1 seq FULL JOIN kd_point_tbl_ord_idx1 idx ON seq.n = idx.n WHERE seq.dist IS DISTINCT FROM idx.dist; EXPLAIN ( COSTS OFF ) SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; CREATE TEMP TABLE kd_point_tbl_ord_idx2 AS SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; SELECT * FROM quad_point_tbl_ord_seq2 seq FULL JOIN kd_point_tbl_ord_idx2 idx ON seq.n = idx.n WHERE seq.dist IS DISTINCT FROM idx.dist; EXPLAIN ( COSTS OFF ) SELECT row_number() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p FROM kd_point_tbl WHERE p IS NOT NULL; CREATE TEMP TABLE kd_point_tbl_ord_idx3 AS SELECT row_number() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p FROM kd_point_tbl WHERE p IS NOT NULL; SELECT * FROM quad_point_tbl_ord_seq3 seq FULL JOIN kd_point_tbl_ord_idx3 idx ON seq.n = idx.n WHERE seq.dist IS DISTINCT FROM idx.dist; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t ^ @ 'Worth'; SELECT count(*) FROM radix_text_tbl WHERE t ^ @ 'Worth'; -- Now check the results from bitmap indexscan SET enable_seqscan = OFF; SET enable_indexscan = OFF; SET enable_bitmapscan = ON; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl; SELECT count(*) FROM quad_point_tbl; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM radix_text_tbl WHERE t ^ @ 'Worth'; SELECT count(*) FROM radix_text_tbl WHERE t ^ @ 'Worth'; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; pgFormatter-4.2/t/pg-test-files/expected/create_misc.sql000066400000000000000000000126701361326045100234060ustar00rootroot00000000000000-- -- CREATE_MISC -- -- CLASS POPULATION -- (any resemblance to real life is purely coincidental) -- INSERT INTO tenk2 SELECT * FROM tenk1; SELECT * INTO TABLE onek2 FROM onek; INSERT INTO fast_emp4000 SELECT * FROM slow_emp4000; SELECT * INTO TABLE Bprime FROM tenk1 WHERE unique2 < 1000; INSERT INTO hobbies_r (name, person) SELECT 'posthacking', p.name FROM person * p WHERE p.name = 'mike' OR p.name = 'jeff'; INSERT INTO hobbies_r (name, person) SELECT 'basketball', p.name FROM person p WHERE p.name = 'joe' OR p.name = 'sally'; INSERT INTO hobbies_r (name) VALUES ('skywalking'); INSERT INTO equipment_r (name, hobby) VALUES ('advil', 'posthacking'); INSERT INTO equipment_r (name, hobby) VALUES ('peet''s coffee', 'posthacking'); INSERT INTO equipment_r (name, hobby) VALUES ('hightops', 'basketball'); INSERT INTO equipment_r (name, hobby) VALUES ('guts', 'skywalking'); INSERT INTO city VALUES ('Podunk', '(1,2),(3,4)', '100,127,1000'), ('Gotham', '(1000,34),(1100,334)', '123456,127,-1000,6789'); TABLE city; SELECT * INTO TABLE ramp FROM road WHERE name ~ '.*Ramp'; INSERT INTO ihighway SELECT * FROM road WHERE name ~ 'I- .*'; INSERT INTO shighway SELECT * FROM road WHERE name ~ 'State Hwy.*'; UPDATE shighway SET surface = 'asphalt'; INSERT INTO a_star (class, a) VALUES ('a', 1); INSERT INTO a_star (class, a) VALUES ('a', 2); INSERT INTO a_star (class) VALUES ('a'); INSERT INTO b_star (class, a, b) VALUES ('b', 3, 'mumble'::text); INSERT INTO b_star (class, a) VALUES ('b', 4); INSERT INTO b_star (class, b) VALUES ('b', 'bumble'::text); INSERT INTO b_star (class) VALUES ('b'); INSERT INTO c_star (class, a, c) VALUES ('c', 5, 'hi mom'::name); INSERT INTO c_star (class, a) VALUES ('c', 6); INSERT INTO c_star (class, c) VALUES ('c', 'hi paul'::name); INSERT INTO c_star (class) VALUES ('c'); INSERT INTO d_star (class, a, b, c, d) VALUES ('d', 7, 'grumble'::text, 'hi sunita'::name, '0.0'::float8); INSERT INTO d_star (class, a, b, c) VALUES ('d', 8, 'stumble'::text, 'hi koko'::name); INSERT INTO d_star (class, a, b, d) VALUES ('d', 9, 'rumble'::text, '1.1'::float8); INSERT INTO d_star (class, a, c, d) VALUES ('d', 10, 'hi kristin'::name, '10.01'::float8); INSERT INTO d_star (class, b, c, d) VALUES ('d', 'crumble'::text, 'hi boris'::name, '100.001'::float8); INSERT INTO d_star (class, a, b) VALUES ('d', 11, 'fumble'::text); INSERT INTO d_star (class, a, c) VALUES ('d', 12, 'hi avi'::name); INSERT INTO d_star (class, a, d) VALUES ('d', 13, '1000.0001'::float8); INSERT INTO d_star (class, b, c) VALUES ('d', 'tumble'::text, 'hi andrew'::name); INSERT INTO d_star (class, b, d) VALUES ('d', 'humble'::text, '10000.00001'::float8); INSERT INTO d_star (class, c, d) VALUES ('d', 'hi ginger'::name, '100000.000001'::float8); INSERT INTO d_star (class, a) VALUES ('d', 14); INSERT INTO d_star (class, b) VALUES ('d', 'jumble'::text); INSERT INTO d_star (class, c) VALUES ('d', 'hi jolly'::name); INSERT INTO d_star (class, d) VALUES ('d', '1000000.0000001'::float8); INSERT INTO d_star (class) VALUES ('d'); INSERT INTO e_star (class, a, c, e) VALUES ('e', 15, 'hi carol'::name, '-1'::int2); INSERT INTO e_star (class, a, c) VALUES ('e', 16, 'hi bob'::name); INSERT INTO e_star (class, a, e) VALUES ('e', 17, '-2'::int2); INSERT INTO e_star (class, c, e) VALUES ('e', 'hi michelle'::name, '-3'::int2); INSERT INTO e_star (class, a) VALUES ('e', 18); INSERT INTO e_star (class, c) VALUES ('e', 'hi elisa'::name); INSERT INTO e_star (class, e) VALUES ('e', '-4'::int2); INSERT INTO f_star (class, a, c, e, f) VALUES ('f', 19, 'hi claire'::name, '-5'::int2, '(1,3),(2,4)'::polygon); INSERT INTO f_star (class, a, c, e) VALUES ('f', 20, 'hi mike'::name, '-6'::int2); INSERT INTO f_star (class, a, c, f) VALUES ('f', 21, 'hi marcel'::name, '(11,44),(22,55),(33,66)'::polygon); INSERT INTO f_star (class, a, e, f) VALUES ('f', 22, '-7'::int2, '(111,555),(222,666),(333,777),(444,888)'::polygon); INSERT INTO f_star (class, c, e, f) VALUES ('f', 'hi keith'::name, '-8'::int2, '(1111,3333),(2222,4444)'::polygon); INSERT INTO f_star (class, a, c) VALUES ('f', 24, 'hi marc'::name); INSERT INTO f_star (class, a, e) VALUES ('f', 25, '-9'::int2); INSERT INTO f_star (class, a, f) VALUES ('f', 26, '(11111,33333),(22222,44444)'::polygon); INSERT INTO f_star (class, c, e) VALUES ('f', 'hi allison'::name, '-10'::int2); INSERT INTO f_star (class, c, f) VALUES ('f', 'hi jeff'::name, '(111111,333333),(222222,444444)'::polygon); INSERT INTO f_star (class, e, f) VALUES ('f', '-11'::int2, '(1111111,3333333),(2222222,4444444)'::polygon); INSERT INTO f_star (class, a) VALUES ('f', 27); INSERT INTO f_star (class, c) VALUES ('f', 'hi carl'::name); INSERT INTO f_star (class, e) VALUES ('f', '-12'::int2); INSERT INTO f_star (class, f) VALUES ('f', '(11111111,33333333),(22222222,44444444)'::polygon); INSERT INTO f_star (class) VALUES ('f'); -- -- for internal portal (cursor) tests -- CREATE TABLE iportaltest ( i int4, d float4, p polygon ); INSERT INTO iportaltest (i, d, p) VALUES (1, 3.567, '(3.0,1.0),(4.0,2.0)'::polygon); INSERT INTO iportaltest (i, d, p) VALUES (2, 89.05, '(4.0,2.0),(3.0,1.0)'::polygon); pgFormatter-4.2/t/pg-test-files/expected/create_operator.sql000066400000000000000000000143101361326045100242770ustar00rootroot00000000000000-- -- CREATE_OPERATOR -- CREATE OPERATOR ## ( LEFTARG = path, RIGHTARG = path, FUNCTION = path_inter, commutator = ## ); CREATE OPERATOR <% ( LEFTARG = point, RIGHTARG = widget, PROCEDURE = pt_in_widget, commutator = >%, negator = >=% ); CREATE OPERATOR @#@ ( RIGHTARG = int8, -- left unary PROCEDURE = numeric_fac ); CREATE OPERATOR #@# ( LEFTARG = int8, -- right unary PROCEDURE = numeric_fac ); CREATE OPERATOR #%# ( LEFTARG = int8, -- right unary PROCEDURE = numeric_fac ); -- Test operator created above SELECT point '(1,2)' < % widget '(0,0,3)' AS t, point '(1,2)' < % widget '(0,0,1)' AS f; -- Test comments COMMENT ON OPERATOR ## ## ## (int4, NONE) IS 'bad right unary'; -- => is disallowed now CREATE OPERATOR => ( LEFTARG = int8, -- right unary PROCEDURE = numeric_fac ); -- lexing of <=, >=, <>, != has a number of edge cases -- (=> is tested elsewhere) -- this is legal because ! is not allowed in sql ops CREATE OPERATOR !=- ( LEFTARG = int8, -- right unary PROCEDURE = numeric_fac ); SELECT 2 != -; -- make sure lexer returns != as <> even in edge cases SELECT 2 != /**/ 1, 2 != /**/ 2; SELECT 2 != -- comment to be removed by psql 1; DO $$ -- use DO to protect -- from psql DECLARE r boolean; BEGIN EXECUTE $e$ select 2 !=-- comment 1 $e$ INTO r; RAISE info 'r = %', r; END; $$; -- check that <= etc. followed by more operator characters are returned -- as the correct token with correct precedence SELECT TRUE <> - 1 BETWEEN 1 AND 1; -- BETWEEN has prec. above <> but below Op SELECT FALSE <> /**/ 1 BETWEEN 1 AND 1; SELECT FALSE <= - 1 BETWEEN 1 AND 1; SELECT FALSE >= - 1 BETWEEN 1 AND 1; SELECT 2 <= /**/ 3, 3 >= /**/ 2, 2 <> /**/ 3; SELECT 3 <= /**/ 2, 2 >= /**/ 3, 2 <> /**/ 2; -- Should fail. CREATE OPERATOR requires USAGE on SCHEMA BEGIN TRANSACTION; CREATE ROLE regress_rol_op1; CREATE SCHEMA schema_op1; GRANT USAGE ON SCHEMA schema_op1 TO PUBLIC; REVOKE USAGE ON SCHEMA schema_op1 FROM regress_rol_op1; SET ROLE regress_rol_op1; CREATE OPERATOR schema_op1. # * # ( LEFTARG = int8, -- right unary PROCEDURE = numeric_fac ); ROLLBACK; -- Should fail. SETOF type functions not allowed as argument (testing leftarg) BEGIN TRANSACTION; CREATE OPERATOR "#*#" ( LEFTARG = SETOF int8, PROCEDURE = numeric_fac ); ROLLBACK; -- Should fail. SETOF type functions not allowed as argument (testing rightarg) BEGIN TRANSACTION; CREATE OPERATOR "#*#" ( RIGHTARG = SETOF int8, PROCEDURE = numeric_fac ); ROLLBACK; -- Should work. Sample text-book case BEGIN TRANSACTION; CREATE OR REPLACE FUNCTION fn_op2 (boolean, boolean) RETURNS boolean AS $$ SELECT NULL::BOOLEAN; $$ LANGUAGE sql IMMUTABLE; CREATE OPERATOR === ( LEFTARG = boolean, RIGHTARG = boolean, PROCEDURE = fn_op2, COMMUTATOR = ===, NEGATOR = !==, RESTRICT = contsel, JOIN = contjoinsel, SORT1, SORT2, LTCMP, GTCMP, HASHES, MERGES ); ROLLBACK; -- Should fail. Invalid attribute CREATE OPERATOR #@%# ( LEFTARG = int8, -- right unary PROCEDURE = numeric_fac, invalid_att = int8 ); -- Should fail. At least leftarg or rightarg should be mandatorily specified CREATE OPERATOR #@%# ( PROCEDURE = numeric_fac ); -- Should fail. Procedure should be mandatorily specified CREATE OPERATOR #@%# ( LEFTARG = int8 ); -- Should fail. CREATE OPERATOR requires USAGE on TYPE BEGIN TRANSACTION; CREATE ROLE regress_rol_op3; CREATE TYPE type_op3 AS ENUM ( 'new', 'open', 'closed' ); CREATE FUNCTION fn_op3 (type_op3, int8) RETURNS int8 AS $$ SELECT NULL::int8; $$ LANGUAGE sql IMMUTABLE; REVOKE USAGE ON TYPE type_op3 FROM regress_rol_op3; REVOKE USAGE ON TYPE type_op3 FROM PUBLIC; -- Need to do this so that regress_rol_op3 is not allowed USAGE via PUBLIC SET ROLE regress_rol_op3; CREATE OPERATOR "#*#" ( LEFTARG = type_op3, RIGHTARG = int8, PROCEDURE = fn_op3 ); ROLLBACK; -- Should fail. CREATE OPERATOR requires USAGE on TYPE (need to check separately for rightarg) BEGIN TRANSACTION; CREATE ROLE regress_rol_op4; CREATE TYPE type_op4 AS ENUM ( 'new', 'open', 'closed' ); CREATE FUNCTION fn_op4 (int8, type_op4) RETURNS int8 AS $$ SELECT NULL::int8; $$ LANGUAGE sql IMMUTABLE; REVOKE USAGE ON TYPE type_op4 FROM regress_rol_op4; REVOKE USAGE ON TYPE type_op4 FROM PUBLIC; -- Need to do this so that regress_rol_op3 is not allowed USAGE via PUBLIC SET ROLE regress_rol_op4; CREATE OPERATOR "#*#" ( LEFTARG = int8, RIGHTARG = type_op4, PROCEDURE = fn_op4 ); ROLLBACK; -- Should fail. CREATE OPERATOR requires EXECUTE on function BEGIN TRANSACTION; CREATE ROLE regress_rol_op5; CREATE TYPE type_op5 AS ENUM ( 'new', 'open', 'closed' ); CREATE FUNCTION fn_op5 (int8, int8) RETURNS int8 AS $$ SELECT NULL::int8; $$ LANGUAGE sql IMMUTABLE; REVOKE EXECUTE ON FUNCTION fn_op5 (int8, int8) FROM regress_rol_op5; REVOKE EXECUTE ON FUNCTION fn_op5 (int8, int8) FROM PUBLIC; -- Need to do this so that regress_rol_op3 is not allowed EXECUTE via PUBLIC SET ROLE regress_rol_op5; CREATE OPERATOR "#*#" ( LEFTARG = int8, RIGHTARG = int8, PROCEDURE = fn_op5 ); ROLLBACK; -- Should fail. CREATE OPERATOR requires USAGE on return TYPE BEGIN TRANSACTION; CREATE ROLE regress_rol_op6; CREATE TYPE type_op6 AS ENUM ( 'new', 'open', 'closed' ); CREATE FUNCTION fn_op6 (int8, int8) RETURNS type_op6 AS $$ SELECT NULL::type_op6; $$ LANGUAGE sql IMMUTABLE; REVOKE USAGE ON TYPE type_op6 FROM regress_rol_op6; REVOKE USAGE ON TYPE type_op6 FROM PUBLIC; -- Need to do this so that regress_rol_op3 is not allowed USAGE via PUBLIC SET ROLE regress_rol_op6; CREATE OPERATOR "#*#" ( LEFTARG = int8, RIGHTARG = int8, PROCEDURE = fn_op6 ); ROLLBACK; -- invalid: non-lowercase quoted identifiers CREATE OPERATOR === ( "Leftarg" = box, "Rightarg" = box, "Procedure" = area_equal_function, "Commutator" = == =, "Negator" = != =, "Restrict" = area_restriction_function, "Join" = area_join_function, "Hashes", "Merges" ); pgFormatter-4.2/t/pg-test-files/expected/create_procedure.sql000066400000000000000000000067111361326045100244420ustar00rootroot00000000000000CALL nonexistent (); -- error CALL random(); -- error CREATE FUNCTION cp_testfunc1 (a int) RETURNS int LANGUAGE SQL AS $$ SELECT a $$; CREATE TABLE cp_test ( a int, b text ); CREATE PROCEDURE ptest1 (x text) LANGUAGE SQL AS $$ INSERT INTO cp_test VALUES (1, x); $$; \df ptest1 SELECT pg_get_functiondef('ptest1'::regproc); -- show only normal functions \dfn public.*test*1 -- show only procedures dfp public.* test * 1 SELECT ptest1 ('x'); -- error CALL ptest1 ('a'); -- ok CALL ptest1 ('xy' || 'zzy'); -- ok, constant-folded arg CALL ptest1 (substring(random()::numeric(20, 15)::text, 1, 1)); -- ok, volatile arg SELECT * FROM cp_test ORDER BY b COLLATE "C"; CREATE PROCEDURE ptest2 () LANGUAGE SQL AS $$ SELECT 5; $$; CALL ptest2 (); -- nested CALL TRUNCATE cp_test; CREATE PROCEDURE ptest3 (y text) LANGUAGE SQL AS $$ CALL ptest1 (y); CALL ptest1 ($1); $$; CALL ptest3 ('b'); SELECT * FROM cp_test; -- output arguments CREATE PROCEDURE ptest4a (INOUT a int, INOUT b int) LANGUAGE SQL AS $$ SELECT 1, 2; $$; CALL ptest4a (NULL, NULL); CREATE PROCEDURE ptest4b (INOUT b int, INOUT a int) LANGUAGE SQL AS $$ CALL ptest4a (a, b); -- error, not supported $$; DROP PROCEDURE ptest4a; -- named and default parameters CREATE OR REPLACE PROCEDURE ptest5 (a int, b text, c int DEFAULT 100) LANGUAGE SQL AS $$ INSERT INTO cp_test VALUES (a, b); INSERT INTO cp_test VALUES (c, b); $$; TRUNCATE cp_test; CALL ptest5 (10, 'Hello', 20); CALL ptest5 (10, 'Hello'); CALL ptest5 (10, b => 'Hello'); CALL ptest5 (b => 'Hello', a => 10); SELECT * FROM cp_test; -- polymorphic types CREATE PROCEDURE ptest6 (a int, b anyelement) LANGUAGE SQL AS $$ SELECT NULL::int; $$; CALL ptest6 (1, 2); -- collation assignment CREATE PROCEDURE ptest7 (a text, b text) LANGUAGE SQL AS $$ SELECT a = b; $$; CALL ptest7 (least ('a', 'b'), 'a'); -- various error cases CALL version(); -- error: not a procedure CALL sum(1); -- error: not a procedure CREATE PROCEDURE ptestx () LANGUAGE SQL WINDOW AS $$ INSERT INTO cp_test VALUES (1, 'a') $$; CREATE PROCEDURE ptestx () LANGUAGE SQL STRICT AS $$ INSERT INTO cp_test VALUES (1, 'a') $$; CREATE PROCEDURE ptestx (OUT a int) LANGUAGE SQL AS $$ INSERT INTO cp_test VALUES (1, 'a') $$; ALTER PROCEDURE ptest1 (text) STRICT; ALTER FUNCTION ptest1 (text) VOLATILE; -- error: not a function ALTER PROCEDURE cp_testfunc1 (int) VOLATILE; -- error: not a procedure ALTER PROCEDURE nonexistent () VOLATILE; DROP FUNCTION ptest1 (text); -- error: not a function DROP PROCEDURE cp_testfunc1 (int); -- error: not a procedure DROP PROCEDURE nonexistent (); -- privileges CREATE USER regress_cp_user1; GRANT INSERT ON cp_test TO regress_cp_user1; REVOKE EXECUTE ON PROCEDURE ptest1 (text) FROM PUBLIC; SET ROLE regress_cp_user1; CALL ptest1 ('a'); -- error RESET ROLE; GRANT EXECUTE ON PROCEDURE ptest1 (text) TO regress_cp_user1; SET ROLE regress_cp_user1; CALL ptest1 ('a'); -- ok RESET ROLE; -- ROUTINE syntax ALTER ROUTINE cp_testfunc1 (int) RENAME TO cp_testfunc1a; ALTER ROUTINE cp_testfunc1a RENAME TO cp_testfunc1; ALTER ROUTINE ptest1 (text) RENAME TO ptest1a; ALTER ROUTINE ptest1a RENAME TO ptest1; DROP ROUTINE cp_testfunc1 (int); -- cleanup DROP PROCEDURE ptest1; DROP PROCEDURE ptest2; DROP TABLE cp_test; DROP USER regress_cp_user1; pgFormatter-4.2/t/pg-test-files/expected/create_table.sql000066400000000000000000000726731361326045100235530ustar00rootroot00000000000000-- -- CREATE_TABLE -- -- -- CLASS DEFINITIONS -- CREATE TABLE hobbies_r ( name text, person text ); CREATE TABLE equipment_r ( name text, hobby text ); CREATE TABLE onek ( unique1 int4, unique2 int4, two int4, four int4, ten int4, twenty int4, hundred int4, thousand int4, twothousand int4, fivethous int4, tenthous int4, odd int4, even int4, stringu1 name, stringu2 name, string4 name ); CREATE TABLE tenk1 ( unique1 int4, unique2 int4, two int4, four int4, ten int4, twenty int4, hundred int4, thousand int4, twothousand int4, fivethous int4, tenthous int4, odd int4, even int4, stringu1 name, stringu2 name, string4 name ); CREATE TABLE tenk2 ( unique1 int4, unique2 int4, two int4, four int4, ten int4, twenty int4, hundred int4, thousand int4, twothousand int4, fivethous int4, tenthous int4, odd int4, even int4, stringu1 name, stringu2 name, string4 name ); CREATE TABLE person ( name text, age int4, location point ); CREATE TABLE emp ( salary int4, manager name ) INHERITS ( person ); CREATE TABLE student ( gpa float8 ) INHERITS ( person ); CREATE TABLE stud_emp ( percent int4 ) INHERITS ( emp, student ); CREATE TABLE city ( name name, location box, budget city_budget ); CREATE TABLE dept ( dname name, mgrname text ); CREATE TABLE slow_emp4000 ( home_base box ); CREATE TABLE fast_emp4000 ( home_base box ); CREATE TABLE road ( name text, thepath path ); CREATE TABLE ihighway () INHERITS ( road ); CREATE TABLE shighway ( surface text ) INHERITS ( road ); CREATE TABLE real_city ( pop int4, cname text, outline path ); -- -- test the "star" operators a bit more thoroughly -- this time, -- throw in lots of NULL fields... -- -- a is the type root -- b and c inherit from a (one-level single inheritance) -- d inherits from b and c (two-level multiple inheritance) -- e inherits from c (two-level single inheritance) -- f inherits from e (three-level single inheritance) -- CREATE TABLE a_star ( class char, a int4 ); CREATE TABLE b_star ( b text ) INHERITS ( a_star ); CREATE TABLE c_star ( c name ) INHERITS ( a_star ); CREATE TABLE d_star ( d float8 ) INHERITS ( b_star, c_star ); CREATE TABLE e_star ( e int2 ) INHERITS ( c_star ); CREATE TABLE f_star ( f polygon ) INHERITS ( e_star ); CREATE TABLE aggtest ( a int2, b float4 ); CREATE TABLE hash_i4_heap ( seqno int4, random int4 ); CREATE TABLE hash_name_heap ( seqno int4, random name ); CREATE TABLE hash_txt_heap ( seqno int4, random text ); CREATE TABLE hash_f8_heap ( seqno int4, random float8 ); -- don't include the hash_ovfl_heap stuff in the distribution -- the data set is too large for what it's worth -- -- CREATE TABLE hash_ovfl_heap ( -- x int4, -- y int4 -- ); CREATE TABLE bt_i4_heap ( seqno int4, random int4 ); CREATE TABLE bt_name_heap ( seqno name, random int4 ); CREATE TABLE bt_txt_heap ( seqno text, random int4 ); CREATE TABLE bt_f8_heap ( seqno float8, random int4 ); CREATE TABLE array_op_test ( seqno int4, i int4[], t text[] ); CREATE TABLE array_index_op_test ( seqno int4, i int4[], t text[] ); CREATE TABLE testjsonb ( j jsonb ); CREATE TABLE unknowntab ( u unknown -- fail ); CREATE TYPE unknown_comptype AS ( u unknown -- fail ); CREATE TABLE IF NOT EXISTS test_tsvector ( t text, a tsvector ); CREATE TABLE IF NOT EXISTS test_tsvector ( t text ); -- invalid: non-lowercase quoted reloptions identifiers CREATE TABLE tas_case WITH ( "Fillfactor" = 10 ) AS SELECT 1 a; CREATE UNLOGGED TABLE unlogged1 ( a int PRIMARY KEY ); -- OK CREATE TEMPORARY TABLE unlogged2 ( a int PRIMARY KEY ); -- OK SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged\d' ORDER BY relname; REINDEX INDEX unlogged1_pkey; REINDEX INDEX unlogged2_pkey; SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged\d' ORDER BY relname; DROP TABLE unlogged2; INSERT INTO unlogged1 VALUES (42); CREATE UNLOGGED TABLE public.unlogged2 ( a int PRIMARY KEY ); -- also OK CREATE UNLOGGED TABLE pg_temp.unlogged3 ( a int PRIMARY KEY ); -- not OK CREATE TABLE pg_temp.implicitly_temp ( a int PRIMARY KEY ); -- OK CREATE TEMP TABLE explicitly_temp ( a int PRIMARY KEY ); -- also OK CREATE TEMP TABLE pg_temp.doubly_temp ( a int PRIMARY KEY ); -- also OK CREATE TEMP TABLE public.temp_to_perm ( a int PRIMARY KEY ); -- not OK DROP TABLE unlogged1, public.unlogged2; CREATE TABLE as_select1 AS SELECT * FROM pg_class WHERE relkind = 'r'; CREATE TABLE as_select1 AS SELECT * FROM pg_class WHERE relkind = 'r'; CREATE TABLE IF NOT EXISTS as_select1 AS SELECT * FROM pg_class WHERE relkind = 'r'; DROP TABLE as_select1; PREPARE select1 AS SELECT 1 AS a; CREATE TABLE as_select1 AS EXECUTE select1; CREATE TABLE as_select1 AS EXECUTE select1; SELECT * FROM as_select1; CREATE TABLE IF NOT EXISTS as_select1 AS EXECUTE select1; DROP TABLE as_select1; DEALLOCATE select1; -- create an extra wide table to test for issues related to that -- (temporarily hide query, to avoid the long CREATE TABLE stmt) \set ECHO none SELECT 'CREATE TABLE extra_wide_table(firstc text, ' || array_to_string(array_agg('c' || i || ' bool'), ',') || ', lastc text);' FROM generate_series(1, 1100) g (i) \gexec \set ECHO all INSERT INTO extra_wide_table (firstc, lastc) VALUES ('first col', 'last col'); SELECT firstc, lastc FROM extra_wide_table; -- check that tables with oids cannot be created anymore CREATE TABLE withoid ( ) WITH OIDS; CREATE TABLE withoid ( ) WITH ( OIDS ); CREATE TABLE withoid ( ) WITH ( OIDS = TRUE ); -- but explicitly not adding oids is still supported CREATE TEMP TABLE withoutoid () WITHOUT OIDS; DROP TABLE withoutoid; CREATE TEMP TABLE withoutoid ( ) WITH ( OIDS = FALSE ); DROP TABLE withoutoid; -- check restriction with default expressions -- invalid use of column reference in default expressions CREATE TABLE default_expr_column ( id int DEFAULT (id) ); CREATE TABLE default_expr_column ( id int DEFAULT (bar.id) ); CREATE TABLE default_expr_agg_column ( id int DEFAULT (avg(id)) ); -- invalid column definition CREATE TABLE default_expr_non_column ( a int DEFAULT (avg(non_existent)) ); -- invalid use of aggregate CREATE TABLE default_expr_agg ( a int DEFAULT (avg(1)) ); -- invalid use of subquery CREATE TABLE default_expr_agg ( a int DEFAULT ( SELECT 1) ); -- invalid use of set-returning function CREATE TABLE default_expr_agg ( a int DEFAULT (generate_series(1, 3)) ); -- -- Partitioned tables -- -- cannot combine INHERITS and PARTITION BY (although grammar allows) CREATE TABLE partitioned ( a int ) INHERITS ( some_table ) PARTITION BY LIST (a); -- cannot use more than 1 column as partition key for list partitioned table CREATE TABLE partitioned ( a1 int, a2 int ) PARTITION BY LIST (a1, a2); -- fail -- unsupported constraint type for partitioned tables CREATE TABLE partitioned ( a int, EXCLUDE USING gist (a WITH &&) ) PARTITION BY RANGE (a); -- prevent using prohibited expressions in the key CREATE FUNCTION retset (a int) RETURNS SETOF int AS $$ SELECT 1; $$ LANGUAGE SQL IMMUTABLE; CREATE TABLE partitioned ( a int ) PARTITION BY RANGE (retset (a)); DROP FUNCTION retset (int); CREATE TABLE partitioned ( a int ) PARTITION BY RANGE ((avg(a))); CREATE TABLE partitioned ( a int, b int ) PARTITION BY RANGE ((avg(a) OVER (PARTITION BY b))); CREATE TABLE partitioned ( a int ) PARTITION BY LIST ((a LIKE ( SELECT 1))); CREATE TABLE partitioned ( a int ) PARTITION BY RANGE (('a')); CREATE FUNCTION const_func () RETURNS int AS $$ SELECT 1; $$ LANGUAGE SQL IMMUTABLE; CREATE TABLE partitioned ( a int ) PARTITION BY RANGE (const_func ()); DROP FUNCTION const_func (); -- only accept valid partitioning strategy CREATE TABLE partitioned ( a int ) PARTITION BY MAGIC (a); -- specified column must be present in the table CREATE TABLE partitioned ( a int ) PARTITION BY RANGE (b); -- cannot use system columns in partition key CREATE TABLE partitioned ( a int ) PARTITION BY RANGE (xmin); -- functions in key must be immutable CREATE FUNCTION immut_func (a int) RETURNS int AS $$ SELECT a + random()::int; $$ LANGUAGE SQL; CREATE TABLE partitioned ( a int ) PARTITION BY RANGE (immut_func (a)); DROP FUNCTION immut_func (int); -- cannot contain whole-row references CREATE TABLE partitioned ( a int ) PARTITION BY RANGE ((partitioned)); -- prevent using columns of unsupported types in key (type must have a btree operator class) CREATE TABLE partitioned ( a point ) PARTITION BY LIST (a); CREATE TABLE partitioned ( a point ) PARTITION BY LIST (a point_ops); CREATE TABLE partitioned ( a point ) PARTITION BY RANGE (a); CREATE TABLE partitioned ( a point ) PARTITION BY RANGE (a point_ops); -- cannot add NO INHERIT constraints to partitioned tables CREATE TABLE partitioned ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); -- some checks after successful creation of a partitioned table CREATE FUNCTION plusone (a int) RETURNS INT AS $$ SELECT a + 1; $$ LANGUAGE SQL; CREATE TABLE partitioned ( a int, b int, c text, d text ) PARTITION BY RANGE (a oid_ops, plusone (b), c COLLATE "default", d COLLATE "C"); -- check relkind SELECT relkind FROM pg_class WHERE relname = 'partitioned'; -- prevent a function referenced in partition key from being dropped DROP FUNCTION plusone (int); -- partitioned table cannot participate in regular inheritance CREATE TABLE partitioned2 ( a int, b text ) PARTITION BY RANGE ((a + 1), substr(b, 1, 5)); CREATE TABLE fail () INHERITS ( partitioned2 ); -- Partition key in describe output \d partitioned \d+ partitioned2 INSERT INTO partitioned2 VALUES (1, 'hello'); CREATE TABLE part2_1 PARTITION OF partitioned2 FOR VALUES FROM (- 1, 'aaaaa') TO (100, 'ccccc'); \d+ part2_1 DROP TABLE partitioned, partitioned2; -- -- Partitions -- -- check partition bound syntax CREATE TABLE list_parted ( a int ) PARTITION BY LIST (a); CREATE TABLE part_p1 PARTITION OF list_parted FOR VALUES IN ('1'); CREATE TABLE part_p2 PARTITION OF list_parted FOR VALUES IN (2); CREATE TABLE part_p3 PARTITION OF list_parted FOR VALUES IN ((2 + 1)); CREATE TABLE part_null PARTITION OF list_parted FOR VALUES IN (NULL); \d+ list_parted -- forbidden expressions for partition bound with list partitioned table CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (somename); CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (somename.somename); CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (a); CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (sum(a)); CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (sum(somename)); CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (sum(1)); CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (( SELECT 1)); CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (generate_series(4, 6)); -- syntax does not allow empty list of values for list partitions CREATE TABLE fail_part PARTITION OF list_parted FOR VALUES IN (); -- trying to specify range for list partitioned table CREATE TABLE fail_part PARTITION OF list_parted FOR VALUES FROM (1) TO (2); -- trying to specify modulus and remainder for list partitioned table CREATE TABLE fail_part PARTITION OF list_parted FOR VALUES WITH (MODULUS 10, REMAINDER 1); -- check default partition cannot be created more than once CREATE TABLE part_default PARTITION OF list_parted DEFAULT; CREATE TABLE fail_default_part PARTITION OF list_parted DEFAULT; -- specified literal can't be cast to the partition column data type CREATE TABLE bools ( a bool ) PARTITION BY LIST (a); CREATE TABLE bools_true PARTITION OF bools FOR VALUES IN (1); DROP TABLE bools; -- specified literal can be cast, and the cast might not be immutable CREATE TABLE moneyp ( a money ) PARTITION BY LIST (a); CREATE TABLE moneyp_10 PARTITION OF moneyp FOR VALUES IN (10); CREATE TABLE moneyp_11 PARTITION OF moneyp FOR VALUES IN ('11'); CREATE TABLE moneyp_12 PARTITION OF moneyp FOR VALUES IN (to_char(12, '99')::int); DROP TABLE moneyp; -- cast is immutable CREATE TABLE bigintp ( a bigint ) PARTITION BY LIST (a); CREATE TABLE bigintp_10 PARTITION OF bigintp FOR VALUES IN (10); -- fails due to overlap: CREATE TABLE bigintp_10_2 PARTITION OF bigintp FOR VALUES IN ('10'); DROP TABLE bigintp; CREATE TABLE range_parted ( a date ) PARTITION BY RANGE (a); -- forbidden expressions for partition bounds with range partitioned table CREATE TABLE part_bogus_expr_fail PARTITION OF range_parted FOR VALUES FROM (somename) TO ('2019-01-01'); CREATE TABLE part_bogus_expr_fail PARTITION OF range_parted FOR VALUES FROM (somename.somename) TO ('2019-01-01'); CREATE TABLE part_bogus_expr_fail PARTITION OF range_parted FOR VALUES FROM (a) TO ('2019-01-01'); CREATE TABLE part_bogus_expr_fail PARTITION OF range_parted FOR VALUES FROM (max(a)) TO ('2019-01-01'); CREATE TABLE part_bogus_expr_fail PARTITION OF range_parted FOR VALUES FROM (max(somename)) TO ('2019-01-01'); CREATE TABLE part_bogus_expr_fail PARTITION OF range_parted FOR VALUES FROM (max('2019-02-01'::date)) TO ('2019-01-01'); CREATE TABLE part_bogus_expr_fail PARTITION OF range_parted FOR VALUES FROM (( SELECT 1)) TO ('2019-01-01'); CREATE TABLE part_bogus_expr_fail PARTITION OF range_parted FOR VALUES FROM (generate_series(1, 3)) TO ('2019-01-01'); -- trying to specify list for range partitioned table CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES IN ('a'); -- trying to specify modulus and remainder for range partitioned table CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES WITH (MODULUS 10, REMAINDER 1); -- each of start and end bounds must have same number of values as the -- length of the partition key CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM ('a', 1) TO ('z'); CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM ('a') TO ('z', 1); -- cannot specify null values in range bounds CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM (NULL) TO (MAXVALUE); -- trying to specify modulus and remainder for range partitioned table CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES WITH (MODULUS 10, REMAINDER 1); -- check partition bound syntax for the hash partition CREATE TABLE hash_parted ( a int ) PARTITION BY HASH (a); CREATE TABLE hpart_1 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 10, REMAINDER 0); CREATE TABLE hpart_2 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 50, REMAINDER 1); CREATE TABLE hpart_3 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 200, REMAINDER 2); -- modulus 25 is factor of modulus of 50 but 10 is not factor of 25. CREATE TABLE fail_part PARTITION OF hash_parted FOR VALUES WITH (MODULUS 25, REMAINDER 3); -- previous modulus 50 is factor of 150 but this modulus is not factor of next modulus 200. CREATE TABLE fail_part PARTITION OF hash_parted FOR VALUES WITH (MODULUS 150, REMAINDER 3); -- trying to specify range for the hash partitioned table CREATE TABLE fail_part PARTITION OF hash_parted FOR VALUES FROM ('a', 1) TO ('z'); -- trying to specify list value for the hash partitioned table CREATE TABLE fail_part PARTITION OF hash_parted FOR VALUES IN (1000); -- trying to create default partition for the hash partitioned table CREATE TABLE fail_default_part PARTITION OF hash_parted DEFAULT; -- check if compatible with the specified parent -- cannot create as partition of a non-partitioned table CREATE TABLE unparted ( a int ); CREATE TABLE fail_part PARTITION OF unparted FOR VALUES IN ('a'); CREATE TABLE fail_part PARTITION OF unparted FOR VALUES WITH (MODULUS 2, REMAINDER 1); DROP TABLE unparted; -- cannot create a permanent rel as partition of a temp rel CREATE TEMP TABLE temp_parted ( a int ) PARTITION BY LIST (a); CREATE TABLE fail_part PARTITION OF temp_parted FOR VALUES IN ('a'); DROP TABLE temp_parted; -- check for partition bound overlap and other invalid specifications CREATE TABLE list_parted2 ( a varchar ) PARTITION BY LIST (a); CREATE TABLE part_null_z PARTITION OF list_parted2 FOR VALUES IN (NULL, 'z'); CREATE TABLE part_ab PARTITION OF list_parted2 FOR VALUES IN ('a', 'b'); CREATE TABLE list_parted2_def PARTITION OF list_parted2 DEFAULT; CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN (NULL); CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN ('b', 'c'); -- check default partition overlap INSERT INTO list_parted2 VALUES ('X'); CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN ('W', 'X', 'Y'); CREATE TABLE range_parted2 ( a int ) PARTITION BY RANGE (a); -- trying to create range partition with empty range CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (0); -- note that the range '[1, 1)' has no elements CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (1); CREATE TABLE part0 PARTITION OF range_parted2 FOR VALUES FROM (MINVALUE) TO (1); CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (MINVALUE) TO (2); CREATE TABLE part1 PARTITION OF range_parted2 FOR VALUES FROM (1) TO (10); CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (9) TO (MAXVALUE); CREATE TABLE part2 PARTITION OF range_parted2 FOR VALUES FROM (20) TO (30); CREATE TABLE part3 PARTITION OF range_parted2 FOR VALUES FROM (30) TO (40); CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (30); CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (50); -- Create a default partition for range partitioned table CREATE TABLE range2_default PARTITION OF range_parted2 DEFAULT; -- More than one default partition is not allowed, so this should give error CREATE TABLE fail_default_part PARTITION OF range_parted2 DEFAULT; -- Check if the range for default partitions overlap INSERT INTO range_parted2 VALUES (85); CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (80) TO (90); CREATE TABLE part4 PARTITION OF range_parted2 FOR VALUES FROM (90) TO (100); -- now check for multi-column range partition key CREATE TABLE range_parted3 ( a int, b int ) PARTITION BY RANGE (a, (b + 1)); CREATE TABLE part00 PARTITION OF range_parted3 FOR VALUES FROM (0, MINVALUE) TO (0, MAXVALUE); CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, MINVALUE) TO (0, 1); CREATE TABLE part10 PARTITION OF range_parted3 FOR VALUES FROM (1, MINVALUE) TO (1, 1); CREATE TABLE part11 PARTITION OF range_parted3 FOR VALUES FROM (1, 1) TO (1, 10); CREATE TABLE part12 PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, MAXVALUE); CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, 20); CREATE TABLE range3_default PARTITION OF range_parted3 DEFAULT; -- cannot create a partition that says column b is allowed to range -- from -infinity to +infinity, while there exist partitions that have -- more specific ranges CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, MINVALUE) TO (1, MAXVALUE); -- check for partition bound overlap and other invalid specifications for the hash partition CREATE TABLE hash_parted2 ( a varchar ) PARTITION BY HASH (a); CREATE TABLE h2part_1 PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 4, REMAINDER 2); CREATE TABLE h2part_2 PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 8, REMAINDER 0); CREATE TABLE h2part_3 PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 8, REMAINDER 4); CREATE TABLE h2part_4 PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 8, REMAINDER 5); -- overlap with part_4 CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 2, REMAINDER 1); -- modulus must be greater than zero CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 0, REMAINDER 1); -- remainder must be greater than or equal to zero and less than modulus CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 8, REMAINDER 8); -- check schema propagation from parent CREATE TABLE parted ( a text, b int NOT NULL DEFAULT 0, CONSTRAINT check_a CHECK (length(a) > 0) ) PARTITION BY LIST (a); CREATE TABLE part_a PARTITION OF parted FOR VALUES IN ('a'); -- only inherited attributes (never local ones) SELECT attname, attislocal, attinhcount FROM pg_attribute WHERE attrelid = 'part_a'::regclass AND attnum > 0 ORDER BY attnum; -- able to specify column default, column constraint, and table constraint -- first check the "column specified more than once" error CREATE TABLE part_b PARTITION OF parted (b NOT NULL, b DEFAULT 1, b CHECK (b >= 0), CONSTRAINT check_a CHECK (length(a) > 0)) FOR VALUES IN ('b'); CREATE TABLE part_b PARTITION OF parted (b NOT NULL DEFAULT 1, CONSTRAINT check_a CHECK (length(a) > 0), CONSTRAINT check_b CHECK (b >= 0)) FOR VALUES IN ('b'); -- conislocal should be false for any merged constraints, true otherwise SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_b'::regclass ORDER BY conislocal, coninhcount; -- Once check_b is added to the parent, it should be made non-local for part_b ALTER TABLE parted ADD CONSTRAINT check_b CHECK (b >= 0); SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_b'::regclass; -- Neither check_a nor check_b are droppable from part_b ALTER TABLE part_b DROP CONSTRAINT check_a; ALTER TABLE part_b DROP CONSTRAINT check_b; -- And dropping it from parted should leave no trace of them on part_b, unlike -- traditional inheritance where they will be left behind, because they would -- be local constraints. ALTER TABLE parted DROP CONSTRAINT check_a, DROP CONSTRAINT check_b; SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_b'::regclass; -- specify PARTITION BY for a partition CREATE TABLE fail_part_col_not_found PARTITION OF parted FOR VALUES IN ('c') PARTITION BY RANGE (c); CREATE TABLE part_c PARTITION OF parted (b WITH OPTIONS NOT NULL DEFAULT 0) FOR VALUES IN ('c') PARTITION BY RANGE ((b)); -- create a level-2 partition CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10); -- check that NOT NULL and default value are inherited correctly CREATE TABLE parted_notnull_inh_test ( a int DEFAULT 1, b int NOT NULL DEFAULT 0 ) PARTITION BY LIST (a); CREATE TABLE parted_notnull_inh_test1 PARTITION OF parted_notnull_inh_test (a NOT NULL, b DEFAULT 1) FOR VALUES IN (1); INSERT INTO parted_notnull_inh_test (b) VALUES (NULL); -- note that while b's default is overriden, a's default is preserved \d parted_notnull_inh_test1 DROP TABLE parted_notnull_inh_test; -- check for a conflicting COLLATE clause CREATE TABLE parted_collate_must_match ( a text COLLATE "C", b text COLLATE "C" ) PARTITION BY RANGE (a); -- on the partition key CREATE TABLE parted_collate_must_match1 PARTITION OF parted_collate_must_match (a COLLATE "POSIX") FOR VALUES FROM ('a') TO ('m'); -- on another column CREATE TABLE parted_collate_must_match2 PARTITION OF parted_collate_must_match (b COLLATE "POSIX") FOR VALUES FROM ('m') TO ('z'); DROP TABLE parted_collate_must_match; -- check that specifying incompatible collations for partition bound -- expressions fails promptly CREATE TABLE test_part_coll_posix ( a text ) PARTITION BY RANGE (a COLLATE "POSIX"); -- fail CREATE TABLE test_part_coll PARTITION OF test_part_coll_posix FOR VALUES FROM ('a' COLLATE "C") TO ('g'); -- ok CREATE TABLE test_part_coll PARTITION OF test_part_coll_posix FOR VALUES FROM ('a' COLLATE "POSIX") TO ('g'); -- ok CREATE TABLE test_part_coll2 PARTITION OF test_part_coll_posix FOR VALUES FROM ('g') TO ('m'); -- using a cast expression uses the target type's default collation -- fail CREATE TABLE test_part_coll_cast PARTITION OF test_part_coll_posix FOR VALUES FROM (name 'm' COLLATE "C") TO ('s'); -- ok CREATE TABLE test_part_coll_cast PARTITION OF test_part_coll_posix FOR VALUES FROM (name 'm' COLLATE "POSIX") TO ('s'); -- ok; partition collation silently overrides the default collation of type 'name' CREATE TABLE test_part_coll_cast2 PARTITION OF test_part_coll_posix FOR VALUES FROM (name 's') TO ('z'); DROP TABLE test_part_coll_posix; -- Partition bound in describe output \d+ part_b -- Both partition bound and partition key in describe output \d+ part_c -- a level-2 partition's constraint will include the parent's expressions \d+ part_c_1_10 -- Show partition count in the parent's describe output -- Tempted to include \d+ output listing partitions with bound info but -- output could vary depending on the order in which partition oids are -- returned. \d parted \d hash_parted -- check that we get the expected partition constraints CREATE TABLE range_parted4 ( a int, b int, c int ) PARTITION BY RANGE (abs(a), abs(b), c); CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (MAXVALUE, MAXVALUE, MAXVALUE); \d+ unbounded_range_part DROP TABLE unbounded_range_part; CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, MAXVALUE, MAXVALUE); \d+ range_parted4_1 CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE); \d+ range_parted4_2 CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MAXVALUE); \d+ range_parted4_3 DROP TABLE range_parted4; -- user-defined operator class in partition key CREATE FUNCTION my_int4_sort (int4, int4) RETURNS int LANGUAGE sql AS $$ SELECT CASE WHEN $1 = $2 THEN 0 WHEN $1 > $2 THEN 1 ELSE - 1 END; $$; CREATE OPERATOR CLASS test_int4_ops FOR TYPE int4 USING btree AS OPERATOR 1 < (int4, int4), OPERATOR 2 <= (int4, int4), OPERATOR 3 = (int4, int4), OPERATOR 4 >= (int4, int4), OPERATOR 5 > (int4, int4), FUNCTION 1 my_int4_sort (int4, int4 ); CREATE TABLE partkey_t ( a int4 ) PARTITION BY RANGE (a test_int4_ops); CREATE TABLE partkey_t_1 PARTITION OF partkey_t FOR VALUES FROM (0) TO (1000); INSERT INTO partkey_t VALUES (100); INSERT INTO partkey_t VALUES (200); -- cleanup DROP TABLE parted, list_parted, range_parted, list_parted2, range_parted2, range_parted3; DROP TABLE partkey_t, hash_parted, hash_parted2; DROP OPERATOR CLASS test_int4_ops USING btree; DROP FUNCTION my_int4_sort (int4, int4); -- comments on partitioned tables columns CREATE TABLE parted_col_comment ( a int, b text ) PARTITION BY LIST (a); COMMENT ON TABLE parted_col_comment IS 'Am partitioned table'; COMMENT ON COLUMN parted_col_comment.a IS 'Partition key'; SELECT obj_description('parted_col_comment'::regclass); \d+ parted_col_comment DROP TABLE parted_col_comment; -- list partitioning on array type column CREATE TABLE arrlp ( a int[] ) PARTITION BY LIST (a); CREATE TABLE arrlp12 PARTITION OF arrlp FOR VALUES IN ('{1}', '{2}'); \d+ arrlp12 DROP TABLE arrlp; -- partition on boolean column CREATE TABLE boolspart ( a bool ) PARTITION BY LIST (a); CREATE TABLE boolspart_t PARTITION OF boolspart FOR VALUES IN (TRUE); CREATE TABLE boolspart_f PARTITION OF boolspart FOR VALUES IN (FALSE); \d+ boolspart DROP TABLE boolspart; -- partitions mixing temporary and permanent relations CREATE TABLE perm_parted ( a int ) PARTITION BY LIST (a); CREATE TEMPORARY TABLE temp_parted ( a int ) PARTITION BY LIST (a); CREATE TABLE perm_part PARTITION OF temp_parted DEFAULT; -- error CREATE temp TABLE temp_part PARTITION OF perm_parted DEFAULT; -- error CREATE temp TABLE temp_part PARTITION OF temp_parted DEFAULT; -- ok DROP TABLE perm_parted CASCADE; DROP TABLE temp_parted CASCADE; -- check that adding partitions to a table while it is being used is prevented CREATE TABLE tab_part_create ( a int ) PARTITION BY LIST (a); CREATE OR REPLACE FUNCTION func_part_create () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN EXECUTE 'create table tab_part_create_1 partition of tab_part_create for values in (1)'; RETURN NULL; END $$; CREATE TRIGGER trig_part_create BEFORE INSERT ON tab_part_create FOR EACH statement EXECUTE PROCEDURE func_part_create (); INSERT INTO tab_part_create VALUES (1); DROP TABLE tab_part_create; DROP FUNCTION func_part_create (); -- test using a volatile expression as partition bound CREATE TABLE volatile_partbound_test ( partkey timestamp ) PARTITION BY RANGE (partkey); CREATE TABLE volatile_partbound_test1 PARTITION OF volatile_partbound_test FOR VALUES FROM (MINVALUE) TO (CURRENT_TIMESTAMP); CREATE TABLE volatile_partbound_test2 PARTITION OF volatile_partbound_test FOR VALUES FROM (CURRENT_TIMESTAMP) TO (MAXVALUE); -- this should go into the partition volatile_partbound_test2 INSERT INTO volatile_partbound_test VALUES (CURRENT_TIMESTAMP); SELECT tableoid::regclass FROM volatile_partbound_test; DROP TABLE volatile_partbound_test; pgFormatter-4.2/t/pg-test-files/expected/create_table_like.sql000066400000000000000000000163051361326045100245450ustar00rootroot00000000000000/* Test inheritance of structure (LIKE) */ CREATE TABLE inhx ( xx text DEFAULT 'text' ); /* * Test double inheritance * * Ensure that defaults are NOT included unless * INCLUDING DEFAULTS is specified */ CREATE TABLE ctla ( aa text ); CREATE TABLE ctlb ( bb text ) INHERITS ( ctla ); CREATE TABLE foo ( LIKE nonexistent ); CREATE TABLE inhe ( ee text, LIKE inhx ) INHERITS ( ctlb ); INSERT INTO inhe VALUES ('ee-col1', 'ee-col2', DEFAULT, 'ee-col4'); SELECT * FROM inhe; /* Columns aa, bb, xx value NULL, ee */ SELECT * FROM inhx; /* Empty set since LIKE inherits structure only */ SELECT * FROM ctlb; /* Has ee entry */ SELECT * FROM ctla; /* Has ee entry */ CREATE TABLE inhf ( LIKE inhx, LIKE inhx ); /* Throw error */ CREATE TABLE inhf ( LIKE inhx INCLUDING DEFAULTS INCLUDING CONSTRAINTS ); INSERT INTO inhf DEFAULT VALUES; SELECT * FROM inhf; /* Single entry with value 'text' */ ALTER TABLE inhx ADD CONSTRAINT foo CHECK (xx = 'text'); ALTER TABLE inhx ADD PRIMARY KEY (xx); CREATE TABLE inhg ( LIKE inhx ); /* Doesn't copy constraint */ INSERT INTO inhg VALUES ('foo'); DROP TABLE inhg; CREATE TABLE inhg ( x text, LIKE inhx INCLUDING CONSTRAINTS, y text ); /* Copies constraints */ INSERT INTO inhg VALUES ('x', 'text', 'y'); /* Succeeds */ INSERT INTO inhg VALUES ('x', 'text', 'y'); /* Succeeds -- Unique constraints not copied */ INSERT INTO inhg VALUES ('x', 'foo', 'y'); /* fails due to constraint */ SELECT * FROM inhg; /* Two records with three columns in order x=x, xx=text, y=y */ DROP TABLE inhg; CREATE TABLE test_like_id_1 ( a bigint GENERATED ALWAYS AS IDENTITY, b text ); \d test_like_id_1 INSERT INTO test_like_id_1 (b) VALUES ('b1'); SELECT * FROM test_like_id_1; CREATE TABLE test_like_id_2 ( LIKE test_like_id_1 ); \d test_like_id_2 INSERT INTO test_like_id_2 (b) VALUES ('b2'); SELECT * FROM test_like_id_2; -- identity was not copied CREATE TABLE test_like_id_3 ( LIKE test_like_id_1 INCLUDING IDENTITY ); \d test_like_id_3 INSERT INTO test_like_id_3 (b) VALUES ('b3'); SELECT * FROM test_like_id_3; -- identity was copied and applied DROP TABLE test_like_id_1, test_like_id_2, test_like_id_3; CREATE TABLE test_like_gen_1 ( a int, b int GENERATED ALWAYS AS (a * 2) STORED ); \d test_like_gen_1 INSERT INTO test_like_gen_1 (a) VALUES (1); SELECT * FROM test_like_gen_1; CREATE TABLE test_like_gen_2 ( LIKE test_like_gen_1 ); \d test_like_gen_2 INSERT INTO test_like_gen_2 (a) VALUES (1); SELECT * FROM test_like_gen_2; CREATE TABLE test_like_gen_3 ( LIKE test_like_gen_1 INCLUDING GENERATED ); \d test_like_gen_3 INSERT INTO test_like_gen_3 (a) VALUES (1); SELECT * FROM test_like_gen_3; DROP TABLE test_like_gen_1, test_like_gen_2, test_like_gen_3; CREATE TABLE inhg ( x text, LIKE inhx INCLUDING INDEXES, y text ); /* copies indexes */ INSERT INTO inhg VALUES (5, 10); INSERT INTO inhg VALUES (20, 10); -- should fail DROP TABLE inhg; /* Multiple primary keys creation should fail */ CREATE TABLE inhg ( x text, LIKE inhx INCLUDING INDEXES, PRIMARY KEY (x) ); /* fails */ CREATE TABLE inhz ( xx text DEFAULT 'text', yy int UNIQUE ); CREATE UNIQUE INDEX inhz_xx_idx ON inhz (xx) WHERE xx <> 'test'; /* Ok to create multiple unique indexes */ CREATE TABLE inhg ( x text UNIQUE, LIKE inhz INCLUDING INDEXES ); INSERT INTO inhg (xx, yy, x) VALUES ('test', 5, 10); INSERT INTO inhg (xx, yy, x) VALUES ('test', 10, 15); INSERT INTO inhg (xx, yy, x) VALUES ('foo', 10, 15); -- should fail DROP TABLE inhg; DROP TABLE inhz; -- including storage and comments CREATE TABLE ctlt1 ( a text CHECK (length(a) > 2) PRIMARY KEY, b text ); CREATE INDEX ctlt1_b_key ON ctlt1 (b); CREATE INDEX ctlt1_fnidx ON ctlt1 ((a || b)); CREATE STATISTICS ctlt1_a_b_stat ON a, b FROM ctlt1; COMMENT ON STATISTICS ctlt1_a_b_stat IS 'ab stats'; COMMENT ON COLUMN ctlt1.a IS 'A'; COMMENT ON COLUMN ctlt1.b IS 'B'; COMMENT ON CONSTRAINT ctlt1_a_check ON ctlt1 IS 't1_a_check'; COMMENT ON INDEX ctlt1_pkey IS 'index pkey'; COMMENT ON INDEX ctlt1_b_key IS 'index b_key'; ALTER TABLE ctlt1 ALTER COLUMN a SET STORAGE MAIN; CREATE TABLE ctlt2 ( c text ); ALTER TABLE ctlt2 ALTER COLUMN c SET STORAGE EXTERNAL; COMMENT ON COLUMN ctlt2.c IS 'C'; CREATE TABLE ctlt3 ( a text CHECK (length(a) < 5), c text ); ALTER TABLE ctlt3 ALTER COLUMN c SET STORAGE EXTERNAL; ALTER TABLE ctlt3 ALTER COLUMN a SET STORAGE MAIN; COMMENT ON COLUMN ctlt3.a IS 'A3'; COMMENT ON COLUMN ctlt3.c IS 'C'; COMMENT ON CONSTRAINT ctlt3_a_check ON ctlt3 IS 't3_a_check'; CREATE TABLE ctlt4 ( a text, c text ); ALTER TABLE ctlt4 ALTER COLUMN c SET STORAGE EXTERNAL; CREATE TABLE ctlt12_storage ( LIKE ctlt1 INCLUDING STORAGE, LIKE ctlt2 INCLUDING STORAGE ); \d+ ctlt12_storage CREATE TABLE ctlt12_comments ( LIKE ctlt1 INCLUDING COMMENTS, LIKE ctlt2 INCLUDING COMMENTS ); \d+ ctlt12_comments CREATE TABLE ctlt1_inh ( LIKE ctlt1 INCLUDING CONSTRAINTS INCLUDING COMMENTS ) INHERITS ( ctlt1 ); \d+ ctlt1_inh SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 'ctlt1_inh'::regclass; CREATE TABLE ctlt13_inh () INHERITS ( ctlt1, ctlt3 ); \d+ ctlt13_inh CREATE TABLE ctlt13_like ( LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING STORAGE ) INHERITS ( ctlt1 ); \d+ ctlt13_like SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 'ctlt13_like'::regclass; CREATE TABLE ctlt_all ( LIKE ctlt1 INCLUDING ALL ); \d+ ctlt_all SELECT c.relname, objsubid, description FROM pg_description, pg_index i, pg_class c WHERE classoid = 'pg_class'::regclass AND objoid = i.indexrelid AND c.oid = i.indexrelid AND i.indrelid = 'ctlt_all'::regclass ORDER BY c.relname, objsubid; SELECT s.stxname, objsubid, description FROM pg_description, pg_statistic_ext s WHERE classoid = 'pg_statistic_ext'::regclass AND objoid = s.oid AND s.stxrelid = 'ctlt_all'::regclass ORDER BY s.stxname, objsubid; CREATE TABLE inh_error1 () INHERITS ( ctlt1, ctlt4 ); CREATE TABLE inh_error2 ( LIKE ctlt4 INCLUDING STORAGE ) INHERITS ( ctlt1 ); DROP TABLE ctlt1, ctlt2, ctlt3, ctlt4, ctlt12_storage, ctlt12_comments, ctlt1_inh, ctlt13_inh, ctlt13_like, ctlt_all, ctla, ctlb CASCADE; /* LIKE with other relation kinds */ CREATE TABLE ctlt4 ( a int, b text ); CREATE SEQUENCE ctlseq1; CREATE TABLE ctlt10 ( LIKE ctlseq1 ); -- fail CREATE VIEW ctlv1 AS SELECT * FROM ctlt4; CREATE TABLE ctlt11 ( LIKE ctlv1 ); CREATE TABLE ctlt11a ( LIKE ctlv1 INCLUDING ALL ); CREATE TYPE ctlty1 AS ( a int, b text ); CREATE TABLE ctlt12 ( LIKE ctlty1 ); DROP SEQUENCE ctlseq1; DROP TYPE ctlty1; DROP VIEW ctlv1; DROP TABLE IF EXISTS ctlt4, ctlt10, ctlt11, ctlt11a, ctlt12; pgFormatter-4.2/t/pg-test-files/expected/create_type.sql000066400000000000000000000110051361326045100234230ustar00rootroot00000000000000-- -- CREATE_TYPE -- -- -- Note: widget_in/out were created in create_function_1, without any -- prior shell-type creation. These commands therefore complete a test -- of the "old style" approach of making the functions first. -- CREATE TYPE widget ( internallength = 24, input = widget_in, output = widget_out, typmod_in = numerictypmodin, typmod_out = numerictypmodout, alignment = double ); CREATE TYPE city_budget ( internallength = 16, input = int44in, output = int44out, element = int4, category = 'x', -- just to verify the system will take it preferred = TRUE -- ditto ); -- Test creation and destruction of shell types CREATE TYPE shell; CREATE TYPE shell; -- fail, type already present DROP TYPE shell; DROP TYPE shell; -- fail, type not exist -- also, let's leave one around for purposes of pg_dump testing CREATE TYPE myshell; -- -- Test type-related default values (broken in releases before PG 7.2) -- -- This part of the test also exercises the "new style" approach of making -- a shell type and then filling it in. -- CREATE TYPE int42; CREATE TYPE text_w_default; -- Make dummy I/O routines using the existing internal support for int4, text CREATE FUNCTION int42_in (cstring) RETURNS int42 AS 'int4in' LANGUAGE internal STRICT IMMUTABLE; CREATE FUNCTION int42_out (int42) RETURNS cstring AS 'int4out' LANGUAGE internal STRICT IMMUTABLE; CREATE FUNCTION text_w_default_in (cstring) RETURNS text_w_default AS 'textin' LANGUAGE internal STRICT IMMUTABLE; CREATE FUNCTION text_w_default_out (text_w_default) RETURNS cstring AS 'textout' LANGUAGE internal STRICT IMMUTABLE; CREATE TYPE int42 ( internallength = 4, input = int42_in, output = int42_out, alignment = int4, DEFAULT = 42, passedbyvalue ); CREATE TYPE text_w_default ( internallength = variable, input = text_w_default_in, output = text_w_default_out, alignment = int4, DEFAULT = 'zippo' ); CREATE TABLE default_test ( f1 text_w_default, f2 int42 ); INSERT INTO default_test DEFAULT VALUES; SELECT * FROM default_test; -- invalid: non-lowercase quoted identifiers CREATE TYPE case_int42 ( "Internallength" = 4, "Input" = int42_in, "Output" = int42_out, "Alignment" = int4, "Default" = 42, "Passedbyvalue" ); -- Test stand-alone composite type CREATE TYPE default_test_row AS ( f1 text_w_default, f2 int42 ); CREATE FUNCTION get_default_test () RETURNS SETOF default_test_row AS $$ SELECT * FROM default_test; $$ LANGUAGE SQL; SELECT * FROM get_default_test (); -- Test comments COMMENT ON TYPE bad IS 'bad comment'; COMMENT ON TYPE default_test_row IS 'good comment'; COMMENT ON TYPE default_test_row IS NULL; COMMENT ON COLUMN default_test_row.nope IS 'bad comment'; COMMENT ON COLUMN default_test_row.f1 IS 'good comment'; COMMENT ON COLUMN default_test_row.f1 IS NULL; -- Check shell type create for existing types CREATE TYPE text_w_default; -- should fail DROP TYPE default_test_row CASCADE; DROP TABLE default_test; -- Check type create with input/output incompatibility CREATE TYPE not_existing_type ( INPUT = array_in, OUTPUT = array_out, ELEMENT = int, INTERNALLENGTH = 32 ); -- Check dependency transfer of opaque functions when creating a new type CREATE FUNCTION base_fn_in (cstring) RETURNS opaque AS 'boolin' LANGUAGE internal IMMUTABLE STRICT; CREATE FUNCTION base_fn_out (opaque) RETURNS opaque AS 'boolout' LANGUAGE internal IMMUTABLE STRICT; CREATE TYPE base_type ( INPUT = base_fn_in, OUTPUT = base_fn_out ); DROP FUNCTION base_fn_in (cstring); -- error DROP FUNCTION base_fn_out (opaque); -- error DROP TYPE base_type; -- error DROP TYPE base_type CASCADE; -- Check usage of typmod with a user-defined type -- (we have borrowed numeric's typmod functions) CREATE TEMP TABLE mytab ( foo widget (42, 13, 7) ); -- should fail CREATE TEMP TABLE mytab ( foo widget (42, 13) ); SELECT format_type(atttypid, atttypmod) FROM pg_attribute WHERE attrelid = 'mytab'::regclass AND attnum > 0; -- might as well exercise the widget type while we're here INSERT INTO mytab VALUES ('(1,2,3)'), ('(-44,5.5,12)'); TABLE mytab; -- and test format_type() a bit more, too SELECT format_type('varchar'::regtype, 42); SELECT format_type('bpchar'::regtype, NULL); -- this behavior difference is intentional SELECT format_type('bpchar'::regtype, - 1); pgFormatter-4.2/t/pg-test-files/expected/create_view.sql000066400000000000000000000524521361326045100234270ustar00rootroot00000000000000-- -- CREATE_VIEW -- Virtual class definitions -- (this also tests the query rewrite system) -- CREATE VIEW street AS SELECT r.name, r.thepath, c.cname AS cname FROM ONLY road r, real_city c WHERE c.outline ## r.thepath; CREATE VIEW iexit AS SELECT ih.name, ih.thepath, interpt_pp (ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE ih.thepath ## r.thepath; CREATE VIEW toyemp AS SELECT name, age, LOCATION, 12 * salary AS annualsal FROM emp; -- Test comments COMMENT ON VIEW noview IS 'no view'; COMMENT ON VIEW toyemp IS 'is a view'; COMMENT ON VIEW toyemp IS NULL; -- These views are left around mainly to exercise special cases in pg_dump. CREATE TABLE view_base_table ( key int PRIMARY KEY, data varchar(20) ); CREATE VIEW key_dependent_view AS SELECT * FROM view_base_table GROUP BY KEY; ALTER TABLE view_base_table DROP CONSTRAINT view_base_table_pkey; -- fails CREATE VIEW key_dependent_view_no_cols AS SELECT FROM view_base_table GROUP BY KEY HAVING length(data) > 0; -- -- CREATE OR REPLACE VIEW -- CREATE OR REPLACE VIEW viewtest AS SELECT * FROM viewtest_tbl; CREATE OR REPLACE VIEW viewtest AS SELECT * FROM viewtest_tbl WHERE a > 10; SELECT * FROM viewtest; CREATE OR REPLACE VIEW viewtest AS SELECT a, b FROM viewtest_tbl WHERE a > 5 ORDER BY b DESC; SELECT * FROM viewtest; -- should fail CREATE OR REPLACE VIEW viewtest AS SELECT a FROM viewtest_tbl WHERE a <> 20; -- should fail CREATE OR REPLACE VIEW viewtest AS SELECT 1, * FROM viewtest_tbl; -- should fail CREATE OR REPLACE VIEW viewtest AS SELECT a, b::numeric FROM viewtest_tbl; -- should work CREATE OR REPLACE VIEW viewtest AS SELECT a, b, 0 AS c FROM viewtest_tbl; DROP VIEW viewtest; DROP TABLE viewtest_tbl; -- tests for temporary views CREATE SCHEMA temp_view_test CREATE TABLE base_table ( a int, id int) CREATE TABLE base_table2 ( a int, id int ); SET search_path TO temp_view_test, public; CREATE TEMPORARY TABLE temp_table ( a int, id int ); -- should be created in temp_view_test schema CREATE VIEW v1 AS SELECT * FROM base_table; -- should be created in temp object schema CREATE VIEW v1_temp AS SELECT * FROM temp_table; -- should be created in temp object schema CREATE TEMP VIEW v2_temp AS SELECT * FROM base_table; -- should be created in temp_views schema CREATE VIEW temp_view_test.v2 AS SELECT * FROM base_table; -- should fail CREATE VIEW temp_view_test.v3_temp AS SELECT * FROM temp_table; -- should fail CREATE SCHEMA test_view_schema CREATE TEMP VIEW testview AS SELECT 1; -- joins: if any of the join relations are temporary, the view -- should also be temporary -- should be non-temp CREATE VIEW v3 AS SELECT t1.a AS t1_a, t2.a AS t2_a FROM base_table t1, base_table2 t2 WHERE t1.id = t2.id; -- should be temp (one join rel is temp) CREATE VIEW v4_temp AS SELECT t1.a AS t1_a, t2.a AS t2_a FROM base_table t1, temp_table t2 WHERE t1.id = t2.id; -- should be temp CREATE VIEW v5_temp AS SELECT t1.a AS t1_a, t2.a AS t2_a, t3.a AS t3_a FROM base_table t1, base_table2 t2, temp_table t3 WHERE t1.id = t2.id AND t2.id = t3.id; -- subqueries CREATE VIEW v4 AS SELECT * FROM base_table WHERE id IN ( SELECT id FROM base_table2); CREATE VIEW v5 AS SELECT t1.id, t2.a FROM base_table t1, ( SELECT * FROM base_table2) t2; CREATE VIEW v6 AS SELECT * FROM base_table WHERE EXISTS ( SELECT 1 FROM base_table2); CREATE VIEW v7 AS SELECT * FROM base_table WHERE NOT EXISTS ( SELECT 1 FROM base_table2); CREATE VIEW v8 AS SELECT * FROM base_table WHERE EXISTS ( SELECT 1); CREATE VIEW v6_temp AS SELECT * FROM base_table WHERE id IN ( SELECT id FROM temp_table); CREATE VIEW v7_temp AS SELECT t1.id, t2.a FROM base_table t1, ( SELECT * FROM temp_table) t2; CREATE VIEW v8_temp AS SELECT * FROM base_table WHERE EXISTS ( SELECT 1 FROM temp_table); CREATE VIEW v9_temp AS SELECT * FROM base_table WHERE NOT EXISTS ( SELECT 1 FROM temp_table); -- a view should also be temporary if it references a temporary view CREATE VIEW v10_temp AS SELECT * FROM v7_temp; CREATE VIEW v11_temp AS SELECT t1.id, t2.a FROM base_table t1, v10_temp t2; CREATE VIEW v12_temp AS SELECT TRUE FROM v11_temp; -- a view should also be temporary if it references a temporary sequence CREATE SEQUENCE seq1; CREATE TEMPORARY SEQUENCE seq1_temp; CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1; CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp; SELECT relname FROM pg_class WHERE relname LIKE 'v_' AND relnamespace = ( SELECT oid FROM pg_namespace WHERE nspname = 'temp_view_test') ORDER BY relname; SELECT relname FROM pg_class WHERE relname LIKE 'v%' AND relnamespace IN ( SELECT oid FROM pg_namespace WHERE nspname LIKE 'pg_temp%') ORDER BY relname; CREATE SCHEMA testviewschm2; SET search_path TO testviewschm2, public; CREATE TABLE t1 ( num int, name text ); CREATE TABLE t2 ( num2 int, value text ); CREATE TEMP TABLE tt ( num2 int, value text ); CREATE VIEW nontemp1 AS SELECT * FROM t1 CROSS JOIN t2; CREATE VIEW temporal1 AS SELECT * FROM t1 CROSS JOIN tt; CREATE VIEW nontemp2 AS SELECT * FROM t1 INNER JOIN t2 ON t1.num = t2.num2; CREATE VIEW temporal2 AS SELECT * FROM t1 INNER JOIN tt ON t1.num = tt.num2; CREATE VIEW nontemp3 AS SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num2; CREATE VIEW temporal3 AS SELECT * FROM t1 LEFT JOIN tt ON t1.num = tt.num2; CREATE VIEW nontemp4 AS SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num2 AND t2.value = 'xxx'; CREATE VIEW temporal4 AS SELECT * FROM t1 LEFT JOIN tt ON t1.num = tt.num2 AND tt.value = 'xxx'; SELECT relname FROM pg_class WHERE relname LIKE 'nontemp%' AND relnamespace = ( SELECT oid FROM pg_namespace WHERE nspname = 'testviewschm2') ORDER BY relname; SELECT relname FROM pg_class WHERE relname LIKE 'temporal%' AND relnamespace IN ( SELECT oid FROM pg_namespace WHERE nspname LIKE 'pg_temp%') ORDER BY relname; CREATE TABLE tbl1 ( a int, b int ); CREATE TABLE tbl2 ( c int, d int ); CREATE TABLE tbl3 ( e int, f int ); CREATE TABLE tbl4 ( g int, h int ); CREATE TEMP TABLE tmptbl ( i int, j int ); --Should be in testviewschm2 CREATE VIEW pubview AS SELECT * FROM tbl1 WHERE tbl1.a BETWEEN ( SELECT d FROM tbl2 WHERE c = 1) AND ( SELECT e FROM tbl3 WHERE f = 2) AND EXISTS ( SELECT g FROM tbl4 LEFT JOIN tbl3 ON tbl4.h = tbl3.f); SELECT count(*) FROM pg_class WHERE relname = 'pubview' AND relnamespace IN ( SELECT OID FROM pg_namespace WHERE nspname = 'testviewschm2'); --Should be in temp object schema CREATE VIEW mytempview AS SELECT * FROM tbl1 WHERE tbl1.a BETWEEN ( SELECT d FROM tbl2 WHERE c = 1) AND ( SELECT e FROM tbl3 WHERE f = 2) AND EXISTS ( SELECT g FROM tbl4 LEFT JOIN tbl3 ON tbl4.h = tbl3.f) AND NOT EXISTS ( SELECT g FROM tbl4 LEFT JOIN tmptbl ON tbl4.h = tmptbl.j); SELECT count(*) FROM pg_class WHERE relname LIKE 'mytempview' AND relnamespace IN ( SELECT OID FROM pg_namespace WHERE nspname LIKE 'pg_temp%'); -- -- CREATE VIEW and WITH(...) clause -- CREATE VIEW mysecview1 AS SELECT * FROM tbl1 WHERE a = 0; CREATE VIEW mysecview2 WITH ( security_barrier = TRUE ) AS SELECT * FROM tbl1 WHERE a > 0; CREATE VIEW mysecview3 WITH ( security_barrier = FALSE ) AS SELECT * FROM tbl1 WHERE a < 0; CREATE VIEW mysecview4 WITH ( security_barrier ) AS SELECT * FROM tbl1 WHERE a <> 0; CREATE VIEW mysecview5 WITH ( security_barrier = 100) -- Error AS SELECT * FROM tbl1 WHERE a > 100; CREATE VIEW mysecview6 WITH ( invalid_option) -- Error AS SELECT * FROM tbl1 WHERE a < 100; SELECT relname, relkind, reloptions FROM pg_class WHERE oid IN ('mysecview1'::regclass, 'mysecview2'::regclass, 'mysecview3'::regclass, 'mysecview4'::regclass) ORDER BY relname; CREATE OR REPLACE VIEW mysecview1 AS SELECT * FROM tbl1 WHERE a = 256; CREATE OR REPLACE VIEW mysecview2 AS SELECT * FROM tbl1 WHERE a > 256; CREATE OR REPLACE VIEW mysecview3 WITH ( security_barrier = TRUE ) AS SELECT * FROM tbl1 WHERE a < 256; CREATE OR REPLACE VIEW mysecview4 WITH ( security_barrier = FALSE ) AS SELECT * FROM tbl1 WHERE a <> 256; SELECT relname, relkind, reloptions FROM pg_class WHERE oid IN ('mysecview1'::regclass, 'mysecview2'::regclass, 'mysecview3'::regclass, 'mysecview4'::regclass) ORDER BY relname; -- Check that unknown literals are converted to "text" in CREATE VIEW, -- so that we don't end up with unknown-type columns. CREATE VIEW unspecified_types AS SELECT 42 AS i, 42.5 AS num, 'foo' AS u, 'foo'::unknown AS u2, NULL AS n; \d+ unspecified_types SELECT * FROM unspecified_types; -- This test checks that proper typmods are assigned in a multi-row VALUES CREATE VIEW tt1 AS SELECT * FROM ( VALUES ('abc'::varchar(3), '0123456789', 42, 'abcd'::varchar(4)), ('0123456789', 'abc'::varchar(3), 42.12, 'abc'::varchar(4))) vv (a, b, c, d); \d+ tt1 SELECT * FROM tt1; SELECT a::varchar(3) FROM tt1; DROP VIEW tt1; -- Test view decompilation in the face of relation renaming conflicts CREATE TABLE tt1 ( f1 int, f2 int, f3 text ); CREATE TABLE tx1 ( x1 int, x2 int, x3 text ); CREATE TABLE temp_view_test.tt1 ( y1 int, f2 int, f3 text ); CREATE VIEW aliased_view_1 AS SELECT * FROM tt1 WHERE EXISTS ( SELECT 1 FROM tx1 WHERE tt1.f1 = tx1.x1); CREATE VIEW aliased_view_2 AS SELECT * FROM tt1 a1 WHERE EXISTS ( SELECT 1 FROM tx1 WHERE a1.f1 = tx1.x1); CREATE VIEW aliased_view_3 AS SELECT * FROM tt1 WHERE EXISTS ( SELECT 1 FROM tx1 a2 WHERE tt1.f1 = a2.x1); CREATE VIEW aliased_view_4 AS SELECT * FROM temp_view_test.tt1 WHERE EXISTS ( SELECT 1 FROM tt1 WHERE temp_view_test.tt1.y1 = tt1.f1); \d+ aliased_view_1 \d+ aliased_view_2 \d+ aliased_view_3 \d+ aliased_view_4 ALTER TABLE tx1 RENAME TO a1; \d+ aliased_view_1 \d+ aliased_view_2 \d+ aliased_view_3 \d+ aliased_view_4 ALTER TABLE tt1 RENAME TO a2; \d+ aliased_view_1 \d+ aliased_view_2 \d+ aliased_view_3 \d+ aliased_view_4 ALTER TABLE a1 RENAME TO tt1; \d+ aliased_view_1 \d+ aliased_view_2 \d+ aliased_view_3 \d+ aliased_view_4 ALTER TABLE a2 RENAME TO tx1; ALTER TABLE tx1 SET SCHEMA temp_view_test; \d+ aliased_view_1 \d+ aliased_view_2 \d+ aliased_view_3 \d+ aliased_view_4 ALTER TABLE temp_view_test.tt1 RENAME TO tmp1; ALTER TABLE temp_view_test.tmp1 SET SCHEMA testviewschm2; ALTER TABLE tmp1 RENAME TO tx1; \d+ aliased_view_1 \d+ aliased_view_2 \d+ aliased_view_3 \d+ aliased_view_4 -- Test view decompilation in the face of column addition/deletion/renaming CREATE TABLE tt2 ( a int, b int, c int ); CREATE TABLE tt3 ( ax int8, b int2, c numeric ); CREATE TABLE tt4 ( ay int, b int, q int ); CREATE VIEW v1 AS SELECT * FROM tt2 NATURAL JOIN tt3; CREATE VIEW v1a AS SELECT * FROM (tt2 NATURAL JOIN tt3) j; CREATE VIEW v2 AS SELECT * FROM tt2 JOIN tt3 USING (b, c) JOIN tt4 USING (b); CREATE VIEW v2a AS SELECT * FROM (tt2 JOIN tt3 USING (b, c) JOIN tt4 USING (b)) j; CREATE VIEW v3 AS SELECT * FROM tt2 JOIN tt3 USING (b, c) FULL JOIN tt4 USING (b); SELECT pg_get_viewdef('v1', TRUE); SELECT pg_get_viewdef('v1a', TRUE); SELECT pg_get_viewdef('v2', TRUE); SELECT pg_get_viewdef('v2a', TRUE); SELECT pg_get_viewdef('v3', TRUE); ALTER TABLE tt2 ADD COLUMN d int; ALTER TABLE tt2 ADD COLUMN e int; SELECT pg_get_viewdef('v1', TRUE); SELECT pg_get_viewdef('v1a', TRUE); SELECT pg_get_viewdef('v2', TRUE); SELECT pg_get_viewdef('v2a', TRUE); SELECT pg_get_viewdef('v3', TRUE); ALTER TABLE tt3 RENAME c TO d; SELECT pg_get_viewdef('v1', TRUE); SELECT pg_get_viewdef('v1a', TRUE); SELECT pg_get_viewdef('v2', TRUE); SELECT pg_get_viewdef('v2a', TRUE); SELECT pg_get_viewdef('v3', TRUE); ALTER TABLE tt3 ADD COLUMN c int; ALTER TABLE tt3 ADD COLUMN e int; SELECT pg_get_viewdef('v1', TRUE); SELECT pg_get_viewdef('v1a', TRUE); SELECT pg_get_viewdef('v2', TRUE); SELECT pg_get_viewdef('v2a', TRUE); SELECT pg_get_viewdef('v3', TRUE); ALTER TABLE tt2 DROP COLUMN d; SELECT pg_get_viewdef('v1', TRUE); SELECT pg_get_viewdef('v1a', TRUE); SELECT pg_get_viewdef('v2', TRUE); SELECT pg_get_viewdef('v2a', TRUE); SELECT pg_get_viewdef('v3', TRUE); CREATE TABLE tt5 ( a int, b int ); CREATE TABLE tt6 ( c int, d int ); CREATE VIEW vv1 AS SELECT * FROM (tt5 CROSS JOIN tt6) j (aa, bb, cc, dd); SELECT pg_get_viewdef('vv1', TRUE); ALTER TABLE tt5 ADD COLUMN c int; SELECT pg_get_viewdef('vv1', TRUE); ALTER TABLE tt5 ADD COLUMN cc int; SELECT pg_get_viewdef('vv1', TRUE); ALTER TABLE tt5 DROP COLUMN c; SELECT pg_get_viewdef('vv1', TRUE); -- Unnamed FULL JOIN USING is lots of fun too CREATE TABLE tt7 ( x int, xx int, y int ); ALTER TABLE tt7 DROP COLUMN xx; CREATE TABLE tt8 ( x int, z int ); CREATE VIEW vv2 AS SELECT * FROM ( VALUES (1, 2, 3, 4, 5)) v (a, b, c, d, e) UNION ALL SELECT * FROM tt7 FULL JOIN tt8 USING (x), tt8 tt8x; SELECT pg_get_viewdef('vv2', TRUE); CREATE VIEW vv3 AS SELECT * FROM ( VALUES (1, 2, 3, 4, 5, 6)) v (a, b, c, x, e, f) UNION ALL SELECT * FROM tt7 FULL JOIN tt8 USING (x), tt7 tt7x FULL JOIN tt8 tt8x USING (x); SELECT pg_get_viewdef('vv3', TRUE); CREATE VIEW vv4 AS SELECT * FROM ( VALUES (1, 2, 3, 4, 5, 6, 7)) v (a, b, c, x, e, f, g) UNION ALL SELECT * FROM tt7 FULL JOIN tt8 USING (x), tt7 tt7x FULL JOIN tt8 tt8x USING (x) FULL JOIN tt8 tt8y USING (x); SELECT pg_get_viewdef('vv4', TRUE); ALTER TABLE tt7 ADD COLUMN zz int; ALTER TABLE tt7 ADD COLUMN z int; ALTER TABLE tt7 DROP COLUMN zz; ALTER TABLE tt8 ADD COLUMN z2 int; SELECT pg_get_viewdef('vv2', TRUE); SELECT pg_get_viewdef('vv3', TRUE); SELECT pg_get_viewdef('vv4', TRUE); -- Implicit coercions in a JOIN USING create issues similar to FULL JOIN CREATE TABLE tt7a ( x date, xx int, y int ); ALTER TABLE tt7a DROP COLUMN xx; CREATE TABLE tt8a ( x timestamptz, z int ); CREATE VIEW vv2a AS SELECT * FROM ( VALUES (now(), 2, 3, now(), 5)) v (a, b, c, d, e) UNION ALL SELECT * FROM tt7a LEFT JOIN tt8a USING (x), tt8a tt8ax; SELECT pg_get_viewdef('vv2a', TRUE); -- -- Also check dropping a column that existed when the view was made -- CREATE TABLE tt9 ( x int, xx int, y int ); CREATE TABLE tt10 ( x int, z int ); CREATE VIEW vv5 AS SELECT x, y, z FROM tt9 JOIN tt10 USING (x); SELECT pg_get_viewdef('vv5', TRUE); ALTER TABLE tt9 DROP COLUMN xx; SELECT pg_get_viewdef('vv5', TRUE); -- -- Another corner case is that we might add a column to a table below a -- JOIN USING, and thereby make the USING column name ambiguous -- CREATE TABLE tt11 ( x int, y int ); CREATE TABLE tt12 ( x int, z int ); CREATE TABLE tt13 ( z int, q int ); CREATE VIEW vv6 AS SELECT x, y, z, q FROM (tt11 JOIN tt12 USING (x)) JOIN tt13 USING (z); SELECT pg_get_viewdef('vv6', TRUE); ALTER TABLE tt11 ADD COLUMN z int; SELECT pg_get_viewdef('vv6', TRUE); -- -- Check cases involving dropped/altered columns in a function's rowtype result -- CREATE TABLE tt14t ( f1 text, f2 text, f3 text, f4 text ); INSERT INTO tt14t VALUES ('foo', 'bar', 'baz', '42'); ALTER TABLE tt14t DROP COLUMN f2; CREATE FUNCTION tt14f () RETURNS SETOF tt14t AS $$ DECLARE rec1 record; BEGIN FOR rec1 IN SELECT * FROM tt14t LOOP RETURN NEXT rec1; END LOOP; END; $$ LANGUAGE plpgsql; CREATE VIEW tt14v AS SELECT t.* FROM tt14f () t; SELECT pg_get_viewdef('tt14v', TRUE); SELECT * FROM tt14v; BEGIN; -- this perhaps should be rejected, but it isn't: ALTER TABLE tt14t DROP COLUMN f3; -- f3 is still in the view ... SELECT pg_get_viewdef('tt14v', TRUE); -- but will fail at execution SELECT f1, f4 FROM tt14v; SELECT * FROM tt14v; ROLLBACK; BEGIN; -- this perhaps should be rejected, but it isn't: ALTER TABLE tt14t ALTER COLUMN f4 TYPE integer USING f4::integer; -- f4 is still in the view ... SELECT pg_get_viewdef('tt14v', TRUE); -- but will fail at execution SELECT f1, f3 FROM tt14v; SELECT * FROM tt14v; ROLLBACK; -- check display of whole-row variables in some corner cases CREATE TYPE nestedcomposite AS ( x int8_tbl ); CREATE VIEW tt15v AS SELECT ROW (i)::nestedcomposite FROM int8_tbl i; SELECT * FROM tt15v; SELECT pg_get_viewdef('tt15v', TRUE); SELECT ROW (i.*::int8_tbl)::nestedcomposite FROM int8_tbl i; CREATE VIEW tt16v AS SELECT * FROM int8_tbl i, LATERAL ( VALUES (i)) ss; SELECT * FROM tt16v; SELECT pg_get_viewdef('tt16v', TRUE); SELECT * FROM int8_tbl i, LATERAL ( VALUES (i.*::int8_tbl)) ss; CREATE VIEW tt17v AS SELECT * FROM int8_tbl i WHERE i IN ( VALUES (i)); SELECT * FROM tt17v; SELECT pg_get_viewdef('tt17v', TRUE); SELECT * FROM int8_tbl i WHERE i.* IN ( VALUES (i.*::int8_tbl)); -- check unique-ification of overlength names CREATE VIEW tt18v AS SELECT * FROM int8_tbl xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxy UNION ALL SELECT * FROM int8_tbl xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxz; SELECT pg_get_viewdef('tt18v', TRUE); EXPLAIN ( COSTS OFF ) SELECT * FROM tt18v; -- check display of ScalarArrayOp with a sub-select SELECT 'foo'::text = ANY (ARRAY['abc', 'def', 'foo']::text[]); SELECT 'foo'::text = ANY (( SELECT ARRAY['abc', 'def', 'foo']::text[])); -- fail SELECT 'foo'::text = ANY (( SELECT ARRAY['abc', 'def', 'foo']::text[])::text[]); CREATE VIEW tt19v AS SELECT 'foo'::text = ANY (ARRAY['abc', 'def', 'foo']::text[]) c1, 'foo'::text = ANY (( SELECT ARRAY['abc', 'def', 'foo']::text[])::text[]) c2; SELECT pg_get_viewdef('tt19v', TRUE); -- check display of assorted RTE_FUNCTION expressions CREATE VIEW tt20v AS SELECT * FROM coalesce(1, 2) AS c, COLLATION FOR ('x'::text) col, CURRENT_DATE AS d, localtimestamp(3) AS t, cast(1 + 2 AS int4) AS i4, cast(1 + 2 AS int8) AS i8; SELECT pg_get_viewdef('tt20v', TRUE); -- corner cases with empty join conditions CREATE VIEW tt21v AS SELECT * FROM tt5 NATURAL INNER JOIN tt6; SELECT pg_get_viewdef('tt21v', TRUE); CREATE VIEW tt22v AS SELECT * FROM tt5 NATURAL LEFT JOIN tt6; SELECT pg_get_viewdef('tt22v', TRUE); -- check handling of views with immediately-renamed columns CREATE VIEW tt23v (col_a, col_b) AS SELECT q1 AS other_name1, q2 AS other_name2 FROM int8_tbl UNION SELECT 42, 43; SELECT pg_get_viewdef('tt23v', TRUE); SELECT pg_get_ruledef(oid, TRUE) FROM pg_rewrite WHERE ev_class = 'tt23v'::regclass AND ev_type = '1'; -- clean up all the random objects we made above DROP SCHEMA temp_view_test CASCADE; DROP SCHEMA testviewschm2 CASCADE; pgFormatter-4.2/t/pg-test-files/expected/date.sql000066400000000000000000000300241361326045100220360ustar00rootroot00000000000000-- -- DATE -- CREATE TABLE DATE_TBL ( f1 date ); INSERT INTO DATE_TBL VALUES ('1957-04-09'); INSERT INTO DATE_TBL VALUES ('1957-06-13'); INSERT INTO DATE_TBL VALUES ('1996-02-28'); INSERT INTO DATE_TBL VALUES ('1996-02-29'); INSERT INTO DATE_TBL VALUES ('1996-03-01'); INSERT INTO DATE_TBL VALUES ('1996-03-02'); INSERT INTO DATE_TBL VALUES ('1997-02-28'); INSERT INTO DATE_TBL VALUES ('1997-02-29'); INSERT INTO DATE_TBL VALUES ('1997-03-01'); INSERT INTO DATE_TBL VALUES ('1997-03-02'); INSERT INTO DATE_TBL VALUES ('2000-04-01'); INSERT INTO DATE_TBL VALUES ('2000-04-02'); INSERT INTO DATE_TBL VALUES ('2000-04-03'); INSERT INTO DATE_TBL VALUES ('2038-04-08'); INSERT INTO DATE_TBL VALUES ('2039-04-09'); INSERT INTO DATE_TBL VALUES ('2040-04-10'); SELECT f1 AS "Fifteen" FROM DATE_TBL; SELECT f1 AS "Nine" FROM DATE_TBL WHERE f1 < '2000-01-01'; SELECT f1 AS "Three" FROM DATE_TBL WHERE f1 BETWEEN '2000-01-01' AND '2001-01-01'; -- -- Check all the documented input formats -- SET datestyle TO iso; -- display results in ISO SET datestyle TO ymd; SELECT date 'January 8, 1999'; SELECT date '1999-01-08'; SELECT date '1999-01-18'; SELECT date '1/8/1999'; SELECT date '1/18/1999'; SELECT date '18/1/1999'; SELECT date '01/02/03'; SELECT date '19990108'; SELECT date '990108'; SELECT date '1999.008'; SELECT date 'J2451187'; SELECT date 'January 8, 99 BC'; SELECT date '99-Jan-08'; SELECT date '1999-Jan-08'; SELECT date '08-Jan-99'; SELECT date '08-Jan-1999'; SELECT date 'Jan-08-99'; SELECT date 'Jan-08-1999'; SELECT date '99-08-Jan'; SELECT date '1999-08-Jan'; SELECT date '99 Jan 08'; SELECT date '1999 Jan 08'; SELECT date '08 Jan 99'; SELECT date '08 Jan 1999'; SELECT date 'Jan 08 99'; SELECT date 'Jan 08 1999'; SELECT date '99 08 Jan'; SELECT date '1999 08 Jan'; SELECT date '99-01-08'; SELECT date '1999-01-08'; SELECT date '08-01-99'; SELECT date '08-01-1999'; SELECT date '01-08-99'; SELECT date '01-08-1999'; SELECT date '99-08-01'; SELECT date '1999-08-01'; SELECT date '99 01 08'; SELECT date '1999 01 08'; SELECT date '08 01 99'; SELECT date '08 01 1999'; SELECT date '01 08 99'; SELECT date '01 08 1999'; SELECT date '99 08 01'; SELECT date '1999 08 01'; SET datestyle TO dmy; SELECT date 'January 8, 1999'; SELECT date '1999-01-08'; SELECT date '1999-01-18'; SELECT date '1/8/1999'; SELECT date '1/18/1999'; SELECT date '18/1/1999'; SELECT date '01/02/03'; SELECT date '19990108'; SELECT date '990108'; SELECT date '1999.008'; SELECT date 'J2451187'; SELECT date 'January 8, 99 BC'; SELECT date '99-Jan-08'; SELECT date '1999-Jan-08'; SELECT date '08-Jan-99'; SELECT date '08-Jan-1999'; SELECT date 'Jan-08-99'; SELECT date 'Jan-08-1999'; SELECT date '99-08-Jan'; SELECT date '1999-08-Jan'; SELECT date '99 Jan 08'; SELECT date '1999 Jan 08'; SELECT date '08 Jan 99'; SELECT date '08 Jan 1999'; SELECT date 'Jan 08 99'; SELECT date 'Jan 08 1999'; SELECT date '99 08 Jan'; SELECT date '1999 08 Jan'; SELECT date '99-01-08'; SELECT date '1999-01-08'; SELECT date '08-01-99'; SELECT date '08-01-1999'; SELECT date '01-08-99'; SELECT date '01-08-1999'; SELECT date '99-08-01'; SELECT date '1999-08-01'; SELECT date '99 01 08'; SELECT date '1999 01 08'; SELECT date '08 01 99'; SELECT date '08 01 1999'; SELECT date '01 08 99'; SELECT date '01 08 1999'; SELECT date '99 08 01'; SELECT date '1999 08 01'; SET datestyle TO mdy; SELECT date 'January 8, 1999'; SELECT date '1999-01-08'; SELECT date '1999-01-18'; SELECT date '1/8/1999'; SELECT date '1/18/1999'; SELECT date '18/1/1999'; SELECT date '01/02/03'; SELECT date '19990108'; SELECT date '990108'; SELECT date '1999.008'; SELECT date 'J2451187'; SELECT date 'January 8, 99 BC'; SELECT date '99-Jan-08'; SELECT date '1999-Jan-08'; SELECT date '08-Jan-99'; SELECT date '08-Jan-1999'; SELECT date 'Jan-08-99'; SELECT date 'Jan-08-1999'; SELECT date '99-08-Jan'; SELECT date '1999-08-Jan'; SELECT date '99 Jan 08'; SELECT date '1999 Jan 08'; SELECT date '08 Jan 99'; SELECT date '08 Jan 1999'; SELECT date 'Jan 08 99'; SELECT date 'Jan 08 1999'; SELECT date '99 08 Jan'; SELECT date '1999 08 Jan'; SELECT date '99-01-08'; SELECT date '1999-01-08'; SELECT date '08-01-99'; SELECT date '08-01-1999'; SELECT date '01-08-99'; SELECT date '01-08-1999'; SELECT date '99-08-01'; SELECT date '1999-08-01'; SELECT date '99 01 08'; SELECT date '1999 01 08'; SELECT date '08 01 99'; SELECT date '08 01 1999'; SELECT date '01 08 99'; SELECT date '01 08 1999'; SELECT date '99 08 01'; SELECT date '1999 08 01'; -- Check upper and lower limits of date range SELECT date '4714-11-24 BC'; SELECT date '4714-11-23 BC'; -- out of range SELECT date '5874897-12-31'; SELECT date '5874898-01-01'; -- out of range RESET datestyle; -- -- Simple math -- Leave most of it for the horology tests -- SELECT f1 - date '2000-01-01' AS "Days From 2K" FROM DATE_TBL; SELECT f1 - date 'epoch' AS "Days From Epoch" FROM DATE_TBL; SELECT date 'yesterday' - date 'today' AS "One day"; SELECT date 'today' - date 'tomorrow' AS "One day"; SELECT date 'yesterday' - date 'tomorrow' AS "Two days"; SELECT date 'tomorrow' - date 'today' AS "One day"; SELECT date 'today' - date 'yesterday' AS "One day"; SELECT date 'tomorrow' - date 'yesterday' AS "Two days"; -- -- test extract! -- -- epoch -- SELECT EXTRACT(EPOCH FROM DATE '1970-01-01'); -- 0 SELECT EXTRACT(EPOCH FROM TIMESTAMP '1970-01-01'); -- 0 SELECT EXTRACT(EPOCH FROM TIMESTAMPTZ '1970-01-01+00'); -- 0 -- -- century -- SELECT EXTRACT(CENTURY FROM DATE '0101-12-31 BC'); -- -2 SELECT EXTRACT(CENTURY FROM DATE '0100-12-31 BC'); -- -1 SELECT EXTRACT(CENTURY FROM DATE '0001-12-31 BC'); -- -1 SELECT EXTRACT(CENTURY FROM DATE '0001-01-01'); -- 1 SELECT EXTRACT(CENTURY FROM DATE '0001-01-01 AD'); -- 1 SELECT EXTRACT(CENTURY FROM DATE '1900-12-31'); -- 19 SELECT EXTRACT(CENTURY FROM DATE '1901-01-01'); -- 20 SELECT EXTRACT(CENTURY FROM DATE '2000-12-31'); -- 20 SELECT EXTRACT(CENTURY FROM DATE '2001-01-01'); -- 21 SELECT EXTRACT(CENTURY FROM CURRENT_DATE) >= 21 AS True; -- true -- -- millennium -- SELECT EXTRACT(MILLENNIUM FROM DATE '0001-12-31 BC'); -- -1 SELECT EXTRACT(MILLENNIUM FROM DATE '0001-01-01 AD'); -- 1 SELECT EXTRACT(MILLENNIUM FROM DATE '1000-12-31'); -- 1 SELECT EXTRACT(MILLENNIUM FROM DATE '1001-01-01'); -- 2 SELECT EXTRACT(MILLENNIUM FROM DATE '2000-12-31'); -- 2 SELECT EXTRACT(MILLENNIUM FROM DATE '2001-01-01'); -- 3 -- next test to be fixed on the turn of the next millennium;-) SELECT EXTRACT(MILLENNIUM FROM CURRENT_DATE); -- 3 -- -- decade -- SELECT EXTRACT(DECADE FROM DATE '1994-12-25'); -- 199 SELECT EXTRACT(DECADE FROM DATE '0010-01-01'); -- 1 SELECT EXTRACT(DECADE FROM DATE '0009-12-31'); -- 0 SELECT EXTRACT(DECADE FROM DATE '0001-01-01 BC'); -- 0 SELECT EXTRACT(DECADE FROM DATE '0002-12-31 BC'); -- -1 SELECT EXTRACT(DECADE FROM DATE '0011-01-01 BC'); -- -1 SELECT EXTRACT(DECADE FROM DATE '0012-12-31 BC'); -- -2 -- -- some other types: -- -- on a timestamp. SELECT EXTRACT(CENTURY FROM NOW()) >= 21 AS True; -- true SELECT EXTRACT(CENTURY FROM TIMESTAMP '1970-03-20 04:30:00.00000'); -- 20 -- on an interval SELECT EXTRACT(CENTURY FROM INTERVAL '100 y'); -- 1 SELECT EXTRACT(CENTURY FROM INTERVAL '99 y'); -- 0 SELECT EXTRACT(CENTURY FROM INTERVAL '-99 y'); -- 0 SELECT EXTRACT(CENTURY FROM INTERVAL '-100 y'); -- -1 -- -- test trunc function! -- SELECT DATE_TRUNC('MILLENNIUM', timestamp '1970-03-20 04:30:00.00000'); -- 1001 SELECT DATE_TRUNC('MILLENNIUM', date '1970-03-20'); -- 1001-01-01 SELECT DATE_TRUNC('CENTURY', timestamp '1970-03-20 04:30:00.00000'); -- 1901 SELECT DATE_TRUNC('CENTURY', date '1970-03-20'); -- 1901 SELECT DATE_TRUNC('CENTURY', date '2004-08-10'); -- 2001-01-01 SELECT DATE_TRUNC('CENTURY', date '0002-02-04'); -- 0001-01-01 SELECT DATE_TRUNC('CENTURY', date '0055-08-10 BC'); -- 0100-01-01 BC SELECT DATE_TRUNC('DECADE', date '1993-12-25'); -- 1990-01-01 SELECT DATE_TRUNC('DECADE', date '0004-12-25'); -- 0001-01-01 BC SELECT DATE_TRUNC('DECADE', date '0002-12-31 BC'); -- 0011-01-01 BC -- -- test infinity -- SELECT 'infinity'::date, '-infinity'::date; SELECT 'infinity'::date > 'today'::date AS t; SELECT '-infinity'::date < 'today'::date AS t; SELECT isfinite('infinity'::date), isfinite('-infinity'::date), isfinite('today'::date); -- -- oscillating fields from non-finite date/timestamptz: -- SELECT EXTRACT(HOUR FROM DATE 'infinity'); -- NULL SELECT EXTRACT(HOUR FROM DATE '-infinity'); -- NULL SELECT EXTRACT(HOUR FROM TIMESTAMP 'infinity'); -- NULL SELECT EXTRACT(HOUR FROM TIMESTAMP '-infinity'); -- NULL SELECT EXTRACT(HOUR FROM TIMESTAMPTZ 'infinity'); -- NULL SELECT EXTRACT(HOUR FROM TIMESTAMPTZ '-infinity'); -- NULL -- all possible fields SELECT EXTRACT(MICROSECONDS FROM DATE 'infinity'); -- NULL SELECT EXTRACT(MILLISECONDS FROM DATE 'infinity'); -- NULL SELECT EXTRACT(SECOND FROM DATE 'infinity'); -- NULL SELECT EXTRACT(MINUTE FROM DATE 'infinity'); -- NULL SELECT EXTRACT(HOUR FROM DATE 'infinity'); -- NULL SELECT EXTRACT(DAY FROM DATE 'infinity'); -- NULL SELECT EXTRACT(MONTH FROM DATE 'infinity'); -- NULL SELECT EXTRACT(QUARTER FROM DATE 'infinity'); -- NULL SELECT EXTRACT(WEEK FROM DATE 'infinity'); -- NULL SELECT EXTRACT(DOW FROM DATE 'infinity'); -- NULL SELECT EXTRACT(ISODOW FROM DATE 'infinity'); -- NULL SELECT EXTRACT(DOY FROM DATE 'infinity'); -- NULL SELECT EXTRACT(TIMEZONE FROM DATE 'infinity'); -- NULL SELECT EXTRACT(TIMEZONE_M FROM DATE 'infinity'); -- NULL SELECT EXTRACT(TIMEZONE_H FROM DATE 'infinity'); -- NULL -- -- monotonic fields from non-finite date/timestamptz: -- SELECT EXTRACT(EPOCH FROM DATE 'infinity'); -- Infinity SELECT EXTRACT(EPOCH FROM DATE '-infinity'); -- -Infinity SELECT EXTRACT(EPOCH FROM TIMESTAMP 'infinity'); -- Infinity SELECT EXTRACT(EPOCH FROM TIMESTAMP '-infinity'); -- -Infinity SELECT EXTRACT(EPOCH FROM TIMESTAMPTZ 'infinity'); -- Infinity SELECT EXTRACT(EPOCH FROM TIMESTAMPTZ '-infinity'); -- -Infinity -- all possible fields SELECT EXTRACT(YEAR FROM DATE 'infinity'); -- Infinity SELECT EXTRACT(DECADE FROM DATE 'infinity'); -- Infinity SELECT EXTRACT(CENTURY FROM DATE 'infinity'); -- Infinity SELECT EXTRACT(MILLENNIUM FROM DATE 'infinity'); -- Infinity SELECT EXTRACT(JULIAN FROM DATE 'infinity'); -- Infinity SELECT EXTRACT(ISOYEAR FROM DATE 'infinity'); -- Infinity SELECT EXTRACT(EPOCH FROM DATE 'infinity'); -- Infinity -- -- wrong fields from non-finite date: -- SELECT EXTRACT(MICROSEC FROM DATE 'infinity'); -- ERROR: timestamp units "microsec" not recognized SELECT EXTRACT(UNDEFINED FROM DATE 'infinity'); -- ERROR: timestamp units "undefined" not supported -- test constructors SELECT make_date (2013, 7, 15); SELECT make_date (- 44, 3, 15); SELECT make_time (8, 20, 0.0); -- should fail SELECT make_date (2013, 2, 30); SELECT make_date (2013, 13, 1); SELECT make_date (2013, 11, - 1); SELECT make_time (10, 55, 100.1); SELECT make_time (24, 0, 2.1); pgFormatter-4.2/t/pg-test-files/expected/dbsize.sql000066400000000000000000000046141361326045100224070ustar00rootroot00000000000000SELECT size, pg_size_pretty(size), pg_size_pretty(- 1 * size) FROM ( VALUES (10::bigint), (1000::bigint), (1000000::bigint), (1000000000::bigint), (1000000000000::bigint), (1000000000000000::bigint)) x (size); SELECT size, pg_size_pretty(size), pg_size_pretty(- 1 * size) FROM ( VALUES (10::numeric), (1000::numeric), (1000000::numeric), (1000000000::numeric), (1000000000000::numeric), (1000000000000000::numeric), (10.5::numeric), (1000.5::numeric), (1000000.5::numeric), (1000000000.5::numeric), (1000000000000.5::numeric), (1000000000000000.5::numeric)) x (size); SELECT size, pg_size_bytes (size) FROM ( VALUES ('1'), ('123bytes'), ('1kB'), ('1MB'), (' 1 GB'), ('1.5 GB '), ('1TB'), ('3000 TB'), ('1e6 MB')) x (size); -- case-insensitive units are supported SELECT size, pg_size_bytes (size) FROM ( VALUES ('1'), ('123bYteS'), ('1kb'), ('1mb'), (' 1 Gb'), ('1.5 gB '), ('1tb'), ('3000 tb'), ('1e6 mb')) x (size); -- negative numbers are supported SELECT size, pg_size_bytes (size) FROM ( VALUES ('-1'), ('-123bytes'), ('-1kb'), ('-1mb'), (' -1 Gb'), ('-1.5 gB '), ('-1tb'), ('-3000 TB'), ('-10e-1 MB')) x (size); -- different cases with allowed points SELECT size, pg_size_bytes (size) FROM ( VALUES ('-1.'), ('-1.kb'), ('-1. kb'), ('-0. gb'), ('-.1'), ('-.1kb'), ('-.1 kb'), ('-.0 gb')) x (size); -- invalid inputs SELECT pg_size_bytes ('1 AB'); SELECT pg_size_bytes ('1 AB A'); SELECT pg_size_bytes ('1 AB A '); SELECT pg_size_bytes ('9223372036854775807.9'); SELECT pg_size_bytes ('1e100'); SELECT pg_size_bytes ('1e1000000000000000000'); SELECT pg_size_bytes ('1 byte'); -- the singular "byte" is not supported SELECT pg_size_bytes (''); SELECT pg_size_bytes ('kb'); SELECT pg_size_bytes ('..'); SELECT pg_size_bytes ('-.'); SELECT pg_size_bytes ('-.kb'); SELECT pg_size_bytes ('-. kb'); SELECT pg_size_bytes ('.+912'); SELECT pg_size_bytes ('+912+ kB'); SELECT pg_size_bytes ('++123 kB'); pgFormatter-4.2/t/pg-test-files/expected/delete.sql000066400000000000000000000013171361326045100223660ustar00rootroot00000000000000CREATE TABLE delete_test ( id serial PRIMARY KEY, a int, b text ); INSERT INTO delete_test (a) VALUES (10); INSERT INTO delete_test (a, b) VALUES (50, repeat('x', 10000)); INSERT INTO delete_test (a) VALUES (100); -- allow an alias to be specified for DELETE's target table DELETE FROM delete_test AS dt WHERE dt.a > 75; -- if an alias is specified, don't allow the original table name -- to be referenced DELETE FROM delete_test dt WHERE delete_test.a > 25; SELECT id, a, char_length(b) FROM delete_test; -- delete a row with a TOASTed value DELETE FROM delete_test WHERE a > 25; SELECT id, a, char_length(b) FROM delete_test; DROP TABLE delete_test; pgFormatter-4.2/t/pg-test-files/expected/dependency.sql000066400000000000000000000073021361326045100232420ustar00rootroot00000000000000-- -- DEPENDENCIES -- CREATE USER regress_dep_user; CREATE USER regress_dep_user2; CREATE USER regress_dep_user3; CREATE GROUP regress_dep_group; CREATE TABLE deptest ( f1 serial PRIMARY KEY, f2 text ); GRANT SELECT ON TABLE deptest TO GROUP regress_dep_group; GRANT ALL ON TABLE deptest TO regress_dep_user, regress_dep_user2; -- can't drop neither because they have privileges somewhere DROP USER regress_dep_user; DROP GROUP regress_dep_group; -- if we revoke the privileges we can drop the group REVOKE SELECT ON deptest FROM GROUP regress_dep_group; DROP GROUP regress_dep_group; -- can't drop the user if we revoke the privileges partially REVOKE SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES ON deptest FROM regress_dep_user; DROP USER regress_dep_user; -- now we are OK to drop him REVOKE TRIGGER ON deptest FROM regress_dep_user; DROP USER regress_dep_user; -- we are OK too if we drop the privileges all at once REVOKE ALL ON deptest FROM regress_dep_user2; DROP USER regress_dep_user2; -- can't drop the owner of an object -- the error message detail here would include a pg_toast_nnn name that -- is not constant, so suppress it \set VERBOSITY terse ALTER TABLE deptest OWNER TO regress_dep_user3; DROP USER regress_dep_user3; \set VERBOSITY default -- if we drop the object, we can drop the user too DROP TABLE deptest; DROP USER regress_dep_user3; -- Test DROP OWNED CREATE USER regress_dep_user0; CREATE USER regress_dep_user1; CREATE USER regress_dep_user2; SET SESSION AUTHORIZATION regress_dep_user0; -- permission denied DROP OWNED BY regress_dep_user1; DROP OWNED BY regress_dep_user0, regress_dep_user2; REASSIGN OWNED BY regress_dep_user0 TO regress_dep_user1; REASSIGN OWNED BY regress_dep_user1 TO regress_dep_user0; -- this one is allowed DROP OWNED BY regress_dep_user0; CREATE TABLE deptest1 ( f1 int UNIQUE ); GRANT ALL ON deptest1 TO regress_dep_user1 WITH GRANT OPTION; SET SESSION AUTHORIZATION regress_dep_user1; CREATE TABLE deptest ( a serial PRIMARY KEY, b text ); GRANT ALL ON deptest1 TO regress_dep_user2; RESET SESSION AUTHORIZATION; \z deptest1 DROP OWNED BY regress_dep_user1; -- all grants revoked \z deptest1 -- table was dropped \d deptest -- Test REASSIGN OWNED GRANT ALL ON deptest1 TO regress_dep_user1; GRANT CREATE ON DATABASE regression TO regress_dep_user1; SET SESSION AUTHORIZATION regress_dep_user1; CREATE SCHEMA deptest; CREATE TABLE deptest ( a serial PRIMARY KEY, b text ); ALTER DEFAULT PRIVILEGES FOR ROLE regress_dep_user1 IN SCHEMA deptest GRANT ALL ON TABLES TO regress_dep_user2; CREATE FUNCTION deptest_func () RETURNS void LANGUAGE plpgsql AS $$ BEGIN END; $$; CREATE TYPE deptest_enum AS ENUM ( 'red' ); CREATE TYPE deptest_range AS RANGE ( SUBTYPE = int4 ); CREATE TABLE deptest2 ( f1 int ); -- make a serial column the hard way CREATE SEQUENCE ss1; ALTER TABLE deptest2 ALTER f1 SET DEFAULT nextval('ss1'); ALTER SEQUENCE ss1 OWNED BY deptest2.f1; -- When reassigning ownership of a composite type, its pg_class entry -- should match CREATE TYPE deptest_t AS ( a int ); SELECT typowner = relowner FROM pg_type JOIN pg_class c ON typrelid = c.oid WHERE typname = 'deptest_t'; RESET SESSION AUTHORIZATION; REASSIGN OWNED BY regress_dep_user1 TO regress_dep_user2; \dt deptest SELECT typowner = relowner FROM pg_type JOIN pg_class c ON typrelid = c.oid WHERE typname = 'deptest_t'; -- doesn't work: grant still exists DROP USER regress_dep_user1; DROP OWNED BY regress_dep_user1; DROP USER regress_dep_user1; DROP USER regress_dep_user2; DROP OWNED BY regress_dep_user2, regress_dep_user0; DROP USER regress_dep_user2; DROP USER regress_dep_user0; pgFormatter-4.2/t/pg-test-files/expected/domain.sql000066400000000000000000000560071361326045100224010ustar00rootroot00000000000000-- -- Test domains. -- -- Test Comment / Drop CREATE DOMAIN domaindroptest int4; COMMENT ON DOMAIN domaindroptest IS 'About to drop this..'; CREATE DOMAIN dependenttypetest domaindroptest; -- fail because of dependent type DROP DOMAIN domaindroptest; DROP DOMAIN domaindroptest CASCADE; -- this should fail because already gone DROP DOMAIN domaindroptest CASCADE; -- Test domain input. -- Note: the point of checking both INSERT and COPY FROM is that INSERT -- exercises CoerceToDomain while COPY exercises domain_in. CREATE DOMAIN domainvarchar varchar(5); CREATE DOMAIN domainnumeric numeric(8, 2); CREATE DOMAIN domainint4 int4; CREATE DOMAIN domaintext text; -- Test explicit coercions --- these should succeed (and truncate) SELECT cast('123456' AS domainvarchar); SELECT cast('12345' AS domainvarchar); -- Test tables using domains CREATE TABLE basictest ( testint4 domainint4, testtext domaintext, testvarchar domainvarchar, testnumeric domainnumeric ); INSERT INTO basictest VALUES ('88', 'haha', 'short', '123.12'); -- Good INSERT INTO basictest VALUES ('88', 'haha', 'short text', '123.12'); -- Bad varchar INSERT INTO basictest VALUES ('88', 'haha', 'short', '123.1212'); -- Truncate numeric SELECT * FROM basictest; -- check that domains inherit operations from base types SELECT testtext || testvarchar AS ||, testnumeric + 42 AS sum FROM basictest; -- check that union/case/coalesce type resolution handles domains properly SELECT coalesce(4::domainint4, 7) IS OF (int4) AS t; SELECT coalesce(4::domainint4, 7) IS OF (domainint4) AS f; SELECT coalesce(4::domainint4, 7::domainint4) IS OF (domainint4) AS t; DROP TABLE basictest; DROP DOMAIN domainvarchar RESTRICT; DROP DOMAIN domainnumeric RESTRICT; DROP DOMAIN domainint4 RESTRICT; DROP DOMAIN domaintext; -- Test domains over array types CREATE DOMAIN domainint4arr int4[1]; CREATE DOMAIN domainchar4arr varchar(4)[2][3]; CREATE TABLE domarrtest ( testint4arr domainint4arr, testchar4arr domainchar4arr ); INSERT INTO domarrtest VALUES ('{2,2}', '{{"a","b"},{"c","d"}}'); INSERT INTO domarrtest VALUES ('{{2,2},{2,2}}', '{{"a","b"}}'); INSERT INTO domarrtest VALUES ('{2,2}', '{{"a","b"},{"c","d"},{"e","f"}}'); INSERT INTO domarrtest VALUES ('{2,2}', '{{"a"},{"c"}}'); INSERT INTO domarrtest VALUES (NULL, '{{"a","b","c"},{"d","e","f"}}'); INSERT INTO domarrtest VALUES (NULL, '{{"toolong","b","c"},{"d","e","f"}}'); INSERT INTO domarrtest (testint4arr[1], testint4arr[3]) VALUES (11, 22); SELECT * FROM domarrtest; SELECT testint4arr[1], testchar4arr[2:2] FROM domarrtest; SELECT array_dims(testint4arr), array_dims(testchar4arr) FROM domarrtest; SELECT * FROM domarrtest; UPDATE domarrtest SET testint4arr[1] = testint4arr[1] + 1, testint4arr[3] = testint4arr[3] - 1 WHERE testchar4arr IS NULL; SELECT * FROM domarrtest WHERE testchar4arr IS NULL; DROP TABLE domarrtest; DROP DOMAIN domainint4arr RESTRICT; DROP DOMAIN domainchar4arr RESTRICT; CREATE DOMAIN dia AS int[]; SELECT '{1,2,3}'::dia; SELECT array_dims('{1,2,3}'::dia); SELECT pg_typeof('{1,2,3}'::dia); SELECT pg_typeof('{1,2,3}'::dia || 42); -- should be int[] not dia DROP DOMAIN dia; -- Test domains over composites CREATE TYPE comptype AS ( r float8, i float8 ); CREATE DOMAIN dcomptype AS comptype; CREATE TABLE dcomptable ( d1 dcomptype UNIQUE ); INSERT INTO dcomptable VALUES (ROW (1, 2)::dcomptype); INSERT INTO dcomptable VALUES (ROW (3, 4)::comptype); INSERT INTO dcomptable VALUES (ROW (1, 2)::dcomptype); -- fail on uniqueness INSERT INTO dcomptable (d1.r) VALUES (11); SELECT * FROM dcomptable; SELECT (d1).r, (d1).i, (d1).* FROM dcomptable; UPDATE dcomptable SET d1.r = (d1).r + 1 WHERE (d1).i > 0; SELECT * FROM dcomptable; ALTER DOMAIN dcomptype ADD CONSTRAINT c1 CHECK ((value).r <= (value).i); ALTER DOMAIN dcomptype ADD CONSTRAINT c2 CHECK ((value).r > (value).i); -- fail SELECT ROW (2, 1)::dcomptype; -- fail INSERT INTO dcomptable VALUES (ROW (1, 2)::comptype); INSERT INTO dcomptable VALUES (ROW (2, 1)::comptype); -- fail INSERT INTO dcomptable (d1.r) VALUES (99); INSERT INTO dcomptable (d1.r, d1.i) VALUES (99, 100); INSERT INTO dcomptable (d1.r, d1.i) VALUES (100, 99); -- fail UPDATE dcomptable SET d1.r = (d1).r + 1 WHERE (d1).i > 0; -- fail UPDATE dcomptable SET d1.r = (d1).r - 1, d1.i = (d1).i + 1 WHERE (d1).i > 0; SELECT * FROM dcomptable; EXPLAIN ( VERBOSE, COSTS OFF ) UPDATE dcomptable SET d1.r = (d1).r - 1, d1.i = (d1).i + 1 WHERE (d1).i > 0; CREATE RULE silly AS ON DELETE TO dcomptable DO INSTEAD UPDATE dcomptable SET d1.r = (d1).r - 1, d1.i = (d1).i + 1 WHERE (d1).i > 0; \d+ dcomptable DROP TABLE dcomptable; DROP TYPE comptype CASCADE; -- check altering and dropping columns used by domain constraints CREATE TYPE comptype AS ( r float8, i float8 ); CREATE DOMAIN dcomptype AS comptype; ALTER DOMAIN dcomptype ADD CONSTRAINT c1 CHECK ((value).r > 0); COMMENT ON CONSTRAINT c1 ON DOMAIN dcomptype IS 'random commentary'; SELECT ROW (0, 1)::dcomptype; -- fail ALTER TYPE comptype ALTER attribute r TYPE varchar; -- fail ALTER TYPE comptype ALTER attribute r TYPE bigint; ALTER TYPE comptype DROP attribute r; -- fail ALTER TYPE comptype DROP attribute i; SELECT conname, obj_description(oid, 'pg_constraint') FROM pg_constraint WHERE contypid = 'dcomptype'::regtype; -- check comment is still there DROP TYPE comptype CASCADE; -- Test domains over arrays of composite CREATE TYPE comptype AS ( r float8, i float8 ); CREATE DOMAIN dcomptypea AS comptype[]; CREATE TABLE dcomptable ( d1 dcomptypea UNIQUE ); INSERT INTO dcomptable VALUES (ARRAY[ROW (1, 2)]::dcomptypea); INSERT INTO dcomptable VALUES (ARRAY[ROW (3, 4), ROW (5, 6)]::comptype[]); INSERT INTO dcomptable VALUES (ARRAY[ROW (7, 8)::comptype, ROW (9, 10)::comptype]); INSERT INTO dcomptable VALUES (ARRAY[ROW (1, 2)]::dcomptypea); -- fail on uniqueness INSERT INTO dcomptable (d1[1]) VALUES (ROW (9, 10)); INSERT INTO dcomptable (d1[1].r) VALUES (11); SELECT * FROM dcomptable; SELECT d1[2], d1[1].r, d1[1].i FROM dcomptable; UPDATE dcomptable SET d1[2] = ROW (d1[2].i, d1[2].r); SELECT * FROM dcomptable; UPDATE dcomptable SET d1[1].r = d1[1].r + 1 WHERE d1[1].i > 0; SELECT * FROM dcomptable; ALTER DOMAIN dcomptypea ADD CONSTRAINT c1 CHECK (value[1].r <= value[1].i); ALTER DOMAIN dcomptypea ADD CONSTRAINT c2 CHECK (value[1].r > value[1].i); -- fail SELECT ARRAY[ROW (2, 1)]::dcomptypea; -- fail INSERT INTO dcomptable VALUES (ARRAY[ROW (1, 2)]::comptype[]); INSERT INTO dcomptable VALUES (ARRAY[ROW (2, 1)]::comptype[]); -- fail INSERT INTO dcomptable (d1[1].r) VALUES (99); INSERT INTO dcomptable (d1[1].r, d1[1].i) VALUES (99, 100); INSERT INTO dcomptable (d1[1].r, d1[1].i) VALUES (100, 99); -- fail UPDATE dcomptable SET d1[1].r = d1[1].r + 1 WHERE d1[1].i > 0; -- fail UPDATE dcomptable SET d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1 WHERE d1[1].i > 0; SELECT * FROM dcomptable; EXPLAIN ( VERBOSE, COSTS OFF ) UPDATE dcomptable SET d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1 WHERE d1[1].i > 0; CREATE RULE silly AS ON DELETE TO dcomptable DO INSTEAD UPDATE dcomptable SET d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1 WHERE d1[1].i > 0; \d+ dcomptable DROP TABLE dcomptable; DROP TYPE comptype CASCADE; -- Test arrays over domains CREATE DOMAIN posint AS int CHECK (value > 0); CREATE TABLE pitable ( f1 posint[] ); INSERT INTO pitable VALUES (ARRAY[42]); INSERT INTO pitable VALUES (ARRAY[- 1]); -- fail INSERT INTO pitable VALUES ('{0}'); -- fail UPDATE pitable SET f1[1] = f1[1] + 1; UPDATE pitable SET f1[1] = 0; -- fail SELECT * FROM pitable; DROP TABLE pitable; CREATE DOMAIN vc4 AS varchar(4); CREATE TABLE vc4table ( f1 vc4[] ); INSERT INTO vc4table VALUES (ARRAY['too long']); -- fail INSERT INTO vc4table VALUES (ARRAY['too long']::vc4[]); -- cast truncates SELECT * FROM vc4table; DROP TABLE vc4table; DROP TYPE vc4; -- You can sort of fake arrays-of-arrays by putting a domain in between CREATE DOMAIN dposinta AS posint[]; CREATE TABLE dposintatable ( f1 dposinta[] ); INSERT INTO dposintatable VALUES (ARRAY[ARRAY[42]]); -- fail INSERT INTO dposintatable VALUES (ARRAY[ARRAY[42]::posint[]]); -- still fail INSERT INTO dposintatable VALUES (ARRAY[ARRAY[42]::dposinta]); -- but this works SELECT f1, f1[1], (f1[1])[1] FROM dposintatable; SELECT pg_typeof(f1) FROM dposintatable; SELECT pg_typeof(f1[1]) FROM dposintatable; SELECT pg_typeof(f1[1][1]) FROM dposintatable; SELECT pg_typeof((f1[1])[1]) FROM dposintatable; UPDATE dposintatable SET f1[2] = ARRAY[99]; SELECT f1, f1[1], (f1[2])[1] FROM dposintatable; -- it'd be nice if you could do something like this, but for now you can't: UPDATE dposintatable SET f1[2][1] = ARRAY[97]; -- maybe someday we can make this syntax work: UPDATE dposintatable SET (f1[2])[1] = ARRAY[98]; DROP TABLE dposintatable; DROP DOMAIN posint CASCADE; -- Test not-null restrictions CREATE DOMAIN dnotnull varchar(15) NOT NULL; CREATE DOMAIN dnull varchar(15); CREATE DOMAIN dcheck varchar(15) NOT NULL CHECK (VALUE = 'a' OR VALUE = 'c' OR VALUE = 'd'); CREATE TABLE nulltest ( col1 dnotnull, col2 dnotnull NULL -- NOT NULL in the domain cannot be overridden , col3 dnull NOT NULL, col4 dnull, col5 dcheck CHECK (col5 IN ('c', 'd')) ); INSERT INTO nulltest DEFAULT VALUES; INSERT INTO nulltest VALUES ('a', 'b', 'c', 'd', 'c'); -- Good INSERT INTO nulltest VALUES ('a', 'b', 'c', 'd', NULL); INSERT INTO nulltest VALUES ('a', 'b', 'c', 'd', 'a'); INSERT INTO nulltest VALUES (NULL, 'b', 'c', 'd', 'd'); INSERT INTO nulltest VALUES ('a', NULL, 'c', 'd', 'c'); INSERT INTO nulltest VALUES ('a', 'b', NULL, 'd', 'c'); INSERT INTO nulltest VALUES ('a', 'b', 'c', NULL, 'd'); -- Good SELECT * FROM nulltest; -- Test out coerced (casted) constraints SELECT cast('1' AS dnotnull); SELECT cast(NULL AS dnotnull); -- fail SELECT cast(cast(NULL AS dnull) AS dnotnull); -- fail SELECT cast(col4 AS dnotnull) FROM nulltest; -- fail -- cleanup DROP TABLE nulltest; DROP DOMAIN dnotnull RESTRICT; DROP DOMAIN dnull RESTRICT; DROP DOMAIN dcheck RESTRICT; CREATE DOMAIN ddef1 int4 DEFAULT 3; CREATE DOMAIN ddef2 oid DEFAULT '12'; -- Type mixing, function returns int8 CREATE DOMAIN ddef3 text DEFAULT 5; CREATE SEQUENCE ddef4_seq; CREATE DOMAIN ddef4 int4 DEFAULT nextval('ddef4_seq'); CREATE DOMAIN ddef5 numeric(8, 2) NOT NULL DEFAULT '12.12'; CREATE TABLE defaulttest ( col1 ddef1, col2 ddef2, col3 ddef3, col4 ddef4 PRIMARY KEY, col5 ddef1 NOT NULL DEFAULT NULL, col6 ddef2 DEFAULT '88', col7 ddef4 DEFAULT 8000, col8 ddef5 ); INSERT INTO defaulttest (col4) VALUES (0); -- fails, col5 defaults to null ALTER TABLE defaulttest ALTER COLUMN col5 DROP DEFAULT; INSERT INTO defaulttest DEFAULT VALUES; -- succeeds, inserts domain default -- We used to treat SET DEFAULT NULL as equivalent to DROP DEFAULT; wrong ALTER TABLE defaulttest ALTER COLUMN col5 SET DEFAULT NULL; INSERT INTO defaulttest (col4) VALUES (0); -- fails ALTER TABLE defaulttest ALTER COLUMN col5 DROP DEFAULT; INSERT INTO defaulttest DEFAULT VALUES; INSERT INTO defaulttest DEFAULT VALUES; SELECT * FROM defaulttest; DROP TABLE defaulttest CASCADE; -- Test ALTER DOMAIN .. NOT NULL CREATE DOMAIN dnotnulltest integer; CREATE TABLE domnotnull ( col1 dnotnulltest, col2 dnotnulltest ); INSERT INTO domnotnull DEFAULT VALUES; ALTER DOMAIN dnotnulltest SET NOT NULL; -- fails UPDATE domnotnull SET col1 = 5; ALTER DOMAIN dnotnulltest SET NOT NULL; -- fails UPDATE domnotnull SET col2 = 6; ALTER DOMAIN dnotnulltest SET NOT NULL; UPDATE domnotnull SET col1 = NULL; -- fails ALTER DOMAIN dnotnulltest DROP NOT NULL; UPDATE domnotnull SET col1 = NULL; DROP DOMAIN dnotnulltest CASCADE; -- Test ALTER DOMAIN .. DEFAULT .. CREATE TABLE domdeftest ( col1 ddef1 ); INSERT INTO domdeftest DEFAULT VALUES; SELECT * FROM domdeftest; ALTER DOMAIN ddef1 SET DEFAULT '42'; INSERT INTO domdeftest DEFAULT VALUES; SELECT * FROM domdeftest; ALTER DOMAIN ddef1 DROP DEFAULT; INSERT INTO domdeftest DEFAULT VALUES; SELECT * FROM domdeftest; DROP TABLE domdeftest; -- Test ALTER DOMAIN .. CONSTRAINT .. CREATE DOMAIN con AS integer; CREATE TABLE domcontest ( col1 con ); INSERT INTO domcontest VALUES (1); INSERT INTO domcontest VALUES (2); ALTER DOMAIN con ADD CONSTRAINT t CHECK (VALUE < 1); -- fails ALTER DOMAIN con ADD CONSTRAINT t CHECK (VALUE < 34); ALTER DOMAIN con ADD CHECK (VALUE > 0); INSERT INTO domcontest VALUES (- 5); -- fails INSERT INTO domcontest VALUES (42); -- fails INSERT INTO domcontest VALUES (5); ALTER DOMAIN con DROP CONSTRAINT t; INSERT INTO domcontest VALUES (- 5); --fails INSERT INTO domcontest VALUES (42); ALTER DOMAIN con DROP CONSTRAINT nonexistent; ALTER DOMAIN con DROP CONSTRAINT IF EXISTS nonexistent; -- Test ALTER DOMAIN .. CONSTRAINT .. NOT VALID CREATE DOMAIN things AS INT; CREATE TABLE thethings ( stuff things ); INSERT INTO thethings (stuff) VALUES (55); ALTER DOMAIN things ADD CONSTRAINT meow CHECK (VALUE < 11); ALTER DOMAIN things ADD CONSTRAINT meow CHECK (VALUE < 11) NOT VALID; ALTER DOMAIN things VALIDATE CONSTRAINT meow; UPDATE thethings SET stuff = 10; ALTER DOMAIN things VALIDATE CONSTRAINT meow; -- Confirm ALTER DOMAIN with RULES. CREATE TABLE domtab ( col1 integer ); CREATE DOMAIN dom AS integer; CREATE VIEW domview AS SELECT cast(col1 AS dom) FROM domtab; INSERT INTO domtab (col1) VALUES (NULL); INSERT INTO domtab (col1) VALUES (5); SELECT * FROM domview; ALTER DOMAIN dom SET NOT NULL; SELECT * FROM domview; -- fail ALTER DOMAIN dom DROP NOT NULL; SELECT * FROM domview; ALTER DOMAIN dom ADD CONSTRAINT domchkgt6 CHECK (value > 6); SELECT * FROM domview; --fail ALTER DOMAIN dom DROP CONSTRAINT domchkgt6 RESTRICT; SELECT * FROM domview; -- cleanup DROP DOMAIN ddef1 RESTRICT; DROP DOMAIN ddef2 RESTRICT; DROP DOMAIN ddef3 RESTRICT; DROP DOMAIN ddef4 RESTRICT; DROP DOMAIN ddef5 RESTRICT; DROP SEQUENCE ddef4_seq; -- Test domains over domains CREATE DOMAIN vchar4 varchar(4); CREATE DOMAIN dinter vchar4 CHECK (substring(VALUE, 1, 1) = 'x'); CREATE DOMAIN dtop dinter CHECK (substring(VALUE, 2, 1) = '1'); SELECT 'x123'::dtop; SELECT 'x1234'::dtop; -- explicit coercion should truncate SELECT 'y1234'::dtop; -- fail SELECT 'y123'::dtop; -- fail SELECT 'yz23'::dtop; -- fail SELECT 'xz23'::dtop; -- fail CREATE temp TABLE dtest ( f1 dtop ); INSERT INTO dtest VALUES ('x123'); INSERT INTO dtest VALUES ('x1234'); -- fail, implicit coercion INSERT INTO dtest VALUES ('y1234'); -- fail, implicit coercion INSERT INTO dtest VALUES ('y123'); -- fail INSERT INTO dtest VALUES ('yz23'); -- fail INSERT INTO dtest VALUES ('xz23'); -- fail DROP TABLE dtest; DROP DOMAIN vchar4 CASCADE; -- Make sure that constraints of newly-added domain columns are -- enforced correctly, even if there's no default value for the new -- column. Per bug #1433 CREATE DOMAIN str_domain AS text NOT NULL; CREATE TABLE domain_test ( a int, b int ); INSERT INTO domain_test VALUES (1, 2); INSERT INTO domain_test VALUES (1, 2); -- should fail ALTER TABLE domain_test ADD COLUMN c str_domain; CREATE DOMAIN str_domain2 AS text CHECK (value <> 'foo') DEFAULT 'foo'; -- should fail ALTER TABLE domain_test ADD COLUMN d str_domain2; -- Check that domain constraints on prepared statement parameters of -- unknown type are enforced correctly. CREATE DOMAIN pos_int AS int4 CHECK (value > 0) NOT NULL; PREPARE s1 AS SELECT $1::pos_int = 10 AS "is_ten"; EXECUTE s1 (10); EXECUTE s1 (0); -- should fail EXECUTE s1 (NULL); -- should fail -- Check that domain constraints on plpgsql function parameters, results, -- and local variables are enforced correctly. CREATE FUNCTION doubledecrement (p1 pos_int) RETURNS pos_int AS $$ DECLARE v pos_int; BEGIN RETURN p1; END $$ LANGUAGE plpgsql; SELECT doubledecrement (3); -- fail because of implicit null assignment CREATE OR REPLACE FUNCTION doubledecrement (p1 pos_int) RETURNS pos_int AS $$ DECLARE v pos_int := 0; BEGIN RETURN p1; END $$ LANGUAGE plpgsql; SELECT doubledecrement (3); -- fail at initialization assignment CREATE OR REPLACE FUNCTION doubledecrement (p1 pos_int) RETURNS pos_int AS $$ DECLARE v pos_int := 1; BEGIN v := p1 - 1; RETURN v - 1; END $$ LANGUAGE plpgsql; SELECT doubledecrement (NULL); -- fail before call SELECT doubledecrement (0); -- fail before call SELECT doubledecrement (1); -- fail at assignment to v SELECT doubledecrement (2); -- fail at return SELECT doubledecrement (3); -- good -- Check that ALTER DOMAIN tests columns of derived types CREATE DOMAIN posint AS int4; -- Currently, this doesn't work for composite types, but verify it complains CREATE TYPE ddtest1 AS ( f1 posint ); CREATE TABLE ddtest2 ( f1 ddtest1 ); INSERT INTO ddtest2 VALUES (ROW (- 1)); ALTER DOMAIN posint ADD CONSTRAINT c1 CHECK (value >= 0); DROP TABLE ddtest2; -- Likewise for domains within arrays of composite CREATE TABLE ddtest2 ( f1 ddtest1[] ); INSERT INTO ddtest2 VALUES ('{(-1)}'); ALTER DOMAIN posint ADD CONSTRAINT c1 CHECK (value >= 0); DROP TABLE ddtest2; -- Likewise for domains within domains over composite CREATE DOMAIN ddtest1d AS ddtest1; CREATE TABLE ddtest2 ( f1 ddtest1d ); INSERT INTO ddtest2 VALUES ('(-1)'); ALTER DOMAIN posint ADD CONSTRAINT c1 CHECK (value >= 0); DROP TABLE ddtest2; DROP DOMAIN ddtest1d; -- Likewise for domains within domains over array of composite CREATE DOMAIN ddtest1d AS ddtest1[]; CREATE TABLE ddtest2 ( f1 ddtest1d ); INSERT INTO ddtest2 VALUES ('{(-1)}'); ALTER DOMAIN posint ADD CONSTRAINT c1 CHECK (value >= 0); DROP TABLE ddtest2; DROP DOMAIN ddtest1d; -- Doesn't work for ranges, either CREATE TYPE rposint AS RANGE ( subtype = posint ); CREATE TABLE ddtest2 ( f1 rposint ); INSERT INTO ddtest2 VALUES ('(-1,3]'); ALTER DOMAIN posint ADD CONSTRAINT c1 CHECK (value >= 0); DROP TABLE ddtest2; DROP TYPE rposint; ALTER DOMAIN posint ADD CONSTRAINT c1 CHECK (value >= 0); CREATE DOMAIN posint2 AS posint CHECK (value % 2 = 0); CREATE TABLE ddtest2 ( f1 posint2 ); INSERT INTO ddtest2 VALUES (11); -- fail INSERT INTO ddtest2 VALUES (- 2); -- fail INSERT INTO ddtest2 VALUES (2); ALTER DOMAIN posint ADD CONSTRAINT c2 CHECK (value >= 10); -- fail ALTER DOMAIN posint ADD CONSTRAINT c2 CHECK (value > 0); -- OK DROP TABLE ddtest2; DROP TYPE ddtest1; DROP DOMAIN posint CASCADE; -- -- Check enforcement of domain-related typmod in plpgsql (bug #5717) -- CREATE OR REPLACE FUNCTION array_elem_check (numeric) RETURNS numeric AS $$ DECLARE x numeric(4, 2)[1]; BEGIN x[1] := $1; RETURN x[1]; END $$ LANGUAGE plpgsql; SELECT array_elem_check (121.00); SELECT array_elem_check (1.23456); CREATE DOMAIN mynums AS numeric(4, 2)[1]; CREATE OR REPLACE FUNCTION array_elem_check (numeric) RETURNS numeric AS $$ DECLARE x mynums; BEGIN x[1] := $1; RETURN x[1]; END $$ LANGUAGE plpgsql; SELECT array_elem_check (121.00); SELECT array_elem_check (1.23456); CREATE DOMAIN mynums2 AS mynums; CREATE OR REPLACE FUNCTION array_elem_check (numeric) RETURNS numeric AS $$ DECLARE x mynums2; BEGIN x[1] := $1; RETURN x[1]; END $$ LANGUAGE plpgsql; SELECT array_elem_check (121.00); SELECT array_elem_check (1.23456); DROP FUNCTION array_elem_check (numeric); -- -- Check enforcement of array-level domain constraints -- CREATE DOMAIN orderedpair AS int[2] CHECK (value[1] < value[2]); SELECT ARRAY[1, 2]::orderedpair; SELECT ARRAY[2, 1]::orderedpair; -- fail CREATE temp TABLE op ( f1 orderedpair ); INSERT INTO op VALUES (ARRAY[1, 2]); INSERT INTO op VALUES (ARRAY[2, 1]); -- fail UPDATE op SET f1[2] = 3; UPDATE op SET f1[2] = 0; -- fail SELECT * FROM op; CREATE OR REPLACE FUNCTION array_elem_check (int) RETURNS int AS $$ DECLARE x orderedpair := '{1,2}'; BEGIN x[2] := $1; RETURN x[2]; END $$ LANGUAGE plpgsql; SELECT array_elem_check (3); SELECT array_elem_check (- 1); DROP FUNCTION array_elem_check (int); -- -- Check enforcement of changing constraints in plpgsql -- CREATE DOMAIN di AS int; CREATE FUNCTION dom_check (int) RETURNS di AS $$ DECLARE d di; BEGIN d := $1::di; RETURN d; END $$ LANGUAGE plpgsql IMMUTABLE; SELECT dom_check (0); ALTER DOMAIN di ADD CONSTRAINT pos CHECK (value > 0); SELECT dom_check (0); -- fail ALTER DOMAIN di DROP CONSTRAINT pos; SELECT dom_check (0); -- implicit cast during assignment is a separate code path, test that too CREATE OR REPLACE FUNCTION dom_check (int) RETURNS di AS $$ DECLARE d di; BEGIN d := $1; RETURN d; END $$ LANGUAGE plpgsql IMMUTABLE; SELECT dom_check (0); ALTER DOMAIN di ADD CONSTRAINT pos CHECK (value > 0); SELECT dom_check (0); -- fail ALTER DOMAIN di DROP CONSTRAINT pos; SELECT dom_check (0); DROP FUNCTION dom_check (int); DROP DOMAIN di; -- -- Check use of a (non-inline-able) SQL function in a domain constraint; -- this has caused issues in the past -- CREATE FUNCTION sql_is_distinct_from (anyelement, anyelement) RETURNS boolean LANGUAGE sql AS 'select $1 is distinct from $2 limit 1' ; CREATE DOMAIN inotnull int CHECK (sql_is_distinct_from (value, NULL)); SELECT 1::inotnull; SELECT NULL::inotnull; CREATE TABLE dom_table ( x inotnull ); INSERT INTO dom_table VALUES ('1'); INSERT INTO dom_table VALUES (1); INSERT INTO dom_table VALUES (NULL); DROP TABLE dom_table; DROP DOMAIN inotnull; DROP FUNCTION sql_is_distinct_from (anyelement, anyelement); -- -- Renaming -- CREATE DOMAIN testdomain1 AS int; ALTER DOMAIN testdomain1 RENAME TO testdomain2; ALTER TYPE testdomain2 RENAME TO testdomain3; -- alter type also works DROP DOMAIN testdomain3; -- -- Renaming domain constraints -- CREATE DOMAIN testdomain1 AS int CONSTRAINT unsigned CHECK (value > 0); ALTER DOMAIN testdomain1 RENAME CONSTRAINT unsigned TO unsigned_foo; ALTER DOMAIN testdomain1 DROP CONSTRAINT unsigned_foo; DROP DOMAIN testdomain1; pgFormatter-4.2/t/pg-test-files/expected/drop_if_exists.sql000066400000000000000000000112501361326045100241420ustar00rootroot00000000000000-- -- IF EXISTS tests -- -- table (will be really dropped at the end) DROP TABLE test_exists; DROP TABLE IF EXISTS test_exists; CREATE TABLE test_exists ( a int, b text ); -- view DROP VIEW test_view_exists; DROP VIEW IF EXISTS test_view_exists; CREATE VIEW test_view_exists AS SELECT * FROM test_exists; DROP VIEW IF EXISTS test_view_exists; DROP VIEW test_view_exists; -- index DROP INDEX test_index_exists; DROP INDEX IF EXISTS test_index_exists; CREATE INDEX test_index_exists ON test_exists (a); DROP INDEX IF EXISTS test_index_exists; DROP INDEX test_index_exists; -- sequence DROP SEQUENCE test_sequence_exists; DROP SEQUENCE IF EXISTS test_sequence_exists; CREATE SEQUENCE test_sequence_exists; DROP SEQUENCE IF EXISTS test_sequence_exists; DROP SEQUENCE test_sequence_exists; -- schema DROP SCHEMA test_schema_exists; DROP SCHEMA IF EXISTS test_schema_exists; CREATE SCHEMA test_schema_exists; DROP SCHEMA IF EXISTS test_schema_exists; DROP SCHEMA test_schema_exists; -- type DROP TYPE test_type_exists; DROP TYPE IF EXISTS test_type_exists; CREATE TYPE test_type_exists AS ( a int, b text ); DROP TYPE IF EXISTS test_type_exists; DROP TYPE test_type_exists; -- domain DROP DOMAIN test_domain_exists; DROP DOMAIN IF EXISTS test_domain_exists; CREATE DOMAIN test_domain_exists AS int NOT NULL CHECK (value > 0); DROP DOMAIN IF EXISTS test_domain_exists; DROP DOMAIN test_domain_exists; --- --- role/user/group --- CREATE USER regress_test_u1; CREATE ROLE regress_test_r1; CREATE GROUP regress_test_g1; DROP USER regress_test_u2; DROP USER IF EXISTS regress_test_u1, regress_test_u2; DROP USER regress_test_u1; DROP ROLE regress_test_r2; DROP ROLE IF EXISTS regress_test_r1, regress_test_r2; DROP ROLE regress_test_r1; DROP GROUP regress_test_g2; DROP GROUP IF EXISTS regress_test_g1, regress_test_g2; DROP GROUP regress_test_g1; -- collation DROP COLLATION IF EXISTS test_collation_exists; -- conversion DROP CONVERSION test_conversion_exists; DROP CONVERSION IF EXISTS test_conversion_exists; CREATE CONVERSION test_conversion_exists FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; DROP CONVERSION test_conversion_exists; -- text search parser DROP TEXT SEARCH PARSER test_tsparser_exists; DROP TEXT SEARCH PARSER IF EXISTS test_tsparser_exists; -- text search dictionary DROP TEXT SEARCH DICTIONARY test_tsdict_exists; DROP TEXT SEARCH DICTIONARY IF EXISTS test_tsdict_exists; CREATE TEXT SEARCH DICTIONARY test_tsdict_exists ( TEMPLATE = ispell, DictFile = ispell_sample, AffFile = ispell_sample ); DROP TEXT SEARCH DICTIONARY test_tsdict_exists; -- test search template DROP TEXT SEARCH TEMPLATE test_tstemplate_exists; DROP TEXT SEARCH TEMPLATE IF EXISTS test_tstemplate_exists; -- text search configuration DROP TEXT SEARCH CONFIGURATION test_tsconfig_exists; DROP TEXT SEARCH CONFIGURATION IF EXISTS test_tsconfig_exists; CREATE TEXT SEARCH CONFIGURATION test_tsconfig_exists ( COPY = english ); DROP TEXT SEARCH CONFIGURATION test_tsconfig_exists; -- extension DROP EXTENSION test_extension_exists; DROP EXTENSION IF EXISTS test_extension_exists; -- functions DROP FUNCTION test_function_exists (); DROP FUNCTION IF EXISTS test_function_exists (); DROP FUNCTION test_function_exists (int, text, int[]); DROP FUNCTION IF EXISTS test_function_exists (int, text, int[]); -- aggregate DROP AGGREGATE test_aggregate_exists (*); DROP AGGREGATE IF EXISTS test_aggregate_exists (*); DROP AGGREGATE test_aggregate_exists (int); DROP AGGREGATE IF EXISTS test_aggregate_exists (int); -- operator DROP OPERATOR @#@ (int, int); DROP OPERATOR IF EXISTS @#@ (int, int); CREATE OPERATOR @#@ ( LEFTARG = int8, RIGHTARG = int8, PROCEDURE = int8xor ); DROP OPERATOR @#@ (int8, int8); -- language CREATE FUNCTION test_ambiguous_funcname (int) RETURNS int AS $$ SELECT $1; $$ LANGUAGE sql; CREATE FUNCTION test_ambiguous_funcname (text) RETURNS text AS $$ SELECT $1; $$ LANGUAGE sql; DROP FUNCTION test_ambiguous_funcname; DROP FUNCTION IF EXISTS test_ambiguous_funcname; -- cleanup DROP FUNCTION test_ambiguous_funcname (int); DROP FUNCTION test_ambiguous_funcname (text); -- Likewise for procedures. CREATE PROCEDURE test_ambiguous_procname (int ) AS $$ BEGIN END; $$ LANGUAGE plpgsql; CREATE PROCEDURE test_ambiguous_procname (text ) AS $$ BEGIN END; $$ LANGUAGE plpgsql; DROP PROCEDURE test_ambiguous_procname; DROP PROCEDURE IF EXISTS test_ambiguous_procname; -- Check we get a similar error if we use ROUTINE instead of PROCEDURE. DROP ROUTINE IF EXISTS test_ambiguous_procname; -- cleanup DROP PROCEDURE test_ambiguous_procname (int); DROP PROCEDURE test_ambiguous_procname (text); pgFormatter-4.2/t/pg-test-files/expected/drop_operator.sql000066400000000000000000000030661361326045100240060ustar00rootroot00000000000000CREATE OPERATOR === ( PROCEDURE = int8eq, LEFTARG = bigint, RIGHTARG = bigint, COMMUTATOR = === ); CREATE OPERATOR !== ( PROCEDURE = int8ne, LEFTARG = bigint, RIGHTARG = bigint, NEGATOR = ===, COMMUTATOR = !== ); DROP OPERATOR !== (bigint, bigint); SELECT ctid, oprcom FROM pg_catalog.pg_operator fk WHERE oprcom != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.oprcom); SELECT ctid, oprnegate FROM pg_catalog.pg_operator fk WHERE oprnegate != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.oprnegate); DROP OPERATOR === (bigint, bigint); CREATE OPERATOR <||<| ( PROCEDURE = int8lt, LEFTARG = bigint, RIGHTARG = bigint ); CREATE OPERATOR <||><| ( PROCEDURE = int8gt, LEFTARG = bigint, RIGHTARG = bigint, NEGATOR = <|, COMMUTATOR = <| ); DROP OPERATOR <||><| (bigint, bigint); SELECT ctid, oprcom FROM pg_catalog.pg_operator fk WHERE oprcom != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.oprcom); SELECT ctid, oprnegate FROM pg_catalog.pg_operator fk WHERE oprnegate != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.oprnegate); DROP OPERATOR <||<| (bigint, bigint); pgFormatter-4.2/t/pg-test-files/expected/enum.sql000066400000000000000000000245431361326045100220760ustar00rootroot00000000000000-- -- Enum tests -- CREATE TYPE rainbow AS ENUM ( 'red', 'orange', 'yellow', 'green', 'blue', 'purple' ); -- -- Did it create the right number of rows? -- SELECT COUNT(*) FROM pg_enum WHERE enumtypid = 'rainbow'::regtype; -- -- I/O functions -- SELECT 'red'::rainbow; SELECT 'mauve'::rainbow; -- -- adding new values -- CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' ); SELECT enumlabel, enumsortorder FROM pg_enum WHERE enumtypid = 'planets'::regtype ORDER BY 2; ALTER TYPE planets ADD VALUE 'uranus'; SELECT enumlabel, enumsortorder FROM pg_enum WHERE enumtypid = 'planets'::regtype ORDER BY 2; ALTER TYPE planets ADD VALUE 'mercury' BEFORE 'venus'; ALTER TYPE planets ADD VALUE 'saturn' BEFORE 'uranus'; ALTER TYPE planets ADD VALUE 'jupiter' AFTER 'mars'; ALTER TYPE planets ADD VALUE 'neptune' AFTER 'uranus'; SELECT enumlabel, enumsortorder FROM pg_enum WHERE enumtypid = 'planets'::regtype ORDER BY 2; SELECT enumlabel, enumsortorder FROM pg_enum WHERE enumtypid = 'planets'::regtype ORDER BY enumlabel::planets; -- errors for adding labels ALTER TYPE planets ADD VALUE 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto'; ALTER TYPE planets ADD VALUE 'pluto' AFTER 'zeus'; -- if not exists tests -- existing value gives error ALTER TYPE planets ADD VALUE 'mercury'; -- unless IF NOT EXISTS is specified ALTER TYPE planets ADD VALUE IF NOT EXISTS 'mercury'; -- should be neptune, not mercury SELECT enum_last(NULL::planets); ALTER TYPE planets ADD VALUE IF NOT EXISTS 'pluto'; -- should be pluto, i.e. the new value SELECT enum_last(NULL::planets); -- -- Test inserting so many values that we have to renumber -- CREATE TYPE insenum AS enum ( 'L1', 'L2' ); ALTER TYPE insenum ADD value 'i1' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i2' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i3' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i4' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i5' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i6' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i7' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i8' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i9' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i10' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i11' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i12' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i13' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i14' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i15' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i16' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i17' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i18' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i19' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i20' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i21' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i22' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i23' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i24' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i25' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i26' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i27' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i28' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i29' BEFORE 'L2'; ALTER TYPE insenum ADD value 'i30' BEFORE 'L2'; -- The exact values of enumsortorder will now depend on the local properties -- of float4, but in any reasonable implementation we should get at least -- 20 splits before having to renumber; so only hide values > 20. SELECT enumlabel, CASE WHEN enumsortorder > 20 THEN NULL ELSE enumsortorder END AS so FROM pg_enum WHERE enumtypid = 'insenum'::regtype ORDER BY enumsortorder; -- -- Basic table creation, row selection -- CREATE TABLE enumtest ( col rainbow ); INSERT INTO enumtest VALUES ('red'), ('orange'), ('yellow'), ('green'); SELECT * FROM enumtest; -- -- Operators, no index -- SELECT * FROM enumtest WHERE col = 'orange'; SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col; SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col; SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col; SELECT * FROM enumtest WHERE col < 'green' ORDER BY col; SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col; -- -- Cast to/from text -- SELECT 'red'::rainbow::text || 'hithere'; SELECT 'red'::text::rainbow = 'red'::rainbow; -- -- Aggregates -- SELECT min(col) FROM enumtest; SELECT max(col) FROM enumtest; SELECT max(col) FROM enumtest WHERE col < 'green'; -- -- Index tests, force use of index -- SET enable_seqscan = OFF; SET enable_bitmapscan = OFF; -- -- Btree index / opclass with the various operators -- CREATE UNIQUE INDEX enumtest_btree ON enumtest USING btree (col); SELECT * FROM enumtest WHERE col = 'orange'; SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col; SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col; SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col; SELECT * FROM enumtest WHERE col < 'green' ORDER BY col; SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col; SELECT min(col) FROM enumtest; SELECT max(col) FROM enumtest; SELECT max(col) FROM enumtest WHERE col < 'green'; DROP INDEX enumtest_btree; -- -- Hash index / opclass with the = operator -- CREATE INDEX enumtest_hash ON enumtest USING HASH (col); SELECT * FROM enumtest WHERE col = 'orange'; DROP INDEX enumtest_hash; -- -- End index tests -- RESET enable_seqscan; RESET enable_bitmapscan; -- -- Domains over enums -- CREATE DOMAIN rgb AS rainbow CHECK (VALUE IN ('red', 'green', 'blue')); SELECT 'red'::rgb; SELECT 'purple'::rgb; SELECT 'purple'::rainbow::rgb; DROP DOMAIN rgb; -- -- Arrays -- SELECT '{red,green,blue}'::rainbow[]; SELECT ('{red,green,blue}'::rainbow[])[2]; SELECT 'red' = ANY ('{red,green,blue}'::rainbow[]); SELECT 'yellow' = ANY ('{red,green,blue}'::rainbow[]); SELECT 'red' = ALL ('{red,green,blue}'::rainbow[]); SELECT 'red' = ALL ('{red,red}'::rainbow[]); -- -- Support functions -- SELECT enum_first(NULL::rainbow); SELECT enum_last('green'::rainbow); SELECT enum_range(NULL::rainbow); SELECT enum_range('orange'::rainbow, 'green'::rainbow); SELECT enum_range(NULL, 'green'::rainbow); SELECT enum_range('orange'::rainbow, NULL); SELECT enum_range(NULL::rainbow, NULL); -- -- User functions, can't test perl/python etc here since may not be compiled. -- CREATE FUNCTION echo_me (anyenum) RETURNS text AS $$ BEGIN RETURN $1::text || 'omg'; END $$ LANGUAGE plpgsql; SELECT echo_me ('red'::rainbow); -- -- Concrete function should override generic one -- CREATE FUNCTION echo_me (rainbow) RETURNS text AS $$ BEGIN RETURN $1::text || 'wtf'; END $$ LANGUAGE plpgsql; SELECT echo_me ('red'::rainbow); -- -- If we drop the original generic one, we don't have to qualify the type -- anymore, since there's only one match -- DROP FUNCTION echo_me (anyenum); SELECT echo_me ('red'); DROP FUNCTION echo_me (rainbow); -- -- RI triggers on enum types -- CREATE TABLE enumtest_parent ( id rainbow PRIMARY KEY ); CREATE TABLE enumtest_child ( parent rainbow REFERENCES enumtest_parent ); INSERT INTO enumtest_parent VALUES ('red'); INSERT INTO enumtest_child VALUES ('red'); INSERT INTO enumtest_child VALUES ('blue'); -- fail DELETE FROM enumtest_parent; -- fail -- -- cross-type RI should fail -- CREATE TYPE bogus AS ENUM ( 'good', 'bad', 'ugly' ); CREATE TABLE enumtest_bogus_child ( parent bogus REFERENCES enumtest_parent ); DROP TYPE bogus; -- check renaming a value ALTER TYPE rainbow RENAME VALUE 'red' TO 'crimson'; SELECT enumlabel, enumsortorder FROM pg_enum WHERE enumtypid = 'rainbow'::regtype ORDER BY 2; -- check that renaming a non-existent value fails ALTER TYPE rainbow RENAME VALUE 'red' TO 'crimson'; -- check that renaming to an existent value fails ALTER TYPE rainbow RENAME VALUE 'blue' TO 'green'; -- -- check transactional behaviour of ALTER TYPE ... ADD VALUE -- CREATE TYPE bogus AS ENUM ( 'good' ); -- check that we can add new values to existing enums in a transaction -- but we can't use them BEGIN; ALTER TYPE bogus ADD VALUE 'new'; SAVEPOINT x; SELECT 'new'::bogus; -- unsafe ROLLBACK TO x; SELECT enum_first(NULL::bogus); -- safe SELECT enum_last(NULL::bogus); -- unsafe ROLLBACK TO x; SELECT enum_range(NULL::bogus); -- unsafe ROLLBACK TO x; COMMIT; SELECT 'new'::bogus; -- now safe SELECT enumlabel, enumsortorder FROM pg_enum WHERE enumtypid = 'bogus'::regtype ORDER BY 2; -- check that we recognize the case where the enum already existed but was -- modified in the current txn; this should not be considered safe BEGIN; ALTER TYPE bogus RENAME TO bogon; ALTER TYPE bogon ADD VALUE 'bad'; SELECT 'bad'::bogon; ROLLBACK; -- but a renamed value is safe to use later in same transaction BEGIN; ALTER TYPE bogus RENAME VALUE 'good' TO 'bad'; SELECT 'bad'::bogus; ROLLBACK; DROP TYPE bogus; -- check that values created during CREATE TYPE can be used in any case BEGIN; CREATE TYPE bogus AS ENUM ( 'good', 'bad', 'ugly' ); ALTER TYPE bogus RENAME TO bogon; SELECT enum_range(NULL::bogon); ROLLBACK; -- ideally, we'd allow this usage; but it requires keeping track of whether -- the enum type was created in the current transaction, which is expensive BEGIN; CREATE TYPE bogus AS ENUM ( 'good' ); ALTER TYPE bogus RENAME TO bogon; ALTER TYPE bogon ADD VALUE 'bad'; ALTER TYPE bogon ADD VALUE 'ugly'; SELECT enum_range(NULL::bogon); -- fails ROLLBACK; -- -- Cleanup -- DROP TABLE enumtest_child; DROP TABLE enumtest_parent; DROP TABLE enumtest; DROP TYPE rainbow; -- -- Verify properly cleaned up -- SELECT COUNT(*) FROM pg_type WHERE typname = 'rainbow'; SELECT * FROM pg_enum WHERE NOT EXISTS ( SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid); pgFormatter-4.2/t/pg-test-files/expected/equivclass.sql000066400000000000000000000245531361326045100233120ustar00rootroot00000000000000-- -- Tests for the planner's "equivalence class" mechanism -- -- One thing that's not tested well during normal querying is the logic -- for handling "broken" ECs. This is because an EC can only become broken -- if its underlying btree operator family doesn't include a complete set -- of cross-type equality operators. There are not (and should not be) -- any such families built into Postgres; so we have to hack things up -- to create one. We do this by making two alias types that are really -- int8 (so we need no new C code) and adding only some operators for them -- into the standard integer_ops opfamily. CREATE TYPE int8alias1; CREATE FUNCTION int8alias1in (cstring) RETURNS int8alias1 STRICT IMMUTABLE LANGUAGE internal AS 'int8in'; CREATE FUNCTION int8alias1out (int8alias1) RETURNS cstring STRICT IMMUTABLE LANGUAGE internal AS 'int8out'; CREATE TYPE int8alias1 ( input = int8alias1in, output = int8alias1out, LIKE = int8 ); CREATE TYPE int8alias2; CREATE FUNCTION int8alias2in (cstring) RETURNS int8alias2 STRICT IMMUTABLE LANGUAGE internal AS 'int8in'; CREATE FUNCTION int8alias2out (int8alias2) RETURNS cstring STRICT IMMUTABLE LANGUAGE internal AS 'int8out'; CREATE TYPE int8alias2 ( input = int8alias2in, output = int8alias2out, LIKE = int8 ); CREATE cast( int8 AS int8alias1) without FUNCTION; CREATE cast( int8 AS int8alias2) without FUNCTION; CREATE cast( int8alias1 AS int8) without FUNCTION; CREATE cast( int8alias2 AS int8) without FUNCTION; CREATE FUNCTION int8alias1eq (int8alias1, int8alias1) RETURNS bool STRICT IMMUTABLE LANGUAGE internal AS 'int8eq'; CREATE OPERATOR = ( PROCEDURE = int8alias1eq, LEFTARG = int8alias1, RIGHTARG = int8alias1, commutator = =, RESTRICT = eqsel, JOIN = eqjoinsel, MERGES ); ALTER OPERATOR family integer_ops USING btree ADD OPERATOR 3 = (int8alias1, int8alias1); CREATE FUNCTION int8alias2eq (int8alias2, int8alias2) RETURNS bool STRICT IMMUTABLE LANGUAGE internal AS 'int8eq'; CREATE OPERATOR = ( PROCEDURE = int8alias2eq, LEFTARG = int8alias2, RIGHTARG = int8alias2, commutator = =, RESTRICT = eqsel, JOIN = eqjoinsel, MERGES ); ALTER OPERATOR family integer_ops USING btree ADD OPERATOR 3 = (int8alias2, int8alias2); CREATE FUNCTION int8alias1eq (int8, int8alias1) RETURNS bool STRICT IMMUTABLE LANGUAGE internal AS 'int8eq'; CREATE OPERATOR = ( PROCEDURE = int8alias1eq, LEFTARG = int8, RIGHTARG = int8alias1, RESTRICT = eqsel, JOIN = eqjoinsel, MERGES ); ALTER OPERATOR family integer_ops USING btree ADD OPERATOR 3 = (int8, int8alias1); CREATE FUNCTION int8alias1eq (int8alias1, int8alias2) RETURNS bool STRICT IMMUTABLE LANGUAGE internal AS 'int8eq'; CREATE OPERATOR = ( PROCEDURE = int8alias1eq, LEFTARG = int8alias1, RIGHTARG = int8alias2, RESTRICT = eqsel, JOIN = eqjoinsel, MERGES ); ALTER OPERATOR family integer_ops USING btree ADD OPERATOR 3 = (int8alias1, int8alias2); CREATE FUNCTION int8alias1lt (int8alias1, int8alias1) RETURNS bool STRICT IMMUTABLE LANGUAGE internal AS 'int8lt'; CREATE OPERATOR < ( PROCEDURE = int8alias1lt, LEFTARG = int8alias1, RIGHTARG = int8alias1 ); ALTER OPERATOR family integer_ops USING btree ADD OPERATOR 1 < (int8alias1, int8alias1); CREATE FUNCTION int8alias1cmp (int8, int8alias1) RETURNS int STRICT IMMUTABLE LANGUAGE internal AS 'btint8cmp'; ALTER OPERATOR family integer_ops USING btree ADD FUNCTION 1 int8alias1cmp (int8, int8alias1); CREATE TABLE ec0 ( ff int8 PRIMARY KEY, f1 int8, f2 int8 ); CREATE TABLE ec1 ( ff int8 PRIMARY KEY, f1 int8alias1, f2 int8alias2 ); CREATE TABLE ec2 ( xf int8 PRIMARY KEY, x1 int8alias1, x2 int8alias2 ); -- for the moment we only want to look at nestloop plans SET enable_hashjoin = OFF; SET enable_mergejoin = OFF; -- -- Note that for cases where there's a missing operator, we don't care so -- much whether the plan is ideal as that we don't fail or generate an -- outright incorrect plan. -- EXPLAIN ( COSTS OFF ) SELECT * FROM ec0 WHERE ff = f1 AND f1 = '42'::int8; EXPLAIN ( COSTS OFF ) SELECT * FROM ec0 WHERE ff = f1 AND f1 = '42'::int8alias1; EXPLAIN ( COSTS OFF ) SELECT * FROM ec1 WHERE ff = f1 AND f1 = '42'::int8alias1; EXPLAIN ( COSTS OFF ) SELECT * FROM ec1 WHERE ff = f1 AND f1 = '42'::int8alias2; EXPLAIN ( COSTS OFF ) SELECT * FROM ec1, ec2 WHERE ff = x1 AND ff = '42'::int8; EXPLAIN ( COSTS OFF ) SELECT * FROM ec1, ec2 WHERE ff = x1 AND ff = '42'::int8alias1; EXPLAIN ( COSTS OFF ) SELECT * FROM ec1, ec2 WHERE ff = x1 AND '42'::int8 = x1; EXPLAIN ( COSTS OFF ) SELECT * FROM ec1, ec2 WHERE ff = x1 AND x1 = '42'::int8alias1; EXPLAIN ( COSTS OFF ) SELECT * FROM ec1, ec2 WHERE ff = x1 AND x1 = '42'::int8alias2; CREATE UNIQUE INDEX ec1_expr1 ON ec1 ((ff + 1)); CREATE UNIQUE INDEX ec1_expr2 ON ec1 ((ff + 2 + 1)); CREATE UNIQUE INDEX ec1_expr3 ON ec1 ((ff + 3 + 1)); CREATE UNIQUE INDEX ec1_expr4 ON ec1 ((ff + 4)); EXPLAIN ( COSTS OFF ) SELECT * FROM ec1, ( SELECT ff + 1 AS x FROM ( SELECT ff + 2 AS ff FROM ec1 UNION ALL SELECT ff + 3 AS ff FROM ec1) ss0 UNION ALL SELECT ff + 4 AS x FROM ec1) AS ss1 WHERE ss1.x = ec1.f1 AND ec1.ff = 42::int8; EXPLAIN ( COSTS OFF ) SELECT * FROM ec1, ( SELECT ff + 1 AS x FROM ( SELECT ff + 2 AS ff FROM ec1 UNION ALL SELECT ff + 3 AS ff FROM ec1) ss0 UNION ALL SELECT ff + 4 AS x FROM ec1) AS ss1 WHERE ss1.x = ec1.f1 AND ec1.ff = 42::int8 AND ec1.ff = ec1.f1; EXPLAIN ( COSTS OFF ) SELECT * FROM ec1, ( SELECT ff + 1 AS x FROM ( SELECT ff + 2 AS ff FROM ec1 UNION ALL SELECT ff + 3 AS ff FROM ec1) ss0 UNION ALL SELECT ff + 4 AS x FROM ec1) AS ss1, ( SELECT ff + 1 AS x FROM ( SELECT ff + 2 AS ff FROM ec1 UNION ALL SELECT ff + 3 AS ff FROM ec1) ss0 UNION ALL SELECT ff + 4 AS x FROM ec1) AS ss2 WHERE ss1.x = ec1.f1 AND ss1.x = ss2.x AND ec1.ff = 42::int8; -- let's try that as a mergejoin SET enable_mergejoin = ON; SET enable_nestloop = OFF; EXPLAIN ( COSTS OFF ) SELECT * FROM ec1, ( SELECT ff + 1 AS x FROM ( SELECT ff + 2 AS ff FROM ec1 UNION ALL SELECT ff + 3 AS ff FROM ec1) ss0 UNION ALL SELECT ff + 4 AS x FROM ec1) AS ss1, ( SELECT ff + 1 AS x FROM ( SELECT ff + 2 AS ff FROM ec1 UNION ALL SELECT ff + 3 AS ff FROM ec1) ss0 UNION ALL SELECT ff + 4 AS x FROM ec1) AS ss2 WHERE ss1.x = ec1.f1 AND ss1.x = ss2.x AND ec1.ff = 42::int8; -- check partially indexed scan SET enable_nestloop = ON; SET enable_mergejoin = OFF; DROP INDEX ec1_expr3; EXPLAIN ( COSTS OFF ) SELECT * FROM ec1, ( SELECT ff + 1 AS x FROM ( SELECT ff + 2 AS ff FROM ec1 UNION ALL SELECT ff + 3 AS ff FROM ec1) ss0 UNION ALL SELECT ff + 4 AS x FROM ec1) AS ss1 WHERE ss1.x = ec1.f1 AND ec1.ff = 42::int8; -- let's try that as a mergejoin SET enable_mergejoin = ON; SET enable_nestloop = OFF; EXPLAIN ( COSTS OFF ) SELECT * FROM ec1, ( SELECT ff + 1 AS x FROM ( SELECT ff + 2 AS ff FROM ec1 UNION ALL SELECT ff + 3 AS ff FROM ec1) ss0 UNION ALL SELECT ff + 4 AS x FROM ec1) AS ss1 WHERE ss1.x = ec1.f1 AND ec1.ff = 42::int8; -- check effects of row-level security SET enable_nestloop = ON; SET enable_mergejoin = OFF; ALTER TABLE ec1 enable ROW level SECURITY; CREATE POLICY p1 ON ec1 USING (f1 < '5'::int8alias1); CREATE USER regress_user_ectest; GRANT SELECT ON ec0 TO regress_user_ectest; GRANT SELECT ON ec1 TO regress_user_ectest; -- without any RLS, we'll treat {a.ff, b.ff, 43} as an EquivalenceClass EXPLAIN ( COSTS OFF ) SELECT * FROM ec0 a, ec1 b WHERE a.ff = b.ff AND a.ff = 43::bigint::int8alias1; SET session AUTHORIZATION regress_user_ectest; -- with RLS active, the non-leakproof a.ff = 43 clause is not treated -- as a suitable source for an EquivalenceClass; currently, this is true -- even though the RLS clause has nothing to do directly with the EC EXPLAIN ( COSTS OFF ) SELECT * FROM ec0 a, ec1 b WHERE a.ff = b.ff AND a.ff = 43::bigint::int8alias1; RESET session AUTHORIZATION; REVOKE SELECT ON ec0 FROM regress_user_ectest; REVOKE SELECT ON ec1 FROM regress_user_ectest; DROP USER regress_user_ectest; -- check that X=X is converted to X IS NOT NULL when appropriate EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1 WHERE unique1 = unique1 AND unique2 = unique2; -- this could be converted, but isn't at present EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1 WHERE unique1 = unique1 OR unique2 = unique2; pgFormatter-4.2/t/pg-test-files/expected/errors.sql000066400000000000000000000146251361326045100224460ustar00rootroot00000000000000-- -- ERRORS -- -- bad in postquel, but ok in PostgreSQL SELECT 1; -- -- UNSUPPORTED STUFF -- doesn't work -- notify pg_class -- -- -- SELECT -- this used to be a syntax error, but now we allow an empty target list SELECT ; -- no such relation SELECT * FROM nonesuch; -- bad name in target list SELECT nonesuch FROM pg_database; -- empty distinct list isn't OK SELECT DISTINCT FROM pg_database; -- bad attribute name on lhs of operator SELECT * FROM pg_database WHERE nonesuch = pg_database.datname; -- bad attribute name on rhs of operator SELECT * FROM pg_database WHERE pg_database.datname = nonesuch; -- bad attribute name in select distinct on SELECT DISTINCT ON (foobar) * FROM pg_database; -- -- DELETE -- missing relation name (this had better not wildcard!) DELETE FROM; -- no such relation DELETE FROM nonesuch; -- -- DROP -- missing relation name (this had better not wildcard!) DROP TABLE; -- no such relation DROP TABLE nonesuch; -- -- ALTER TABLE -- relation renaming -- missing relation name ALTER TABLE RENAME; -- no such relation ALTER TABLE nonesuch RENAME TO newnonesuch; -- no such relation ALTER TABLE nonesuch RENAME TO stud_emp; -- conflict ALTER TABLE stud_emp RENAME TO aggtest; -- self-conflict ALTER TABLE stud_emp RENAME TO stud_emp; -- attribute renaming -- no such relation ALTER TABLE nonesuchrel RENAME COLUMN nonesuchatt TO newnonesuchatt; -- no such attribute ALTER TABLE emp RENAME COLUMN nonesuchatt TO newnonesuchatt; -- conflict ALTER TABLE emp RENAME COLUMN salary TO manager; -- conflict ALTER TABLE emp RENAME COLUMN salary TO ctid; -- -- TRANSACTION STUFF -- not in a xact abort; -- not in a xact END; -- -- CREATE AGGREGATE -- sfunc/finalfunc type disagreement CREATE AGGREGATE newavg2 ( SFUNC = int4pl, BASETYPE = int4, STYPE = int4, FINALFUNC = int2um, INITCOND = '0' ); -- left out basetype CREATE AGGREGATE newcnt1 ( SFUNC = int4inc, STYPE = int4, INITCOND = '0' ); -- -- DROP INDEX -- missing index name DROP INDEX; -- bad index name DROP INDEX 314159; -- no such index DROP INDEX nonesuch; -- -- DROP AGGREGATE -- missing aggregate name DROP AGGREGATE; -- missing aggregate type DROP AGGREGATE newcnt1; -- bad aggregate name DROP AGGREGATE 314159 (int); -- bad aggregate type DROP AGGREGATE newcnt (nonesuch); -- no such aggregate DROP AGGREGATE nonesuch (int4); -- no such aggregate for type DROP AGGREGATE newcnt (float4); -- -- DROP FUNCTION -- missing function name DROP FUNCTION (); -- bad function name DROP FUNCTION 314159 (); -- no such function DROP FUNCTION nonesuch (); -- -- DROP TYPE -- missing type name DROP TYPE; -- bad type name DROP TYPE 314159; -- no such type DROP TYPE nonesuch; -- -- DROP OPERATOR -- missing everything DROP OPERATOR; -- bad operator name DROP OPERATOR equals; -- missing type list DROP OPERATOR ===; -- missing parentheses DROP OPERATOR int4, int4; -- missing operator name DROP OPERATOR (int4, int4); -- missing type list contents DROP OPERATOR === (); -- no such operator DROP OPERATOR === (int4); -- no such operator by that name DROP OPERATOR === (int4, int4); -- no such type1 DROP OPERATOR = ( nonesuch); -- no such type1 DROP OPERATOR = ( , int4); -- no such type1 DROP OPERATOR = ( nonesuch, int4); -- no such type2 DROP OPERATOR = (int4, nonesuch); -- no such type2 DROP OPERATOR = (int4,); -- -- DROP RULE -- missing rule name DROP RULE; -- bad rule name DROP RULE 314159; -- no such rule DROP RULE nonesuch ON noplace; -- these postquel variants are no longer supported DROP tuple RULE nonesuch; DROP instance RULE nonesuch ON noplace; DROP rewrite RULE nonesuch; -- -- Check that division-by-zero is properly caught. -- SELECT 1 / 0; SELECT 1::int8 / 0; SELECT 1 / 0::int8; SELECT 1::int2 / 0; SELECT 1 / 0::int2; SELECT 1::numeric / 0; SELECT 1 / 0::numeric; SELECT 1::float8 / 0; SELECT 1 / 0::float8; SELECT 1::float4 / 0; SELECT 1 / 0::float4; -- -- Test psql's reporting of syntax error location -- xxx; CREATE foo; CREATE TABLE; CREATE TABLE \g INSERT INTO foo VALUES (123) foo; INSERT INTO 123 VALUES (123); INSERT INTO foo VALUES (123) 123; -- with a tab CREATE TABLE foo ( id int4 UNIQUE NOT NULL, id2 text NOT NULL PRIMARY KEY, id3 integer NOT NUL, id4 int4 UNIQUE NOT NULL, id5 text UNIQUE NOT NULL ); -- long line to be truncated on the left CREATE TABLE foo ( id int4 UNIQUE NOT NULL, id2 text NOT NULL PRIMARY KEY, id3 integer NOT NUL, id4 int4 UNIQUE NOT NULL, id5 text UNIQUE NOT NULL ); -- long line to be truncated on the right CREATE TABLE foo ( id3 integer NOT NUL, id4 int4 UNIQUE NOT NULL, id5 text UNIQUE NOT NULL, id int4 UNIQUE NOT NULL, id2 text NOT NULL PRIMARY KEY ); -- long line to be truncated both ways CREATE TABLE foo ( id int4 UNIQUE NOT NULL, id2 text NOT NULL PRIMARY KEY, id3 integer NOT NUL, id4 int4 UNIQUE NOT NULL, id5 text UNIQUE NOT NULL ); -- long line to be truncated on the left, many lines CREATE TEMPORARY TABLE foo ( id int4 UNIQUE NOT NULL, id2 text NOT NULL PRIMARY KEY, id3 integer NOT NUL, id4 int4 UNIQUE NOT NULL, id5 text UNIQUE NOT NULL ); -- long line to be truncated on the right, many lines CREATE TEMPORARY TABLE foo ( id3 integer NOT NUL, id4 int4 UNIQUE NOT NULL, id5 text UNIQUE NOT NULL, id int4 UNIQUE NOT NULL, id2 text NOT NULL PRIMARY KEY ); -- long line to be truncated both ways, many lines CREATE TEMPORARY TABLE foo ( id int4 UNIQUE NOT NULL, idx int4 UNIQUE NOT NULL, idy int4 UNIQUE NOT NULL, id2 text NOT NULL PRIMARY KEY, id3 integer NOT NUL, id4 int4 UNIQUE NOT NULL, id5 text UNIQUE NOT NULL, idz int4 UNIQUE NOT NULL, idv int4 UNIQUE NOT NULL ); -- more than 10 lines... CREATE TEMPORARY TABLE foo ( id int4 UNIQUE NOT NULL, idm int4 UNIQUE NOT NULL, idx int4 UNIQUE NOT NULL, idy int4 UNIQUE NOT NULL, id2 text NOT NULL PRIMARY KEY, id3 integer NOT NUL, id4 int4 UNIQUE NOT NULL, id5 text UNIQUE NOT NULL, idz int4 UNIQUE NOT NULL, idv int4 UNIQUE NOT NULL ); -- Check that stack depth detection mechanism works and -- max_stack_depth is not set too high CREATE FUNCTION infinite_recurse () RETURNS int AS 'select infinite_recurse()' LANGUAGE sql; \set VERBOSITY terse SELECT infinite_recurse (); pgFormatter-4.2/t/pg-test-files/expected/event_trigger.sql000066400000000000000000000356751361326045100240060ustar00rootroot00000000000000-- should fail, return type mismatch CREATE EVENT TRIGGER regress_event_trigger ON ddl_command_start EXECUTE PROCEDURE pg_backend_pid(); -- OK CREATE FUNCTION test_event_trigger () RETURNS event_trigger AS $$ BEGIN RAISE NOTICE 'test_event_trigger: % %', tg_event, tg_tag; END $$ LANGUAGE plpgsql; -- should fail, event triggers cannot have declared arguments CREATE FUNCTION test_event_trigger_arg (name text) RETURNS event_trigger AS $$ BEGIN RETURN 1; END $$ LANGUAGE plpgsql; -- should fail, SQL functions cannot be event triggers CREATE FUNCTION test_event_trigger_sql () RETURNS event_trigger AS $$ SELECT 1 $$ LANGUAGE sql; -- should fail, no elephant_bootstrap entry point CREATE EVENT TRIGGER regress_event_trigger ON elephant_bootstrap EXECUTE PROCEDURE test_event_trigger (); -- OK CREATE EVENT TRIGGER regress_event_trigger ON ddl_command_start EXECUTE PROCEDURE test_event_trigger (); -- OK CREATE EVENT TRIGGER regress_event_trigger_end ON ddl_command_end EXECUTE FUNCTION test_event_trigger (); -- should fail, food is not a valid filter variable CREATE EVENT TRIGGER regress_event_trigger2 ON ddl_command_start WHEN food IN ('sandwich') EXECUTE PROCEDURE test_event_trigger (); -- should fail, sandwich is not a valid command tag CREATE EVENT TRIGGER regress_event_trigger2 ON ddl_command_start WHEN tag IN ('sandwich') EXECUTE PROCEDURE test_event_trigger (); -- should fail, create skunkcabbage is not a valid command tag CREATE EVENT TRIGGER regress_event_trigger2 ON ddl_command_start WHEN tag IN ('create table', 'create skunkcabbage') EXECUTE PROCEDURE test_event_trigger (); -- should fail, can't have event triggers on event triggers CREATE EVENT TRIGGER regress_event_trigger2 ON ddl_command_start WHEN tag IN ('DROP EVENT TRIGGER') EXECUTE PROCEDURE test_event_trigger (); -- should fail, can't have event triggers on global objects CREATE EVENT TRIGGER regress_event_trigger2 ON ddl_command_start WHEN tag IN ('CREATE ROLE') EXECUTE PROCEDURE test_event_trigger (); -- should fail, can't have event triggers on global objects CREATE EVENT TRIGGER regress_event_trigger2 ON ddl_command_start WHEN tag IN ('CREATE DATABASE') EXECUTE PROCEDURE test_event_trigger (); -- should fail, can't have event triggers on global objects CREATE EVENT TRIGGER regress_event_trigger2 ON ddl_command_start WHEN tag IN ('CREATE TABLESPACE') EXECUTE PROCEDURE test_event_trigger (); -- should fail, can't have same filter variable twice CREATE EVENT TRIGGER regress_event_trigger2 ON ddl_command_start WHEN tag IN ('create table') AND tag IN ('CREATE FUNCTION') EXECUTE PROCEDURE test_event_trigger (); -- should fail, can't have arguments CREATE EVENT TRIGGER regress_event_trigger2 ON ddl_command_start EXECUTE PROCEDURE test_event_trigger ('argument not allowed'); -- OK CREATE EVENT TRIGGER regress_event_trigger2 ON ddl_command_start WHEN tag IN ('create table', 'CREATE FUNCTION') EXECUTE PROCEDURE test_event_trigger (); -- OK COMMENT ON EVENT TRIGGER regress_event_trigger IS 'test comment'; -- drop as non-superuser should fail CREATE ROLE regress_evt_user; SET ROLE regress_evt_user; CREATE EVENT TRIGGER regress_event_trigger_noperms ON ddl_command_start EXECUTE PROCEDURE test_event_trigger (); RESET ROLE; -- test enabling and disabling ALTER EVENT TRIGGER regress_event_trigger disable; -- fires _trigger2 and _trigger_end should fire, but not _trigger CREATE TABLE event_trigger_fire1 ( a int ); ALTER EVENT TRIGGER regress_event_trigger enable; SET session_replication_role = REPLICA; -- fires nothing CREATE TABLE event_trigger_fire2 ( a int ); ALTER EVENT TRIGGER regress_event_trigger enable REPLICA; -- fires only _trigger CREATE TABLE event_trigger_fire3 ( a int ); ALTER EVENT TRIGGER regress_event_trigger enable always; -- fires only _trigger CREATE TABLE event_trigger_fire4 ( a int ); RESET session_replication_role; -- fires all three CREATE TABLE event_trigger_fire5 ( a int ); -- non-top-level command CREATE FUNCTION f1 () RETURNS int LANGUAGE plpgsql AS $$ BEGIN CREATE TABLE event_trigger_fire6 ( a int ); RETURN 0; END $$; SELECT f1 (); -- non-top-level command CREATE PROCEDURE p1 () LANGUAGE plpgsql AS $$ BEGIN CREATE TABLE event_trigger_fire7 ( a int ); END $$; CALL p1 (); -- clean up ALTER EVENT TRIGGER regress_event_trigger disable; DROP TABLE event_trigger_fire2, event_trigger_fire3, event_trigger_fire4, event_trigger_fire5, event_trigger_fire6, event_trigger_fire7; DROP ROUTINE f1 (), p1 (); -- regress_event_trigger_end should fire on these commands GRANT ALL ON TABLE event_trigger_fire1 TO public; COMMENT ON TABLE event_trigger_fire1 IS 'here is a comment'; REVOKE ALL ON TABLE event_trigger_fire1 FROM public; DROP TABLE event_trigger_fire1; CREATE FOREIGN data wrapper useless; CREATE SERVER useless_server FOREIGN data wrapper useless; CREATE USER MAPPING FOR regress_evt_user SERVER useless_server; ALTER DEFAULT privileges FOR ROLE regress_evt_user REVOKE DELETE ON tables FROM regress_evt_user; -- alter owner to non-superuser should fail ALTER EVENT TRIGGER regress_event_trigger OWNER TO regress_evt_user; -- alter owner to superuser should work ALTER ROLE regress_evt_user superuser; ALTER EVENT TRIGGER regress_event_trigger OWNER TO regress_evt_user; -- should fail, name collision ALTER EVENT TRIGGER regress_event_trigger RENAME TO regress_event_trigger2; -- OK ALTER EVENT TRIGGER regress_event_trigger RENAME TO regress_event_trigger3; -- should fail, doesn't exist any more DROP EVENT TRIGGER regress_event_trigger; -- should fail, regress_evt_user owns some objects DROP ROLE regress_evt_user; -- cleanup before next test -- these are all OK; the second one should emit a NOTICE DROP EVENT TRIGGER IF EXISTS regress_event_trigger2; DROP EVENT TRIGGER IF EXISTS regress_event_trigger2; DROP EVENT TRIGGER regress_event_trigger3; DROP EVENT TRIGGER regress_event_trigger_end; -- test support for dropped objects CREATE SCHEMA schema_one AUTHORIZATION regress_evt_user; CREATE SCHEMA schema_two AUTHORIZATION regress_evt_user; CREATE SCHEMA audit_tbls AUTHORIZATION regress_evt_user; CREATE TEMP TABLE a_temp_tbl (); SET SESSION AUTHORIZATION regress_evt_user; CREATE TABLE schema_one.table_one ( a int ); CREATE TABLE schema_one. "table two" ( a int ); CREATE TABLE schema_one.table_three ( a int ); CREATE TABLE audit_tbls.schema_one_table_two ( the_value text ); CREATE TABLE schema_two.table_two ( a int ); CREATE TABLE schema_two.table_three ( a int, b text ); CREATE TABLE audit_tbls.schema_two_table_three ( the_value text ); CREATE OR REPLACE FUNCTION schema_two.add (int, int) RETURNS int LANGUAGE plpgsql CALLED ON NULL INPUT AS $$ BEGIN RETURN coalesce($1, 0) + coalesce($2, 0); END; $$; CREATE AGGREGATE schema_two.newton ( BASETYPE = int, SFUNC = schema_two.add, STYPE = int ); RESET SESSION AUTHORIZATION; CREATE TABLE undroppable_objs ( object_type text, object_identity text ); INSERT INTO undroppable_objs VALUES ('table', 'schema_one.table_three'), ('table', 'audit_tbls.schema_two_table_three'); CREATE TABLE dropped_objects ( TYPE text, SCHEMA text, object text ); -- This tests errors raised within event triggers; the one in audit_tbls -- uses 2nd-level recursive invocation via test_evtrig_dropped_objects(). CREATE OR REPLACE FUNCTION undroppable () RETURNS event_trigger LANGUAGE plpgsql AS $$ DECLARE obj record; BEGIN PERFORM 1 FROM pg_tables WHERE tablename = 'undroppable_objs'; IF NOT FOUND THEN RAISE NOTICE 'table undroppable_objs not found, skipping'; RETURN; END IF; FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects () JOIN undroppable_objs USING (object_type, object_identity) LOOP RAISE EXCEPTION 'object % of type % cannot be dropped', obj.object_identity, obj.object_type; END LOOP; END; $$; CREATE EVENT TRIGGER undroppable ON sql_drop EXECUTE PROCEDURE undroppable (); CREATE OR REPLACE FUNCTION test_evtrig_dropped_objects () RETURNS event_trigger LANGUAGE plpgsql AS $$ DECLARE obj record; BEGIN FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects () LOOP IF obj.object_type = 'table' THEN EXECUTE format('DROP TABLE IF EXISTS audit_tbls.%I', format('%s_%s', obj.schema_name, obj.object_name)); END IF; INSERT INTO dropped_objects (TYPE, SCHEMA, object) VALUES (obj.object_type, obj.schema_name, obj.object_identity); END LOOP; END $$; CREATE EVENT TRIGGER regress_event_trigger_drop_objects ON sql_drop WHEN TAG IN ('drop table', 'drop function', 'drop view', 'drop owned', 'drop schema', 'alter table') EXECUTE PROCEDURE test_evtrig_dropped_objects (); ALTER TABLE schema_one.table_one DROP COLUMN a; DROP SCHEMA schema_one, schema_two CASCADE; DELETE FROM undroppable_objs WHERE object_identity = 'audit_tbls.schema_two_table_three'; DROP SCHEMA schema_one, schema_two CASCADE; DELETE FROM undroppable_objs WHERE object_identity = 'schema_one.table_three'; DROP SCHEMA schema_one, schema_two CASCADE; SELECT * FROM dropped_objects WHERE SCHEMA IS NULL OR SCHEMA <> 'pg_toast'; DROP OWNED BY regress_evt_user; SELECT * FROM dropped_objects WHERE TYPE = 'schema'; DROP ROLE regress_evt_user; DROP EVENT TRIGGER regress_event_trigger_drop_objects; DROP EVENT TRIGGER undroppable; CREATE OR REPLACE FUNCTION event_trigger_report_dropped () RETURNS event_trigger LANGUAGE plpgsql AS $$ DECLARE r record; BEGIN FOR r IN SELECT * FROM pg_event_trigger_dropped_objects () LOOP IF NOT r.normal AND NOT r.original THEN CONTINUE; END IF; RAISE NOTICE 'NORMAL: orig=% normal=% istemp=% type=% identity=% name=% args=%', r.original, r.normal, r.is_temporary, r.object_type, r.object_identity, r.address_names, r.address_args; END LOOP; END; $$; CREATE EVENT TRIGGER regress_event_trigger_report_dropped ON sql_drop EXECUTE PROCEDURE event_trigger_report_dropped (); CREATE SCHEMA evttrig CREATE TABLE one ( col_a serial PRIMARY KEY, col_b text DEFAULT 'forty two') CREATE INDEX one_idx ON one ( col_b) CREATE TABLE two ( col_c integer CHECK (col_c > 0) REFERENCES one DEFAULT 42 ); -- Partitioned tables with a partitioned index CREATE TABLE evttrig.parted ( id int PRIMARY KEY ) PARTITION BY RANGE (id); CREATE TABLE evttrig.part_1_10 PARTITION OF evttrig.parted (id) FOR VALUES FROM (1) TO (10); CREATE TABLE evttrig.part_10_20 PARTITION OF evttrig.parted (id) FOR VALUES FROM (10) TO (20) PARTITION BY RANGE (id); CREATE TABLE evttrig.part_10_15 PARTITION OF evttrig.part_10_20 (id) FOR VALUES FROM (10) TO (15); CREATE TABLE evttrig.part_15_20 PARTITION OF evttrig.part_10_20 (id) FOR VALUES FROM (15) TO (20); ALTER TABLE evttrig.two DROP COLUMN col_c; ALTER TABLE evttrig.one ALTER COLUMN col_b DROP DEFAULT; ALTER TABLE evttrig.one DROP CONSTRAINT one_pkey; DROP INDEX evttrig.one_idx; DROP SCHEMA evttrig CASCADE; DROP TABLE a_temp_tbl; DROP EVENT TRIGGER regress_event_trigger_report_dropped; -- only allowed from within an event trigger function, should fail SELECT pg_event_trigger_table_rewrite_oid (); -- test Table Rewrite Event Trigger CREATE OR REPLACE FUNCTION test_evtrig_no_rewrite () RETURNS event_trigger LANGUAGE plpgsql AS $$ BEGIN RAISE EXCEPTION 'rewrites not allowed'; END; $$; CREATE EVENT TRIGGER no_rewrite_allowed ON table_rewrite EXECUTE PROCEDURE test_evtrig_no_rewrite (); CREATE TABLE rewriteme ( id serial PRIMARY KEY, foo float, bar timestamptz ); INSERT INTO rewriteme SELECT x * 1.001 FROM generate_series(1, 500) AS t (x); ALTER TABLE rewriteme ALTER COLUMN foo TYPE numeric; ALTER TABLE rewriteme ADD COLUMN baz int DEFAULT 0; -- test with more than one reason to rewrite a single table CREATE OR REPLACE FUNCTION test_evtrig_no_rewrite () RETURNS event_trigger LANGUAGE plpgsql AS $$ BEGIN RAISE NOTICE 'Table ''%'' is being rewritten (reason = %)', pg_event_trigger_table_rewrite_oid ()::regclass, pg_event_trigger_table_rewrite_reason (); END; $$; ALTER TABLE rewriteme ADD COLUMN onemore int DEFAULT 0, ADD COLUMN another int DEFAULT - 1, ALTER COLUMN foo TYPE numeric(10, 4); -- shouldn't trigger a table_rewrite event ALTER TABLE rewriteme ALTER COLUMN foo TYPE numeric(12, 4); BEGIN; SET timezone TO 'UTC'; ALTER TABLE rewriteme ALTER COLUMN bar TYPE timestamp; SET timezone TO '0'; ALTER TABLE rewriteme ALTER COLUMN bar TYPE timestamptz; SET timezone TO 'Europe/London'; ALTER TABLE rewriteme ALTER COLUMN bar TYPE timestamp; -- does rewrite ROLLBACK; -- typed tables are rewritten when their type changes. Don't emit table -- name, because firing order is not stable. CREATE OR REPLACE FUNCTION test_evtrig_no_rewrite () RETURNS event_trigger LANGUAGE plpgsql AS $$ BEGIN RAISE NOTICE 'Table is being rewritten (reason = %)', pg_event_trigger_table_rewrite_reason (); END; $$; CREATE TYPE rewritetype AS ( a int ); CREATE TABLE rewritemetoo1 OF rewritetype; CREATE TABLE rewritemetoo2 OF rewritetype; ALTER TYPE rewritetype ALTER attribute a TYPE text CASCADE; -- but this doesn't work CREATE TABLE rewritemetoo3 ( a rewritetype ); ALTER TYPE rewritetype ALTER attribute a TYPE varchar CASCADE; DROP TABLE rewriteme; DROP EVENT TRIGGER no_rewrite_allowed; DROP FUNCTION test_evtrig_no_rewrite (); -- test Row Security Event Trigger RESET SESSION AUTHORIZATION; CREATE TABLE event_trigger_test ( a integer, b text ); CREATE OR REPLACE FUNCTION start_command () RETURNS event_trigger AS $$ BEGIN RAISE NOTICE '% - ddl_command_start', tg_tag; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION end_command () RETURNS event_trigger AS $$ BEGIN RAISE NOTICE '% - ddl_command_end', tg_tag; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION drop_sql_command () RETURNS event_trigger AS $$ BEGIN RAISE NOTICE '% - sql_drop', tg_tag; END; $$ LANGUAGE plpgsql; CREATE EVENT TRIGGER start_rls_command ON ddl_command_start WHEN TAG IN ('CREATE POLICY', 'ALTER POLICY', 'DROP POLICY') EXECUTE PROCEDURE start_command (); CREATE EVENT TRIGGER end_rls_command ON ddl_command_end WHEN TAG IN ('CREATE POLICY', 'ALTER POLICY', 'DROP POLICY') EXECUTE PROCEDURE end_command (); CREATE EVENT TRIGGER sql_drop_command ON sql_drop WHEN TAG IN ('DROP POLICY') EXECUTE PROCEDURE drop_sql_command (); CREATE POLICY p1 ON event_trigger_test USING (FALSE); ALTER POLICY p1 ON event_trigger_test USING (TRUE); ALTER POLICY p1 ON event_trigger_test RENAME TO p2; DROP POLICY p2 ON event_trigger_test; DROP EVENT TRIGGER start_rls_command; DROP EVENT TRIGGER end_rls_command; DROP EVENT TRIGGER sql_drop_command; pgFormatter-4.2/t/pg-test-files/expected/expressions.sql000066400000000000000000000017301361326045100235050ustar00rootroot00000000000000-- -- expression evaluated tests that don't fit into a more specific file -- -- -- Tests for SQLVAlueFunction -- -- current_date (always matches because of transactional behaviour) SELECT date(now())::text = CURRENT_DATE::text; -- current_time / localtime SELECT now()::timetz::text = CURRENT_TIME::text; SELECT now()::time::text = LOCALTIME::text; -- current_timestamp / localtimestamp (always matches because of transactional behaviour) SELECT CURRENT_TIMESTAMP = NOW(); -- precision SELECT length(CURRENT_TIMESTAMP::text) >= length(current_timestamp(0)::text); -- localtimestamp SELECT now()::timestamp::text = LOCALTIMESTAMP::text; -- current_role/user/user is tested in rolnames.sql -- current database / catalog SELECT current_catalog = current_database(); -- current_schema SELECT current_schema; SET search_path = 'notme'; SELECT current_schema; SET search_path = 'pg_catalog'; SELECT current_schema; RESET search_path; pgFormatter-4.2/t/pg-test-files/expected/fast_default.sql000066400000000000000000000413161361326045100235700ustar00rootroot00000000000000-- -- ALTER TABLE ADD COLUMN DEFAULT test -- SET search_path = fast_default; CREATE SCHEMA fast_default; CREATE TABLE m ( id OID ); INSERT INTO m VALUES (NULL::OID); CREATE FUNCTION SET (tabname name) RETURNS VOID AS $$ BEGIN UPDATE m SET id = ( SELECT c.relfilenode FROM pg_class AS c, pg_namespace AS s WHERE c.relname = tabname AND c.relnamespace = s.oid AND s.nspname = 'fast_default'); END; $$ LANGUAGE 'plpgsql'; CREATE FUNCTION comp () RETURNS TEXT AS $$ BEGIN RETURN ( SELECT CASE WHEN m.id = c.relfilenode THEN 'Unchanged' ELSE 'Rewritten' END FROM m, pg_class AS c, pg_namespace AS s WHERE c.relname = 't' AND c.relnamespace = s.oid AND s.nspname = 'fast_default'); END; $$ LANGUAGE 'plpgsql'; CREATE FUNCTION log_rewrite () RETURNS event_trigger LANGUAGE plpgsql AS $func$ DECLARE this_schema text; BEGIN SELECT INTO this_schema relnamespace::regnamespace::text FROM pg_class WHERE oid = pg_event_trigger_table_rewrite_oid (); IF this_schema = 'fast_default' THEN RAISE NOTICE 'rewriting table % for reason %', pg_event_trigger_table_rewrite_oid ()::regclass, pg_event_trigger_table_rewrite_reason (); END IF; END; $func$; CREATE TABLE has_volatile AS SELECT * FROM generate_series(1, 10) id; CREATE EVENT TRIGGER has_volatile_rewrite ON table_rewrite EXECUTE PROCEDURE log_rewrite (); -- only the last of these should trigger a rewrite ALTER TABLE has_volatile ADD col1 int; ALTER TABLE has_volatile ADD col2 int DEFAULT 1; ALTER TABLE has_volatile ADD col3 timestamptz DEFAULT CURRENT_TIMESTAMP; ALTER TABLE has_volatile ADD col4 int DEFAULT (random() * 10000)::int; -- Test a large sample of different datatypes CREATE TABLE T ( pk int NOT NULL PRIMARY KEY, c_int int DEFAULT 1 ); SELECT SET ('t'); INSERT INTO T VALUES (1), (2); ALTER TABLE T ADD COLUMN c_bpchar BPCHAR(5) DEFAULT 'hello', ALTER COLUMN c_int SET DEFAULT 2; INSERT INTO T VALUES (3), (4); ALTER TABLE T ADD COLUMN c_text text DEFAULT 'world', ALTER COLUMN c_bpchar SET DEFAULT 'dog'; INSERT INTO T VALUES (5), (6); ALTER TABLE T ADD COLUMN c_date date DEFAULT '2016-06-02', ALTER COLUMN c_text SET DEFAULT 'cat'; INSERT INTO T VALUES (7), (8); ALTER TABLE T ADD COLUMN c_timestamp timestamp DEFAULT '2016-09-01 12:00:00', ADD COLUMN c_timestamp_null timestamp, ALTER COLUMN c_date SET DEFAULT '2010-01-01'; INSERT INTO T VALUES (9), (10); ALTER TABLE T ADD COLUMN c_array text[] DEFAULT '{"This", "is", "the", "real", "world"}', ALTER COLUMN c_timestamp SET DEFAULT '1970-12-31 11:12:13', ALTER COLUMN c_timestamp_null SET DEFAULT '2016-09-29 12:00:00'; INSERT INTO T VALUES (11), (12); ALTER TABLE T ADD COLUMN c_small smallint DEFAULT - 5, ADD COLUMN c_small_null smallint, ALTER COLUMN c_array SET DEFAULT '{"This", "is", "no", "fantasy"}'; INSERT INTO T VALUES (13), (14); ALTER TABLE T ADD COLUMN c_big bigint DEFAULT 180000000000018, ALTER COLUMN c_small SET DEFAULT 9, ALTER COLUMN c_small_null SET DEFAULT 13; INSERT INTO T VALUES (15), (16); ALTER TABLE T ADD COLUMN c_num numeric DEFAULT 1.00000000001, ALTER COLUMN c_big SET DEFAULT - 9999999999999999; INSERT INTO T VALUES (17), (18); ALTER TABLE T ADD COLUMN c_time time DEFAULT '12:00:00', ALTER COLUMN c_num SET DEFAULT 2.000000000000002; INSERT INTO T VALUES (19), (20); ALTER TABLE T ADD COLUMN c_interval interval DEFAULT '1 day', ALTER COLUMN c_time SET DEFAULT '23:59:59'; INSERT INTO T VALUES (21), (22); ALTER TABLE T ADD COLUMN c_hugetext text DEFAULT repeat('abcdefg', 1000), ALTER COLUMN c_interval SET DEFAULT '3 hours'; INSERT INTO T VALUES (23), (24); ALTER TABLE T ALTER COLUMN c_interval DROP DEFAULT, ALTER COLUMN c_hugetext SET DEFAULT repeat('poiuyt', 1000); INSERT INTO T VALUES (25), (26); ALTER TABLE T ALTER COLUMN c_bpchar DROP DEFAULT, ALTER COLUMN c_date DROP DEFAULT, ALTER COLUMN c_text DROP DEFAULT, ALTER COLUMN c_timestamp DROP DEFAULT, ALTER COLUMN c_array DROP DEFAULT, ALTER COLUMN c_small DROP DEFAULT, ALTER COLUMN c_big DROP DEFAULT, ALTER COLUMN c_num DROP DEFAULT, ALTER COLUMN c_time DROP DEFAULT, ALTER COLUMN c_hugetext DROP DEFAULT; INSERT INTO T VALUES (27), (28); SELECT pk, c_int, c_bpchar, c_text, c_date, c_timestamp, c_timestamp_null, c_array, c_small, c_small_null, c_big, c_num, c_time, c_interval, c_hugetext = repeat('abcdefg', 1000) AS c_hugetext_origdef, c_hugetext = repeat('poiuyt', 1000) AS c_hugetext_newdef FROM T ORDER BY pk; SELECT comp (); DROP TABLE T; -- Test expressions in the defaults CREATE OR REPLACE FUNCTION foo (a int) RETURNS TEXT AS $$ DECLARE res text := ''; i int; BEGIN i := 0; WHILE (i < a) LOOP res := res || chr(ascii('a') + i); i := i + 1; END LOOP; RETURN res; END; $$ LANGUAGE PLPGSQL STABLE; CREATE TABLE T ( pk int NOT NULL PRIMARY KEY, c_int int DEFAULT LENGTH(foo (6)) ); SELECT SET ('t'); INSERT INTO T VALUES (1), (2); ALTER TABLE T ADD COLUMN c_bpchar BPCHAR(5) DEFAULT foo (4), ALTER COLUMN c_int SET DEFAULT LENGTH(foo (8)); INSERT INTO T VALUES (3), (4); ALTER TABLE T ADD COLUMN c_text text DEFAULT foo (6), ALTER COLUMN c_bpchar SET DEFAULT foo (3); INSERT INTO T VALUES (5), (6); ALTER TABLE T ADD COLUMN c_date date DEFAULT '2016-06-02'::DATE + LENGTH(foo (10)), ALTER COLUMN c_text SET DEFAULT foo (12); INSERT INTO T VALUES (7), (8); ALTER TABLE T ADD COLUMN c_timestamp timestamp DEFAULT '2016-09-01'::DATE + LENGTH(foo (10)), ALTER COLUMN c_date SET DEFAULT '2010-01-01'::DATE - LENGTH(foo (4)); INSERT INTO T VALUES (9), (10); ALTER TABLE T ADD COLUMN c_array text[] DEFAULT ('{"This", "is", "' || foo (4) || '","the", "real", "world"}')::TEXT[], ALTER COLUMN c_timestamp SET DEFAULT '1970-12-31'::DATE + LENGTH(foo (30)); INSERT INTO T VALUES (11), (12); ALTER TABLE T ALTER COLUMN c_int DROP DEFAULT, ALTER COLUMN c_array SET DEFAULT ('{"This", "is", "' || foo (1) || '", "fantasy"}')::text[]; INSERT INTO T VALUES (13), (14); ALTER TABLE T ALTER COLUMN c_bpchar DROP DEFAULT, ALTER COLUMN c_date DROP DEFAULT, ALTER COLUMN c_text DROP DEFAULT, ALTER COLUMN c_timestamp DROP DEFAULT, ALTER COLUMN c_array DROP DEFAULT; INSERT INTO T VALUES (15), (16); SELECT * FROM T; SELECT comp (); DROP TABLE T; DROP FUNCTION foo (int); -- Fall back to full rewrite for volatile expressions CREATE TABLE T ( pk int NOT NULL PRIMARY KEY ); INSERT INTO T VALUES (1); SELECT SET ('t'); -- now() is stable, because it returns the transaction timestamp ALTER TABLE T ADD COLUMN c1 timestamp DEFAULT now(); SELECT comp (); -- clock_timestamp() is volatile ALTER TABLE T ADD COLUMN c2 timestamp DEFAULT clock_timestamp(); SELECT comp (); DROP TABLE T; -- Simple querie CREATE TABLE T ( pk int NOT NULL PRIMARY KEY ); SELECT SET ('t'); INSERT INTO T SELECT * FROM generate_series(1, 10) a; ALTER TABLE T ADD COLUMN c_bigint bigint NOT NULL DEFAULT - 1; INSERT INTO T SELECT b, b - 10 FROM generate_series(11, 20) a (b); ALTER TABLE T ADD COLUMN c_text text DEFAULT 'hello'; INSERT INTO T SELECT b, b - 10, (b + 10)::text FROM generate_series(21, 30) a (b); -- WHERE clause SELECT c_bigint, c_text FROM T WHERE c_bigint = - 1 LIMIT 1; EXPLAIN ( VERBOSE TRUE, COSTS FALSE ) SELECT c_bigint, c_text FROM T WHERE c_bigint = - 1 LIMIT 1; SELECT c_bigint, c_text FROM T WHERE c_text = 'hello' LIMIT 1; EXPLAIN ( VERBOSE TRUE, COSTS FALSE ) SELECT c_bigint, c_text FROM T WHERE c_text = 'hello' LIMIT 1; -- COALESCE SELECT COALESCE(c_bigint, pk), COALESCE(c_text, pk::text) FROM T ORDER BY pk LIMIT 10; -- Aggregate function SELECT SUM(c_bigint), MAX(c_text COLLATE "C"), MIN(c_text COLLATE "C") FROM T; -- ORDER BY SELECT * FROM T ORDER BY c_bigint, c_text, pk LIMIT 10; EXPLAIN ( VERBOSE TRUE, COSTS FALSE ) SELECT * FROM T ORDER BY c_bigint, c_text, pk LIMIT 10; -- LIMIT SELECT * FROM T WHERE c_bigint > - 1 ORDER BY c_bigint, c_text, pk LIMIT 10; EXPLAIN ( VERBOSE TRUE, COSTS FALSE ) SELECT * FROM T WHERE c_bigint > - 1 ORDER BY c_bigint, c_text, pk LIMIT 10; -- DELETE with RETURNING DELETE FROM T WHERE pk BETWEEN 10 AND 20 RETURNING *; EXPLAIN ( VERBOSE TRUE, COSTS FALSE ) DELETE FROM T WHERE pk BETWEEN 10 AND 20 RETURNING *; -- UPDATE UPDATE T SET c_text = '"' || c_text || '"' WHERE pk < 10; SELECT * FROM T WHERE c_text LIKE '"%"' ORDER BY PK; SELECT comp (); DROP TABLE T; -- Combine with other DDL CREATE TABLE T ( pk int NOT NULL PRIMARY KEY ); SELECT SET ('t'); INSERT INTO T VALUES (1), (2); ALTER TABLE T ADD COLUMN c_int int NOT NULL DEFAULT - 1; INSERT INTO T VALUES (3), (4); ALTER TABLE T ADD COLUMN c_text text DEFAULT 'Hello'; INSERT INTO T VALUES (5), (6); ALTER TABLE T ALTER COLUMN c_text SET DEFAULT 'world', ALTER COLUMN c_int SET DEFAULT 1; INSERT INTO T VALUES (7), (8); SELECT * FROM T ORDER BY pk; -- Add an index CREATE INDEX i ON T (c_int, c_text); SELECT c_text FROM T WHERE c_int = - 1; SELECT comp (); -- query to exercise expand_tuple function CREATE TABLE t1 AS SELECT 1::int AS a, 2::int AS b FROM generate_series(1, 20) q; ALTER TABLE t1 ADD COLUMN c text; SELECT a, stddev(cast(( SELECT sum(1) FROM generate_series(1, 20) x) AS float4)) OVER (PARTITION BY a, b, c ORDER BY b) AS z FROM t1; DROP TABLE T; -- test that we account for missing columns without defaults correctly -- in expand_tuple, and that rows are correctly expanded for triggers CREATE FUNCTION test_trigger () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RAISE notice 'old tuple: %', to_json(OLD)::text; IF TG_OP = 'DELETE' THEN RETURN OLD; ELSE RETURN NEW; END IF; END; $$; -- 2 new columns, both have defaults CREATE TABLE t ( id serial PRIMARY KEY, a int, b int, c int ); INSERT INTO t (a, b, c) VALUES (1, 2, 3); ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4; ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5; CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger (); SELECT * FROM t; UPDATE t SET y = 2; SELECT * FROM t; DROP TABLE t; -- 2 new columns, first has default CREATE TABLE t ( id serial PRIMARY KEY, a int, b int, c int ); INSERT INTO t (a, b, c) VALUES (1, 2, 3); ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4; ALTER TABLE t ADD COLUMN y int; CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger (); SELECT * FROM t; UPDATE t SET y = 2; SELECT * FROM t; DROP TABLE t; -- 2 new columns, second has default CREATE TABLE t ( id serial PRIMARY KEY, a int, b int, c int ); INSERT INTO t (a, b, c) VALUES (1, 2, 3); ALTER TABLE t ADD COLUMN x int; ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5; CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger (); SELECT * FROM t; UPDATE t SET y = 2; SELECT * FROM t; DROP TABLE t; -- 2 new columns, neither has default CREATE TABLE t ( id serial PRIMARY KEY, a int, b int, c int ); INSERT INTO t (a, b, c) VALUES (1, 2, 3); ALTER TABLE t ADD COLUMN x int; ALTER TABLE t ADD COLUMN y int; CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger (); SELECT * FROM t; UPDATE t SET y = 2; SELECT * FROM t; DROP TABLE t; -- same as last 4 tests but here the last original column has a NULL value -- 2 new columns, both have defaults CREATE TABLE t ( id serial PRIMARY KEY, a int, b int, c int ); INSERT INTO t (a, b, c) VALUES (1, 2, NULL); ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4; ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5; CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger (); SELECT * FROM t; UPDATE t SET y = 2; SELECT * FROM t; DROP TABLE t; -- 2 new columns, first has default CREATE TABLE t ( id serial PRIMARY KEY, a int, b int, c int ); INSERT INTO t (a, b, c) VALUES (1, 2, NULL); ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4; ALTER TABLE t ADD COLUMN y int; CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger (); SELECT * FROM t; UPDATE t SET y = 2; SELECT * FROM t; DROP TABLE t; -- 2 new columns, second has default CREATE TABLE t ( id serial PRIMARY KEY, a int, b int, c int ); INSERT INTO t (a, b, c) VALUES (1, 2, NULL); ALTER TABLE t ADD COLUMN x int; ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5; CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger (); SELECT * FROM t; UPDATE t SET y = 2; SELECT * FROM t; DROP TABLE t; -- 2 new columns, neither has default CREATE TABLE t ( id serial PRIMARY KEY, a int, b int, c int ); INSERT INTO t (a, b, c) VALUES (1, 2, NULL); ALTER TABLE t ADD COLUMN x int; ALTER TABLE t ADD COLUMN y int; CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger (); SELECT * FROM t; UPDATE t SET y = 2; SELECT * FROM t; DROP TABLE t; -- make sure expanded tuple has correct self pointer -- it will be required by the RI trigger doing the cascading delete CREATE TABLE leader ( a int PRIMARY KEY, b int ); CREATE TABLE follower ( a int REFERENCES leader ON DELETE CASCADE, b int ); INSERT INTO leader VALUES (1, 1), (2, 2); ALTER TABLE leader ADD c int; ALTER TABLE leader DROP c; DELETE FROM leader; -- check that ALTER TABLE ... ALTER TYPE does the right thing CREATE TABLE vtype ( a integer ); INSERT INTO vtype VALUES (1); ALTER TABLE vtype ADD COLUMN b double PRECISION DEFAULT 0.2; ALTER TABLE vtype ADD COLUMN c boolean DEFAULT TRUE; SELECT * FROM vtype; ALTER TABLE vtype ALTER b TYPE text USING b::text, ALTER c TYPE text USING c::text; SELECT * FROM vtype; -- also check the case that doesn't rewrite the table CREATE TABLE vtype2 ( a int ); INSERT INTO vtype2 VALUES (1); ALTER TABLE vtype2 ADD COLUMN b varchar(10) DEFAULT 'xxx'; ALTER TABLE vtype2 ALTER COLUMN b SET DEFAULT 'yyy'; INSERT INTO vtype2 VALUES (2); ALTER TABLE vtype2 ALTER COLUMN b TYPE varchar(20) USING b::varchar(20); SELECT * FROM vtype2; -- Ensure that defaults are checked when evaluating whether HOT update -- is possible, this was broken for a while: -- https://postgr.es/m/20190202133521.ylauh3ckqa7colzj%40alap3.anarazel.de BEGIN; CREATE TABLE t (); INSERT INTO t DEFAULT VALUES; ALTER TABLE t ADD COLUMN a int DEFAULT 1; CREATE INDEX ON t (a); -- set column with a default 1 to NULL, due to a bug that wasn't -- noticed has heap_getattr buggily returned NULL for default columns UPDATE t SET a = NULL; -- verify that index and non-index scans show the same result SET LOCAL enable_seqscan = TRUE; SELECT * FROM t WHERE a IS NULL; SET LOCAL enable_seqscan = FALSE; SELECT * FROM t WHERE a IS NULL; ROLLBACK; -- cleanup DROP TABLE vtype; DROP TABLE vtype2; DROP TABLE follower; DROP TABLE leader; DROP FUNCTION test_trigger (); DROP TABLE t1; DROP FUNCTION SET (name); DROP FUNCTION comp (); DROP TABLE m; DROP TABLE has_volatile; DROP EVENT TRIGGER has_volatile_rewrite; DROP FUNCTION log_rewrite; DROP SCHEMA fast_default; -- Leave a table with an active fast default in place, for pg_upgrade testing SET search_path = public; CREATE TABLE has_fast_default ( f1 int ); INSERT INTO has_fast_default VALUES (1); ALTER TABLE has_fast_default ADD COLUMN f2 int DEFAULT 42; TABLE has_fast_default; pgFormatter-4.2/t/pg-test-files/expected/float4.sql000066400000000000000000000337151361326045100223240ustar00rootroot00000000000000-- -- FLOAT4 -- CREATE TABLE FLOAT4_TBL ( f1 float4 ); INSERT INTO FLOAT4_TBL (f1) VALUES (' 0.0'); INSERT INTO FLOAT4_TBL (f1) VALUES ('1004.30 '); INSERT INTO FLOAT4_TBL (f1) VALUES (' -34.84 '); INSERT INTO FLOAT4_TBL (f1) VALUES ('1.2345678901234e+20'); INSERT INTO FLOAT4_TBL (f1) VALUES ('1.2345678901234e-20'); -- test for over and under flow INSERT INTO FLOAT4_TBL (f1) VALUES ('10e70'); INSERT INTO FLOAT4_TBL (f1) VALUES ('-10e70'); INSERT INTO FLOAT4_TBL (f1) VALUES ('10e-70'); INSERT INTO FLOAT4_TBL (f1) VALUES ('-10e-70'); INSERT INTO FLOAT4_TBL (f1) VALUES ('10e400'); INSERT INTO FLOAT4_TBL (f1) VALUES ('-10e400'); INSERT INTO FLOAT4_TBL (f1) VALUES ('10e-400'); INSERT INTO FLOAT4_TBL (f1) VALUES ('-10e-400'); -- bad input INSERT INTO FLOAT4_TBL (f1) VALUES (''); INSERT INTO FLOAT4_TBL (f1) VALUES (' '); INSERT INTO FLOAT4_TBL (f1) VALUES ('xyz'); INSERT INTO FLOAT4_TBL (f1) VALUES ('5.0.0'); INSERT INTO FLOAT4_TBL (f1) VALUES ('5 . 0'); INSERT INTO FLOAT4_TBL (f1) VALUES ('5. 0'); INSERT INTO FLOAT4_TBL (f1) VALUES (' - 3.0'); INSERT INTO FLOAT4_TBL (f1) VALUES ('123 5'); -- special inputs SELECT 'NaN'::float4; SELECT 'nan'::float4; SELECT ' NAN '::float4; SELECT 'infinity'::float4; SELECT ' -INFINiTY '::float4; -- bad special inputs SELECT 'N A N'::float4; SELECT 'NaN x'::float4; SELECT ' INFINITY x'::float4; SELECT 'Infinity'::float4 + 100.0; SELECT 'Infinity'::float4 / 'Infinity'::float4; SELECT 'nan'::float4 / 'nan'::float4; SELECT 'nan'::numeric::float4; SELECT '' AS five, * FROM FLOAT4_TBL; SELECT '' AS four, f.* FROM FLOAT4_TBL f WHERE f.f1 <> '1004.3'; SELECT '' AS one, f.* FROM FLOAT4_TBL f WHERE f.f1 = '1004.3'; SELECT '' AS three, f.* FROM FLOAT4_TBL f WHERE '1004.3' > f.f1; SELECT '' AS three, f.* FROM FLOAT4_TBL f WHERE f.f1 < '1004.3'; SELECT '' AS four, f.* FROM FLOAT4_TBL f WHERE '1004.3' >= f.f1; SELECT '' AS four, f.* FROM FLOAT4_TBL f WHERE f.f1 <= '1004.3'; SELECT '' AS three, f.f1, f.f1 * '-10' AS x FROM FLOAT4_TBL f WHERE f.f1 > '0.0'; SELECT '' AS three, f.f1, f.f1 + '-10' AS x FROM FLOAT4_TBL f WHERE f.f1 > '0.0'; SELECT '' AS three, f.f1, f.f1 / '-10' AS x FROM FLOAT4_TBL f WHERE f.f1 > '0.0'; SELECT '' AS three, f.f1, f.f1 - '-10' AS x FROM FLOAT4_TBL f WHERE f.f1 > '0.0'; -- test divide by zero SELECT '' AS bad, f.f1 / '0.0' FROM FLOAT4_TBL f; SELECT '' AS five, * FROM FLOAT4_TBL; -- test the unary float4abs operator SELECT '' AS five, f.f1, @f.f1 AS abs_f1 FROM FLOAT4_TBL f; UPDATE FLOAT4_TBL SET f1 = FLOAT4_TBL.f1 * '-1' WHERE FLOAT4_TBL.f1 > '0.0'; SELECT '' AS five, * FROM FLOAT4_TBL; -- test edge-case coercions to integer SELECT '32767.4'::float4::int2; SELECT '32767.6'::float4::int2; SELECT '-32768.4'::float4::int2; SELECT '-32768.6'::float4::int2; SELECT '2147483520'::float4::int4; SELECT '2147483647'::float4::int4; SELECT '-2147483648.5'::float4::int4; SELECT '-2147483900'::float4::int4; SELECT '9223369837831520256'::float4::int8; SELECT '9223372036854775807'::float4::int8; SELECT '-9223372036854775808.5'::float4::int8; SELECT '-9223380000000000000'::float4::int8; -- Test for correct input rounding in edge cases. -- These lists are from Paxson 1991, excluding subnormals and -- inputs of over 9 sig. digits. SELECT float4send('5e-20'::float4); SELECT float4send('67e14'::float4); SELECT float4send('985e15'::float4); SELECT float4send('55895e-16'::float4); SELECT float4send('7038531e-32'::float4); SELECT float4send('702990899e-20'::float4); SELECT float4send('3e-23'::float4); SELECT float4send('57e18'::float4); SELECT float4send('789e-35'::float4); SELECT float4send('2539e-18'::float4); SELECT float4send('76173e28'::float4); SELECT float4send('887745e-11'::float4); SELECT float4send('5382571e-37'::float4); SELECT float4send('82381273e-35'::float4); SELECT float4send('750486563e-38'::float4); -- Test that the smallest possible normalized input value inputs -- correctly, either in 9-significant-digit or shortest-decimal -- format. -- -- exact val is 1.1754943508... -- shortest val is 1.1754944000 -- midpoint to next val is 1.1754944208... SELECT float4send('1.17549435e-38'::float4); SELECT float4send('1.1754944e-38'::float4); -- test output (and round-trip safety) of various values. -- To ensure we're testing what we think we're testing, start with -- float values specified by bit patterns (as a useful side effect, -- this means we'll fail on non-IEEE platforms). CREATE TYPE xfloat4; CREATE FUNCTION xfloat4in (cstring) RETURNS xfloat4 IMMUTABLE STRICT LANGUAGE internal AS 'int4in'; CREATE FUNCTION xfloat4out (xfloat4) RETURNS cstring IMMUTABLE STRICT LANGUAGE internal AS 'int4out'; CREATE TYPE xfloat4 ( input = xfloat4in, output = xfloat4out, LIKE = float4 ); CREATE cast( xfloat4 AS float4) without FUNCTION; CREATE cast( float4 AS xfloat4) without FUNCTION; CREATE cast( xfloat4 AS integer) without FUNCTION; CREATE cast( integer AS xfloat4) without FUNCTION; -- float4: seeeeeee emmmmmmm mmmmmmmm mmmmmmmm -- we don't care to assume the platform's strtod() handles subnormals -- correctly; those are "use at your own risk". However we do test -- subnormal outputs, since those are under our control. WITH testdata ( bits ) AS ( VALUES -- small subnormals (x '00000001'), (x '00000002'), (x '00000003'), (x '00000010'), (x '00000011'), (x '00000100'), (x '00000101'), (x '00004000'), (x '00004001'), (x '00080000'), (x '00080001'), -- stress values (x '0053c4f4'), -- 7693e-42 (x '006c85c4'), -- 996622e-44 (x '0041ca76'), -- 60419369e-46 (x '004b7678'), -- 6930161142e-48 -- taken from upstream testsuite (x '00000007'), (x '00424fe2'), -- borderline between subnormal and normal (x '007ffff0'), (x '007ffff1'), (x '007ffffe'), (x '007fffff')) SELECT float4send(flt) AS ibits, flt FROM ( SELECT bits::integer::xfloat4::float4 AS flt FROM testdata offset 0) s; WITH testdata ( bits ) AS ( VALUES (x '00000000'), -- smallest normal values (x '00800000'), (x '00800001'), (x '00800004'), (x '00800005'), (x '00800006'), -- small normal values chosen for short vs. long output (x '008002f1'), (x '008002f2'), (x '008002f3'), (x '00800e17'), (x '00800e18'), (x '00800e19'), -- assorted values (random mantissae) (x '01000001'), (x '01102843'), (x '01a52c98'), (x '0219c229'), (x '02e4464d'), (x '037343c1'), (x '03a91b36'), (x '047ada65'), (x '0496fe87'), (x '0550844f'), (x '05999da3'), (x '060ea5e2'), (x '06e63c45'), (x '07f1e548'), (x '0fc5282b'), (x '1f850283'), (x '2874a9d6'), -- values around 5e-08 (x '3356bf94'), (x '3356bf95'), (x '3356bf96'), -- around 1e-07 (x '33d6bf94'), (x '33d6bf95'), (x '33d6bf96'), -- around 3e-07 .. 1e-04 (x '34a10faf'), (x '34a10fb0'), (x '34a10fb1'), (x '350637bc'), (x '350637bd'), (x '350637be'), (x '35719786'), (x '35719787'), (x '35719788'), (x '358637bc'), (x '358637bd'), (x '358637be'), (x '36a7c5ab'), (x '36a7c5ac'), (x '36a7c5ad'), (x '3727c5ab'), (x '3727c5ac'), (x '3727c5ad'), -- format crossover at 1e-04 (x '38d1b714'), (x '38d1b715'), (x '38d1b716'), (x '38d1b717'), (x '38d1b718'), (x '38d1b719'), (x '38d1b71a'), (x '38d1b71b'), (x '38d1b71c'), (x '38d1b71d'), -- (x '38dffffe'), (x '38dfffff'), (x '38e00000'), (x '38efffff'), (x '38f00000'), (x '38f00001'), (x '3a83126e'), (x '3a83126f'), (x '3a831270'), (x '3c23d709'), (x '3c23d70a'), (x '3c23d70b'), (x '3dcccccc'), (x '3dcccccd'), (x '3dccccce'), -- chosen to need 9 digits for 3dcccd70 (x '3dcccd6f'), (x '3dcccd70'), (x '3dcccd71'), -- (x '3effffff'), (x '3f000000'), (x '3f000001'), (x '3f333332'), (x '3f333333'), (x '3f333334'), -- approach 1.0 with increasing numbers of 9s (x '3f666665'), (x '3f666666'), (x '3f666667'), (x '3f7d70a3'), (x '3f7d70a4'), (x '3f7d70a5'), (x '3f7fbe76'), (x '3f7fbe77'), (x '3f7fbe78'), (x '3f7ff971'), (x '3f7ff972'), (x '3f7ff973'), (x '3f7fff57'), (x '3f7fff58'), (x '3f7fff59'), (x '3f7fffee'), (x '3f7fffef'), -- values very close to 1 (x '3f7ffff0'), (x '3f7ffff1'), (x '3f7ffff2'), (x '3f7ffff3'), (x '3f7ffff4'), (x '3f7ffff5'), (x '3f7ffff6'), (x '3f7ffff7'), (x '3f7ffff8'), (x '3f7ffff9'), (x '3f7ffffa'), (x '3f7ffffb'), (x '3f7ffffc'), (x '3f7ffffd'), (x '3f7ffffe'), (x '3f7fffff'), (x '3f800000'), (x '3f800001'), (x '3f800002'), (x '3f800003'), (x '3f800004'), (x '3f800005'), (x '3f800006'), (x '3f800007'), (x '3f800008'), (x '3f800009'), -- values 1 to 1.1 (x '3f80000f'), (x '3f800010'), (x '3f800011'), (x '3f800012'), (x '3f800013'), (x '3f800014'), (x '3f800017'), (x '3f800018'), (x '3f800019'), (x '3f80001a'), (x '3f80001b'), (x '3f80001c'), (x '3f800029'), (x '3f80002a'), (x '3f80002b'), (x '3f800053'), (x '3f800054'), (x '3f800055'), (x '3f800346'), (x '3f800347'), (x '3f800348'), (x '3f8020c4'), (x '3f8020c5'), (x '3f8020c6'), (x '3f8147ad'), (x '3f8147ae'), (x '3f8147af'), (x '3f8ccccc'), (x '3f8ccccd'), (x '3f8cccce'), -- (x '3fc90fdb'), -- pi/2 (x '402df854'), -- e (x '40490fdb'), -- pi -- (x '409fffff'), (x '40a00000'), (x '40a00001'), (x '40afffff'), (x '40b00000'), (x '40b00001'), (x '411fffff'), (x '41200000'), (x '41200001'), (x '42c7ffff'), (x '42c80000'), (x '42c80001'), (x '4479ffff'), (x '447a0000'), (x '447a0001'), (x '461c3fff'), (x '461c4000'), (x '461c4001'), (x '47c34fff'), (x '47c35000'), (x '47c35001'), (x '497423ff'), (x '49742400'), (x '49742401'), (x '4b18967f'), (x '4b189680'), (x '4b189681'), (x '4cbebc1f'), (x '4cbebc20'), (x '4cbebc21'), (x '4e6e6b27'), (x '4e6e6b28'), (x '4e6e6b29'), (x '501502f8'), (x '501502f9'), (x '501502fa'), (x '51ba43b6'), (x '51ba43b7'), (x '51ba43b8'), -- stress values (x '1f6c1e4a'), -- 5e-20 (x '59be6cea'), -- 67e14 (x '5d5ab6c4'), -- 985e15 (x '2cc4a9bd'), -- 55895e-16 (x '15ae43fd'), -- 7038531e-32 (x '2cf757ca'), -- 702990899e-20 (x '665ba998'), -- 25933168707e13 (x '743c3324'), -- 596428896559e20 -- exercise fixed-point memmoves (x '47f1205a'), (x '4640e6ae'), (x '449a5225'), (x '42f6e9d5'), (x '414587dd'), (x '3f9e064b'), -- these cases come from the upstream's testsuite -- BoundaryRoundEven (x '4c000004'), (x '50061c46'), (x '510006a8'), -- ExactValueRoundEven (x '48951f84'), (x '45fd1840'), -- LotsOfTrailingZeros (x '39800000'), (x '3b200000'), (x '3b900000'), (x '3bd00000'), -- Regression (x '63800000'), (x '4b000000'), (x '4b800000'), (x '4c000001'), (x '4c800b0d'), (x '00d24584'), (x '00d90b88'), (x '45803f34'), (x '4f9f24f7'), (x '3a8722c3'), (x '5c800041'), (x '15ae43fd'), (x '5d4cccfb'), (x '4c800001'), (x '57800ed8'), (x '5f000000'), (x '700000f0'), (x '5f23e9ac'), (x '5e9502f9'), (x '5e8012b1'), (x '3c000028'), (x '60cde861'), (x '03aa2a50'), (x '43480000'), (x '4c000000'), -- LooksLikePow5 (x '5D1502F9'), (x '5D9502F9'), (x '5E1502F9'), -- OutputLength (x '3f99999a'), (x '3f9d70a4'), (x '3f9df3b6'), (x '3f9e0419'), (x '3f9e0610'), (x '3f9e064b'), (x '3f9e0651'), (x '03d20cfe')) SELECT float4send(flt) AS ibits, flt, flt::text::float4 AS r_flt, float4send(flt::text::float4) AS obits, float4send(flt::text::float4) = float4send(flt) AS correct FROM ( SELECT bits::integer::xfloat4::float4 AS flt FROM testdata offset 0) s; -- clean up, lest opr_sanity complain DROP TYPE xfloat4 CASCADE; pgFormatter-4.2/t/pg-test-files/expected/float8.sql000066400000000000000000000440411361326045100223220ustar00rootroot00000000000000-- -- FLOAT8 -- CREATE TABLE FLOAT8_TBL ( f1 float8 ); INSERT INTO FLOAT8_TBL (f1) VALUES (' 0.0 '); INSERT INTO FLOAT8_TBL (f1) VALUES ('1004.30 '); INSERT INTO FLOAT8_TBL (f1) VALUES (' -34.84'); INSERT INTO FLOAT8_TBL (f1) VALUES ('1.2345678901234e+200'); INSERT INTO FLOAT8_TBL (f1) VALUES ('1.2345678901234e-200'); -- test for underflow and overflow handling SELECT '10e400'::float8; SELECT '-10e400'::float8; SELECT '10e-400'::float8; SELECT '-10e-400'::float8; -- test smallest normalized input SELECT float8send('2.2250738585072014E-308'::float8); -- bad input INSERT INTO FLOAT8_TBL (f1) VALUES (''); INSERT INTO FLOAT8_TBL (f1) VALUES (' '); INSERT INTO FLOAT8_TBL (f1) VALUES ('xyz'); INSERT INTO FLOAT8_TBL (f1) VALUES ('5.0.0'); INSERT INTO FLOAT8_TBL (f1) VALUES ('5 . 0'); INSERT INTO FLOAT8_TBL (f1) VALUES ('5. 0'); INSERT INTO FLOAT8_TBL (f1) VALUES (' - 3'); INSERT INTO FLOAT8_TBL (f1) VALUES ('123 5'); -- special inputs SELECT 'NaN'::float8; SELECT 'nan'::float8; SELECT ' NAN '::float8; SELECT 'infinity'::float8; SELECT ' -INFINiTY '::float8; -- bad special inputs SELECT 'N A N'::float8; SELECT 'NaN x'::float8; SELECT ' INFINITY x'::float8; SELECT 'Infinity'::float8 + 100.0; SELECT 'Infinity'::float8 / 'Infinity'::float8; SELECT 'nan'::float8 / 'nan'::float8; SELECT 'nan'::numeric::float8; SELECT '' AS five, * FROM FLOAT8_TBL; SELECT '' AS four, f.* FROM FLOAT8_TBL f WHERE f.f1 <> '1004.3'; SELECT '' AS one, f.* FROM FLOAT8_TBL f WHERE f.f1 = '1004.3'; SELECT '' AS three, f.* FROM FLOAT8_TBL f WHERE '1004.3' > f.f1; SELECT '' AS three, f.* FROM FLOAT8_TBL f WHERE f.f1 < '1004.3'; SELECT '' AS four, f.* FROM FLOAT8_TBL f WHERE '1004.3' >= f.f1; SELECT '' AS four, f.* FROM FLOAT8_TBL f WHERE f.f1 <= '1004.3'; SELECT '' AS three, f.f1, f.f1 * '-10' AS x FROM FLOAT8_TBL f WHERE f.f1 > '0.0'; SELECT '' AS three, f.f1, f.f1 + '-10' AS x FROM FLOAT8_TBL f WHERE f.f1 > '0.0'; SELECT '' AS three, f.f1, f.f1 / '-10' AS x FROM FLOAT8_TBL f WHERE f.f1 > '0.0'; SELECT '' AS three, f.f1, f.f1 - '-10' AS x FROM FLOAT8_TBL f WHERE f.f1 > '0.0'; SELECT '' AS one, f.f1 ^ '2.0' AS square_f1 FROM FLOAT8_TBL f WHERE f.f1 = '1004.3'; -- absolute value SELECT '' AS five, f.f1, @f.f1 AS abs_f1 FROM FLOAT8_TBL f; -- truncate SELECT '' AS five, f.f1, trunc(f.f1) AS trunc_f1 FROM FLOAT8_TBL f; -- round SELECT '' AS five, f.f1, round(f.f1) AS round_f1 FROM FLOAT8_TBL f; -- ceil / ceiling SELECT ceil(f1) AS ceil_f1 FROM float8_tbl f; SELECT ceiling(f1) AS ceiling_f1 FROM float8_tbl f; -- floor SELECT floor(f1) AS floor_f1 FROM float8_tbl f; -- sign SELECT sign(f1) AS sign_f1 FROM float8_tbl f; -- avoid bit-exact output here because operations may not be bit-exact. SET extra_float_digits = 0; -- square root SELECT sqrt(float8 '64') AS eight; SELECT |/ float8 '64' AS eight; SELECT '' AS three, f.f1, |/ f.f1 AS sqrt_f1 FROM FLOAT8_TBL f WHERE f.f1 > '0.0'; -- power SELECT power(float8 '144', float8 '0.5'); SELECT power(float8 'NaN', float8 '0.5'); SELECT power(float8 '144', float8 'NaN'); SELECT power(float8 'NaN', float8 'NaN'); SELECT power(float8 '-1', float8 'NaN'); SELECT power(float8 '1', float8 'NaN'); SELECT power(float8 'NaN', float8 '0'); -- take exp of ln(f.f1) SELECT '' AS three, f.f1, exp(ln(f.f1)) AS exp_ln_f1 FROM FLOAT8_TBL f WHERE f.f1 > '0.0'; -- cube root SELECT ||/ float8 '27' AS three; SELECT '' AS five, f.f1, ||/ f.f1 AS cbrt_f1 FROM FLOAT8_TBL f; SELECT '' AS five, * FROM FLOAT8_TBL; UPDATE FLOAT8_TBL SET f1 = FLOAT8_TBL.f1 * '-1' WHERE FLOAT8_TBL.f1 > '0.0'; SELECT '' AS bad, f.f1 * '1e200' FROM FLOAT8_TBL f; SELECT '' AS bad, f.f1 ^ '1e200' FROM FLOAT8_TBL f; SELECT 0 ^ 0 + 0 ^ 1 + 0 ^ 0.0 + 0 ^ 0.5; SELECT '' AS bad, ln(f.f1) FROM FLOAT8_TBL f WHERE f.f1 = '0.0'; SELECT '' AS bad, ln(f.f1) FROM FLOAT8_TBL f WHERE f.f1 < '0.0'; SELECT '' AS bad, exp(f.f1) FROM FLOAT8_TBL f; SELECT '' AS bad, f.f1 / '0.0' FROM FLOAT8_TBL f; SELECT '' AS five, * FROM FLOAT8_TBL; -- hyperbolic functions -- we run these with extra_float_digits = 0 too, since different platforms -- tend to produce results that vary in the last place. SELECT sinh (float8 '1'); SELECT cosh (float8 '1'); SELECT tanh (float8 '1'); SELECT asinh (float8 '1'); SELECT acosh (float8 '2'); SELECT atanh (float8 '0.5'); -- test Inf/NaN cases for hyperbolic functions SELECT sinh (float8 'infinity'); SELECT sinh (float8 '-infinity'); SELECT sinh (float8 'nan'); SELECT cosh (float8 'infinity'); SELECT cosh (float8 '-infinity'); SELECT cosh (float8 'nan'); SELECT tanh (float8 'infinity'); SELECT tanh (float8 '-infinity'); SELECT tanh (float8 'nan'); SELECT asinh (float8 'infinity'); SELECT asinh (float8 '-infinity'); SELECT asinh (float8 'nan'); -- acosh(Inf) should be Inf, but some mingw versions produce NaN, so skip test -- SELECT acosh(float8 'infinity'); SELECT acosh (float8 '-infinity'); SELECT acosh (float8 'nan'); SELECT atanh (float8 'infinity'); SELECT atanh (float8 '-infinity'); SELECT atanh (float8 'nan'); RESET extra_float_digits; -- test for over- and underflow INSERT INTO FLOAT8_TBL (f1) VALUES ('10e400'); INSERT INTO FLOAT8_TBL (f1) VALUES ('-10e400'); INSERT INTO FLOAT8_TBL (f1) VALUES ('10e-400'); INSERT INTO FLOAT8_TBL (f1) VALUES ('-10e-400'); -- maintain external table consistency across platforms -- delete all values and reinsert well-behaved ones DELETE FROM FLOAT8_TBL; INSERT INTO FLOAT8_TBL (f1) VALUES ('0.0'); INSERT INTO FLOAT8_TBL (f1) VALUES ('-34.84'); INSERT INTO FLOAT8_TBL (f1) VALUES ('-1004.30'); INSERT INTO FLOAT8_TBL (f1) VALUES ('-1.2345678901234e+200'); INSERT INTO FLOAT8_TBL (f1) VALUES ('-1.2345678901234e-200'); SELECT '' AS five, * FROM FLOAT8_TBL; -- test edge-case coercions to integer SELECT '32767.4'::float8::int2; SELECT '32767.6'::float8::int2; SELECT '-32768.4'::float8::int2; SELECT '-32768.6'::float8::int2; SELECT '2147483647.4'::float8::int4; SELECT '2147483647.6'::float8::int4; SELECT '-2147483648.4'::float8::int4; SELECT '-2147483648.6'::float8::int4; SELECT '9223372036854773760'::float8::int8; SELECT '9223372036854775807'::float8::int8; SELECT '-9223372036854775808.5'::float8::int8; SELECT '-9223372036854780000'::float8::int8; -- test exact cases for trigonometric functions in degrees SELECT x, sind (x), sind (x) IN (- 1, - 0.5, 0, 0.5, 1) AS sind_exact FROM ( VALUES (0), (30), (90), (150), (180), (210), (270), (330), (360)) AS t (x); SELECT x, cosd (x), cosd (x) IN (- 1, - 0.5, 0, 0.5, 1) AS cosd_exact FROM ( VALUES (0), (60), (90), (120), (180), (240), (270), (300), (360)) AS t (x); SELECT x, tand (x), tand (x) IN ('-Infinity'::float8, - 1, 0, 1, 'Infinity'::float8) AS tand_exact, cotd (x), cotd (x) IN ('-Infinity'::float8, - 1, 0, 1, 'Infinity'::float8) AS cotd_exact FROM ( VALUES (0), (45), (90), (135), (180), (225), (270), (315), (360)) AS t (x); SELECT x, asind (x), asind (x) IN (- 90, - 30, 0, 30, 90) AS asind_exact, acosd (x), acosd (x) IN (0, 60, 90, 120, 180) AS acosd_exact FROM ( VALUES (- 1), (- 0.5), (0), (0.5), (1)) AS t (x); SELECT x, atand (x), atand (x) IN (- 90, - 45, 0, 45, 90) AS atand_exact FROM ( VALUES ('-Infinity'::float8), (- 1), (0), (1), ('Infinity'::float8)) AS t (x); SELECT x, y, atan2d (y, x), atan2d (y, x) IN (- 90, 0, 90, 180) AS atan2d_exact FROM ( SELECT 10 * cosd (a), 10 * sind (a) FROM generate_series(0, 360, 90) AS t (a)) AS t (x, y); -- -- test output (and round-trip safety) of various values. -- To ensure we're testing what we think we're testing, start with -- float values specified by bit patterns (as a useful side effect, -- this means we'll fail on non-IEEE platforms). CREATE TYPE xfloat8; CREATE FUNCTION xfloat8in (cstring) RETURNS xfloat8 IMMUTABLE STRICT LANGUAGE internal AS 'int8in'; CREATE FUNCTION xfloat8out (xfloat8) RETURNS cstring IMMUTABLE STRICT LANGUAGE internal AS 'int8out'; CREATE TYPE xfloat8 ( input = xfloat8in, output = xfloat8out, LIKE = float8 ); CREATE cast( xfloat8 AS float8) without FUNCTION; CREATE cast( float8 AS xfloat8) without FUNCTION; CREATE cast( xfloat8 AS bigint) without FUNCTION; CREATE cast( bigint AS xfloat8) without FUNCTION; -- float8: seeeeeee eeeeeeee eeeeeeee mmmmmmmm mmmmmmmm(x4) -- we don't care to assume the platform's strtod() handles subnormals -- correctly; those are "use at your own risk". However we do test -- subnormal outputs, since those are under our control. WITH testdata ( bits ) AS ( VALUES -- small subnormals (x '0000000000000001'), (x '0000000000000002'), (x '0000000000000003'), (x '0000000000001000'), (x '0000000100000000'), (x '0000010000000000'), (x '0000010100000000'), (x '0000400000000000'), (x '0000400100000000'), (x '0000800000000000'), (x '0000800000000001'), -- these values taken from upstream testsuite (x '00000000000f4240'), (x '00000000016e3600'), (x '0000008cdcdea440'), -- borderline between subnormal and normal (x '000ffffffffffff0'), (x '000ffffffffffff1'), (x '000ffffffffffffe'), (x '000fffffffffffff')) SELECT float8send(flt) AS ibits, flt FROM ( SELECT bits::bigint::xfloat8::float8 AS flt FROM testdata offset 0) s; -- round-trip tests WITH testdata ( bits ) AS ( VALUES (x '0000000000000000'), -- smallest normal values (x '0010000000000000'), (x '0010000000000001'), (x '0010000000000002'), (x '0018000000000000'), -- (x '3ddb7cdfd9d7bdba'), (x '3ddb7cdfd9d7bdbb'), (x '3ddb7cdfd9d7bdbc'), (x '3e112e0be826d694'), (x '3e112e0be826d695'), (x '3e112e0be826d696'), (x '3e45798ee2308c39'), (x '3e45798ee2308c3a'), (x '3e45798ee2308c3b'), (x '3e7ad7f29abcaf47'), (x '3e7ad7f29abcaf48'), (x '3e7ad7f29abcaf49'), (x '3eb0c6f7a0b5ed8c'), (x '3eb0c6f7a0b5ed8d'), (x '3eb0c6f7a0b5ed8e'), (x '3ee4f8b588e368ef'), (x '3ee4f8b588e368f0'), (x '3ee4f8b588e368f1'), (x '3f1a36e2eb1c432c'), (x '3f1a36e2eb1c432d'), (x '3f1a36e2eb1c432e'), (x '3f50624dd2f1a9fb'), (x '3f50624dd2f1a9fc'), (x '3f50624dd2f1a9fd'), (x '3f847ae147ae147a'), (x '3f847ae147ae147b'), (x '3f847ae147ae147c'), (x '3fb9999999999999'), (x '3fb999999999999a'), (x '3fb999999999999b'), -- values very close to 1 (x '3feffffffffffff0'), (x '3feffffffffffff1'), (x '3feffffffffffff2'), (x '3feffffffffffff3'), (x '3feffffffffffff4'), (x '3feffffffffffff5'), (x '3feffffffffffff6'), (x '3feffffffffffff7'), (x '3feffffffffffff8'), (x '3feffffffffffff9'), (x '3feffffffffffffa'), (x '3feffffffffffffb'), (x '3feffffffffffffc'), (x '3feffffffffffffd'), (x '3feffffffffffffe'), (x '3fefffffffffffff'), (x '3ff0000000000000'), (x '3ff0000000000001'), (x '3ff0000000000002'), (x '3ff0000000000003'), (x '3ff0000000000004'), (x '3ff0000000000005'), (x '3ff0000000000006'), (x '3ff0000000000007'), (x '3ff0000000000008'), (x '3ff0000000000009'), -- (x '3ff921fb54442d18'), (x '4005bf0a8b14576a'), (x '400921fb54442d18'), -- (x '4023ffffffffffff'), (x '4024000000000000'), (x '4024000000000001'), (x '4058ffffffffffff'), (x '4059000000000000'), (x '4059000000000001'), (x '408f3fffffffffff'), (x '408f400000000000'), (x '408f400000000001'), (x '40c387ffffffffff'), (x '40c3880000000000'), (x '40c3880000000001'), (x '40f869ffffffffff'), (x '40f86a0000000000'), (x '40f86a0000000001'), (x '412e847fffffffff'), (x '412e848000000000'), (x '412e848000000001'), (x '416312cfffffffff'), (x '416312d000000000'), (x '416312d000000001'), (x '4197d783ffffffff'), (x '4197d78400000000'), (x '4197d78400000001'), (x '41cdcd64ffffffff'), (x '41cdcd6500000000'), (x '41cdcd6500000001'), (x '4202a05f1fffffff'), (x '4202a05f20000000'), (x '4202a05f20000001'), (x '42374876e7ffffff'), (x '42374876e8000000'), (x '42374876e8000001'), (x '426d1a94a1ffffff'), (x '426d1a94a2000000'), (x '426d1a94a2000001'), (x '42a2309ce53fffff'), (x '42a2309ce5400000'), (x '42a2309ce5400001'), (x '42d6bcc41e8fffff'), (x '42d6bcc41e900000'), (x '42d6bcc41e900001'), (x '430c6bf52633ffff'), (x '430c6bf526340000'), (x '430c6bf526340001'), (x '4341c37937e07fff'), (x '4341c37937e08000'), (x '4341c37937e08001'), (x '4376345785d89fff'), (x '4376345785d8a000'), (x '4376345785d8a001'), (x '43abc16d674ec7ff'), (x '43abc16d674ec800'), (x '43abc16d674ec801'), (x '43e158e460913cff'), (x '43e158e460913d00'), (x '43e158e460913d01'), (x '4415af1d78b58c3f'), (x '4415af1d78b58c40'), (x '4415af1d78b58c41'), (x '444b1ae4d6e2ef4f'), (x '444b1ae4d6e2ef50'), (x '444b1ae4d6e2ef51'), (x '4480f0cf064dd591'), (x '4480f0cf064dd592'), (x '4480f0cf064dd593'), (x '44b52d02c7e14af5'), (x '44b52d02c7e14af6'), (x '44b52d02c7e14af7'), (x '44ea784379d99db3'), (x '44ea784379d99db4'), (x '44ea784379d99db5'), (x '45208b2a2c280290'), (x '45208b2a2c280291'), (x '45208b2a2c280292'), -- (x '7feffffffffffffe'), (x '7fefffffffffffff'), -- round to even tests (+ve) (x '4350000000000002'), (x '4350000000002e06'), (x '4352000000000003'), (x '4352000000000004'), (x '4358000000000003'), (x '4358000000000004'), (x '435f000000000020'), -- round to even tests (-ve) (x 'c350000000000002'), (x 'c350000000002e06'), (x 'c352000000000003'), (x 'c352000000000004'), (x 'c358000000000003'), (x 'c358000000000004'), (x 'c35f000000000020'), -- exercise fixed-point memmoves (x '42dc12218377de66'), (x '42a674e79c5fe51f'), (x '4271f71fb04cb74c'), (x '423cbe991a145879'), (x '4206fee0e1a9e061'), (x '41d26580b487e6b4'), (x '419d6f34540ca453'), (x '41678c29dcd6e9dc'), (x '4132d687e3df217d'), (x '40fe240c9fcb68c8'), (x '40c81cd6e63c53d3'), (x '40934a4584fd0fdc'), (x '405edd3c07fb4c93'), (x '4028b0fcd32f7076'), (x '3ff3c0ca428c59f8'), -- these cases come from the upstream's testsuite -- LotsOfTrailingZeros) (x '3e60000000000000'), -- Regression (x 'c352bd2668e077c4'), (x '434018601510c000'), (x '43d055dc36f24000'), (x '43e052961c6f8000'), (x '3ff3c0ca2a5b1d5d'), -- LooksLikePow5 (x '4830f0cf064dd592'), (x '4840f0cf064dd592'), (x '4850f0cf064dd592'), -- OutputLength (x '3ff3333333333333'), (x '3ff3ae147ae147ae'), (x '3ff3be76c8b43958'), (x '3ff3c083126e978d'), (x '3ff3c0c1fc8f3238'), (x '3ff3c0c9539b8887'), (x '3ff3c0ca2a5b1d5d'), (x '3ff3c0ca4283de1b'), (x '3ff3c0ca43db770a'), (x '3ff3c0ca428abd53'), (x '3ff3c0ca428c1d2b'), (x '3ff3c0ca428c51f2'), (x '3ff3c0ca428c58fc'), (x '3ff3c0ca428c59dd'), (x '3ff3c0ca428c59f8'), (x '3ff3c0ca428c59fb'), -- 32-bit chunking (x '40112e0be8047a7d'), (x '40112e0be815a889'), (x '40112e0be826d695'), (x '40112e0be83804a1'), (x '40112e0be84932ad'), -- MinMaxShift (x '0040000000000000'), (x '007fffffffffffff'), (x '0290000000000000'), (x '029fffffffffffff'), (x '4350000000000000'), (x '435fffffffffffff'), (x '1330000000000000'), (x '133fffffffffffff'), (x '3a6fa7161a4d6e0c')) SELECT float8send(flt) AS ibits, flt, flt::text::float8 AS r_flt, float8send(flt::text::float8) AS obits, float8send(flt::text::float8) = float8send(flt) AS correct FROM ( SELECT bits::bigint::xfloat8::float8 AS flt FROM testdata offset 0) s; -- clean up, lest opr_sanity complain DROP TYPE xfloat8 CASCADE; pgFormatter-4.2/t/pg-test-files/expected/foreign_data.sql000066400000000000000000001027721361326045100235550ustar00rootroot00000000000000-- -- Test foreign-data wrapper and server management. -- -- Clean up in case a prior regression run failed -- Suppress NOTICE messages when roles don't exist SET client_min_messages TO 'warning'; DROP ROLE IF EXISTS regress_foreign_data_user, regress_test_role, regress_test_role2, regress_test_role_super, regress_test_indirect, regress_unprivileged_role; RESET client_min_messages; CREATE ROLE regress_foreign_data_user LOGIN SUPERUSER; SET SESSION AUTHORIZATION 'regress_foreign_data_user'; CREATE ROLE regress_test_role; CREATE ROLE regress_test_role2; CREATE ROLE regress_test_role_super SUPERUSER; CREATE ROLE regress_test_indirect; CREATE ROLE regress_unprivileged_role; CREATE FOREIGN DATA WRAPPER dummy; COMMENT ON FOREIGN DATA WRAPPER dummy IS 'useless'; CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator; -- At this point we should have 2 built-in wrappers and no servers. SELECT fdwname, fdwhandler::regproc, fdwvalidator::regproc, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; SELECT srvname, srvoptions FROM pg_foreign_server; SELECT * FROM pg_user_mapping; -- CREATE FOREIGN DATA WRAPPER CREATE FOREIGN DATA WRAPPER foo VALIDATOR bar; -- ERROR CREATE FOREIGN DATA WRAPPER foo; \dew CREATE FOREIGN DATA WRAPPER foo; -- duplicate DROP FOREIGN DATA WRAPPER foo; CREATE FOREIGN DATA WRAPPER foo OPTIONS ( testing '1' ); \dew+ DROP FOREIGN DATA WRAPPER foo; CREATE FOREIGN DATA WRAPPER foo OPTIONS ( testing '1', testing '2' ); -- ERROR CREATE FOREIGN DATA WRAPPER foo OPTIONS ( testing '1', another '2' ); \dew+ DROP FOREIGN DATA WRAPPER foo; SET ROLE regress_test_role; CREATE FOREIGN DATA WRAPPER foo; -- ERROR RESET ROLE; CREATE FOREIGN DATA WRAPPER foo VALIDATOR postgresql_fdw_validator; \dew+ -- HANDLER related checks CREATE FUNCTION invalid_fdw_handler () RETURNS int LANGUAGE SQL AS 'SELECT 1;' ; CREATE FOREIGN DATA WRAPPER test_fdw HANDLER invalid_fdw_handler; -- ERROR CREATE FOREIGN DATA WRAPPER test_fdw HANDLER test_fdw_handler HANDLER invalid_fdw_handler; -- ERROR CREATE FOREIGN DATA WRAPPER test_fdw HANDLER test_fdw_handler; DROP FOREIGN DATA WRAPPER test_fdw; -- ALTER FOREIGN DATA WRAPPER ALTER FOREIGN DATA WRAPPER foo; -- ERROR ALTER FOREIGN DATA WRAPPER foo VALIDATOR bar; -- ERROR ALTER FOREIGN DATA WRAPPER foo NO VALIDATOR; \dew+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '1', b '2'); ALTER FOREIGN DATA WRAPPER foo OPTIONS ( SET c '4'); -- ERROR ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP c); -- ERROR ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD x '1', DROP x); \dew+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4'); \dew+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '2'); ALTER FOREIGN DATA WRAPPER foo OPTIONS (b '4'); -- ERROR \dew+ SET ROLE regress_test_role; ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5'); -- ERROR SET ROLE regress_test_role_super; ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5'); \dew+ ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_role; -- ERROR ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_role_super; ALTER ROLE regress_test_role_super NOSUPERUSER; SET ROLE regress_test_role_super; ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD e '6'); -- ERROR RESET ROLE; \dew+ ALTER FOREIGN DATA WRAPPER foo RENAME TO foo1; \dew+ ALTER FOREIGN DATA WRAPPER foo1 RENAME TO foo; -- HANDLER related checks ALTER FOREIGN DATA WRAPPER foo HANDLER invalid_fdw_handler; -- ERROR ALTER FOREIGN DATA WRAPPER foo HANDLER test_fdw_handler HANDLER anything; -- ERROR ALTER FOREIGN DATA WRAPPER foo HANDLER test_fdw_handler; DROP FUNCTION invalid_fdw_handler (); -- DROP FOREIGN DATA WRAPPER DROP FOREIGN DATA WRAPPER nonexistent; -- ERROR DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent; \dew+ DROP ROLE regress_test_role_super; -- ERROR SET ROLE regress_test_role_super; DROP FOREIGN DATA WRAPPER foo; RESET ROLE; DROP ROLE regress_test_role_super; \dew+ CREATE FOREIGN DATA WRAPPER foo; CREATE SERVER s1 FOREIGN DATA WRAPPER foo; COMMENT ON SERVER s1 IS 'foreign server'; CREATE USER MAPPING FOR CURRENT_USER SERVER s1; CREATE USER MAPPING FOR CURRENT_USER SERVER s1; -- ERROR CREATE USER MAPPING IF NOT EXISTS FOR CURRENT_USER SERVER s1; -- NOTICE \dew+ \des+ \deu+ DROP FOREIGN DATA WRAPPER foo; -- ERROR SET ROLE regress_test_role; DROP FOREIGN DATA WRAPPER foo CASCADE; -- ERROR RESET ROLE; DROP FOREIGN DATA WRAPPER foo CASCADE; \dew+ \des+ \deu+ -- exercise CREATE SERVER CREATE SERVER s1 FOREIGN DATA WRAPPER foo; -- ERROR CREATE FOREIGN DATA WRAPPER foo OPTIONS ( "test wrapper" 'true' ); CREATE SERVER s1 FOREIGN DATA WRAPPER foo; CREATE SERVER s1 FOREIGN DATA WRAPPER foo; -- ERROR CREATE SERVER IF NOT EXISTS s1 FOREIGN DATA WRAPPER foo; -- No ERROR, just NOTICE CREATE SERVER s2 FOREIGN DATA WRAPPER foo OPTIONS ( host 'a', dbname 'b' ); CREATE SERVER s3 TYPE 'oracle' FOREIGN DATA WRAPPER foo; CREATE SERVER s4 TYPE 'oracle' FOREIGN DATA WRAPPER foo OPTIONS ( host 'a', dbname 'b' ); CREATE SERVER s5 VERSION '15.0' FOREIGN DATA WRAPPER foo; CREATE SERVER s6 VERSION '16.0' FOREIGN DATA WRAPPER foo OPTIONS ( host 'a', dbname 'b' ); CREATE SERVER s7 TYPE 'oracle' VERSION '17.0' FOREIGN DATA WRAPPER foo OPTIONS ( host 'a', dbname 'b' ); CREATE SERVER s8 FOREIGN DATA WRAPPER postgresql OPTIONS ( foo '1' ); -- ERROR CREATE SERVER s8 FOREIGN DATA WRAPPER postgresql OPTIONS ( host 'localhost', dbname 's8db' ); \des+ SET ROLE regress_test_role; CREATE SERVER t1 FOREIGN DATA WRAPPER foo; -- ERROR: no usage on FDW RESET ROLE; GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role; SET ROLE regress_test_role; CREATE SERVER t1 FOREIGN DATA WRAPPER foo; RESET ROLE; \des+ REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_test_role; GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_indirect; SET ROLE regress_test_role; CREATE SERVER t2 FOREIGN DATA WRAPPER foo; -- ERROR RESET ROLE; GRANT regress_test_indirect TO regress_test_role; SET ROLE regress_test_role; CREATE SERVER t2 FOREIGN DATA WRAPPER foo; \des+ RESET ROLE; REVOKE regress_test_indirect FROM regress_test_role; -- ALTER SERVER ALTER SERVER s0; -- ERROR ALTER SERVER s0 OPTIONS (a '1'); -- ERROR ALTER SERVER s1 VERSION '1.0' OPTIONS (servername 's1'); ALTER SERVER s2 VERSION '1.1'; ALTER SERVER s3 OPTIONS ("tns name" 'orcl', port '1521'); GRANT USAGE ON FOREIGN SERVER s1 TO regress_test_role; GRANT USAGE ON FOREIGN SERVER s6 TO regress_test_role2 WITH GRANT OPTION; \des+ SET ROLE regress_test_role; ALTER SERVER s1 VERSION '1.1'; -- ERROR ALTER SERVER s1 OWNER TO regress_test_role; -- ERROR RESET ROLE; ALTER SERVER s1 OWNER TO regress_test_role; GRANT regress_test_role2 TO regress_test_role; SET ROLE regress_test_role; ALTER SERVER s1 VERSION '1.1'; ALTER SERVER s1 OWNER TO regress_test_role2; -- ERROR RESET ROLE; ALTER SERVER s8 OPTIONS (foo '1'); -- ERROR option validation ALTER SERVER s8 OPTIONS (connect_timeout '30', SET dbname 'db1', DROP host); SET ROLE regress_test_role; ALTER SERVER s1 OWNER TO regress_test_indirect; -- ERROR RESET ROLE; GRANT regress_test_indirect TO regress_test_role; SET ROLE regress_test_role; ALTER SERVER s1 OWNER TO regress_test_indirect; RESET ROLE; GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_indirect; SET ROLE regress_test_role; ALTER SERVER s1 OWNER TO regress_test_indirect; RESET ROLE; DROP ROLE regress_test_indirect; -- ERROR \des+ ALTER SERVER s8 RENAME TO s8new; \des+ ALTER SERVER s8new RENAME TO s8; -- DROP SERVER DROP SERVER nonexistent; -- ERROR DROP SERVER IF EXISTS nonexistent; \des SET ROLE regress_test_role; DROP SERVER s2; -- ERROR DROP SERVER s1; RESET ROLE; \des ALTER SERVER s2 OWNER TO regress_test_role; SET ROLE regress_test_role; DROP SERVER s2; RESET ROLE; \des CREATE USER MAPPING FOR CURRENT_USER SERVER s3; \deu DROP SERVER s3; -- ERROR DROP SERVER s3 CASCADE; \des \deu -- CREATE USER MAPPING CREATE USER MAPPING FOR regress_test_missing_role SERVER s1; -- ERROR CREATE USER MAPPING FOR CURRENT_USER SERVER s1; -- ERROR CREATE USER MAPPING FOR CURRENT_USER SERVER s4; CREATE USER MAPPING FOR USER SERVER s4; -- ERROR duplicate CREATE USER MAPPING FOR public SERVER s4 OPTIONS ( "this mapping" 'is public' ); CREATE USER MAPPING FOR USER SERVER s8 OPTIONS ( username 'test', PASSWORD 'secret' ); -- ERROR CREATE USER MAPPING FOR USER SERVER s8 OPTIONS ( USER 'test', PASSWORD 'secret' ); ALTER SERVER s5 OWNER TO regress_test_role; ALTER SERVER s6 OWNER TO regress_test_indirect; SET ROLE regress_test_role; CREATE USER MAPPING FOR CURRENT_USER SERVER s5; CREATE USER MAPPING FOR CURRENT_USER SERVER s6 OPTIONS ( username 'test' ); CREATE USER MAPPING FOR CURRENT_USER SERVER s7; -- ERROR CREATE USER MAPPING FOR public SERVER s8; -- ERROR RESET ROLE; ALTER SERVER t1 OWNER TO regress_test_indirect; SET ROLE regress_test_role; CREATE USER MAPPING FOR CURRENT_USER SERVER t1 OPTIONS ( username 'bob', PASSWORD 'boo' ); CREATE USER MAPPING FOR public SERVER t1; RESET ROLE; \deu -- ALTER USER MAPPING ALTER USER MAPPING FOR regress_test_missing_role SERVER s4 OPTIONS (gotcha 'true'); -- ERROR ALTER USER MAPPING FOR USER SERVER ss4 OPTIONS (gotcha 'true'); -- ERROR ALTER USER MAPPING FOR public SERVER s5 OPTIONS (gotcha 'true'); -- ERROR ALTER USER MAPPING FOR CURRENT_USER SERVER s8 OPTIONS (username 'test'); -- ERROR ALTER USER MAPPING FOR CURRENT_USER SERVER s8 OPTIONS (DROP USER, SET PASSWORD 'public'); SET ROLE regress_test_role; ALTER USER MAPPING FOR CURRENT_USER SERVER s5 OPTIONS (ADD modified '1'); ALTER USER MAPPING FOR public SERVER s4 OPTIONS (ADD modified '1'); -- ERROR ALTER USER MAPPING FOR public SERVER t1 OPTIONS (ADD modified '1'); RESET ROLE; \deu+ -- DROP USER MAPPING DROP USER MAPPING FOR regress_test_missing_role SERVER s4; -- ERROR DROP USER MAPPING FOR USER SERVER ss4; DROP USER MAPPING FOR public SERVER s7; -- ERROR DROP USER MAPPING IF EXISTS FOR regress_test_missing_role SERVER s4; DROP USER MAPPING IF EXISTS FOR USER SERVER ss4; DROP USER MAPPING IF EXISTS FOR public SERVER s7; CREATE USER MAPPING FOR public SERVER s8; SET ROLE regress_test_role; DROP USER MAPPING FOR public SERVER s8; -- ERROR RESET ROLE; DROP SERVER s7; \deu -- CREATE FOREIGN TABLE CREATE SCHEMA foreign_schema; CREATE SERVER s0 FOREIGN DATA WRAPPER dummy; CREATE FOREIGN TABLE ft1 (); -- ERROR CREATE FOREIGN TABLE ft1 () SERVER no_server; -- ERROR CREATE FOREIGN TABLE ft1 ( c1 integer OPTIONS ("param 1" 'val1') PRIMARY KEY, c2 text OPTIONS (param2 'val2', param3 'val3'), c3 date) SERVER s0 OPTIONS ( DELIMITER ',', quote '"', "be quoted" 'value' ); -- ERROR CREATE TABLE ref_table ( id integer PRIMARY KEY ); CREATE FOREIGN TABLE ft1 ( c1 integer OPTIONS ("param 1" 'val1') REFERENCES ref_table (id), c2 text OPTIONS (param2 'val2', param3 'val3'), c3 date) SERVER s0 OPTIONS ( DELIMITER ',', quote '"', "be quoted" 'value' ); -- ERROR DROP TABLE ref_table; CREATE FOREIGN TABLE ft1 ( c1 integer OPTIONS ("param 1" 'val1') NOT NULL, c2 text OPTIONS (param2 'val2', param3 'val3'), c3 date, UNIQUE (c3)) SERVER s0 OPTIONS ( DELIMITER ',', quote '"', "be quoted" 'value' ); -- ERROR CREATE FOREIGN TABLE ft1 ( c1 integer OPTIONS ("param 1" 'val1') NOT NULL, c2 text OPTIONS (param2 'val2', param3 'val3') CHECK (c2 <> ''), c3 date, CHECK (c3 BETWEEN '1994-01-01'::date AND '1994-01-31'::date)) SERVER s0 OPTIONS ( DELIMITER ',', quote '"', "be quoted" 'value' ); COMMENT ON FOREIGN TABLE ft1 IS 'ft1'; COMMENT ON COLUMN ft1.c1 IS 'ft1.c1'; \d+ ft1 \det+ CREATE INDEX id_ft1_c2 ON ft1 (c2); -- ERROR SELECT * FROM ft1; -- ERROR EXPLAIN SELECT * FROM ft1; -- ERROR CREATE TABLE lt1 ( a int ) PARTITION BY RANGE (a); CREATE FOREIGN TABLE ft_part1 PARTITION OF lt1 FOR VALUES FROM (0) TO (1000) SERVER s0; CREATE INDEX ON lt1 (a); -- ERROR DROP TABLE lt1; -- ALTER FOREIGN TABLE COMMENT ON FOREIGN TABLE ft1 IS 'foreign table'; COMMENT ON FOREIGN TABLE ft1 IS NULL; COMMENT ON COLUMN ft1.c1 IS 'foreign column'; COMMENT ON COLUMN ft1.c1 IS NULL; ALTER FOREIGN TABLE ft1 ADD COLUMN c4 integer; ALTER FOREIGN TABLE ft1 ADD COLUMN c5 integer DEFAULT 0; ALTER FOREIGN TABLE ft1 ADD COLUMN c6 integer; ALTER FOREIGN TABLE ft1 ADD COLUMN c7 integer NOT NULL; ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer; ALTER FOREIGN TABLE ft1 ADD COLUMN c9 integer; ALTER FOREIGN TABLE ft1 ADD COLUMN c10 integer OPTIONS (p1 'v1'); ALTER FOREIGN TABLE ft1 ALTER COLUMN c4 SET DEFAULT 0; ALTER FOREIGN TABLE ft1 ALTER COLUMN c5 DROP DEFAULT; ALTER FOREIGN TABLE ft1 ALTER COLUMN c6 SET NOT NULL; ALTER FOREIGN TABLE ft1 ALTER COLUMN c7 DROP NOT NULL; ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10) USING '0'; -- ERROR ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10); ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE text; ALTER FOREIGN TABLE ft1 ALTER COLUMN xmin OPTIONS (ADD p1 'v1'); -- ERROR ALTER FOREIGN TABLE ft1 ALTER COLUMN c7 OPTIONS (ADD p1 'v1', ADD p2 'v2'), ALTER COLUMN c8 OPTIONS (ADD p1 'v1', ADD p2 'v2'); ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 OPTIONS ( SET p2 'V2', DROP p1); ALTER FOREIGN TABLE ft1 ALTER COLUMN c1 SET STATISTICS 10000; ALTER FOREIGN TABLE ft1 ALTER COLUMN c1 SET (n_distinct = 100); ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET STATISTICS - 1; ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET STORAGE PLAIN; \d+ ft1 -- can't change the column type if it's used elsewhere CREATE TABLE use_ft1_column_type ( x ft1 ); ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE integer; -- ERROR DROP TABLE use_ft1_column_type; ALTER FOREIGN TABLE ft1 ADD PRIMARY KEY (c7); -- ERROR ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0) NOT VALID; ALTER FOREIGN TABLE ft1 ALTER CONSTRAINT ft1_c9_check DEFERRABLE; -- ERROR ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c9_check; ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const; -- ERROR ALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const; ALTER FOREIGN TABLE ft1 OWNER TO regress_test_role; ALTER FOREIGN TABLE ft1 OPTIONS (DROP DELIMITER, SET quote '~', ADD escape '@'); ALTER FOREIGN TABLE ft1 DROP COLUMN no_column; -- ERROR ALTER FOREIGN TABLE ft1 DROP COLUMN IF EXISTS no_column; ALTER FOREIGN TABLE ft1 DROP COLUMN c9; ALTER FOREIGN TABLE ft1 SET SCHEMA foreign_schema; ALTER FOREIGN TABLE ft1 SET TABLESPACE ts; -- ERROR ALTER FOREIGN TABLE foreign_schema.ft1 RENAME c1 TO foreign_column_1; ALTER FOREIGN TABLE foreign_schema.ft1 RENAME TO foreign_table_1; \d foreign_schema.foreign_table_1 -- alter noexisting table ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c4 integer; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c6 integer; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c7 integer NOT NULL; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c8 integer; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c9 integer; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c10 integer OPTIONS (p1 'v1'); ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ALTER COLUMN c6 SET NOT NULL; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ALTER COLUMN c7 DROP NOT NULL; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ALTER COLUMN c8 TYPE char(10); ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ALTER COLUMN c8 SET DATA TYPE text; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ALTER COLUMN c7 OPTIONS (ADD p1 'v1', ADD p2 'v2'), ALTER COLUMN c8 OPTIONS (ADD p1 'v1', ADD p2 'v2'); ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ALTER COLUMN c8 OPTIONS ( SET p2 'V2', DROP p1); ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP CONSTRAINT IF EXISTS no_const; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP CONSTRAINT ft1_c1_check; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 OWNER TO regress_test_role; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 OPTIONS (DROP DELIMITER, SET quote '~', ADD escape '@'); ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP COLUMN IF EXISTS no_column; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP COLUMN c9; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 SET SCHEMA foreign_schema; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 RENAME c1 TO foreign_column_1; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 RENAME TO foreign_table_1; -- Information schema SELECT * FROM information_schema.foreign_data_wrappers ORDER BY 1, 2; SELECT * FROM information_schema.foreign_data_wrapper_options ORDER BY 1, 2, 3; SELECT * FROM information_schema.foreign_servers ORDER BY 1, 2; SELECT * FROM information_schema.foreign_server_options ORDER BY 1, 2, 3; SELECT * FROM information_schema.user_mappings ORDER BY lower(authorization_identifier), 2, 3; SELECT * FROM information_schema.user_mapping_options ORDER BY lower(authorization_identifier), 2, 3, 4; SELECT * FROM information_schema.usage_privileges WHERE object_type LIKE 'FOREIGN%' AND object_name IN ('s6', 'foo') ORDER BY 1, 2, 3, 4, 5; SELECT * FROM information_schema.role_usage_grants WHERE object_type LIKE 'FOREIGN%' AND object_name IN ('s6', 'foo') ORDER BY 1, 2, 3, 4, 5; SELECT * FROM information_schema.foreign_tables ORDER BY 1, 2, 3; SELECT * FROM information_schema.foreign_table_options ORDER BY 1, 2, 3, 4; SET ROLE regress_test_role; SELECT * FROM information_schema.user_mapping_options ORDER BY 1, 2, 3, 4; SELECT * FROM information_schema.usage_privileges WHERE object_type LIKE 'FOREIGN%' AND object_name IN ('s6', 'foo') ORDER BY 1, 2, 3, 4, 5; SELECT * FROM information_schema.role_usage_grants WHERE object_type LIKE 'FOREIGN%' AND object_name IN ('s6', 'foo') ORDER BY 1, 2, 3, 4, 5; DROP USER MAPPING FOR CURRENT_USER SERVER t1; SET ROLE regress_test_role2; SELECT * FROM information_schema.user_mapping_options ORDER BY 1, 2, 3, 4; RESET ROLE; -- has_foreign_data_wrapper_privilege SELECT has_foreign_data_wrapper_privilege('regress_test_role', ( SELECT oid FROM pg_foreign_data_wrapper WHERE fdwname = 'foo'), 'USAGE'); SELECT has_foreign_data_wrapper_privilege('regress_test_role', 'foo', 'USAGE'); SELECT has_foreign_data_wrapper_privilege(( SELECT oid FROM pg_roles WHERE rolname = 'regress_test_role'), ( SELECT oid FROM pg_foreign_data_wrapper WHERE fdwname = 'foo'), 'USAGE'); SELECT has_foreign_data_wrapper_privilege(( SELECT oid FROM pg_foreign_data_wrapper WHERE fdwname = 'foo'), 'USAGE'); SELECT has_foreign_data_wrapper_privilege(( SELECT oid FROM pg_roles WHERE rolname = 'regress_test_role'), 'foo', 'USAGE'); SELECT has_foreign_data_wrapper_privilege('foo', 'USAGE'); GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role; SELECT has_foreign_data_wrapper_privilege('regress_test_role', 'foo', 'USAGE'); -- has_server_privilege SELECT has_server_privilege('regress_test_role', ( SELECT oid FROM pg_foreign_server WHERE srvname = 's8'), 'USAGE'); SELECT has_server_privilege('regress_test_role', 's8', 'USAGE'); SELECT has_server_privilege(( SELECT oid FROM pg_roles WHERE rolname = 'regress_test_role'), ( SELECT oid FROM pg_foreign_server WHERE srvname = 's8'), 'USAGE'); SELECT has_server_privilege(( SELECT oid FROM pg_foreign_server WHERE srvname = 's8'), 'USAGE'); SELECT has_server_privilege(( SELECT oid FROM pg_roles WHERE rolname = 'regress_test_role'), 's8', 'USAGE'); SELECT has_server_privilege('s8', 'USAGE'); GRANT USAGE ON FOREIGN SERVER s8 TO regress_test_role; SELECT has_server_privilege('regress_test_role', 's8', 'USAGE'); REVOKE USAGE ON FOREIGN SERVER s8 FROM regress_test_role; GRANT USAGE ON FOREIGN SERVER s4 TO regress_test_role; DROP USER MAPPING FOR public SERVER s4; ALTER SERVER s6 OPTIONS (DROP host, DROP dbname); ALTER USER MAPPING FOR regress_test_role SERVER s6 OPTIONS (DROP username); ALTER FOREIGN DATA WRAPPER foo VALIDATOR postgresql_fdw_validator; -- Privileges SET ROLE regress_unprivileged_role; CREATE FOREIGN DATA WRAPPER foobar; -- ERROR ALTER FOREIGN DATA WRAPPER foo OPTIONS (gotcha 'true'); -- ERROR ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_unprivileged_role; -- ERROR DROP FOREIGN DATA WRAPPER foo; -- ERROR GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role; -- ERROR CREATE SERVER s9 FOREIGN DATA WRAPPER foo; -- ERROR ALTER SERVER s4 VERSION '0.5'; -- ERROR ALTER SERVER s4 OWNER TO regress_unprivileged_role; -- ERROR DROP SERVER s4; -- ERROR GRANT USAGE ON FOREIGN SERVER s4 TO regress_test_role; -- ERROR CREATE USER MAPPING FOR public SERVER s4; -- ERROR ALTER USER MAPPING FOR regress_test_role SERVER s6 OPTIONS (gotcha 'true'); -- ERROR DROP USER MAPPING FOR regress_test_role SERVER s6; -- ERROR RESET ROLE; GRANT USAGE ON FOREIGN DATA WRAPPER postgresql TO regress_unprivileged_role; GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_unprivileged_role WITH GRANT OPTION; SET ROLE regress_unprivileged_role; CREATE FOREIGN DATA WRAPPER foobar; -- ERROR ALTER FOREIGN DATA WRAPPER foo OPTIONS (gotcha 'true'); -- ERROR DROP FOREIGN DATA WRAPPER foo; -- ERROR GRANT USAGE ON FOREIGN DATA WRAPPER postgresql TO regress_test_role; -- WARNING GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role; CREATE SERVER s9 FOREIGN DATA WRAPPER postgresql; ALTER SERVER s6 VERSION '0.5'; -- ERROR DROP SERVER s6; -- ERROR GRANT USAGE ON FOREIGN SERVER s6 TO regress_test_role; -- ERROR GRANT USAGE ON FOREIGN SERVER s9 TO regress_test_role; CREATE USER MAPPING FOR public SERVER s6; -- ERROR CREATE USER MAPPING FOR public SERVER s9; ALTER USER MAPPING FOR regress_test_role SERVER s6 OPTIONS (gotcha 'true'); -- ERROR DROP USER MAPPING FOR regress_test_role SERVER s6; -- ERROR RESET ROLE; REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_unprivileged_role; -- ERROR REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_unprivileged_role CASCADE; SET ROLE regress_unprivileged_role; GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role; -- ERROR CREATE SERVER s10 FOREIGN DATA WRAPPER foo; -- ERROR ALTER SERVER s9 VERSION '1.1'; GRANT USAGE ON FOREIGN SERVER s9 TO regress_test_role; CREATE USER MAPPING FOR CURRENT_USER SERVER s9; DROP SERVER s9 CASCADE; RESET ROLE; CREATE SERVER s9 FOREIGN DATA WRAPPER foo; GRANT USAGE ON FOREIGN SERVER s9 TO regress_unprivileged_role; SET ROLE regress_unprivileged_role; ALTER SERVER s9 VERSION '1.2'; -- ERROR GRANT USAGE ON FOREIGN SERVER s9 TO regress_test_role; -- WARNING CREATE USER MAPPING FOR CURRENT_USER SERVER s9; DROP SERVER s9 CASCADE; -- ERROR -- Check visibility of user mapping data SET ROLE regress_test_role; CREATE SERVER s10 FOREIGN DATA WRAPPER foo; CREATE USER MAPPING FOR public SERVER s10 OPTIONS ( USER 'secret' ); CREATE USER MAPPING FOR regress_unprivileged_role SERVER s10 OPTIONS ( USER 'secret' ); -- owner of server can see some option fields \deu+ RESET ROLE; -- superuser can see all option fields \deu+ -- unprivileged user cannot see any option field SET ROLE regress_unprivileged_role; \deu+ RESET ROLE; DROP SERVER s10 CASCADE; -- Triggers CREATE FUNCTION dummy_trigger () RETURNS TRIGGER AS $$ BEGIN RETURN NULL; END $$ LANGUAGE plpgsql; CREATE TRIGGER trigtest_before_stmt BEFORE INSERT OR UPDATE OR DELETE ON foreign_schema.foreign_table_1 FOR EACH STATEMENT EXECUTE PROCEDURE dummy_trigger (); CREATE TRIGGER trigtest_after_stmt AFTER INSERT OR UPDATE OR DELETE ON foreign_schema.foreign_table_1 FOR EACH STATEMENT EXECUTE PROCEDURE dummy_trigger (); CREATE TRIGGER trigtest_after_stmt_tt AFTER INSERT OR UPDATE OR DELETE -- ERROR ON foreign_schema.foreign_table_1 REFERENCING NEW TABLE AS new_table FOR EACH STATEMENT EXECUTE PROCEDURE dummy_trigger (); CREATE TRIGGER trigtest_before_row BEFORE INSERT OR UPDATE OR DELETE ON foreign_schema.foreign_table_1 FOR EACH ROW EXECUTE PROCEDURE dummy_trigger (); CREATE TRIGGER trigtest_after_row AFTER INSERT OR UPDATE OR DELETE ON foreign_schema.foreign_table_1 FOR EACH ROW EXECUTE PROCEDURE dummy_trigger (); CREATE CONSTRAINT TRIGGER trigtest_constraint AFTER INSERT OR UPDATE OR DELETE ON foreign_schema.foreign_table_1 FOR EACH ROW EXECUTE PROCEDURE dummy_trigger (); ALTER FOREIGN TABLE foreign_schema.foreign_table_1 DISABLE TRIGGER trigtest_before_stmt; ALTER FOREIGN TABLE foreign_schema.foreign_table_1 ENABLE TRIGGER trigtest_before_stmt; DROP TRIGGER trigtest_before_stmt ON foreign_schema.foreign_table_1; DROP TRIGGER trigtest_before_row ON foreign_schema.foreign_table_1; DROP TRIGGER trigtest_after_stmt ON foreign_schema.foreign_table_1; DROP TRIGGER trigtest_after_row ON foreign_schema.foreign_table_1; DROP FUNCTION dummy_trigger (); -- Table inheritance CREATE TABLE fd_pt1 ( c1 integer NOT NULL, c2 text, c3 date ); CREATE FOREIGN TABLE ft2 () INHERITS ( fd_pt1) SERVER s0 OPTIONS ( DELIMITER ',', quote '"', "be quoted" 'value' ); \d+ fd_pt1 \d+ ft2 DROP FOREIGN TABLE ft2; \d+ fd_pt1 CREATE FOREIGN TABLE ft2 ( c1 integer NOT NULL, c2 text, c3 date) SERVER s0 OPTIONS ( DELIMITER ',', quote '"', "be quoted" 'value' ); \d+ ft2 ALTER FOREIGN TABLE ft2 INHERIT fd_pt1; \d+ fd_pt1 \d+ ft2 CREATE TABLE ct3 () INHERITS ( ft2 ); CREATE FOREIGN TABLE ft3 ( c1 integer NOT NULL, c2 text, c3 date ) INHERITS ( ft2) SERVER s0; \d+ ft2 \d+ ct3 \d+ ft3 -- add attributes recursively ALTER TABLE fd_pt1 ADD COLUMN c4 integer; ALTER TABLE fd_pt1 ADD COLUMN c5 integer DEFAULT 0; ALTER TABLE fd_pt1 ADD COLUMN c6 integer; ALTER TABLE fd_pt1 ADD COLUMN c7 integer NOT NULL; ALTER TABLE fd_pt1 ADD COLUMN c8 integer; \d+ fd_pt1 \d+ ft2 \d+ ct3 \d+ ft3 -- alter attributes recursively ALTER TABLE fd_pt1 ALTER COLUMN c4 SET DEFAULT 0; ALTER TABLE fd_pt1 ALTER COLUMN c5 DROP DEFAULT; ALTER TABLE fd_pt1 ALTER COLUMN c6 SET NOT NULL; ALTER TABLE fd_pt1 ALTER COLUMN c7 DROP NOT NULL; ALTER TABLE fd_pt1 ALTER COLUMN c8 TYPE char(10) USING '0'; -- ERROR ALTER TABLE fd_pt1 ALTER COLUMN c8 TYPE char(10); ALTER TABLE fd_pt1 ALTER COLUMN c8 SET DATA TYPE text; ALTER TABLE fd_pt1 ALTER COLUMN c1 SET STATISTICS 10000; ALTER TABLE fd_pt1 ALTER COLUMN c1 SET (n_distinct = 100); ALTER TABLE fd_pt1 ALTER COLUMN c8 SET STATISTICS - 1; ALTER TABLE fd_pt1 ALTER COLUMN c8 SET STORAGE EXTERNAL; \d+ fd_pt1 \d+ ft2 -- drop attributes recursively ALTER TABLE fd_pt1 DROP COLUMN c4; ALTER TABLE fd_pt1 DROP COLUMN c5; ALTER TABLE fd_pt1 DROP COLUMN c6; ALTER TABLE fd_pt1 DROP COLUMN c7; ALTER TABLE fd_pt1 DROP COLUMN c8; \d+ fd_pt1 \d+ ft2 -- add constraints recursively ALTER TABLE fd_pt1 ADD CONSTRAINT fd_pt1chk1 CHECK (c1 > 0) NO INHERIT; ALTER TABLE fd_pt1 ADD CONSTRAINT fd_pt1chk2 CHECK (c2 <> ''); -- connoinherit should be true for NO INHERIT constraint SELECT relname, conname, contype, conislocal, coninhcount, connoinherit FROM pg_class AS pc JOIN pg_constraint AS pgc ON (conrelid = pc.oid) WHERE pc.relname = 'fd_pt1' ORDER BY 1, 2; -- child does not inherit NO INHERIT constraints \d+ fd_pt1 \d+ ft2 DROP FOREIGN TABLE ft2; -- ERROR DROP FOREIGN TABLE ft2 CASCADE; CREATE FOREIGN TABLE ft2 ( c1 integer NOT NULL, c2 text, c3 date) SERVER s0 OPTIONS ( DELIMITER ',', quote '"', "be quoted" 'value' ); -- child must have parent's INHERIT constraints ALTER FOREIGN TABLE ft2 INHERIT fd_pt1; -- ERROR ALTER FOREIGN TABLE ft2 ADD CONSTRAINT fd_pt1chk2 CHECK (c2 <> ''); ALTER FOREIGN TABLE ft2 INHERIT fd_pt1; -- child does not inherit NO INHERIT constraints \d+ fd_pt1 \d+ ft2 -- drop constraints recursively ALTER TABLE fd_pt1 DROP CONSTRAINT fd_pt1chk1 CASCADE; ALTER TABLE fd_pt1 DROP CONSTRAINT fd_pt1chk2 CASCADE; -- NOT VALID case INSERT INTO fd_pt1 VALUES (1, 'fd_pt1'::text, '1994-01-01'::date); ALTER TABLE fd_pt1 ADD CONSTRAINT fd_pt1chk3 CHECK (c2 <> '') NOT VALID; \d+ fd_pt1 \d+ ft2 -- VALIDATE CONSTRAINT need do nothing on foreign tables ALTER TABLE fd_pt1 VALIDATE CONSTRAINT fd_pt1chk3; \d+ fd_pt1 \d+ ft2 -- changes name of an attribute recursively ALTER TABLE fd_pt1 RENAME COLUMN c1 TO f1; ALTER TABLE fd_pt1 RENAME COLUMN c2 TO f2; ALTER TABLE fd_pt1 RENAME COLUMN c3 TO f3; -- changes name of a constraint recursively ALTER TABLE fd_pt1 RENAME CONSTRAINT fd_pt1chk3 TO f2_check; \d+ fd_pt1 \d+ ft2 -- TRUNCATE doesn't work on foreign tables, either directly or recursively TRUNCATE ft2; -- ERROR TRUNCATE fd_pt1; -- ERROR DROP TABLE fd_pt1 CASCADE; -- IMPORT FOREIGN SCHEMA IMPORT FOREIGN SCHEMA s1 FROM SERVER s9 INTO public; -- ERROR IMPORT FOREIGN SCHEMA s1 LIMIT TO (t1) FROM SERVER s9 INTO public; --ERROR IMPORT FOREIGN SCHEMA s1 EXCEPT (t1) FROM SERVER s9 INTO public; -- ERROR IMPORT FOREIGN SCHEMA s1 EXCEPT (t1, t2) FROM SERVER s9 INTO public OPTIONS (option1 'value1', option2 'value2'); -- ERROR -- DROP FOREIGN TABLE DROP FOREIGN TABLE no_table; -- ERROR DROP FOREIGN TABLE IF EXISTS no_table; DROP FOREIGN TABLE foreign_schema.foreign_table_1; -- REASSIGN OWNED/DROP OWNED of foreign objects REASSIGN OWNED BY regress_test_role TO regress_test_role2; DROP OWNED BY regress_test_role2; DROP OWNED BY regress_test_role2 CASCADE; -- Foreign partition DDL stuff CREATE TABLE fd_pt2 ( c1 integer NOT NULL, c2 text, c3 date ) PARTITION BY LIST (c1); CREATE FOREIGN TABLE fd_pt2_1 PARTITION OF fd_pt2 FOR VALUES IN (1) SERVER s0 OPTIONS (DELIMITER ',', quote '"', "be quoted" 'value'); \d+ fd_pt2 \d+ fd_pt2_1 -- partition cannot have additional columns DROP FOREIGN TABLE fd_pt2_1; CREATE FOREIGN TABLE fd_pt2_1 ( c1 integer NOT NULL, c2 text, c3 date, c4 char) SERVER s0 OPTIONS ( DELIMITER ',', quote '"', "be quoted" 'value' ); \d+ fd_pt2_1 ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); -- ERROR DROP FOREIGN TABLE fd_pt2_1; \d+ fd_pt2 CREATE FOREIGN TABLE fd_pt2_1 ( c1 integer NOT NULL, c2 text, c3 date) SERVER s0 OPTIONS ( DELIMITER ',', quote '"', "be quoted" 'value' ); \d+ fd_pt2_1 -- no attach partition validation occurs for foreign tables ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); \d+ fd_pt2 \d+ fd_pt2_1 -- cannot add column to a partition ALTER TABLE fd_pt2_1 ADD c4 char; -- ok to have a partition's own constraints though ALTER TABLE fd_pt2_1 ALTER c3 SET NOT NULL; ALTER TABLE fd_pt2_1 ADD CONSTRAINT p21chk CHECK (c2 <> ''); \d+ fd_pt2 \d+ fd_pt2_1 -- cannot drop inherited NOT NULL constraint from a partition ALTER TABLE fd_pt2_1 ALTER c1 DROP NOT NULL; -- partition must have parent's constraints ALTER TABLE fd_pt2 DETACH PARTITION fd_pt2_1; ALTER TABLE fd_pt2 ALTER c2 SET NOT NULL; \d+ fd_pt2 \d+ fd_pt2_1 ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); -- ERROR ALTER FOREIGN TABLE fd_pt2_1 ALTER c2 SET NOT NULL; ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); ALTER TABLE fd_pt2 DETACH PARTITION fd_pt2_1; ALTER TABLE fd_pt2 ADD CONSTRAINT fd_pt2chk1 CHECK (c1 > 0); \d+ fd_pt2 \d+ fd_pt2_1 ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); -- ERROR ALTER FOREIGN TABLE fd_pt2_1 ADD CONSTRAINT fd_pt2chk1 CHECK (c1 > 0); ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); -- TRUNCATE doesn't work on foreign tables, either directly or recursively TRUNCATE fd_pt2_1; -- ERROR TRUNCATE fd_pt2; -- ERROR DROP FOREIGN TABLE fd_pt2_1; DROP TABLE fd_pt2; -- foreign table cannot be part of partition tree made of temporary -- relations. CREATE TEMP TABLE temp_parted ( a int ) PARTITION BY LIST (a); CREATE FOREIGN TABLE foreign_part PARTITION OF temp_parted DEFAULT SERVER s0; -- ERROR CREATE FOREIGN TABLE foreign_part ( a int) SERVER s0; ALTER TABLE temp_parted ATTACH PARTITION foreign_part DEFAULT; -- ERROR DROP FOREIGN TABLE foreign_part; DROP TABLE temp_parted; -- Cleanup DROP SCHEMA foreign_schema CASCADE; DROP ROLE regress_test_role; -- ERROR DROP SERVER t1 CASCADE; DROP USER MAPPING FOR regress_test_role SERVER s6; DROP FOREIGN DATA WRAPPER foo CASCADE; DROP SERVER s8 CASCADE; DROP ROLE regress_test_indirect; DROP ROLE regress_test_role; DROP ROLE regress_unprivileged_role; -- ERROR REVOKE ALL ON FOREIGN DATA WRAPPER postgresql FROM regress_unprivileged_role; DROP ROLE regress_unprivileged_role; DROP ROLE regress_test_role2; DROP FOREIGN DATA WRAPPER postgresql CASCADE; DROP FOREIGN DATA WRAPPER dummy CASCADE; \c DROP ROLE regress_foreign_data_user; -- At this point we should have no wrappers, no servers, and no mappings. SELECT fdwname, fdwhandler, fdwvalidator, fdwoptions FROM pg_foreign_data_wrapper; SELECT srvname, srvoptions FROM pg_foreign_server; SELECT * FROM pg_user_mapping; pgFormatter-4.2/t/pg-test-files/expected/foreign_key.sql000066400000000000000000001771021361326045100234330ustar00rootroot00000000000000-- -- FOREIGN KEY -- -- MATCH FULL -- -- First test, check and cascade -- CREATE TABLE PKTABLE ( ptest1 int PRIMARY KEY, ptest2 text ); CREATE TABLE FKTABLE ( ftest1 int REFERENCES PKTABLE MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int ); -- Insert test data into PKTABLE INSERT INTO PKTABLE VALUES (1, 'Test1'); INSERT INTO PKTABLE VALUES (2, 'Test2'); INSERT INTO PKTABLE VALUES (3, 'Test3'); INSERT INTO PKTABLE VALUES (4, 'Test4'); INSERT INTO PKTABLE VALUES (5, 'Test5'); -- Insert successful rows into FK TABLE INSERT INTO FKTABLE VALUES (1, 2); INSERT INTO FKTABLE VALUES (2, 3); INSERT INTO FKTABLE VALUES (3, 4); INSERT INTO FKTABLE VALUES (NULL, 1); -- Insert a failed row into FK TABLE INSERT INTO FKTABLE VALUES (100, 2); -- Check FKTABLE SELECT * FROM FKTABLE; -- Delete a row from PK TABLE DELETE FROM PKTABLE WHERE ptest1 = 1; -- Check FKTABLE for removal of matched row SELECT * FROM FKTABLE; -- Update a row from PK TABLE UPDATE PKTABLE SET ptest1 = 1 WHERE ptest1 = 2; -- Check FKTABLE for update of matched row SELECT * FROM FKTABLE; DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- -- check set NULL and table constraint on multiple columns -- CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 text, PRIMARY KEY (ptest1, ptest2) ); CREATE TABLE FKTABLE ( ftest1 int, ftest2 int, ftest3 int, CONSTRAINT constrname FOREIGN KEY (ftest1, ftest2) REFERENCES PKTABLE MATCH FULL ON DELETE SET NULL ON UPDATE SET NULL ); -- Test comments COMMENT ON CONSTRAINT constrname_wrong ON FKTABLE IS 'fk constraint comment'; COMMENT ON CONSTRAINT constrname ON FKTABLE IS 'fk constraint comment'; COMMENT ON CONSTRAINT constrname ON FKTABLE IS NULL; -- Insert test data into PKTABLE INSERT INTO PKTABLE VALUES (1, 2, 'Test1'); INSERT INTO PKTABLE VALUES (1, 3, 'Test1-2'); INSERT INTO PKTABLE VALUES (2, 4, 'Test2'); INSERT INTO PKTABLE VALUES (3, 6, 'Test3'); INSERT INTO PKTABLE VALUES (4, 8, 'Test4'); INSERT INTO PKTABLE VALUES (5, 10, 'Test5'); -- Insert successful rows into FK TABLE INSERT INTO FKTABLE VALUES (1, 2, 4); INSERT INTO FKTABLE VALUES (1, 3, 5); INSERT INTO FKTABLE VALUES (2, 4, 8); INSERT INTO FKTABLE VALUES (3, 6, 12); INSERT INTO FKTABLE VALUES (NULL, NULL, 0); -- Insert failed rows into FK TABLE INSERT INTO FKTABLE VALUES (100, 2, 4); INSERT INTO FKTABLE VALUES (2, 2, 4); INSERT INTO FKTABLE VALUES (NULL, 2, 4); INSERT INTO FKTABLE VALUES (1, NULL, 4); -- Check FKTABLE SELECT * FROM FKTABLE; -- Delete a row from PK TABLE DELETE FROM PKTABLE WHERE ptest1 = 1 AND ptest2 = 2; -- Check FKTABLE for removal of matched row SELECT * FROM FKTABLE; -- Delete another row from PK TABLE DELETE FROM PKTABLE WHERE ptest1 = 5 AND ptest2 = 10; -- Check FKTABLE (should be no change) SELECT * FROM FKTABLE; -- Update a row from PK TABLE UPDATE PKTABLE SET ptest1 = 1 WHERE ptest1 = 2; -- Check FKTABLE for update of matched row SELECT * FROM FKTABLE; -- Check update with part of key null UPDATE FKTABLE SET ftest1 = NULL WHERE ftest1 = 1; -- Check update with old and new key values equal UPDATE FKTABLE SET ftest1 = 1 WHERE ftest1 = 1; -- Try altering the column type where foreign keys are involved ALTER TABLE PKTABLE ALTER COLUMN ptest1 TYPE bigint; ALTER TABLE FKTABLE ALTER COLUMN ftest1 TYPE bigint; SELECT * FROM PKTABLE; SELECT * FROM FKTABLE; DROP TABLE PKTABLE CASCADE; DROP TABLE FKTABLE; -- -- check set default and table constraint on multiple columns -- CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 text, PRIMARY KEY (ptest1, ptest2) ); CREATE TABLE FKTABLE ( ftest1 int DEFAULT - 1, ftest2 int DEFAULT - 2, ftest3 int, CONSTRAINT constrname2 FOREIGN KEY (ftest1, ftest2) REFERENCES PKTABLE MATCH FULL ON DELETE SET DEFAULT ON UPDATE SET DEFAULT ); -- Insert a value in PKTABLE for default INSERT INTO PKTABLE VALUES (- 1, - 2, 'The Default!'); -- Insert test data into PKTABLE INSERT INTO PKTABLE VALUES (1, 2, 'Test1'); INSERT INTO PKTABLE VALUES (1, 3, 'Test1-2'); INSERT INTO PKTABLE VALUES (2, 4, 'Test2'); INSERT INTO PKTABLE VALUES (3, 6, 'Test3'); INSERT INTO PKTABLE VALUES (4, 8, 'Test4'); INSERT INTO PKTABLE VALUES (5, 10, 'Test5'); -- Insert successful rows into FK TABLE INSERT INTO FKTABLE VALUES (1, 2, 4); INSERT INTO FKTABLE VALUES (1, 3, 5); INSERT INTO FKTABLE VALUES (2, 4, 8); INSERT INTO FKTABLE VALUES (3, 6, 12); INSERT INTO FKTABLE VALUES (NULL, NULL, 0); -- Insert failed rows into FK TABLE INSERT INTO FKTABLE VALUES (100, 2, 4); INSERT INTO FKTABLE VALUES (2, 2, 4); INSERT INTO FKTABLE VALUES (NULL, 2, 4); INSERT INTO FKTABLE VALUES (1, NULL, 4); -- Check FKTABLE SELECT * FROM FKTABLE; -- Delete a row from PK TABLE DELETE FROM PKTABLE WHERE ptest1 = 1 AND ptest2 = 2; -- Check FKTABLE to check for removal SELECT * FROM FKTABLE; -- Delete another row from PK TABLE DELETE FROM PKTABLE WHERE ptest1 = 5 AND ptest2 = 10; -- Check FKTABLE (should be no change) SELECT * FROM FKTABLE; -- Update a row from PK TABLE UPDATE PKTABLE SET ptest1 = 1 WHERE ptest1 = 2; -- Check FKTABLE for update of matched row SELECT * FROM FKTABLE; -- this should fail for lack of CASCADE DROP TABLE PKTABLE; DROP TABLE PKTABLE CASCADE; DROP TABLE FKTABLE; -- -- First test, check with no on delete or on update -- CREATE TABLE PKTABLE ( ptest1 int PRIMARY KEY, ptest2 text ); CREATE TABLE FKTABLE ( ftest1 int REFERENCES PKTABLE MATCH FULL, ftest2 int ); -- Insert test data into PKTABLE INSERT INTO PKTABLE VALUES (1, 'Test1'); INSERT INTO PKTABLE VALUES (2, 'Test2'); INSERT INTO PKTABLE VALUES (3, 'Test3'); INSERT INTO PKTABLE VALUES (4, 'Test4'); INSERT INTO PKTABLE VALUES (5, 'Test5'); -- Insert successful rows into FK TABLE INSERT INTO FKTABLE VALUES (1, 2); INSERT INTO FKTABLE VALUES (2, 3); INSERT INTO FKTABLE VALUES (3, 4); INSERT INTO FKTABLE VALUES (NULL, 1); -- Insert a failed row into FK TABLE INSERT INTO FKTABLE VALUES (100, 2); -- Check FKTABLE SELECT * FROM FKTABLE; -- Check PKTABLE SELECT * FROM PKTABLE; -- Delete a row from PK TABLE (should fail) DELETE FROM PKTABLE WHERE ptest1 = 1; -- Delete a row from PK TABLE (should succeed) DELETE FROM PKTABLE WHERE ptest1 = 5; -- Check PKTABLE for deletes SELECT * FROM PKTABLE; -- Update a row from PK TABLE (should fail) UPDATE PKTABLE SET ptest1 = 0 WHERE ptest1 = 2; -- Update a row from PK TABLE (should succeed) UPDATE PKTABLE SET ptest1 = 0 WHERE ptest1 = 4; -- Check PKTABLE for updates SELECT * FROM PKTABLE; DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- -- Check initial check upon ALTER TABLE -- CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, PRIMARY KEY (ptest1, ptest2) ); CREATE TABLE FKTABLE ( ftest1 int, ftest2 int ); INSERT INTO PKTABLE VALUES (1, 2); INSERT INTO FKTABLE VALUES (1, NULL); ALTER TABLE FKTABLE ADD FOREIGN KEY (ftest1, ftest2) REFERENCES PKTABLE MATCH FULL; DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- MATCH SIMPLE -- Base test restricting update/delete CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY (ptest1, ptest2, ptest3) ); CREATE TABLE FKTABLE ( ftest1 int, ftest2 int, ftest3 int, ftest4 int, CONSTRAINT constrname3 FOREIGN KEY (ftest1, ftest2, ftest3) REFERENCES PKTABLE ); -- Insert Primary Key values INSERT INTO PKTABLE VALUES (1, 2, 3, 'test1'); INSERT INTO PKTABLE VALUES (1, 3, 3, 'test2'); INSERT INTO PKTABLE VALUES (2, 3, 4, 'test3'); INSERT INTO PKTABLE VALUES (2, 4, 5, 'test4'); -- Insert Foreign Key values INSERT INTO FKTABLE VALUES (1, 2, 3, 1); INSERT INTO FKTABLE VALUES (NULL, 2, 3, 2); INSERT INTO FKTABLE VALUES (2, NULL, 3, 3); INSERT INTO FKTABLE VALUES (NULL, 2, 7, 4); INSERT INTO FKTABLE VALUES (NULL, 3, 4, 5); -- Insert a failed values INSERT INTO FKTABLE VALUES (1, 2, 7, 6); -- Show FKTABLE SELECT * FROM FKTABLE; -- Try to update something that should fail UPDATE PKTABLE SET ptest2 = 5 WHERE ptest2 = 2; -- Try to update something that should succeed UPDATE PKTABLE SET ptest1 = 1 WHERE ptest2 = 3; -- Try to delete something that should fail DELETE FROM PKTABLE WHERE ptest1 = 1 AND ptest2 = 2 AND ptest3 = 3; -- Try to delete something that should work DELETE FROM PKTABLE WHERE ptest1 = 2; -- Show PKTABLE and FKTABLE SELECT * FROM PKTABLE; SELECT * FROM FKTABLE; DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- restrict with null values CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, UNIQUE (ptest1, ptest2, ptest3) ); CREATE TABLE FKTABLE ( ftest1 int, ftest2 int, ftest3 int, ftest4 int, CONSTRAINT constrname3 FOREIGN KEY (ftest1, ftest2, ftest3) REFERENCES PKTABLE (ptest1, ptest2, ptest3) ); INSERT INTO PKTABLE VALUES (1, 2, 3, 'test1'); INSERT INTO PKTABLE VALUES (1, 3, NULL, 'test2'); INSERT INTO PKTABLE VALUES (2, NULL, 4, 'test3'); INSERT INTO FKTABLE VALUES (1, 2, 3, 1); DELETE FROM PKTABLE WHERE ptest1 = 2; SELECT * FROM PKTABLE; SELECT * FROM FKTABLE; DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- cascade update/delete CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY (ptest1, ptest2, ptest3) ); CREATE TABLE FKTABLE ( ftest1 int, ftest2 int, ftest3 int, ftest4 int, CONSTRAINT constrname3 FOREIGN KEY (ftest1, ftest2, ftest3) REFERENCES PKTABLE ON DELETE CASCADE ON UPDATE CASCADE ); -- Insert Primary Key values INSERT INTO PKTABLE VALUES (1, 2, 3, 'test1'); INSERT INTO PKTABLE VALUES (1, 3, 3, 'test2'); INSERT INTO PKTABLE VALUES (2, 3, 4, 'test3'); INSERT INTO PKTABLE VALUES (2, 4, 5, 'test4'); -- Insert Foreign Key values INSERT INTO FKTABLE VALUES (1, 2, 3, 1); INSERT INTO FKTABLE VALUES (NULL, 2, 3, 2); INSERT INTO FKTABLE VALUES (2, NULL, 3, 3); INSERT INTO FKTABLE VALUES (NULL, 2, 7, 4); INSERT INTO FKTABLE VALUES (NULL, 3, 4, 5); -- Insert a failed values INSERT INTO FKTABLE VALUES (1, 2, 7, 6); -- Show FKTABLE SELECT * FROM FKTABLE; -- Try to update something that will cascade UPDATE PKTABLE SET ptest2 = 5 WHERE ptest2 = 2; -- Try to update something that should not cascade UPDATE PKTABLE SET ptest1 = 1 WHERE ptest2 = 3; -- Show PKTABLE and FKTABLE SELECT * FROM PKTABLE; SELECT * FROM FKTABLE; -- Try to delete something that should cascade DELETE FROM PKTABLE WHERE ptest1 = 1 AND ptest2 = 5 AND ptest3 = 3; -- Show PKTABLE and FKTABLE SELECT * FROM PKTABLE; SELECT * FROM FKTABLE; -- Try to delete something that should not have a cascade DELETE FROM PKTABLE WHERE ptest1 = 2; -- Show PKTABLE and FKTABLE SELECT * FROM PKTABLE; SELECT * FROM FKTABLE; DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- set null update / set default delete CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY (ptest1, ptest2, ptest3) ); CREATE TABLE FKTABLE ( ftest1 int DEFAULT 0, ftest2 int, ftest3 int, ftest4 int, CONSTRAINT constrname3 FOREIGN KEY (ftest1, ftest2, ftest3) REFERENCES PKTABLE ON DELETE SET DEFAULT ON UPDATE SET NULL ); -- Insert Primary Key values INSERT INTO PKTABLE VALUES (1, 2, 3, 'test1'); INSERT INTO PKTABLE VALUES (1, 3, 3, 'test2'); INSERT INTO PKTABLE VALUES (2, 3, 4, 'test3'); INSERT INTO PKTABLE VALUES (2, 4, 5, 'test4'); -- Insert Foreign Key values INSERT INTO FKTABLE VALUES (1, 2, 3, 1); INSERT INTO FKTABLE VALUES (2, 3, 4, 1); INSERT INTO FKTABLE VALUES (NULL, 2, 3, 2); INSERT INTO FKTABLE VALUES (2, NULL, 3, 3); INSERT INTO FKTABLE VALUES (NULL, 2, 7, 4); INSERT INTO FKTABLE VALUES (NULL, 3, 4, 5); -- Insert a failed values INSERT INTO FKTABLE VALUES (1, 2, 7, 6); -- Show FKTABLE SELECT * FROM FKTABLE; -- Try to update something that will set null UPDATE PKTABLE SET ptest2 = 5 WHERE ptest2 = 2; -- Try to update something that should not set null UPDATE PKTABLE SET ptest2 = 2 WHERE ptest2 = 3 AND ptest1 = 1; -- Show PKTABLE and FKTABLE SELECT * FROM PKTABLE; SELECT * FROM FKTABLE; -- Try to delete something that should set default DELETE FROM PKTABLE WHERE ptest1 = 2 AND ptest2 = 3 AND ptest3 = 4; -- Show PKTABLE and FKTABLE SELECT * FROM PKTABLE; SELECT * FROM FKTABLE; -- Try to delete something that should not set default DELETE FROM PKTABLE WHERE ptest2 = 5; -- Show PKTABLE and FKTABLE SELECT * FROM PKTABLE; SELECT * FROM FKTABLE; DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- set default update / set null delete CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY (ptest1, ptest2, ptest3) ); CREATE TABLE FKTABLE ( ftest1 int DEFAULT 0, ftest2 int DEFAULT - 1, ftest3 int DEFAULT - 2, ftest4 int, CONSTRAINT constrname3 FOREIGN KEY (ftest1, ftest2, ftest3) REFERENCES PKTABLE ON DELETE SET NULL ON UPDATE SET DEFAULT ); -- Insert Primary Key values INSERT INTO PKTABLE VALUES (1, 2, 3, 'test1'); INSERT INTO PKTABLE VALUES (1, 3, 3, 'test2'); INSERT INTO PKTABLE VALUES (2, 3, 4, 'test3'); INSERT INTO PKTABLE VALUES (2, 4, 5, 'test4'); INSERT INTO PKTABLE VALUES (2, - 1, 5, 'test5'); -- Insert Foreign Key values INSERT INTO FKTABLE VALUES (1, 2, 3, 1); INSERT INTO FKTABLE VALUES (2, 3, 4, 1); INSERT INTO FKTABLE VALUES (2, 4, 5, 1); INSERT INTO FKTABLE VALUES (NULL, 2, 3, 2); INSERT INTO FKTABLE VALUES (2, NULL, 3, 3); INSERT INTO FKTABLE VALUES (NULL, 2, 7, 4); INSERT INTO FKTABLE VALUES (NULL, 3, 4, 5); -- Insert a failed values INSERT INTO FKTABLE VALUES (1, 2, 7, 6); -- Show FKTABLE SELECT * FROM FKTABLE; -- Try to update something that will fail UPDATE PKTABLE SET ptest2 = 5 WHERE ptest2 = 2; -- Try to update something that will set default UPDATE PKTABLE SET ptest1 = 0, ptest2 = - 1, ptest3 = - 2 WHERE ptest2 = 2; UPDATE PKTABLE SET ptest2 = 10 WHERE ptest2 = 4; -- Try to update something that should not set default UPDATE PKTABLE SET ptest2 = 2 WHERE ptest2 = 3 AND ptest1 = 1; -- Show PKTABLE and FKTABLE SELECT * FROM PKTABLE; SELECT * FROM FKTABLE; -- Try to delete something that should set null DELETE FROM PKTABLE WHERE ptest1 = 2 AND ptest2 = 3 AND ptest3 = 4; -- Show PKTABLE and FKTABLE SELECT * FROM PKTABLE; SELECT * FROM FKTABLE; -- Try to delete something that should not set null DELETE FROM PKTABLE WHERE ptest2 = - 1 AND ptest3 = 5; -- Show PKTABLE and FKTABLE SELECT * FROM PKTABLE; SELECT * FROM FKTABLE; DROP TABLE FKTABLE; DROP TABLE PKTABLE; CREATE TABLE PKTABLE ( ptest1 int PRIMARY KEY ); CREATE TABLE FKTABLE_FAIL1 ( ftest1 int, CONSTRAINT fkfail1 FOREIGN KEY (ftest2) REFERENCES PKTABLE ); CREATE TABLE FKTABLE_FAIL2 ( ftest1 int, CONSTRAINT fkfail1 FOREIGN KEY (ftest1) REFERENCES PKTABLE (ptest2) ); DROP TABLE FKTABLE_FAIL1; DROP TABLE FKTABLE_FAIL2; DROP TABLE PKTABLE; -- Test for referencing column number smaller than referenced constraint CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, UNIQUE (ptest1, ptest2) ); CREATE TABLE FKTABLE_FAIL1 ( ftest1 int REFERENCES pktable (ptest1) ); DROP TABLE FKTABLE_FAIL1; DROP TABLE PKTABLE; -- -- Tests for mismatched types -- -- Basic one column, two table setup CREATE TABLE PKTABLE ( ptest1 int PRIMARY KEY ); INSERT INTO PKTABLE VALUES (42); -- This next should fail, because int=inet does not exist CREATE TABLE FKTABLE ( ftest1 inet REFERENCES pktable ); -- This should also fail for the same reason, but here we -- give the column name CREATE TABLE FKTABLE ( ftest1 inet REFERENCES pktable (ptest1) ); -- This should succeed, even though they are different types, -- because int=int8 exists and is a member of the integer opfamily CREATE TABLE FKTABLE ( ftest1 int8 REFERENCES pktable ); -- Check it actually works INSERT INTO FKTABLE VALUES (42); -- should succeed INSERT INTO FKTABLE VALUES (43); -- should fail UPDATE FKTABLE SET ftest1 = ftest1; -- should succeed UPDATE FKTABLE SET ftest1 = ftest1 + 1; -- should fail DROP TABLE FKTABLE; -- This should fail, because we'd have to cast numeric to int which is -- not an implicit coercion (or use numeric=numeric, but that's not part -- of the integer opfamily) CREATE TABLE FKTABLE ( ftest1 numeric REFERENCES pktable ); DROP TABLE PKTABLE; -- On the other hand, this should work because int implicitly promotes to -- numeric, and we allow promotion on the FK side CREATE TABLE PKTABLE ( ptest1 numeric PRIMARY KEY ); INSERT INTO PKTABLE VALUES (42); CREATE TABLE FKTABLE ( ftest1 int REFERENCES pktable ); -- Check it actually works INSERT INTO FKTABLE VALUES (42); -- should succeed INSERT INTO FKTABLE VALUES (43); -- should fail UPDATE FKTABLE SET ftest1 = ftest1; -- should succeed UPDATE FKTABLE SET ftest1 = ftest1 + 1; -- should fail DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- Two columns, two tables CREATE TABLE PKTABLE ( ptest1 int, ptest2 inet, PRIMARY KEY (ptest1, ptest2) ); -- This should fail, because we just chose really odd types CREATE TABLE FKTABLE ( ftest1 cidr, ftest2 timestamp, FOREIGN KEY (ftest1, ftest2) REFERENCES pktable ); -- Again, so should this... CREATE TABLE FKTABLE ( ftest1 cidr, ftest2 timestamp, FOREIGN KEY (ftest1, ftest2) REFERENCES pktable (ptest1, ptest2) ); -- This fails because we mixed up the column ordering CREATE TABLE FKTABLE ( ftest1 int, ftest2 inet, FOREIGN KEY (ftest2, ftest1) REFERENCES pktable ); -- As does this... CREATE TABLE FKTABLE ( ftest1 int, ftest2 inet, FOREIGN KEY (ftest2, ftest1) REFERENCES pktable (ptest1, ptest2) ); -- And again.. CREATE TABLE FKTABLE ( ftest1 int, ftest2 inet, FOREIGN KEY (ftest1, ftest2) REFERENCES pktable (ptest2, ptest1) ); -- This works... CREATE TABLE FKTABLE ( ftest1 int, ftest2 inet, FOREIGN KEY (ftest2, ftest1) REFERENCES pktable (ptest2, ptest1) ); DROP TABLE FKTABLE; -- As does this CREATE TABLE FKTABLE ( ftest1 int, ftest2 inet, FOREIGN KEY (ftest1, ftest2) REFERENCES pktable (ptest1, ptest2) ); DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- Two columns, same table -- Make sure this still works... CREATE TABLE PKTABLE ( ptest1 int, ptest2 inet, ptest3 int, ptest4 inet, PRIMARY KEY (ptest1, ptest2), FOREIGN KEY (ptest3, ptest4) REFERENCES pktable (ptest1, ptest2) ); DROP TABLE PKTABLE; -- And this, CREATE TABLE PKTABLE ( ptest1 int, ptest2 inet, ptest3 int, ptest4 inet, PRIMARY KEY (ptest1, ptest2), FOREIGN KEY (ptest3, ptest4) REFERENCES pktable ); DROP TABLE PKTABLE; -- This shouldn't (mixed up columns) CREATE TABLE PKTABLE ( ptest1 int, ptest2 inet, ptest3 int, ptest4 inet, PRIMARY KEY (ptest1, ptest2), FOREIGN KEY (ptest3, ptest4) REFERENCES pktable (ptest2, ptest1) ); -- Nor should this... (same reason, we have 4,3 referencing 1,2 which mismatches types CREATE TABLE PKTABLE ( ptest1 int, ptest2 inet, ptest3 int, ptest4 inet, PRIMARY KEY (ptest1, ptest2), FOREIGN KEY (ptest4, ptest3) REFERENCES pktable (ptest1, ptest2) ); -- Not this one either... Same as the last one except we didn't defined the columns being referenced. CREATE TABLE PKTABLE ( ptest1 int, ptest2 inet, ptest3 int, ptest4 inet, PRIMARY KEY (ptest1, ptest2), FOREIGN KEY (ptest4, ptest3) REFERENCES pktable ); -- -- Now some cases with inheritance -- Basic 2 table case: 1 column of matching types. CREATE TABLE pktable_base ( base1 int NOT NULL ); CREATE TABLE pktable ( ptest1 int, PRIMARY KEY (base1), UNIQUE (base1, ptest1) ) INHERITS ( pktable_base ); CREATE TABLE fktable ( ftest1 int REFERENCES pktable (base1) ); -- now some ins, upd, del INSERT INTO pktable (base1) VALUES (1); INSERT INTO pktable (base1) VALUES (2); -- let's insert a non-existent fktable value INSERT INTO fktable (ftest1) VALUES (3); -- let's make a valid row for that INSERT INTO pktable (base1) VALUES (3); INSERT INTO fktable (ftest1) VALUES (3); -- let's try removing a row that should fail from pktable DELETE FROM pktable WHERE base1 > 2; -- okay, let's try updating all of the base1 values to *4 -- which should fail. UPDATE pktable SET base1 = base1 * 4; -- okay, let's try an update that should work. UPDATE pktable SET base1 = base1 * 4 WHERE base1 < 3; -- and a delete that should work DELETE FROM pktable WHERE base1 > 3; -- cleanup DROP TABLE fktable; DELETE FROM pktable; -- Now 2 columns 2 tables, matching types CREATE TABLE fktable ( ftest1 int, ftest2 int, FOREIGN KEY (ftest1, ftest2) REFERENCES pktable (base1, ptest1) ); -- now some ins, upd, del INSERT INTO pktable (base1, ptest1) VALUES (1, 1); INSERT INTO pktable (base1, ptest1) VALUES (2, 2); -- let's insert a non-existent fktable value INSERT INTO fktable (ftest1, ftest2) VALUES (3, 1); -- let's make a valid row for that INSERT INTO pktable (base1, ptest1) VALUES (3, 1); INSERT INTO fktable (ftest1, ftest2) VALUES (3, 1); -- let's try removing a row that should fail from pktable DELETE FROM pktable WHERE base1 > 2; -- okay, let's try updating all of the base1 values to *4 -- which should fail. UPDATE pktable SET base1 = base1 * 4; -- okay, let's try an update that should work. UPDATE pktable SET base1 = base1 * 4 WHERE base1 < 3; -- and a delete that should work DELETE FROM pktable WHERE base1 > 3; -- cleanup DROP TABLE fktable; DROP TABLE pktable; DROP TABLE pktable_base; -- Now we'll do one all in 1 table with 2 columns of matching types CREATE TABLE pktable_base ( base1 int NOT NULL, base2 int ); CREATE TABLE pktable ( ptest1 int, ptest2 int, PRIMARY KEY (base1, ptest1), FOREIGN KEY (base2, ptest2) REFERENCES pktable (base1, ptest1) ) INHERITS ( pktable_base ); INSERT INTO pktable (base1, ptest1, base2, ptest2) VALUES (1, 1, 1, 1); INSERT INTO pktable (base1, ptest1, base2, ptest2) VALUES (2, 1, 1, 1); INSERT INTO pktable (base1, ptest1, base2, ptest2) VALUES (2, 2, 2, 1); INSERT INTO pktable (base1, ptest1, base2, ptest2) VALUES (1, 3, 2, 2); -- fails (3,2) isn't in base1, ptest1 INSERT INTO pktable (base1, ptest1, base2, ptest2) VALUES (2, 3, 3, 2); -- fails (2,2) is being referenced DELETE FROM pktable WHERE base1 = 2; -- fails (1,1) is being referenced (twice) UPDATE pktable SET base1 = 3 WHERE base1 = 1; -- this sequence of two deletes will work, since after the first there will be no (2,*) references DELETE FROM pktable WHERE base2 = 2; DELETE FROM pktable WHERE base1 = 2; DROP TABLE pktable; DROP TABLE pktable_base; -- 2 columns (2 tables), mismatched types CREATE TABLE pktable_base ( base1 int NOT NULL ); CREATE TABLE pktable ( ptest1 inet, PRIMARY KEY (base1, ptest1) ) INHERITS ( pktable_base ); -- just generally bad types (with and without column references on the referenced table) CREATE TABLE fktable ( ftest1 cidr, ftest2 int[], FOREIGN KEY (ftest1, ftest2) REFERENCES pktable ); CREATE TABLE fktable ( ftest1 cidr, ftest2 int[], FOREIGN KEY (ftest1, ftest2) REFERENCES pktable (base1, ptest1) ); -- let's mix up which columns reference which CREATE TABLE fktable ( ftest1 int, ftest2 inet, FOREIGN KEY (ftest2, ftest1) REFERENCES pktable ); CREATE TABLE fktable ( ftest1 int, ftest2 inet, FOREIGN KEY (ftest2, ftest1) REFERENCES pktable (base1, ptest1) ); CREATE TABLE fktable ( ftest1 int, ftest2 inet, FOREIGN KEY (ftest1, ftest2) REFERENCES pktable (ptest1, base1) ); DROP TABLE pktable; DROP TABLE pktable_base; -- 2 columns (1 table), mismatched types CREATE TABLE pktable_base ( base1 int NOT NULL, base2 int ); CREATE TABLE pktable ( ptest1 inet, ptest2 inet[], PRIMARY KEY (base1, ptest1), FOREIGN KEY (base2, ptest2) REFERENCES pktable (base1, ptest1) ) INHERITS ( pktable_base ); CREATE TABLE pktable ( ptest1 inet, ptest2 inet, PRIMARY KEY (base1, ptest1), FOREIGN KEY (base2, ptest2) REFERENCES pktable (ptest1, base1) ) INHERITS ( pktable_base ); CREATE TABLE pktable ( ptest1 inet, ptest2 inet, PRIMARY KEY (base1, ptest1), FOREIGN KEY (ptest2, base2) REFERENCES pktable (base1, ptest1) ) INHERITS ( pktable_base ); CREATE TABLE pktable ( ptest1 inet, ptest2 inet, PRIMARY KEY (base1, ptest1), FOREIGN KEY (ptest2, base2) REFERENCES pktable (base1, ptest1) ) INHERITS ( pktable_base ); DROP TABLE pktable; DROP TABLE pktable_base; -- -- Deferrable constraints -- -- deferrable, explicitly deferred CREATE TABLE pktable ( id int4 PRIMARY KEY, other int4 ); CREATE TABLE fktable ( id int4 PRIMARY KEY, fk int4 REFERENCES pktable DEFERRABLE ); -- default to immediate: should fail INSERT INTO fktable VALUES (5, 10); -- explicitly defer the constraint BEGIN; SET CONSTRAINTS ALL DEFERRED; INSERT INTO fktable VALUES (10, 15); INSERT INTO pktable VALUES (15, 0); -- make the FK insert valid COMMIT; DROP TABLE fktable, pktable; -- deferrable, initially deferred CREATE TABLE pktable ( id int4 PRIMARY KEY, other int4 ); CREATE TABLE fktable ( id int4 PRIMARY KEY, fk int4 REFERENCES pktable DEFERRABLE INITIALLY DEFERRED ); -- default to deferred, should succeed BEGIN; INSERT INTO fktable VALUES (100, 200); INSERT INTO pktable VALUES (200, 500); -- make the FK insert valid COMMIT; -- default to deferred, explicitly make immediate BEGIN; SET CONSTRAINTS ALL IMMEDIATE; -- should fail INSERT INTO fktable VALUES (500, 1000); COMMIT; DROP TABLE fktable, pktable; -- tricky behavior: according to SQL99, if a deferred constraint is set -- to 'immediate' mode, it should be checked for validity *immediately*, -- not when the current transaction commits (i.e. the mode change applies -- retroactively) CREATE TABLE pktable ( id int4 PRIMARY KEY, other int4 ); CREATE TABLE fktable ( id int4 PRIMARY KEY, fk int4 REFERENCES pktable DEFERRABLE ); BEGIN; SET CONSTRAINTS ALL DEFERRED; -- should succeed, for now INSERT INTO fktable VALUES (1000, 2000); -- should cause transaction abort, due to preceding error SET CONSTRAINTS ALL IMMEDIATE; INSERT INTO pktable VALUES (2000, 3); -- too late COMMIT; DROP TABLE fktable, pktable; -- deferrable, initially deferred CREATE TABLE pktable ( id int4 PRIMARY KEY, other int4 ); CREATE TABLE fktable ( id int4 PRIMARY KEY, fk int4 REFERENCES pktable DEFERRABLE INITIALLY DEFERRED ); BEGIN; -- no error here INSERT INTO fktable VALUES (100, 200); -- error here on commit COMMIT; DROP TABLE pktable, fktable; -- test notice about expensive referential integrity checks, -- where the index cannot be used because of type incompatibilities. CREATE TEMP TABLE pktable ( id1 int4 PRIMARY KEY, id2 varchar(4) UNIQUE, id3 real UNIQUE, UNIQUE (id1, id2, id3) ); CREATE TEMP TABLE fktable ( x1 int4 REFERENCES pktable (id1), x2 varchar(4) REFERENCES pktable (id2), x3 real REFERENCES pktable (id3), x4 text, x5 int2 ); -- check individual constraints with alter table. -- should fail -- varchar does not promote to real ALTER TABLE fktable ADD CONSTRAINT fk_2_3 FOREIGN KEY (x2) REFERENCES pktable (id3); -- nor to int4 ALTER TABLE fktable ADD CONSTRAINT fk_2_1 FOREIGN KEY (x2) REFERENCES pktable (id1); -- real does not promote to int4 ALTER TABLE fktable ADD CONSTRAINT fk_3_1 FOREIGN KEY (x3) REFERENCES pktable (id1); -- int4 does not promote to text ALTER TABLE fktable ADD CONSTRAINT fk_1_2 FOREIGN KEY (x1) REFERENCES pktable (id2); -- should succeed -- int4 promotes to real ALTER TABLE fktable ADD CONSTRAINT fk_1_3 FOREIGN KEY (x1) REFERENCES pktable (id3); -- text is compatible with varchar ALTER TABLE fktable ADD CONSTRAINT fk_4_2 FOREIGN KEY (x4) REFERENCES pktable (id2); -- int2 is part of integer opfamily as of 8.0 ALTER TABLE fktable ADD CONSTRAINT fk_5_1 FOREIGN KEY (x5) REFERENCES pktable (id1); -- check multikey cases, especially out-of-order column lists -- these should work ALTER TABLE fktable ADD CONSTRAINT fk_123_123 FOREIGN KEY (x1, x2, x3) REFERENCES pktable (id1, id2, id3); ALTER TABLE fktable ADD CONSTRAINT fk_213_213 FOREIGN KEY (x2, x1, x3) REFERENCES pktable (id2, id1, id3); ALTER TABLE fktable ADD CONSTRAINT fk_253_213 FOREIGN KEY (x2, x5, x3) REFERENCES pktable (id2, id1, id3); -- these should fail ALTER TABLE fktable ADD CONSTRAINT fk_123_231 FOREIGN KEY (x1, x2, x3) REFERENCES pktable (id2, id3, id1); ALTER TABLE fktable ADD CONSTRAINT fk_241_132 FOREIGN KEY (x2, x4, x1) REFERENCES pktable (id1, id3, id2); DROP TABLE pktable, fktable; -- test a tricky case: we can elide firing the FK check trigger during -- an UPDATE if the UPDATE did not change the foreign key -- field. However, we can't do this if our transaction was the one that -- created the updated row and the trigger is deferred, since our UPDATE -- will have invalidated the original newly-inserted tuple, and therefore -- cause the on-INSERT RI trigger not to be fired. CREATE TEMP TABLE pktable ( id int PRIMARY KEY, other int ); CREATE TEMP TABLE fktable ( id int PRIMARY KEY, fk int REFERENCES pktable DEFERRABLE INITIALLY DEFERRED ); INSERT INTO pktable VALUES (5, 10); BEGIN; -- doesn't match PK, but no error yet INSERT INTO fktable VALUES (0, 20); -- don't change FK UPDATE fktable SET id = id + 1; -- should catch error from initial INSERT COMMIT; -- check same case when insert is in a different subtransaction than update BEGIN; -- doesn't match PK, but no error yet INSERT INTO fktable VALUES (0, 20); -- UPDATE will be in a subxact SAVEPOINT savept1; -- don't change FK UPDATE fktable SET id = id + 1; -- should catch error from initial INSERT COMMIT; BEGIN; -- INSERT will be in a subxact SAVEPOINT savept1; -- doesn't match PK, but no error yet INSERT INTO fktable VALUES (0, 20); RELEASE SAVEPOINT savept1; -- don't change FK UPDATE fktable SET id = id + 1; -- should catch error from initial INSERT COMMIT; BEGIN; -- doesn't match PK, but no error yet INSERT INTO fktable VALUES (0, 20); -- UPDATE will be in a subxact SAVEPOINT savept1; -- don't change FK UPDATE fktable SET id = id + 1; -- Roll back the UPDATE ROLLBACK TO savept1; -- should catch error from initial INSERT COMMIT; -- -- check ALTER CONSTRAINT -- INSERT INTO fktable VALUES (1, 5); ALTER TABLE fktable ALTER CONSTRAINT fktable_fk_fkey DEFERRABLE INITIALLY IMMEDIATE; BEGIN; -- doesn't match FK, should throw error now UPDATE pktable SET id = 10 WHERE id = 5; COMMIT; BEGIN; -- doesn't match PK, should throw error now INSERT INTO fktable VALUES (0, 20); COMMIT; -- try additional syntax ALTER TABLE fktable ALTER CONSTRAINT fktable_fk_fkey NOT DEFERRABLE; -- illegal option ALTER TABLE fktable ALTER CONSTRAINT fktable_fk_fkey NOT DEFERRABLE INITIALLY DEFERRED; -- test order of firing of FK triggers when several RI-induced changes need to -- be made to the same row. This was broken by subtransaction-related -- changes in 8.0. CREATE TEMP TABLE users ( id int PRIMARY KEY, name varchar NOT NULL ); INSERT INTO users VALUES (1, 'Jozko'); INSERT INTO users VALUES (2, 'Ferko'); INSERT INTO users VALUES (3, 'Samko'); CREATE TEMP TABLE tasks ( id int PRIMARY KEY, owner INT REFERENCES users ON UPDATE CASCADE ON DELETE SET NULL, worker int REFERENCES users ON UPDATE CASCADE ON DELETE SET NULL, checked_by int REFERENCES users ON UPDATE CASCADE ON DELETE SET NULL ); INSERT INTO tasks VALUES (1, 1, NULL, NULL); INSERT INTO tasks VALUES (2, 2, 2, NULL); INSERT INTO tasks VALUES (3, 3, 3, 3); SELECT * FROM tasks; UPDATE users SET id = 4 WHERE id = 3; SELECT * FROM tasks; DELETE FROM users WHERE id = 4; SELECT * FROM tasks; -- could fail with only 2 changes to make, if row was already updated BEGIN; UPDATE tasks SET id = id WHERE id = 2; SELECT * FROM tasks; DELETE FROM users WHERE id = 2; SELECT * FROM tasks; COMMIT; -- -- Test self-referential FK with CASCADE (bug #6268) -- CREATE temp TABLE selfref ( a int PRIMARY KEY, b int, FOREIGN KEY (b) REFERENCES selfref (a) ON UPDATE CASCADE ON DELETE CASCADE ); INSERT INTO selfref (a, b) VALUES (0, 0), (1, 1); BEGIN; UPDATE selfref SET a = 123 WHERE a = 0; SELECT a, b FROM selfref; UPDATE selfref SET a = 456 WHERE a = 123; SELECT a, b FROM selfref; COMMIT; -- -- Test that SET DEFAULT actions recognize updates to default values -- CREATE temp TABLE defp ( f1 int PRIMARY KEY ); CREATE temp TABLE defc ( f1 int DEFAULT 0 REFERENCES defp ON DELETE SET DEFAULT ); INSERT INTO defp VALUES (0), (1), (2); INSERT INTO defc VALUES (2); SELECT * FROM defc; DELETE FROM defp WHERE f1 = 2; SELECT * FROM defc; DELETE FROM defp WHERE f1 = 0; -- fail ALTER TABLE defc ALTER COLUMN f1 SET DEFAULT 1; DELETE FROM defp WHERE f1 = 0; SELECT * FROM defc; DELETE FROM defp WHERE f1 = 1; -- fail -- -- Test the difference between NO ACTION and RESTRICT -- CREATE temp TABLE pp ( f1 int PRIMARY KEY ); CREATE temp TABLE cc ( f1 int REFERENCES pp ON UPDATE NO action ON DELETE NO action ); INSERT INTO pp VALUES (12); INSERT INTO pp VALUES (11); UPDATE pp SET f1 = f1 + 1; INSERT INTO cc VALUES (13); UPDATE pp SET f1 = f1 + 1; UPDATE pp SET f1 = f1 + 1; -- fail DELETE FROM pp WHERE f1 = 13; -- fail DROP TABLE pp, cc; CREATE temp TABLE pp ( f1 int PRIMARY KEY ); CREATE temp TABLE cc ( f1 int REFERENCES pp ON UPDATE RESTRICT ON DELETE RESTRICT ); INSERT INTO pp VALUES (12); INSERT INTO pp VALUES (11); UPDATE pp SET f1 = f1 + 1; INSERT INTO cc VALUES (13); UPDATE pp SET f1 = f1 + 1; -- fail DELETE FROM pp WHERE f1 = 13; -- fail DROP TABLE pp, cc; -- -- Test interaction of foreign-key optimization with rules (bug #14219) -- CREATE temp TABLE t1 ( a integer PRIMARY KEY, b text ); CREATE temp TABLE t2 ( a integer PRIMARY KEY, b integer REFERENCES t1 ); CREATE RULE r1 AS ON DELETE TO t1 DO DELETE FROM t2 WHERE t2.b = old.a; EXPLAIN ( COSTS OFF ) DELETE FROM t1 WHERE a = 1; DELETE FROM t1 WHERE a = 1; -- Test a primary key with attributes located in later attnum positions -- compared to the fk attributes. CREATE TABLE pktable2 ( a int, b int, c int, d int, e int, PRIMARY KEY (d, e) ); CREATE TABLE fktable2 ( d int, e int, FOREIGN KEY (d, e) REFERENCES pktable2 ); INSERT INTO pktable2 VALUES (1, 2, 3, 4, 5); INSERT INTO fktable2 VALUES (4, 5); DELETE FROM pktable2; UPDATE pktable2 SET d = 5; DROP TABLE pktable2, fktable2; -- Test truncation of long foreign key names CREATE TABLE pktable1 ( a int PRIMARY KEY ); CREATE TABLE pktable2 ( a int, b int, PRIMARY KEY (a, b) ); CREATE TABLE fktable2 ( a int, b int, very_very_long_column_name_to_exceed_63_characters int, FOREIGN KEY (very_very_long_column_name_to_exceed_63_characters) REFERENCES pktable1, FOREIGN KEY (a, very_very_long_column_name_to_exceed_63_characters) REFERENCES pktable2, FOREIGN KEY (a, very_very_long_column_name_to_exceed_63_characters) REFERENCES pktable2 ); SELECT conname FROM pg_constraint WHERE conrelid = 'fktable2'::regclass ORDER BY conname; DROP TABLE pktable1, pktable2, fktable2; -- -- Test deferred FK check on a tuple deleted by a rolled-back subtransaction -- CREATE TABLE pktable2 ( f1 int PRIMARY KEY ); CREATE TABLE fktable2 ( f1 int REFERENCES pktable2 DEFERRABLE INITIALLY DEFERRED ); INSERT INTO pktable2 VALUES (1); BEGIN; INSERT INTO fktable2 VALUES (1); SAVEPOINT x; DELETE FROM fktable2; ROLLBACK TO x; COMMIT; BEGIN; INSERT INTO fktable2 VALUES (2); SAVEPOINT x; DELETE FROM fktable2; ROLLBACK TO x; COMMIT; -- fail -- -- Test that we prevent dropping FK constraint with pending trigger events -- BEGIN; INSERT INTO fktable2 VALUES (2); ALTER TABLE fktable2 DROP CONSTRAINT fktable2_f1_fkey; COMMIT; BEGIN; DELETE FROM pktable2 WHERE f1 = 1; ALTER TABLE fktable2 DROP CONSTRAINT fktable2_f1_fkey; COMMIT; DROP TABLE pktable2, fktable2; -- -- Test keys that "look" different but compare as equal -- CREATE TABLE pktable2 ( a float8, b float8, PRIMARY KEY (a, b) ); CREATE TABLE fktable2 ( x float8, y float8, FOREIGN KEY (x, y) REFERENCES pktable2 (a, b) ON UPDATE CASCADE ); INSERT INTO pktable2 VALUES ('-0', '-0'); INSERT INTO fktable2 VALUES ('-0', '-0'); SELECT * FROM pktable2; SELECT * FROM fktable2; UPDATE pktable2 SET a = '0' WHERE a = '-0'; SELECT * FROM pktable2; -- should have updated fktable2.x SELECT * FROM fktable2; DROP TABLE pktable2, fktable2; -- -- Foreign keys and partitioned tables -- -- Creation of a partitioned hierarchy with irregular definitions CREATE TABLE fk_notpartitioned_pk ( fdrop1 int, a int, fdrop2 int, b int, PRIMARY KEY (a, b) ); ALTER TABLE fk_notpartitioned_pk DROP COLUMN fdrop1, DROP COLUMN fdrop2; CREATE TABLE fk_partitioned_fk ( b int, fdrop1 int, a int ) PARTITION BY RANGE (a, b); ALTER TABLE fk_partitioned_fk DROP COLUMN fdrop1; CREATE TABLE fk_partitioned_fk_1 ( fdrop1 int, fdrop2 int, a int, fdrop3 int, b int ); ALTER TABLE fk_partitioned_fk_1 DROP COLUMN fdrop1, DROP COLUMN fdrop2, DROP COLUMN fdrop3; ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_1 FOR VALUES FROM (0, 0) TO (1000, 1000); ALTER TABLE fk_partitioned_fk ADD FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk; CREATE TABLE fk_partitioned_fk_2 ( b int, fdrop1 int, fdrop2 int, a int ); ALTER TABLE fk_partitioned_fk_2 DROP COLUMN fdrop1, DROP COLUMN fdrop2; ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_2 FOR VALUES FROM (1000, 1000) TO (2000, 2000); CREATE TABLE fk_partitioned_fk_3 ( fdrop1 int, fdrop2 int, fdrop3 int, fdrop4 int, b int, a int ) PARTITION BY HASH (a); ALTER TABLE fk_partitioned_fk_3 DROP COLUMN fdrop1, DROP COLUMN fdrop2, DROP COLUMN fdrop3, DROP COLUMN fdrop4; CREATE TABLE fk_partitioned_fk_3_0 PARTITION OF fk_partitioned_fk_3 FOR VALUES WITH (MODULUS 5, REMAINDER 0); CREATE TABLE fk_partitioned_fk_3_1 PARTITION OF fk_partitioned_fk_3 FOR VALUES WITH (MODULUS 5, REMAINDER 1); ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_3 FOR VALUES FROM (2000, 2000) TO (3000, 3000); -- Creating a foreign key with ONLY on a partitioned table referencing -- a non-partitioned table fails. ALTER TABLE ONLY fk_partitioned_fk ADD FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk; -- Adding a NOT VALID foreign key on a partitioned table referencing -- a non-partitioned table fails. ALTER TABLE fk_partitioned_fk ADD FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk NOT VALID; -- these inserts, targeting both the partition directly as well as the -- partitioned table, should all fail INSERT INTO fk_partitioned_fk (a, b) VALUES (500, 501); INSERT INTO fk_partitioned_fk_1 (a, b) VALUES (500, 501); INSERT INTO fk_partitioned_fk (a, b) VALUES (1500, 1501); INSERT INTO fk_partitioned_fk_2 (a, b) VALUES (1500, 1501); INSERT INTO fk_partitioned_fk (a, b) VALUES (2500, 2502); INSERT INTO fk_partitioned_fk_3 (a, b) VALUES (2500, 2502); INSERT INTO fk_partitioned_fk (a, b) VALUES (2501, 2503); INSERT INTO fk_partitioned_fk_3 (a, b) VALUES (2501, 2503); -- but if we insert the values that make them valid, then they work INSERT INTO fk_notpartitioned_pk VALUES (500, 501), (1500, 1501), (2500, 2502), (2501, 2503); INSERT INTO fk_partitioned_fk (a, b) VALUES (500, 501); INSERT INTO fk_partitioned_fk (a, b) VALUES (1500, 1501); INSERT INTO fk_partitioned_fk (a, b) VALUES (2500, 2502); INSERT INTO fk_partitioned_fk (a, b) VALUES (2501, 2503); -- this update fails because there is no referenced row UPDATE fk_partitioned_fk SET a = a + 1 WHERE a = 2501; -- but we can fix it thusly: INSERT INTO fk_notpartitioned_pk (a, b) VALUES (2502, 2503); UPDATE fk_partitioned_fk SET a = a + 1 WHERE a = 2501; -- these updates would leave lingering rows in the referencing table; disallow UPDATE fk_notpartitioned_pk SET b = 502 WHERE a = 500; UPDATE fk_notpartitioned_pk SET b = 1502 WHERE a = 1500; UPDATE fk_notpartitioned_pk SET b = 2504 WHERE a = 2500; -- check psql behavior \d fk_notpartitioned_pk ALTER TABLE fk_partitioned_fk DROP CONSTRAINT fk_partitioned_fk_a_b_fkey; -- done. DROP TABLE fk_notpartitioned_pk, fk_partitioned_fk; -- Altering a type referenced by a foreign key needs to drop/recreate the FK. -- Ensure that works. CREATE TABLE fk_notpartitioned_pk ( a int, PRIMARY KEY (a), CHECK (a > 0) ); CREATE TABLE fk_partitioned_fk ( a int REFERENCES fk_notpartitioned_pk (a) PRIMARY KEY ) PARTITION BY RANGE (a); CREATE TABLE fk_partitioned_fk_1 PARTITION OF fk_partitioned_fk FOR VALUES FROM (MINVALUE) TO (MAXVALUE); INSERT INTO fk_notpartitioned_pk VALUES (1); INSERT INTO fk_partitioned_fk VALUES (1); ALTER TABLE fk_notpartitioned_pk ALTER COLUMN a TYPE bigint; DELETE FROM fk_notpartitioned_pk WHERE a = 1; DROP TABLE fk_notpartitioned_pk, fk_partitioned_fk; -- Test some other exotic foreign key features: MATCH SIMPLE, ON UPDATE/DELETE -- actions CREATE TABLE fk_notpartitioned_pk ( a int, b int, PRIMARY KEY (a, b) ); CREATE TABLE fk_partitioned_fk ( a int DEFAULT 2501, b int DEFAULT 142857 ) PARTITION BY LIST (a); CREATE TABLE fk_partitioned_fk_1 PARTITION OF fk_partitioned_fk FOR VALUES IN (NULL, 500, 501, 502); ALTER TABLE fk_partitioned_fk ADD FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk MATCH SIMPLE ON DELETE SET NULL ON UPDATE SET NULL; CREATE TABLE fk_partitioned_fk_2 PARTITION OF fk_partitioned_fk FOR VALUES IN (1500, 1502); CREATE TABLE fk_partitioned_fk_3 ( a int, b int ); ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_3 FOR VALUES IN (2500, 2501, 2502, 2503); -- this insert fails INSERT INTO fk_partitioned_fk (a, b) VALUES (2502, 2503); INSERT INTO fk_partitioned_fk_3 (a, b) VALUES (2502, 2503); -- but since the FK is MATCH SIMPLE, this one doesn't INSERT INTO fk_partitioned_fk_3 (a, b) VALUES (2502, NULL); -- now create the referenced row ... INSERT INTO fk_notpartitioned_pk VALUES (2502, 2503); --- and now the same insert work INSERT INTO fk_partitioned_fk_3 (a, b) VALUES (2502, 2503); -- this always works INSERT INTO fk_partitioned_fk (a, b) VALUES (NULL, NULL); -- MATCH FULL INSERT INTO fk_notpartitioned_pk VALUES (1, 2); CREATE TABLE fk_partitioned_fk_full ( x int, y int ) PARTITION BY RANGE (x); CREATE TABLE fk_partitioned_fk_full_1 PARTITION OF fk_partitioned_fk_full DEFAULT; INSERT INTO fk_partitioned_fk_full VALUES (1, NULL); ALTER TABLE fk_partitioned_fk_full ADD FOREIGN KEY (x, y) REFERENCES fk_notpartitioned_pk MATCH FULL; -- fails TRUNCATE fk_partitioned_fk_full; ALTER TABLE fk_partitioned_fk_full ADD FOREIGN KEY (x, y) REFERENCES fk_notpartitioned_pk MATCH FULL; INSERT INTO fk_partitioned_fk_full VALUES (1, NULL); -- fails DROP TABLE fk_partitioned_fk_full; -- ON UPDATE SET NULL SELECT tableoid::regclass, a, b FROM fk_partitioned_fk WHERE b IS NULL ORDER BY a; UPDATE fk_notpartitioned_pk SET a = a + 1 WHERE a = 2502; SELECT tableoid::regclass, a, b FROM fk_partitioned_fk WHERE b IS NULL ORDER BY a; -- ON DELETE SET NULL INSERT INTO fk_partitioned_fk VALUES (2503, 2503); SELECT count(*) FROM fk_partitioned_fk WHERE a IS NULL; DELETE FROM fk_notpartitioned_pk; SELECT count(*) FROM fk_partitioned_fk WHERE a IS NULL; -- ON UPDATE/DELETE SET DEFAULT ALTER TABLE fk_partitioned_fk DROP CONSTRAINT fk_partitioned_fk_a_b_fkey; ALTER TABLE fk_partitioned_fk ADD FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk ON DELETE SET DEFAULT ON UPDATE SET DEFAULT; INSERT INTO fk_notpartitioned_pk VALUES (2502, 2503); INSERT INTO fk_partitioned_fk_3 (a, b) VALUES (2502, 2503); -- this fails, because the defaults for the referencing table are not present -- in the referenced table: UPDATE fk_notpartitioned_pk SET a = 1500 WHERE a = 2502; -- but inserting the row we can make it work: INSERT INTO fk_notpartitioned_pk VALUES (2501, 142857); UPDATE fk_notpartitioned_pk SET a = 1500 WHERE a = 2502; SELECT * FROM fk_partitioned_fk WHERE b = 142857; -- ON UPDATE/DELETE CASCADE ALTER TABLE fk_partitioned_fk DROP CONSTRAINT fk_partitioned_fk_a_b_fkey; ALTER TABLE fk_partitioned_fk ADD FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk ON DELETE CASCADE ON UPDATE CASCADE; UPDATE fk_notpartitioned_pk SET a = 2502 WHERE a = 2501; SELECT * FROM fk_partitioned_fk WHERE b = 142857; -- Now you see it ... SELECT * FROM fk_partitioned_fk WHERE b = 142857; DELETE FROM fk_notpartitioned_pk WHERE b = 142857; -- now you don't. SELECT * FROM fk_partitioned_fk WHERE a = 142857; -- verify that DROP works DROP TABLE fk_partitioned_fk_2; -- Test behavior of the constraint together with attaching and detaching -- partitions. CREATE TABLE fk_partitioned_fk_2 PARTITION OF fk_partitioned_fk FOR VALUES IN (1500, 1502); ALTER TABLE fk_partitioned_fk DETACH PARTITION fk_partitioned_fk_2; BEGIN; DROP TABLE fk_partitioned_fk; -- constraint should still be there \d fk_partitioned_fk_2; ROLLBACK; ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_2 FOR VALUES IN (1500, 1502); DROP TABLE fk_partitioned_fk_2; CREATE TABLE fk_partitioned_fk_2 ( b int, c text, a int, FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk ON UPDATE CASCADE ON DELETE CASCADE ); ALTER TABLE fk_partitioned_fk_2 DROP COLUMN c; ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_2 FOR VALUES IN (1500, 1502); -- should have only one constraint \d fk_partitioned_fk_2 DROP TABLE fk_partitioned_fk_2; CREATE TABLE fk_partitioned_fk_4 ( a int, b int, FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk (a, b) ON UPDATE CASCADE ON DELETE CASCADE ) PARTITION BY RANGE (b, a); CREATE TABLE fk_partitioned_fk_4_1 PARTITION OF fk_partitioned_fk_4 FOR VALUES FROM (1, 1) TO (100, 100); CREATE TABLE fk_partitioned_fk_4_2 ( a int, b int, FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk (a, b) ON UPDATE SET NULL ); ALTER TABLE fk_partitioned_fk_4 ATTACH PARTITION fk_partitioned_fk_4_2 FOR VALUES FROM (100, 100) TO (1000, 1000); ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_4 FOR VALUES IN (3500, 3502); ALTER TABLE fk_partitioned_fk DETACH PARTITION fk_partitioned_fk_4; ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_4 FOR VALUES IN (3500, 3502); -- should only have one constraint \d fk_partitioned_fk_4 \d fk_partitioned_fk_4_1 -- this one has an FK with mismatched properties \d fk_partitioned_fk_4_2 CREATE TABLE fk_partitioned_fk_5 ( a int, b int, FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk (a, b) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE, FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk (a, b) MATCH FULL ON UPDATE CASCADE ON DELETE CASCADE ) PARTITION BY RANGE (a); CREATE TABLE fk_partitioned_fk_5_1 ( a int, b int, FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk ); ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_5 FOR VALUES IN (4500); ALTER TABLE fk_partitioned_fk_5 ATTACH PARTITION fk_partitioned_fk_5_1 FOR VALUES FROM (0) TO (10); ALTER TABLE fk_partitioned_fk DETACH PARTITION fk_partitioned_fk_5; ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_5 FOR VALUES IN (4500); -- this one has two constraints, similar but not quite the one in the parent, -- so it gets a new one \d fk_partitioned_fk_5 -- verify that it works to reattaching a child with multiple candidate -- constraints ALTER TABLE fk_partitioned_fk_5 DETACH PARTITION fk_partitioned_fk_5_1; ALTER TABLE fk_partitioned_fk_5 ATTACH PARTITION fk_partitioned_fk_5_1 FOR VALUES FROM (0) TO (10); \d fk_partitioned_fk_5_1 -- verify that attaching a table checks that the existing data satisfies the -- constraint CREATE TABLE fk_partitioned_fk_2 ( a int, b int ) PARTITION BY RANGE (b); CREATE TABLE fk_partitioned_fk_2_1 PARTITION OF fk_partitioned_fk_2 FOR VALUES FROM (0) TO (1000); CREATE TABLE fk_partitioned_fk_2_2 PARTITION OF fk_partitioned_fk_2 FOR VALUES FROM (1000) TO (2000); INSERT INTO fk_partitioned_fk_2 VALUES (1600, 601), (1600, 1601); ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_2 FOR VALUES IN (1600); INSERT INTO fk_notpartitioned_pk VALUES (1600, 601), (1600, 1601); ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_2 FOR VALUES IN (1600); -- leave these tables around intentionally -- test the case when the referenced table is owned by a different user CREATE ROLE regress_other_partitioned_fk_owner; GRANT REFERENCES ON fk_notpartitioned_pk TO regress_other_partitioned_fk_owner; SET ROLE regress_other_partitioned_fk_owner; CREATE TABLE other_partitioned_fk ( a int, b int ) PARTITION BY LIST (a); CREATE TABLE other_partitioned_fk_1 PARTITION OF other_partitioned_fk FOR VALUES IN (2048); INSERT INTO other_partitioned_fk SELECT 2048, x FROM generate_series(1, 10) x; -- this should fail ALTER TABLE other_partitioned_fk ADD FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk (a, b); -- add the missing keys and retry RESET ROLE; INSERT INTO fk_notpartitioned_pk (a, b) SELECT 2048, x FROM generate_series(1, 10) x; SET ROLE regress_other_partitioned_fk_owner; ALTER TABLE other_partitioned_fk ADD FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk (a, b); -- clean up DROP TABLE other_partitioned_fk; RESET ROLE; REVOKE ALL ON fk_notpartitioned_pk FROM regress_other_partitioned_fk_owner; DROP ROLE regress_other_partitioned_fk_owner; -- Test creating a constraint at the parent that already exists in partitions. -- There should be no duplicated constraints, and attempts to drop the -- constraint in partitions should raise appropriate errors. CREATE SCHEMA fkpart0 CREATE TABLE pkey ( a int PRIMARY KEY) CREATE TABLE fk_part ( a int ) PARTITION BY LIST (a) CREATE TABLE fk_part_1 PARTITION OF fk_part (FOREIGN KEY (a) REFERENCES fkpart0.pkey) FOR VALUES IN (1) CREATE TABLE fk_part_23 PARTITION OF fk_part (FOREIGN KEY (a) REFERENCES fkpart0.pkey) FOR VALUES IN (2, 3) PARTITION BY LIST (a) CREATE TABLE fk_part_23_2 PARTITION OF fk_part_23 FOR VALUES IN (2); ALTER TABLE fkpart0.fk_part ADD FOREIGN KEY (a) REFERENCES fkpart0.pkey; \d fkpart0.fk_part_1 -- should have only one FK ALTER TABLE fkpart0.fk_part_1 DROP CONSTRAINT fk_part_1_a_fkey; \d fkpart0.fk_part_23 -- should have only one FK \d fkpart0.fk_part_23_2 -- should have only one FK ALTER TABLE fkpart0.fk_part_23 DROP CONSTRAINT fk_part_23_a_fkey; ALTER TABLE fkpart0.fk_part_23_2 DROP CONSTRAINT fk_part_23_a_fkey; CREATE TABLE fkpart0.fk_part_4 PARTITION OF fkpart0.fk_part FOR VALUES IN (4); \d fkpart0.fk_part_4 ALTER TABLE fkpart0.fk_part_4 DROP CONSTRAINT fk_part_a_fkey; CREATE TABLE fkpart0.fk_part_56 PARTITION OF fkpart0.fk_part FOR VALUES IN (5, 6) PARTITION BY LIST (a); CREATE TABLE fkpart0.fk_part_56_5 PARTITION OF fkpart0.fk_part_56 FOR VALUES IN (5); \d fkpart0.fk_part_56 ALTER TABLE fkpart0.fk_part_56 DROP CONSTRAINT fk_part_a_fkey; ALTER TABLE fkpart0.fk_part_56_5 DROP CONSTRAINT fk_part_a_fkey; -- verify that attaching and detaching partitions maintains the right set of -- triggers CREATE SCHEMA fkpart1 CREATE TABLE pkey ( a int PRIMARY KEY) CREATE TABLE fk_part ( a int ) PARTITION BY LIST (a) CREATE TABLE fk_part_1 PARTITION OF fk_part FOR VALUES IN (1) PARTITION BY LIST (a) CREATE TABLE fk_part_1_1 PARTITION OF fk_part_1 FOR VALUES IN (1); ALTER TABLE fkpart1.fk_part ADD FOREIGN KEY (a) REFERENCES fkpart1.pkey; INSERT INTO fkpart1.fk_part VALUES (1); -- should fail INSERT INTO fkpart1.pkey VALUES (1); INSERT INTO fkpart1.fk_part VALUES (1); DELETE FROM fkpart1.pkey WHERE a = 1; -- should fail ALTER TABLE fkpart1.fk_part DETACH PARTITION fkpart1.fk_part_1; CREATE TABLE fkpart1.fk_part_1_2 PARTITION OF fkpart1.fk_part_1 FOR VALUES IN (2); INSERT INTO fkpart1.fk_part_1 VALUES (2); -- should fail DELETE FROM fkpart1.pkey WHERE a = 1; -- verify that attaching and detaching partitions manipulates the inheritance -- properties of their FK constraints correctly CREATE SCHEMA fkpart2 CREATE TABLE pkey ( a int PRIMARY KEY) CREATE TABLE fk_part ( a int, CONSTRAINT fkey FOREIGN KEY (a) REFERENCES fkpart2.pkey ) PARTITION BY LIST (a) CREATE TABLE fk_part_1 PARTITION OF fkpart2.fk_part FOR VALUES IN (1) PARTITION BY LIST (a) CREATE TABLE fk_part_1_1 (a int, CONSTRAINT my_fkey FOREIGN KEY (a) REFERENCES fkpart2.pkey); ALTER TABLE fkpart2.fk_part_1 ATTACH PARTITION fkpart2.fk_part_1_1 FOR VALUES IN (1); ALTER TABLE fkpart2.fk_part_1 DROP CONSTRAINT fkey; -- should fail ALTER TABLE fkpart2.fk_part_1_1 DROP CONSTRAINT my_fkey; -- should fail ALTER TABLE fkpart2.fk_part DETACH PARTITION fkpart2.fk_part_1; ALTER TABLE fkpart2.fk_part_1 DROP CONSTRAINT fkey; -- ok ALTER TABLE fkpart2.fk_part_1_1 DROP CONSTRAINT my_fkey; -- doesn't exist DROP SCHEMA fkpart0, fkpart1, fkpart2 CASCADE; -- Test a partitioned table as referenced table. -- Verify basic functionality with a regular partition creation and a partition -- with a different column layout, as well as partitions added (created and -- attached) after creating the foreign key. CREATE SCHEMA fkpart3; SET search_path TO fkpart3; CREATE TABLE pk ( a int PRIMARY KEY ) PARTITION BY RANGE (a); CREATE TABLE pk1 PARTITION OF pk FOR VALUES FROM (0) TO (1000); CREATE TABLE pk2 ( b int, a int ); ALTER TABLE pk2 DROP COLUMN b; ALTER TABLE pk2 ALTER a SET NOT NULL; ALTER TABLE pk ATTACH PARTITION pk2 FOR VALUES FROM (1000) TO (2000); CREATE TABLE fk ( a int ) PARTITION BY RANGE (a); CREATE TABLE fk1 PARTITION OF fk FOR VALUES FROM (0) TO (750); ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk; CREATE TABLE fk2 ( b int, a int ); ALTER TABLE fk2 DROP COLUMN b; ALTER TABLE fk ATTACH PARTITION fk2 FOR VALUES FROM (750) TO (3500); CREATE TABLE pk3 PARTITION OF pk FOR VALUES FROM (2000) TO (3000); CREATE TABLE pk4 ( LIKE pk ); ALTER TABLE pk ATTACH PARTITION pk4 FOR VALUES FROM (3000) TO (4000); CREATE TABLE pk5 ( c int, b int, a int NOT NULL ) PARTITION BY RANGE (a); ALTER TABLE pk5 DROP COLUMN b, DROP COLUMN c; CREATE TABLE pk51 PARTITION OF pk5 FOR VALUES FROM (4000) TO (4500); CREATE TABLE pk52 PARTITION OF pk5 FOR VALUES FROM (4500) TO (5000); ALTER TABLE pk ATTACH PARTITION pk5 FOR VALUES FROM (4000) TO (5000); CREATE TABLE fk3 PARTITION OF fk FOR VALUES FROM (3500) TO (5000); -- these should fail: referenced value not present INSERT INTO fk VALUES (1); INSERT INTO fk VALUES (1000); INSERT INTO fk VALUES (2000); INSERT INTO fk VALUES (3000); INSERT INTO fk VALUES (4000); INSERT INTO fk VALUES (4500); -- insert into the referenced table, now they should work INSERT INTO pk VALUES (1), (1000), (2000), (3000), (4000), (4500); INSERT INTO fk VALUES (1), (1000), (2000), (3000), (4000), (4500); -- should fail: referencing value present DELETE FROM pk WHERE a = 1; DELETE FROM pk WHERE a = 1000; DELETE FROM pk WHERE a = 2000; DELETE FROM pk WHERE a = 3000; DELETE FROM pk WHERE a = 4000; DELETE FROM pk WHERE a = 4500; UPDATE pk SET a = 2 WHERE a = 1; UPDATE pk SET a = 1002 WHERE a = 1000; UPDATE pk SET a = 2002 WHERE a = 2000; UPDATE pk SET a = 3002 WHERE a = 3000; UPDATE pk SET a = 4002 WHERE a = 4000; UPDATE pk SET a = 4502 WHERE a = 4500; -- now they should work DELETE FROM fk; UPDATE pk SET a = 2 WHERE a = 1; DELETE FROM pk WHERE a = 2; UPDATE pk SET a = 1002 WHERE a = 1000; DELETE FROM pk WHERE a = 1002; UPDATE pk SET a = 2002 WHERE a = 2000; DELETE FROM pk WHERE a = 2002; UPDATE pk SET a = 3002 WHERE a = 3000; DELETE FROM pk WHERE a = 3002; UPDATE pk SET a = 4002 WHERE a = 4000; DELETE FROM pk WHERE a = 4002; UPDATE pk SET a = 4502 WHERE a = 4500; DELETE FROM pk WHERE a = 4502; CREATE SCHEMA fkpart4; SET search_path TO fkpart4; -- dropping/detaching PARTITIONs is prevented if that would break -- a foreign key's existing data CREATE TABLE droppk ( a int PRIMARY KEY ) PARTITION BY RANGE (a); CREATE TABLE droppk1 PARTITION OF droppk FOR VALUES FROM (0) TO (1000); CREATE TABLE droppk_d PARTITION OF droppk DEFAULT; CREATE TABLE droppk2 PARTITION OF droppk FOR VALUES FROM (1000) TO (2000) PARTITION BY RANGE (a); CREATE TABLE droppk21 PARTITION OF droppk2 FOR VALUES FROM (1000) TO (1400); CREATE TABLE droppk2_d PARTITION OF droppk2 DEFAULT; INSERT INTO droppk VALUES (1), (1000), (1500), (2000); CREATE TABLE dropfk ( a int REFERENCES droppk ); INSERT INTO dropfk VALUES (1), (1000), (1500), (2000); -- these should all fail ALTER TABLE droppk DETACH PARTITION droppk_d; ALTER TABLE droppk2 DETACH PARTITION droppk2_d; ALTER TABLE droppk DETACH PARTITION droppk1; ALTER TABLE droppk DETACH PARTITION droppk2; ALTER TABLE droppk2 DETACH PARTITION droppk21; -- dropping partitions is disallowed DROP TABLE droppk_d; DROP TABLE droppk2_d; DROP TABLE droppk1; DROP TABLE droppk2; DROP TABLE droppk21; DELETE FROM dropfk; -- dropping partitions is disallowed, even when no referencing values DROP TABLE droppk_d; DROP TABLE droppk2_d; DROP TABLE droppk1; -- but DETACH is allowed, and DROP afterwards works ALTER TABLE droppk2 DETACH PARTITION droppk21; DROP TABLE droppk2; -- Verify that initial constraint creation and cloning behave correctly CREATE SCHEMA fkpart5; SET search_path TO fkpart5; CREATE TABLE pk ( a int PRIMARY KEY ) PARTITION BY LIST (a); CREATE TABLE pk1 PARTITION OF pk FOR VALUES IN (1) PARTITION BY LIST (a); CREATE TABLE pk11 PARTITION OF pk1 FOR VALUES IN (1); CREATE TABLE fk ( a int ) PARTITION BY LIST (a); CREATE TABLE fk1 PARTITION OF fk FOR VALUES IN (1) PARTITION BY LIST (a); CREATE TABLE fk11 PARTITION OF fk1 FOR VALUES IN (1); ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk; CREATE TABLE pk2 PARTITION OF pk FOR VALUES IN (2); CREATE TABLE pk3 ( a int NOT NULL ) PARTITION BY LIST (a); CREATE TABLE pk31 PARTITION OF pk3 FOR VALUES IN (31); CREATE TABLE pk32 ( b int, a int NOT NULL ); ALTER TABLE pk32 DROP COLUMN b; ALTER TABLE pk3 ATTACH PARTITION pk32 FOR VALUES IN (32); ALTER TABLE pk ATTACH PARTITION pk3 FOR VALUES IN (31, 32); CREATE TABLE fk2 PARTITION OF fk FOR VALUES IN (2); CREATE TABLE fk3 ( b int, a int ); ALTER TABLE fk3 DROP COLUMN b; ALTER TABLE fk ATTACH PARTITION fk3 FOR VALUES IN (3); SELECT pg_describe_object('pg_constraint'::regclass, oid, 0), confrelid::regclass, CASE WHEN conparentid <> 0 THEN pg_describe_object('pg_constraint'::regclass, conparentid, 0) ELSE 'TOP' END FROM pg_catalog.pg_constraint WHERE conrelid IN ( SELECT relid FROM pg_partition_tree ('fk')) ORDER BY conrelid::regclass::text, conname; CREATE TABLE fk4 ( LIKE fk ); INSERT INTO fk4 VALUES (50); ALTER TABLE fk ATTACH PARTITION fk4 FOR VALUES IN (50); -- Verify ON UPDATE/DELETE behavior CREATE SCHEMA fkpart6; SET search_path TO fkpart6; CREATE TABLE pk ( a int PRIMARY KEY ) PARTITION BY RANGE (a); CREATE TABLE pk1 PARTITION OF pk FOR VALUES FROM (1) TO (100) PARTITION BY RANGE (a); CREATE TABLE pk11 PARTITION OF pk1 FOR VALUES FROM (1) TO (50); CREATE TABLE pk12 PARTITION OF pk1 FOR VALUES FROM (50) TO (100); CREATE TABLE fk ( a int ) PARTITION BY RANGE (a); CREATE TABLE fk1 PARTITION OF fk FOR VALUES FROM (1) TO (100) PARTITION BY RANGE (a); CREATE TABLE fk11 PARTITION OF fk1 FOR VALUES FROM (1) TO (10); CREATE TABLE fk12 PARTITION OF fk1 FOR VALUES FROM (10) TO (100); ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk ON UPDATE CASCADE ON DELETE CASCADE; CREATE TABLE fk_d PARTITION OF fk DEFAULT; INSERT INTO pk VALUES (1); INSERT INTO fk VALUES (1); UPDATE pk SET a = 20; SELECT tableoid::regclass, * FROM fk; DELETE FROM pk WHERE a = 20; SELECT tableoid::regclass, * FROM fk; DROP TABLE fk; TRUNCATE TABLE pk; INSERT INTO pk VALUES (20), (50); CREATE TABLE fk ( a int ) PARTITION BY RANGE (a); CREATE TABLE fk1 PARTITION OF fk FOR VALUES FROM (1) TO (100) PARTITION BY RANGE (a); CREATE TABLE fk11 PARTITION OF fk1 FOR VALUES FROM (1) TO (10); CREATE TABLE fk12 PARTITION OF fk1 FOR VALUES FROM (10) TO (100); ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk ON UPDATE SET NULL ON DELETE SET NULL; CREATE TABLE fk_d PARTITION OF fk DEFAULT; INSERT INTO fk VALUES (20), (50); UPDATE pk SET a = 21 WHERE a = 20; DELETE FROM pk WHERE a = 50; SELECT tableoid::regclass, * FROM fk; DROP TABLE fk; TRUNCATE TABLE pk; INSERT INTO pk VALUES (20), (30), (50); CREATE TABLE fk ( id int, a int DEFAULT 50 ) PARTITION BY RANGE (a); CREATE TABLE fk1 PARTITION OF fk FOR VALUES FROM (1) TO (100) PARTITION BY RANGE (a); CREATE TABLE fk11 PARTITION OF fk1 FOR VALUES FROM (1) TO (10); CREATE TABLE fk12 PARTITION OF fk1 FOR VALUES FROM (10) TO (100); ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk ON UPDATE SET DEFAULT ON DELETE SET DEFAULT; CREATE TABLE fk_d PARTITION OF fk DEFAULT; INSERT INTO fk VALUES (1, 20), (2, 30); DELETE FROM pk WHERE a = 20 RETURNING *; UPDATE pk SET a = 90 WHERE a = 30 RETURNING *; SELECT tableoid::regclass, * FROM fk; DROP TABLE fk; TRUNCATE TABLE pk; INSERT INTO pk VALUES (20), (30); CREATE TABLE fk ( a int DEFAULT 50 ) PARTITION BY RANGE (a); CREATE TABLE fk1 PARTITION OF fk FOR VALUES FROM (1) TO (100) PARTITION BY RANGE (a); CREATE TABLE fk11 PARTITION OF fk1 FOR VALUES FROM (1) TO (10); CREATE TABLE fk12 PARTITION OF fk1 FOR VALUES FROM (10) TO (100); ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk ON UPDATE RESTRICT ON DELETE RESTRICT; CREATE TABLE fk_d PARTITION OF fk DEFAULT; INSERT INTO fk VALUES (20), (30); DELETE FROM pk WHERE a = 20; UPDATE pk SET a = 90 WHERE a = 30; SELECT tableoid::regclass, * FROM fk; DROP TABLE fk; -- test for reported bug: relispartition not set -- https://postgr.es/m/CA+HiwqHMsRtRYRWYTWavKJ8x14AFsv7bmAV46mYwnfD3vy8goQ@mail.gmail.com CREATE SCHEMA fkpart7 CREATE TABLE pkpart ( a int ) PARTITION BY LIST (a) CREATE TABLE pkpart1 PARTITION OF pkpart FOR VALUES IN (1); ALTER TABLE fkpart7.pkpart1 ADD PRIMARY KEY (a); ALTER TABLE fkpart7.pkpart ADD PRIMARY KEY (a); CREATE TABLE fkpart7.fk ( a int REFERENCES fkpart7.pkpart ); DROP SCHEMA fkpart7 CASCADE; pgFormatter-4.2/t/pg-test-files/expected/fsm.sql000066400000000000000000000046101361326045100217100ustar00rootroot00000000000000-- -- Free Space Map test -- SELECT current_setting('block_size')::integer AS blocksize, current_setting('block_size')::integer / 8 AS strsize \gset CREATE TABLE fsm_check_size ( num int, str text ); -- Fill 3 blocks with one record each ALTER TABLE fsm_check_size SET (fillfactor = 15); INSERT INTO fsm_check_size SELECT i, rpad('', :strsize, 'a') FROM generate_series(1, 3) i; -- There should be no FSM VACUUM fsm_check_size; SELECT pg_relation_size('fsm_check_size', 'main') / :blocksize AS heap_nblocks, pg_relation_size('fsm_check_size', 'fsm') / :blocksize AS fsm_nblocks; -- The following operations are for testing the functionality of the local -- in-memory map. In particular, we want to be able to insert into some -- other block than the one at the end of the heap, without using a FSM. -- Fill most of the last block ALTER TABLE fsm_check_size SET (fillfactor = 100); INSERT INTO fsm_check_size SELECT i, rpad('', :strsize, 'a') FROM generate_series(101, 105) i; -- Make sure records can go into any block but the last one ALTER TABLE fsm_check_size SET (fillfactor = 30); -- Insert large record and make sure it does not cause the relation to extend INSERT INTO fsm_check_size VALUES (111, rpad('', :strsize, 'a')); VACUUM fsm_check_size; SELECT pg_relation_size('fsm_check_size', 'main') / :blocksize AS heap_nblocks, pg_relation_size('fsm_check_size', 'fsm') / :blocksize AS fsm_nblocks; -- Extend table with enough blocks to exceed the FSM threshold DO $$ DECLARE curtid tid; num int; BEGIN num = 11; LOOP INSERT INTO fsm_check_size VALUES (num, 'b') RETURNING ctid INTO curtid; EXIT WHEN curtid >= tid '(4, 0)'; num = num + 1; END LOOP; END; $$; VACUUM fsm_check_size; SELECT pg_relation_size('fsm_check_size', 'fsm') / :blocksize AS fsm_nblocks; -- Add long random string to extend TOAST table to 1 block INSERT INTO fsm_check_size VALUES (0, ( SELECT string_agg(md5(chr(i)), '') FROM generate_series(1, :blocksize / 100) i)); VACUUM fsm_check_size; SELECT pg_relation_size(reltoastrelid, 'main') / :blocksize AS toast_nblocks, pg_relation_size(reltoastrelid, 'fsm') / :blocksize AS toast_fsm_nblocks FROM pg_class WHERE relname = 'fsm_check_size'; DROP TABLE fsm_check_size; pgFormatter-4.2/t/pg-test-files/expected/functional_deps.sql000066400000000000000000000137751361326045100243140ustar00rootroot00000000000000-- from http://www.depesz.com/index.php/2010/04/19/getting-unique-elements/ CREATE TEMP TABLE articles ( id int CONSTRAINT articles_pkey PRIMARY KEY, keywords text, title text UNIQUE NOT NULL, body text UNIQUE, created date ); CREATE TEMP TABLE articles_in_category ( article_id int, category_id int, changed date, PRIMARY KEY (article_id, category_id) ); -- test functional dependencies based on primary keys/unique constraints -- base tables -- group by primary key (OK) SELECT id, keywords, title, body, created FROM articles GROUP BY id; -- group by unique not null (fail/todo) SELECT id, keywords, title, body, created FROM articles GROUP BY title; -- group by unique nullable (fail) SELECT id, keywords, title, body, created FROM articles GROUP BY body; -- group by something else (fail) SELECT id, keywords, title, body, created FROM articles GROUP BY keywords; -- multiple tables -- group by primary key (OK) SELECT a.id, a.keywords, a.title, a.body, a.created FROM articles AS a, articles_in_category AS aic WHERE a.id = aic.article_id AND aic.category_id IN (14, 62, 70, 53, 138) GROUP BY a.id; -- group by something else (fail) SELECT a.id, a.keywords, a.title, a.body, a.created FROM articles AS a, articles_in_category AS aic WHERE a.id = aic.article_id AND aic.category_id IN (14, 62, 70, 53, 138) GROUP BY aic.article_id, aic.category_id; -- JOIN syntax -- group by left table's primary key (OK) SELECT a.id, a.keywords, a.title, a.body, a.created FROM articles AS a JOIN articles_in_category AS aic ON a.id = aic.article_id WHERE aic.category_id IN (14, 62, 70, 53, 138) GROUP BY a.id; -- group by something else (fail) SELECT a.id, a.keywords, a.title, a.body, a.created FROM articles AS a JOIN articles_in_category AS aic ON a.id = aic.article_id WHERE aic.category_id IN (14, 62, 70, 53, 138) GROUP BY aic.article_id, aic.category_id; -- group by right table's (composite) primary key (OK) SELECT aic.changed FROM articles AS a JOIN articles_in_category AS aic ON a.id = aic.article_id WHERE aic.category_id IN (14, 62, 70, 53, 138) GROUP BY aic.category_id, aic.article_id; -- group by right table's partial primary key (fail) SELECT aic.changed FROM articles AS a JOIN articles_in_category AS aic ON a.id = aic.article_id WHERE aic.category_id IN (14, 62, 70, 53, 138) GROUP BY aic.article_id; -- example from documentation CREATE TEMP TABLE products ( product_id int, name text, price numeric ); CREATE TEMP TABLE sales ( product_id int, units int ); -- OK SELECT product_id, p.name, (sum(s.units) * p.price) AS sales FROM products p LEFT JOIN sales s USING (product_id) GROUP BY product_id, p.name, p.price; -- fail SELECT product_id, p.name, (sum(s.units) * p.price) AS sales FROM products p LEFT JOIN sales s USING (product_id) GROUP BY product_id; ALTER TABLE products ADD PRIMARY KEY (product_id); -- OK now SELECT product_id, p.name, (sum(s.units) * p.price) AS sales FROM products p LEFT JOIN sales s USING (product_id) GROUP BY product_id; -- Drupal example, http://drupal.org/node/555530 CREATE TEMP TABLE node ( nid serial, vid integer NOT NULL DEFAULT '0', type varchar(32) NOT NULL DEFAULT '', title varchar(128) NOT NULL DEFAULT '', uid integer NOT NULL DEFAULT '0', status integer NOT NULL DEFAULT '1', created integer NOT NULL DEFAULT '0', -- snip PRIMARY KEY (nid, vid) ); CREATE TEMP TABLE users ( uid integer NOT NULL DEFAULT '0', name varchar(60) NOT NULL DEFAULT '', pass varchar(32) NOT NULL DEFAULT '', -- snip PRIMARY KEY (uid), UNIQUE (name) ); -- OK SELECT u.uid, u.name FROM node n INNER JOIN users u ON u.uid = n.uid WHERE n.type = 'blog' AND n.status = 1 GROUP BY u.uid, u.name; -- OK SELECT u.uid, u.name FROM node n INNER JOIN users u ON u.uid = n.uid WHERE n.type = 'blog' AND n.status = 1 GROUP BY u.uid; -- Check views and dependencies -- fail CREATE TEMP VIEW fdv1 AS SELECT id, keywords, title, body, created FROM articles GROUP BY body; -- OK CREATE TEMP VIEW fdv1 AS SELECT id, keywords, title, body, created FROM articles GROUP BY id; -- fail ALTER TABLE articles DROP CONSTRAINT articles_pkey RESTRICT; DROP VIEW fdv1; -- multiple dependencies CREATE TEMP VIEW fdv2 AS SELECT a.id, a.keywords, a.title, aic.category_id, aic.changed FROM articles AS a JOIN articles_in_category AS aic ON a.id = aic.article_id WHERE aic.category_id IN (14, 62, 70, 53, 138) GROUP BY a.id, aic.category_id, aic.article_id; ALTER TABLE articles DROP CONSTRAINT articles_pkey RESTRICT; -- fail ALTER TABLE articles_in_category DROP CONSTRAINT articles_in_category_pkey RESTRICT; --fail DROP VIEW fdv2; -- nested queries CREATE TEMP VIEW fdv3 AS SELECT id, keywords, title, body, created FROM articles GROUP BY id UNION SELECT id, keywords, title, body, created FROM articles GROUP BY id; ALTER TABLE articles DROP CONSTRAINT articles_pkey RESTRICT; -- fail DROP VIEW fdv3; CREATE TEMP VIEW fdv4 AS SELECT * FROM articles WHERE title IN ( SELECT title FROM articles GROUP BY id); ALTER TABLE articles DROP CONSTRAINT articles_pkey RESTRICT; -- fail DROP VIEW fdv4; -- prepared query plans: this results in failure on reuse PREPARE foo AS SELECT id, keywords, title, body, created FROM articles GROUP BY id; EXECUTE foo; ALTER TABLE articles DROP CONSTRAINT articles_pkey RESTRICT; EXECUTE foo; -- fail pgFormatter-4.2/t/pg-test-files/expected/generated.sql000066400000000000000000000377571361326045100231030ustar00rootroot00000000000000-- sanity check of system catalog SELECT attrelid, attname, attgenerated FROM pg_attribute WHERE attgenerated NOT IN ('', 's'); CREATE TABLE gtest0 ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (55) STORED ); CREATE TABLE gtest1 ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED ); SELECT table_name, column_name, column_default, is_nullable, is_generated, generation_expression FROM information_schema.columns WHERE table_name LIKE 'gtest_' ORDER BY 1, 2; SELECT table_name, column_name, dependent_column FROM information_schema.column_column_usage ORDER BY 1, 2, 3; \d gtest1 -- duplicate generated CREATE TABLE gtest_err_1 ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED GENERATED ALWAYS AS (a * 3) STORED ); -- references to other generated columns, including self-references CREATE TABLE gtest_err_2a ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (b * 2) STORED ); CREATE TABLE gtest_err_2b ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED, c int GENERATED ALWAYS AS (b * 3) STORED ); -- invalid reference CREATE TABLE gtest_err_3 ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (c * 2) STORED ); -- generation expression must be immutable CREATE TABLE gtest_err_4 ( a int PRIMARY KEY, b double precision GENERATED ALWAYS AS (random()) STORED ); -- cannot have default/identity and generated CREATE TABLE gtest_err_5a ( a int PRIMARY KEY, b int DEFAULT 5 GENERATED ALWAYS AS (a * 2) STORED ); CREATE TABLE gtest_err_5b ( a int PRIMARY KEY, b int GENERATED ALWAYS AS IDENTITY GENERATED ALWAYS AS (a * 2) STORED ); -- reference to system column not allowed in generated column CREATE TABLE gtest_err_6a ( a int PRIMARY KEY, b bool GENERATED ALWAYS AS (xmin <> 37) STORED ); -- various prohibited constructs CREATE TABLE gtest_err_7a ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (avg(a)) STORED ); CREATE TABLE gtest_err_7b ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (row_number() OVER (ORDER BY a)) STORED ); CREATE TABLE gtest_err_7c ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (( SELECT a)) STORED ); CREATE TABLE gtest_err_7d ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (generate_series(1, a)) STORED ); -- GENERATED BY DEFAULT not allowed CREATE TABLE gtest_err_8 ( a int PRIMARY KEY, b int GENERATED BY DEFAULT AS (a * 2) STORED ); INSERT INTO gtest1 VALUES (1); INSERT INTO gtest1 VALUES (2, DEFAULT); INSERT INTO gtest1 VALUES (3, 33); -- error SELECT * FROM gtest1 ORDER BY a; UPDATE gtest1 SET b = DEFAULT WHERE a = 1; UPDATE gtest1 SET b = 11 WHERE a = 1; -- error SELECT * FROM gtest1 ORDER BY a; SELECT a, b, b * 2 AS b2 FROM gtest1 ORDER BY a; SELECT a, b FROM gtest1 WHERE b = 4 ORDER BY a; -- test that overflow error happens on write INSERT INTO gtest1 VALUES (2000000000); SELECT * FROM gtest1; DELETE FROM gtest1 WHERE a = 2000000000; -- test with joins CREATE TABLE gtestx ( x int, y int ); INSERT INTO gtestx VALUES (11, 1), (22, 2), (33, 3); SELECT * FROM gtestx, gtest1 WHERE gtestx.y = gtest1.a; DROP TABLE gtestx; -- test UPDATE/DELETE quals SELECT * FROM gtest1 ORDER BY a; UPDATE gtest1 SET a = 3 WHERE b = 4; SELECT * FROM gtest1 ORDER BY a; DELETE FROM gtest1 WHERE b = 2; SELECT * FROM gtest1 ORDER BY a; -- views CREATE VIEW gtest1v AS SELECT * FROM gtest1; SELECT * FROM gtest1v; INSERT INTO gtest1v VALUES (4, 8); -- fails DROP VIEW gtest1v; -- CTEs WITH foo AS ( SELECT * FROM gtest1 ) SELECT * FROM foo; -- inheritance CREATE TABLE gtest1_1 () INHERITS ( gtest1 ); SELECT * FROM gtest1_1; \d gtest1_1 INSERT INTO gtest1_1 VALUES (4); SELECT * FROM gtest1_1; SELECT * FROM gtest1; -- test inheritance mismatch CREATE TABLE gtesty ( x int, b int ); CREATE TABLE gtest1_2 () INHERITS ( gtest1, gtesty ); -- error DROP TABLE gtesty; -- test stored update CREATE TABLE gtest3 ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 3) STORED ); INSERT INTO gtest3 (a) VALUES (1), (2), (3); SELECT * FROM gtest3 ORDER BY a; UPDATE gtest3 SET a = 22 WHERE a = 2; SELECT * FROM gtest3 ORDER BY a; -- COPY TRUNCATE gtest1; INSERT INTO gtest1 (a) VALUES (1), (2); SELECT * FROM gtest1 ORDER BY a; TRUNCATE gtest3; INSERT INTO gtest3 (a) VALUES (1), (2); SELECT * FROM gtest3 ORDER BY a; -- null values CREATE TABLE gtest2 ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (NULL) STORED ); INSERT INTO gtest2 VALUES (1); SELECT * FROM gtest2; -- composite types CREATE TYPE double_int AS ( a int, b int ); CREATE TABLE gtest4 ( a int, b double_int GENERATED ALWAYS AS ((a * 2, a * 3)) STORED ); INSERT INTO gtest4 VALUES (1), (6); SELECT * FROM gtest4; DROP TABLE gtest4; DROP TYPE double_int; -- using tableoid is allowed CREATE TABLE gtest_tableoid ( a int PRIMARY KEY, b bool GENERATED ALWAYS AS (tableoid <> 0) STORED ); INSERT INTO gtest_tableoid VALUES (1), (2); SELECT * FROM gtest_tableoid; -- drop column behavior CREATE TABLE gtest10 ( a int PRIMARY KEY, b int, c int GENERATED ALWAYS AS (b * 2) STORED ); ALTER TABLE gtest10 DROP COLUMN b; \d gtest10 CREATE TABLE gtest10a ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED ); ALTER TABLE gtest10a DROP COLUMN b; INSERT INTO gtest10a (a) VALUES (1); -- privileges CREATE USER regress_user11; CREATE TABLE gtest11s ( a int PRIMARY KEY, b int, c int GENERATED ALWAYS AS (b * 2) STORED ); INSERT INTO gtest11s VALUES (1, 10), (2, 20); GRANT SELECT (a, c) ON gtest11s TO regress_user11; CREATE FUNCTION gf1 (a int) RETURNS int AS $$ SELECT a * 3 $$ IMMUTABLE LANGUAGE SQL; REVOKE ALL ON FUNCTION gf1 (int) FROM PUBLIC; CREATE TABLE gtest12s ( a int PRIMARY KEY, b int, c int GENERATED ALWAYS AS (gf1 (b)) STORED ); INSERT INTO gtest12s VALUES (1, 10), (2, 20); GRANT SELECT (a, c) ON gtest12s TO regress_user11; SET ROLE regress_user11; SELECT a, b FROM gtest11s; -- not allowed SELECT a, c FROM gtest11s; -- allowed SELECT gf1 (10); -- not allowed SELECT a, c FROM gtest12s; -- allowed RESET ROLE; DROP TABLE gtest11s, gtest12s; DROP FUNCTION gf1 (int); DROP USER regress_user11; -- check constraints CREATE TABLE gtest20 ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED CHECK (b < 50) ); INSERT INTO gtest20 (a) VALUES (10); -- ok INSERT INTO gtest20 (a) VALUES (30); -- violates constraint CREATE TABLE gtest20a ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED ); INSERT INTO gtest20a (a) VALUES (10); INSERT INTO gtest20a (a) VALUES (30); ALTER TABLE gtest20a ADD CHECK (b < 50); -- fails on existing row CREATE TABLE gtest20b ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED ); INSERT INTO gtest20b (a) VALUES (10); INSERT INTO gtest20b (a) VALUES (30); ALTER TABLE gtest20b ADD CONSTRAINT chk CHECK (b < 50) NOT VALID; ALTER TABLE gtest20b VALIDATE CONSTRAINT chk; -- fails on existing row -- not-null constraints CREATE TABLE gtest21a ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (nullif (a, 0)) STORED NOT NULL ); INSERT INTO gtest21a (a) VALUES (1); -- ok INSERT INTO gtest21a (a) VALUES (0); -- violates constraint CREATE TABLE gtest21b ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (nullif (a, 0)) STORED ); ALTER TABLE gtest21b ALTER COLUMN b SET NOT NULL; INSERT INTO gtest21b (a) VALUES (1); -- ok INSERT INTO gtest21b (a) VALUES (0); -- violates constraint ALTER TABLE gtest21b ALTER COLUMN b DROP NOT NULL; INSERT INTO gtest21b (a) VALUES (0); -- ok now -- index constraints CREATE TABLE gtest22a ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (a / 2) STORED UNIQUE ); INSERT INTO gtest22a VALUES (2); INSERT INTO gtest22a VALUES (3); INSERT INTO gtest22a VALUES (4); CREATE TABLE gtest22b ( a int, b int GENERATED ALWAYS AS (a / 2) STORED, PRIMARY KEY (a, b) ); INSERT INTO gtest22b VALUES (2); INSERT INTO gtest22b VALUES (2); -- indexes CREATE TABLE gtest22c ( a int, b int GENERATED ALWAYS AS (a * 2) STORED ); CREATE INDEX gtest22c_b_idx ON gtest22c (b); CREATE INDEX gtest22c_expr_idx ON gtest22c ((b * 3)); CREATE INDEX gtest22c_pred_idx ON gtest22c (a) WHERE b > 0; \d gtest22c INSERT INTO gtest22c VALUES (1), (2), (3); SET enable_seqscan TO OFF; SET enable_bitmapscan TO OFF; EXPLAIN ( COSTS OFF ) SELECT * FROM gtest22c WHERE b = 4; SELECT * FROM gtest22c WHERE b = 4; EXPLAIN ( COSTS OFF ) SELECT * FROM gtest22c WHERE b * 3 = 6; SELECT * FROM gtest22c WHERE b * 3 = 6; EXPLAIN ( COSTS OFF ) SELECT * FROM gtest22c WHERE a = 1 AND b > 0; SELECT * FROM gtest22c WHERE a = 1 AND b > 0; RESET enable_seqscan; RESET enable_bitmapscan; -- foreign keys CREATE TABLE gtest23a ( x int PRIMARY KEY, y int ); INSERT INTO gtest23a VALUES (1, 11), (2, 22), (3, 33); CREATE TABLE gtest23x ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED REFERENCES gtest23a (x) ON UPDATE CASCADE ); -- error CREATE TABLE gtest23x ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED REFERENCES gtest23a (x) ON DELETE SET NULL ); -- error CREATE TABLE gtest23b ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED REFERENCES gtest23a (x) ); \d gtest23b INSERT INTO gtest23b VALUES (1); -- ok INSERT INTO gtest23b VALUES (5); -- error DROP TABLE gtest23b; DROP TABLE gtest23a; CREATE TABLE gtest23p ( x int, y int GENERATED ALWAYS AS (x * 2) STORED, PRIMARY KEY (y) ); INSERT INTO gtest23p VALUES (1), (2), (3); CREATE TABLE gtest23q ( a int PRIMARY KEY, b int REFERENCES gtest23p (y) ); INSERT INTO gtest23q VALUES (1, 2); -- ok INSERT INTO gtest23q VALUES (2, 5); -- error -- domains CREATE DOMAIN gtestdomain1 AS int CHECK (VALUE < 10); CREATE TABLE gtest24 ( a int PRIMARY KEY, b gtestdomain1 GENERATED ALWAYS AS (a * 2) STORED ); INSERT INTO gtest24 (a) VALUES (4); -- ok INSERT INTO gtest24 (a) VALUES (6); -- error -- typed tables (currently not supported) CREATE TYPE gtest_type AS ( f1 integer, f2 text, f3 bigint ); CREATE TABLE gtest28 OF gtest_type ( f1 WITH OPTIONS GENERATED ALWAYS AS (f2 * 2) STORED ); DROP TYPE gtest_type CASCADE; -- table partitions (currently not supported) CREATE TABLE gtest_parent ( f1 date NOT NULL, f2 text, f3 bigint ) PARTITION BY RANGE (f1); CREATE TABLE gtest_child PARTITION OF gtest_parent (f3 WITH OPTIONS GENERATED ALWAYS AS (f2 * 2) STORED) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error DROP TABLE gtest_parent; -- partitioned table CREATE TABLE gtest_parent ( f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED ) PARTITION BY RANGE (f1); CREATE TABLE gtest_child PARTITION OF gtest_parent FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); INSERT INTO gtest_parent (f1, f2) VALUES ('2016-07-15', 1); SELECT * FROM gtest_parent; SELECT * FROM gtest_child; DROP TABLE gtest_parent; -- generated columns in partition key (not allowed) CREATE TABLE gtest_parent ( f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED ) PARTITION BY RANGE (f3); CREATE TABLE gtest_parent ( f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED ) PARTITION BY RANGE ((f3 * 3)); -- ALTER TABLE ... ADD COLUMN CREATE TABLE gtest25 ( a int PRIMARY KEY ); INSERT INTO gtest25 VALUES (3), (4); ALTER TABLE gtest25 ADD COLUMN b int GENERATED ALWAYS AS (a * 3) STORED; SELECT * FROM gtest25 ORDER BY a; ALTER TABLE gtest25 ADD COLUMN x int GENERATED ALWAYS AS (b * 4) STORED; -- error ALTER TABLE gtest25 ADD COLUMN x int GENERATED ALWAYS AS (z * 4) STORED; -- error -- ALTER TABLE ... ALTER COLUMN CREATE TABLE gtest27 ( a int, b int GENERATED ALWAYS AS (a * 2) STORED ); INSERT INTO gtest27 (a) VALUES (3), (4); ALTER TABLE gtest27 ALTER COLUMN a TYPE text; -- error ALTER TABLE gtest27 ALTER COLUMN b TYPE numeric; \d gtest27 SELECT * FROM gtest27; ALTER TABLE gtest27 ALTER COLUMN b TYPE boolean USING b <> 0; -- error ALTER TABLE gtest27 ALTER COLUMN b DROP DEFAULT; -- error \d gtest27 -- triggers CREATE TABLE gtest26 ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED ); CREATE FUNCTION gtest_trigger_func () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN IF tg_op IN ('DELETE', 'UPDATE') THEN RAISE INFO '%: %: old = %', TG_NAME, TG_WHEN, OLD; END IF; IF tg_op IN ('INSERT', 'UPDATE') THEN RAISE INFO '%: %: new = %', TG_NAME, TG_WHEN, NEW; END IF; IF tg_op = 'DELETE' THEN RETURN OLD; ELSE RETURN NEW; END IF; END $$; CREATE TRIGGER gtest1 BEFORE DELETE OR UPDATE ON gtest26 FOR EACH ROW WHEN (OLD.b < 0) -- ok EXECUTE PROCEDURE gtest_trigger_func (); CREATE TRIGGER gtest2a BEFORE INSERT OR UPDATE ON gtest26 FOR EACH ROW WHEN (NEW.b < 0) -- error EXECUTE PROCEDURE gtest_trigger_func (); CREATE TRIGGER gtest2b BEFORE INSERT OR UPDATE ON gtest26 FOR EACH ROW WHEN (NEW.* IS NOT NULL) -- error EXECUTE PROCEDURE gtest_trigger_func (); CREATE TRIGGER gtest2 BEFORE INSERT ON gtest26 FOR EACH ROW WHEN (NEW.a < 0) EXECUTE PROCEDURE gtest_trigger_func (); CREATE TRIGGER gtest3 AFTER DELETE OR UPDATE ON gtest26 FOR EACH ROW WHEN (OLD.b < 0) -- ok EXECUTE PROCEDURE gtest_trigger_func (); CREATE TRIGGER gtest4 AFTER INSERT OR UPDATE ON gtest26 FOR EACH ROW WHEN (NEW.b < 0) -- ok EXECUTE PROCEDURE gtest_trigger_func (); INSERT INTO gtest26 (a) VALUES (- 2), (0), (3); SELECT * FROM gtest26 ORDER BY a; UPDATE gtest26 SET a = a * - 2; SELECT * FROM gtest26 ORDER BY a; DELETE FROM gtest26 WHERE a = - 6; SELECT * FROM gtest26 ORDER BY a; DROP TRIGGER gtest1 ON gtest26; DROP TRIGGER gtest2 ON gtest26; DROP TRIGGER gtest3 ON gtest26; -- Check that an UPDATE of "a" fires the trigger for UPDATE OF b, per -- SQL standard. CREATE FUNCTION gtest_trigger_func3 () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RAISE NOTICE 'OK'; RETURN NEW; END $$; CREATE TRIGGER gtest11 BEFORE UPDATE OF b ON gtest26 FOR EACH ROW EXECUTE PROCEDURE gtest_trigger_func3 (); UPDATE gtest26 SET a = 1 WHERE a = 0; DROP TRIGGER gtest11 ON gtest26; TRUNCATE gtest26; -- check that modifications of stored generated columns in triggers do -- not get propagated CREATE FUNCTION gtest_trigger_func4 () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN NEW.a = 10; NEW.b = 300; RETURN NEW; END; $$; CREATE TRIGGER gtest12_01 BEFORE UPDATE ON gtest26 FOR EACH ROW EXECUTE PROCEDURE gtest_trigger_func (); CREATE TRIGGER gtest12_02 BEFORE UPDATE ON gtest26 FOR EACH ROW EXECUTE PROCEDURE gtest_trigger_func4 (); CREATE TRIGGER gtest12_03 BEFORE UPDATE ON gtest26 FOR EACH ROW EXECUTE PROCEDURE gtest_trigger_func (); INSERT INTO gtest26 (a) VALUES (1); UPDATE gtest26 SET a = 11 WHERE a = 1; SELECT * FROM gtest26 ORDER BY a; -- LIKE INCLUDING GENERATED and dropped column handling CREATE TABLE gtest28a ( a int, b int, c int, x int GENERATED ALWAYS AS (b * 2) STORED ); ALTER TABLE gtest28a DROP COLUMN a; CREATE TABLE gtest28b ( LIKE gtest28a INCLUDING GENERATED ); \d gtest28* pgFormatter-4.2/t/pg-test-files/expected/geometry.sql000066400000000000000000000405721361326045100227650ustar00rootroot00000000000000-- -- GEOMETRY -- -- Back off displayed precision a little bit to reduce platform-to-platform -- variation in results. SET extra_float_digits TO - 3; -- -- Points -- SELECT '' AS four, center(f1) AS center FROM BOX_TBL; SELECT '' AS four, (@@ f1) AS center FROM BOX_TBL; SELECT '' AS six, point(f1) AS center FROM CIRCLE_TBL; SELECT '' AS six, (@@ f1) AS center FROM CIRCLE_TBL; SELECT '' AS two, (@@ f1) AS center FROM POLYGON_TBL WHERE (# f1) > 2; -- "is horizontal" function SELECT '' AS two, p1.f1 FROM POINT_TBL p1 WHERE ishorizontal(p1.f1, point '(0,0)'); -- "is horizontal" operator SELECT '' AS two, p1.f1 FROM POINT_TBL p1 WHERE p1.f1 ? - point '(0,0)'; -- "is vertical" function SELECT '' AS one, p1.f1 FROM POINT_TBL p1 WHERE isvertical(p1.f1, point '(5.1,34.5)'); -- "is vertical" operator SELECT '' AS one, p1.f1 FROM POINT_TBL p1 WHERE p1.f1 ? | point '(5.1,34.5)'; -- Slope SELECT p1.f1, p2.f1, slope(p1.f1, p2.f1) FROM POINT_TBL p1, POINT_TBL p2; -- Add point SELECT p1.f1, p2.f1, p1.f1 + p2.f1 FROM POINT_TBL p1, POINT_TBL p2; -- Subtract point SELECT p1.f1, p2.f1, p1.f1 - p2.f1 FROM POINT_TBL p1, POINT_TBL p2; -- Multiply with point SELECT p1.f1, p2.f1, p1.f1 * p2.f1 FROM POINT_TBL p1, POINT_TBL p2 WHERE p1.f1[0] BETWEEN 1 AND 1000; -- Underflow error SELECT p1.f1, p2.f1, p1.f1 * p2.f1 FROM POINT_TBL p1, POINT_TBL p2 WHERE p1.f1[0] < 1; -- Divide by point SELECT p1.f1, p2.f1, p1.f1 / p2.f1 FROM POINT_TBL p1, POINT_TBL p2 WHERE p2.f1[0] BETWEEN 1 AND 1000; -- Overflow error SELECT p1.f1, p2.f1, p1.f1 / p2.f1 FROM POINT_TBL p1, POINT_TBL p2 WHERE p2.f1[0] > 1000; -- Division by 0 error SELECT p1.f1, p2.f1, p1.f1 / p2.f1 FROM POINT_TBL p1, POINT_TBL p2 WHERE p2.f1 ~= '(0,0)'::point; -- Distance to line SELECT p.f1, l.s, p.f1 <-> l.s FROM POINT_TBL p, LINE_TBL l; -- Distance to line segment SELECT p.f1, l.s, p.f1 <-> l.s FROM POINT_TBL p, LSEG_TBL l; -- Distance to box SELECT p.f1, b.f1, p.f1 <-> b.f1 FROM POINT_TBL p, BOX_TBL b; -- Distance to path SELECT p.f1, p1.f1, p.f1 <-> p1.f1 FROM POINT_TBL p, PATH_TBL p1; -- Distance to polygon SELECT p.f1, p1.f1, p.f1 <-> p1.f1 FROM POINT_TBL p, POLYGON_TBL p1; -- Closest point to line SELECT p.f1, l.s, p.f1 ## l.s FROM POINT_TBL p, LINE_TBL l; -- Closest point to line segment SELECT p.f1, l.s, p.f1 ## l.s FROM POINT_TBL p, LSEG_TBL l; -- Closest point to box SELECT p.f1, b.f1, p.f1 ## b.f1 FROM POINT_TBL p, BOX_TBL b; -- On line SELECT p.f1, l.s FROM POINT_TBL p, LINE_TBL l WHERE p.f1 <@ l.s; -- On line segment SELECT p.f1, l.s FROM POINT_TBL p, LSEG_TBL l WHERE p.f1 <@ l.s; -- On path SELECT p.f1, p1.f1 FROM POINT_TBL p, PATH_TBL p1 WHERE p.f1 <@ p1.f1; -- -- Lines -- -- Vertical SELECT s FROM LINE_TBL WHERE ? | s; -- Horizontal SELECT s FROM LINE_TBL WHERE ? - s; -- Same as line SELECT l1.s, l2.s FROM LINE_TBL l1, LINE_TBL l2 WHERE l1.s = l2.s; -- Parallel to line SELECT l1.s, l2.s FROM LINE_TBL l1, LINE_TBL l2 WHERE l1.s ? || l2.s; -- Perpendicular to line SELECT l1.s, l2.s FROM LINE_TBL l1, LINE_TBL l2 WHERE l1.s ? - | l2.s; -- Distance to line SELECT l1.s, l2.s, l1.s <-> l2.s FROM LINE_TBL l1, LINE_TBL l2; -- Distance to box SELECT l.s, b.f1, l.s <-> b.f1 FROM LINE_TBL l, BOX_TBL b; -- Intersect with line SELECT l1.s, l2.s FROM LINE_TBL l1, LINE_TBL l2 WHERE l1.s ? # l2.s; -- Intersect with box SELECT l.s, b.f1 FROM LINE_TBL l, BOX_TBL b WHERE l.s ? # b.f1; -- Intersection point with line SELECT l1.s, l2.s, l1.s # l2.s FROM LINE_TBL l1, LINE_TBL l2; -- Closest point to line segment SELECT l.s, l1.s, l.s ## l1.s FROM LINE_TBL l, LSEG_TBL l1; -- Closest point to box SELECT l.s, b.f1, l.s ## b.f1 FROM LINE_TBL l, BOX_TBL b; -- -- Line segments -- -- intersection SELECT '' AS count, p.f1, l.s, l.s # p.f1 AS intersection FROM LSEG_TBL l, POINT_TBL p; -- Length SELECT s, @-@ s FROM LSEG_TBL; -- Vertical SELECT s FROM LSEG_TBL WHERE ? | s; -- Horizontal SELECT s FROM LSEG_TBL WHERE ? - s; -- Center SELECT s, @@ s FROM LSEG_TBL; -- To point SELECT s, s::point FROM LSEG_TBL; -- Has points less than line segment SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s < l2.s; -- Has points less than or equal to line segment SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s <= l2.s; -- Has points equal to line segment SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s = l2.s; -- Has points greater than or equal to line segment SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s >= l2.s; -- Has points greater than line segment SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s > l2.s; -- Has points not equal to line segment SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s != l2.s; -- Parallel with line segment SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s ? || l2.s; -- Perpendicular with line segment SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s ? - | l2.s; -- Distance to line SELECT l.s, l1.s, l.s <-> l1.s FROM LSEG_TBL l, LINE_TBL l1; -- Distance to line segment SELECT l1.s, l2.s, l1.s <-> l2.s FROM LSEG_TBL l1, LSEG_TBL l2; -- Distance to box SELECT l.s, b.f1, l.s <-> b.f1 FROM LSEG_TBL l, BOX_TBL b; -- Intersect with line segment SELECT l.s, l1.s FROM LSEG_TBL l, LINE_TBL l1 WHERE l.s ? # l1.s; -- Intersect with box SELECT l.s, b.f1 FROM LSEG_TBL l, BOX_TBL b WHERE l.s ? # b.f1; -- Intersection point with line segment SELECT l1.s, l2.s, l1.s # l2.s FROM LSEG_TBL l1, LSEG_TBL l2; -- Closest point to line SELECT l.s, l1.s, l.s ## l1.s FROM LSEG_TBL l, LINE_TBL l1; -- Closest point to line segment SELECT l1.s, l2.s, l1.s ## l2.s FROM LSEG_TBL l1, LSEG_TBL l2; -- Closest point to box SELECT l.s, b.f1, l.s ## b.f1 FROM LSEG_TBL l, BOX_TBL b; -- On line SELECT l.s, l1.s FROM LSEG_TBL l, LINE_TBL l1 WHERE l.s <@ l1.s; -- On box SELECT l.s, b.f1 FROM LSEG_TBL l, BOX_TBL b WHERE l.s <@ b.f1; -- -- Boxes -- SELECT '' AS six, box(f1) AS box FROM CIRCLE_TBL; -- translation SELECT '' AS twentyfour, b.f1 + p.f1 AS translation FROM BOX_TBL b, POINT_TBL p; SELECT '' AS twentyfour, b.f1 - p.f1 AS translation FROM BOX_TBL b, POINT_TBL p; -- Multiply with point SELECT b.f1, p.f1, b.f1 * p.f1 FROM BOX_TBL b, POINT_TBL p WHERE p.f1[0] BETWEEN 1 AND 1000; -- Overflow error SELECT b.f1, p.f1, b.f1 * p.f1 FROM BOX_TBL b, POINT_TBL p WHERE p.f1[0] > 1000; -- Divide by point SELECT b.f1, p.f1, b.f1 / p.f1 FROM BOX_TBL b, POINT_TBL p WHERE p.f1[0] BETWEEN 1 AND 1000; -- To box SELECT f1::box FROM POINT_TBL; SELECT bound_box (a.f1, b.f1) FROM BOX_TBL a, BOX_TBL b; -- Below box SELECT b1.f1, b2.f1, b1.f1 <^ b2.f1 FROM BOX_TBL b1, BOX_TBL b2; -- Above box SELECT b1.f1, b2.f1, b1.f1 >^ b2.f1 FROM BOX_TBL b1, BOX_TBL b2; -- Intersection point with box SELECT b1.f1, b2.f1, b1.f1 # b2.f1 FROM BOX_TBL b1, BOX_TBL b2; -- Diagonal SELECT f1, diagonal(f1) FROM BOX_TBL; -- Distance to box SELECT b1.f1, b2.f1, b1.f1 <-> b2.f1 FROM BOX_TBL b1, BOX_TBL b2; -- -- Paths -- -- Points SELECT f1, npoints(f1) FROM PATH_TBL; -- Area SELECT f1, area(f1) FROM PATH_TBL; -- Length SELECT f1, @-@ f1 FROM PATH_TBL; -- Center SELECT f1, @@ f1 FROM PATH_TBL; -- To polygon SELECT f1, f1::polygon FROM PATH_TBL WHERE isclosed(f1); -- Open path cannot be converted to polygon error SELECT f1, f1::polygon FROM PATH_TBL WHERE isopen(f1); -- Has points less than path SELECT p1.f1, p2.f1 FROM PATH_TBL p1, PATH_TBL p2 WHERE p1.f1 < p2.f1; -- Has points less than or equal to path SELECT p1.f1, p2.f1 FROM PATH_TBL p1, PATH_TBL p2 WHERE p1.f1 <= p2.f1; -- Has points equal to path SELECT p1.f1, p2.f1 FROM PATH_TBL p1, PATH_TBL p2 WHERE p1.f1 = p2.f1; -- Has points greater than or equal to path SELECT p1.f1, p2.f1 FROM PATH_TBL p1, PATH_TBL p2 WHERE p1.f1 >= p2.f1; -- Has points greater than path SELECT p1.f1, p2.f1 FROM PATH_TBL p1, PATH_TBL p2 WHERE p1.f1 > p2.f1; -- Add path SELECT p1.f1, p2.f1, p1.f1 + p2.f1 FROM PATH_TBL p1, PATH_TBL p2; -- Add point SELECT p.f1, p1.f1, p.f1 + p1.f1 FROM PATH_TBL p, POINT_TBL p1; -- Subtract point SELECT p.f1, p1.f1, p.f1 - p1.f1 FROM PATH_TBL p, POINT_TBL p1; -- Multiply with point SELECT p.f1, p1.f1, p.f1 * p1.f1 FROM PATH_TBL p, POINT_TBL p1; -- Divide by point SELECT p.f1, p1.f1, p.f1 / p1.f1 FROM PATH_TBL p, POINT_TBL p1 WHERE p1.f1[0] BETWEEN 1 AND 1000; -- Division by 0 error SELECT p.f1, p1.f1, p.f1 / p1.f1 FROM PATH_TBL p, POINT_TBL p1 WHERE p1.f1 ~= '(0,0)'::point; -- Distance to path SELECT p1.f1, p2.f1, p1.f1 <-> p2.f1 FROM PATH_TBL p1, PATH_TBL p2; -- -- Polygons -- -- containment SELECT '' AS twentyfour, p.f1, poly.f1, poly.f1 @> p.f1 AS contains FROM POLYGON_TBL poly, POINT_TBL p; SELECT '' AS twentyfour, p.f1, poly.f1, p.f1 <@ poly.f1 AS contained FROM POLYGON_TBL poly, POINT_TBL p; SELECT '' AS four, npoints(f1) AS npoints, f1 AS polygon FROM POLYGON_TBL; SELECT '' AS four, polygon(f1) FROM BOX_TBL; SELECT '' AS four, polygon(f1) FROM PATH_TBL WHERE isclosed(f1); SELECT '' AS four, f1 AS open_path, polygon(pclose(f1)) AS polygon FROM PATH_TBL WHERE isopen(f1); -- To box SELECT f1, f1::box FROM POLYGON_TBL; -- To path SELECT f1, f1::path FROM POLYGON_TBL; -- Same as polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 ~= p2.f1; -- Contained by polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 <@ p2.f1; -- Contains polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 @> p2.f1; -- Overlap with polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 && p2.f1; -- Left of polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 << p2.f1; -- Overlap of left of polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 &< p2.f1; -- Right of polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 >> p2.f1; -- Overlap of right of polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 &> p2.f1; -- Below polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 <<| p2.f1; -- Overlap or below polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 &<| p2.f1; -- Above polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 |>> p2.f1; -- Overlap or above polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 |&> p2.f1; -- Distance to polygon SELECT p1.f1, p2.f1, p1.f1 <-> p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2; -- -- Circles -- SELECT '' AS six, circle(f1, 50.0) FROM POINT_TBL; SELECT '' AS four, circle(f1) FROM BOX_TBL; SELECT '' AS two, circle(f1) FROM POLYGON_TBL WHERE (# f1) >= 3; SELECT '' AS twentyfour, c1.f1 AS circle, p1.f1 AS point, (p1.f1 <-> c1.f1) AS distance FROM CIRCLE_TBL c1, POINT_TBL p1 WHERE (p1.f1 <-> c1.f1) > 0 ORDER BY distance, area(c1.f1), p1.f1[0]; -- To polygon SELECT f1, f1::polygon FROM CIRCLE_TBL WHERE f1 >= '<(0,0),1>'; -- To polygon with less points SELECT f1, polygon(8, f1) FROM CIRCLE_TBL WHERE f1 >= '<(0,0),1>'; -- Too less points error SELECT f1, polygon(1, f1) FROM CIRCLE_TBL WHERE f1 >= '<(0,0),1>'; -- Zero radius error SELECT f1, polygon(10, f1) FROM CIRCLE_TBL WHERE f1 < '<(0,0),1>'; -- Same as circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 ~= c2.f1; -- Overlap with circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 && c2.f1; -- Overlap or left of circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 &< c2.f1; -- Left of circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 << c2.f1; -- Right of circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 >> c2.f1; -- Overlap or right of circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 &> c2.f1; -- Contained by circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 <@ c2.f1; -- Contain by circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 @> c2.f1; -- Below circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 <<| c2.f1; -- Above circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 |>> c2.f1; -- Overlap or below circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 &<| c2.f1; -- Overlap or above circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 |&> c2.f1; -- Area equal with circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 = c2.f1; -- Area not equal with circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 != c2.f1; -- Area less than circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 < c2.f1; -- Area greater than circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 > c2.f1; -- Area less than or equal circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 <= c2.f1; -- Area greater than or equal circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 >= c2.f1; -- Area less than circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 < c2.f1; -- Area greater than circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 < c2.f1; -- Add point SELECT c.f1, p.f1, c.f1 + p.f1 FROM CIRCLE_TBL c, POINT_TBL p; -- Subtract point SELECT c.f1, p.f1, c.f1 - p.f1 FROM CIRCLE_TBL c, POINT_TBL p; -- Multiply with point SELECT c.f1, p.f1, c.f1 * p.f1 FROM CIRCLE_TBL c, POINT_TBL p; -- Divide by point SELECT c.f1, p.f1, c.f1 / p.f1 FROM CIRCLE_TBL c, POINT_TBL p WHERE p.f1[0] BETWEEN 1 AND 1000; -- Overflow error SELECT c.f1, p.f1, c.f1 / p.f1 FROM CIRCLE_TBL c, POINT_TBL p WHERE p.f1[0] > 1000; -- Division by 0 error SELECT c.f1, p.f1, c.f1 / p.f1 FROM CIRCLE_TBL c, POINT_TBL p WHERE p.f1 ~= '(0,0)'::point; -- Distance to polygon SELECT c.f1, p.f1, c.f1 <-> p.f1 FROM CIRCLE_TBL c, POLYGON_TBL p; pgFormatter-4.2/t/pg-test-files/expected/gin.sql000066400000000000000000000027361361326045100217070ustar00rootroot00000000000000-- -- Test GIN indexes. -- -- There are other tests to test different GIN opclassed. This is for testing -- GIN itself. -- Create and populate a test table with a GIN index. CREATE TABLE gin_test_tbl ( i int4[] ) WITH ( autovacuum_enabled = OFF ); CREATE INDEX gin_test_idx ON gin_test_tbl USING gin (i) WITH (fastupdate = ON, gin_pending_list_limit = 4096); INSERT INTO gin_test_tbl SELECT ARRAY[1, 2, g] FROM generate_series(1, 20000) g; INSERT INTO gin_test_tbl SELECT ARRAY[1, 3, g] FROM generate_series(1, 1000) g; SELECT gin_clean_pending_list ('gin_test_idx') > 10 AS many; -- flush the fastupdate buffers INSERT INTO gin_test_tbl SELECT ARRAY[3, 1, g] FROM generate_series(1, 1000) g; VACUUM gin_test_tbl; -- flush the fastupdate buffers SELECT gin_clean_pending_list ('gin_test_idx'); -- nothing to flush -- Test vacuuming DELETE FROM gin_test_tbl WHERE i @> ARRAY[2]; VACUUM gin_test_tbl; -- Disable fastupdate, and do more insertions. With fastupdate enabled, most -- insertions (by flushing the list pages) cause page splits. Without -- fastupdate, we get more churn in the GIN data leaf pages, and exercise the -- recompression codepaths. ALTER INDEX gin_test_idx SET (fastupdate = OFF); INSERT INTO gin_test_tbl SELECT ARRAY[1, 2, g] FROM generate_series(1, 1000) g; INSERT INTO gin_test_tbl SELECT ARRAY[1, 3, g] FROM generate_series(1, 1000) g; DELETE FROM gin_test_tbl WHERE i @> ARRAY[2]; VACUUM gin_test_tbl; pgFormatter-4.2/t/pg-test-files/expected/gist.sql000066400000000000000000000114471361326045100220770ustar00rootroot00000000000000-- -- Test GiST indexes. -- -- There are other tests to test different GiST opclasses. This is for -- testing GiST code itself. Vacuuming in particular. CREATE TABLE gist_point_tbl ( id int4, p point ); CREATE INDEX gist_pointidx ON gist_point_tbl USING gist (p); -- Verify the fillfactor and buffering options CREATE INDEX gist_pointidx2 ON gist_point_tbl USING gist (p) WITH (buffering = ON, fillfactor = 50); CREATE INDEX gist_pointidx3 ON gist_point_tbl USING gist (p) WITH (buffering = OFF); CREATE INDEX gist_pointidx4 ON gist_point_tbl USING gist (p) WITH (buffering = auto); DROP INDEX gist_pointidx2, gist_pointidx3, gist_pointidx4; -- Make sure bad values are refused CREATE INDEX gist_pointidx5 ON gist_point_tbl USING gist (p) WITH (buffering = invalid_value); CREATE INDEX gist_pointidx5 ON gist_point_tbl USING gist (p) WITH (fillfactor = 9); CREATE INDEX gist_pointidx5 ON gist_point_tbl USING gist (p) WITH (fillfactor = 101); -- Insert enough data to create a tree that's a couple of levels deep. INSERT INTO gist_point_tbl (id, p) SELECT g, point(g * 10, g * 10) FROM generate_series(1, 10000) g; INSERT INTO gist_point_tbl (id, p) SELECT g + 100000, point(g * 10 + 1, g * 10 + 1) FROM generate_series(1, 10000) g; -- To test vacuum, delete some entries from all over the index. DELETE FROM gist_point_tbl WHERE id % 2 = 1; -- And also delete some concentration of values. DELETE FROM gist_point_tbl WHERE id > 5000; VACUUM ANALYZE gist_point_tbl; -- rebuild the index with a different fillfactor ALTER INDEX gist_pointidx SET (fillfactor = 40); REINDEX INDEX gist_pointidx; -- -- Test Index-only plans on GiST indexes -- CREATE TABLE gist_tbl ( b box, p point, c circle ); INSERT INTO gist_tbl SELECT box(point(0.05 * i, 0.05 * i), point(0.05 * i, 0.05 * i)), point(0.05 * i, 0.05 * i), circle(point(0.05 * i, 0.05 * i), 1.0) FROM generate_series(0, 10000) AS i; VACUUM ANALYZE gist_tbl; SET enable_seqscan = OFF; SET enable_bitmapscan = OFF; SET enable_indexonlyscan = ON; -- Test index-only scan with point opclass CREATE INDEX gist_tbl_point_index ON gist_tbl USING gist (p); -- check that the planner chooses an index-only scan EXPLAIN ( COSTS OFF ) SELECT p FROM gist_tbl WHERE p <@ box(point(0, 0), point(0.5, 0.5)); -- execute the same SELECT p FROM gist_tbl WHERE p <@ box(point(0, 0), point(0.5, 0.5)); -- Also test an index-only knn-search EXPLAIN ( COSTS OFF ) SELECT p FROM gist_tbl WHERE p <@ box(point(0, 0), point(0.5, 0.5)) ORDER BY p <-> point(0.201, 0.201); SELECT p FROM gist_tbl WHERE p <@ box(point(0, 0), point(0.5, 0.5)) ORDER BY p <-> point(0.201, 0.201); -- Check commuted case as well EXPLAIN ( COSTS OFF ) SELECT p FROM gist_tbl WHERE p <@ box(point(0, 0), point(0.5, 0.5)) ORDER BY point(0.101, 0.101) <-> p; SELECT p FROM gist_tbl WHERE p <@ box(point(0, 0), point(0.5, 0.5)) ORDER BY point(0.101, 0.101) <-> p; -- Check case with multiple rescans (bug #14641) EXPLAIN ( COSTS OFF ) SELECT p FROM ( VALUES (box(point(0, 0), point(0.5, 0.5))), (box(point(0.5, 0.5), point(0.75, 0.75))), (box(point(0.8, 0.8), point(1.0, 1.0)))) AS v (bb) CROSS JOIN LATERAL ( SELECT p FROM gist_tbl WHERE p <@ bb ORDER BY p <-> bb[0] LIMIT 2) ss; SELECT p FROM ( VALUES (box(point(0, 0), point(0.5, 0.5))), (box(point(0.5, 0.5), point(0.75, 0.75))), (box(point(0.8, 0.8), point(1.0, 1.0)))) AS v (bb) CROSS JOIN LATERAL ( SELECT p FROM gist_tbl WHERE p <@ bb ORDER BY p <-> bb[0] LIMIT 2) ss; DROP INDEX gist_tbl_point_index; -- Test index-only scan with box opclass CREATE INDEX gist_tbl_box_index ON gist_tbl USING gist (b); -- check that the planner chooses an index-only scan EXPLAIN ( COSTS OFF ) SELECT b FROM gist_tbl WHERE b <@ box(point(5, 5), point(6, 6)); -- execute the same SELECT b FROM gist_tbl WHERE b <@ box(point(5, 5), point(6, 6)); DROP INDEX gist_tbl_box_index; -- Test that an index-only scan is not chosen, when the query involves the -- circle column (the circle opclass does not support index-only scans). CREATE INDEX gist_tbl_multi_index ON gist_tbl USING gist (p, c); EXPLAIN ( COSTS OFF ) SELECT p, c FROM gist_tbl WHERE p <@ box(point(5, 5), point(6, 6)); -- execute the same SELECT b, p FROM gist_tbl WHERE b <@ box(point(4.5, 4.5), point(5.5, 5.5)) AND p <@ box(point(5, 5), point(6, 6)); DROP INDEX gist_tbl_multi_index; -- Clean up RESET enable_seqscan; RESET enable_bitmapscan; RESET enable_indexonlyscan; DROP TABLE gist_tbl; pgFormatter-4.2/t/pg-test-files/expected/groupingsets.sql000066400000000000000000000556661361326045100236750ustar00rootroot00000000000000-- -- grouping sets -- -- test data sources CREATE temp VIEW gstest1 (a, b, v) AS VALUES (1, 1, 10), (1, 1, 11), (1, 2, 12), (1, 2, 13), (1, 3, 14), (2, 3, 15), (3, 3, 16), (3, 4, 17), (4, 1, 18), (4, 1, 19); CREATE temp TABLE gstest2 ( a integer, b integer, c integer, d integer, e integer, f integer, g integer, h integer ); CREATE temp TABLE gstest3 ( a integer, b integer, c integer, d integer ); ALTER TABLE gstest3 ADD PRIMARY KEY (a); CREATE temp TABLE gstest4 ( id integer, v integer, unhashable_col bit(4), unsortable_col xid ); INSERT INTO gstest4 VALUES (1, 1, b '0000', '1'), (2, 2, b '0001', '1'), (3, 4, b '0010', '2'), (4, 8, b '0011', '2'), (5, 16, b '0000', '2'), (6, 32, b '0001', '2'), (7, 64, b '0010', '1'), (8, 128, b '0011', '1'); CREATE temp TABLE gstest_empty ( a integer, b integer, v integer ); CREATE FUNCTION gstest_data (v integer, out a integer, out b integer) RETURNS SETOF record AS $f$ BEGIN RETURN query SELECT v, i FROM generate_series(1, 3) i; END; $f$ LANGUAGE plpgsql; -- basic functionality SET enable_hashagg = FALSE; -- test hashing explicitly later -- simple rollup with multiple plain aggregates, with and without ordering -- (and with ordering differing from grouping) SELECT a, b, GROUPING (a, b), sum(v), count(*), max(v) FROM gstest1 GROUP BY ROLLUP (a, b); SELECT a, b, GROUPING (a, b), sum(v), count(*), max(v) FROM gstest1 GROUP BY ROLLUP (a, b) ORDER BY a, b; SELECT a, b, GROUPING (a, b), sum(v), count(*), max(v) FROM gstest1 GROUP BY ROLLUP (a, b) ORDER BY b DESC, a; SELECT a, b, GROUPING (a, b), sum(v), count(*), max(v) FROM gstest1 GROUP BY ROLLUP (a, b) ORDER BY coalesce(a, 0) + coalesce(b, 0); -- various types of ordered aggs SELECT a, b, GROUPING (a, b), array_agg(v ORDER BY v), string_agg(v::text, ':' ORDER BY v DESC), percentile_disc(0.5) WITHIN GROUP (ORDER BY v), rank(1, 2, 12) WITHIN GROUP (ORDER BY a, b, v) FROM gstest1 GROUP BY ROLLUP (a, b) ORDER BY a, b; -- test usage of grouped columns in direct args of aggs SELECT GROUPING (a), a, array_agg(b), rank(a) WITHIN GROUP (ORDER BY b nulls FIRST), rank(a) WITHIN GROUP (ORDER BY b nulls LAST) FROM ( VALUES (1, 1), (1, 4), (1, 5), (3, 1), (3, 2)) v (a, b) GROUP BY ROLLUP (a) ORDER BY a; -- nesting with window functions SELECT a, b, sum(c), sum(sum(c)) OVER (ORDER BY a, b) AS rsum FROM gstest2 GROUP BY ROLLUP (a, b) ORDER BY rsum, a, b; -- nesting with grouping sets SELECT sum(c) FROM gstest2 GROUP BY GROUPING SETS ((), GROUPING SETS ((), GROUPING SETS (()))) ORDER BY 1 DESC; SELECT sum(c) FROM gstest2 GROUP BY GROUPING SETS ((), GROUPING SETS ((), GROUPING SETS (((a, b))))) ORDER BY 1 DESC; SELECT sum(c) FROM gstest2 GROUP BY GROUPING SETS (GROUPING SETS (ROLLUP (c), GROUPING SETS (CUBE (c)))) ORDER BY 1 DESC; SELECT sum(c) FROM gstest2 GROUP BY GROUPING SETS (a, GROUPING SETS (a, CUBE (b))) ORDER BY 1 DESC; SELECT sum(c) FROM gstest2 GROUP BY GROUPING SETS (GROUPING SETS ((a, (b)))) ORDER BY 1 DESC; SELECT sum(c) FROM gstest2 GROUP BY GROUPING SETS (GROUPING SETS ((a, b))) ORDER BY 1 DESC; SELECT sum(c) FROM gstest2 GROUP BY GROUPING SETS (GROUPING SETS (a, GROUPING SETS (a), a)) ORDER BY 1 DESC; SELECT sum(c) FROM gstest2 GROUP BY GROUPING SETS (GROUPING SETS (a, GROUPING SETS (a, GROUPING SETS (a), ((a)), a, GROUPING SETS (a), (a)), a)) ORDER BY 1 DESC; SELECT sum(c) FROM gstest2 GROUP BY GROUPING SETS ((a, (a, b)), GROUPING SETS ((a, (a, b)), a)) ORDER BY 1 DESC; -- empty input: first is 0 rows, second 1, third 3 etc. SELECT a, b, sum(v), count(*) FROM gstest_empty GROUP BY GROUPING SETS ((a, b), a); SELECT a, b, sum(v), count(*) FROM gstest_empty GROUP BY GROUPING SETS ((a, b), ()); SELECT a, b, sum(v), count(*) FROM gstest_empty GROUP BY GROUPING SETS ((a, b), (), (), ()); SELECT sum(v), count(*) FROM gstest_empty GROUP BY GROUPING SETS ((), (), ()); -- empty input with joins tests some important code paths SELECT t1.a, t2.b, sum(t1.v), count(*) FROM gstest_empty t1, gstest_empty t2 GROUP BY GROUPING SETS ((t1.a, t2.b), ()); -- simple joins, var resolution, GROUPING on join vars SELECT t1.a, t2.b, GROUPING (t1.a, t2.b), sum(t1.v), max(t2.a) FROM gstest1 t1, gstest2 t2 GROUP BY GROUPING SETS ((t1.a, t2.b), ()); SELECT t1.a, t2.b, GROUPING (t1.a, t2.b), sum(t1.v), max(t2.a) FROM gstest1 t1 JOIN gstest2 t2 ON (t1.a = t2.a) GROUP BY GROUPING SETS ((t1.a, t2.b), ()); SELECT a, b, GROUPING (a, b), sum(t1.v), max(t2.c) FROM gstest1 t1 JOIN gstest2 t2 USING (a, b) GROUP BY GROUPING SETS ((a, b), ()); -- check that functionally dependent cols are not nulled SELECT a, d, GROUPING (a, b, c) FROM gstest3 GROUP BY GROUPING SETS ((a, b), (a, c)); -- check that distinct grouping columns are kept separate -- even if they are equal() EXPLAIN ( COSTS OFF ) SELECT g AS alias1, g AS alias2 FROM generate_series(1, 3) g GROUP BY alias1, ROLLUP (alias2); SELECT g AS alias1, g AS alias2 FROM generate_series(1, 3) g GROUP BY alias1, ROLLUP (alias2); -- check that pulled-up subquery outputs still go to null when appropriate SELECT four, x FROM ( SELECT four, ten, 'foo'::text AS x FROM tenk1) AS t GROUP BY GROUPING SETS (four, x) HAVING x = 'foo'; SELECT four, x || 'x' FROM ( SELECT four, ten, 'foo'::text AS x FROM tenk1) AS t GROUP BY GROUPING SETS (four, x) ORDER BY four; SELECT (x + y) * 1, sum(z) FROM ( SELECT 1 AS x, 2 AS y, 3 AS z) s GROUP BY GROUPING SETS (x + y, x); SELECT x, NOT x AS not_x, q2 FROM ( SELECT *, q1 = 1 AS x FROM int8_tbl i1) AS t GROUP BY GROUPING SETS (x, q2) ORDER BY x, q2; -- simple rescan tests SELECT a, b, sum(v.x) FROM ( VALUES (1), (2)) v (x), gstest_data (v.x) GROUP BY ROLLUP (a, b); SELECT * FROM ( VALUES (1), (2)) v (x), LATERAL ( SELECT a, b, sum(v.x) FROM gstest_data (v.x) GROUP BY ROLLUP (a, b)) s; -- min max optimization should still work with GROUP BY () EXPLAIN ( COSTS OFF ) SELECT min(unique1) FROM tenk1 GROUP BY (); -- Views with GROUPING SET queries CREATE VIEW gstest_view AS SELECT a, b, GROUPING (a, b), sum(c), count(*), max(c) FROM gstest2 GROUP BY ROLLUP ((a, b, c), (c, d)); SELECT pg_get_viewdef('gstest_view'::regclass, TRUE); -- Nested queries with 3 or more levels of nesting SELECT ( SELECT ( SELECT GROUPING (a, b) FROM ( VALUES (1)) v2 (c)) FROM ( VALUES (1, 2)) v1 (a, b) GROUP BY (a, b)) FROM ( VALUES (6, 7)) v3 (e, f) GROUP BY ROLLUP (e, f); SELECT ( SELECT ( SELECT GROUPING (e, f) FROM ( VALUES (1)) v2 (c)) FROM ( VALUES (1, 2)) v1 (a, b) GROUP BY (a, b)) FROM ( VALUES (6, 7)) v3 (e, f) GROUP BY ROLLUP (e, f); SELECT ( SELECT ( SELECT GROUPING (c) FROM ( VALUES (1)) v2 (c) GROUP BY c) FROM ( VALUES (1, 2)) v1 (a, b) GROUP BY (a, b)) FROM ( VALUES (6, 7)) v3 (e, f) GROUP BY ROLLUP (e, f); -- Combinations of operations SELECT a, b, c, d FROM gstest2 GROUP BY ROLLUP (a, b), GROUPING SETS (c, d); SELECT a, b FROM ( VALUES (1, 2), (2, 3)) v (a, b) GROUP BY a, b, GROUPING SETS (a); -- Tests for chained aggregates SELECT a, b, GROUPING (a, b), sum(v), count(*), max(v) FROM gstest1 GROUP BY GROUPING SETS ((a, b), (a + 1, b + 1), (a + 2, b + 2)) ORDER BY 3, 6; SELECT ( SELECT ( SELECT GROUPING (a, b) FROM ( VALUES (1)) v2 (c)) FROM ( VALUES (1, 2)) v1 (a, b) GROUP BY (a, b)) FROM ( VALUES (6, 7)) v3 (e, f) GROUP BY ROLLUP ((e + 1), (f + 1)); SELECT ( SELECT ( SELECT GROUPING (a, b) FROM ( VALUES (1)) v2 (c)) FROM ( VALUES (1, 2)) v1 (a, b) GROUP BY (a, b)) FROM ( VALUES (6, 7)) v3 (e, f) GROUP BY CUBE ((e + 1), (f + 1)) ORDER BY (e + 1), (f + 1); SELECT a, b, sum(c), sum(sum(c)) OVER (ORDER BY a, b) AS rsum FROM gstest2 GROUP BY CUBE (a, b) ORDER BY rsum, a, b; SELECT a, b, sum(c) FROM ( VALUES (1, 1, 10), (1, 1, 11), (1, 2, 12), (1, 2, 13), (1, 3, 14), (2, 3, 15), (3, 3, 16), (3, 4, 17), (4, 1, 18), (4, 1, 19)) v (a, b, c) GROUP BY ROLLUP (a, b); SELECT a, b, sum(v.x) FROM ( VALUES (1), (2)) v (x), gstest_data (v.x) GROUP BY CUBE (a, b) ORDER BY a, b; -- Agg level check. This query should error out. SELECT ( SELECT GROUPING (a, b) FROM gstest2) FROM gstest2 GROUP BY a, b; --Nested queries SELECT a, b, sum(c), count(*) FROM gstest2 GROUP BY GROUPING SETS (ROLLUP (a, b), a); -- HAVING queries SELECT ten, sum(DISTINCT four) FROM onek a GROUP BY GROUPING SETS ((ten, four), (ten)) HAVING EXISTS ( SELECT 1 FROM onek b WHERE sum(DISTINCT a.four) = b.four); -- Tests around pushdown of HAVING clauses, partially testing against previous bugs SELECT a, count(*) FROM gstest2 GROUP BY ROLLUP (a) ORDER BY a; SELECT a, count(*) FROM gstest2 GROUP BY ROLLUP (a) HAVING a IS DISTINCT FROM 1 ORDER BY a; EXPLAIN ( COSTS OFF ) SELECT a, count(*) FROM gstest2 GROUP BY ROLLUP (a) HAVING a IS DISTINCT FROM 1 ORDER BY a; SELECT v.c, ( SELECT count(*) FROM gstest2 GROUP BY () HAVING v.c) FROM ( VALUES (FALSE), (TRUE)) v (c) ORDER BY v.c; EXPLAIN ( COSTS OFF ) SELECT v.c, ( SELECT count(*) FROM gstest2 GROUP BY () HAVING v.c) FROM ( VALUES (FALSE), (TRUE)) v (c) ORDER BY v.c; -- HAVING with GROUPING queries SELECT ten, GROUPING (ten) FROM onek GROUP BY GROUPING SETS (ten) HAVING GROUPING (ten) >= 0 ORDER BY 2, 1; SELECT ten, GROUPING (ten) FROM onek GROUP BY GROUPING SETS (ten, four) HAVING GROUPING (ten) > 0 ORDER BY 2, 1; SELECT ten, GROUPING (ten) FROM onek GROUP BY ROLLUP (ten) HAVING GROUPING (ten) > 0 ORDER BY 2, 1; SELECT ten, GROUPING (ten) FROM onek GROUP BY CUBE (ten) HAVING GROUPING (ten) > 0 ORDER BY 2, 1; SELECT ten, GROUPING (ten) FROM onek GROUP BY (ten) HAVING GROUPING (ten) >= 0 ORDER BY 2, 1; -- FILTER queries SELECT ten, sum(DISTINCT four) FILTER (WHERE four::text ~ '123') FROM onek a GROUP BY ROLLUP (ten); -- More rescan tests SELECT * FROM ( VALUES (1), (2)) v (a) LEFT JOIN LATERAL ( SELECT v.a, four, ten, count(*) FROM onek GROUP BY CUBE (four, ten)) s ON TRUE ORDER BY v.a, four, ten; SELECT ARRAY ( SELECT ROW (v.a, s1.*) FROM ( SELECT two, four, count(*) FROM onek GROUP BY CUBE (two, four) ORDER BY two, four) s1) FROM ( VALUES (1), (2)) v (a); -- Grouping on text columns SELECT sum(ten) FROM onek GROUP BY two, ROLLUP (four::text) ORDER BY 1; SELECT sum(ten) FROM onek GROUP BY ROLLUP (four::text), two ORDER BY 1; -- hashing support SET enable_hashagg = TRUE; -- failure cases SELECT count(*) FROM gstest4 GROUP BY ROLLUP (unhashable_col, unsortable_col); SELECT array_agg(v ORDER BY v) FROM gstest4 GROUP BY GROUPING SETS ((id, unsortable_col), (id)); -- simple cases SELECT a, b, GROUPING (a, b), sum(v), count(*), max(v) FROM gstest1 GROUP BY GROUPING SETS ((a), (b)) ORDER BY 3, 1, 2; EXPLAIN ( COSTS OFF ) SELECT a, b, GROUPING (a, b), sum(v), count(*), max(v) FROM gstest1 GROUP BY GROUPING SETS ((a), (b)) ORDER BY 3, 1, 2; SELECT a, b, GROUPING (a, b), sum(v), count(*), max(v) FROM gstest1 GROUP BY CUBE (a, b) ORDER BY 3, 1, 2; EXPLAIN ( COSTS OFF ) SELECT a, b, GROUPING (a, b), sum(v), count(*), max(v) FROM gstest1 GROUP BY CUBE (a, b) ORDER BY 3, 1, 2; -- shouldn't try and hash EXPLAIN ( COSTS OFF ) SELECT a, b, GROUPING (a, b), array_agg(v ORDER BY v) FROM gstest1 GROUP BY CUBE (a, b); -- unsortable cases SELECT unsortable_col, count(*) FROM gstest4 GROUP BY GROUPING SETS ((unsortable_col), (unsortable_col)) ORDER BY unsortable_col::text; -- mixed hashable/sortable cases SELECT unhashable_col, unsortable_col, GROUPING (unhashable_col, unsortable_col), count(*), sum(v) FROM gstest4 GROUP BY GROUPING SETS ((unhashable_col), (unsortable_col)) ORDER BY 3, 5; EXPLAIN ( COSTS OFF ) SELECT unhashable_col, unsortable_col, GROUPING (unhashable_col, unsortable_col), count(*), sum(v) FROM gstest4 GROUP BY GROUPING SETS ((unhashable_col), (unsortable_col)) ORDER BY 3, 5; SELECT unhashable_col, unsortable_col, GROUPING (unhashable_col, unsortable_col), count(*), sum(v) FROM gstest4 GROUP BY GROUPING SETS ((v, unhashable_col), (v, unsortable_col)) ORDER BY 3, 5; EXPLAIN ( COSTS OFF ) SELECT unhashable_col, unsortable_col, GROUPING (unhashable_col, unsortable_col), count(*), sum(v) FROM gstest4 GROUP BY GROUPING SETS ((v, unhashable_col), (v, unsortable_col)) ORDER BY 3, 5; -- empty input: first is 0 rows, second 1, third 3 etc. SELECT a, b, sum(v), count(*) FROM gstest_empty GROUP BY GROUPING SETS ((a, b), a); EXPLAIN ( COSTS OFF ) SELECT a, b, sum(v), count(*) FROM gstest_empty GROUP BY GROUPING SETS ((a, b), a); SELECT a, b, sum(v), count(*) FROM gstest_empty GROUP BY GROUPING SETS ((a, b), ()); SELECT a, b, sum(v), count(*) FROM gstest_empty GROUP BY GROUPING SETS ((a, b), (), (), ()); EXPLAIN ( COSTS OFF ) SELECT a, b, sum(v), count(*) FROM gstest_empty GROUP BY GROUPING SETS ((a, b), (), (), ()); SELECT sum(v), count(*) FROM gstest_empty GROUP BY GROUPING SETS ((), (), ()); EXPLAIN ( COSTS OFF ) SELECT sum(v), count(*) FROM gstest_empty GROUP BY GROUPING SETS ((), (), ()); -- check that functionally dependent cols are not nulled SELECT a, d, GROUPING (a, b, c) FROM gstest3 GROUP BY GROUPING SETS ((a, b), (a, c)); EXPLAIN ( COSTS OFF ) SELECT a, d, GROUPING (a, b, c) FROM gstest3 GROUP BY GROUPING SETS ((a, b), (a, c)); -- simple rescan tests SELECT a, b, sum(v.x) FROM ( VALUES (1), (2)) v (x), gstest_data (v.x) GROUP BY GROUPING SETS (a, b) ORDER BY 1, 2, 3; EXPLAIN ( COSTS OFF ) SELECT a, b, sum(v.x) FROM ( VALUES (1), (2)) v (x), gstest_data (v.x) GROUP BY GROUPING SETS (a, b) ORDER BY 3, 1, 2; SELECT * FROM ( VALUES (1), (2)) v (x), LATERAL ( SELECT a, b, sum(v.x) FROM gstest_data (v.x) GROUP BY GROUPING SETS (a, b)) s; EXPLAIN ( COSTS OFF ) SELECT * FROM ( VALUES (1), (2)) v (x), LATERAL ( SELECT a, b, sum(v.x) FROM gstest_data (v.x) GROUP BY GROUPING SETS (a, b)) s; -- Tests for chained aggregates SELECT a, b, GROUPING (a, b), sum(v), count(*), max(v) FROM gstest1 GROUP BY GROUPING SETS ((a, b), (a + 1, b + 1), (a + 2, b + 2)) ORDER BY 3, 6; EXPLAIN ( COSTS OFF ) SELECT a, b, GROUPING (a, b), sum(v), count(*), max(v) FROM gstest1 GROUP BY GROUPING SETS ((a, b), (a + 1, b + 1), (a + 2, b + 2)) ORDER BY 3, 6; SELECT a, b, sum(c), sum(sum(c)) OVER (ORDER BY a, b) AS rsum FROM gstest2 GROUP BY CUBE (a, b) ORDER BY rsum, a, b; EXPLAIN ( COSTS OFF ) SELECT a, b, sum(c), sum(sum(c)) OVER (ORDER BY a, b) AS rsum FROM gstest2 GROUP BY CUBE (a, b) ORDER BY rsum, a, b; SELECT a, b, sum(v.x) FROM ( VALUES (1), (2)) v (x), gstest_data (v.x) GROUP BY CUBE (a, b) ORDER BY a, b; EXPLAIN ( COSTS OFF ) SELECT a, b, sum(v.x) FROM ( VALUES (1), (2)) v (x), gstest_data (v.x) GROUP BY CUBE (a, b) ORDER BY a, b; -- More rescan tests SELECT * FROM ( VALUES (1), (2)) v (a) LEFT JOIN LATERAL ( SELECT v.a, four, ten, count(*) FROM onek GROUP BY CUBE (four, ten)) s ON TRUE ORDER BY v.a, four, ten; SELECT ARRAY ( SELECT ROW (v.a, s1.*) FROM ( SELECT two, four, count(*) FROM onek GROUP BY CUBE (two, four) ORDER BY two, four) s1) FROM ( VALUES (1), (2)) v (a); -- Rescan logic changes when there are no empty grouping sets, so test -- that too: SELECT * FROM ( VALUES (1), (2)) v (a) LEFT JOIN LATERAL ( SELECT v.a, four, ten, count(*) FROM onek GROUP BY GROUPING SETS (four, ten)) s ON TRUE ORDER BY v.a, four, ten; SELECT ARRAY ( SELECT ROW (v.a, s1.*) FROM ( SELECT two, four, count(*) FROM onek GROUP BY GROUPING SETS (two, four) ORDER BY two, four) s1) FROM ( VALUES (1), (2)) v (a); -- test the knapsack SET enable_indexscan = FALSE; SET work_mem = '64kB'; EXPLAIN ( COSTS OFF ) SELECT unique1, count(two), count(four), count(ten), count(hundred), count(thousand), count(twothousand), count(*) FROM tenk1 GROUP BY GROUPING SETS (unique1, twothousand, thousand, hundred, ten, four, two); EXPLAIN ( COSTS OFF ) SELECT unique1, count(two), count(four), count(ten), count(hundred), count(thousand), count(twothousand), count(*) FROM tenk1 GROUP BY GROUPING SETS (unique1, hundred, ten, four, two); SET work_mem = '384kB'; EXPLAIN ( COSTS OFF ) SELECT unique1, count(two), count(four), count(ten), count(hundred), count(thousand), count(twothousand), count(*) FROM tenk1 GROUP BY GROUPING SETS (unique1, twothousand, thousand, hundred, ten, four, two); -- check collation-sensitive matching between grouping expressions -- (similar to a check for aggregates, but there are additional code -- paths for GROUPING, so check again here) SELECT v || 'a', CASE GROUPING (v || 'a') WHEN 1 THEN 1 ELSE 0 END, count(*) FROM unnest(ARRAY[1, 1], ARRAY['a', 'b']) u (i, v) GROUP BY ROLLUP (i, v || 'a') ORDER BY 1, 3; SELECT v || 'a', CASE WHEN GROUPING (v || 'a') = 1 THEN 1 ELSE 0 END, count(*) FROM unnest(ARRAY[1, 1], ARRAY['a', 'b']) u (i, v) GROUP BY ROLLUP (i, v || 'a') ORDER BY 1, 3; -- end pgFormatter-4.2/t/pg-test-files/expected/guc.sql000066400000000000000000000201721361326045100217020ustar00rootroot00000000000000-- pg_regress should ensure that this default value applies; however -- we can't rely on any specific default value of vacuum_cost_delay SHOW datestyle; -- SET to some nondefault value SET vacuum_cost_delay TO 40; SET datestyle = 'ISO, YMD'; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; -- SET LOCAL has no effect outside of a transaction SET LOCAL vacuum_cost_delay TO 50; SHOW vacuum_cost_delay; SET LOCAL datestyle = 'SQL'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; -- SET LOCAL within a transaction that commits BEGIN; SET LOCAL vacuum_cost_delay TO 50; SHOW vacuum_cost_delay; SET LOCAL datestyle = 'SQL'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; COMMIT; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; -- SET should be reverted after ROLLBACK BEGIN; SET vacuum_cost_delay TO 60; SHOW vacuum_cost_delay; SET datestyle = 'German'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; ROLLBACK; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; -- Some tests with subtransactions BEGIN; SET vacuum_cost_delay TO 70; SET datestyle = 'MDY'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; SAVEPOINT first_sp; SET vacuum_cost_delay TO 80.1; SHOW vacuum_cost_delay; SET datestyle = 'German, DMY'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; ROLLBACK TO first_sp; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; SAVEPOINT second_sp; SET vacuum_cost_delay TO '900us'; SET datestyle = 'SQL, YMD'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; SAVEPOINT third_sp; SET vacuum_cost_delay TO 100; SHOW vacuum_cost_delay; SET datestyle = 'Postgres, MDY'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; ROLLBACK TO third_sp; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; ROLLBACK TO second_sp; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; ROLLBACK; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; -- SET LOCAL with Savepoints BEGIN; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; SAVEPOINT sp; SET LOCAL vacuum_cost_delay TO 30; SHOW vacuum_cost_delay; SET LOCAL datestyle = 'Postgres, MDY'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; ROLLBACK TO sp; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; ROLLBACK; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; -- SET LOCAL persists through RELEASE (which was not true in 8.0-8.2) BEGIN; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; SAVEPOINT sp; SET LOCAL vacuum_cost_delay TO 30; SHOW vacuum_cost_delay; SET LOCAL datestyle = 'Postgres, MDY'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; RELEASE SAVEPOINT sp; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; ROLLBACK; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; -- SET followed by SET LOCAL BEGIN; SET vacuum_cost_delay TO 40; SET LOCAL vacuum_cost_delay TO 50; SHOW vacuum_cost_delay; SET datestyle = 'ISO, DMY'; SET LOCAL datestyle = 'Postgres, MDY'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; COMMIT; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; -- -- Test RESET. We use datestyle because the reset value is forced by -- pg_regress, so it doesn't depend on the installation's configuration. -- SET datestyle = iso, ymd; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; RESET datestyle; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; -- Test some simple error cases SET seq_page_cost TO 'NaN'; SET vacuum_cost_delay TO '10s'; -- -- Test DISCARD TEMP -- CREATE TEMP TABLE reset_test ( data text ) ON COMMIT DELETE ROWS; SELECT relname FROM pg_class WHERE relname = 'reset_test'; DISCARD TEMP; SELECT relname FROM pg_class WHERE relname = 'reset_test'; -- -- Test DISCARD ALL -- -- do changes DECLARE foo CURSOR WITH HOLD FOR SELECT 1; PREPARE foo AS SELECT 1; LISTEN foo_event; SET vacuum_cost_delay = 13; CREATE TEMP TABLE tmp_foo ( data text ) ON COMMIT DELETE ROWS; CREATE ROLE regress_guc_user; SET SESSION AUTHORIZATION regress_guc_user; -- look changes SELECT pg_listening_channels(); SELECT name FROM pg_prepared_statements; SELECT name FROM pg_cursors; SHOW vacuum_cost_delay; SELECT relname FROM pg_class WHERE relname = 'tmp_foo'; SELECT CURRENT_USER = 'regress_guc_user'; -- discard everything DISCARD ALL; -- look again SELECT pg_listening_channels(); SELECT name FROM pg_prepared_statements; SELECT name FROM pg_cursors; SHOW vacuum_cost_delay; SELECT relname FROM pg_class WHERE relname = 'tmp_foo'; SELECT CURRENT_USER = 'regress_guc_user'; DROP ROLE regress_guc_user; -- -- search_path should react to changes in pg_namespace -- SET search_path = foo, public, not_there_initially; SELECT current_schemas(FALSE); CREATE SCHEMA not_there_initially; SELECT current_schemas(FALSE); DROP SCHEMA not_there_initially; SELECT current_schemas(FALSE); RESET search_path; -- -- Tests for function-local GUC settings -- SET work_mem = '3MB'; CREATE FUNCTION report_guc (text) RETURNS text AS $$ SELECT current_setting($1) $$ LANGUAGE sql SET work_mem = '1MB'; SELECT report_guc ('work_mem'), current_setting('work_mem'); ALTER FUNCTION report_guc (text) SET work_mem = '2MB'; SELECT report_guc ('work_mem'), current_setting('work_mem'); ALTER FUNCTION report_guc (text) RESET ALL; SELECT report_guc ('work_mem'), current_setting('work_mem'); -- SET LOCAL is restricted by a function SET option CREATE OR REPLACE FUNCTION myfunc (int) RETURNS text AS $$ BEGIN SET local work_mem = '2MB'; RETURN current_setting('work_mem'); END $$ LANGUAGE plpgsql SET work_mem = '1MB'; SELECT myfunc (0), current_setting('work_mem'); ALTER FUNCTION myfunc (int) RESET ALL; SELECT myfunc (0), current_setting('work_mem'); SET work_mem = '3MB'; -- but SET isn't CREATE OR REPLACE FUNCTION myfunc (int) RETURNS text AS $$ BEGIN SET work_mem = '2MB'; RETURN current_setting('work_mem'); END $$ LANGUAGE plpgsql SET work_mem = '1MB'; SELECT myfunc (0), current_setting('work_mem'); SET work_mem = '3MB'; -- it should roll back on error, though CREATE OR REPLACE FUNCTION myfunc (int) RETURNS text AS $$ BEGIN SET work_mem = '2MB'; PERFORM 1 / $1; RETURN current_setting('work_mem'); END $$ LANGUAGE plpgsql SET work_mem = '1MB'; SELECT myfunc (0); SELECT current_setting('work_mem'); SELECT myfunc (1), current_setting('work_mem'); -- check current_setting()'s behavior with invalid setting name SELECT current_setting('nosuch.setting'); -- FAIL SELECT current_setting('nosuch.setting', FALSE); -- FAIL SELECT current_setting('nosuch.setting', TRUE) IS NULL; -- after this, all three cases should yield 'nada' SET nosuch.setting = 'nada'; SELECT current_setting('nosuch.setting'); SELECT current_setting('nosuch.setting', FALSE); SELECT current_setting('nosuch.setting', TRUE); -- Normally, CREATE FUNCTION should complain about invalid values in -- function SET options; but not if check_function_bodies is off, -- because that creates ordering hazards for pg_dump CREATE FUNCTION func_with_bad_set () RETURNS int AS $$ SELECT 1 $$ LANGUAGE sql SET default_text_search_config = no_such_config; SET check_function_bodies = OFF; CREATE FUNCTION func_with_bad_set () RETURNS int AS $$ SELECT 1 $$ LANGUAGE sql SET default_text_search_config = no_such_config; SELECT func_with_bad_set (); RESET check_function_bodies; SET default_with_oids TO f; -- Should not allow to set it to true. SET default_with_oids TO t; pgFormatter-4.2/t/pg-test-files/expected/hash_func.sql000066400000000000000000000304061361326045100230630ustar00rootroot00000000000000-- -- Test hash functions -- -- When the salt is 0, the extended hash function should produce a result -- whose low 32 bits match the standard hash function. When the salt is -- not 0, we should get a different result. -- SELECT v AS value, hashint2(v)::bit(32) AS standard, hashint2extended(v, 0)::bit(32) AS extended0, hashint2extended(v, 1)::bit(32) AS extended1 FROM ( VALUES (0::int2), (1::int2), (17::int2), (42::int2)) x (v) WHERE hashint2(v)::bit(32) != hashint2extended(v, 0)::bit(32) OR hashint2(v)::bit(32) = hashint2extended(v, 1)::bit(32); SELECT v AS value, hashint4(v)::bit(32) AS standard, hashint4extended(v, 0)::bit(32) AS extended0, hashint4extended(v, 1)::bit(32) AS extended1 FROM ( VALUES (0), (1), (17), (42), (550273), (207112489)) x (v) WHERE hashint4(v)::bit(32) != hashint4extended(v, 0)::bit(32) OR hashint4(v)::bit(32) = hashint4extended(v, 1)::bit(32); SELECT v AS value, hashint8(v)::bit(32) AS standard, hashint8extended(v, 0)::bit(32) AS extended0, hashint8extended(v, 1)::bit(32) AS extended1 FROM ( VALUES (0), (1), (17), (42), (550273), (207112489)) x (v) WHERE hashint8(v)::bit(32) != hashint8extended(v, 0)::bit(32) OR hashint8(v)::bit(32) = hashint8extended(v, 1)::bit(32); SELECT v AS value, hashfloat4(v)::bit(32) AS standard, hashfloat4extended(v, 0)::bit(32) AS extended0, hashfloat4extended(v, 1)::bit(32) AS extended1 FROM ( VALUES (0), (1), (17), (42), (550273), (207112489)) x (v) WHERE hashfloat4(v)::bit(32) != hashfloat4extended(v, 0)::bit(32) OR hashfloat4(v)::bit(32) = hashfloat4extended(v, 1)::bit(32); SELECT v AS value, hashfloat8(v)::bit(32) AS standard, hashfloat8extended(v, 0)::bit(32) AS extended0, hashfloat8extended(v, 1)::bit(32) AS extended1 FROM ( VALUES (0), (1), (17), (42), (550273), (207112489)) x (v) WHERE hashfloat8(v)::bit(32) != hashfloat8extended(v, 0)::bit(32) OR hashfloat8(v)::bit(32) = hashfloat8extended(v, 1)::bit(32); SELECT v AS value, hashoid(v)::bit(32) AS standard, hashoidextended(v, 0)::bit(32) AS extended0, hashoidextended(v, 1)::bit(32) AS extended1 FROM ( VALUES (0), (1), (17), (42), (550273), (207112489)) x (v) WHERE hashoid(v)::bit(32) != hashoidextended(v, 0)::bit(32) OR hashoid(v)::bit(32) = hashoidextended(v, 1)::bit(32); SELECT v AS value, hashchar(v)::bit(32) AS standard, hashcharextended(v, 0)::bit(32) AS extended0, hashcharextended(v, 1)::bit(32) AS extended1 FROM ( VALUES (NULL "char"), ('1'), ('x'), ('X'), ('p'), ('N')) x (v) WHERE hashchar(v)::bit(32) != hashcharextended(v, 0)::bit(32) OR hashchar(v)::bit(32) = hashcharextended(v, 1)::bit(32); SELECT v AS value, hashname(v)::bit(32) AS standard, hashnameextended(v, 0)::bit(32) AS extended0, hashnameextended(v, 1)::bit(32) AS extended1 FROM ( VALUES (NULL), ('PostgreSQL'), ('eIpUEtqmY89'), ('AXKEJBTK'), ('muop28x03'), ('yi3nm0d73')) x (v) WHERE hashname(v)::bit(32) != hashnameextended(v, 0)::bit(32) OR hashname(v)::bit(32) = hashnameextended(v, 1)::bit(32); SELECT v AS value, hashtext(v)::bit(32) AS standard, hashtextextended(v, 0)::bit(32) AS extended0, hashtextextended(v, 1)::bit(32) AS extended1 FROM ( VALUES (NULL), ('PostgreSQL'), ('eIpUEtqmY89'), ('AXKEJBTK'), ('muop28x03'), ('yi3nm0d73')) x (v) WHERE hashtext(v)::bit(32) != hashtextextended(v, 0)::bit(32) OR hashtext(v)::bit(32) = hashtextextended(v, 1)::bit(32); SELECT v AS value, hashoidvector(v)::bit(32) AS standard, hashoidvectorextended(v, 0)::bit(32) AS extended0, hashoidvectorextended(v, 1)::bit(32) AS extended1 FROM ( VALUES (NULL::oidvector), ('0 1 2 3 4'), ('17 18 19 20'), ('42 43 42 45'), ('550273 550273 570274'), ('207112489 207112499 21512 2155 372325 1363252')) x (v) WHERE hashoidvector(v)::bit(32) != hashoidvectorextended(v, 0)::bit(32) OR hashoidvector(v)::bit(32) = hashoidvectorextended(v, 1)::bit(32); SELECT v AS value, hash_aclitem(v)::bit(32) AS standard, hash_aclitem_extended(v, 0)::bit(32) AS extended0, hash_aclitem_extended(v, 1)::bit(32) AS extended1 FROM ( SELECT DISTINCT (relacl[1]) FROM pg_class LIMIT 10) x (v) WHERE hash_aclitem(v)::bit(32) != hash_aclitem_extended(v, 0)::bit(32) OR hash_aclitem(v)::bit(32) = hash_aclitem_extended(v, 1)::bit(32); SELECT v AS value, hashmacaddr(v)::bit(32) AS standard, hashmacaddrextended(v, 0)::bit(32) AS extended0, hashmacaddrextended(v, 1)::bit(32) AS extended1 FROM ( VALUES (NULL::macaddr), ('08:00:2b:01:02:04'), ('08:00:2b:01:02:04'), ('e2:7f:51:3e:70:49'), ('d6:a9:4a:78:1c:d5'), ('ea:29:b1:5e:1f:a5')) x (v) WHERE hashmacaddr(v)::bit(32) != hashmacaddrextended(v, 0)::bit(32) OR hashmacaddr(v)::bit(32) = hashmacaddrextended(v, 1)::bit(32); SELECT v AS value, hashinet(v)::bit(32) AS standard, hashinetextended(v, 0)::bit(32) AS extended0, hashinetextended(v, 1)::bit(32) AS extended1 FROM ( VALUES (NULL::inet), ('192.168.100.128/25'), ('192.168.100.0/8'), ('172.168.10.126/16'), ('172.18.103.126/24'), ('192.188.13.16/32')) x (v) WHERE hashinet(v)::bit(32) != hashinetextended(v, 0)::bit(32) OR hashinet(v)::bit(32) = hashinetextended(v, 1)::bit(32); SELECT v AS value, hash_numeric(v)::bit(32) AS standard, hash_numeric_extended(v, 0)::bit(32) AS extended0, hash_numeric_extended(v, 1)::bit(32) AS extended1 FROM ( VALUES (0), (1.149484958), (17.149484958), (42.149484958), (149484958.550273), (2071124898672)) x (v) WHERE hash_numeric(v)::bit(32) != hash_numeric_extended(v, 0)::bit(32) OR hash_numeric(v)::bit(32) = hash_numeric_extended(v, 1)::bit(32); SELECT v AS value, hashmacaddr8 (v)::bit(32) AS standard, hashmacaddr8extended(v, 0)::bit(32) AS extended0, hashmacaddr8extended(v, 1)::bit(32) AS extended1 FROM ( VALUES (NULL::macaddr8), ('08:00:2b:01:02:04:36:49'), ('08:00:2b:01:02:04:f0:e8'), ('e2:7f:51:3e:70:49:16:29'), ('d6:a9:4a:78:1c:d5:47:32'), ('ea:29:b1:5e:1f:a5')) x (v) WHERE hashmacaddr8 (v)::bit(32) != hashmacaddr8extended(v, 0)::bit(32) OR hashmacaddr8 (v)::bit(32) = hashmacaddr8extended(v, 1)::bit(32); SELECT v AS value, hash_array(v)::bit(32) AS standard, hash_array_extended(v, 0)::bit(32) AS extended0, hash_array_extended(v, 1)::bit(32) AS extended1 FROM ( VALUES ('{0}'::int4[]), ('{0,1,2,3,4}'), ('{17,18,19,20}'), ('{42,34,65,98}'), ('{550273,590027, 870273}'), ('{207112489, 807112489}')) x (v) WHERE hash_array(v)::bit(32) != hash_array_extended(v, 0)::bit(32) OR hash_array(v)::bit(32) = hash_array_extended(v, 1)::bit(32); SELECT v AS value, hashbpchar(v)::bit(32) AS standard, hashbpcharextended(v, 0)::bit(32) AS extended0, hashbpcharextended(v, 1)::bit(32) AS extended1 FROM ( VALUES (NULL), ('PostgreSQL'), ('eIpUEtqmY89'), ('AXKEJBTK'), ('muop28x03'), ('yi3nm0d73')) x (v) WHERE hashbpchar(v)::bit(32) != hashbpcharextended(v, 0)::bit(32) OR hashbpchar(v)::bit(32) = hashbpcharextended(v, 1)::bit(32); SELECT v AS value, time_hash(v)::bit(32) AS standard, time_hash_extended(v, 0)::bit(32) AS extended0, time_hash_extended(v, 1)::bit(32) AS extended1 FROM ( VALUES (NULL::time), ('11:09:59'), ('1:09:59'), ('11:59:59'), ('7:9:59'), ('5:15:59')) x (v) WHERE time_hash(v)::bit(32) != time_hash_extended(v, 0)::bit(32) OR time_hash(v)::bit(32) = time_hash_extended(v, 1)::bit(32); SELECT v AS value, timetz_hash(v)::bit(32) AS standard, timetz_hash_extended(v, 0)::bit(32) AS extended0, timetz_hash_extended(v, 1)::bit(32) AS extended1 FROM ( VALUES (NULL::timetz), ('00:11:52.518762-07'), ('00:11:52.51762-08'), ('00:11:52.62-01'), ('00:11:52.62+01'), ('11:59:59+04')) x (v) WHERE timetz_hash(v)::bit(32) != timetz_hash_extended(v, 0)::bit(32) OR timetz_hash(v)::bit(32) = timetz_hash_extended(v, 1)::bit(32); SELECT v AS value, interval_hash(v)::bit(32) AS standard, interval_hash_extended(v, 0)::bit(32) AS extended0, interval_hash_extended(v, 1)::bit(32) AS extended1 FROM ( VALUES (NULL::interval), ('5 month 7 day 46 minutes'), ('1 year 7 day 46 minutes'), ('1 year 7 month 20 day 46 minutes'), ('5 month'), ('17 year 11 month 7 day 9 hours 46 minutes 5 seconds')) x (v) WHERE interval_hash(v)::bit(32) != interval_hash_extended(v, 0)::bit(32) OR interval_hash(v)::bit(32) = interval_hash_extended(v, 1)::bit(32); SELECT v AS value, timestamp_hash(v)::bit(32) AS standard, timestamp_hash_extended(v, 0)::bit(32) AS extended0, timestamp_hash_extended(v, 1)::bit(32) AS extended1 FROM ( VALUES (NULL::timestamp), ('2017-08-22 00:09:59.518762'), ('2015-08-20 00:11:52.51762-08'), ('2017-05-22 00:11:52.62-01'), ('2013-08-22 00:11:52.62+01'), ('2013-08-22 11:59:59+04')) x (v) WHERE timestamp_hash(v)::bit(32) != timestamp_hash_extended(v, 0)::bit(32) OR timestamp_hash(v)::bit(32) = timestamp_hash_extended(v, 1)::bit(32); SELECT v AS value, uuid_hash(v)::bit(32) AS standard, uuid_hash_extended(v, 0)::bit(32) AS extended0, uuid_hash_extended(v, 1)::bit(32) AS extended1 FROM ( VALUES (NULL::uuid), ('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'), ('5a9ba4ac-8d6f-11e7-bb31-be2e44b06b34'), ('99c6705c-d939-461c-a3c9-1690ad64ed7b'), ('7deed3ca-8d6f-11e7-bb31-be2e44b06b34'), ('9ad46d4f-6f2a-4edd-aadb-745993928e1e')) x (v) WHERE uuid_hash(v)::bit(32) != uuid_hash_extended(v, 0)::bit(32) OR uuid_hash(v)::bit(32) = uuid_hash_extended(v, 1)::bit(32); SELECT v AS value, pg_lsn_hash (v)::bit(32) AS standard, pg_lsn_hash_extended(v, 0)::bit(32) AS extended0, pg_lsn_hash_extended(v, 1)::bit(32) AS extended1 FROM ( VALUES (NULL::pg_lsn), ('16/B374D84'), ('30/B374D84'), ('255/B374D84'), ('25/B379D90'), ('900/F37FD90')) x (v) WHERE pg_lsn_hash (v)::bit(32) != pg_lsn_hash_extended(v, 0)::bit(32) OR pg_lsn_hash (v)::bit(32) = pg_lsn_hash_extended(v, 1)::bit(32); CREATE TYPE mood AS ENUM ( 'sad', 'ok', 'happy' ); SELECT v AS value, hashenum(v)::bit(32) AS standard, hashenumextended(v, 0)::bit(32) AS extended0, hashenumextended(v, 1)::bit(32) AS extended1 FROM ( VALUES ('sad'::mood), ('ok'), ('happy')) x (v) WHERE hashenum(v)::bit(32) != hashenumextended(v, 0)::bit(32) OR hashenum(v)::bit(32) = hashenumextended(v, 1)::bit(32); DROP TYPE mood; SELECT v AS value, jsonb_hash (v)::bit(32) AS standard, jsonb_hash_extended(v, 0)::bit(32) AS extended0, jsonb_hash_extended(v, 1)::bit(32) AS extended1 FROM ( VALUES (NULL::jsonb), ('{"a": "aaa bbb ddd ccc", "b": ["eee fff ggg"], "c": {"d": "hhh iii"}}'), ('{"foo": [true, "bar"], "tags": {"e": 1, "f": null}}'), ('{"g": {"h": "value"}}')) x (v) WHERE jsonb_hash (v)::bit(32) != jsonb_hash_extended(v, 0)::bit(32) OR jsonb_hash (v)::bit(32) = jsonb_hash_extended(v, 1)::bit(32); SELECT v AS value, hash_range(v)::bit(32) AS standard, hash_range_extended(v, 0)::bit(32) AS extended0, hash_range_extended(v, 1)::bit(32) AS extended1 FROM ( VALUES (int4range(10, 20)), (int4range(23, 43)), (int4range(5675, 550273)), (int4range(550274, 1550274)), (int4range(1550275, 208112489))) x (v) WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32) OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32); pgFormatter-4.2/t/pg-test-files/expected/hash_index.sql000066400000000000000000000116361361326045100232430ustar00rootroot00000000000000-- -- HASH_INDEX -- grep 843938989 hash.data -- SELECT * FROM hash_i4_heap WHERE hash_i4_heap.random = 843938989; -- -- hash index -- grep 66766766 hash.data -- SELECT * FROM hash_i4_heap WHERE hash_i4_heap.random = 66766766; -- -- hash index -- grep 1505703298 hash.data -- SELECT * FROM hash_name_heap WHERE hash_name_heap.random = '1505703298'::name; -- -- hash index -- grep 7777777 hash.data -- SELECT * FROM hash_name_heap WHERE hash_name_heap.random = '7777777'::name; -- -- hash index -- grep 1351610853 hash.data -- SELECT * FROM hash_txt_heap WHERE hash_txt_heap.random = '1351610853'::text; -- -- hash index -- grep 111111112222222233333333 hash.data -- SELECT * FROM hash_txt_heap WHERE hash_txt_heap.random = '111111112222222233333333'::text; -- -- hash index -- grep 444705537 hash.data -- SELECT * FROM hash_f8_heap WHERE hash_f8_heap.random = '444705537'::float8; -- -- hash index -- grep 88888888 hash.data -- SELECT * FROM hash_f8_heap WHERE hash_f8_heap.random = '88888888'::float8; -- -- hash index -- grep '^90[^0-9]' hashovfl.data -- -- SELECT count(*) AS i988 FROM hash_ovfl_heap -- WHERE x = 90; -- -- hash index -- grep '^1000[^0-9]' hashovfl.data -- -- SELECT count(*) AS i0 FROM hash_ovfl_heap -- WHERE x = 1000; -- -- HASH -- UPDATE hash_i4_heap SET random = 1 WHERE hash_i4_heap.seqno = 1492; SELECT h.seqno AS i1492, h.random AS i1 FROM hash_i4_heap h WHERE h.random = 1; UPDATE hash_i4_heap SET seqno = 20000 WHERE hash_i4_heap.random = 1492795354; SELECT h.seqno AS i20000 FROM hash_i4_heap h WHERE h.random = 1492795354; UPDATE hash_name_heap SET random = '0123456789abcdef'::name WHERE hash_name_heap.seqno = 6543; SELECT h.seqno AS i6543, h.random AS c0_to_f FROM hash_name_heap h WHERE h.random = '0123456789abcdef'::name; UPDATE hash_name_heap SET seqno = 20000 WHERE hash_name_heap.random = '76652222'::name; -- -- this is the row we just replaced; index scan should return zero rows -- SELECT h.seqno AS emptyset FROM hash_name_heap h WHERE h.random = '76652222'::name; UPDATE hash_txt_heap SET random = '0123456789abcdefghijklmnop'::text WHERE hash_txt_heap.seqno = 4002; SELECT h.seqno AS i4002, h.random AS c0_to_p FROM hash_txt_heap h WHERE h.random = '0123456789abcdefghijklmnop'::text; UPDATE hash_txt_heap SET seqno = 20000 WHERE hash_txt_heap.random = '959363399'::text; SELECT h.seqno AS t20000 FROM hash_txt_heap h WHERE h.random = '959363399'::text; UPDATE hash_f8_heap SET random = '-1234.1234'::float8 WHERE hash_f8_heap.seqno = 8906; SELECT h.seqno AS i8096, h.random AS f1234_1234 FROM hash_f8_heap h WHERE h.random = '-1234.1234'::float8; UPDATE hash_f8_heap SET seqno = 20000 WHERE hash_f8_heap.random = '488912369'::float8; SELECT h.seqno AS f20000 FROM hash_f8_heap h WHERE h.random = '488912369'::float8; -- UPDATE hash_ovfl_heap -- SET x = 1000 -- WHERE x = 90; -- this vacuums the index as well -- VACUUM hash_ovfl_heap; -- SELECT count(*) AS i0 FROM hash_ovfl_heap -- WHERE x = 90; -- SELECT count(*) AS i988 FROM hash_ovfl_heap -- WHERE x = 1000; -- -- Cause some overflow insert and splits. -- CREATE TABLE hash_split_heap ( keycol int ); INSERT INTO hash_split_heap SELECT 1 FROM generate_series(1, 500) a; CREATE INDEX hash_split_index ON hash_split_heap USING HASH (keycol); INSERT INTO hash_split_heap SELECT 1 FROM generate_series(1, 5000) a; -- Let's do a backward scan. BEGIN; SET enable_seqscan = OFF; SET enable_bitmapscan = OFF; DECLARE c CURSOR FOR SELECT * FROM hash_split_heap WHERE keycol = 1; MOVE FORWARD ALL FROM c; MOVE BACKWARD 10000 FROM c; MOVE BACKWARD ALL FROM c; CLOSE c; END; -- DELETE, INSERT, VACUUM. DELETE FROM hash_split_heap WHERE keycol = 1; INSERT INTO hash_split_heap SELECT a / 2 FROM generate_series(1, 25000) a; VACUUM hash_split_heap; -- Rebuild the index using a different fillfactor ALTER INDEX hash_split_index SET (fillfactor = 10); REINDEX INDEX hash_split_index; -- Clean up. DROP TABLE hash_split_heap; -- Index on temp table. CREATE TEMP TABLE hash_temp_heap ( x int, y int ); INSERT INTO hash_temp_heap VALUES (1, 1); CREATE INDEX hash_idx ON hash_temp_heap USING HASH (x); DROP TABLE hash_temp_heap CASCADE; -- Float4 type. CREATE TABLE hash_heap_float4 ( x float4, y int ); INSERT INTO hash_heap_float4 VALUES (1.1, 1); CREATE INDEX hash_idx ON hash_heap_float4 USING HASH (x); DROP TABLE hash_heap_float4 CASCADE; -- Test out-of-range fillfactor values CREATE INDEX hash_f8_index2 ON hash_f8_heap USING HASH (random float8_ops) WITH (fillfactor = 9); CREATE INDEX hash_f8_index2 ON hash_f8_heap USING HASH (random float8_ops) WITH (fillfactor = 101); pgFormatter-4.2/t/pg-test-files/expected/hash_part.sql000066400000000000000000000072351361326045100231020ustar00rootroot00000000000000-- -- Hash partitioning. -- -- Use hand-rolled hash functions and operator classes to get predictable -- result on different matchines. See the definitions of -- part_part_test_int4_ops and part_test_text_ops in insert.sql. CREATE TABLE mchash ( a int, b text, c jsonb ) PARTITION BY HASH (a part_test_int4_ops, b part_test_text_ops); CREATE TABLE mchash1 PARTITION OF mchash FOR VALUES WITH (MODULUS 4, REMAINDER 0); -- invalid OID, no such table SELECT satisfies_hash_partition (0, 4, 0, NULL); -- not partitioned SELECT satisfies_hash_partition ('tenk1'::regclass, 4, 0, NULL); -- partition rather than the parent SELECT satisfies_hash_partition ('mchash1'::regclass, 4, 0, NULL); -- invalid modulus SELECT satisfies_hash_partition ('mchash'::regclass, 0, 0, NULL); -- remainder too small SELECT satisfies_hash_partition ('mchash'::regclass, 1, - 1, NULL); -- remainder too large SELECT satisfies_hash_partition ('mchash'::regclass, 1, 1, NULL); -- modulus is null SELECT satisfies_hash_partition ('mchash'::regclass, NULL, 0, NULL); -- remainder is null SELECT satisfies_hash_partition ('mchash'::regclass, 4, NULL, NULL); -- too many arguments SELECT satisfies_hash_partition ('mchash'::regclass, 4, 0, NULL::int, NULL::text, NULL::json); -- too few arguments SELECT satisfies_hash_partition ('mchash'::regclass, 3, 1, NULL::int); -- wrong argument type SELECT satisfies_hash_partition ('mchash'::regclass, 2, 1, NULL::int, NULL::int); -- ok, should be false SELECT satisfies_hash_partition ('mchash'::regclass, 4, 0, 0, ''::text); -- ok, should be true SELECT satisfies_hash_partition ('mchash'::regclass, 4, 0, 2, ''::text); -- argument via variadic syntax, should fail because not all partitioning -- columns are of the correct type SELECT satisfies_hash_partition ('mchash'::regclass, 2, 1, VARIADIC ARRAY[1, 2]::int[]); -- multiple partitioning columns of the same type CREATE TABLE mcinthash ( a int, b int, c jsonb ) PARTITION BY HASH (a part_test_int4_ops, b part_test_int4_ops); -- now variadic should work, should be false SELECT satisfies_hash_partition ('mcinthash'::regclass, 4, 0, VARIADIC ARRAY[0, 0]); -- should be true SELECT satisfies_hash_partition ('mcinthash'::regclass, 4, 0, VARIADIC ARRAY[0, 1]); -- wrong length SELECT satisfies_hash_partition ('mcinthash'::regclass, 4, 0, VARIADIC ARRAY[]::int[]); -- wrong type SELECT satisfies_hash_partition ('mcinthash'::regclass, 4, 0, VARIADIC ARRAY[now(), now()]); -- check satisfies_hash_partition passes correct collation CREATE TABLE text_hashp ( a text ) PARTITION BY HASH (a); CREATE TABLE text_hashp0 PARTITION OF text_hashp FOR VALUES WITH (MODULUS 2, REMAINDER 0); CREATE TABLE text_hashp1 PARTITION OF text_hashp FOR VALUES WITH (MODULUS 2, REMAINDER 1); -- The result here should always be true, because 'xxx' must belong to -- one of the two defined partitions SELECT satisfies_hash_partition ('text_hashp'::regclass, 2, 0, 'xxx'::text) OR satisfies_hash_partition ('text_hashp'::regclass, 2, 1, 'xxx'::text) AS satisfies; -- cleanup DROP TABLE mchash; DROP TABLE mcinthash; DROP TABLE text_hashp; pgFormatter-4.2/t/pg-test-files/expected/horology.sql000066400000000000000000000533041361326045100227710ustar00rootroot00000000000000-- -- HOROLOGY -- SET DateStyle = 'Postgres, MDY'; -- -- Test various input formats -- SELECT timestamp WITH time zone '20011227 040506+08'; SELECT timestamp WITH time zone '20011227 040506-08'; SELECT timestamp WITH time zone '20011227 040506.789+08'; SELECT timestamp WITH time zone '20011227 040506.789-08'; SELECT timestamp WITH time zone '20011227T040506+08'; SELECT timestamp WITH time zone '20011227T040506-08'; SELECT timestamp WITH time zone '20011227T040506.789+08'; SELECT timestamp WITH time zone '20011227T040506.789-08'; SELECT timestamp WITH time zone '2001-12-27 04:05:06.789-08'; SELECT timestamp WITH time zone '2001.12.27 04:05:06.789-08'; SELECT timestamp WITH time zone '2001/12/27 04:05:06.789-08'; SELECT timestamp WITH time zone '12/27/2001 04:05:06.789-08'; -- should fail in mdy mode: SELECT timestamp WITH time zone '27/12/2001 04:05:06.789-08'; SET datestyle TO dmy; SELECT timestamp WITH time zone '27/12/2001 04:05:06.789-08'; RESET datestyle; SELECT timestamp WITH time zone 'Y2001M12D27H04M05S06.789+08'; SELECT timestamp WITH time zone 'Y2001M12D27H04M05S06.789-08'; SELECT timestamp WITH time zone 'Y2001M12D27H04MM05S06.789+08'; SELECT timestamp WITH time zone 'Y2001M12D27H04MM05S06.789-08'; SELECT timestamp WITH time zone 'J2452271+08'; SELECT timestamp WITH time zone 'J2452271-08'; SELECT timestamp WITH time zone 'J2452271.5+08'; SELECT timestamp WITH time zone 'J2452271.5-08'; SELECT timestamp WITH time zone 'J2452271 04:05:06+08'; SELECT timestamp WITH time zone 'J2452271 04:05:06-08'; SELECT timestamp WITH time zone 'J2452271T040506+08'; SELECT timestamp WITH time zone 'J2452271T040506-08'; SELECT timestamp WITH time zone 'J2452271T040506.789+08'; SELECT timestamp WITH time zone 'J2452271T040506.789-08'; -- German/European-style dates with periods as delimiters SELECT timestamp WITH time zone '12.27.2001 04:05:06.789+08'; SELECT timestamp WITH time zone '12.27.2001 04:05:06.789-08'; SET DateStyle = 'German'; SELECT timestamp WITH time zone '27.12.2001 04:05:06.789+08'; SELECT timestamp WITH time zone '27.12.2001 04:05:06.789-08'; SET DateStyle = 'ISO'; -- As of 7.4, allow time without time zone having a time zone specified SELECT time without time zone '040506.789+08'; SELECT time without time zone '040506.789-08'; SELECT time without time zone 'T040506.789+08'; SELECT time without time zone 'T040506.789-08'; SELECT time WITH time zone '040506.789+08'; SELECT time WITH time zone '040506.789-08'; SELECT time WITH time zone 'T040506.789+08'; SELECT time WITH time zone 'T040506.789-08'; SELECT time WITH time zone 'T040506.789 +08'; SELECT time WITH time zone 'T040506.789 -08'; SET DateStyle = 'Postgres, MDY'; -- Check Julian dates BC SELECT date 'J1520447' AS "Confucius' Birthday"; SELECT date 'J0' AS "Julian Epoch"; -- -- date, time arithmetic -- SELECT date '1981-02-03' + time '04:05:06' AS "Date + Time"; SELECT date '1991-02-03' + time WITH time zone '04:05:06 PST' AS "Date + Time PST"; SELECT date '2001-02-03' + time WITH time zone '04:05:06 UTC' AS "Date + Time UTC"; SELECT date '1991-02-03' + interval '2 years' AS "Add Two Years"; SELECT date '2001-12-13' - interval '2 years' AS "Subtract Two Years"; -- subtract time from date should not make sense; use interval instead SELECT date '1991-02-03' - time '04:05:06' AS "Subtract Time"; SELECT date '1991-02-03' - time WITH time zone '04:05:06 UTC' AS "Subtract Time UTC"; -- -- timestamp, interval arithmetic -- SELECT timestamp without time zone '1996-03-01' - interval '1 second' AS "Feb 29"; SELECT timestamp without time zone '1999-03-01' - interval '1 second' AS "Feb 28"; SELECT timestamp without time zone '2000-03-01' - interval '1 second' AS "Feb 29"; SELECT timestamp without time zone '1999-12-01' + interval '1 month - 1 second' AS "Dec 31"; SELECT timestamp without time zone 'Jan 1, 4713 BC' + interval '106000000 days' AS "Feb 23, 285506"; SELECT timestamp without time zone 'Jan 1, 4713 BC' + interval '107000000 days' AS "Jan 20, 288244"; SELECT timestamp without time zone 'Jan 1, 4713 BC' + interval '109203489 days' AS "Dec 31, 294276"; SELECT timestamp without time zone '12/31/294276' - timestamp without time zone '12/23/1999' AS "106751991 Days"; -- Shorthand values -- Not directly usable for regression testing since these are not constants. -- So, just try to test parser and hope for the best - thomas 97/04/26 SELECT (timestamp without time zone 'today' = (timestamp without time zone 'yesterday' + interval '1 day')) AS "True"; SELECT (timestamp without time zone 'today' = (timestamp without time zone 'tomorrow' - interval '1 day')) AS "True"; SELECT (timestamp without time zone 'today 10:30' = (timestamp without time zone 'yesterday' + interval '1 day 10 hr 30 min')) AS "True"; SELECT (timestamp without time zone '10:30 today' = (timestamp without time zone 'yesterday' + interval '1 day 10 hr 30 min')) AS "True"; SELECT (timestamp without time zone 'tomorrow' = (timestamp without time zone 'yesterday' + interval '2 days')) AS "True"; SELECT (timestamp without time zone 'tomorrow 16:00:00' = (timestamp without time zone 'today' + interval '1 day 16 hours')) AS "True"; SELECT (timestamp without time zone '16:00:00 tomorrow' = (timestamp without time zone 'today' + interval '1 day 16 hours')) AS "True"; SELECT (timestamp without time zone 'yesterday 12:34:56' = (timestamp without time zone 'tomorrow' - interval '2 days - 12:34:56')) AS "True"; SELECT (timestamp without time zone '12:34:56 yesterday' = (timestamp without time zone 'tomorrow' - interval '2 days - 12:34:56')) AS "True"; SELECT (timestamp without time zone 'tomorrow' > 'now') AS "True"; -- Convert from date and time to timestamp -- This test used to be timestamp(date,time) but no longer allowed by grammar -- to enable support for SQL99 timestamp type syntax. SELECT date '1994-01-01' + time '11:00' AS "Jan_01_1994_11am"; SELECT date '1994-01-01' + time '10:00' AS "Jan_01_1994_10am"; SELECT date '1994-01-01' + timetz '11:00-5' AS "Jan_01_1994_8am"; SELECT timestamptz(date '1994-01-01', time WITH time zone '11:00-5') AS "Jan_01_1994_8am"; SELECT '' AS "64", d1 + interval '1 year' AS one_year FROM TIMESTAMP_TBL; SELECT '' AS "64", d1 - interval '1 year' AS one_year FROM TIMESTAMP_TBL; SELECT timestamp WITH time zone '1996-03-01' - interval '1 second' AS "Feb 29"; SELECT timestamp WITH time zone '1999-03-01' - interval '1 second' AS "Feb 28"; SELECT timestamp WITH time zone '2000-03-01' - interval '1 second' AS "Feb 29"; SELECT timestamp WITH time zone '1999-12-01' + interval '1 month - 1 second' AS "Dec 31"; SELECT (timestamp WITH time zone 'today' = (timestamp WITH time zone 'yesterday' + interval '1 day')) AS "True"; SELECT (timestamp WITH time zone 'today' = (timestamp WITH time zone 'tomorrow' - interval '1 day')) AS "True"; SELECT (timestamp WITH time zone 'tomorrow' = (timestamp WITH time zone 'yesterday' + interval '2 days')) AS "True"; SELECT (timestamp WITH time zone 'tomorrow' > 'now') AS "True"; -- timestamp with time zone, interval arithmetic around DST change SET TIME ZONE 'CST7CDT'; SELECT timestamp WITH time zone '2005-04-02 12:00-07' + interval '1 day' AS "Apr 3, 12:00"; SELECT timestamp WITH time zone '2005-04-02 12:00-07' + interval '24 hours' AS "Apr 3, 13:00"; SELECT timestamp WITH time zone '2005-04-03 12:00-06' - interval '1 day' AS "Apr 2, 12:00"; SELECT timestamp WITH time zone '2005-04-03 12:00-06' - interval '24 hours' AS "Apr 2, 11:00"; RESET time zone; SELECT timestamptz(date '1994-01-01', time '11:00') AS "Jan_01_1994_10am"; SELECT timestamptz(date '1994-01-01', time '10:00') AS "Jan_01_1994_9am"; SELECT timestamptz(date '1994-01-01', time WITH time zone '11:00-8') AS "Jan_01_1994_11am"; SELECT timestamptz(date '1994-01-01', time WITH time zone '10:00-8') AS "Jan_01_1994_10am"; SELECT timestamptz(date '1994-01-01', time WITH time zone '11:00-5') AS "Jan_01_1994_8am"; SELECT '' AS "64", d1 + interval '1 year' AS one_year FROM TIMESTAMPTZ_TBL; SELECT '' AS "64", d1 - interval '1 year' AS one_year FROM TIMESTAMPTZ_TBL; -- -- time, interval arithmetic -- SELECT CAST(time '01:02' AS interval) AS "+01:02"; SELECT CAST(interval '02:03' AS time) AS "02:03:00"; SELECT time '01:30' + interval '02:01' AS "03:31:00"; SELECT time '01:30' - interval '02:01' AS "23:29:00"; SELECT time '02:30' + interval '36:01' AS "14:31:00"; SELECT time '03:30' + interval '1 month 04:01' AS "07:31:00"; SELECT CAST(time WITH time zone '01:02-08' AS interval) AS "+00:01"; SELECT CAST(interval '02:03' AS time WITH time zone) AS "02:03:00-08"; SELECT time WITH time zone '01:30-08' - interval '02:01' AS "23:29:00-08"; SELECT time WITH time zone '02:30-08' + interval '36:01' AS "14:31:00-08"; -- These two tests cannot be used because they default to current timezone, -- which may be either -08 or -07 depending on the time of year. -- SELECT time with time zone '01:30' + interval '02:01' AS "03:31:00-08"; -- SELECT time with time zone '03:30' + interval '1 month 04:01' AS "07:31:00-08"; -- Try the following two tests instead, as a poor substitute SELECT CAST(CAST(date 'today' + time WITH time zone '05:30' + interval '02:01' AS time WITH time zone) AS time) AS "07:31:00"; SELECT CAST(cast(date 'today' + time WITH time zone '03:30' + interval '1 month 04:01' AS timestamp without time zone) AS time) AS "07:31:00"; SELECT t.d1 AS t, i.f1 AS i, t.d1 + i.f1 AS "add", t.d1 - i.f1 AS "subtract" FROM TIMESTAMP_TBL t, INTERVAL_TBL i WHERE t.d1 BETWEEN '1990-01-01' AND '2001-01-01' AND i.f1 BETWEEN '00:00' AND '23:00' ORDER BY 1, 2; SELECT t.f1 AS t, i.f1 AS i, t.f1 + i.f1 AS "add", t.f1 - i.f1 AS "subtract" FROM TIME_TBL t, INTERVAL_TBL i ORDER BY 1, 2; SELECT t.f1 AS t, i.f1 AS i, t.f1 + i.f1 AS "add", t.f1 - i.f1 AS "subtract" FROM TIMETZ_TBL t, INTERVAL_TBL i ORDER BY 1, 2; -- SQL9x OVERLAPS operator -- test with time zone SELECT (timestamp WITH time zone '2000-11-27', timestamp WITH time zone '2000-11-28') OVERLAPS(timestamp WITH time zone '2000-11-27 12:00', timestamp WITH time zone '2000-11-30') AS "True"; SELECT (timestamp WITH time zone '2000-11-26', timestamp WITH time zone '2000-11-27') OVERLAPS(timestamp WITH time zone '2000-11-27 12:00', timestamp WITH time zone '2000-11-30') AS "False"; SELECT (timestamp WITH time zone '2000-11-27', timestamp WITH time zone '2000-11-28') OVERLAPS(timestamp WITH time zone '2000-11-27 12:00', interval '1 day') AS "True"; SELECT (timestamp WITH time zone '2000-11-27', interval '12 hours') OVERLAPS(timestamp WITH time zone '2000-11-27 12:00', timestamp WITH time zone '2000-11-30') AS "False"; SELECT (timestamp WITH time zone '2000-11-27', interval '12 hours') OVERLAPS(timestamp WITH time zone '2000-11-27', interval '12 hours') AS "True"; SELECT (timestamp WITH time zone '2000-11-27', interval '12 hours') OVERLAPS(timestamp WITH time zone '2000-11-27 12:00', interval '12 hours') AS "False"; -- test without time zone SELECT (timestamp without time zone '2000-11-27', timestamp without time zone '2000-11-28') OVERLAPS(timestamp without time zone '2000-11-27 12:00', timestamp without time zone '2000-11-30') AS "True"; SELECT (timestamp without time zone '2000-11-26', timestamp without time zone '2000-11-27') OVERLAPS(timestamp without time zone '2000-11-27 12:00', timestamp without time zone '2000-11-30') AS "False"; SELECT (timestamp without time zone '2000-11-27', timestamp without time zone '2000-11-28') OVERLAPS(timestamp without time zone '2000-11-27 12:00', interval '1 day') AS "True"; SELECT (timestamp without time zone '2000-11-27', interval '12 hours') OVERLAPS(timestamp without time zone '2000-11-27 12:00', timestamp without time zone '2000-11-30') AS "False"; SELECT (timestamp without time zone '2000-11-27', interval '12 hours') OVERLAPS(timestamp without time zone '2000-11-27', interval '12 hours') AS "True"; SELECT (timestamp without time zone '2000-11-27', interval '12 hours') OVERLAPS(timestamp without time zone '2000-11-27 12:00', interval '12 hours') AS "False"; -- test time and interval SELECT (time '00:00', time '01:00') OVERLAPS(time '00:30', time '01:30') AS "True"; SELECT (time '00:00', interval '1 hour') OVERLAPS(time '00:30', interval '1 hour') AS "True"; SELECT (time '00:00', interval '1 hour') OVERLAPS(time '01:30', interval '1 hour') AS "False"; -- SQL99 seems to want this to be false (and we conform to the spec). -- istm that this *should* return true, on the theory that time -- intervals can wrap around the day boundary - thomas 2001-09-25 SELECT (time '00:00', interval '1 hour') OVERLAPS(time '01:30', interval '1 day') AS "False"; CREATE TABLE TEMP_TIMESTAMP ( f1 timestamp with time zone ); -- get some candidate input values INSERT INTO TEMP_TIMESTAMP (f1) SELECT d1 FROM TIMESTAMP_TBL WHERE d1 BETWEEN '13-jun-1957' AND '1-jan-1997' OR d1 BETWEEN '1-jan-1999' AND '1-jan-2010'; SELECT '' AS "16", f1 AS "timestamp" FROM TEMP_TIMESTAMP ORDER BY "timestamp"; SELECT '' AS "160", d.f1 AS "timestamp", t.f1 AS "interval", d.f1 + t.f1 AS plus FROM TEMP_TIMESTAMP d, INTERVAL_TBL t ORDER BY plus, "timestamp", "interval"; SELECT '' AS "160", d.f1 AS "timestamp", t.f1 AS "interval", d.f1 - t.f1 AS minus FROM TEMP_TIMESTAMP d, INTERVAL_TBL t WHERE isfinite(d.f1) ORDER BY minus, "timestamp", "interval"; SELECT '' AS "16", d.f1 AS "timestamp", timestamp WITH time zone '1980-01-06 00:00 GMT' AS gpstime_zero, d.f1 - timestamp WITH time zone '1980-01-06 00:00 GMT' AS difference FROM TEMP_TIMESTAMP d ORDER BY difference; SELECT '' AS "226", d1.f1 AS timestamp1, d2.f1 AS timestamp2, d1.f1 - d2.f1 AS difference FROM TEMP_TIMESTAMP d1, TEMP_TIMESTAMP d2 ORDER BY timestamp1, timestamp2, difference; -- -- Conversions -- SELECT '' AS "16", f1 AS "timestamp", date(f1) AS date FROM TEMP_TIMESTAMP WHERE f1 <> timestamp 'now' ORDER BY date, "timestamp"; DROP TABLE TEMP_TIMESTAMP; -- -- Formats -- SET DateStyle TO 'US,Postgres'; SHOW DateStyle; SELECT '' AS "64", d1 AS us_postgres FROM TIMESTAMP_TBL; SET DateStyle TO 'US,ISO'; SELECT '' AS "64", d1 AS us_iso FROM TIMESTAMP_TBL; SET DateStyle TO 'US,SQL'; SHOW DateStyle; SELECT '' AS "64", d1 AS us_sql FROM TIMESTAMP_TBL; SET DateStyle TO 'European,Postgres'; SHOW DateStyle; INSERT INTO TIMESTAMP_TBL VALUES ('13/06/1957'); SELECT count(*) AS one FROM TIMESTAMP_TBL WHERE d1 = 'Jun 13 1957'; SELECT '' AS "65", d1 AS european_postgres FROM TIMESTAMP_TBL; SET DateStyle TO 'European,ISO'; SHOW DateStyle; SELECT '' AS "65", d1 AS european_iso FROM TIMESTAMP_TBL; SET DateStyle TO 'European,SQL'; SHOW DateStyle; SELECT '' AS "65", d1 AS european_sql FROM TIMESTAMP_TBL; RESET DateStyle; -- -- to_timestamp() -- SELECT to_timestamp( '0097/Feb/16 --> 08:14:30', 'YYYY/Mon/DD --> HH:MI:SS'); SELECT to_timestamp('97/2/16 8:14:30', 'FMYYYY/FMMM/FMDD FMHH:FMMI:FMSS'); SELECT to_timestamp('2011$03!18 23_38_15', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('1985 January 12', 'YYYY FMMonth DD'); SELECT to_timestamp('1985 FMMonth 12', 'YYYY "FMMonth" DD'); SELECT to_timestamp('1985 \ 12', 'YYYY \\ DD'); SELECT to_timestamp('My birthday-> Year: 1976, Month: May, Day: 16', '"My birthday-> Year:" YYYY, "Month:" FMMonth, "Day:" DD'); SELECT to_timestamp('1,582nd VIII 21', 'Y,YYYth FMRM DD'); SELECT to_timestamp('15 "text between quote marks" 98 54 45', E'HH24 "\\"text between quote marks\\"" YY MI SS'); SELECT to_timestamp('05121445482000', 'MMDDHH24MISSYYYY'); SELECT to_timestamp('2000January09Sunday', 'YYYYFMMonthDDFMDay'); SELECT to_timestamp('97/Feb/16', 'YYMonDD'); SELECT to_timestamp('97/Feb/16', 'YY:Mon:DD'); SELECT to_timestamp('97/Feb/16', 'FXYY:Mon:DD'); SELECT to_timestamp('97/Feb/16', 'FXYY/Mon/DD'); SELECT to_timestamp('19971116', 'YYYYMMDD'); SELECT to_timestamp('20000-1116', 'YYYY-MMDD'); SELECT to_timestamp('1997 AD 11 16', 'YYYY BC MM DD'); SELECT to_timestamp('1997 BC 11 16', 'YYYY BC MM DD'); SELECT to_timestamp('9-1116', 'Y-MMDD'); SELECT to_timestamp('95-1116', 'YY-MMDD'); SELECT to_timestamp('995-1116', 'YYY-MMDD'); SELECT to_timestamp('2005426', 'YYYYWWD'); SELECT to_timestamp('2005300', 'YYYYDDD'); SELECT to_timestamp('2005527', 'IYYYIWID'); SELECT to_timestamp('005527', 'IYYIWID'); SELECT to_timestamp('05527', 'IYIWID'); SELECT to_timestamp('5527', 'IIWID'); SELECT to_timestamp('2005364', 'IYYYIDDD'); SELECT to_timestamp('20050302', 'YYYYMMDD'); SELECT to_timestamp('2005 03 02', 'YYYYMMDD'); SELECT to_timestamp(' 2005 03 02', 'YYYYMMDD'); SELECT to_timestamp(' 20050302', 'YYYYMMDD'); SELECT to_timestamp('2011-12-18 11:38 AM', 'YYYY-MM-DD HH12:MI PM'); SELECT to_timestamp('2011-12-18 11:38 PM', 'YYYY-MM-DD HH12:MI PM'); SELECT to_timestamp('2011-12-18 11:38 +05', 'YYYY-MM-DD HH12:MI TZH'); SELECT to_timestamp('2011-12-18 11:38 -05', 'YYYY-MM-DD HH12:MI TZH'); SELECT to_timestamp('2011-12-18 11:38 +05:20', 'YYYY-MM-DD HH12:MI TZH:TZM'); SELECT to_timestamp('2011-12-18 11:38 -05:20', 'YYYY-MM-DD HH12:MI TZH:TZM'); SELECT to_timestamp('2011-12-18 11:38 20', 'YYYY-MM-DD HH12:MI TZM'); -- -- Check handling of multiple spaces in format and/or input -- SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2000+ JUN', 'YYYY/MON'); SELECT to_timestamp(' 2000 +JUN', 'YYYY/MON'); SELECT to_timestamp(' 2000 +JUN', 'YYYY//MON'); SELECT to_timestamp('2000 +JUN', 'YYYY//MON'); SELECT to_timestamp('2000 + JUN', 'YYYY MON'); SELECT to_timestamp('2000 ++ JUN', 'YYYY MON'); SELECT to_timestamp('2000 + + JUN', 'YYYY MON'); SELECT to_timestamp('2000 + + JUN', 'YYYY MON'); SELECT to_timestamp('2000 -10', 'YYYY TZH'); SELECT to_timestamp('2000 -10', 'YYYY TZH'); SELECT to_date('2011 12 18', 'YYYY MM DD'); SELECT to_date('2011 12 18', 'YYYY MM DD'); SELECT to_date('2011 12 18', 'YYYY MM DD'); SELECT to_date('2011 12 18', 'YYYY MM DD'); SELECT to_date('2011 12 18', 'YYYY MM DD'); SELECT to_date('2011 12 18', 'YYYY MM DD'); SELECT to_date('2011 12 18', 'YYYYxMMxDD'); SELECT to_date('2011x 12x 18', 'YYYYxMMxDD'); SELECT to_date('2011 x12 x18', 'YYYYxMMxDD'); -- -- Check errors for some incorrect usages of to_timestamp() and to_date() -- -- Mixture of date conventions (ISO week and Gregorian): SELECT to_timestamp('2005527', 'YYYYIWID'); -- Insufficient characters in the source string: SELECT to_timestamp('19971', 'YYYYMMDD'); -- Insufficient digit characters for a single node: SELECT to_timestamp('19971)24', 'YYYYMMDD'); -- Value clobbering: SELECT to_timestamp('1997-11-Jan-16', 'YYYY-MM-Mon-DD'); -- Non-numeric input: SELECT to_timestamp('199711xy', 'YYYYMMDD'); -- Input that doesn't fit in an int: SELECT to_timestamp('10000000000', 'FMYYYY'); -- Out-of-range and not-quite-out-of-range fields: SELECT to_timestamp('2016-06-13 25:00:00', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2016-06-13 15:60:00', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2016-06-13 15:50:60', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2016-06-13 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); -- ok SELECT to_timestamp('2016-06-13 15:50:55', 'YYYY-MM-DD HH:MI:SS'); SELECT to_timestamp('2016-13-01 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2016-02-30 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2016-02-29 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); -- ok SELECT to_timestamp('2015-02-29 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2015-02-11 86000', 'YYYY-MM-DD SSSS'); -- ok SELECT to_timestamp('2015-02-11 86400', 'YYYY-MM-DD SSSS'); SELECT to_date('2016-13-10', 'YYYY-MM-DD'); SELECT to_date('2016-02-30', 'YYYY-MM-DD'); SELECT to_date('2016-02-29', 'YYYY-MM-DD'); -- ok SELECT to_date('2015-02-29', 'YYYY-MM-DD'); SELECT to_date('2015 365', 'YYYY DDD'); -- ok SELECT to_date('2015 366', 'YYYY DDD'); SELECT to_date('2016 365', 'YYYY DDD'); -- ok SELECT to_date('2016 366', 'YYYY DDD'); -- ok SELECT to_date('2016 367', 'YYYY DDD'); -- -- Check behavior with SQL-style fixed-GMT-offset time zone (cf bug #8572) -- SET TIME ZONE 'America/New_York'; SET TIME ZONE '-1.5'; SHOW time zone; SELECT '2012-12-12 12:00'::timestamptz; SELECT '2012-12-12 12:00 America/New_York'::timestamptz; SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD HH:MI:SS TZ'); RESET time zone; pgFormatter-4.2/t/pg-test-files/expected/hs_primary_extremes.sql000066400000000000000000000031121361326045100252100ustar00rootroot00000000000000-- -- Hot Standby tests -- -- hs_primary_extremes.sql -- DROP TABLE IF EXISTS hs_extreme; CREATE TABLE hs_extreme ( col1 integer ); CREATE OR REPLACE FUNCTION hs_subxids (n integer) RETURNS void LANGUAGE plpgsql AS $$ BEGIN IF n <= 0 THEN RETURN; END IF; INSERT INTO hs_extreme VALUES (n); PERFORM hs_subxids (n - 1); RETURN; EXCEPTION WHEN raise_exception THEN NULL; END; $$; BEGIN; SELECT hs_subxids (257); ROLLBACK; BEGIN; SELECT hs_subxids (257); COMMIT; SET client_min_messages = 'warning'; CREATE OR REPLACE FUNCTION hs_locks_create (n integer) RETURNS void LANGUAGE plpgsql AS $$ BEGIN IF n <= 0 THEN CHECKPOINT; RETURN; END IF; EXECUTE 'CREATE TABLE hs_locks_' || n::text || ' ()'; PERFORM hs_locks_create (n - 1); RETURN; EXCEPTION WHEN raise_exception THEN NULL; END; $$; CREATE OR REPLACE FUNCTION hs_locks_drop (n integer) RETURNS void LANGUAGE plpgsql AS $$ BEGIN IF n <= 0 THEN CHECKPOINT; RETURN; END IF; EXECUTE 'DROP TABLE IF EXISTS hs_locks_' || n::text; PERFORM hs_locks_drop (n - 1); RETURN; EXCEPTION WHEN raise_exception THEN NULL; END; $$; BEGIN; SELECT hs_locks_drop (257); SELECT hs_locks_create (257); SELECT count(*) > 257 FROM pg_locks; ROLLBACK; BEGIN; SELECT hs_locks_drop (257); SELECT hs_locks_create (257); SELECT count(*) > 257 FROM pg_locks; COMMIT; SELECT hs_locks_drop (257); SELECT pg_switch_wal (); pgFormatter-4.2/t/pg-test-files/expected/hs_primary_setup.sql000066400000000000000000000011101361326045100245100ustar00rootroot00000000000000-- -- Hot Standby tests -- -- hs_primary_setup.sql -- DROP TABLE IF EXISTS hs1; CREATE TABLE hs1 ( col1 integer PRIMARY KEY ); INSERT INTO hs1 VALUES (1); DROP TABLE IF EXISTS hs2; CREATE TABLE hs2 ( col1 integer PRIMARY KEY ); INSERT INTO hs2 VALUES (12); INSERT INTO hs2 VALUES (13); DROP TABLE IF EXISTS hs3; CREATE TABLE hs3 ( col1 integer PRIMARY KEY ); INSERT INTO hs3 VALUES (113); INSERT INTO hs3 VALUES (114); INSERT INTO hs3 VALUES (115); DROP SEQUENCE IF EXISTS hsseq; CREATE SEQUENCE hsseq; SELECT pg_switch_wal (); pgFormatter-4.2/t/pg-test-files/expected/hs_standby_allowed.sql000066400000000000000000000042641361326045100247750ustar00rootroot00000000000000-- -- Hot Standby tests -- -- hs_standby_allowed.sql -- -- SELECT SELECT count(*) AS should_be_1 FROM hs1; SELECT count(*) AS should_be_2 FROM hs2; SELECT count(*) AS should_be_3 FROM hs3; \! cat /tmp/copy_test -- Access sequence directly SELECT is_called FROM hsseq; -- Transactions BEGIN; SELECT count(*) AS should_be_1 FROM hs1; END; BEGIN TRANSACTION read ONLY; SELECT count(*) AS should_be_1 FROM hs1; END; BEGIN TRANSACTION ISOLATION level REPEATABLE read; SELECT count(*) AS should_be_1 FROM hs1; SELECT count(*) AS should_be_1 FROM hs1; SELECT count(*) AS should_be_1 FROM hs1; COMMIT; BEGIN; SELECT count(*) AS should_be_1 FROM hs1; COMMIT; BEGIN; SELECT count(*) AS should_be_1 FROM hs1; abort; START TRANSACTION; SELECT count(*) AS should_be_1 FROM hs1; COMMIT; BEGIN; SELECT count(*) AS should_be_1 FROM hs1; ROLLBACK; BEGIN; SELECT count(*) AS should_be_1 FROM hs1; SAVEPOINT s; SELECT count(*) AS should_be_2 FROM hs2; COMMIT; BEGIN; SELECT count(*) AS should_be_1 FROM hs1; SAVEPOINT s; SELECT count(*) AS should_be_2 FROM hs2; release SAVEPOINT s; SELECT count(*) AS should_be_2 FROM hs2; SAVEPOINT s; SELECT count(*) AS should_be_3 FROM hs3; ROLLBACK TO SAVEPOINT s; SELECT count(*) AS should_be_2 FROM hs2; COMMIT; -- SET parameters -- has no effect on read only transactions, but we can still set it SET synchronous_commit = ON; SHOW synchronous_commit; RESET synchronous_commit; discard temp; discard ALL; -- CURSOR commands BEGIN; DECLARE hsc CURSOR FOR SELECT * FROM hs3; FETCH NEXT FROM hsc; FETCH FIRST FROM hsc; FETCH LAST FROM hsc; FETCH 1 FROM hsc; CLOSE hsc; COMMIT; -- Prepared plans PREPARE hsp AS SELECT count(*) FROM hs1; PREPARE hsp_noexec (integer) AS INSERT INTO hs1 VALUES ($1); EXECUTE hsp; DEALLOCATE hsp; -- LOCK BEGIN; LOCK hs1 IN ACCESS SHARE MODE; LOCK hs1 IN ROW SHARE MODE; LOCK hs1 IN ROW EXCLUSIVE MODE; COMMIT; -- UNLISTEN UNLISTEN a; UNLISTEN *; -- LOAD -- should work, easier if there is no test for that... -- ALLOWED COMMANDS CHECKPOINT; discard ALL; pgFormatter-4.2/t/pg-test-files/expected/hs_standby_check.sql000066400000000000000000000010111361326045100244060ustar00rootroot00000000000000-- -- Hot Standby tests -- -- hs_standby_check.sql -- -- -- If the query below returns false then all other tests will fail after it. -- SELECT CASE pg_is_in_recovery() WHEN FALSE THEN 'These tests are intended only for execution on a standby server that is reading ' || 'WAL from a server upon which the regression database is already created and into ' || 'which src/test/regress/sql/hs_primary_setup.sql has been run' ELSE 'Tests are running on a standby server during recovery' END; pgFormatter-4.2/t/pg-test-files/expected/hs_standby_disallowed.sql000066400000000000000000000027321361326045100254730ustar00rootroot00000000000000-- -- Hot Standby tests -- -- hs_standby_disallowed.sql -- SET transaction_read_only = OFF; BEGIN TRANSACTION read write; COMMIT; -- SELECT SELECT * FROM hs1 FOR SHARE; SELECT * FROM hs1 FOR UPDATE; -- DML BEGIN; INSERT INTO hs1 VALUES (37); ROLLBACK; BEGIN; DELETE FROM hs1 WHERE col1 = 1; ROLLBACK; BEGIN; UPDATE hs1 SET col1 = NULL WHERE col1 > 0; ROLLBACK; BEGIN; TRUNCATE hs3; ROLLBACK; -- DDL CREATE TEMPORARY TABLE hstemp1 ( col1 integer ); BEGIN; DROP TABLE hs2; ROLLBACK; BEGIN; CREATE TABLE hs4 ( col1 integer ); ROLLBACK; -- Sequences SELECT nextval('hsseq'); -- Two-phase commit transaction stuff BEGIN; SELECT count(*) FROM hs1; PREPARE TRANSACTION 'foobar'; ROLLBACK; BEGIN; SELECT count(*) FROM hs1; COMMIT PREPARED 'foobar'; ROLLBACK; BEGIN; SELECT count(*) FROM hs1; PREPARE TRANSACTION 'foobar'; ROLLBACK PREPARED 'foobar'; ROLLBACK; BEGIN; SELECT count(*) FROM hs1; ROLLBACK PREPARED 'foobar'; ROLLBACK; -- Locks BEGIN; LOCK hs1; COMMIT; BEGIN; LOCK hs1 IN SHARE UPDATE EXCLUSIVE MODE; COMMIT; BEGIN; LOCK hs1 IN SHARE MODE; COMMIT; BEGIN; LOCK hs1 IN SHARE ROW EXCLUSIVE MODE; COMMIT; BEGIN; LOCK hs1 IN EXCLUSIVE MODE; COMMIT; BEGIN; LOCK hs1 IN ACCESS EXCLUSIVE MODE; COMMIT; -- Listen LISTEN a; NOTIFY a; -- disallowed commands ANALYZE hs1; VACUUM hs2; CLUSTER hs2 USING hs1_pkey; REINDEX TABLE hs2; REVOKE SELECT ON hs1 FROM PUBLIC; GRANT SELECT ON hs1 TO PUBLIC; pgFormatter-4.2/t/pg-test-files/expected/hs_standby_functions.sql000066400000000000000000000010611361326045100253460ustar00rootroot00000000000000-- -- Hot Standby tests -- -- hs_standby_functions.sql -- -- should fail SELECT txid_current(); SELECT length(txid_current_snapshot()::text) >= 4; SELECT pg_start_backup('should fail'); SELECT pg_switch_wal (); SELECT pg_stop_backup(); -- should return no rows SELECT * FROM pg_prepared_xacts; -- just the startup process SELECT locktype, virtualxid, virtualtransaction, mode, granted FROM pg_locks WHERE virtualxid = '1/1'; -- suicide is painless SELECT pg_cancel_backend(pg_backend_pid()); pgFormatter-4.2/t/pg-test-files/expected/identity.sql000066400000000000000000000210131361326045100227500ustar00rootroot00000000000000-- sanity check of system catalog SELECT attrelid, attname, attidentity FROM pg_attribute WHERE attidentity NOT IN ('', 'a', 'd'); CREATE TABLE itest1 ( a int GENERATED BY DEFAULT AS IDENTITY, b text ); CREATE TABLE itest2 ( a bigint GENERATED always AS IDENTITY, b text ); CREATE TABLE itest3 ( a smallint GENERATED BY DEFAULT AS IDENTITY (START WITH 7 INCREMENT BY 5), b text ); ALTER TABLE itest3 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- error SELECT table_name, column_name, column_default, is_nullable, is_identity, identity_generation, identity_start, identity_increment, identity_maximum, identity_minimum, identity_cycle FROM information_schema.columns WHERE table_name LIKE 'itest_' ORDER BY 1, 2; -- internal sequences should not be shown here SELECT sequence_name FROM information_schema.sequences WHERE sequence_name LIKE 'itest%'; SELECT pg_get_serial_sequence('itest1', 'a'); \d itest1_a_seq CREATE TABLE itest4 ( a int, b text ); ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- error, requires NOT NULL ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL; ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- ok ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL; -- error, disallowed ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- error, already set ALTER TABLE itest4 ALTER COLUMN b ADD GENERATED ALWAYS AS IDENTITY; -- error, wrong data type -- for later ALTER TABLE itest4 ALTER COLUMN b SET DEFAULT ''; -- invalid column type CREATE TABLE itest_err_1 ( a text GENERATED BY DEFAULT AS IDENTITY ); -- duplicate identity CREATE TABLE itest_err_2 ( a int GENERATED always AS IDENTITY GENERATED BY DEFAULT AS IDENTITY ); -- cannot have default and identity CREATE TABLE itest_err_3 ( a int DEFAULT 5 GENERATED BY DEFAULT AS IDENTITY ); -- cannot combine serial and identity CREATE TABLE itest_err_4 ( a serial GENERATED BY DEFAULT AS IDENTITY ); INSERT INTO itest1 DEFAULT VALUES; INSERT INTO itest1 DEFAULT VALUES; INSERT INTO itest2 DEFAULT VALUES; INSERT INTO itest2 DEFAULT VALUES; INSERT INTO itest3 DEFAULT VALUES; INSERT INTO itest3 DEFAULT VALUES; INSERT INTO itest4 DEFAULT VALUES; INSERT INTO itest4 DEFAULT VALUES; SELECT * FROM itest1; SELECT * FROM itest2; SELECT * FROM itest3; SELECT * FROM itest4; -- VALUES RTEs INSERT INTO itest3 VALUES (DEFAULT, 'a'); INSERT INTO itest3 VALUES (DEFAULT, 'b'), (DEFAULT, 'c'); SELECT * FROM itest3; -- OVERRIDING tests INSERT INTO itest1 VALUES (10, 'xyz'); INSERT INTO itest1 OVERRIDING USER VALUE VALUES (10, 'xyz'); SELECT * FROM itest1; INSERT INTO itest2 VALUES (10, 'xyz'); INSERT INTO itest2 OVERRIDING SYSTEM VALUE VALUES (10, 'xyz'); SELECT * FROM itest2; -- UPDATE tests UPDATE itest1 SET a = 101 WHERE a = 1; UPDATE itest1 SET a = DEFAULT WHERE a = 2; SELECT * FROM itest1; UPDATE itest2 SET a = 101 WHERE a = 1; UPDATE itest2 SET a = DEFAULT WHERE a = 2; SELECT * FROM itest2; -- COPY tests CREATE TABLE itest9 ( a int GENERATED ALWAYS AS IDENTITY, b text, c bigint ); SELECT * FROM itest9 ORDER BY c; -- DROP IDENTITY tests ALTER TABLE itest4 ALTER COLUMN a DROP IDENTITY; ALTER TABLE itest4 ALTER COLUMN a DROP IDENTITY; -- error ALTER TABLE itest4 ALTER COLUMN a DROP IDENTITY IF EXISTS; -- noop INSERT INTO itest4 DEFAULT VALUES; -- fails because NOT NULL is not dropped ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL; INSERT INTO itest4 DEFAULT VALUES; SELECT * FROM itest4; -- check that sequence is removed SELECT sequence_name FROM itest4_a_seq; -- test views CREATE TABLE itest10 ( a int GENERATED BY DEFAULT AS IDENTITY, b text ); CREATE TABLE itest11 ( a int GENERATED always AS IDENTITY, b text ); CREATE VIEW itestv10 AS SELECT * FROM itest10; CREATE VIEW itestv11 AS SELECT * FROM itest11; INSERT INTO itestv10 DEFAULT VALUES; INSERT INTO itestv10 DEFAULT VALUES; INSERT INTO itestv11 DEFAULT VALUES; INSERT INTO itestv11 DEFAULT VALUES; SELECT * FROM itestv10; SELECT * FROM itestv11; INSERT INTO itestv10 VALUES (10, 'xyz'); INSERT INTO itestv10 OVERRIDING USER VALUE VALUES (11, 'xyz'); SELECT * FROM itestv10; INSERT INTO itestv11 VALUES (10, 'xyz'); INSERT INTO itestv11 OVERRIDING SYSTEM VALUE VALUES (11, 'xyz'); SELECT * FROM itestv11; DROP VIEW itestv10, itestv11; -- ADD COLUMN CREATE TABLE itest13 ( a int ); -- add column to empty table ALTER TABLE itest13 ADD COLUMN b int GENERATED BY DEFAULT AS IDENTITY; INSERT INTO itest13 VALUES (1), (2), (3); -- add column to populated table ALTER TABLE itest13 ADD COLUMN c int GENERATED BY DEFAULT AS IDENTITY; SELECT * FROM itest13; -- various ALTER COLUMN tests -- fail, not allowed for identity columns ALTER TABLE itest1 ALTER COLUMN a SET DEFAULT 1; -- fail, not allowed, already has a default CREATE TABLE itest5 ( a serial, b text ); ALTER TABLE itest5 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; ALTER TABLE itest3 ALTER COLUMN a TYPE int; SELECT seqtypid::regtype FROM pg_sequence WHERE seqrelid = 'itest3_a_seq'::regclass; \d itest3 ALTER TABLE itest3 ALTER COLUMN a TYPE text; -- error -- kinda silly to change property in the same command, but it should work ALTER TABLE itest3 ADD COLUMN c int GENERATED BY DEFAULT AS IDENTITY, ALTER COLUMN c SET GENERATED ALWAYS; \d itest3 -- ALTER COLUMN ... SET CREATE TABLE itest6 ( a int GENERATED ALWAYS AS IDENTITY, b text ); INSERT INTO itest6 DEFAULT VALUES; ALTER TABLE itest6 ALTER COLUMN a SET GENERATED BY DEFAULT SET INCREMENT BY 2 SET START WITH 100 RESTART; INSERT INTO itest6 DEFAULT VALUES; INSERT INTO itest6 DEFAULT VALUES; SELECT * FROM itest6; SELECT table_name, column_name, is_identity, identity_generation FROM information_schema.columns WHERE table_name = 'itest6'; ALTER TABLE itest6 ALTER COLUMN b SET INCREMENT BY 2; -- fail, not identity -- prohibited direct modification of sequence ALTER SEQUENCE itest6_a_seq OWNED BY NONE; -- inheritance CREATE TABLE itest7 ( a int GENERATED ALWAYS AS IDENTITY ); INSERT INTO itest7 DEFAULT VALUES; SELECT * FROM itest7; -- identity property is not inherited CREATE TABLE itest7a ( b text ) INHERITS ( itest7 ); -- make column identity in child table CREATE TABLE itest7b ( a int ); CREATE TABLE itest7c ( a int GENERATED ALWAYS AS IDENTITY ) INHERITS ( itest7b ); INSERT INTO itest7c DEFAULT VALUES; SELECT * FROM itest7c; CREATE TABLE itest7d ( a int NOT NULL ); CREATE TABLE itest7e () INHERITS ( itest7d ); ALTER TABLE itest7d ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; ALTER TABLE itest7d ADD COLUMN b int GENERATED ALWAYS AS IDENTITY; -- error SELECT table_name, column_name, is_nullable, is_identity, identity_generation FROM information_schema.columns WHERE table_name LIKE 'itest7%' ORDER BY 1, 2; -- These ALTER TABLE variants will not recurse. ALTER TABLE itest7 ALTER COLUMN a SET GENERATED BY DEFAULT; ALTER TABLE itest7 ALTER COLUMN a RESTART; ALTER TABLE itest7 ALTER COLUMN a DROP IDENTITY; -- privileges CREATE USER regress_identity_user1; CREATE TABLE itest8 ( a int GENERATED ALWAYS AS IDENTITY, b text ); GRANT SELECT, INSERT ON itest8 TO regress_identity_user1; SET ROLE regress_identity_user1; INSERT INTO itest8 DEFAULT VALUES; SELECT * FROM itest8; RESET ROLE; DROP TABLE itest8; DROP USER regress_identity_user1; -- typed tables (currently not supported) CREATE TYPE itest_type AS ( f1 integer, f2 text, f3 bigint ); CREATE TABLE itest12 OF itest_type ( f1 WITH OPTIONS GENERATED ALWAYS AS IDENTITY ); -- error DROP TYPE itest_type CASCADE; -- table partitions (currently not supported) CREATE TABLE itest_parent ( f1 date NOT NULL, f2 text, f3 bigint ) PARTITION BY RANGE (f1); CREATE TABLE itest_child PARTITION OF itest_parent (f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error DROP TABLE itest_parent; pgFormatter-4.2/t/pg-test-files/expected/index_including.sql000066400000000000000000000272251361326045100242750ustar00rootroot00000000000000/* * 1.test CREATE INDEX * * Deliberately avoid dropping objects in this section, to get some pg_dump * coverage. */ -- Regular index with included columns CREATE TABLE tbl_include_reg ( c1 int, c2 int, c3 int, c4 box ); INSERT INTO tbl_include_reg SELECT x, 2 * x, 3 * x, box('4,4,4,4') FROM generate_series(1, 10) AS x; CREATE INDEX tbl_include_reg_idx ON tbl_include_reg (c1, c2) INCLUDE (c3, c4); -- duplicate column is pretty pointless, but we allow it anyway CREATE INDEX ON tbl_include_reg (c1, c2) INCLUDE (c1, c3); SELECT pg_get_indexdef(i.indexrelid) FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid WHERE i.indrelid = 'tbl_include_reg'::regclass ORDER BY c.relname; \d tbl_include_reg_idx -- Unique index and unique constraint CREATE TABLE tbl_include_unique1 ( c1 int, c2 int, c3 int, c4 box ); INSERT INTO tbl_include_unique1 SELECT x, 2 * x, 3 * x, box('4,4,4,4') FROM generate_series(1, 10) AS x; CREATE UNIQUE INDEX tbl_include_unique1_idx_unique ON tbl_include_unique1 USING btree (c1, c2) INCLUDE (c3, c4); ALTER TABLE tbl_include_unique1 ADD UNIQUE USING INDEX tbl_include_unique1_idx_unique; ALTER TABLE tbl_include_unique1 ADD UNIQUE (c1, c2) INCLUDE (c3, c4); SELECT pg_get_indexdef(i.indexrelid) FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid WHERE i.indrelid = 'tbl_include_unique1'::regclass ORDER BY c.relname; -- Unique index and unique constraint. Both must fail. CREATE TABLE tbl_include_unique2 ( c1 int, c2 int, c3 int, c4 box ); INSERT INTO tbl_include_unique2 SELECT 1, 2, 3 * x, box('4,4,4,4') FROM generate_series(1, 10) AS x; CREATE UNIQUE INDEX tbl_include_unique2_idx_unique ON tbl_include_unique2 USING btree (c1, c2) INCLUDE (c3, c4); ALTER TABLE tbl_include_unique2 ADD UNIQUE (c1, c2) INCLUDE (c3, c4); -- PK constraint CREATE TABLE tbl_include_pk ( c1 int, c2 int, c3 int, c4 box ); INSERT INTO tbl_include_pk SELECT 1, 2 * x, 3 * x, box('4,4,4,4') FROM generate_series(1, 10) AS x; ALTER TABLE tbl_include_pk ADD PRIMARY KEY (c1, c2) INCLUDE (c3, c4); SELECT pg_get_indexdef(i.indexrelid) FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid WHERE i.indrelid = 'tbl_include_pk'::regclass ORDER BY c.relname; CREATE TABLE tbl_include_box ( c1 int, c2 int, c3 int, c4 box ); INSERT INTO tbl_include_box SELECT 1, 2 * x, 3 * x, box('4,4,4,4') FROM generate_series(1, 10) AS x; CREATE UNIQUE INDEX tbl_include_box_idx_unique ON tbl_include_box USING btree (c1, c2) INCLUDE (c3, c4); ALTER TABLE tbl_include_box ADD PRIMARY KEY USING INDEX tbl_include_box_idx_unique; SELECT pg_get_indexdef(i.indexrelid) FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid WHERE i.indrelid = 'tbl_include_box'::regclass ORDER BY c.relname; -- PK constraint. Must fail. CREATE TABLE tbl_include_box_pk ( c1 int, c2 int, c3 int, c4 box ); INSERT INTO tbl_include_box_pk SELECT 1, 2, 3 * x, box('4,4,4,4') FROM generate_series(1, 10) AS x; ALTER TABLE tbl_include_box_pk ADD PRIMARY KEY (c1, c2) INCLUDE (c3, c4); /* * 2. Test CREATE TABLE with constraint */ CREATE TABLE tbl ( c1 int, c2 int, c3 int, c4 box, CONSTRAINT covering UNIQUE (c1, c2) INCLUDE (c3, c4) ); SELECT indexrelid::regclass, indnatts, indnkeyatts, indisunique, indisprimary, indkey, indclass FROM pg_index WHERE indrelid = 'tbl'::regclass::oid; SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'tbl'::regclass::oid; -- ensure that constraint works INSERT INTO tbl SELECT 1, 2, 3 * x, box('4,4,4,4') FROM generate_series(1, 10) AS x; DROP TABLE tbl; CREATE TABLE tbl ( c1 int, c2 int, c3 int, c4 box, CONSTRAINT covering PRIMARY KEY (c1, c2) INCLUDE (c3, c4) ); SELECT indexrelid::regclass, indnatts, indnkeyatts, indisunique, indisprimary, indkey, indclass FROM pg_index WHERE indrelid = 'tbl'::regclass::oid; SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'tbl'::regclass::oid; -- ensure that constraint works INSERT INTO tbl SELECT 1, 2, 3 * x, box('4,4,4,4') FROM generate_series(1, 10) AS x; INSERT INTO tbl SELECT 1, NULL, 3 * x, box('4,4,4,4') FROM generate_series(1, 10) AS x; INSERT INTO tbl SELECT x, 2 * x, NULL, NULL FROM generate_series(1, 300) AS x; EXPLAIN ( COSTS OFF ) SELECT * FROM tbl WHERE (c1, c2, c3) < (2, 5, 1); SELECT * FROM tbl WHERE (c1, c2, c3) < (2, 5, 1); -- row comparison that compares high key at page boundary SET enable_seqscan = OFF; EXPLAIN ( COSTS OFF ) SELECT * FROM tbl WHERE (c1, c2, c3) < (262, 1, 1) LIMIT 1; SELECT * FROM tbl WHERE (c1, c2, c3) < (262, 1, 1) LIMIT 1; DROP TABLE tbl; RESET enable_seqscan; CREATE TABLE tbl ( c1 int, c2 int, c3 int, c4 box, UNIQUE (c1, c2) INCLUDE (c3, c4) ); SELECT indexrelid::regclass, indnatts, indnkeyatts, indisunique, indisprimary, indkey, indclass FROM pg_index WHERE indrelid = 'tbl'::regclass::oid; SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'tbl'::regclass::oid; -- ensure that constraint works INSERT INTO tbl SELECT 1, 2, 3 * x, box('4,4,4,4') FROM generate_series(1, 10) AS x; DROP TABLE tbl; CREATE TABLE tbl ( c1 int, c2 int, c3 int, c4 box, PRIMARY KEY (c1, c2) INCLUDE (c3, c4) ); SELECT indexrelid::regclass, indnatts, indnkeyatts, indisunique, indisprimary, indkey, indclass FROM pg_index WHERE indrelid = 'tbl'::regclass::oid; SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'tbl'::regclass::oid; -- ensure that constraint works INSERT INTO tbl SELECT 1, 2, 3 * x, box('4,4,4,4') FROM generate_series(1, 10) AS x; INSERT INTO tbl SELECT 1, NULL, 3 * x, box('4,4,4,4') FROM generate_series(1, 10) AS x; INSERT INTO tbl SELECT x, 2 * x, NULL, NULL FROM generate_series(1, 10) AS x; DROP TABLE tbl; CREATE TABLE tbl ( c1 int, c2 int, c3 int, c4 box, EXCLUDE USING btree (c1 WITH =) INCLUDE (c3, c4) ); SELECT indexrelid::regclass, indnatts, indnkeyatts, indisunique, indisprimary, indkey, indclass FROM pg_index WHERE indrelid = 'tbl'::regclass::oid; SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'tbl'::regclass::oid; -- ensure that constraint works INSERT INTO tbl SELECT 1, 2, 3 * x, box('4,4,4,4') FROM generate_series(1, 10) AS x; INSERT INTO tbl SELECT x, 2 * x, NULL, NULL FROM generate_series(1, 10) AS x; DROP TABLE tbl; /* * 3.0 Test ALTER TABLE DROP COLUMN. * Any column deletion leads to index deletion. */ CREATE TABLE tbl ( c1 int, c2 int, c3 int, c4 int ); CREATE UNIQUE INDEX tbl_idx ON tbl USING btree (c1, c2, c3, c4); SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; ALTER TABLE tbl DROP COLUMN c3; SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; DROP TABLE tbl; /* * 3.1 Test ALTER TABLE DROP COLUMN. * Included column deletion leads to the index deletion, * AS well AS key columns deletion. It's explained in documentation. */ CREATE TABLE tbl ( c1 int, c2 int, c3 int, c4 box ); CREATE UNIQUE INDEX tbl_idx ON tbl USING btree (c1, c2) INCLUDE (c3, c4); SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; ALTER TABLE tbl DROP COLUMN c3; SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; DROP TABLE tbl; /* * 3.2 Test ALTER TABLE DROP COLUMN. * Included column deletion leads to the index deletion. * AS well AS key columns deletion. It's explained in documentation. */ CREATE TABLE tbl ( c1 int, c2 int, c3 int, c4 box, UNIQUE (c1, c2) INCLUDE (c3, c4) ); SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; ALTER TABLE tbl DROP COLUMN c3; SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; ALTER TABLE tbl DROP COLUMN c1; SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; DROP TABLE tbl; /* * 3.3 Test ALTER TABLE SET STATISTICS */ CREATE TABLE tbl ( c1 int, c2 int ); CREATE INDEX tbl_idx ON tbl (c1, (c1 + 0)) INCLUDE (c2); ALTER INDEX tbl_idx ALTER COLUMN 1 SET STATISTICS 1000; ALTER INDEX tbl_idx ALTER COLUMN 2 SET STATISTICS 1000; ALTER INDEX tbl_idx ALTER COLUMN 3 SET STATISTICS 1000; ALTER INDEX tbl_idx ALTER COLUMN 4 SET STATISTICS 1000; DROP TABLE tbl; /* * 4. CREATE INDEX CONCURRENTLY */ CREATE TABLE tbl ( c1 int, c2 int, c3 int, c4 box, UNIQUE (c1, c2) INCLUDE (c3, c4) ); INSERT INTO tbl SELECT x, 2 * x, 3 * x, box('4,4,4,4') FROM generate_series(1, 1000) AS x; CREATE UNIQUE INDEX CONCURRENTLY ON tbl (c1, c2) INCLUDE (c3, c4); SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; DROP TABLE tbl; /* * 5. REINDEX */ CREATE TABLE tbl ( c1 int, c2 int, c3 int, c4 box, UNIQUE (c1, c2) INCLUDE (c3, c4) ); SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; ALTER TABLE tbl DROP COLUMN c3; SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; REINDEX INDEX tbl_c1_c2_c3_c4_key; SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; ALTER TABLE tbl DROP COLUMN c1; SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; DROP TABLE tbl; /* * 7. Check various AMs. All but btree and gist must fail. */ CREATE TABLE tbl ( c1 int, c2 int, c3 box, c4 box ); CREATE INDEX ON tbl USING brin (c1, c2) INCLUDE (c3, c4); CREATE INDEX ON tbl USING gist (c3) INCLUDE (c1, c4); CREATE INDEX ON tbl USING spgist (c3) INCLUDE (c4); CREATE INDEX ON tbl USING gin (c1, c2) INCLUDE (c3, c4); CREATE INDEX ON tbl USING HASH (c1, c2) INCLUDE (c3, c4); CREATE INDEX ON tbl USING rtree (c3) INCLUDE (c1, c4); CREATE INDEX ON tbl USING btree (c1, c2) INCLUDE (c3, c4); DROP TABLE tbl; /* * 8. Update, delete values in indexed table. */ CREATE TABLE tbl ( c1 int, c2 int, c3 int, c4 box ); INSERT INTO tbl SELECT x, 2 * x, 3 * x, box('4,4,4,4') FROM generate_series(1, 10) AS x; CREATE UNIQUE INDEX tbl_idx_unique ON tbl USING btree (c1, c2) INCLUDE (c3, c4); UPDATE tbl SET c1 = 100 WHERE c1 = 2; UPDATE tbl SET c1 = 1 WHERE c1 = 3; -- should fail UPDATE tbl SET c2 = 2 WHERE c1 = 1; UPDATE tbl SET c3 = 1; DELETE FROM tbl WHERE c1 = 5 OR c3 = 12; DROP TABLE tbl; /* * 9. Alter column type. */ CREATE TABLE tbl ( c1 int, c2 int, c3 int, c4 box, UNIQUE (c1, c2) INCLUDE (c3, c4) ); INSERT INTO tbl SELECT x, 2 * x, 3 * x, box('4,4,4,4') FROM generate_series(1, 10) AS x; ALTER TABLE tbl ALTER c1 TYPE bigint; ALTER TABLE tbl ALTER c3 TYPE bigint; \d tbl DROP TABLE tbl; pgFormatter-4.2/t/pg-test-files/expected/index_including_gist.sql000066400000000000000000000107521361326045100253200ustar00rootroot00000000000000/* * 1.1. test CREATE INDEX with buffered build */ -- Regular index with included columns CREATE TABLE tbl_gist ( c1 int, c2 int, c3 int, c4 box ); -- size is chosen to exceed page size and trigger actual truncation INSERT INTO tbl_gist SELECT x, 2 * x, 3 * x, box(point(x, x + 1), point(2 * x, 2 * x + 1)) FROM generate_series(1, 8000) AS x; CREATE INDEX tbl_gist_idx ON tbl_gist USING gist (c4) INCLUDE (c1, c2, c3); SELECT pg_get_indexdef(i.indexrelid) FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid WHERE i.indrelid = 'tbl_gist'::regclass ORDER BY c.relname; SELECT * FROM tbl_gist WHERE c4 <@ box(point(1, 1), point(10, 10)); SET enable_bitmapscan TO OFF; EXPLAIN ( COSTS OFF ) SELECT * FROM tbl_gist WHERE c4 <@ box(point(1, 1), point(10, 10)); SET enable_bitmapscan TO DEFAULT; DROP TABLE tbl_gist; /* * 1.2. test CREATE INDEX with inserts */ -- Regular index with included columns CREATE TABLE tbl_gist ( c1 int, c2 int, c3 int, c4 box ); -- size is chosen to exceed page size and trigger actual truncation CREATE INDEX tbl_gist_idx ON tbl_gist USING gist (c4) INCLUDE (c1, c2, c3); INSERT INTO tbl_gist SELECT x, 2 * x, 3 * x, box(point(x, x + 1), point(2 * x, 2 * x + 1)) FROM generate_series(1, 8000) AS x; SELECT pg_get_indexdef(i.indexrelid) FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid WHERE i.indrelid = 'tbl_gist'::regclass ORDER BY c.relname; SELECT * FROM tbl_gist WHERE c4 <@ box(point(1, 1), point(10, 10)); SET enable_bitmapscan TO OFF; EXPLAIN ( COSTS OFF ) SELECT * FROM tbl_gist WHERE c4 <@ box(point(1, 1), point(10, 10)); SET enable_bitmapscan TO DEFAULT; DROP TABLE tbl_gist; /* * 2. CREATE INDEX CONCURRENTLY */ CREATE TABLE tbl_gist ( c1 int, c2 int, c3 int, c4 box ); INSERT INTO tbl_gist SELECT x, 2 * x, 3 * x, box(point(x, x + 1), point(2 * x, 2 * x + 1)) FROM generate_series(1, 10) AS x; CREATE INDEX CONCURRENTLY tbl_gist_idx ON tbl_gist USING gist (c4) INCLUDE (c1, c2, c3); SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl_gist' ORDER BY indexname; DROP TABLE tbl_gist; /* * 3. REINDEX */ CREATE TABLE tbl_gist ( c1 int, c2 int, c3 int, c4 box ); INSERT INTO tbl_gist SELECT x, 2 * x, 3 * x, box(point(x, x + 1), point(2 * x, 2 * x + 1)) FROM generate_series(1, 10) AS x; CREATE INDEX tbl_gist_idx ON tbl_gist USING gist (c4) INCLUDE (c1, c3); SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl_gist' ORDER BY indexname; REINDEX INDEX tbl_gist_idx; SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl_gist' ORDER BY indexname; ALTER TABLE tbl_gist DROP COLUMN c1; SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl_gist' ORDER BY indexname; DROP TABLE tbl_gist; /* * 4. Update, delete values in indexed table. */ CREATE TABLE tbl_gist ( c1 int, c2 int, c3 int, c4 box ); INSERT INTO tbl_gist SELECT x, 2 * x, 3 * x, box(point(x, x + 1), point(2 * x, 2 * x + 1)) FROM generate_series(1, 10) AS x; CREATE INDEX tbl_gist_idx ON tbl_gist USING gist (c4) INCLUDE (c1, c3); UPDATE tbl_gist SET c1 = 100 WHERE c1 = 2; UPDATE tbl_gist SET c1 = 1 WHERE c1 = 3; DELETE FROM tbl_gist WHERE c1 = 5 OR c3 = 12; DROP TABLE tbl_gist; /* * 5. Alter column type. */ CREATE TABLE tbl_gist ( c1 int, c2 int, c3 int, c4 box ); INSERT INTO tbl_gist SELECT x, 2 * x, 3 * x, box(point(x, x + 1), point(2 * x, 2 * x + 1)) FROM generate_series(1, 10) AS x; CREATE INDEX tbl_gist_idx ON tbl_gist USING gist (c4) INCLUDE (c1, c3); ALTER TABLE tbl_gist ALTER c1 TYPE bigint; ALTER TABLE tbl_gist ALTER c3 TYPE bigint; \d tbl_gist DROP TABLE tbl_gist; /* * 6. EXCLUDE constraint. */ CREATE TABLE tbl_gist ( c1 int, c2 int, c3 int, c4 box, EXCLUDE USING gist (c4 WITH &&) INCLUDE (c1, c2, c3) ); INSERT INTO tbl_gist SELECT x, 2 * x, 3 * x, box(point(x, x + 1), point(2 * x, 2 * x + 1)) FROM generate_series(1, 10) AS x; INSERT INTO tbl_gist SELECT x, 2 * x, 3 * x, box(point(3 * x, 2 * x), point(3 * x + 1, 2 * x + 1)) FROM generate_series(1, 10) AS x; EXPLAIN ( COSTS OFF ) SELECT * FROM tbl_gist WHERE c4 <@ box(point(1, 1), point(10, 10)); \d tbl_gist DROP TABLE tbl_gist; pgFormatter-4.2/t/pg-test-files/expected/indexing.sql000066400000000000000000001117151361326045100227350ustar00rootroot00000000000000-- Creating an index on a partitioned table makes the partitions -- automatically get the index CREATE TABLE idxpart ( a int, b int, c text ) PARTITION BY RANGE (a); -- relhassubclass of a partitioned index is false before creating any partition. -- It will be set after the first partition is created. CREATE INDEX idxpart_idx ON idxpart (a); SELECT relhassubclass FROM pg_class WHERE relname = 'idxpart_idx'; -- Check that partitioned indexes are present in pg_indexes. SELECT indexdef FROM pg_indexes WHERE indexname LIKE 'idxpart_idx%'; DROP INDEX idxpart_idx; CREATE TABLE idxpart1 PARTITION OF idxpart FOR VALUES FROM (0) TO (10); CREATE TABLE idxpart2 PARTITION OF idxpart FOR VALUES FROM (10) TO (100) PARTITION BY RANGE (b); CREATE TABLE idxpart21 PARTITION OF idxpart2 FOR VALUES FROM (0) TO (100); -- Even with partitions, relhassubclass should not be set if a partitioned -- index is created only on the parent. CREATE INDEX idxpart_idx ON ONLY idxpart (a); SELECT relhassubclass FROM pg_class WHERE relname = 'idxpart_idx'; DROP INDEX idxpart_idx; CREATE INDEX ON idxpart (a); SELECT relname, relkind, relhassubclass, inhparent::regclass FROM pg_class LEFT JOIN pg_index ix ON (indexrelid = oid) LEFT JOIN pg_inherits ON (ix.indexrelid = inhrelid) WHERE relname LIKE 'idxpart%' ORDER BY relname; DROP TABLE idxpart; -- Some unsupported features CREATE TABLE idxpart ( a int, b int, c text ) PARTITION BY RANGE (a); CREATE TABLE idxpart1 PARTITION OF idxpart FOR VALUES FROM (0) TO (10); CREATE INDEX CONCURRENTLY ON idxpart (a); DROP TABLE idxpart; -- Verify bugfix with query on indexed partitioned table with no partitions -- https://postgr.es/m/20180124162006.pmapfiznhgngwtjf@alvherre.pgsql CREATE TABLE idxpart ( col1 int ) PARTITION BY RANGE (col1); CREATE INDEX ON idxpart (col1); CREATE TABLE idxpart_two ( col2 int ); SELECT col2 FROM idxpart_two fk LEFT OUTER JOIN idxpart pk ON (col1 = col2); DROP TABLE idxpart, idxpart_two; -- Verify bugfix with index rewrite on ALTER TABLE / SET DATA TYPE -- https://postgr.es/m/CAKcux6mxNCGsgATwf5CGMF8g4WSupCXicCVMeKUTuWbyxHOMsQ@mail.gmail.com CREATE TABLE idxpart ( a int, b text, c int ) PARTITION BY RANGE (a); CREATE TABLE idxpart1 PARTITION OF idxpart FOR VALUES FROM (MINVALUE) TO (MAXVALUE); CREATE INDEX partidx_abc_idx ON idxpart (a, b, c); INSERT INTO idxpart (a, b, c) SELECT i, i, i FROM generate_series(1, 50) i; ALTER TABLE idxpart ALTER COLUMN c TYPE numeric; DROP TABLE idxpart; -- If a table without index is attached as partition to a table with -- an index, the index is automatically created CREATE TABLE idxpart ( a int, b int, c text ) PARTITION BY RANGE (a); CREATE INDEX idxparti ON idxpart (a); CREATE INDEX idxparti2 ON idxpart (b, c); CREATE TABLE idxpart1 ( LIKE idxpart ); \d idxpart1 ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (0) TO (10); \d idxpart1 \d+ idxpart1_a_idx \d+ idxpart1_b_c_idx DROP TABLE idxpart; -- If a partition already has an index, don't create a duplicative one CREATE TABLE idxpart ( a int, b int ) PARTITION BY RANGE (a, b); CREATE TABLE idxpart1 PARTITION OF idxpart FOR VALUES FROM (0, 0) TO (10, 10); CREATE INDEX ON idxpart1 (a, b); CREATE INDEX ON idxpart (a, b); \d idxpart1 SELECT relname, relkind, relhassubclass, inhparent::regclass FROM pg_class LEFT JOIN pg_index ix ON (indexrelid = oid) LEFT JOIN pg_inherits ON (ix.indexrelid = inhrelid) WHERE relname LIKE 'idxpart%' ORDER BY relname; DROP TABLE idxpart; -- DROP behavior for partitioned indexes CREATE TABLE idxpart ( a int ) PARTITION BY RANGE (a); CREATE INDEX ON idxpart (a); CREATE TABLE idxpart1 PARTITION OF idxpart FOR VALUES FROM (0) TO (10); DROP INDEX idxpart1_a_idx; -- no way DROP INDEX idxpart_a_idx; -- both indexes go away SELECT relname, relkind FROM pg_class WHERE relname LIKE 'idxpart%' ORDER BY relname; CREATE INDEX ON idxpart (a); DROP TABLE idxpart1; -- the index on partition goes away too SELECT relname, relkind FROM pg_class WHERE relname LIKE 'idxpart%' ORDER BY relname; DROP TABLE idxpart; -- ALTER INDEX .. ATTACH, error cases CREATE TABLE idxpart ( a int, b int ) PARTITION BY RANGE (a, b); CREATE TABLE idxpart1 PARTITION OF idxpart FOR VALUES FROM (0, 0) TO (10, 10); CREATE INDEX idxpart_a_b_idx ON ONLY idxpart (a, b); CREATE INDEX idxpart1_a_b_idx ON idxpart1 (a, b); CREATE INDEX idxpart1_tst1 ON idxpart1 (b, a); CREATE INDEX idxpart1_tst2 ON idxpart1 USING HASH (a); CREATE INDEX idxpart1_tst3 ON idxpart1 (a, b) WHERE a > 10; ALTER INDEX idxpart ATTACH PARTITION idxpart1; ALTER INDEX idxpart_a_b_idx ATTACH PARTITION idxpart1; ALTER INDEX idxpart_a_b_idx ATTACH PARTITION idxpart_a_b_idx; ALTER INDEX idxpart_a_b_idx ATTACH PARTITION idxpart1_b_idx; ALTER INDEX idxpart_a_b_idx ATTACH PARTITION idxpart1_tst1; ALTER INDEX idxpart_a_b_idx ATTACH PARTITION idxpart1_tst2; ALTER INDEX idxpart_a_b_idx ATTACH PARTITION idxpart1_tst3; -- OK ALTER INDEX idxpart_a_b_idx ATTACH PARTITION idxpart1_a_b_idx; ALTER INDEX idxpart_a_b_idx ATTACH PARTITION idxpart1_a_b_idx; -- quiet -- reject dupe CREATE INDEX idxpart1_2_a_b ON idxpart1 (a, b); ALTER INDEX idxpart_a_b_idx ATTACH PARTITION idxpart1_2_a_b; DROP TABLE idxpart; -- make sure everything's gone SELECT indexrelid::regclass, indrelid::regclass FROM pg_index WHERE indexrelid::regclass::text LIKE 'idxpart%'; -- Don't auto-attach incompatible indexes CREATE TABLE idxpart ( a int, b int ) PARTITION BY RANGE (a); CREATE TABLE idxpart1 ( a int, b int ); CREATE INDEX ON idxpart1 USING HASH (a); CREATE INDEX ON idxpart1 (a) WHERE b > 1; CREATE INDEX ON idxpart1 ((a + 0)); CREATE INDEX ON idxpart1 (a, a); CREATE INDEX ON idxpart (a); ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (0) TO (1000); \d idxpart1 DROP TABLE idxpart; -- If CREATE INDEX ONLY, don't create indexes on partitions; and existing -- indexes on partitions don't change parent. ALTER INDEX ATTACH can change -- the parent after the fact. CREATE TABLE idxpart ( a int ) PARTITION BY RANGE (a); CREATE TABLE idxpart1 PARTITION OF idxpart FOR VALUES FROM (0) TO (100); CREATE TABLE idxpart2 PARTITION OF idxpart FOR VALUES FROM (100) TO (1000) PARTITION BY RANGE (a); CREATE TABLE idxpart21 PARTITION OF idxpart2 FOR VALUES FROM (100) TO (200); CREATE TABLE idxpart22 PARTITION OF idxpart2 FOR VALUES FROM (200) TO (300); CREATE INDEX ON idxpart22 (a); CREATE INDEX ON ONLY idxpart2 (a); CREATE INDEX ON idxpart (a); -- Here we expect that idxpart1 and idxpart2 have a new index, but idxpart21 -- does not; also, idxpart22 is not attached. \d idxpart1 \d idxpart2 \d idxpart21 SELECT indexrelid::regclass, indrelid::regclass, inhparent::regclass FROM pg_index idx LEFT JOIN pg_inherits inh ON (idx.indexrelid = inh.inhrelid) WHERE indexrelid::regclass::text LIKE 'idxpart%' ORDER BY indexrelid::regclass::text COLLATE "C"; ALTER INDEX idxpart2_a_idx ATTACH PARTITION idxpart22_a_idx; SELECT indexrelid::regclass, indrelid::regclass, inhparent::regclass FROM pg_index idx LEFT JOIN pg_inherits inh ON (idx.indexrelid = inh.inhrelid) WHERE indexrelid::regclass::text LIKE 'idxpart%' ORDER BY indexrelid::regclass::text COLLATE "C"; -- attaching idxpart22 is not enough to set idxpart22_a_idx valid ... ALTER INDEX idxpart2_a_idx ATTACH PARTITION idxpart22_a_idx; \d idxpart2 -- ... but this one is. CREATE INDEX ON idxpart21 (a); ALTER INDEX idxpart2_a_idx ATTACH PARTITION idxpart21_a_idx; \d idxpart2 DROP TABLE idxpart; -- When a table is attached a partition and it already has an index, a -- duplicate index should not get created, but rather the index becomes -- attached to the parent's index. CREATE TABLE idxpart ( a int, b int, c text ) PARTITION BY RANGE (a); CREATE INDEX idxparti ON idxpart (a); CREATE INDEX idxparti2 ON idxpart (b, c); CREATE TABLE idxpart1 ( LIKE idxpart INCLUDING indexes ); \d idxpart1 SELECT relname, relkind, inhparent::regclass FROM pg_class LEFT JOIN pg_index ix ON (indexrelid = oid) LEFT JOIN pg_inherits ON (ix.indexrelid = inhrelid) WHERE relname LIKE 'idxpart%' ORDER BY relname; ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (0) TO (10); \d idxpart1 SELECT relname, relkind, inhparent::regclass FROM pg_class LEFT JOIN pg_index ix ON (indexrelid = oid) LEFT JOIN pg_inherits ON (ix.indexrelid = inhrelid) WHERE relname LIKE 'idxpart%' ORDER BY relname; DROP TABLE idxpart; -- Verify that attaching an invalid index does not mark the parent index valid. -- On the other hand, attaching a valid index marks not only its direct -- ancestor valid, but also any indirect ancestor that was only missing the one -- that was just made valid CREATE TABLE idxpart ( a int, b int ) PARTITION BY RANGE (a); CREATE TABLE idxpart1 PARTITION OF idxpart FOR VALUES FROM (1) TO (1000) PARTITION BY RANGE (a); CREATE TABLE idxpart11 PARTITION OF idxpart1 FOR VALUES FROM (1) TO (100); CREATE INDEX ON ONLY idxpart1 (a); CREATE INDEX ON ONLY idxpart (a); -- this results in two invalid indexes: SELECT relname, indisvalid FROM pg_class JOIN pg_index ON indexrelid = oid WHERE relname LIKE 'idxpart%' ORDER BY relname; -- idxpart1_a_idx is not valid, so idxpart_a_idx should not become valid: ALTER INDEX idxpart_a_idx ATTACH PARTITION idxpart1_a_idx; SELECT relname, indisvalid FROM pg_class JOIN pg_index ON indexrelid = oid WHERE relname LIKE 'idxpart%' ORDER BY relname; -- after creating and attaching this, both idxpart1_a_idx and idxpart_a_idx -- should become valid CREATE INDEX ON idxpart11 (a); ALTER INDEX idxpart1_a_idx ATTACH PARTITION idxpart11_a_idx; SELECT relname, indisvalid FROM pg_class JOIN pg_index ON indexrelid = oid WHERE relname LIKE 'idxpart%' ORDER BY relname; DROP TABLE idxpart; -- verify dependency handling during ALTER TABLE DETACH PARTITION CREATE TABLE idxpart ( a int ) PARTITION BY RANGE (a); CREATE TABLE idxpart1 ( LIKE idxpart ); CREATE INDEX ON idxpart1 (a); CREATE INDEX ON idxpart (a); CREATE TABLE idxpart2 ( LIKE idxpart ); ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (0000) TO (1000); ALTER TABLE idxpart ATTACH PARTITION idxpart2 FOR VALUES FROM (1000) TO (2000); CREATE TABLE idxpart3 PARTITION OF idxpart FOR VALUES FROM (2000) TO (3000); SELECT relname, relkind FROM pg_class WHERE relname LIKE 'idxpart%' ORDER BY relname; -- a) after detaching partitions, the indexes can be dropped independently ALTER TABLE idxpart DETACH PARTITION idxpart1; ALTER TABLE idxpart DETACH PARTITION idxpart2; ALTER TABLE idxpart DETACH PARTITION idxpart3; DROP INDEX idxpart1_a_idx; DROP INDEX idxpart2_a_idx; DROP INDEX idxpart3_a_idx; SELECT relname, relkind FROM pg_class WHERE relname LIKE 'idxpart%' ORDER BY relname; DROP TABLE idxpart, idxpart1, idxpart2, idxpart3; SELECT relname, relkind FROM pg_class WHERE relname LIKE 'idxpart%' ORDER BY relname; CREATE TABLE idxpart ( a int ) PARTITION BY RANGE (a); CREATE TABLE idxpart1 ( LIKE idxpart ); CREATE INDEX ON idxpart1 (a); CREATE INDEX ON idxpart (a); CREATE TABLE idxpart2 ( LIKE idxpart ); ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (0000) TO (1000); ALTER TABLE idxpart ATTACH PARTITION idxpart2 FOR VALUES FROM (1000) TO (2000); CREATE TABLE idxpart3 PARTITION OF idxpart FOR VALUES FROM (2000) TO (3000); -- b) after detaching, dropping the index on parent does not remove the others SELECT relname, relkind FROM pg_class WHERE relname LIKE 'idxpart%' ORDER BY relname; ALTER TABLE idxpart DETACH PARTITION idxpart1; ALTER TABLE idxpart DETACH PARTITION idxpart2; ALTER TABLE idxpart DETACH PARTITION idxpart3; DROP INDEX idxpart_a_idx; SELECT relname, relkind FROM pg_class WHERE relname LIKE 'idxpart%' ORDER BY relname; DROP TABLE idxpart, idxpart1, idxpart2, idxpart3; SELECT relname, relkind FROM pg_class WHERE relname LIKE 'idxpart%' ORDER BY relname; CREATE TABLE idxpart ( a int, b int, c int ) PARTITION BY RANGE (a); CREATE INDEX ON idxpart (c); CREATE TABLE idxpart1 PARTITION OF idxpart FOR VALUES FROM (0) TO (250); CREATE TABLE idxpart2 PARTITION OF idxpart FOR VALUES FROM (250) TO (500); ALTER TABLE idxpart DETACH PARTITION idxpart2; \d idxpart2 ALTER TABLE idxpart2 DROP COLUMN c; \d idxpart2 DROP TABLE idxpart, idxpart2; -- Verify that expression indexes inherit correctly CREATE TABLE idxpart ( a int, b int ) PARTITION BY RANGE (a); CREATE TABLE idxpart1 ( LIKE idxpart ); CREATE INDEX ON idxpart1 ((a + b)); CREATE INDEX ON idxpart ((a + b)); CREATE TABLE idxpart2 ( LIKE idxpart ); ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (0000) TO (1000); ALTER TABLE idxpart ATTACH PARTITION idxpart2 FOR VALUES FROM (1000) TO (2000); CREATE TABLE idxpart3 PARTITION OF idxpart FOR VALUES FROM (2000) TO (3000); SELECT relname AS child, inhparent::regclass AS parent, pg_get_indexdef AS childdef FROM pg_class JOIN pg_inherits ON inhrelid = oid, LATERAL pg_get_indexdef(pg_class.oid) WHERE relkind IN ('i', 'I') AND relname LIKE 'idxpart%' ORDER BY relname; DROP TABLE idxpart; -- Verify behavior for collation (mis)matches CREATE TABLE idxpart ( a text ) PARTITION BY RANGE (a); CREATE TABLE idxpart1 ( LIKE idxpart ); CREATE TABLE idxpart2 ( LIKE idxpart ); CREATE INDEX ON idxpart2 (a COLLATE "POSIX"); CREATE INDEX ON idxpart2 (a); CREATE INDEX ON idxpart2 (a COLLATE "C"); ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM ('aaa') TO ('bbb'); ALTER TABLE idxpart ATTACH PARTITION idxpart2 FOR VALUES FROM ('bbb') TO ('ccc'); CREATE TABLE idxpart3 PARTITION OF idxpart FOR VALUES FROM ('ccc') TO ('ddd'); CREATE INDEX ON idxpart (a COLLATE "C"); CREATE TABLE idxpart4 PARTITION OF idxpart FOR VALUES FROM ('ddd') TO ('eee'); SELECT relname AS child, inhparent::regclass AS parent, pg_get_indexdef AS childdef FROM pg_class LEFT JOIN pg_inherits ON inhrelid = oid, LATERAL pg_get_indexdef(pg_class.oid) WHERE relkind IN ('i', 'I') AND relname LIKE 'idxpart%' ORDER BY relname; DROP TABLE idxpart; -- Verify behavior for opclass (mis)matches CREATE TABLE idxpart ( a text ) PARTITION BY RANGE (a); CREATE TABLE idxpart1 ( LIKE idxpart ); CREATE TABLE idxpart2 ( LIKE idxpart ); CREATE INDEX ON idxpart2 (a); ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM ('aaa') TO ('bbb'); ALTER TABLE idxpart ATTACH PARTITION idxpart2 FOR VALUES FROM ('bbb') TO ('ccc'); CREATE TABLE idxpart3 PARTITION OF idxpart FOR VALUES FROM ('ccc') TO ('ddd'); CREATE INDEX ON idxpart (a text_pattern_ops); CREATE TABLE idxpart4 PARTITION OF idxpart FOR VALUES FROM ('ddd') TO ('eee'); -- must *not* have attached the index we created on idxpart2 SELECT relname AS child, inhparent::regclass AS parent, pg_get_indexdef AS childdef FROM pg_class LEFT JOIN pg_inherits ON inhrelid = oid, LATERAL pg_get_indexdef(pg_class.oid) WHERE relkind IN ('i', 'I') AND relname LIKE 'idxpart%' ORDER BY relname; DROP INDEX idxpart_a_idx; CREATE INDEX ON ONLY idxpart (a text_pattern_ops); -- must reject ALTER INDEX idxpart_a_idx ATTACH PARTITION idxpart2_a_idx; DROP TABLE idxpart; -- Verify that attaching indexes maps attribute numbers correctly CREATE TABLE idxpart ( col1 int, a int, col2 int, b int ) PARTITION BY RANGE (a); CREATE TABLE idxpart1 ( b int, col1 int, col2 int, col3 int, a int ); ALTER TABLE idxpart DROP COLUMN col1, DROP COLUMN col2; ALTER TABLE idxpart1 DROP COLUMN col1, DROP COLUMN col2, DROP COLUMN col3; ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (0) TO (1000); CREATE INDEX idxpart_1_idx ON ONLY idxpart (b, a); CREATE INDEX idxpart1_1_idx ON idxpart1 (b, a); CREATE INDEX idxpart1_1b_idx ON idxpart1 (b); -- test expressions and partial-index predicate, too CREATE INDEX idxpart_2_idx ON ONLY idxpart ((b + a)) WHERE a > 1; CREATE INDEX idxpart1_2_idx ON idxpart1 ((b + a)) WHERE a > 1; CREATE INDEX idxpart1_2b_idx ON idxpart1 ((a + b)) WHERE a > 1; CREATE INDEX idxpart1_2c_idx ON idxpart1 ((b + a)) WHERE b > 1; ALTER INDEX idxpart_1_idx ATTACH PARTITION idxpart1_1b_idx; -- fail ALTER INDEX idxpart_1_idx ATTACH PARTITION idxpart1_1_idx; ALTER INDEX idxpart_2_idx ATTACH PARTITION idxpart1_2b_idx; -- fail ALTER INDEX idxpart_2_idx ATTACH PARTITION idxpart1_2c_idx; -- fail ALTER INDEX idxpart_2_idx ATTACH PARTITION idxpart1_2_idx; -- ok SELECT relname AS child, inhparent::regclass AS parent, pg_get_indexdef AS childdef FROM pg_class LEFT JOIN pg_inherits ON inhrelid = oid, LATERAL pg_get_indexdef(pg_class.oid) WHERE relkind IN ('i', 'I') AND relname LIKE 'idxpart%' ORDER BY relname; DROP TABLE idxpart; -- Make sure the partition columns are mapped correctly CREATE TABLE idxpart ( a int, b int, c text ) PARTITION BY RANGE (a); CREATE INDEX idxparti ON idxpart (a); CREATE INDEX idxparti2 ON idxpart (c, b); CREATE TABLE idxpart1 ( c text, a int, b int ); ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (0) TO (10); CREATE TABLE idxpart2 ( c text, a int, b int ); CREATE INDEX ON idxpart2 (a); CREATE INDEX ON idxpart2 (c, b); ALTER TABLE idxpart ATTACH PARTITION idxpart2 FOR VALUES FROM (10) TO (20); SELECT c.relname, pg_get_indexdef(indexrelid) FROM pg_class c JOIN pg_index i ON c.oid = i.indexrelid WHERE indrelid::regclass::text LIKE 'idxpart%' ORDER BY indexrelid::regclass::text COLLATE "C"; DROP TABLE idxpart; -- Verify that columns are mapped correctly in expression indexes CREATE TABLE idxpart ( col1 int, col2 int, a int, b int ) PARTITION BY RANGE (a); CREATE TABLE idxpart1 ( col2 int, b int, col1 int, a int ); CREATE TABLE idxpart2 ( col1 int, col2 int, b int, a int ); ALTER TABLE idxpart DROP COLUMN col1, DROP COLUMN col2; ALTER TABLE idxpart1 DROP COLUMN col1, DROP COLUMN col2; ALTER TABLE idxpart2 DROP COLUMN col1, DROP COLUMN col2; CREATE INDEX ON idxpart2 (abs(b)); ALTER TABLE idxpart ATTACH PARTITION idxpart2 FOR VALUES FROM (0) TO (1); CREATE INDEX ON idxpart (abs(b)); CREATE INDEX ON idxpart ((b + 1)); ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (1) TO (2); SELECT c.relname, pg_get_indexdef(indexrelid) FROM pg_class c JOIN pg_index i ON c.oid = i.indexrelid WHERE indrelid::regclass::text LIKE 'idxpart%' ORDER BY indexrelid::regclass::text COLLATE "C"; DROP TABLE idxpart; -- Verify that columns are mapped correctly for WHERE in a partial index CREATE TABLE idxpart ( col1 int, a int, col3 int, b int ) PARTITION BY RANGE (a); ALTER TABLE idxpart DROP COLUMN col1, DROP COLUMN col3; CREATE TABLE idxpart1 ( col1 int, col2 int, col3 int, col4 int, b int, a int ); ALTER TABLE idxpart1 DROP COLUMN col1, DROP COLUMN col2, DROP COLUMN col3, DROP COLUMN col4; ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (0) TO (1000); CREATE TABLE idxpart2 ( col1 int, col2 int, b int, a int ); CREATE INDEX ON idxpart2 (a) WHERE b > 1000; ALTER TABLE idxpart2 DROP COLUMN col1, DROP COLUMN col2; ALTER TABLE idxpart ATTACH PARTITION idxpart2 FOR VALUES FROM (1000) TO (2000); CREATE INDEX ON idxpart (a) WHERE b > 1000; SELECT c.relname, pg_get_indexdef(indexrelid) FROM pg_class c JOIN pg_index i ON c.oid = i.indexrelid WHERE indrelid::regclass::text LIKE 'idxpart%' ORDER BY indexrelid::regclass::text COLLATE "C"; DROP TABLE idxpart; -- Column number mapping: dropped columns in the partition CREATE TABLE idxpart1 ( drop_1 int, drop_2 int, col_keep int, drop_3 int ); ALTER TABLE idxpart1 DROP COLUMN drop_1; ALTER TABLE idxpart1 DROP COLUMN drop_2; ALTER TABLE idxpart1 DROP COLUMN drop_3; CREATE INDEX ON idxpart1 (col_keep); CREATE TABLE idxpart ( col_keep int ) PARTITION BY RANGE (col_keep); CREATE INDEX ON idxpart (col_keep); ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (0) TO (1000); \d idxpart \d idxpart1 SELECT attrelid::regclass, attname, attnum FROM pg_attribute WHERE attrelid::regclass::text LIKE 'idxpart%' AND attnum > 0 ORDER BY attrelid::regclass, attnum; DROP TABLE idxpart; -- Column number mapping: dropped columns in the parent table CREATE TABLE idxpart ( drop_1 int, drop_2 int, col_keep int, drop_3 int ) PARTITION BY RANGE (col_keep); ALTER TABLE idxpart DROP COLUMN drop_1; ALTER TABLE idxpart DROP COLUMN drop_2; ALTER TABLE idxpart DROP COLUMN drop_3; CREATE TABLE idxpart1 ( col_keep int ); CREATE INDEX ON idxpart1 (col_keep); CREATE INDEX ON idxpart (col_keep); ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (0) TO (1000); \d idxpart \d idxpart1 SELECT attrelid::regclass, attname, attnum FROM pg_attribute WHERE attrelid::regclass::text LIKE 'idxpart%' AND attnum > 0 ORDER BY attrelid::regclass, attnum; DROP TABLE idxpart; -- -- Constraint-related indexes -- -- Verify that it works to add primary key / unique to partitioned tables CREATE TABLE idxpart ( a int PRIMARY KEY, b int ) PARTITION BY RANGE (a); \d idxpart -- multiple primary key on child should fail CREATE TABLE failpart PARTITION OF idxpart (b PRIMARY KEY) FOR VALUES FROM (0) TO (100); DROP TABLE idxpart; -- primary key on child is okay if there's no PK in the parent, though CREATE TABLE idxpart ( a int ) PARTITION BY RANGE (a); CREATE TABLE idxpart1pk PARTITION OF idxpart (a PRIMARY KEY) FOR VALUES FROM (0) TO (100); \d idxpart1pk DROP TABLE idxpart; -- Failing to use the full partition key is not allowed CREATE TABLE idxpart ( a int UNIQUE, b int ) PARTITION BY RANGE (a, b); CREATE TABLE idxpart ( a int, b int UNIQUE ) PARTITION BY RANGE (a, b); CREATE TABLE idxpart ( a int PRIMARY KEY, b int ) PARTITION BY RANGE (b, a); CREATE TABLE idxpart ( a int, b int PRIMARY KEY ) PARTITION BY RANGE (b, a); -- OK if you use them in some other order CREATE TABLE idxpart ( a int, b int, c text, PRIMARY KEY (a, b, c) ) PARTITION BY RANGE (b, c, a); DROP TABLE idxpart; -- not other types of index-based constraints CREATE TABLE idxpart ( a int, EXCLUDE (a WITH =) ) PARTITION BY RANGE (a); -- no expressions in partition key for PK/UNIQUE CREATE TABLE idxpart ( a int PRIMARY KEY, b int ) PARTITION BY RANGE ((b + a)); CREATE TABLE idxpart ( a int UNIQUE, b int ) PARTITION BY RANGE ((b + a)); -- use ALTER TABLE to add a primary key CREATE TABLE idxpart ( a int, b int, c text ) PARTITION BY RANGE (a, b); ALTER TABLE idxpart ADD PRIMARY KEY (a); -- not an incomplete one though ALTER TABLE idxpart ADD PRIMARY KEY (a, b); -- this works \d idxpart CREATE TABLE idxpart1 PARTITION OF idxpart FOR VALUES FROM (0, 0) TO (1000, 1000); \d idxpart1 DROP TABLE idxpart; -- use ALTER TABLE to add a unique constraint CREATE TABLE idxpart ( a int, b int ) PARTITION BY RANGE (a, b); ALTER TABLE idxpart ADD UNIQUE (a); -- not an incomplete one though ALTER TABLE idxpart ADD UNIQUE (b, a); -- this works \d idxpart DROP TABLE idxpart; -- Exclusion constraints cannot be added CREATE TABLE idxpart ( a int, b int ) PARTITION BY RANGE (a); ALTER TABLE idxpart ADD EXCLUDE (a WITH =); DROP TABLE idxpart; -- When (sub)partitions are created, they also contain the constraint CREATE TABLE idxpart ( a int, b int, PRIMARY KEY (a, b) ) PARTITION BY RANGE (a, b); CREATE TABLE idxpart1 PARTITION OF idxpart FOR VALUES FROM (1, 1) TO (10, 10); CREATE TABLE idxpart2 PARTITION OF idxpart FOR VALUES FROM (10, 10) TO (20, 20) PARTITION BY RANGE (b); CREATE TABLE idxpart21 PARTITION OF idxpart2 FOR VALUES FROM (10) TO (15); CREATE TABLE idxpart22 PARTITION OF idxpart2 FOR VALUES FROM (15) TO (20); CREATE TABLE idxpart3 ( b int NOT NULL, a int NOT NULL ); ALTER TABLE idxpart ATTACH PARTITION idxpart3 FOR VALUES FROM (20, 20) TO (30, 30); SELECT conname, contype, conrelid::regclass, conindid::regclass, conkey FROM pg_constraint WHERE conrelid::regclass::text LIKE 'idxpart%' ORDER BY conname; DROP TABLE idxpart; -- Verify that multi-layer partitioning honors the requirement that all -- columns in the partition key must appear in primary/unique key CREATE TABLE idxpart ( a int, b int, PRIMARY KEY (a) ) PARTITION BY RANGE (a); CREATE TABLE idxpart2 PARTITION OF idxpart FOR VALUES FROM (0) TO (1000) PARTITION BY RANGE (b); -- fail DROP TABLE idxpart; -- Ditto for the ATTACH PARTITION case CREATE TABLE idxpart ( a int UNIQUE, b int ) PARTITION BY RANGE (a); CREATE TABLE idxpart1 ( a int NOT NULL, b int, UNIQUE (a, b) ) PARTITION BY RANGE (a, b); ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (1) TO (1000); DROP TABLE idxpart, idxpart1; -- Multi-layer partitioning works correctly in this case: CREATE TABLE idxpart ( a int, b int, PRIMARY KEY (a, b) ) PARTITION BY RANGE (a); CREATE TABLE idxpart2 PARTITION OF idxpart FOR VALUES FROM (0) TO (1000) PARTITION BY RANGE (b); CREATE TABLE idxpart21 PARTITION OF idxpart2 FOR VALUES FROM (0) TO (1000); SELECT conname, contype, conrelid::regclass, conindid::regclass, conkey FROM pg_constraint WHERE conrelid::regclass::text LIKE 'idxpart%' ORDER BY conname; DROP TABLE idxpart; -- If a partitioned table has a unique/PK constraint, then it's not possible -- to drop the corresponding constraint in the children; nor it's possible -- to drop the indexes individually. Dropping the constraint in the parent -- gets rid of the lot. CREATE TABLE idxpart ( i int ) PARTITION BY HASH (i); CREATE TABLE idxpart0 PARTITION OF idxpart (i) FOR VALUES WITH (MODULUS 2, REMAINDER 0); CREATE TABLE idxpart1 PARTITION OF idxpart (i) FOR VALUES WITH (MODULUS 2, REMAINDER 1); ALTER TABLE idxpart0 ADD PRIMARY KEY (i); ALTER TABLE idxpart ADD PRIMARY KEY (i); SELECT indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid, conname, conislocal, coninhcount, connoinherit, convalidated FROM pg_index idx LEFT JOIN pg_inherits inh ON (idx.indexrelid = inh.inhrelid) LEFT JOIN pg_constraint con ON (idx.indexrelid = con.conindid) WHERE indrelid::regclass::text LIKE 'idxpart%' ORDER BY indexrelid::regclass::text COLLATE "C"; DROP INDEX idxpart0_pkey; -- fail DROP INDEX idxpart1_pkey; -- fail ALTER TABLE idxpart0 DROP CONSTRAINT idxpart0_pkey; -- fail ALTER TABLE idxpart1 DROP CONSTRAINT idxpart1_pkey; -- fail ALTER TABLE idxpart DROP CONSTRAINT idxpart_pkey; -- ok SELECT indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid, conname, conislocal, coninhcount, connoinherit, convalidated FROM pg_index idx LEFT JOIN pg_inherits inh ON (idx.indexrelid = inh.inhrelid) LEFT JOIN pg_constraint con ON (idx.indexrelid = con.conindid) WHERE indrelid::regclass::text LIKE 'idxpart%' ORDER BY indexrelid::regclass::text COLLATE "C"; DROP TABLE idxpart; -- If the partition to be attached already has a primary key, fail if -- it doesn't match the parent's PK. CREATE TABLE idxpart ( c1 int PRIMARY KEY, c2 int, c3 varchar(10) ) PARTITION BY RANGE (c1); CREATE TABLE idxpart1 ( LIKE idxpart ); ALTER TABLE idxpart1 ADD PRIMARY KEY (c1, c2); ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (100) TO (200); DROP TABLE idxpart, idxpart1; -- Ditto if there is some distance between the PKs (subpartitioning) CREATE TABLE idxpart ( a int, b int, PRIMARY KEY (a) ) PARTITION BY RANGE (a); CREATE TABLE idxpart1 ( a int NOT NULL, b int ) PARTITION BY RANGE (a); CREATE TABLE idxpart11 ( a int NOT NULL, b int PRIMARY KEY ); ALTER TABLE idxpart1 ATTACH PARTITION idxpart11 FOR VALUES FROM (0) TO (1000); ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (0) TO (10000); DROP TABLE idxpart, idxpart1, idxpart11; -- If a partitioned table has a constraint whose index is not valid, -- attaching a missing partition makes it valid. CREATE TABLE idxpart ( a int ) PARTITION BY RANGE (a); CREATE TABLE idxpart0 ( LIKE idxpart ); ALTER TABLE idxpart0 ADD PRIMARY KEY (a); ALTER TABLE idxpart ATTACH PARTITION idxpart0 FOR VALUES FROM (0) TO (1000); ALTER TABLE ONLY idxpart ADD PRIMARY KEY (a); SELECT indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid, conname, conislocal, coninhcount, connoinherit, convalidated FROM pg_index idx LEFT JOIN pg_inherits inh ON (idx.indexrelid = inh.inhrelid) LEFT JOIN pg_constraint con ON (idx.indexrelid = con.conindid) WHERE indrelid::regclass::text LIKE 'idxpart%' ORDER BY indexrelid::regclass::text COLLATE "C"; ALTER INDEX idxpart_pkey ATTACH PARTITION idxpart0_pkey; SELECT indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid, conname, conislocal, coninhcount, connoinherit, convalidated FROM pg_index idx LEFT JOIN pg_inherits inh ON (idx.indexrelid = inh.inhrelid) LEFT JOIN pg_constraint con ON (idx.indexrelid = con.conindid) WHERE indrelid::regclass::text LIKE 'idxpart%' ORDER BY indexrelid::regclass::text COLLATE "C"; DROP TABLE idxpart; -- Related to the above scenario: ADD PRIMARY KEY on the parent mustn't -- automatically propagate NOT NULL to child columns. CREATE TABLE idxpart ( a int ) PARTITION BY RANGE (a); CREATE TABLE idxpart0 ( LIKE idxpart ); ALTER TABLE idxpart0 ADD UNIQUE (a); ALTER TABLE idxpart ATTACH PARTITION idxpart0 DEFAULT; ALTER TABLE ONLY idxpart ADD PRIMARY KEY (a); -- fail, no NOT NULL constraint ALTER TABLE idxpart0 ALTER COLUMN a SET NOT NULL; ALTER TABLE ONLY idxpart ADD PRIMARY KEY (a); -- now it works ALTER TABLE idxpart0 ALTER COLUMN a DROP NOT NULL; -- fail, pkey needs it DROP TABLE idxpart; -- if a partition has a unique index without a constraint, does not attach -- automatically; creates a new index instead. CREATE TABLE idxpart ( a int, b int ) PARTITION BY RANGE (a); CREATE TABLE idxpart1 ( a int NOT NULL, b int ); CREATE UNIQUE INDEX ON idxpart1 (a); ALTER TABLE idxpart ADD PRIMARY KEY (a); ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (1) TO (1000); SELECT indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid, conname, conislocal, coninhcount, connoinherit, convalidated FROM pg_index idx LEFT JOIN pg_inherits inh ON (idx.indexrelid = inh.inhrelid) LEFT JOIN pg_constraint con ON (idx.indexrelid = con.conindid) WHERE indrelid::regclass::text LIKE 'idxpart%' ORDER BY indexrelid::regclass::text COLLATE "C"; DROP TABLE idxpart; -- Can't attach an index without a corresponding constraint CREATE TABLE idxpart ( a int, b int ) PARTITION BY RANGE (a); CREATE TABLE idxpart1 ( a int NOT NULL, b int ); CREATE UNIQUE INDEX ON idxpart1 (a); ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (1) TO (1000); ALTER TABLE ONLY idxpart ADD PRIMARY KEY (a); ALTER INDEX idxpart_pkey ATTACH PARTITION idxpart1_a_idx; -- fail DROP TABLE idxpart; -- Test that unique constraints are working CREATE TABLE idxpart ( a int, b text, PRIMARY KEY (a, b) ) PARTITION BY RANGE (a); CREATE TABLE idxpart1 PARTITION OF idxpart FOR VALUES FROM (0) TO (100000); CREATE TABLE idxpart2 ( c int, LIKE idxpart ); INSERT INTO idxpart2 (c, a, b) VALUES (42, 572814, 'inserted first'); ALTER TABLE idxpart2 DROP COLUMN c; CREATE UNIQUE INDEX ON idxpart (a); ALTER TABLE idxpart ATTACH PARTITION idxpart2 FOR VALUES FROM (100000) TO (1000000); INSERT INTO idxpart VALUES (0, 'zero'), (42, 'life'), (2 ^ 16, 'sixteen'); INSERT INTO idxpart SELECT 2 ^ g, format('two to power of %s', g) FROM generate_series(15, 17) g; INSERT INTO idxpart VALUES (16, 'sixteen'); INSERT INTO idxpart (b, a) VALUES ('one', 142857), ('two', 285714); INSERT INTO idxpart SELECT a * 2, b || b FROM idxpart WHERE a BETWEEN 2 ^ 16 AND 2 ^ 19; INSERT INTO idxpart VALUES (572814, 'five'); INSERT INTO idxpart VALUES (857142, 'six'); SELECT tableoid::regclass, * FROM idxpart ORDER BY a; DROP TABLE idxpart; -- intentionally leave some objects around CREATE TABLE idxpart ( a int ) PARTITION BY RANGE (a); CREATE TABLE idxpart1 PARTITION OF idxpart FOR VALUES FROM (0) TO (100); CREATE TABLE idxpart2 PARTITION OF idxpart FOR VALUES FROM (100) TO (1000) PARTITION BY RANGE (a); CREATE TABLE idxpart21 PARTITION OF idxpart2 FOR VALUES FROM (100) TO (200); CREATE TABLE idxpart22 PARTITION OF idxpart2 FOR VALUES FROM (200) TO (300); CREATE INDEX ON idxpart22 (a); CREATE INDEX ON ONLY idxpart2 (a); ALTER INDEX idxpart2_a_idx ATTACH PARTITION idxpart22_a_idx; CREATE INDEX ON idxpart (a); CREATE TABLE idxpart_another ( a int, b int, PRIMARY KEY (a, b) ) PARTITION BY RANGE (a); CREATE TABLE idxpart_another_1 PARTITION OF idxpart_another FOR VALUES FROM (0) TO (100); CREATE TABLE idxpart3 ( c int, b int, a int ) PARTITION BY RANGE (a); ALTER TABLE idxpart3 DROP COLUMN b, DROP COLUMN c; CREATE TABLE idxpart31 PARTITION OF idxpart3 FOR VALUES FROM (1000) TO (1200); CREATE TABLE idxpart32 PARTITION OF idxpart3 FOR VALUES FROM (1200) TO (1400); ALTER TABLE idxpart ATTACH PARTITION idxpart3 FOR VALUES FROM (1000) TO (2000); -- More objects intentionally left behind, to verify some pg_dump/pg_upgrade -- behavior; see https://postgr.es/m/20190321204928.GA17535@alvherre.pgsql CREATE SCHEMA regress_indexing; SET search_path TO regress_indexing; CREATE TABLE pk ( a int PRIMARY KEY ) PARTITION BY RANGE (a); CREATE TABLE pk1 PARTITION OF pk FOR VALUES FROM (0) TO (1000); CREATE TABLE pk2 ( b int, a int ); ALTER TABLE pk2 DROP COLUMN b; ALTER TABLE pk2 ALTER a SET NOT NULL; ALTER TABLE pk ATTACH PARTITION pk2 FOR VALUES FROM (1000) TO (2000); CREATE TABLE pk3 PARTITION OF pk FOR VALUES FROM (2000) TO (3000); CREATE TABLE pk4 ( LIKE pk ); ALTER TABLE pk ATTACH PARTITION pk4 FOR VALUES FROM (3000) TO (4000); CREATE TABLE pk5 ( LIKE pk ) PARTITION BY RANGE (a); CREATE TABLE pk51 PARTITION OF pk5 FOR VALUES FROM (4000) TO (4500); CREATE TABLE pk52 PARTITION OF pk5 FOR VALUES FROM (4500) TO (5000); ALTER TABLE pk ATTACH PARTITION pk5 FOR VALUES FROM (4000) TO (5000); RESET search_path; -- Test that covering partitioned indexes work in various cases CREATE TABLE covidxpart ( a int, b int ) PARTITION BY LIST (a); CREATE UNIQUE INDEX ON covidxpart (a) INCLUDE (b); CREATE TABLE covidxpart1 PARTITION OF covidxpart FOR VALUES IN (1); CREATE TABLE covidxpart2 PARTITION OF covidxpart FOR VALUES IN (2); INSERT INTO covidxpart VALUES (1, 1); INSERT INTO covidxpart VALUES (1, 1); CREATE TABLE covidxpart3 ( b int, c int, a int ); ALTER TABLE covidxpart3 DROP c; ALTER TABLE covidxpart ATTACH PARTITION covidxpart3 FOR VALUES IN (3); INSERT INTO covidxpart VALUES (3, 1); INSERT INTO covidxpart VALUES (3, 1); CREATE TABLE covidxpart4 ( b int, a int ); CREATE UNIQUE INDEX ON covidxpart4 (a) INCLUDE (b); CREATE UNIQUE INDEX ON covidxpart4 (a); ALTER TABLE covidxpart ATTACH PARTITION covidxpart4 FOR VALUES IN (4); INSERT INTO covidxpart VALUES (4, 1); INSERT INTO covidxpart VALUES (4, 1); CREATE UNIQUE INDEX ON covidxpart (b) INCLUDE (a); -- should fail -- check that detaching a partition also detaches the primary key constraint CREATE TABLE parted_pk_detach_test ( a int PRIMARY KEY ) PARTITION BY LIST (a); CREATE TABLE parted_pk_detach_test1 PARTITION OF parted_pk_detach_test FOR VALUES IN (1); ALTER TABLE parted_pk_detach_test1 DROP CONSTRAINT parted_pk_detach_test1_pkey; -- should fail ALTER TABLE parted_pk_detach_test DETACH PARTITION parted_pk_detach_test1; ALTER TABLE parted_pk_detach_test1 DROP CONSTRAINT parted_pk_detach_test1_pkey; DROP TABLE parted_pk_detach_test, parted_pk_detach_test1; CREATE TABLE parted_uniq_detach_test ( a int UNIQUE ) PARTITION BY LIST (a); CREATE TABLE parted_uniq_detach_test1 PARTITION OF parted_uniq_detach_test FOR VALUES IN (1); ALTER TABLE parted_uniq_detach_test1 DROP CONSTRAINT parted_uniq_detach_test1_a_key; -- should fail ALTER TABLE parted_uniq_detach_test DETACH PARTITION parted_uniq_detach_test1; ALTER TABLE parted_uniq_detach_test1 DROP CONSTRAINT parted_uniq_detach_test1_a_key; DROP TABLE parted_uniq_detach_test, parted_uniq_detach_test1; pgFormatter-4.2/t/pg-test-files/expected/indirect_toast.sql000066400000000000000000000056771361326045100241540ustar00rootroot00000000000000CREATE TABLE indtoasttest ( descr text, cnt int DEFAULT 0, f1 text, f2 text ); INSERT INTO indtoasttest (descr, f1, f2) VALUES ('two-compressed', repeat('1234567890', 1000), repeat('1234567890', 1000)); INSERT INTO indtoasttest (descr, f1, f2) VALUES ('two-toasted', repeat('1234567890', 30000), repeat('1234567890', 50000)); INSERT INTO indtoasttest (descr, f1, f2) VALUES ('one-compressed,one-null', NULL, repeat('1234567890', 1000)); INSERT INTO indtoasttest (descr, f1, f2) VALUES ('one-toasted,one-null', NULL, repeat('1234567890', 50000)); -- check whether indirect tuples works on the most basic level SELECT descr, substring(make_tuple_indirect (indtoasttest)::text, 1, 200) FROM indtoasttest; -- modification without changing varlenas UPDATE indtoasttest SET cnt = cnt + 1 RETURNING substring(indtoasttest::text, 1, 200); -- modification without modifying assigned value UPDATE indtoasttest SET cnt = cnt + 1, f1 = f1 RETURNING substring(indtoasttest::text, 1, 200); -- modification modifying, but effectively not changing UPDATE indtoasttest SET cnt = cnt + 1, f1 = f1 || '' RETURNING substring(indtoasttest::text, 1, 200); UPDATE indtoasttest SET cnt = cnt + 1, f1 = '-' || f1 || '-' RETURNING substring(indtoasttest::text, 1, 200); SELECT substring(indtoasttest::text, 1, 200) FROM indtoasttest; -- check we didn't screw with main/toast tuple visibility VACUUM FREEZE indtoasttest; SELECT substring(indtoasttest::text, 1, 200) FROM indtoasttest; -- now create a trigger that forces all Datums to be indirect ones CREATE FUNCTION update_using_indirect () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN NEW := make_tuple_indirect (NEW); RETURN NEW; END $$; CREATE TRIGGER indtoasttest_update_indirect BEFORE INSERT OR UPDATE ON indtoasttest FOR EACH ROW EXECUTE PROCEDURE update_using_indirect (); -- modification without changing varlenas UPDATE indtoasttest SET cnt = cnt + 1 RETURNING substring(indtoasttest::text, 1, 200); -- modification without modifying assigned value UPDATE indtoasttest SET cnt = cnt + 1, f1 = f1 RETURNING substring(indtoasttest::text, 1, 200); -- modification modifying, but effectively not changing UPDATE indtoasttest SET cnt = cnt + 1, f1 = f1 || '' RETURNING substring(indtoasttest::text, 1, 200); UPDATE indtoasttest SET cnt = cnt + 1, f1 = '-' || f1 || '-' RETURNING substring(indtoasttest::text, 1, 200); INSERT INTO indtoasttest (descr, f1, f2) VALUES ('one-toasted,one-null, via indirect', repeat('1234567890', 30000), NULL); SELECT substring(indtoasttest::text, 1, 200) FROM indtoasttest; -- check we didn't screw with main/toast tuple visibility VACUUM FREEZE indtoasttest; SELECT substring(indtoasttest::text, 1, 200) FROM indtoasttest; DROP TABLE indtoasttest; DROP FUNCTION update_using_indirect (); pgFormatter-4.2/t/pg-test-files/expected/inet.sql000066400000000000000000000176121361326045100220700ustar00rootroot00000000000000-- -- INET -- -- prepare the table... DROP TABLE INET_TBL; CREATE TABLE INET_TBL ( c cidr, i inet ); INSERT INTO INET_TBL (c, i) VALUES ('192.168.1', '192.168.1.226/24'); INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.0/26', '192.168.1.226'); INSERT INTO INET_TBL (c, i) VALUES ('192.168.1', '192.168.1.0/24'); INSERT INTO INET_TBL (c, i) VALUES ('192.168.1', '192.168.1.0/25'); INSERT INTO INET_TBL (c, i) VALUES ('192.168.1', '192.168.1.255/24'); INSERT INTO INET_TBL (c, i) VALUES ('192.168.1', '192.168.1.255/25'); INSERT INTO INET_TBL (c, i) VALUES ('10', '10.1.2.3/8'); INSERT INTO INET_TBL (c, i) VALUES ('10.0.0.0', '10.1.2.3/8'); INSERT INTO INET_TBL (c, i) VALUES ('10.1.2.3', '10.1.2.3/32'); INSERT INTO INET_TBL (c, i) VALUES ('10.1.2', '10.1.2.3/24'); INSERT INTO INET_TBL (c, i) VALUES ('10.1', '10.1.2.3/16'); INSERT INTO INET_TBL (c, i) VALUES ('10', '10.1.2.3/8'); INSERT INTO INET_TBL (c, i) VALUES ('10', '11.1.2.3/8'); INSERT INTO INET_TBL (c, i) VALUES ('10', '9.1.2.3/8'); INSERT INTO INET_TBL (c, i) VALUES ('10:23::f1', '10:23::f1/64'); INSERT INTO INET_TBL (c, i) VALUES ('10:23::8000/113', '10:23::ffff'); INSERT INTO INET_TBL (c, i) VALUES ('::ffff:1.2.3.4', '::4.3.2.1/24'); -- check that CIDR rejects invalid input: INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.2/30', '192.168.1.226'); INSERT INTO INET_TBL (c, i) VALUES ('1234::1234::1234', '::1.2.3.4'); -- check that CIDR rejects invalid input when converting from text: INSERT INTO INET_TBL (c, i) VALUES (cidr('192.168.1.2/30'), '192.168.1.226'); INSERT INTO INET_TBL (c, i) VALUES (cidr('ffff:ffff:ffff:ffff::/24'), '::192.168.1.226'); SELECT '' AS ten, c AS cidr, i AS inet FROM INET_TBL; -- now test some support functions SELECT '' AS ten, i AS inet, host(i), text(i), family(i) FROM INET_TBL; SELECT '' AS ten, c AS cidr, broadcast(c), i AS inet, broadcast(i) FROM INET_TBL; SELECT '' AS ten, c AS cidr, network(c) AS "network(cidr)", i AS inet, network(i) AS "network(inet)" FROM INET_TBL; SELECT '' AS ten, c AS cidr, masklen(c) AS "masklen(cidr)", i AS inet, masklen(i) AS "masklen(inet)" FROM INET_TBL; SELECT '' AS four, c AS cidr, masklen(c) AS "masklen(cidr)", i AS inet, masklen(i) AS "masklen(inet)" FROM INET_TBL WHERE masklen(c) <= 8; SELECT '' AS six, c AS cidr, i AS inet FROM INET_TBL WHERE c = i; SELECT '' AS ten, i, c, i < c AS lt, i <= c AS le, i = c AS eq, i >= c AS ge, i > c AS gt, i <> c AS ne, i << c AS sb, i <<= c AS sbe, i >> c AS sup, i >>= c AS spe, i && c AS ovr FROM INET_TBL; SELECT max(i) AS max, min(i) AS min FROM INET_TBL; SELECT max(c) AS max, min(c) AS min FROM INET_TBL; -- check the conversion to/from text and set_netmask SELECT '' AS ten, set_masklen(inet (text(i)), 24) FROM INET_TBL; -- check that btree index works correctly CREATE INDEX inet_idx1 ON inet_tbl (i); SET enable_seqscan TO OFF; EXPLAIN ( COSTS OFF ) SELECT * FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr; SELECT * FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr; EXPLAIN ( COSTS OFF ) SELECT * FROM inet_tbl WHERE i <<= '192.168.1.0/24'::cidr; SELECT * FROM inet_tbl WHERE i <<= '192.168.1.0/24'::cidr; EXPLAIN ( COSTS OFF ) SELECT * FROM inet_tbl WHERE '192.168.1.0/24'::cidr >>= i; SELECT * FROM inet_tbl WHERE '192.168.1.0/24'::cidr >>= i; EXPLAIN ( COSTS OFF ) SELECT * FROM inet_tbl WHERE '192.168.1.0/24'::cidr >> i; SELECT * FROM inet_tbl WHERE '192.168.1.0/24'::cidr >> i; SET enable_seqscan TO ON; DROP INDEX inet_idx1; -- check that gist index works correctly CREATE INDEX inet_idx2 ON inet_tbl USING gist (i inet_ops); SET enable_seqscan TO OFF; SELECT * FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i <<= '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i && '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i >>= '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i >> '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i < '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i <= '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i = '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i >= '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i > '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i <> '192.168.1.0/24'::cidr ORDER BY i; -- test index-only scans EXPLAIN ( COSTS OFF ) SELECT i FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i; SELECT i FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i; SET enable_seqscan TO ON; DROP INDEX inet_idx2; -- check that spgist index works correctly CREATE INDEX inet_idx3 ON inet_tbl USING spgist (i); SET enable_seqscan TO OFF; SELECT * FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i <<= '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i && '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i >>= '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i >> '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i < '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i <= '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i = '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i >= '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i > '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i <> '192.168.1.0/24'::cidr ORDER BY i; -- test index-only scans EXPLAIN ( COSTS OFF ) SELECT i FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i; SELECT i FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i; SET enable_seqscan TO ON; DROP INDEX inet_idx3; -- simple tests of inet boolean and arithmetic operators SELECT i, ~ i AS "~i" FROM inet_tbl; SELECT i, c, i & c AS "and" FROM inet_tbl; SELECT i, c, i | c AS "or" FROM inet_tbl; SELECT i, i + 500 AS "i+500" FROM inet_tbl; SELECT i, i - 500 AS "i-500" FROM inet_tbl; SELECT i, c, i - c AS "minus" FROM inet_tbl; SELECT '127.0.0.1'::inet + 257; SELECT ('127.0.0.1'::inet + 257) - 257; SELECT '127::1'::inet + 257; SELECT ('127::1'::inet + 257) - 257; SELECT '127.0.0.2'::inet - ('127.0.0.2'::inet + 500); SELECT '127.0.0.2'::inet - ('127.0.0.2'::inet - 500); SELECT '127::2'::inet - ('127::2'::inet + 500); SELECT '127::2'::inet - ('127::2'::inet - 500); -- these should give overflow errors: SELECT '127.0.0.1'::inet + 10000000000; SELECT '127.0.0.1'::inet - 10000000000; SELECT '126::1'::inet - '127::2'::inet; SELECT '127::1'::inet - '126::2'::inet; -- but not these SELECT '127::1'::inet + 10000000000; SELECT '127::1'::inet - '127::2'::inet; -- insert one more row with addressed from different families INSERT INTO INET_TBL (c, i) VALUES ('10', '10::/8'); -- now, this one should fail SELECT inet_merge (c, i) FROM INET_TBL; -- fix it by inet_same_family() condition SELECT inet_merge (c, i) FROM INET_TBL WHERE inet_same_family (c, i); pgFormatter-4.2/t/pg-test-files/expected/inherit.sql000066400000000000000000001213631361326045100225720ustar00rootroot00000000000000-- -- Test inheritance features -- CREATE TABLE a ( aa text ); CREATE TABLE b ( bb text ) INHERITS ( a ); CREATE TABLE c ( cc text ) INHERITS ( a ); CREATE TABLE d ( dd text ) INHERITS ( b, c, a ); INSERT INTO a (aa) VALUES ('aaa'); INSERT INTO a (aa) VALUES ('aaaa'); INSERT INTO a (aa) VALUES ('aaaaa'); INSERT INTO a (aa) VALUES ('aaaaaa'); INSERT INTO a (aa) VALUES ('aaaaaaa'); INSERT INTO a (aa) VALUES ('aaaaaaaa'); INSERT INTO b (aa) VALUES ('bbb'); INSERT INTO b (aa) VALUES ('bbbb'); INSERT INTO b (aa) VALUES ('bbbbb'); INSERT INTO b (aa) VALUES ('bbbbbb'); INSERT INTO b (aa) VALUES ('bbbbbbb'); INSERT INTO b (aa) VALUES ('bbbbbbbb'); INSERT INTO c (aa) VALUES ('ccc'); INSERT INTO c (aa) VALUES ('cccc'); INSERT INTO c (aa) VALUES ('ccccc'); INSERT INTO c (aa) VALUES ('cccccc'); INSERT INTO c (aa) VALUES ('ccccccc'); INSERT INTO c (aa) VALUES ('cccccccc'); INSERT INTO d (aa) VALUES ('ddd'); INSERT INTO d (aa) VALUES ('dddd'); INSERT INTO d (aa) VALUES ('ddddd'); INSERT INTO d (aa) VALUES ('dddddd'); INSERT INTO d (aa) VALUES ('ddddddd'); INSERT INTO d (aa) VALUES ('dddddddd'); SELECT relname, a.* FROM a, pg_class WHERE a.tableoid = pg_class.oid; SELECT relname, b.* FROM b, pg_class WHERE b.tableoid = pg_class.oid; SELECT relname, c.* FROM c, pg_class WHERE c.tableoid = pg_class.oid; SELECT relname, d.* FROM d, pg_class WHERE d.tableoid = pg_class.oid; SELECT relname, a.* FROM ONLY a, pg_class WHERE a.tableoid = pg_class.oid; SELECT relname, b.* FROM ONLY b, pg_class WHERE b.tableoid = pg_class.oid; SELECT relname, c.* FROM ONLY c, pg_class WHERE c.tableoid = pg_class.oid; SELECT relname, d.* FROM ONLY d, pg_class WHERE d.tableoid = pg_class.oid; UPDATE a SET aa = 'zzzz' WHERE aa = 'aaaa'; UPDATE ONLY a SET aa = 'zzzzz' WHERE aa = 'aaaaa'; UPDATE b SET aa = 'zzz' WHERE aa = 'aaa'; UPDATE ONLY b SET aa = 'zzz' WHERE aa = 'aaa'; UPDATE a SET aa = 'zzzzzz' WHERE aa LIKE 'aaa%'; SELECT relname, a.* FROM a, pg_class WHERE a.tableoid = pg_class.oid; SELECT relname, b.* FROM b, pg_class WHERE b.tableoid = pg_class.oid; SELECT relname, c.* FROM c, pg_class WHERE c.tableoid = pg_class.oid; SELECT relname, d.* FROM d, pg_class WHERE d.tableoid = pg_class.oid; SELECT relname, a.* FROM ONLY a, pg_class WHERE a.tableoid = pg_class.oid; SELECT relname, b.* FROM ONLY b, pg_class WHERE b.tableoid = pg_class.oid; SELECT relname, c.* FROM ONLY c, pg_class WHERE c.tableoid = pg_class.oid; SELECT relname, d.* FROM ONLY d, pg_class WHERE d.tableoid = pg_class.oid; UPDATE b SET aa = 'new'; SELECT relname, a.* FROM a, pg_class WHERE a.tableoid = pg_class.oid; SELECT relname, b.* FROM b, pg_class WHERE b.tableoid = pg_class.oid; SELECT relname, c.* FROM c, pg_class WHERE c.tableoid = pg_class.oid; SELECT relname, d.* FROM d, pg_class WHERE d.tableoid = pg_class.oid; SELECT relname, a.* FROM ONLY a, pg_class WHERE a.tableoid = pg_class.oid; SELECT relname, b.* FROM ONLY b, pg_class WHERE b.tableoid = pg_class.oid; SELECT relname, c.* FROM ONLY c, pg_class WHERE c.tableoid = pg_class.oid; SELECT relname, d.* FROM ONLY d, pg_class WHERE d.tableoid = pg_class.oid; UPDATE a SET aa = 'new'; DELETE FROM ONLY c WHERE aa = 'new'; SELECT relname, a.* FROM a, pg_class WHERE a.tableoid = pg_class.oid; SELECT relname, b.* FROM b, pg_class WHERE b.tableoid = pg_class.oid; SELECT relname, c.* FROM c, pg_class WHERE c.tableoid = pg_class.oid; SELECT relname, d.* FROM d, pg_class WHERE d.tableoid = pg_class.oid; SELECT relname, a.* FROM ONLY a, pg_class WHERE a.tableoid = pg_class.oid; SELECT relname, b.* FROM ONLY b, pg_class WHERE b.tableoid = pg_class.oid; SELECT relname, c.* FROM ONLY c, pg_class WHERE c.tableoid = pg_class.oid; SELECT relname, d.* FROM ONLY d, pg_class WHERE d.tableoid = pg_class.oid; DELETE FROM a; SELECT relname, a.* FROM a, pg_class WHERE a.tableoid = pg_class.oid; SELECT relname, b.* FROM b, pg_class WHERE b.tableoid = pg_class.oid; SELECT relname, c.* FROM c, pg_class WHERE c.tableoid = pg_class.oid; SELECT relname, d.* FROM d, pg_class WHERE d.tableoid = pg_class.oid; SELECT relname, a.* FROM ONLY a, pg_class WHERE a.tableoid = pg_class.oid; SELECT relname, b.* FROM ONLY b, pg_class WHERE b.tableoid = pg_class.oid; SELECT relname, c.* FROM ONLY c, pg_class WHERE c.tableoid = pg_class.oid; SELECT relname, d.* FROM ONLY d, pg_class WHERE d.tableoid = pg_class.oid; -- Confirm PRIMARY KEY adds NOT NULL constraint to child table CREATE TEMP TABLE z ( b text, PRIMARY KEY (aa, b) ) INHERITS ( a ); INSERT INTO z VALUES (NULL, 'text'); -- should fail -- Check inherited UPDATE with all children excluded CREATE TABLE some_tab ( a int, b int ); CREATE TABLE some_tab_child () INHERITS ( some_tab ); INSERT INTO some_tab_child VALUES (1, 2); EXPLAIN ( VERBOSE, COSTS OFF ) UPDATE some_tab SET a = a + 1 WHERE FALSE; UPDATE some_tab SET a = a + 1 WHERE FALSE; EXPLAIN ( VERBOSE, COSTS OFF ) UPDATE some_tab SET a = a + 1 WHERE FALSE RETURNING b, a; UPDATE some_tab SET a = a + 1 WHERE FALSE RETURNING b, a; TABLE some_tab; DROP TABLE some_tab CASCADE; -- Check UPDATE with inherited target and an inherited source table CREATE temp TABLE foo ( f1 int, f2 int ); CREATE temp TABLE foo2 ( f3 int ) INHERITS ( foo ); CREATE temp TABLE bar ( f1 int, f2 int ); CREATE temp TABLE bar2 ( f3 int ) INHERITS ( bar ); INSERT INTO foo VALUES (1, 1); INSERT INTO foo VALUES (3, 3); INSERT INTO foo2 VALUES (2, 2, 2); INSERT INTO foo2 VALUES (3, 3, 3); INSERT INTO bar VALUES (1, 1); INSERT INTO bar VALUES (2, 2); INSERT INTO bar VALUES (3, 3); INSERT INTO bar VALUES (4, 4); INSERT INTO bar2 VALUES (1, 1, 1); INSERT INTO bar2 VALUES (2, 2, 2); INSERT INTO bar2 VALUES (3, 3, 3); INSERT INTO bar2 VALUES (4, 4, 4); UPDATE bar SET f2 = f2 + 100 WHERE f1 IN ( SELECT f1 FROM foo); SELECT tableoid::regclass::text AS relname, bar.* FROM bar ORDER BY 1, 2; -- Check UPDATE with inherited target and an appendrel subquery UPDATE bar SET f2 = f2 + 100 FROM ( SELECT f1 FROM foo UNION ALL SELECT f1 + 3 FROM foo) ss WHERE bar.f1 = ss.f1; SELECT tableoid::regclass::text AS relname, bar.* FROM bar ORDER BY 1, 2; -- Check UPDATE with *partitioned* inherited target and an appendrel subquery CREATE TABLE some_tab ( a int ); INSERT INTO some_tab VALUES (0); CREATE TABLE some_tab_child () INHERITS ( some_tab ); INSERT INTO some_tab_child VALUES (1); CREATE TABLE parted_tab ( a int, b char ) PARTITION BY LIST (a); CREATE TABLE parted_tab_part1 PARTITION OF parted_tab FOR VALUES IN (1); CREATE TABLE parted_tab_part2 PARTITION OF parted_tab FOR VALUES IN (2); CREATE TABLE parted_tab_part3 PARTITION OF parted_tab FOR VALUES IN (3); INSERT INTO parted_tab VALUES (1, 'a'), (2, 'a'), (3, 'a'); UPDATE parted_tab SET b = 'b' FROM ( SELECT a FROM some_tab UNION ALL SELECT a + 1 FROM some_tab) ss (a) WHERE parted_tab.a = ss.a; SELECT tableoid::regclass::text AS relname, parted_tab.* FROM parted_tab ORDER BY 1, 2; TRUNCATE parted_tab; INSERT INTO parted_tab VALUES (1, 'a'), (2, 'a'), (3, 'a'); UPDATE parted_tab SET b = 'b' FROM ( SELECT 0 FROM parted_tab UNION ALL SELECT 1 FROM parted_tab) ss (a) WHERE parted_tab.a = ss.a; SELECT tableoid::regclass::text AS relname, parted_tab.* FROM parted_tab ORDER BY 1, 2; -- modifies partition key, but no rows will actually be updated EXPLAIN UPDATE parted_tab SET a = 2 WHERE FALSE; DROP TABLE parted_tab; -- Check UPDATE with multi-level partitioned inherited target CREATE TABLE mlparted_tab ( a int, b char, c text ) PARTITION BY LIST (a); CREATE TABLE mlparted_tab_part1 PARTITION OF mlparted_tab FOR VALUES IN (1); CREATE TABLE mlparted_tab_part2 PARTITION OF mlparted_tab FOR VALUES IN (2) PARTITION BY LIST (b); CREATE TABLE mlparted_tab_part3 PARTITION OF mlparted_tab FOR VALUES IN (3); CREATE TABLE mlparted_tab_part2a PARTITION OF mlparted_tab_part2 FOR VALUES IN ('a'); CREATE TABLE mlparted_tab_part2b PARTITION OF mlparted_tab_part2 FOR VALUES IN ('b'); INSERT INTO mlparted_tab VALUES (1, 'a'), (2, 'a'), (2, 'b'), (3, 'a'); UPDATE mlparted_tab mlp SET c = 'xxx' FROM ( SELECT a FROM some_tab UNION ALL SELECT a + 1 FROM some_tab) ss (a) WHERE (mlp.a = ss.a AND mlp.b = 'b') OR mlp.a = 3; SELECT tableoid::regclass::text AS relname, mlparted_tab.* FROM mlparted_tab ORDER BY 1, 2; DROP TABLE mlparted_tab; DROP TABLE some_tab CASCADE; /* Test multiple inheritance of column defaults */ CREATE TABLE firstparent ( tomorrow date DEFAULT now() ::date + 1 ); CREATE TABLE secondparent ( tomorrow date DEFAULT now() ::date + 1 ); CREATE TABLE jointchild () INHERITS ( firstparent, secondparent ); -- ok CREATE TABLE thirdparent ( tomorrow date DEFAULT now() ::date - 1 ); CREATE TABLE otherchild () INHERITS ( firstparent, thirdparent ); -- not ok CREATE TABLE otherchild ( tomorrow date DEFAULT now() ) INHERITS ( firstparent, thirdparent ); -- ok, child resolves ambiguous default DROP TABLE firstparent, secondparent, jointchild, thirdparent, otherchild; -- Test changing the type of inherited columns INSERT INTO d VALUES ('test', 'one', 'two', 'three'); ALTER TABLE a ALTER COLUMN aa TYPE integer USING bit_length(aa); SELECT * FROM d; -- Test non-inheritable parent constraints CREATE TABLE p1 ( ff1 int ); ALTER TABLE p1 ADD CONSTRAINT p1chk CHECK (ff1 > 0) NO inherit; ALTER TABLE p1 ADD CONSTRAINT p2chk CHECK (ff1 > 10); -- connoinherit should be true for NO INHERIT constraint SELECT pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.connoinherit FROM pg_class AS pc INNER JOIN pg_constraint AS pgc ON (pgc.conrelid = pc.oid) WHERE pc.relname = 'p1' ORDER BY 1, 2; -- Test that child does not inherit NO INHERIT constraints CREATE TABLE c1 () INHERITS ( p1 ); \d p1 \d c1 -- Test that child does not override inheritable constraints of the parent CREATE TABLE c2 ( CONSTRAINT p2chk CHECK (ff1 > 10) NO inherit ) INHERITS ( p1 ); --fails DROP TABLE p1 CASCADE; -- Tests for casting between the rowtypes of parent and child -- tables. See the pgsql-hackers thread beginning Dec. 4/04 CREATE TABLE base ( i integer ); CREATE TABLE derived () INHERITS ( base ); CREATE TABLE more_derived ( LIKE derived, b int ) INHERITS ( derived ); INSERT INTO derived (i) VALUES (0); SELECT derived::base FROM derived; SELECT NULL::derived::base; -- remove redundant conversions. EXPLAIN ( VERBOSE ON, COSTS OFF ) SELECT ROW (i, b)::more_derived::derived::base FROM more_derived; EXPLAIN ( VERBOSE ON, COSTS OFF ) SELECT (1, 2)::more_derived::derived::base; DROP TABLE more_derived; DROP TABLE derived; DROP TABLE base; CREATE TABLE p1 ( ff1 int ); CREATE TABLE p2 ( f1 text ); CREATE FUNCTION p2text (p2) RETURNS text AS 'select $1.f1' LANGUAGE sql; CREATE TABLE c1 ( f3 int ) INHERITS ( p1, p2 ); INSERT INTO c1 VALUES (123456789, 'hi', 42); SELECT p2text (c1.*) FROM c1; DROP FUNCTION p2text (p2); DROP TABLE c1; DROP TABLE p2; DROP TABLE p1; CREATE TABLE ac ( aa text ); ALTER TABLE ac ADD CONSTRAINT ac_check CHECK (aa IS NOT NULL); CREATE TABLE bc ( bb text ) INHERITS ( ac ); SELECT pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) AS consrc FROM pg_class AS pc INNER JOIN pg_constraint AS pgc ON (pgc.conrelid = pc.oid) WHERE pc.relname IN ('ac', 'bc') ORDER BY 1, 2; INSERT INTO ac (aa) VALUES (NULL); INSERT INTO bc (aa) VALUES (NULL); ALTER TABLE bc DROP CONSTRAINT ac_check; -- fail, disallowed ALTER TABLE ac DROP CONSTRAINT ac_check; SELECT pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) AS consrc FROM pg_class AS pc INNER JOIN pg_constraint AS pgc ON (pgc.conrelid = pc.oid) WHERE pc.relname IN ('ac', 'bc') ORDER BY 1, 2; -- try the unnamed-constraint case ALTER TABLE ac ADD CHECK (aa IS NOT NULL); SELECT pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) AS consrc FROM pg_class AS pc INNER JOIN pg_constraint AS pgc ON (pgc.conrelid = pc.oid) WHERE pc.relname IN ('ac', 'bc') ORDER BY 1, 2; INSERT INTO ac (aa) VALUES (NULL); INSERT INTO bc (aa) VALUES (NULL); ALTER TABLE bc DROP CONSTRAINT ac_aa_check; -- fail, disallowed ALTER TABLE ac DROP CONSTRAINT ac_aa_check; SELECT pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) AS consrc FROM pg_class AS pc INNER JOIN pg_constraint AS pgc ON (pgc.conrelid = pc.oid) WHERE pc.relname IN ('ac', 'bc') ORDER BY 1, 2; ALTER TABLE ac ADD CONSTRAINT ac_check CHECK (aa IS NOT NULL); ALTER TABLE bc NO inherit ac; SELECT pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) AS consrc FROM pg_class AS pc INNER JOIN pg_constraint AS pgc ON (pgc.conrelid = pc.oid) WHERE pc.relname IN ('ac', 'bc') ORDER BY 1, 2; ALTER TABLE bc DROP CONSTRAINT ac_check; SELECT pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) AS consrc FROM pg_class AS pc INNER JOIN pg_constraint AS pgc ON (pgc.conrelid = pc.oid) WHERE pc.relname IN ('ac', 'bc') ORDER BY 1, 2; ALTER TABLE ac DROP CONSTRAINT ac_check; SELECT pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) AS consrc FROM pg_class AS pc INNER JOIN pg_constraint AS pgc ON (pgc.conrelid = pc.oid) WHERE pc.relname IN ('ac', 'bc') ORDER BY 1, 2; DROP TABLE bc; DROP TABLE ac; CREATE TABLE ac ( a int CONSTRAINT check_a CHECK (a <> 0) ); CREATE TABLE bc ( a int CONSTRAINT check_a CHECK (a <> 0), b int CONSTRAINT check_b CHECK (b <> 0) ) INHERITS ( ac ); SELECT pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) AS consrc FROM pg_class AS pc INNER JOIN pg_constraint AS pgc ON (pgc.conrelid = pc.oid) WHERE pc.relname IN ('ac', 'bc') ORDER BY 1, 2; DROP TABLE bc; DROP TABLE ac; CREATE TABLE ac ( a int CONSTRAINT check_a CHECK (a <> 0) ); CREATE TABLE bc ( b int CONSTRAINT check_b CHECK (b <> 0) ); CREATE TABLE cc ( c int CONSTRAINT check_c CHECK (c <> 0) ) INHERITS ( ac, bc ); SELECT pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) AS consrc FROM pg_class AS pc INNER JOIN pg_constraint AS pgc ON (pgc.conrelid = pc.oid) WHERE pc.relname IN ('ac', 'bc', 'cc') ORDER BY 1, 2; ALTER TABLE cc NO inherit bc; SELECT pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) AS consrc FROM pg_class AS pc INNER JOIN pg_constraint AS pgc ON (pgc.conrelid = pc.oid) WHERE pc.relname IN ('ac', 'bc', 'cc') ORDER BY 1, 2; DROP TABLE cc; DROP TABLE bc; DROP TABLE ac; CREATE TABLE p1 ( f1 int ); CREATE TABLE p2 ( f2 int ); CREATE TABLE c1 ( f3 int ) INHERITS ( p1, p2 ); INSERT INTO c1 VALUES (1, - 1, 2); ALTER TABLE p2 ADD CONSTRAINT cc CHECK (f2 > 0); -- fail ALTER TABLE p2 ADD CHECK (f2 > 0); -- check it without a name, too DELETE FROM c1; INSERT INTO c1 VALUES (1, 1, 2); ALTER TABLE p2 ADD CHECK (f2 > 0); INSERT INTO c1 VALUES (1, - 1, 2); -- fail CREATE TABLE c2 ( f3 int ) INHERITS ( p1, p2 ); \d c2 CREATE TABLE c3 ( f4 int ) INHERITS ( c1, c2 ); \d c3 DROP TABLE p1 CASCADE; DROP TABLE p2 CASCADE; CREATE TABLE pp1 ( f1 int ); CREATE TABLE cc1 ( f2 text, f3 int ) INHERITS ( pp1 ); ALTER TABLE pp1 ADD COLUMN a1 int CHECK (a1 > 0); \d cc1 CREATE TABLE cc2 ( f4 float ) INHERITS ( pp1, cc1 ); \d cc2 ALTER TABLE pp1 ADD COLUMN a2 int CHECK (a2 > 0); \d cc2 DROP TABLE pp1 CASCADE; -- Test for renaming in simple multiple inheritance CREATE TABLE inht1 ( a int, b int ); CREATE TABLE inhs1 ( b int, c int ); CREATE TABLE inhts ( d int ) INHERITS ( inht1, inhs1 ); ALTER TABLE inht1 RENAME a TO aa; ALTER TABLE inht1 RENAME b TO bb; -- to be failed ALTER TABLE inhts RENAME aa TO aaa; -- to be failed ALTER TABLE inhts RENAME d TO dd; \d+ inhts DROP TABLE inhts; -- Test for renaming in diamond inheritance CREATE TABLE inht2 ( x int ) INHERITS ( inht1 ); CREATE TABLE inht3 ( y int ) INHERITS ( inht1 ); CREATE TABLE inht4 ( z int ) INHERITS ( inht2, inht3 ); ALTER TABLE inht1 RENAME aa TO aaa; \d+ inht4 CREATE TABLE inhts ( d int ) INHERITS ( inht2, inhs1 ); ALTER TABLE inht1 RENAME aaa TO aaaa; ALTER TABLE inht1 RENAME b TO bb; -- to be failed \d+ inhts WITH RECURSIVE r AS ( SELECT 'inht1'::regclass AS inhrelid UNION ALL SELECT c.inhrelid FROM pg_inherits c, r WHERE r.inhrelid = c.inhparent ) SELECT a.attrelid::regclass, a.attname, a.attinhcount, e.expected FROM ( SELECT inhrelid, count(*) AS expected FROM pg_inherits WHERE inhparent IN ( SELECT inhrelid FROM r) GROUP BY inhrelid) e JOIN pg_attribute a ON e.inhrelid = a.attrelid WHERE NOT attislocal ORDER BY a.attrelid::regclass::name, a.attnum; DROP TABLE inht1, inhs1 CASCADE; -- Test non-inheritable indices [UNIQUE, EXCLUDE] constraints CREATE TABLE test_constraints ( id int, val1 varchar, val2 int, UNIQUE (val1, val2) ); CREATE TABLE test_constraints_inh () INHERITS ( test_constraints ); \d+ test_constraints ALTER TABLE ONLY test_constraints DROP CONSTRAINT test_constraints_val1_val2_key; \d+ test_constraints \d+ test_constraints_inh DROP TABLE test_constraints_inh; DROP TABLE test_constraints; CREATE TABLE test_ex_constraints ( c circle, EXCLUDE USING gist (c WITH &&) ); CREATE TABLE test_ex_constraints_inh () INHERITS ( test_ex_constraints ); \d+ test_ex_constraints ALTER TABLE test_ex_constraints DROP CONSTRAINT test_ex_constraints_c_excl; \d+ test_ex_constraints \d+ test_ex_constraints_inh DROP TABLE test_ex_constraints_inh; DROP TABLE test_ex_constraints; -- Test non-inheritable foreign key constraints CREATE TABLE test_primary_constraints ( id int PRIMARY KEY ); CREATE TABLE test_foreign_constraints ( id1 int REFERENCES test_primary_constraints (id) ); CREATE TABLE test_foreign_constraints_inh () INHERITS ( test_foreign_constraints ); \d+ test_primary_constraints \d+ test_foreign_constraints ALTER TABLE test_foreign_constraints DROP CONSTRAINT test_foreign_constraints_id1_fkey; \d+ test_foreign_constraints \d+ test_foreign_constraints_inh DROP TABLE test_foreign_constraints_inh; DROP TABLE test_foreign_constraints; DROP TABLE test_primary_constraints; -- Test foreign key behavior CREATE TABLE inh_fk_1 ( a int PRIMARY KEY ); INSERT INTO inh_fk_1 VALUES (1), (2), (3); CREATE TABLE inh_fk_2 ( x int PRIMARY KEY, y int REFERENCES inh_fk_1 ON DELETE CASCADE ); INSERT INTO inh_fk_2 VALUES (11, 1), (22, 2), (33, 3); CREATE TABLE inh_fk_2_child () INHERITS ( inh_fk_2 ); INSERT INTO inh_fk_2_child VALUES (111, 1), (222, 2); DELETE FROM inh_fk_1 WHERE a = 1; SELECT * FROM inh_fk_1 ORDER BY 1; SELECT * FROM inh_fk_2 ORDER BY 1, 2; DROP TABLE inh_fk_1, inh_fk_2, inh_fk_2_child; -- Test that parent and child CHECK constraints can be created in either order CREATE TABLE p1 ( f1 int ); CREATE TABLE p1_c1 () INHERITS ( p1 ); ALTER TABLE p1 ADD CONSTRAINT inh_check_constraint1 CHECK (f1 > 0); ALTER TABLE p1_c1 ADD CONSTRAINT inh_check_constraint1 CHECK (f1 > 0); ALTER TABLE p1_c1 ADD CONSTRAINT inh_check_constraint2 CHECK (f1 < 10); ALTER TABLE p1 ADD CONSTRAINT inh_check_constraint2 CHECK (f1 < 10); SELECT conrelid::regclass::text AS relname, conname, conislocal, coninhcount FROM pg_constraint WHERE conname LIKE 'inh\_check\_constraint%' ORDER BY 1, 2; DROP TABLE p1 CASCADE; -- Test that a valid child can have not-valid parent, but not vice versa CREATE TABLE invalid_check_con ( f1 int ); CREATE TABLE invalid_check_con_child () INHERITS ( invalid_check_con ); ALTER TABLE invalid_check_con_child ADD CONSTRAINT inh_check_constraint CHECK (f1 > 0) NOT valid; ALTER TABLE invalid_check_con ADD CONSTRAINT inh_check_constraint CHECK (f1 > 0); -- fail ALTER TABLE invalid_check_con_child DROP CONSTRAINT inh_check_constraint; INSERT INTO invalid_check_con VALUES (0); ALTER TABLE invalid_check_con_child ADD CONSTRAINT inh_check_constraint CHECK (f1 > 0); ALTER TABLE invalid_check_con ADD CONSTRAINT inh_check_constraint CHECK (f1 > 0) NOT valid; INSERT INTO invalid_check_con VALUES (0); -- fail INSERT INTO invalid_check_con_child VALUES (0); -- fail SELECT conrelid::regclass::text AS relname, conname, convalidated, conislocal, coninhcount, connoinherit FROM pg_constraint WHERE conname LIKE 'inh\_check\_constraint%' ORDER BY 1, 2; -- We don't drop the invalid_check_con* tables, to test dump/reload with -- -- Test parameterized append plans for inheritance trees -- CREATE temp TABLE patest0 ( id, x ) AS SELECT x, x FROM generate_series(0, 1000) x; CREATE temp TABLE patest1 () INHERITS ( patest0 ); INSERT INTO patest1 SELECT x, x FROM generate_series(0, 1000) x; CREATE temp TABLE patest2 () INHERITS ( patest0 ); INSERT INTO patest2 SELECT x, x FROM generate_series(0, 1000) x; CREATE INDEX patest0i ON patest0 (id); CREATE INDEX patest1i ON patest1 (id); CREATE INDEX patest2i ON patest2 (id); ANALYZE patest0; ANALYZE patest1; ANALYZE patest2; EXPLAIN ( COSTS OFF ) SELECT * FROM patest0 JOIN ( SELECT f1 FROM int4_tbl LIMIT 1) ss ON id = f1; SELECT * FROM patest0 JOIN ( SELECT f1 FROM int4_tbl LIMIT 1) ss ON id = f1; DROP INDEX patest2i; EXPLAIN ( COSTS OFF ) SELECT * FROM patest0 JOIN ( SELECT f1 FROM int4_tbl LIMIT 1) ss ON id = f1; SELECT * FROM patest0 JOIN ( SELECT f1 FROM int4_tbl LIMIT 1) ss ON id = f1; DROP TABLE patest0 CASCADE; -- -- Test merge-append plans for inheritance trees -- CREATE TABLE matest0 ( id serial PRIMARY KEY, name text ); CREATE TABLE matest1 ( id integer PRIMARY KEY ) INHERITS ( matest0 ); CREATE TABLE matest2 ( id integer PRIMARY KEY ) INHERITS ( matest0 ); CREATE TABLE matest3 ( id integer PRIMARY KEY ) INHERITS ( matest0 ); CREATE INDEX matest0i ON matest0 ((1 - id)); CREATE INDEX matest1i ON matest1 ((1 - id)); -- create index matest2i on matest2 ((1-id)); -- intentionally missing CREATE INDEX matest3i ON matest3 ((1 - id)); INSERT INTO matest1 (name) VALUES ('Test 1'); INSERT INTO matest1 (name) VALUES ('Test 2'); INSERT INTO matest2 (name) VALUES ('Test 3'); INSERT INTO matest2 (name) VALUES ('Test 4'); INSERT INTO matest3 (name) VALUES ('Test 5'); INSERT INTO matest3 (name) VALUES ('Test 6'); SET enable_indexscan = OFF; -- force use of seqscan/sort, so no merge EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM matest0 ORDER BY 1 - id; SELECT * FROM matest0 ORDER BY 1 - id; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT min(1 - id) FROM matest0; SELECT min(1 - id) FROM matest0; RESET enable_indexscan; SET enable_seqscan = OFF; -- plan with fewest seqscans should be merge SET enable_parallel_append = OFF; -- Don't let parallel-append interfere EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM matest0 ORDER BY 1 - id; SELECT * FROM matest0 ORDER BY 1 - id; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT min(1 - id) FROM matest0; SELECT min(1 - id) FROM matest0; RESET enable_seqscan; RESET enable_parallel_append; DROP TABLE matest0 CASCADE; -- -- Check that use of an index with an extraneous column doesn't produce -- a plan with extraneous sorting -- CREATE TABLE matest0 ( a int, b int, c int, d int ); CREATE TABLE matest1 () INHERITS ( matest0 ); CREATE INDEX matest0i ON matest0 (b, c); CREATE INDEX matest1i ON matest1 (b, c); SET enable_nestloop = OFF; -- we want a plan with two MergeAppends EXPLAIN ( COSTS OFF ) SELECT t1.* FROM matest0 t1, matest0 t2 WHERE t1.b = t2.b AND t2.c = t2.d ORDER BY t1.b LIMIT 10; RESET enable_nestloop; DROP TABLE matest0 CASCADE; -- -- Test merge-append for UNION ALL append relations -- SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = OFF; -- Check handling of duplicated, constant, or volatile targetlist items EXPLAIN ( COSTS OFF ) SELECT thousand, tenthous FROM tenk1 UNION ALL SELECT thousand, thousand FROM tenk1 ORDER BY thousand, tenthous; EXPLAIN ( COSTS OFF ) SELECT thousand, tenthous, thousand + tenthous AS x FROM tenk1 UNION ALL SELECT 42, 42, hundred FROM tenk1 ORDER BY thousand, tenthous; EXPLAIN ( COSTS OFF ) SELECT thousand, tenthous FROM tenk1 UNION ALL SELECT thousand, random()::integer FROM tenk1 ORDER BY thousand, tenthous; -- Check min/max aggregate optimization EXPLAIN ( COSTS OFF ) SELECT min(x) FROM ( SELECT unique1 AS x FROM tenk1 a UNION ALL SELECT unique2 AS x FROM tenk1 b) s; EXPLAIN ( COSTS OFF ) SELECT min(y) FROM ( SELECT unique1 AS x, unique1 AS y FROM tenk1 a UNION ALL SELECT unique2 AS x, unique2 AS y FROM tenk1 b) s; -- XXX planner doesn't recognize that index on unique2 is sufficiently sorted EXPLAIN ( COSTS OFF ) SELECT x, y FROM ( SELECT thousand AS x, tenthous AS y FROM tenk1 a UNION ALL SELECT unique2 AS x, unique2 AS y FROM tenk1 b) s ORDER BY x, y; -- exercise rescan code path via a repeatedly-evaluated subquery EXPLAIN ( COSTS OFF ) SELECT ARRAY ( SELECT f.i FROM (( SELECT d + g.i FROM generate_series(4, 30, 3) d ORDER BY 1) UNION ALL ( SELECT d + g.i FROM generate_series(0, 30, 5) d ORDER BY 1)) f (i) ORDER BY f.i LIMIT 10) FROM generate_series(1, 3) g (i); SELECT ARRAY ( SELECT f.i FROM (( SELECT d + g.i FROM generate_series(4, 30, 3) d ORDER BY 1) UNION ALL ( SELECT d + g.i FROM generate_series(0, 30, 5) d ORDER BY 1)) f (i) ORDER BY f.i LIMIT 10) FROM generate_series(1, 3) g (i); RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; -- -- Check handling of a constant-null CHECK constraint -- CREATE TABLE cnullparent ( f1 int ); CREATE TABLE cnullchild ( CHECK (f1 = 1 OR f1 = NULL) ) INHERITS ( cnullparent ); INSERT INTO cnullchild VALUES (1); INSERT INTO cnullchild VALUES (2); INSERT INTO cnullchild VALUES (NULL); SELECT * FROM cnullparent; SELECT * FROM cnullparent WHERE f1 = 2; DROP TABLE cnullparent CASCADE; -- -- Check use of temporary tables with inheritance trees -- CREATE TABLE inh_perm_parent ( a1 int ); CREATE temp TABLE inh_temp_parent ( a1 int ); CREATE temp TABLE inh_temp_child () INHERITS ( inh_perm_parent ); -- ok CREATE TABLE inh_perm_child () INHERITS ( inh_temp_parent ); -- error CREATE temp TABLE inh_temp_child_2 () INHERITS ( inh_temp_parent ); -- ok INSERT INTO inh_perm_parent VALUES (1); INSERT INTO inh_temp_parent VALUES (2); INSERT INTO inh_temp_child VALUES (3); INSERT INTO inh_temp_child_2 VALUES (4); SELECT tableoid::regclass, a1 FROM inh_perm_parent; SELECT tableoid::regclass, a1 FROM inh_temp_parent; DROP TABLE inh_perm_parent CASCADE; DROP TABLE inh_temp_parent CASCADE; -- -- Check that constraint exclusion works correctly with partitions using -- implicit constraints generated from the partition bound information. -- CREATE TABLE list_parted ( a varchar ) PARTITION BY LIST (a); CREATE TABLE part_ab_cd PARTITION OF list_parted FOR VALUES IN ('ab', 'cd'); CREATE TABLE part_ef_gh PARTITION OF list_parted FOR VALUES IN ('ef', 'gh'); CREATE TABLE part_null_xy PARTITION OF list_parted FOR VALUES IN (NULL, 'xy'); EXPLAIN ( COSTS OFF ) SELECT * FROM list_parted; EXPLAIN ( COSTS OFF ) SELECT * FROM list_parted WHERE a IS NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM list_parted WHERE a IS NOT NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM list_parted WHERE a IN ('ab', 'cd', 'ef'); EXPLAIN ( COSTS OFF ) SELECT * FROM list_parted WHERE a = 'ab' OR a IN (NULL, 'cd'); EXPLAIN ( COSTS OFF ) SELECT * FROM list_parted WHERE a = 'ab'; CREATE TABLE range_list_parted ( a int, b char(2) ) PARTITION BY RANGE (a); CREATE TABLE part_1_10 PARTITION OF range_list_parted FOR VALUES FROM (1) TO (10) PARTITION BY LIST (b); CREATE TABLE part_1_10_ab PARTITION OF part_1_10 FOR VALUES IN ('ab'); CREATE TABLE part_1_10_cd PARTITION OF part_1_10 FOR VALUES IN ('cd'); CREATE TABLE part_10_20 PARTITION OF range_list_parted FOR VALUES FROM (10) TO (20) PARTITION BY LIST (b); CREATE TABLE part_10_20_ab PARTITION OF part_10_20 FOR VALUES IN ('ab'); CREATE TABLE part_10_20_cd PARTITION OF part_10_20 FOR VALUES IN ('cd'); CREATE TABLE part_21_30 PARTITION OF range_list_parted FOR VALUES FROM (21) TO (30) PARTITION BY LIST (b); CREATE TABLE part_21_30_ab PARTITION OF part_21_30 FOR VALUES IN ('ab'); CREATE TABLE part_21_30_cd PARTITION OF part_21_30 FOR VALUES IN ('cd'); CREATE TABLE part_40_inf PARTITION OF range_list_parted FOR VALUES FROM (40) TO (MAXVALUE) PARTITION BY LIST (b); CREATE TABLE part_40_inf_ab PARTITION OF part_40_inf FOR VALUES IN ('ab'); CREATE TABLE part_40_inf_cd PARTITION OF part_40_inf FOR VALUES IN ('cd'); CREATE TABLE part_40_inf_null PARTITION OF part_40_inf FOR VALUES IN (NULL); EXPLAIN ( COSTS OFF ) SELECT * FROM range_list_parted; EXPLAIN ( COSTS OFF ) SELECT * FROM range_list_parted WHERE a = 5; EXPLAIN ( COSTS OFF ) SELECT * FROM range_list_parted WHERE b = 'ab'; EXPLAIN ( COSTS OFF ) SELECT * FROM range_list_parted WHERE a BETWEEN 3 AND 23 AND b IN ('ab'); /* Should select no rows because range partition key cannot be null */ EXPLAIN ( COSTS OFF ) SELECT * FROM range_list_parted WHERE a IS NULL; /* Should only select rows from the null-accepting partition */ EXPLAIN ( COSTS OFF ) SELECT * FROM range_list_parted WHERE b IS NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM range_list_parted WHERE a IS NOT NULL AND a < 67; EXPLAIN ( COSTS OFF ) SELECT * FROM range_list_parted WHERE a >= 30; DROP TABLE list_parted; DROP TABLE range_list_parted; -- check that constraint exclusion is able to cope with the partition -- constraint emitted for multi-column range partitioned tables CREATE TABLE mcrparted ( a int, b int, c int ) PARTITION BY RANGE (a, abs(b), c); CREATE TABLE mcrparted_def PARTITION OF mcrparted DEFAULT; CREATE TABLE mcrparted0 PARTITION OF mcrparted FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, 1, 1); CREATE TABLE mcrparted1 PARTITION OF mcrparted FOR VALUES FROM (1, 1, 1) TO (10, 5, 10); CREATE TABLE mcrparted2 PARTITION OF mcrparted FOR VALUES FROM (10, 5, 10) TO (10, 10, 10); CREATE TABLE mcrparted3 PARTITION OF mcrparted FOR VALUES FROM (11, 1, 1) TO (20, 10, 10); CREATE TABLE mcrparted4 PARTITION OF mcrparted FOR VALUES FROM (20, 10, 10) TO (20, 20, 20); CREATE TABLE mcrparted5 PARTITION OF mcrparted FOR VALUES FROM (20, 20, 20) TO (MAXVALUE, MAXVALUE, MAXVALUE); EXPLAIN ( COSTS OFF ) SELECT * FROM mcrparted WHERE a = 0; -- scans mcrparted0, mcrparted_def EXPLAIN ( COSTS OFF ) SELECT * FROM mcrparted WHERE a = 10 AND abs(b) < 5; -- scans mcrparted1, mcrparted_def EXPLAIN ( COSTS OFF ) SELECT * FROM mcrparted WHERE a = 10 AND abs(b) = 5; -- scans mcrparted1, mcrparted2, mcrparted_def EXPLAIN ( COSTS OFF ) SELECT * FROM mcrparted WHERE abs(b) = 5; -- scans all partitions EXPLAIN ( COSTS OFF ) SELECT * FROM mcrparted WHERE a > - 1; -- scans all partitions EXPLAIN ( COSTS OFF ) SELECT * FROM mcrparted WHERE a = 20 AND abs(b) = 10 AND c > 10; -- scans mcrparted4 EXPLAIN ( COSTS OFF ) SELECT * FROM mcrparted WHERE a = 20 AND c > 20; -- scans mcrparted3, mcrparte4, mcrparte5, mcrparted_def -- check that partitioned table Appends cope with being referenced in -- subplans CREATE TABLE parted_minmax ( a int, b varchar(16) ) PARTITION BY RANGE (a); CREATE TABLE parted_minmax1 PARTITION OF parted_minmax FOR VALUES FROM (1) TO (10); CREATE INDEX parted_minmax1i ON parted_minmax1 (a, b); INSERT INTO parted_minmax VALUES (1, '12345'); EXPLAIN ( COSTS OFF ) SELECT min(a), max(a) FROM parted_minmax WHERE b = '12345'; SELECT min(a), max(a) FROM parted_minmax WHERE b = '12345'; DROP TABLE parted_minmax; -- Test code that uses Append nodes in place of MergeAppend when the -- partition ordering matches the desired ordering. CREATE INDEX mcrparted_a_abs_c_idx ON mcrparted (a, abs(b), c); -- MergeAppend must be used when a default partition exists EXPLAIN ( COSTS OFF ) SELECT * FROM mcrparted ORDER BY a, abs(b), c; DROP TABLE mcrparted_def; -- Append is used for a RANGE partitioned table with no default -- and no subpartitions EXPLAIN ( COSTS OFF ) SELECT * FROM mcrparted ORDER BY a, abs(b), c; -- Append is used with subpaths in reverse order with backwards index scans EXPLAIN ( COSTS OFF ) SELECT * FROM mcrparted ORDER BY a DESC, abs(b) DESC, c DESC; -- check that Append plan is used containing a MergeAppend for sub-partitions -- that are unordered. DROP TABLE mcrparted5; CREATE TABLE mcrparted5 PARTITION OF mcrparted FOR VALUES FROM (20, 20, 20) TO (MAXVALUE, MAXVALUE, MAXVALUE) PARTITION BY LIST (a); CREATE TABLE mcrparted5a PARTITION OF mcrparted5 FOR VALUES IN (20); CREATE TABLE mcrparted5_def PARTITION OF mcrparted5 DEFAULT; EXPLAIN ( COSTS OFF ) SELECT * FROM mcrparted ORDER BY a, abs(b), c; DROP TABLE mcrparted5_def; -- check that an Append plan is used and the sub-partitions are flattened -- into the main Append when the sub-partition is unordered but contains -- just a single sub-partition. EXPLAIN ( COSTS OFF ) SELECT a, abs(b) FROM mcrparted ORDER BY a, abs(b), c; -- check that Append is used when the sub-partitioned tables are pruned -- during planning. EXPLAIN ( COSTS OFF ) SELECT * FROM mcrparted WHERE a < 20 ORDER BY a, abs(b), c; CREATE TABLE mclparted ( a int ) PARTITION BY LIST (a); CREATE TABLE mclparted1 PARTITION OF mclparted FOR VALUES IN (1); CREATE TABLE mclparted2 PARTITION OF mclparted FOR VALUES IN (2); CREATE INDEX ON mclparted (a); -- Ensure an Append is used for a list partition with an order by. EXPLAIN ( COSTS OFF ) SELECT * FROM mclparted ORDER BY a; -- Ensure a MergeAppend is used when a partition exists with interleaved -- datums in the partition bound. CREATE TABLE mclparted3_5 PARTITION OF mclparted FOR VALUES IN (3, 5); CREATE TABLE mclparted4 PARTITION OF mclparted FOR VALUES IN (4); EXPLAIN ( COSTS OFF ) SELECT * FROM mclparted ORDER BY a; DROP TABLE mclparted; -- Ensure subplans which don't have a path with the correct pathkeys get -- sorted correctly. DROP INDEX mcrparted_a_abs_c_idx; CREATE INDEX ON mcrparted1 (a, abs(b), c); CREATE INDEX ON mcrparted2 (a, abs(b), c); CREATE INDEX ON mcrparted3 (a, abs(b), c); CREATE INDEX ON mcrparted4 (a, abs(b), c); EXPLAIN ( COSTS OFF ) SELECT * FROM mcrparted WHERE a < 20 ORDER BY a, abs(b), c LIMIT 1; SET enable_bitmapscan = 0; -- Ensure Append node can be used when the partition is ordered by some -- pathkeys which were deemed redundant. EXPLAIN ( COSTS OFF ) SELECT * FROM mcrparted WHERE a = 10 ORDER BY a, abs(b), c; RESET enable_bitmapscan; DROP TABLE mcrparted; -- Ensure LIST partitions allow an Append to be used instead of a MergeAppend CREATE TABLE bool_lp ( b bool ) PARTITION BY LIST (b); CREATE TABLE bool_lp_true PARTITION OF bool_lp FOR VALUES IN (TRUE); CREATE TABLE bool_lp_false PARTITION OF bool_lp FOR VALUES IN (FALSE); CREATE INDEX ON bool_lp (b); EXPLAIN ( COSTS OFF ) SELECT * FROM bool_lp ORDER BY b; DROP TABLE bool_lp; -- Ensure const bool quals can be properly detected as redundant CREATE TABLE bool_rp ( b bool, a int ) PARTITION BY RANGE (b, a); CREATE TABLE bool_rp_false_1k PARTITION OF bool_rp FOR VALUES FROM (FALSE, 0) TO (FALSE, 1000); CREATE TABLE bool_rp_true_1k PARTITION OF bool_rp FOR VALUES FROM (TRUE, 0) TO (TRUE, 1000); CREATE TABLE bool_rp_false_2k PARTITION OF bool_rp FOR VALUES FROM (FALSE, 1000) TO (FALSE, 2000); CREATE TABLE bool_rp_true_2k PARTITION OF bool_rp FOR VALUES FROM (TRUE, 1000) TO (TRUE, 2000); CREATE INDEX ON bool_rp (b, a); EXPLAIN ( COSTS OFF ) SELECT * FROM bool_rp WHERE b = TRUE ORDER BY b, a; EXPLAIN ( COSTS OFF ) SELECT * FROM bool_rp WHERE b = FALSE ORDER BY b, a; EXPLAIN ( COSTS OFF ) SELECT * FROM bool_rp WHERE b = TRUE ORDER BY a; EXPLAIN ( COSTS OFF ) SELECT * FROM bool_rp WHERE b = FALSE ORDER BY a; DROP TABLE bool_rp; -- Ensure an Append scan is chosen when the partition order is a subset of -- the required order. CREATE TABLE range_parted ( a int, b int, c int ) PARTITION BY RANGE (a, b); CREATE TABLE range_parted1 PARTITION OF range_parted FOR VALUES FROM (0, 0) TO (10, 10); CREATE TABLE range_parted2 PARTITION OF range_parted FOR VALUES FROM (10, 10) TO (20, 20); CREATE INDEX ON range_parted (a, b, c); EXPLAIN ( COSTS OFF ) SELECT * FROM range_parted ORDER BY a, b, c; EXPLAIN ( COSTS OFF ) SELECT * FROM range_parted ORDER BY a DESC, b DESC, c DESC; DROP TABLE range_parted; pgFormatter-4.2/t/pg-test-files/expected/init_privs.sql000066400000000000000000000005661361326045100233170ustar00rootroot00000000000000-- Test initial privileges -- There should always be some initial privileges, set up by initdb SELECT count(*) > 0 FROM pg_init_privs; -- Intentionally include some non-initial privs for pg_dump to dump out GRANT SELECT ON pg_proc TO CURRENT_USER; GRANT SELECT (prosrc) ON pg_proc TO CURRENT_USER; GRANT SELECT (rolname, rolsuper) ON pg_authid TO CURRENT_USER; pgFormatter-4.2/t/pg-test-files/expected/insert.sql000066400000000000000000000647451361326045100224460ustar00rootroot00000000000000-- -- insert with DEFAULT in the target_list -- CREATE TABLE inserttest ( col1 int4, col2 int4 NOT NULL, col3 text DEFAULT 'testing' ); INSERT INTO inserttest (col1, col2, col3) VALUES (DEFAULT, DEFAULT, DEFAULT); INSERT INTO inserttest (col2, col3) VALUES (3, DEFAULT); INSERT INTO inserttest (col1, col2, col3) VALUES (DEFAULT, 5, DEFAULT); INSERT INTO inserttest VALUES (DEFAULT, 5, 'test'); INSERT INTO inserttest VALUES (DEFAULT, 7); SELECT * FROM inserttest; -- -- insert with similar expression / target_list values (all fail) -- INSERT INTO inserttest (col1, col2, col3) VALUES (DEFAULT, DEFAULT); INSERT INTO inserttest (col1, col2, col3) VALUES (1, 2); INSERT INTO inserttest (col1) VALUES (1, 2); INSERT INTO inserttest (col1) VALUES (DEFAULT, DEFAULT); SELECT * FROM inserttest; -- -- VALUES test -- INSERT INTO inserttest VALUES (10, 20, '40'), (- 1, 2, DEFAULT), (( SELECT 2), ( SELECT i FROM ( VALUES (3)) AS foo (i)), 'values are fun!'); SELECT * FROM inserttest; -- -- TOASTed value test -- INSERT INTO inserttest VALUES (30, 50, repeat('x', 10000)); SELECT col1, col2, char_length(col3) FROM inserttest; DROP TABLE inserttest; -- -- check indirection (field/array assignment), cf bug #14265 -- -- these tests are aware that transformInsertStmt has 3 separate code paths -- CREATE TYPE insert_test_type AS ( if1 int, if2 text[] ); CREATE TABLE inserttest ( f1 int, f2 int[], f3 insert_test_type, f4 insert_test_type[] ); INSERT INTO inserttest (f2[1], f2[2]) VALUES (1, 2); INSERT INTO inserttest (f2[1], f2[2]) VALUES (3, 4), (5, 6); INSERT INTO inserttest (f2[1], f2[2]) SELECT 7, 8; INSERT INTO inserttest (f2[1], f2[2]) VALUES (1, DEFAULT); -- not supported INSERT INTO inserttest (f3.if1, f3.if2) VALUES (1, ARRAY['foo']); INSERT INTO inserttest (f3.if1, f3.if2) VALUES (1, '{foo}'), (2, '{bar}'); INSERT INTO inserttest (f3.if1, f3.if2) SELECT 3, '{baz,quux}'; INSERT INTO inserttest (f3.if1, f3.if2) VALUES (1, DEFAULT); -- not supported INSERT INTO inserttest (f3.if2[1], f3.if2[2]) VALUES ('foo', 'bar'); INSERT INTO inserttest (f3.if2[1], f3.if2[2]) VALUES ('foo', 'bar'), ('baz', 'quux'); INSERT INTO inserttest (f3.if2[1], f3.if2[2]) SELECT 'bear', 'beer'; INSERT INTO inserttest (f4[1].if2[1], f4[1].if2[2]) VALUES ('foo', 'bar'); INSERT INTO inserttest (f4[1].if2[1], f4[1].if2[2]) VALUES ('foo', 'bar'), ('baz', 'quux'); INSERT INTO inserttest (f4[1].if2[1], f4[1].if2[2]) SELECT 'bear', 'beer'; SELECT * FROM inserttest; -- also check reverse-listing CREATE TABLE inserttest2 ( f1 bigint, f2 text ); CREATE RULE irule1 AS ON INSERT TO inserttest2 DO also INSERT INTO inserttest (f3.if2[1], f3.if2[2]) VALUES (new.f1, new.f2); CREATE RULE irule2 AS ON INSERT TO inserttest2 DO also INSERT INTO inserttest (f4[1].if1, f4[1].if2[2]) VALUES (1, 'fool'), (new.f1, new.f2); CREATE RULE irule3 AS ON INSERT TO inserttest2 DO also INSERT INTO inserttest (f4[1].if1, f4[1].if2[2]) SELECT new.f1, new.f2; \d+ inserttest2 DROP TABLE inserttest2; DROP TABLE inserttest; DROP TYPE insert_test_type; -- direct partition inserts should check partition bound constraint CREATE TABLE range_parted ( a text, b int ) PARTITION BY RANGE (a, (b + 0)); -- no partitions, so fail INSERT INTO range_parted VALUES ('a', 11); CREATE TABLE part1 PARTITION OF range_parted FOR VALUES FROM ('a', 1) TO ('a', 10); CREATE TABLE part2 PARTITION OF range_parted FOR VALUES FROM ('a', 10) TO ('a', 20); CREATE TABLE part3 PARTITION OF range_parted FOR VALUES FROM ('b', 1) TO ('b', 10); CREATE TABLE part4 PARTITION OF range_parted FOR VALUES FROM ('b', 10) TO ('b', 20); -- fail INSERT INTO part1 VALUES ('a', 11); INSERT INTO part1 VALUES ('b', 1); -- ok INSERT INTO part1 VALUES ('a', 1); -- fail INSERT INTO part4 VALUES ('b', 21); INSERT INTO part4 VALUES ('a', 10); -- ok INSERT INTO part4 VALUES ('b', 10); -- fail (partition key a has a NOT NULL constraint) INSERT INTO part1 VALUES (NULL); -- fail (expression key (b+0) cannot be null either) INSERT INTO part1 VALUES (1); CREATE TABLE list_parted ( a text, b int ) PARTITION BY LIST (lower(a)); CREATE TABLE part_aa_bb PARTITION OF list_parted FOR VALUES IN ('aa', 'bb'); CREATE TABLE part_cc_dd PARTITION OF list_parted FOR VALUES IN ('cc', 'dd'); CREATE TABLE part_null PARTITION OF list_parted FOR VALUES IN (NULL); -- fail INSERT INTO part_aa_bb VALUES ('cc', 1); INSERT INTO part_aa_bb VALUES ('AAa', 1); INSERT INTO part_aa_bb VALUES (NULL); -- ok INSERT INTO part_cc_dd VALUES ('cC', 1); INSERT INTO part_null VALUES (NULL, 0); -- check in case of multi-level partitioned table CREATE TABLE part_ee_ff PARTITION OF list_parted FOR VALUES IN ('ee', 'ff') PARTITION BY RANGE (b); CREATE TABLE part_ee_ff1 PARTITION OF part_ee_ff FOR VALUES FROM (1) TO (10); CREATE TABLE part_ee_ff2 PARTITION OF part_ee_ff FOR VALUES FROM (10) TO (20); -- test default partition CREATE TABLE part_default PARTITION OF list_parted DEFAULT; -- Negative test: a row, which would fit in other partition, does not fit -- default partition, even when inserted directly INSERT INTO part_default VALUES ('aa', 2); INSERT INTO part_default VALUES (NULL, 2); -- ok INSERT INTO part_default VALUES ('Zz', 2); -- test if default partition works as expected for multi-level partitioned -- table as well as when default partition itself is further partitioned DROP TABLE part_default; CREATE TABLE part_xx_yy PARTITION OF list_parted FOR VALUES IN ('xx', 'yy') PARTITION BY LIST (a); CREATE TABLE part_xx_yy_p1 PARTITION OF part_xx_yy FOR VALUES IN ('xx'); CREATE TABLE part_xx_yy_defpart PARTITION OF part_xx_yy DEFAULT; CREATE TABLE part_default PARTITION OF list_parted DEFAULT PARTITION BY RANGE (b); CREATE TABLE part_default_p1 PARTITION OF part_default FOR VALUES FROM (20) TO (30); CREATE TABLE part_default_p2 PARTITION OF part_default FOR VALUES FROM (30) TO (40); -- fail INSERT INTO part_ee_ff1 VALUES ('EE', 11); INSERT INTO part_default_p2 VALUES ('gg', 43); -- fail (even the parent's, ie, part_ee_ff's partition constraint applies) INSERT INTO part_ee_ff1 VALUES ('cc', 1); INSERT INTO part_default VALUES ('gg', 43); -- ok INSERT INTO part_ee_ff1 VALUES ('ff', 1); INSERT INTO part_ee_ff2 VALUES ('ff', 11); INSERT INTO part_default_p1 VALUES ('cd', 25); INSERT INTO part_default_p2 VALUES ('de', 35); INSERT INTO list_parted VALUES ('ab', 21); INSERT INTO list_parted VALUES ('xx', 1); INSERT INTO list_parted VALUES ('yy', 2); SELECT tableoid::regclass, * FROM list_parted; -- Check tuple routing for partitioned tables -- fail INSERT INTO range_parted VALUES ('a', 0); -- ok INSERT INTO range_parted VALUES ('a', 1); INSERT INTO range_parted VALUES ('a', 10); -- fail INSERT INTO range_parted VALUES ('a', 20); -- ok INSERT INTO range_parted VALUES ('b', 1); INSERT INTO range_parted VALUES ('b', 10); -- fail (partition key (b+0) is null) INSERT INTO range_parted VALUES ('a'); -- Check default partition CREATE TABLE part_def PARTITION OF range_parted DEFAULT; -- fail INSERT INTO part_def VALUES ('b', 10); -- ok INSERT INTO part_def VALUES ('c', 10); INSERT INTO range_parted VALUES (NULL, NULL); INSERT INTO range_parted VALUES ('a', NULL); INSERT INTO range_parted VALUES (NULL, 19); INSERT INTO range_parted VALUES ('b', 20); SELECT tableoid::regclass, * FROM range_parted; -- ok INSERT INTO list_parted VALUES (NULL, 1); INSERT INTO list_parted (a) VALUES ('aA'); -- fail (partition of part_ee_ff not found in both cases) INSERT INTO list_parted VALUES ('EE', 0); INSERT INTO part_ee_ff VALUES ('EE', 0); -- ok INSERT INTO list_parted VALUES ('EE', 1); INSERT INTO part_ee_ff VALUES ('EE', 10); SELECT tableoid::regclass, * FROM list_parted; -- some more tests to exercise tuple-routing with multi-level partitioning CREATE TABLE part_gg PARTITION OF list_parted FOR VALUES IN ('gg') PARTITION BY RANGE (b); CREATE TABLE part_gg1 PARTITION OF part_gg FOR VALUES FROM (MINVALUE) TO (1); CREATE TABLE part_gg2 PARTITION OF part_gg FOR VALUES FROM (1) TO (10) PARTITION BY RANGE (b); CREATE TABLE part_gg2_1 PARTITION OF part_gg2 FOR VALUES FROM (1) TO (5); CREATE TABLE part_gg2_2 PARTITION OF part_gg2 FOR VALUES FROM (5) TO (10); CREATE TABLE part_ee_ff3 PARTITION OF part_ee_ff FOR VALUES FROM (20) TO (30) PARTITION BY RANGE (b); CREATE TABLE part_ee_ff3_1 PARTITION OF part_ee_ff3 FOR VALUES FROM (20) TO (25); CREATE TABLE part_ee_ff3_2 PARTITION OF part_ee_ff3 FOR VALUES FROM (25) TO (30); TRUNCATE list_parted; INSERT INTO list_parted VALUES ('aa'), ('cc'); INSERT INTO list_parted SELECT 'Ff', s.a FROM generate_series(1, 29) s (a); INSERT INTO list_parted SELECT 'gg', s.a FROM generate_series(1, 9) s (a); INSERT INTO list_parted (b) VALUES (1); SELECT tableoid::regclass::text, a, min(b) AS min_b, max(b) AS max_b FROM list_parted GROUP BY 1, 2 ORDER BY 1; -- direct partition inserts should check hash partition bound constraint -- Use hand-rolled hash functions and operator classes to get predictable -- result on different matchines. The hash function for int4 simply returns -- the sum of the values passed to it and the one for text returns the length -- of the non-empty string value passed to it or 0. CREATE OR REPLACE FUNCTION part_hashint4_noop (value int4, seed int8) RETURNS int8 AS $$ SELECT value + seed; $$ LANGUAGE sql IMMUTABLE; CREATE OPERATOR class part_test_int4_ops FOR TYPE int4 USING HASH AS OPERATOR 1 =, FUNCTION 2 part_hashint4_noop (int4, int8 ); CREATE OR REPLACE FUNCTION part_hashtext_length (value text, seed int8) RETURNS int8 AS $$ SELECT length(coalesce(value, ''))::int8 $$ LANGUAGE sql IMMUTABLE; CREATE OPERATOR class part_test_text_ops FOR TYPE text USING HASH AS OPERATOR 1 =, FUNCTION 2 part_hashtext_length (text, int8 ); CREATE TABLE hash_parted ( a int ) PARTITION BY HASH (a part_test_int4_ops); CREATE TABLE hpart0 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 4, REMAINDER 0); CREATE TABLE hpart1 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 4, REMAINDER 1); CREATE TABLE hpart2 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 4, REMAINDER 2); CREATE TABLE hpart3 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 4, REMAINDER 3); INSERT INTO hash_parted VALUES (generate_series(1, 10)); -- direct insert of values divisible by 4 - ok; INSERT INTO hpart0 VALUES (12), (16); -- fail; INSERT INTO hpart0 VALUES (11); -- 11 % 4 -> 3 remainder i.e. valid data for hpart3 partition INSERT INTO hpart3 VALUES (11); -- view data SELECT tableoid::regclass AS part, a, a % 4 AS "remainder = a % 4" FROM hash_parted ORDER BY part; -- test \d+ output on a table which has both partitioned and unpartitioned -- partitions \d+ list_parted -- cleanup DROP TABLE range_parted, list_parted; DROP TABLE hash_parted; -- test that a default partition added as the first partition accepts any value -- including null CREATE TABLE list_parted ( a int ) PARTITION BY LIST (a); CREATE TABLE part_default PARTITION OF list_parted DEFAULT; \d+ part_default INSERT INTO part_default VALUES (NULL); INSERT INTO part_default VALUES (1); INSERT INTO part_default VALUES (- 1); SELECT tableoid::regclass, a FROM list_parted; -- cleanup DROP TABLE list_parted; -- more tests for certain multi-level partitioning scenarios CREATE TABLE mlparted ( a int, b int ) PARTITION BY RANGE (a, b); CREATE TABLE mlparted1 ( b int NOT NULL, a int NOT NULL ) PARTITION BY RANGE ((b + 0)); CREATE TABLE mlparted11 ( LIKE mlparted1 ); ALTER TABLE mlparted11 DROP a; ALTER TABLE mlparted11 ADD a int; ALTER TABLE mlparted11 DROP a; ALTER TABLE mlparted11 ADD a int NOT NULL; -- attnum for key attribute 'a' is different in mlparted, mlparted1, and mlparted11 SELECT attrelid::regclass, attname, attnum FROM pg_attribute WHERE attname = 'a' AND (attrelid = 'mlparted'::regclass OR attrelid = 'mlparted1'::regclass OR attrelid = 'mlparted11'::regclass) ORDER BY attrelid::regclass::text; ALTER TABLE mlparted1 ATTACH PARTITION mlparted11 FOR VALUES FROM (2) TO (5); ALTER TABLE mlparted ATTACH PARTITION mlparted1 FOR VALUES FROM (1, 2) TO (1, 10); -- check that "(1, 2)" is correctly routed to mlparted11. INSERT INTO mlparted VALUES (1, 2); SELECT tableoid::regclass, * FROM mlparted; -- check that proper message is shown after failure to route through mlparted1 INSERT INTO mlparted (a, b) VALUES (1, 5); TRUNCATE mlparted; ALTER TABLE mlparted ADD CONSTRAINT check_b CHECK (b = 3); -- have a BR trigger modify the row such that the check_b is violated CREATE FUNCTION mlparted11_trig_fn () RETURNS TRIGGER AS $$ BEGIN NEW.b := 4; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER mlparted11_trig BEFORE INSERT ON mlparted11 FOR EACH ROW EXECUTE PROCEDURE mlparted11_trig_fn (); -- check that the correct row is shown when constraint check_b fails after -- "(1, 2)" is routed to mlparted11 (actually "(1, 4)" would be shown due -- to the BR trigger mlparted11_trig_fn) INSERT INTO mlparted VALUES (1, 2); DROP TRIGGER mlparted11_trig ON mlparted11; DROP FUNCTION mlparted11_trig_fn (); -- check that inserting into an internal partition successfully results in -- checking its partition constraint before inserting into the leaf partition -- selected by tuple-routing INSERT INTO mlparted1 (a, b) VALUES (2, 3); -- check routing error through a list partitioned table when the key is null CREATE TABLE lparted_nonullpart ( a int, b char ) PARTITION BY LIST (b); CREATE TABLE lparted_nonullpart_a PARTITION OF lparted_nonullpart FOR VALUES IN ('a'); INSERT INTO lparted_nonullpart VALUES (1); DROP TABLE lparted_nonullpart; -- check that RETURNING works correctly with tuple-routing ALTER TABLE mlparted DROP CONSTRAINT check_b; CREATE TABLE mlparted12 PARTITION OF mlparted1 FOR VALUES FROM (5) TO (10); CREATE TABLE mlparted2 ( b int NOT NULL, a int NOT NULL ); ALTER TABLE mlparted ATTACH PARTITION mlparted2 FOR VALUES FROM (1, 10) TO (1, 20); CREATE TABLE mlparted3 PARTITION OF mlparted FOR VALUES FROM (1, 20) TO (1, 30); CREATE TABLE mlparted4 ( LIKE mlparted ); ALTER TABLE mlparted4 DROP a; ALTER TABLE mlparted4 ADD a int NOT NULL; ALTER TABLE mlparted ATTACH PARTITION mlparted4 FOR VALUES FROM (1, 30) TO (1, 40); WITH ins ( a, b, c ) AS ( INSERT INTO mlparted (b, a) SELECT s.a, 1 FROM generate_series(2, 39) s (a) RETURNING tableoid::regclass, * ) SELECT a, b, min(c), max(c) FROM ins GROUP BY a, b ORDER BY 1; ALTER TABLE mlparted ADD c text; CREATE TABLE mlparted5 ( c text, a int NOT NULL, b int NOT NULL ) PARTITION BY LIST (c); CREATE TABLE mlparted5a ( a int NOT NULL, c text, b int NOT NULL ); ALTER TABLE mlparted5 ATTACH PARTITION mlparted5a FOR VALUES IN ('a'); ALTER TABLE mlparted ATTACH PARTITION mlparted5 FOR VALUES FROM (1, 40) TO (1, 50); ALTER TABLE mlparted ADD CONSTRAINT check_b CHECK (a = 1 AND b < 45); INSERT INTO mlparted VALUES (1, 45, 'a'); CREATE FUNCTION mlparted5abrtrig_func () RETURNS TRIGGER AS $$ BEGIN new.c = 'b'; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER mlparted5abrtrig BEFORE INSERT ON mlparted5a FOR EACH ROW EXECUTE PROCEDURE mlparted5abrtrig_func (); INSERT INTO mlparted5 (a, b, c) VALUES (1, 40, 'a'); DROP TABLE mlparted5; ALTER TABLE mlparted DROP CONSTRAINT check_b; -- Check multi-level default partition CREATE TABLE mlparted_def PARTITION OF mlparted DEFAULT PARTITION BY RANGE (a); CREATE TABLE mlparted_def1 PARTITION OF mlparted_def FOR VALUES FROM (40) TO (50); CREATE TABLE mlparted_def2 PARTITION OF mlparted_def FOR VALUES FROM (50) TO (60); INSERT INTO mlparted VALUES (40, 100); INSERT INTO mlparted_def1 VALUES (42, 100); INSERT INTO mlparted_def2 VALUES (54, 50); -- fail INSERT INTO mlparted VALUES (70, 100); INSERT INTO mlparted_def1 VALUES (52, 50); INSERT INTO mlparted_def2 VALUES (34, 50); -- ok CREATE TABLE mlparted_defd PARTITION OF mlparted_def DEFAULT; INSERT INTO mlparted VALUES (70, 100); SELECT tableoid::regclass, * FROM mlparted_def; -- Check multi-level tuple routing with attributes dropped from the -- top-most parent. First remove the last attribute. ALTER TABLE mlparted ADD d int, ADD e int; ALTER TABLE mlparted DROP e; CREATE TABLE mlparted5 PARTITION OF mlparted FOR VALUES FROM (1, 40) TO (1, 50) PARTITION BY RANGE (c); CREATE TABLE mlparted5_ab PARTITION OF mlparted5 FOR VALUES FROM ('a') TO ('c') PARTITION BY LIST (c); CREATE TABLE mlparted5_a PARTITION OF mlparted5_ab FOR VALUES IN ('a'); CREATE TABLE mlparted5_b ( d int, b int, c text, a int ); ALTER TABLE mlparted5_ab ATTACH PARTITION mlparted5_b FOR VALUES IN ('b'); TRUNCATE mlparted; INSERT INTO mlparted VALUES (1, 2, 'a', 1); INSERT INTO mlparted VALUES (1, 40, 'a', 1); -- goes to mlparted5_a INSERT INTO mlparted VALUES (1, 45, 'b', 1); -- goes to mlparted5_b SELECT tableoid::regclass, * FROM mlparted ORDER BY a, b, c, d; ALTER TABLE mlparted DROP d; TRUNCATE mlparted; -- Remove the before last attribute. ALTER TABLE mlparted ADD e int, ADD d int; ALTER TABLE mlparted DROP e; INSERT INTO mlparted VALUES (1, 2, 'a', 1); INSERT INTO mlparted VALUES (1, 40, 'a', 1); -- goes to mlparted5_a INSERT INTO mlparted VALUES (1, 45, 'b', 1); -- goes to mlparted5_b SELECT tableoid::regclass, * FROM mlparted ORDER BY a, b, c, d; ALTER TABLE mlparted DROP d; DROP TABLE mlparted5; -- check that message shown after failure to find a partition shows the -- appropriate key description (or none) in various situations CREATE TABLE key_desc ( a int, b int ) PARTITION BY LIST ((a + 0)); CREATE TABLE key_desc_1 PARTITION OF key_desc FOR VALUES IN (1) PARTITION BY RANGE (b); CREATE USER regress_insert_other_user; GRANT SELECT (a) ON key_desc_1 TO regress_insert_other_user; GRANT INSERT ON key_desc TO regress_insert_other_user; SET ROLE regress_insert_other_user; -- no key description is shown INSERT INTO key_desc VALUES (1, 1); RESET ROLE; GRANT SELECT (b) ON key_desc_1 TO regress_insert_other_user; SET ROLE regress_insert_other_user; -- key description (b)=(1) is now shown INSERT INTO key_desc VALUES (1, 1); -- key description is not shown if key contains expression INSERT INTO key_desc VALUES (2, 1); RESET ROLE; REVOKE ALL ON key_desc FROM regress_insert_other_user; REVOKE ALL ON key_desc_1 FROM regress_insert_other_user; DROP ROLE regress_insert_other_user; DROP TABLE key_desc, key_desc_1; -- test minvalue/maxvalue restrictions CREATE TABLE mcrparted ( a int, b int, c int ) PARTITION BY RANGE (a, abs(b), c); CREATE TABLE mcrparted0 PARTITION OF mcrparted FOR VALUES FROM (MINVALUE, 0, 0) TO (1, MAXVALUE, MAXVALUE); CREATE TABLE mcrparted2 PARTITION OF mcrparted FOR VALUES FROM (10, 6, MINVALUE) TO (10, MAXVALUE, MINVALUE); CREATE TABLE mcrparted4 PARTITION OF mcrparted FOR VALUES FROM (21, MINVALUE, 0) TO (30, 20, MINVALUE); -- check multi-column range partitioning expression enforces the same -- constraint as what tuple-routing would determine it to be CREATE TABLE mcrparted0 PARTITION OF mcrparted FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, MAXVALUE, MAXVALUE); CREATE TABLE mcrparted1 PARTITION OF mcrparted FOR VALUES FROM (2, 1, MINVALUE) TO (10, 5, 10); CREATE TABLE mcrparted2 PARTITION OF mcrparted FOR VALUES FROM (10, 6, MINVALUE) TO (10, MAXVALUE, MAXVALUE); CREATE TABLE mcrparted3 PARTITION OF mcrparted FOR VALUES FROM (11, 1, 1) TO (20, 10, 10); CREATE TABLE mcrparted4 PARTITION OF mcrparted FOR VALUES FROM (21, MINVALUE, MINVALUE) TO (30, 20, MAXVALUE); CREATE TABLE mcrparted5 PARTITION OF mcrparted FOR VALUES FROM (30, 21, 20) TO (MAXVALUE, MAXVALUE, MAXVALUE); -- null not allowed in range partition INSERT INTO mcrparted VALUES (NULL, NULL, NULL); -- routed to mcrparted0 INSERT INTO mcrparted VALUES (0, 1, 1); INSERT INTO mcrparted0 VALUES (0, 1, 1); -- routed to mcparted1 INSERT INTO mcrparted VALUES (9, 1000, 1); INSERT INTO mcrparted1 VALUES (9, 1000, 1); INSERT INTO mcrparted VALUES (10, 5, - 1); INSERT INTO mcrparted1 VALUES (10, 5, - 1); INSERT INTO mcrparted VALUES (2, 1, 0); INSERT INTO mcrparted1 VALUES (2, 1, 0); -- routed to mcparted2 INSERT INTO mcrparted VALUES (10, 6, 1000); INSERT INTO mcrparted2 VALUES (10, 6, 1000); INSERT INTO mcrparted VALUES (10, 1000, 1000); INSERT INTO mcrparted2 VALUES (10, 1000, 1000); -- no partition exists, nor does mcrparted3 accept it INSERT INTO mcrparted VALUES (11, 1, - 1); INSERT INTO mcrparted3 VALUES (11, 1, - 1); -- routed to mcrparted5 INSERT INTO mcrparted VALUES (30, 21, 20); INSERT INTO mcrparted5 VALUES (30, 21, 20); INSERT INTO mcrparted4 VALUES (30, 21, 20); -- error -- check rows SELECT tableoid::regclass::text, * FROM mcrparted ORDER BY 1; -- cleanup DROP TABLE mcrparted; -- check that a BR constraint can't make partition contain violating rows CREATE TABLE brtrigpartcon ( a int, b text ) PARTITION BY LIST (a); CREATE TABLE brtrigpartcon1 PARTITION OF brtrigpartcon FOR VALUES IN (1); CREATE OR REPLACE FUNCTION brtrigpartcon1trigf () RETURNS TRIGGER AS $$ BEGIN new.a := 2; RETURN new; END $$ LANGUAGE plpgsql; CREATE TRIGGER brtrigpartcon1trig BEFORE INSERT ON brtrigpartcon1 FOR EACH ROW EXECUTE PROCEDURE brtrigpartcon1trigf (); INSERT INTO brtrigpartcon VALUES (1, 'hi there'); INSERT INTO brtrigpartcon1 VALUES (1, 'hi there'); -- check that the message shows the appropriate column description in a -- situation where the partitioned table is not the primary ModifyTable node CREATE TABLE inserttest3 ( f1 text DEFAULT 'foo', f2 text DEFAULT 'bar', f3 int ); CREATE ROLE regress_coldesc_role; GRANT INSERT ON inserttest3 TO regress_coldesc_role; GRANT INSERT ON brtrigpartcon TO regress_coldesc_role; REVOKE SELECT ON brtrigpartcon FROM regress_coldesc_role; SET ROLE regress_coldesc_role; WITH result AS ( INSERT INTO brtrigpartcon VALUES (1, 'hi there') RETURNING 1) INSERT INTO inserttest3 (f3) SELECT * FROM result; RESET ROLE; -- cleanup REVOKE ALL ON inserttest3 FROM regress_coldesc_role; REVOKE ALL ON brtrigpartcon FROM regress_coldesc_role; DROP ROLE regress_coldesc_role; DROP TABLE inserttest3; DROP TABLE brtrigpartcon; DROP FUNCTION brtrigpartcon1trigf (); -- check that "do nothing" BR triggers work with tuple-routing (this checks -- that estate->es_result_relation_info is appropriately set/reset for each -- routed tuple) CREATE TABLE donothingbrtrig_test ( a int, b text ) PARTITION BY LIST (a); CREATE TABLE donothingbrtrig_test1 ( b text, a int ); CREATE TABLE donothingbrtrig_test2 ( c text, b text, a int ); ALTER TABLE donothingbrtrig_test2 DROP COLUMN c; CREATE OR REPLACE FUNCTION donothingbrtrig_func () RETURNS TRIGGER AS $$ BEGIN RAISE notice 'b: %', new.b; RETURN NULL; END $$ LANGUAGE plpgsql; CREATE TRIGGER donothingbrtrig1 BEFORE INSERT ON donothingbrtrig_test1 FOR EACH ROW EXECUTE PROCEDURE donothingbrtrig_func (); CREATE TRIGGER donothingbrtrig2 BEFORE INSERT ON donothingbrtrig_test2 FOR EACH ROW EXECUTE PROCEDURE donothingbrtrig_func (); ALTER TABLE donothingbrtrig_test ATTACH PARTITION donothingbrtrig_test1 FOR VALUES IN (1); ALTER TABLE donothingbrtrig_test ATTACH PARTITION donothingbrtrig_test2 FOR VALUES IN (2); INSERT INTO donothingbrtrig_test VALUES (1, 'foo'), (2, 'bar'); SELECT tableoid::regclass, * FROM donothingbrtrig_test; -- cleanup DROP TABLE donothingbrtrig_test; DROP FUNCTION donothingbrtrig_func (); -- check multi-column range partitioning with minvalue/maxvalue constraints CREATE TABLE mcrparted ( a text, b int ) PARTITION BY RANGE (a, b); CREATE TABLE mcrparted1_lt_b PARTITION OF mcrparted FOR VALUES FROM (MINVALUE, MINVALUE) TO ('b', MINVALUE); CREATE TABLE mcrparted2_b PARTITION OF mcrparted FOR VALUES FROM ('b', MINVALUE) TO ('c', MINVALUE); CREATE TABLE mcrparted3_c_to_common PARTITION OF mcrparted FOR VALUES FROM ('c', MINVALUE) TO ('common', MINVALUE); CREATE TABLE mcrparted4_common_lt_0 PARTITION OF mcrparted FOR VALUES FROM ('common', MINVALUE) TO ('common', 0); CREATE TABLE mcrparted5_common_0_to_10 PARTITION OF mcrparted FOR VALUES FROM ('common', 0) TO ('common', 10); CREATE TABLE mcrparted6_common_ge_10 PARTITION OF mcrparted FOR VALUES FROM ('common', 10) TO ('common', MAXVALUE); CREATE TABLE mcrparted7_gt_common_lt_d PARTITION OF mcrparted FOR VALUES FROM ('common', MAXVALUE) TO ('d', MINVALUE); CREATE TABLE mcrparted8_ge_d PARTITION OF mcrparted FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, MAXVALUE); \d+ mcrparted \d+ mcrparted1_lt_b \d+ mcrparted2_b \d+ mcrparted3_c_to_common \d+ mcrparted4_common_lt_0 \d+ mcrparted5_common_0_to_10 \d+ mcrparted6_common_ge_10 \d+ mcrparted7_gt_common_lt_d \d+ mcrparted8_ge_d INSERT INTO mcrparted VALUES ('aaa', 0), ('b', 0), ('bz', 10), ('c', - 10), ('comm', - 10), ('common', - 10), ('common', 0), ('common', 10), ('commons', 0), ('d', - 10), ('e', 0); SELECT tableoid::regclass, * FROM mcrparted ORDER BY a, b; DROP TABLE mcrparted; -- check that wholerow vars in the RETURNING list work with partitioned tables CREATE TABLE returningwrtest ( a int ) PARTITION BY LIST (a); CREATE TABLE returningwrtest1 PARTITION OF returningwrtest FOR VALUES IN (1); INSERT INTO returningwrtest VALUES (1) RETURNING returningwrtest; -- check also that the wholerow vars in RETURNING list are converted as needed ALTER TABLE returningwrtest ADD b text; CREATE TABLE returningwrtest2 ( b text, c int, a int ); ALTER TABLE returningwrtest2 DROP c; ALTER TABLE returningwrtest ATTACH PARTITION returningwrtest2 FOR VALUES IN (2); INSERT INTO returningwrtest VALUES (2, 'foo') RETURNING returningwrtest; DROP TABLE returningwrtest; pgFormatter-4.2/t/pg-test-files/expected/insert_conflict.sql000066400000000000000000000747441361326045100243270ustar00rootroot00000000000000-- -- insert...on conflict do unique index inference -- CREATE TABLE insertconflicttest ( key int4, fruit text ); -- -- Test unique index inference with operator class specifications and -- named collations -- CREATE UNIQUE INDEX op_index_key ON insertconflicttest (KEY, fruit text_pattern_ops); CREATE UNIQUE INDEX collation_index_key ON insertconflicttest (KEY, fruit COLLATE "C"); CREATE UNIQUE INDEX both_index_key ON insertconflicttest (KEY, fruit COLLATE "C" text_pattern_ops); CREATE UNIQUE INDEX both_index_expr_key ON insertconflicttest (KEY, lower(fruit) COLLATE "C" text_pattern_ops); -- fails EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (KEY) DO NOTHING; EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (fruit) DO NOTHING; -- succeeds EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (KEY, fruit) DO NOTHING; EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (fruit, KEY, fruit, KEY) DO NOTHING; EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (lower(fruit), KEY, lower(fruit), KEY) DO NOTHING; EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (KEY, fruit) DO UPDATE SET fruit = excluded.fruit WHERE EXISTS ( SELECT 1 FROM insertconflicttest ii WHERE ii.key = excluded.key); -- Neither collation nor operator class specifications are required -- -- supplying them merely *limits* matches to indexes with matching opclasses -- used for relevant indexes EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (KEY, fruit text_pattern_ops) DO NOTHING; -- Okay, arbitrates using both index where text_pattern_ops opclass does and -- does not appear. EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (KEY, fruit COLLATE "C") DO NOTHING; -- Okay, but only accepts the single index where both opclass and collation are -- specified EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (fruit COLLATE "C" text_pattern_ops, KEY) DO NOTHING; -- Okay, but only accepts the single index where both opclass and collation are -- specified (plus expression variant) EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (lower(fruit) COLLATE "C", KEY, KEY) DO NOTHING; -- Attribute appears twice, while not all attributes/expressions on attributes -- appearing within index definition match in terms of both opclass and -- collation. -- -- Works because every attribute in inference specification needs to be -- satisfied once or more by cataloged index attribute, and as always when an -- attribute in the cataloged definition has a non-default opclass/collation, -- it still satisfied some inference attribute lacking any particular -- opclass/collation specification. -- -- The implementation is liberal in accepting inference specifications on the -- assumption that multiple inferred unique indexes will prevent problematic -- cases. It rolls with unique indexes where attributes redundantly appear -- multiple times, too (which is not tested here). EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (fruit, KEY, fruit text_pattern_ops, KEY) DO NOTHING; EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (lower(fruit) COLLATE "C" text_pattern_ops, KEY, KEY) DO NOTHING; DROP INDEX op_index_key; DROP INDEX collation_index_key; DROP INDEX both_index_key; DROP INDEX both_index_expr_key; -- -- Make sure that cross matching of attribute opclass/collation does not occur -- CREATE UNIQUE INDEX cross_match ON insertconflicttest (lower(fruit) COLLATE "C", upper(fruit) text_pattern_ops); -- fails: EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (lower(fruit) text_pattern_ops, upper(fruit) COLLATE "C") DO NOTHING; -- works: EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (lower(fruit) COLLATE "C", upper(fruit) text_pattern_ops) DO NOTHING; DROP INDEX cross_match; -- -- Single key tests -- CREATE UNIQUE INDEX key_index ON insertconflicttest (KEY); -- -- Explain tests -- EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Bilberry') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.fruit; -- Should display qual actually attributable to internal sequential scan: EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Bilberry') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.fruit WHERE insertconflicttest.fruit != 'Cawesh'; -- With EXCLUDED.* expression in scan node: EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.fruit WHERE excluded.fruit != 'Elderberry'; -- Does the same, but JSON format shows "Conflict Arbiter Index" as JSON array: EXPLAIN ( COSTS OFF, format json ) INSERT INTO insertconflicttest VALUES (0, 'Bilberry') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.fruit WHERE insertconflicttest.fruit != 'Lime' RETURNING *; -- Fails (no unique index inference specification, required for do update variant): INSERT INTO insertconflicttest VALUES (1, 'Apple') ON CONFLICT DO UPDATE SET fruit = excluded.fruit; -- inference succeeds: INSERT INTO insertconflicttest VALUES (1, 'Apple') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (2, 'Orange') ON CONFLICT (KEY, KEY, KEY) DO UPDATE SET fruit = excluded.fruit; -- Succeed, since multi-assignment does not involve subquery: INSERT INTO insertconflicttest VALUES (1, 'Apple'), (2, 'Orange') ON CONFLICT (KEY) DO UPDATE SET (fruit, KEY) = (excluded.fruit, excluded.key); -- Give good diagnostic message when EXCLUDED.* spuriously referenced from -- RETURNING: INSERT INTO insertconflicttest VALUES (1, 'Apple') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.fruit RETURNING excluded.fruit; -- Only suggest .* column when inference element misspelled: INSERT INTO insertconflicttest VALUES (1, 'Apple') ON CONFLICT (keyy) DO UPDATE SET fruit = excluded.fruit; -- Have useful HINT for EXCLUDED.* RTE within UPDATE: INSERT INTO insertconflicttest VALUES (1, 'Apple') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.fruitt; -- inference fails: INSERT INTO insertconflicttest VALUES (3, 'Kiwi') ON CONFLICT (KEY, fruit) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (4, 'Mango') ON CONFLICT (fruit, KEY) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (5, 'Lemon') ON CONFLICT (fruit) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (6, 'Passionfruit') ON CONFLICT (lower(fruit)) DO UPDATE SET fruit = excluded.fruit; -- Check the target relation can be aliased INSERT INTO insertconflicttest AS ict VALUES (6, 'Passionfruit') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.fruit; -- ok, no reference to target table INSERT INTO insertconflicttest AS ict VALUES (6, 'Passionfruit') ON CONFLICT (KEY) DO UPDATE SET fruit = ict.fruit; -- ok, alias INSERT INTO insertconflicttest AS ict VALUES (6, 'Passionfruit') ON CONFLICT (KEY) DO UPDATE SET fruit = insertconflicttest.fruit; -- error, references aliased away name DROP INDEX key_index; -- -- Composite key tests -- CREATE UNIQUE INDEX comp_key_index ON insertconflicttest (KEY, fruit); -- inference succeeds: INSERT INTO insertconflicttest VALUES (7, 'Raspberry') ON CONFLICT (KEY, fruit) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (8, 'Lime') ON CONFLICT (fruit, KEY) DO UPDATE SET fruit = excluded.fruit; -- inference fails: INSERT INTO insertconflicttest VALUES (9, 'Banana') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (10, 'Blueberry') ON CONFLICT (KEY, KEY, KEY) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (11, 'Cherry') ON CONFLICT (KEY, lower(fruit)) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (12, 'Date') ON CONFLICT (lower(fruit), KEY) DO UPDATE SET fruit = excluded.fruit; DROP INDEX comp_key_index; -- -- Partial index tests, no inference predicate specified -- CREATE UNIQUE INDEX part_comp_key_index ON insertconflicttest (KEY, fruit) WHERE KEY < 5; CREATE UNIQUE INDEX expr_part_comp_key_index ON insertconflicttest (KEY, lower(fruit)) WHERE KEY < 5; -- inference fails: INSERT INTO insertconflicttest VALUES (13, 'Grape') ON CONFLICT (KEY, fruit) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (14, 'Raisin') ON CONFLICT (fruit, KEY) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (15, 'Cranberry') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (16, 'Melon') ON CONFLICT (KEY, KEY, KEY) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (17, 'Mulberry') ON CONFLICT (KEY, lower(fruit)) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (18, 'Pineapple') ON CONFLICT (lower(fruit), KEY) DO UPDATE SET fruit = excluded.fruit; DROP INDEX part_comp_key_index; DROP INDEX expr_part_comp_key_index; -- -- Expression index tests -- CREATE UNIQUE INDEX expr_key_index ON insertconflicttest (lower(fruit)); -- inference succeeds: INSERT INTO insertconflicttest VALUES (20, 'Quince') ON CONFLICT (lower(fruit)) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (21, 'Pomegranate') ON CONFLICT (lower(fruit), lower(fruit)) DO UPDATE SET fruit = excluded.fruit; -- inference fails: INSERT INTO insertconflicttest VALUES (22, 'Apricot') ON CONFLICT (upper(fruit)) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (23, 'Blackberry') ON CONFLICT (fruit) DO UPDATE SET fruit = excluded.fruit; DROP INDEX expr_key_index; -- -- Expression index tests (with regular column) -- CREATE UNIQUE INDEX expr_comp_key_index ON insertconflicttest (KEY, lower(fruit)); CREATE UNIQUE INDEX tricky_expr_comp_key_index ON insertconflicttest (KEY, lower(fruit), upper(fruit)); -- inference succeeds: INSERT INTO insertconflicttest VALUES (24, 'Plum') ON CONFLICT (KEY, lower(fruit)) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (25, 'Peach') ON CONFLICT (lower(fruit), KEY) DO UPDATE SET fruit = excluded.fruit; -- Should not infer "tricky_expr_comp_key_index" index: EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (26, 'Fig') ON CONFLICT (lower(fruit), KEY, lower(fruit), KEY) DO UPDATE SET fruit = excluded.fruit; -- inference fails: INSERT INTO insertconflicttest VALUES (27, 'Prune') ON CONFLICT (KEY, upper(fruit)) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (28, 'Redcurrant') ON CONFLICT (fruit, KEY) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (29, 'Nectarine') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.fruit; DROP INDEX expr_comp_key_index; DROP INDEX tricky_expr_comp_key_index; -- -- Non-spurious duplicate violation tests -- CREATE UNIQUE INDEX key_index ON insertconflicttest (KEY); CREATE UNIQUE INDEX fruit_index ON insertconflicttest (fruit); -- succeeds, since UPDATE happens to update "fruit" to existing value: INSERT INTO insertconflicttest VALUES (26, 'Fig') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.fruit; -- fails, since UPDATE is to row with key value 26, and we're updating "fruit" -- to a value that happens to exist in another row ('peach'): INSERT INTO insertconflicttest VALUES (26, 'Peach') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.fruit; -- succeeds, since "key" isn't repeated/referenced in UPDATE, and "fruit" -- arbitrates that statement updates existing "Fig" row: INSERT INTO insertconflicttest VALUES (25, 'Fig') ON CONFLICT (fruit) DO UPDATE SET fruit = excluded.fruit; DROP INDEX key_index; DROP INDEX fruit_index; -- -- Test partial unique index inference -- CREATE UNIQUE INDEX partial_key_index ON insertconflicttest (KEY) WHERE fruit LIKE '%berry'; -- Succeeds INSERT INTO insertconflicttest VALUES (23, 'Blackberry') ON CONFLICT (KEY) WHERE fruit LIKE '%berry' DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (23, 'Blackberry') ON CONFLICT (KEY) WHERE fruit LIKE '%berry' AND fruit = 'inconsequential' DO NOTHING; -- fails INSERT INTO insertconflicttest VALUES (23, 'Blackberry') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.fruit; INSERT INTO insertconflicttest VALUES (23, 'Blackberry') ON CONFLICT (KEY) WHERE fruit LIKE '%berry' OR fruit = 'consequential' DO NOTHING; INSERT INTO insertconflicttest VALUES (23, 'Blackberry') ON CONFLICT (fruit) WHERE fruit LIKE '%berry' DO UPDATE SET fruit = excluded.fruit; DROP INDEX partial_key_index; -- -- Test that wholerow references to ON CONFLICT's EXCLUDED work -- CREATE UNIQUE INDEX plain ON insertconflicttest (KEY); -- Succeeds, updates existing row: INSERT INTO insertconflicttest AS i VALUES (23, 'Jackfruit') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.fruit WHERE i.* != excluded.* RETURNING *; -- No update this time, though: INSERT INTO insertconflicttest AS i VALUES (23, 'Jackfruit') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.fruit WHERE i.* != excluded.* RETURNING *; -- Predicate changed to require match rather than non-match, so updates once more: INSERT INTO insertconflicttest AS i VALUES (23, 'Jackfruit') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.fruit WHERE i.* = excluded.* RETURNING *; -- Assign: INSERT INTO insertconflicttest AS i VALUES (23, 'Avocado') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.*::text RETURNING *; -- deparse whole row var in WHERE and SET clauses: EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest AS i VALUES (23, 'Avocado') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.fruit WHERE excluded.* IS NULL; EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest AS i VALUES (23, 'Avocado') ON CONFLICT (KEY) DO UPDATE SET fruit = excluded.*::text; DROP INDEX plain; -- Cleanup DROP TABLE insertconflicttest; -- -- Verify that EXCLUDED does not allow system column references. These -- do not make sense because EXCLUDED isn't an already stored tuple -- (and thus doesn't have a ctid etc). -- CREATE TABLE syscolconflicttest ( key int4, data text ); INSERT INTO syscolconflicttest VALUES (1); INSERT INTO syscolconflicttest VALUES (1) ON CONFLICT (KEY) DO UPDATE SET data = excluded.ctid::text; DROP TABLE syscolconflicttest; -- -- Previous tests all managed to not test any expressions requiring -- planner preprocessing ... -- CREATE TABLE insertconflict ( a bigint, b bigint ); CREATE UNIQUE INDEX insertconflicti1 ON insertconflict (coalesce(a, 0)); CREATE UNIQUE INDEX insertconflicti2 ON insertconflict (b) WHERE coalesce(a, 1) > 0; INSERT INTO insertconflict VALUES (1, 2) ON CONFLICT (coalesce(a, 0)) DO NOTHING; INSERT INTO insertconflict VALUES (1, 2) ON CONFLICT (b) WHERE coalesce(a, 1) > 0 DO NOTHING; INSERT INTO insertconflict VALUES (1, 2) ON CONFLICT (b) WHERE coalesce(a, 1) > 1 DO NOTHING; DROP TABLE insertconflict; -- -- test insertion through view -- CREATE TABLE insertconflict ( f1 int PRIMARY KEY, f2 text ); CREATE VIEW insertconflictv AS SELECT * FROM insertconflict WITH cascaded CHECK option; INSERT INTO insertconflictv VALUES (1, 'foo') ON CONFLICT (f1) DO UPDATE SET f2 = excluded.f2; SELECT * FROM insertconflict; INSERT INTO insertconflictv VALUES (1, 'bar') ON CONFLICT (f1) DO UPDATE SET f2 = excluded.f2; SELECT * FROM insertconflict; DROP VIEW insertconflictv; DROP TABLE insertconflict; -- ****************************************************************** -- * * -- * Test inheritance (example taken from tutorial) * -- * * -- ****************************************************************** CREATE TABLE cities ( name text, population float8, altitude int -- (in ft) ); CREATE TABLE capitals ( state char(2) ) INHERITS ( cities ); -- Create unique indexes. Due to a general limitation of inheritance, -- uniqueness is only enforced per-relation. Unique index inference -- specification will do the right thing, though. CREATE UNIQUE INDEX cities_names_unique ON cities (name); CREATE UNIQUE INDEX capitals_names_unique ON capitals (name); -- prepopulate the tables. INSERT INTO cities VALUES ('San Francisco', 7.24E + 5, 63); INSERT INTO cities VALUES ('Las Vegas', 2.583E + 5, 2174); INSERT INTO cities VALUES ('Mariposa', 1200, 1953); INSERT INTO capitals VALUES ('Sacramento', 3.694E + 5, 30, 'CA'); INSERT INTO capitals VALUES ('Madison', 1.913E + 5, 845, 'WI'); -- Tests proper for inheritance: SELECT * FROM capitals; -- Succeeds: INSERT INTO cities VALUES ('Las Vegas', 2.583E + 5, 2174) ON CONFLICT DO NOTHING; INSERT INTO capitals VALUES ('Sacramento', 4664.E + 5, 30, 'CA') ON CONFLICT (name) DO UPDATE SET population = excluded.population; -- Wrong "Sacramento", so do nothing: INSERT INTO capitals VALUES ('Sacramento', 50, 2267, 'NE') ON CONFLICT (name) DO NOTHING; SELECT * FROM capitals; INSERT INTO cities VALUES ('Las Vegas', 5.83E + 5, 2001) ON CONFLICT (name) DO UPDATE SET population = excluded.population, altitude = excluded.altitude; SELECT tableoid::regclass, * FROM cities; INSERT INTO capitals VALUES ('Las Vegas', 5.83E + 5, 2222, 'NV') ON CONFLICT (name) DO UPDATE SET population = excluded.population; -- Capitals will contain new capital, Las Vegas: SELECT * FROM capitals; -- Cities contains two instances of "Las Vegas", since unique constraints don't -- work across inheritance: SELECT tableoid::regclass, * FROM cities; -- This only affects "cities" version of "Las Vegas": INSERT INTO cities VALUES ('Las Vegas', 5.86E + 5, 2223) ON CONFLICT (name) DO UPDATE SET population = excluded.population, altitude = excluded.altitude; SELECT tableoid::regclass, * FROM cities; -- clean up DROP TABLE capitals; DROP TABLE cities; -- Make sure a table named excluded is handled properly CREATE TABLE excluded ( key int PRIMARY KEY, data text ); INSERT INTO excluded VALUES (1, '1'); -- error, ambiguous INSERT INTO excluded VALUES (1, '2') ON CONFLICT (KEY) DO UPDATE SET data = excluded.data RETURNING *; -- ok, aliased INSERT INTO excluded AS target VALUES (1, '2') ON CONFLICT (KEY) DO UPDATE SET data = excluded.data RETURNING *; -- ok, aliased INSERT INTO excluded AS target VALUES (1, '2') ON CONFLICT (KEY) DO UPDATE SET data = target.data RETURNING *; -- make sure excluded isn't a problem in returning clause INSERT INTO excluded VALUES (1, '2') ON CONFLICT (KEY) DO UPDATE SET data = 3 RETURNING excluded.*; -- clean up DROP TABLE excluded; -- check that references to columns after dropped columns are handled correctly CREATE TABLE dropcol ( key int PRIMARY KEY, drop1 int, keep1 text, drop2 numeric, keep2 float ); INSERT INTO dropcol (KEY, drop1, keep1, drop2, keep2) VALUES (1, 1, '1', '1', 1); -- set using excluded INSERT INTO dropcol (KEY, drop1, keep1, drop2, keep2) VALUES (1, 2, '2', '2', 2) ON CONFLICT (KEY) DO UPDATE SET drop1 = excluded.drop1, keep1 = excluded.keep1, drop2 = excluded.drop2, keep2 = excluded.keep2 WHERE excluded.drop1 IS NOT NULL AND excluded.keep1 IS NOT NULL AND excluded.drop2 IS NOT NULL AND excluded.keep2 IS NOT NULL AND dropcol.drop1 IS NOT NULL AND dropcol.keep1 IS NOT NULL AND dropcol.drop2 IS NOT NULL AND dropcol.keep2 IS NOT NULL RETURNING *; ; -- set using existing table INSERT INTO dropcol (KEY, drop1, keep1, drop2, keep2) VALUES (1, 3, '3', '3', 3) ON CONFLICT (KEY) DO UPDATE SET drop1 = dropcol.drop1, keep1 = dropcol.keep1, drop2 = dropcol.drop2, keep2 = dropcol.keep2 RETURNING *; ; ALTER TABLE dropcol DROP COLUMN drop1, DROP COLUMN drop2; -- set using excluded INSERT INTO dropcol (KEY, keep1, keep2) VALUES (1, '4', 4) ON CONFLICT (KEY) DO UPDATE SET keep1 = excluded.keep1, keep2 = excluded.keep2 WHERE excluded.keep1 IS NOT NULL AND excluded.keep2 IS NOT NULL AND dropcol.keep1 IS NOT NULL AND dropcol.keep2 IS NOT NULL RETURNING *; ; -- set using existing table INSERT INTO dropcol (KEY, keep1, keep2) VALUES (1, '5', 5) ON CONFLICT (KEY) DO UPDATE SET keep1 = dropcol.keep1, keep2 = dropcol.keep2 RETURNING *; ; DROP TABLE dropcol; -- check handling of regular btree constraint along with gist constraint CREATE TABLE twoconstraints ( f1 int UNIQUE, f2 box, EXCLUDE USING gist (f2 WITH &&) ); INSERT INTO twoconstraints VALUES (1, '((0,0),(1,1))'); INSERT INTO twoconstraints VALUES (1, '((2,2),(3,3))'); -- fail on f1 INSERT INTO twoconstraints VALUES (2, '((0,0),(1,2))'); -- fail on f2 INSERT INTO twoconstraints VALUES (2, '((0,0),(1,2))') ON CONFLICT ON CONSTRAINT twoconstraints_f1_key DO NOTHING; -- fail on f2 INSERT INTO twoconstraints VALUES (2, '((0,0),(1,2))') ON CONFLICT ON CONSTRAINT twoconstraints_f2_excl DO NOTHING; -- do nothing SELECT * FROM twoconstraints; DROP TABLE twoconstraints; -- check handling of self-conflicts at various isolation levels CREATE TABLE selfconflict ( f1 int PRIMARY KEY, f2 int ); BEGIN TRANSACTION ISOLATION level read COMMITTED; INSERT INTO selfconflict VALUES (1, 1), (1, 2) ON CONFLICT DO NOTHING; COMMIT; BEGIN TRANSACTION ISOLATION level REPEATABLE read; INSERT INTO selfconflict VALUES (2, 1), (2, 2) ON CONFLICT DO NOTHING; COMMIT; BEGIN TRANSACTION ISOLATION level SERIALIZABLE; INSERT INTO selfconflict VALUES (3, 1), (3, 2) ON CONFLICT DO NOTHING; COMMIT; BEGIN TRANSACTION ISOLATION level read COMMITTED; INSERT INTO selfconflict VALUES (4, 1), (4, 2) ON CONFLICT (f1) DO UPDATE SET f2 = 0; COMMIT; BEGIN TRANSACTION ISOLATION level REPEATABLE read; INSERT INTO selfconflict VALUES (5, 1), (5, 2) ON CONFLICT (f1) DO UPDATE SET f2 = 0; COMMIT; BEGIN TRANSACTION ISOLATION level SERIALIZABLE; INSERT INTO selfconflict VALUES (6, 1), (6, 2) ON CONFLICT (f1) DO UPDATE SET f2 = 0; COMMIT; SELECT * FROM selfconflict; DROP TABLE selfconflict; -- check ON CONFLICT handling with partitioned tables CREATE TABLE parted_conflict_test ( a int UNIQUE, b char ) PARTITION BY LIST (a); CREATE TABLE parted_conflict_test_1 PARTITION OF parted_conflict_test (b UNIQUE) FOR VALUES IN (1, 2); -- no indexes required here INSERT INTO parted_conflict_test VALUES (1, 'a') ON CONFLICT DO NOTHING; -- index on a required, which does exist in parent INSERT INTO parted_conflict_test VALUES (1, 'a') ON CONFLICT (a) DO NOTHING; INSERT INTO parted_conflict_test VALUES (1, 'a') ON CONFLICT (a) DO UPDATE SET b = excluded.b; -- targeting partition directly will work INSERT INTO parted_conflict_test_1 VALUES (1, 'a') ON CONFLICT (a) DO NOTHING; INSERT INTO parted_conflict_test_1 VALUES (1, 'b') ON CONFLICT (a) DO UPDATE SET b = excluded.b; -- index on b required, which doesn't exist in parent INSERT INTO parted_conflict_test VALUES (2, 'b') ON CONFLICT (b) DO UPDATE SET a = excluded.a; -- targeting partition directly will work INSERT INTO parted_conflict_test_1 VALUES (2, 'b') ON CONFLICT (b) DO UPDATE SET a = excluded.a; -- should see (2, 'b') SELECT * FROM parted_conflict_test ORDER BY a; -- now check that DO UPDATE works correctly for target partition with -- different attribute numbers CREATE TABLE parted_conflict_test_2 ( b char, a int UNIQUE ); ALTER TABLE parted_conflict_test ATTACH PARTITION parted_conflict_test_2 FOR VALUES IN (3); TRUNCATE parted_conflict_test; INSERT INTO parted_conflict_test VALUES (3, 'a') ON CONFLICT (a) DO UPDATE SET b = excluded.b; INSERT INTO parted_conflict_test VALUES (3, 'b') ON CONFLICT (a) DO UPDATE SET b = excluded.b; -- should see (3, 'b') SELECT * FROM parted_conflict_test ORDER BY a; -- case where parent will have a dropped column, but the partition won't ALTER TABLE parted_conflict_test DROP b, ADD b char; CREATE TABLE parted_conflict_test_3 PARTITION OF parted_conflict_test FOR VALUES IN (4); TRUNCATE parted_conflict_test; INSERT INTO parted_conflict_test (a, b) VALUES (4, 'a') ON CONFLICT (a) DO UPDATE SET b = excluded.b; INSERT INTO parted_conflict_test (a, b) VALUES (4, 'b') ON CONFLICT (a) DO UPDATE SET b = excluded.b WHERE parted_conflict_test.b = 'a'; -- should see (4, 'b') SELECT * FROM parted_conflict_test ORDER BY a; -- case with multi-level partitioning CREATE TABLE parted_conflict_test_4 PARTITION OF parted_conflict_test FOR VALUES IN (5) PARTITION BY LIST (a); CREATE TABLE parted_conflict_test_4_1 PARTITION OF parted_conflict_test_4 FOR VALUES IN (5); TRUNCATE parted_conflict_test; INSERT INTO parted_conflict_test (a, b) VALUES (5, 'a') ON CONFLICT (a) DO UPDATE SET b = excluded.b; INSERT INTO parted_conflict_test (a, b) VALUES (5, 'b') ON CONFLICT (a) DO UPDATE SET b = excluded.b WHERE parted_conflict_test.b = 'a'; -- should see (5, 'b') SELECT * FROM parted_conflict_test ORDER BY a; -- test with multiple rows TRUNCATE parted_conflict_test; INSERT INTO parted_conflict_test (a, b) VALUES (1, 'a'), (2, 'a'), (4, 'a') ON CONFLICT (a) DO UPDATE SET b = excluded.b WHERE excluded.b = 'b'; INSERT INTO parted_conflict_test (a, b) VALUES (1, 'b'), (2, 'c'), (4, 'b') ON CONFLICT (a) DO UPDATE SET b = excluded.b WHERE excluded.b = 'b'; -- should see (1, 'b'), (2, 'a'), (4, 'b') SELECT * FROM parted_conflict_test ORDER BY a; DROP TABLE parted_conflict_test; -- test behavior of inserting a conflicting tuple into an intermediate -- partitioning level CREATE TABLE parted_conflict ( a int PRIMARY KEY, b text ) PARTITION BY RANGE (a); CREATE TABLE parted_conflict_1 PARTITION OF parted_conflict FOR VALUES FROM (0) TO (1000) PARTITION BY RANGE (a); CREATE TABLE parted_conflict_1_1 PARTITION OF parted_conflict_1 FOR VALUES FROM (0) TO (500); INSERT INTO parted_conflict VALUES (40, 'forty'); INSERT INTO parted_conflict_1 VALUES (40, 'cuarenta') ON CONFLICT (a) DO UPDATE SET b = excluded.b; DROP TABLE parted_conflict; -- same thing, but this time try to use an index that's created not in the -- partition CREATE TABLE parted_conflict ( a int, b text ) PARTITION BY RANGE (a); CREATE TABLE parted_conflict_1 PARTITION OF parted_conflict FOR VALUES FROM (0) TO (1000) PARTITION BY RANGE (a); CREATE TABLE parted_conflict_1_1 PARTITION OF parted_conflict_1 FOR VALUES FROM (0) TO (500); CREATE UNIQUE INDEX ON ONLY parted_conflict_1 (a); CREATE UNIQUE INDEX ON ONLY parted_conflict (a); ALTER INDEX parted_conflict_a_idx ATTACH PARTITION parted_conflict_1_a_idx; INSERT INTO parted_conflict VALUES (40, 'forty'); INSERT INTO parted_conflict_1 VALUES (40, 'cuarenta') ON CONFLICT (a) DO UPDATE SET b = excluded.b; DROP TABLE parted_conflict; -- test whole-row Vars in ON CONFLICT expressions CREATE TABLE parted_conflict ( a int, b text, c int ) PARTITION BY RANGE (a); CREATE TABLE parted_conflict_1 ( drp text, c int, a int, b text ); ALTER TABLE parted_conflict_1 DROP COLUMN drp; CREATE UNIQUE INDEX ON parted_conflict (a, b); ALTER TABLE parted_conflict ATTACH PARTITION parted_conflict_1 FOR VALUES FROM (0) TO (1000); TRUNCATE parted_conflict; INSERT INTO parted_conflict VALUES (50, 'cincuenta', 1); INSERT INTO parted_conflict VALUES (50, 'cincuenta', 2) ON CONFLICT (a, b) DO UPDATE SET (a, b, c) = ROW (excluded.*) WHERE parted_conflict = (50, text 'cincuenta', 1) AND excluded = (50, text 'cincuenta', 2); -- should see (50, 'cincuenta', 2) SELECT * FROM parted_conflict ORDER BY a; -- test with statement level triggers CREATE OR REPLACE FUNCTION parted_conflict_update_func () RETURNS TRIGGER AS $$ DECLARE r record; BEGIN FOR r IN SELECT * FROM inserted LOOP RAISE notice 'a = %, b = %, c = %', r.a, r.b, r.c; END LOOP; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER parted_conflict_update AFTER UPDATE ON parted_conflict referencing new TABLE AS inserted FOR EACH statement EXECUTE PROCEDURE parted_conflict_update_func (); TRUNCATE parted_conflict; INSERT INTO parted_conflict VALUES (0, 'cero', 1); INSERT INTO parted_conflict VALUES (0, 'cero', 1) ON CONFLICT (a, b) DO UPDATE SET c = parted_conflict.c + 1; DROP TABLE parted_conflict; DROP FUNCTION parted_conflict_update_func (); pgFormatter-4.2/t/pg-test-files/expected/int2.sql000066400000000000000000000072441361326045100220050ustar00rootroot00000000000000-- -- INT2 -- CREATE TABLE INT2_TBL ( f1 int2 ); INSERT INTO INT2_TBL (f1) VALUES ('0 '); INSERT INTO INT2_TBL (f1) VALUES (' 1234 '); INSERT INTO INT2_TBL (f1) VALUES (' -1234'); INSERT INTO INT2_TBL (f1) VALUES ('34.5'); -- largest and smallest values INSERT INTO INT2_TBL (f1) VALUES ('32767'); INSERT INTO INT2_TBL (f1) VALUES ('-32767'); -- bad input values -- should give errors INSERT INTO INT2_TBL (f1) VALUES ('100000'); INSERT INTO INT2_TBL (f1) VALUES ('asdf'); INSERT INTO INT2_TBL (f1) VALUES (' '); INSERT INTO INT2_TBL (f1) VALUES ('- 1234'); INSERT INTO INT2_TBL (f1) VALUES ('4 444'); INSERT INTO INT2_TBL (f1) VALUES ('123 dt'); INSERT INTO INT2_TBL (f1) VALUES (''); SELECT '' AS five, * FROM INT2_TBL; SELECT '' AS four, i.* FROM INT2_TBL i WHERE i.f1 <> int2 '0'; SELECT '' AS four, i.* FROM INT2_TBL i WHERE i.f1 <> int4 '0'; SELECT '' AS one, i.* FROM INT2_TBL i WHERE i.f1 = int2 '0'; SELECT '' AS one, i.* FROM INT2_TBL i WHERE i.f1 = int4 '0'; SELECT '' AS two, i.* FROM INT2_TBL i WHERE i.f1 < int2 '0'; SELECT '' AS two, i.* FROM INT2_TBL i WHERE i.f1 < int4 '0'; SELECT '' AS three, i.* FROM INT2_TBL i WHERE i.f1 <= int2 '0'; SELECT '' AS three, i.* FROM INT2_TBL i WHERE i.f1 <= int4 '0'; SELECT '' AS two, i.* FROM INT2_TBL i WHERE i.f1 > int2 '0'; SELECT '' AS two, i.* FROM INT2_TBL i WHERE i.f1 > int4 '0'; SELECT '' AS three, i.* FROM INT2_TBL i WHERE i.f1 >= int2 '0'; SELECT '' AS three, i.* FROM INT2_TBL i WHERE i.f1 >= int4 '0'; -- positive odds SELECT '' AS one, i.* FROM INT2_TBL i WHERE (i.f1 % int2 '2') = int2 '1'; -- any evens SELECT '' AS three, i.* FROM INT2_TBL i WHERE (i.f1 % int4 '2') = int2 '0'; SELECT '' AS five, i.f1, i.f1 * int2 '2' AS x FROM INT2_TBL i; SELECT '' AS five, i.f1, i.f1 * int2 '2' AS x FROM INT2_TBL i WHERE abs(f1) < 16384; SELECT '' AS five, i.f1, i.f1 * int4 '2' AS x FROM INT2_TBL i; SELECT '' AS five, i.f1, i.f1 + int2 '2' AS x FROM INT2_TBL i; SELECT '' AS five, i.f1, i.f1 + int2 '2' AS x FROM INT2_TBL i WHERE f1 < 32766; SELECT '' AS five, i.f1, i.f1 + int4 '2' AS x FROM INT2_TBL i; SELECT '' AS five, i.f1, i.f1 - int2 '2' AS x FROM INT2_TBL i; SELECT '' AS five, i.f1, i.f1 - int2 '2' AS x FROM INT2_TBL i WHERE f1 > - 32767; SELECT '' AS five, i.f1, i.f1 - int4 '2' AS x FROM INT2_TBL i; SELECT '' AS five, i.f1, i.f1 / int2 '2' AS x FROM INT2_TBL i; SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT2_TBL i; -- corner cases SELECT (- 1::int2 << 15)::text; SELECT ((- 1::int2 << 15) + 1::int2)::text; -- check sane handling of INT16_MIN overflow cases SELECT (- 32768)::int2 * (- 1)::int2; SELECT (- 32768)::int2 / (- 1)::int2; SELECT (- 32768)::int2 % (- 1)::int2; -- check rounding when casting from float SELECT x, x::int2 AS int2_value FROM ( VALUES (- 2.5::float8), (- 1.5::float8), (- 0.5::float8), (0.0::float8), (0.5::float8), (1.5::float8), (2.5::float8)) t (x); -- check rounding when casting from numeric SELECT x, x::int2 AS int2_value FROM ( VALUES (- 2.5::numeric), (- 1.5::numeric), (- 0.5::numeric), (0.0::numeric), (0.5::numeric), (1.5::numeric), (2.5::numeric)) t (x); pgFormatter-4.2/t/pg-test-files/expected/int4.sql000066400000000000000000000114151361326045100220020ustar00rootroot00000000000000-- -- INT4 -- CREATE TABLE INT4_TBL ( f1 int4 ); INSERT INTO INT4_TBL (f1) VALUES (' 0 '); INSERT INTO INT4_TBL (f1) VALUES ('123456 '); INSERT INTO INT4_TBL (f1) VALUES (' -123456'); INSERT INTO INT4_TBL (f1) VALUES ('34.5'); -- largest and smallest values INSERT INTO INT4_TBL (f1) VALUES ('2147483647'); INSERT INTO INT4_TBL (f1) VALUES ('-2147483647'); -- bad input values -- should give errors INSERT INTO INT4_TBL (f1) VALUES ('1000000000000'); INSERT INTO INT4_TBL (f1) VALUES ('asdf'); INSERT INTO INT4_TBL (f1) VALUES (' '); INSERT INTO INT4_TBL (f1) VALUES (' asdf '); INSERT INTO INT4_TBL (f1) VALUES ('- 1234'); INSERT INTO INT4_TBL (f1) VALUES ('123 5'); INSERT INTO INT4_TBL (f1) VALUES (''); SELECT '' AS five, * FROM INT4_TBL; SELECT '' AS four, i.* FROM INT4_TBL i WHERE i.f1 <> int2 '0'; SELECT '' AS four, i.* FROM INT4_TBL i WHERE i.f1 <> int4 '0'; SELECT '' AS one, i.* FROM INT4_TBL i WHERE i.f1 = int2 '0'; SELECT '' AS one, i.* FROM INT4_TBL i WHERE i.f1 = int4 '0'; SELECT '' AS two, i.* FROM INT4_TBL i WHERE i.f1 < int2 '0'; SELECT '' AS two, i.* FROM INT4_TBL i WHERE i.f1 < int4 '0'; SELECT '' AS three, i.* FROM INT4_TBL i WHERE i.f1 <= int2 '0'; SELECT '' AS three, i.* FROM INT4_TBL i WHERE i.f1 <= int4 '0'; SELECT '' AS two, i.* FROM INT4_TBL i WHERE i.f1 > int2 '0'; SELECT '' AS two, i.* FROM INT4_TBL i WHERE i.f1 > int4 '0'; SELECT '' AS three, i.* FROM INT4_TBL i WHERE i.f1 >= int2 '0'; SELECT '' AS three, i.* FROM INT4_TBL i WHERE i.f1 >= int4 '0'; -- positive odds SELECT '' AS one, i.* FROM INT4_TBL i WHERE (i.f1 % int2 '2') = int2 '1'; -- any evens SELECT '' AS three, i.* FROM INT4_TBL i WHERE (i.f1 % int4 '2') = int2 '0'; SELECT '' AS five, i.f1, i.f1 * int2 '2' AS x FROM INT4_TBL i; SELECT '' AS five, i.f1, i.f1 * int2 '2' AS x FROM INT4_TBL i WHERE abs(f1) < 1073741824; SELECT '' AS five, i.f1, i.f1 * int4 '2' AS x FROM INT4_TBL i; SELECT '' AS five, i.f1, i.f1 * int4 '2' AS x FROM INT4_TBL i WHERE abs(f1) < 1073741824; SELECT '' AS five, i.f1, i.f1 + int2 '2' AS x FROM INT4_TBL i; SELECT '' AS five, i.f1, i.f1 + int2 '2' AS x FROM INT4_TBL i WHERE f1 < 2147483646; SELECT '' AS five, i.f1, i.f1 + int4 '2' AS x FROM INT4_TBL i; SELECT '' AS five, i.f1, i.f1 + int4 '2' AS x FROM INT4_TBL i WHERE f1 < 2147483646; SELECT '' AS five, i.f1, i.f1 - int2 '2' AS x FROM INT4_TBL i; SELECT '' AS five, i.f1, i.f1 - int2 '2' AS x FROM INT4_TBL i WHERE f1 > - 2147483647; SELECT '' AS five, i.f1, i.f1 - int4 '2' AS x FROM INT4_TBL i; SELECT '' AS five, i.f1, i.f1 - int4 '2' AS x FROM INT4_TBL i WHERE f1 > - 2147483647; SELECT '' AS five, i.f1, i.f1 / int2 '2' AS x FROM INT4_TBL i; SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT4_TBL i; -- -- more complex expressions -- -- variations on unary minus parsing SELECT - 2 + 3 AS one; SELECT 4 - 2 AS two; SELECT 2 - - 1 AS three; SELECT 2 - - 2 AS four; SELECT int2 '2' * int2 '2' = int2 '16' / int2 '4' AS true; SELECT int4 '2' * int2 '2' = int2 '16' / int4 '4' AS true; SELECT int2 '2' * int4 '2' = int4 '16' / int2 '4' AS true; SELECT int4 '1000' < int4 '999' AS false; SELECT 4 ! AS twenty_four; SELECT !! 3 AS six; SELECT 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 AS ten; SELECT 2 + 2 / 2 AS three; SELECT (2 + 2) / 2 AS two; -- corner case SELECT (- 1::int4 << 31)::text; SELECT ((- 1::int4 << 31) + 1)::text; -- check sane handling of INT_MIN overflow cases SELECT (- 2147483648)::int4 * (- 1)::int4; SELECT (- 2147483648)::int4 / (- 1)::int4; SELECT (- 2147483648)::int4 % (- 1)::int4; SELECT (- 2147483648)::int4 * (- 1)::int2; SELECT (- 2147483648)::int4 / (- 1)::int2; SELECT (- 2147483648)::int4 % (- 1)::int2; -- check rounding when casting from float SELECT x, x::int4 AS int4_value FROM ( VALUES (- 2.5::float8), (- 1.5::float8), (- 0.5::float8), (0.0::float8), (0.5::float8), (1.5::float8), (2.5::float8)) t (x); -- check rounding when casting from numeric SELECT x, x::int4 AS int4_value FROM ( VALUES (- 2.5::numeric), (- 1.5::numeric), (- 0.5::numeric), (0.0::numeric), (0.5::numeric), (1.5::numeric), (2.5::numeric)) t (x); pgFormatter-4.2/t/pg-test-files/expected/int8.sql000066400000000000000000000237351361326045100220160ustar00rootroot00000000000000-- -- INT8 -- Test int8 64-bit integers. -- CREATE TABLE INT8_TBL ( q1 int8, q2 int8 ); INSERT INTO INT8_TBL VALUES (' 123 ', ' 456'); INSERT INTO INT8_TBL VALUES ('123 ', '4567890123456789'); INSERT INTO INT8_TBL VALUES ('4567890123456789', '123'); INSERT INTO INT8_TBL VALUES (+ 4567890123456789, '4567890123456789'); INSERT INTO INT8_TBL VALUES ('+4567890123456789', '-4567890123456789'); -- bad inputs INSERT INTO INT8_TBL (q1) VALUES (' '); INSERT INTO INT8_TBL (q1) VALUES ('xxx'); INSERT INTO INT8_TBL (q1) VALUES ('3908203590239580293850293850329485'); INSERT INTO INT8_TBL (q1) VALUES ('-1204982019841029840928340329840934'); INSERT INTO INT8_TBL (q1) VALUES ('- 123'); INSERT INTO INT8_TBL (q1) VALUES (' 345 5'); INSERT INTO INT8_TBL (q1) VALUES (''); SELECT * FROM INT8_TBL; -- int8/int8 cmp SELECT * FROM INT8_TBL WHERE q2 = 4567890123456789; SELECT * FROM INT8_TBL WHERE q2 <> 4567890123456789; SELECT * FROM INT8_TBL WHERE q2 < 4567890123456789; SELECT * FROM INT8_TBL WHERE q2 > 4567890123456789; SELECT * FROM INT8_TBL WHERE q2 <= 4567890123456789; SELECT * FROM INT8_TBL WHERE q2 >= 4567890123456789; -- int8/int4 cmp SELECT * FROM INT8_TBL WHERE q2 = 456; SELECT * FROM INT8_TBL WHERE q2 <> 456; SELECT * FROM INT8_TBL WHERE q2 < 456; SELECT * FROM INT8_TBL WHERE q2 > 456; SELECT * FROM INT8_TBL WHERE q2 <= 456; SELECT * FROM INT8_TBL WHERE q2 >= 456; -- int4/int8 cmp SELECT * FROM INT8_TBL WHERE 123 = q1; SELECT * FROM INT8_TBL WHERE 123 <> q1; SELECT * FROM INT8_TBL WHERE 123 < q1; SELECT * FROM INT8_TBL WHERE 123 > q1; SELECT * FROM INT8_TBL WHERE 123 <= q1; SELECT * FROM INT8_TBL WHERE 123 >= q1; -- int8/int2 cmp SELECT * FROM INT8_TBL WHERE q2 = '456'::int2; SELECT * FROM INT8_TBL WHERE q2 <> '456'::int2; SELECT * FROM INT8_TBL WHERE q2 < '456'::int2; SELECT * FROM INT8_TBL WHERE q2 > '456'::int2; SELECT * FROM INT8_TBL WHERE q2 <= '456'::int2; SELECT * FROM INT8_TBL WHERE q2 >= '456'::int2; -- int2/int8 cmp SELECT * FROM INT8_TBL WHERE '123'::int2 = q1; SELECT * FROM INT8_TBL WHERE '123'::int2 <> q1; SELECT * FROM INT8_TBL WHERE '123'::int2 < q1; SELECT * FROM INT8_TBL WHERE '123'::int2 > q1; SELECT * FROM INT8_TBL WHERE '123'::int2 <= q1; SELECT * FROM INT8_TBL WHERE '123'::int2 >= q1; SELECT '' AS five, q1 AS plus, - q1 AS minus FROM INT8_TBL; SELECT '' AS five, q1, q2, q1 + q2 AS plus FROM INT8_TBL; SELECT '' AS five, q1, q2, q1 - q2 AS minus FROM INT8_TBL; SELECT '' AS three, q1, q2, q1 * q2 AS multiply FROM INT8_TBL; SELECT '' AS three, q1, q2, q1 * q2 AS multiply FROM INT8_TBL WHERE q1 < 1000 OR (q2 > 0 AND q2 < 1000); SELECT '' AS five, q1, q2, q1 / q2 AS divide, q1 % q2 AS mod FROM INT8_TBL; SELECT '' AS five, q1, float8(q1) FROM INT8_TBL; SELECT '' AS five, q2, float8(q2) FROM INT8_TBL; SELECT 37 + q1 AS plus4 FROM INT8_TBL; SELECT 37 - q1 AS minus4 FROM INT8_TBL; SELECT '' AS five, 2 * q1 AS "twice int4" FROM INT8_TBL; SELECT '' AS five, q1 * 2 AS "twice int4" FROM INT8_TBL; -- int8 op int4 SELECT q1 + 42::int4 AS "8plus4", q1 - 42::int4 AS "8minus4", q1 * 42::int4 AS "8mul4", q1 / 42::int4 AS "8div4" FROM INT8_TBL; -- int4 op int8 SELECT 246::int4 + q1 AS "4plus8", 246::int4 - q1 AS "4minus8", 246::int4 * q1 AS "4mul8", 246::int4 / q1 AS "4div8" FROM INT8_TBL; -- int8 op int2 SELECT q1 + 42::int2 AS "8plus2", q1 - 42::int2 AS "8minus2", q1 * 42::int2 AS "8mul2", q1 / 42::int2 AS "8div2" FROM INT8_TBL; -- int2 op int8 SELECT 246::int2 + q1 AS "2plus8", 246::int2 - q1 AS "2minus8", 246::int2 * q1 AS "2mul8", 246::int2 / q1 AS "2div8" FROM INT8_TBL; SELECT q2, abs(q2) FROM INT8_TBL; SELECT min(q1), min(q2) FROM INT8_TBL; SELECT max(q1), max(q2) FROM INT8_TBL; -- TO_CHAR() -- SELECT '' AS to_char_1, to_char(q1, '9G999G999G999G999G999'), to_char(q2, '9,999,999,999,999,999') FROM INT8_TBL; SELECT '' AS to_char_2, to_char(q1, '9G999G999G999G999G999D999G999'), to_char(q2, '9,999,999,999,999,999.999,999') FROM INT8_TBL; SELECT '' AS to_char_3, to_char((q1 * - 1), '9999999999999999PR'), to_char((q2 * - 1), '9999999999999999.999PR') FROM INT8_TBL; SELECT '' AS to_char_4, to_char((q1 * - 1), '9999999999999999S'), to_char((q2 * - 1), 'S9999999999999999') FROM INT8_TBL; SELECT '' AS to_char_5, to_char(q2, 'MI9999999999999999') FROM INT8_TBL; SELECT '' AS to_char_6, to_char(q2, 'FMS9999999999999999') FROM INT8_TBL; SELECT '' AS to_char_7, to_char(q2, 'FM9999999999999999THPR') FROM INT8_TBL; SELECT '' AS to_char_8, to_char(q2, 'SG9999999999999999th') FROM INT8_TBL; SELECT '' AS to_char_9, to_char(q2, '0999999999999999') FROM INT8_TBL; SELECT '' AS to_char_10, to_char(q2, 'S0999999999999999') FROM INT8_TBL; SELECT '' AS to_char_11, to_char(q2, 'FM0999999999999999') FROM INT8_TBL; SELECT '' AS to_char_12, to_char(q2, 'FM9999999999999999.000') FROM INT8_TBL; SELECT '' AS to_char_13, to_char(q2, 'L9999999999999999.000') FROM INT8_TBL; SELECT '' AS to_char_14, to_char(q2, 'FM9999999999999999.999') FROM INT8_TBL; SELECT '' AS to_char_15, to_char(q2, 'S 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 . 9 9 9') FROM INT8_TBL; SELECT '' AS to_char_16, to_char(q2, E'99999 "text" 9999 "9999" 999 "\\"text between quote marks\\"" 9999') FROM INT8_TBL; SELECT '' AS to_char_17, to_char(q2, '999999SG9999999999') FROM INT8_TBL; -- check min/max values and overflow behavior SELECT '-9223372036854775808'::int8; SELECT '-9223372036854775809'::int8; SELECT '9223372036854775807'::int8; SELECT '9223372036854775808'::int8; SELECT - ('-9223372036854775807'::int8); SELECT - ('-9223372036854775808'::int8); SELECT '9223372036854775800'::int8 + '9223372036854775800'::int8; SELECT '-9223372036854775800'::int8 + '-9223372036854775800'::int8; SELECT '9223372036854775800'::int8 - '-9223372036854775800'::int8; SELECT '-9223372036854775800'::int8 - '9223372036854775800'::int8; SELECT '9223372036854775800'::int8 * '9223372036854775800'::int8; SELECT '9223372036854775800'::int8 / '0'::int8; SELECT '9223372036854775800'::int8 % '0'::int8; SELECT abs('-9223372036854775808'::int8); SELECT '9223372036854775800'::int8 + '100'::int4; SELECT '-9223372036854775800'::int8 - '100'::int4; SELECT '9223372036854775800'::int8 * '100'::int4; SELECT '100'::int4 + '9223372036854775800'::int8; SELECT '-100'::int4 - '9223372036854775800'::int8; SELECT '100'::int4 * '9223372036854775800'::int8; SELECT '9223372036854775800'::int8 + '100'::int2; SELECT '-9223372036854775800'::int8 - '100'::int2; SELECT '9223372036854775800'::int8 * '100'::int2; SELECT '-9223372036854775808'::int8 / '0'::int2; SELECT '100'::int2 + '9223372036854775800'::int8; SELECT '-100'::int2 - '9223372036854775800'::int8; SELECT '100'::int2 * '9223372036854775800'::int8; SELECT '100'::int2 / '0'::int8; SELECT CAST(q1 AS int4) FROM int8_tbl WHERE q2 = 456; SELECT CAST(q1 AS int4) FROM int8_tbl WHERE q2 <> 456; SELECT CAST(q1 AS int2) FROM int8_tbl WHERE q2 = 456; SELECT CAST(q1 AS int2) FROM int8_tbl WHERE q2 <> 456; SELECT CAST('42'::int2 AS int8), CAST('-37'::int2 AS int8); SELECT CAST(q1 AS float4), CAST(q2 AS float8) FROM INT8_TBL; SELECT CAST('36854775807.0'::float4 AS int8); SELECT CAST('922337203685477580700.0'::float8 AS int8); SELECT CAST(q1 AS oid) FROM INT8_TBL; SELECT oid::int8 FROM pg_class WHERE relname = 'pg_class'; -- bit operations SELECT q1, q2, q1 & q2 AS "and", q1 | q2 AS "or", q1 # q2 AS "xor", ~ q1 AS "not" FROM INT8_TBL; SELECT q1, q1 << 2 AS "shl", q1 >> 3 AS "shr" FROM INT8_TBL; -- generate_series SELECT * FROM generate_series('+4567890123456789'::int8, '+4567890123456799'::int8); SELECT * FROM generate_series('+4567890123456789'::int8, '+4567890123456799'::int8, 0); SELECT * FROM generate_series('+4567890123456789'::int8, '+4567890123456799'::int8, 2); -- corner case SELECT (- 1::int8 << 63)::text; SELECT ((- 1::int8 << 63) + 1)::text; -- check sane handling of INT64_MIN overflow cases SELECT (- 9223372036854775808)::int8 * (- 1)::int8; SELECT (- 9223372036854775808)::int8 / (- 1)::int8; SELECT (- 9223372036854775808)::int8 % (- 1)::int8; SELECT (- 9223372036854775808)::int8 * (- 1)::int4; SELECT (- 9223372036854775808)::int8 / (- 1)::int4; SELECT (- 9223372036854775808)::int8 % (- 1)::int4; SELECT (- 9223372036854775808)::int8 * (- 1)::int2; SELECT (- 9223372036854775808)::int8 / (- 1)::int2; SELECT (- 9223372036854775808)::int8 % (- 1)::int2; -- check rounding when casting from float SELECT x, x::int8 AS int8_value FROM ( VALUES (- 2.5::float8), (- 1.5::float8), (- 0.5::float8), (0.0::float8), (0.5::float8), (1.5::float8), (2.5::float8)) t (x); -- check rounding when casting from numeric SELECT x, x::int8 AS int8_value FROM ( VALUES (- 2.5::numeric), (- 1.5::numeric), (- 0.5::numeric), (0.0::numeric), (0.5::numeric), (1.5::numeric), (2.5::numeric)) t (x); pgFormatter-4.2/t/pg-test-files/expected/interval.sql000066400000000000000000000300261361326045100227470ustar00rootroot00000000000000-- -- INTERVAL -- SET DATESTYLE = 'ISO'; SET IntervalStyle TO postgres; -- check acceptance of "time zone style" SELECT INTERVAL '01:00' AS "One hour"; SELECT INTERVAL '+02:00' AS "Two hours"; SELECT INTERVAL '-08:00' AS "Eight hours"; SELECT INTERVAL '-1 +02:03' AS "22 hours ago..."; SELECT INTERVAL '-1 days +02:03' AS "22 hours ago..."; SELECT INTERVAL '1.5 weeks' AS "Ten days twelve hours"; SELECT INTERVAL '1.5 months' AS "One month 15 days"; SELECT INTERVAL '10 years -11 month -12 days +13:14' AS "9 years..."; CREATE TABLE INTERVAL_TBL ( f1 interval ); INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 1 minute'); INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 5 hour'); INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 10 day'); INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 34 year'); INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 3 months'); INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 14 seconds ago'); INSERT INTO INTERVAL_TBL (f1) VALUES ('1 day 2 hours 3 minutes 4 seconds'); INSERT INTO INTERVAL_TBL (f1) VALUES ('6 years'); INSERT INTO INTERVAL_TBL (f1) VALUES ('5 months'); INSERT INTO INTERVAL_TBL (f1) VALUES ('5 months 12 hours'); -- badly formatted interval INSERT INTO INTERVAL_TBL (f1) VALUES ('badly formatted interval'); INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 30 eons ago'); -- test interval operators SELECT '' AS ten, * FROM INTERVAL_TBL; SELECT '' AS nine, * FROM INTERVAL_TBL WHERE INTERVAL_TBL.f1 <> interval '@ 10 days'; SELECT '' AS three, * FROM INTERVAL_TBL WHERE INTERVAL_TBL.f1 <= interval '@ 5 hours'; SELECT '' AS three, * FROM INTERVAL_TBL WHERE INTERVAL_TBL.f1 < interval '@ 1 day'; SELECT '' AS one, * FROM INTERVAL_TBL WHERE INTERVAL_TBL.f1 = interval '@ 34 years'; SELECT '' AS five, * FROM INTERVAL_TBL WHERE INTERVAL_TBL.f1 >= interval '@ 1 month'; SELECT '' AS nine, * FROM INTERVAL_TBL WHERE INTERVAL_TBL.f1 > interval '@ 3 seconds ago'; SELECT '' AS fortyfive, r1.*, r2.* FROM INTERVAL_TBL r1, INTERVAL_TBL r2 WHERE r1.f1 > r2.f1 ORDER BY r1.f1, r2.f1; -- Test intervals that are large enough to overflow 64 bits in comparisons CREATE TEMP TABLE INTERVAL_TBL_OF ( f1 interval ); INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('2147483647 days 2147483647 months'), ('2147483647 days -2147483648 months'), ('1 year'), ('-2147483648 days 2147483647 months'), ('-2147483648 days -2147483648 months'); -- these should fail as out-of-range INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('2147483648 days'); INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('-2147483649 days'); INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('2147483647 years'); INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('-2147483648 years'); SELECT r1.*, r2.* FROM INTERVAL_TBL_OF r1, INTERVAL_TBL_OF r2 WHERE r1.f1 > r2.f1 ORDER BY r1.f1, r2.f1; CREATE INDEX ON INTERVAL_TBL_OF USING btree (f1); SET enable_seqscan TO FALSE; EXPLAIN ( COSTS OFF ) SELECT f1 FROM INTERVAL_TBL_OF r1 ORDER BY f1; SELECT f1 FROM INTERVAL_TBL_OF r1 ORDER BY f1; RESET enable_seqscan; DROP TABLE INTERVAL_TBL_OF; -- Test multiplication and division with intervals. -- Floating point arithmetic rounding errors can lead to unexpected results, -- though the code attempts to do the right thing and round up to days and -- minutes to avoid results such as '3 days 24:00 hours' or '14:20:60'. -- Note that it is expected for some day components to be greater than 29 and -- some time components be greater than 23:59:59 due to how intervals are -- stored internally. CREATE TABLE INTERVAL_MULDIV_TBL ( span interval ); SELECT span * 0.3 AS product FROM INTERVAL_MULDIV_TBL; SELECT span * 8.2 AS product FROM INTERVAL_MULDIV_TBL; SELECT span / 10 AS quotient FROM INTERVAL_MULDIV_TBL; SELECT span / 100 AS quotient FROM INTERVAL_MULDIV_TBL; DROP TABLE INTERVAL_MULDIV_TBL; SET DATESTYLE = 'postgres'; SET IntervalStyle TO postgres_verbose; SELECT '' AS ten, * FROM INTERVAL_TBL; -- test avg(interval), which is somewhat fragile since people have been -- known to change the allowed input syntax for type interval without -- updating pg_aggregate.agginitval SELECT avg(f1) FROM interval_tbl; -- test long interval input SELECT '4 millenniums 5 centuries 4 decades 1 year 4 months 4 days 17 minutes 31 seconds'::interval; -- test long interval output -- Note: the actual maximum length of the interval output is longer, -- but we need the test to work for both integer and floating-point -- timestamps. SELECT '100000000y 10mon -1000000000d -100000h -10min -10.000001s ago'::interval; -- test justify_hours() and justify_days() SELECT justify_hours(interval '6 months 3 days 52 hours 3 minutes 2 seconds') AS "6 mons 5 days 4 hours 3 mins 2 seconds"; SELECT justify_days(interval '6 months 36 days 5 hours 4 minutes 3 seconds') AS "7 mons 6 days 5 hours 4 mins 3 seconds"; -- test justify_interval() SELECT justify_interval(interval '1 month -1 hour') AS "1 month -1 hour"; -- test fractional second input, and detection of duplicate units SET DATESTYLE = 'ISO'; SET IntervalStyle TO postgres; SELECT '1 millisecond'::interval, '1 microsecond'::interval, '500 seconds 99 milliseconds 51 microseconds'::interval; SELECT '3 days 5 milliseconds'::interval; SELECT '1 second 2 seconds'::interval; -- error SELECT '10 milliseconds 20 milliseconds'::interval; -- error SELECT '5.5 seconds 3 milliseconds'::interval; -- error SELECT '1:20:05 5 microseconds'::interval; -- error SELECT '1 day 1 day'::interval; -- error SELECT interval '1-2'; -- SQL year-month literal SELECT interval '999' second; -- oversize leading field is ok SELECT interval '999' minute; SELECT interval '999' hour; SELECT interval '999' day; SELECT interval '999' month; -- test SQL-spec syntaxes for restricted field sets SELECT interval '1' year; SELECT interval '2' month; SELECT interval '3' day; SELECT interval '4' hour; SELECT interval '5' minute; SELECT interval '6' second; SELECT interval '1' year TO month; SELECT interval '1-2' year TO month; SELECT interval '1 2' day TO hour; SELECT interval '1 2:03' day TO hour; SELECT interval '1 2:03:04' day TO hour; SELECT interval '1 2' day TO minute; SELECT interval '1 2:03' day TO minute; SELECT interval '1 2:03:04' day TO minute; SELECT interval '1 2' day TO second; SELECT interval '1 2:03' day TO second; SELECT interval '1 2:03:04' day TO second; SELECT interval '1 2' hour TO minute; SELECT interval '1 2:03' hour TO minute; SELECT interval '1 2:03:04' hour TO minute; SELECT interval '1 2' hour TO second; SELECT interval '1 2:03' hour TO second; SELECT interval '1 2:03:04' hour TO second; SELECT interval '1 2' minute TO second; SELECT interval '1 2:03' minute TO second; SELECT interval '1 2:03:04' minute TO second; SELECT interval '1 +2:03' minute TO second; SELECT interval '1 +2:03:04' minute TO second; SELECT interval '1 -2:03' minute TO second; SELECT interval '1 -2:03:04' minute TO second; SELECT interval '123 11' day TO hour; -- ok SELECT interval '123 11' day; -- not ok SELECT interval '123 11'; -- not ok, too ambiguous SELECT interval '123 2:03 -2:04'; -- not ok, redundant hh:mm fields -- test syntaxes for restricted precision SELECT interval(0) '1 day 01:23:45.6789'; SELECT interval(2) '1 day 01:23:45.6789'; SELECT interval '12:34.5678' minute TO second (2); -- per SQL spec SELECT interval '1.234' second; SELECT interval '1.234' second (2); SELECT interval '1 2.345' day TO second (2); SELECT interval '1 2:03' day TO second (2); SELECT interval '1 2:03.4567' day TO second (2); SELECT interval '1 2:03:04.5678' day TO second (2); SELECT interval '1 2.345' hour TO second (2); SELECT interval '1 2:03.45678' hour TO second (2); SELECT interval '1 2:03:04.5678' hour TO second (2); SELECT interval '1 2.3456' minute TO second (2); SELECT interval '1 2:03.5678' minute TO second (2); SELECT interval '1 2:03:04.5678' minute TO second (2); -- test casting to restricted precision (bug #14479) SELECT f1, f1::INTERVAL DAY TO MINUTE AS "minutes", (f1 + interval '1 month')::INTERVAL MONTH::INTERVAL YEAR AS "years" FROM interval_tbl; -- test inputting and outputting SQL standard interval literals SET IntervalStyle TO sql_standard; SELECT interval '0' AS "zero", interval '1-2' year TO month AS "year-month", interval '1 2:03:04' day TO second AS "day-time", - interval '1-2' AS "negative year-month", - interval '1 2:03:04' AS "negative day-time"; -- test input of some not-quite-standard interval values in the sql style SET IntervalStyle TO postgres; SELECT interval '+1 -1:00:00', interval '-1 +1:00:00', interval '+1-2 -3 +4:05:06.789', interval '-1-2 +3 -4:05:06.789'; -- test output of couple non-standard interval values in the sql style SET IntervalStyle TO sql_standard; SELECT interval '1 day -1 hours', interval '-1 days +1 hours', interval '1 years 2 months -3 days 4 hours 5 minutes 6.789 seconds', - interval '1 years 2 months -3 days 4 hours 5 minutes 6.789 seconds'; -- test outputting iso8601 intervals SET IntervalStyle TO iso_8601; SELECT interval '0' AS "zero", interval '1-2' AS "a year 2 months", interval '1 2:03:04' AS "a bit over a day", interval '2:03:04.45679' AS "a bit over 2 hours", (interval '1-2' + interval '3 4:05:06.7') AS "all fields", (interval '1-2' - interval '3 4:05:06.7') AS "mixed sign", (- interval '1-2' + interval '3 4:05:06.7') AS "negative"; -- test inputting ISO 8601 4.4.2.1 "Format With Time Unit Designators" SET IntervalStyle TO sql_standard; SELECT interval 'P0Y' AS "zero", interval 'P1Y2M' AS "a year 2 months", interval 'P1W' AS "a week", interval 'P1DT2H3M4S' AS "a bit over a day", interval 'P1Y2M3DT4H5M6.7S' AS "all fields", interval 'P-1Y-2M-3DT-4H-5M-6.7S' AS "negative", interval 'PT-0.1S' AS "fractional second"; -- test inputting ISO 8601 4.4.2.2 "Alternative Format" SET IntervalStyle TO postgres; SELECT interval 'P00021015T103020' AS "ISO8601 Basic Format", interval 'P0002-10-15T10:30:20' AS "ISO8601 Extended Format"; -- Make sure optional ISO8601 alternative format fields are optional. SELECT interval 'P0002' AS "year only", interval 'P0002-10' AS "year month", interval 'P0002-10-15' AS "year month day", interval 'P0002T1S' AS "year only plus time", interval 'P0002-10T1S' AS "year month plus time", interval 'P0002-10-15T1S' AS "year month day plus time", interval 'PT10' AS "hour only", interval 'PT10:30' AS "hour minute"; -- test a couple rounding cases that changed since 8.3 w/ HAVE_INT64_TIMESTAMP. SET IntervalStyle TO postgres_verbose; SELECT interval '-10 mons -3 days +03:55:06.70'; SELECT interval '1 year 2 mons 3 days 04:05:06.699999'; SELECT interval '0:0:0.7', interval '@ 0.70 secs', interval '0.7 seconds'; -- check that '30 days' equals '1 month' according to the hash function SELECT '30 days'::interval = '1 month'::interval AS t; SELECT interval_hash('30 days'::interval) = interval_hash('1 month'::interval) AS t; -- numeric constructor SELECT make_interval(years := 2); SELECT make_interval(years := 1, months := 6); SELECT make_interval(years := 1, months := - 1, weeks := 5, days := - 7, hours := 25, mins := - 180); SELECT make_interval() = make_interval(years := 0, months := 0, weeks := 0, days := 0, mins := 0, secs := 0.0); SELECT make_interval(hours := - 2, mins := - 10, secs := - 25.3); SELECT make_interval(years := 'inf'::float::int); SELECT make_interval(months := 'NaN'::float::int); SELECT make_interval(secs := 'inf'); SELECT make_interval(secs := 'NaN'); SELECT make_interval(secs := 7e12); pgFormatter-4.2/t/pg-test-files/expected/join.sql000066400000000000000000002354601361326045100220730ustar00rootroot00000000000000-- -- JOIN -- Test JOIN clauses -- CREATE TABLE J1_TBL ( i integer, j integer, t text ); CREATE TABLE J2_TBL ( i integer, k integer ); INSERT INTO J1_TBL VALUES (1, 4, 'one'); INSERT INTO J1_TBL VALUES (2, 3, 'two'); INSERT INTO J1_TBL VALUES (3, 2, 'three'); INSERT INTO J1_TBL VALUES (4, 1, 'four'); INSERT INTO J1_TBL VALUES (5, 0, 'five'); INSERT INTO J1_TBL VALUES (6, 6, 'six'); INSERT INTO J1_TBL VALUES (7, 7, 'seven'); INSERT INTO J1_TBL VALUES (8, 8, 'eight'); INSERT INTO J1_TBL VALUES (0, NULL, 'zero'); INSERT INTO J1_TBL VALUES (NULL, NULL, 'null'); INSERT INTO J1_TBL VALUES (NULL, 0, 'zero'); INSERT INTO J2_TBL VALUES (1, - 1); INSERT INTO J2_TBL VALUES (2, 2); INSERT INTO J2_TBL VALUES (3, - 3); INSERT INTO J2_TBL VALUES (2, 4); INSERT INTO J2_TBL VALUES (5, - 5); INSERT INTO J2_TBL VALUES (5, - 5); INSERT INTO J2_TBL VALUES (0, NULL); INSERT INTO J2_TBL VALUES (NULL, NULL); INSERT INTO J2_TBL VALUES (NULL, 0); -- useful in some tests below CREATE temp TABLE onerow (); INSERT INTO onerow DEFAULT VALUES; ANALYZE onerow; -- -- CORRELATION NAMES -- Make sure that table/column aliases are supported -- before diving into more complex join syntax. -- SELECT '' AS "xxx", * FROM J1_TBL AS tx; SELECT '' AS "xxx", * FROM J1_TBL tx; SELECT '' AS "xxx", * FROM J1_TBL AS t1 (a, b, c); SELECT '' AS "xxx", * FROM J1_TBL t1 (a, b, c); SELECT '' AS "xxx", * FROM J1_TBL t1 (a, b, c), J2_TBL t2 (d, e); SELECT '' AS "xxx", t1.a, t2.e FROM J1_TBL t1 (a, b, c), J2_TBL t2 (d, e) WHERE t1.a = t2.d; -- -- CROSS JOIN -- Qualifications are not allowed on cross joins, -- which degenerate into a standard unqualified inner join. -- SELECT '' AS "xxx", * FROM J1_TBL CROSS JOIN J2_TBL; -- ambiguous column SELECT '' AS "xxx", i, k, t FROM J1_TBL CROSS JOIN J2_TBL; -- resolve previous ambiguity by specifying the table name SELECT '' AS "xxx", t1.i, k, t FROM J1_TBL t1 CROSS JOIN J2_TBL t2; SELECT '' AS "xxx", ii, tt, kk FROM (J1_TBL CROSS JOIN J2_TBL) AS tx (ii, jj, tt, ii2, kk); SELECT '' AS "xxx", tx.ii, tx.jj, tx.kk FROM (J1_TBL t1 (a, b, c) CROSS JOIN J2_TBL t2 (d, e)) AS tx (ii, jj, tt, ii2, kk); SELECT '' AS "xxx", * FROM J1_TBL CROSS JOIN J2_TBL a CROSS JOIN J2_TBL b; -- -- -- Inner joins (equi-joins) -- -- -- -- Inner joins (equi-joins) with USING clause -- The USING syntax changes the shape of the resulting table -- by including a column in the USING clause only once in the result. -- -- Inner equi-join on specified column SELECT '' AS "xxx", * FROM J1_TBL INNER JOIN J2_TBL USING (i); -- Same as above, slightly different syntax SELECT '' AS "xxx", * FROM J1_TBL JOIN J2_TBL USING (i); SELECT '' AS "xxx", * FROM J1_TBL t1 (a, b, c) JOIN J2_TBL t2 (a, d) USING (a) ORDER BY a, d; SELECT '' AS "xxx", * FROM J1_TBL t1 (a, b, c) JOIN J2_TBL t2 (a, b) USING (b) ORDER BY b, t1.a; -- -- NATURAL JOIN -- Inner equi-join on all columns with the same name -- SELECT '' AS "xxx", * FROM J1_TBL NATURAL JOIN J2_TBL; SELECT '' AS "xxx", * FROM J1_TBL t1 (a, b, c) NATURAL JOIN J2_TBL t2 (a, d); SELECT '' AS "xxx", * FROM J1_TBL t1 (a, b, c) NATURAL JOIN J2_TBL t2 (d, a); -- mismatch number of columns -- currently, Postgres will fill in with underlying names SELECT '' AS "xxx", * FROM J1_TBL t1 (a, b) NATURAL JOIN J2_TBL t2 (a); -- -- Inner joins (equi-joins) -- SELECT '' AS "xxx", * FROM J1_TBL JOIN J2_TBL ON (J1_TBL.i = J2_TBL.i); SELECT '' AS "xxx", * FROM J1_TBL JOIN J2_TBL ON (J1_TBL.i = J2_TBL.k); -- -- Non-equi-joins -- SELECT '' AS "xxx", * FROM J1_TBL JOIN J2_TBL ON (J1_TBL.i <= J2_TBL.k); -- -- Outer joins -- Note that OUTER is a noise word -- SELECT '' AS "xxx", * FROM J1_TBL LEFT OUTER JOIN J2_TBL USING (i) ORDER BY i, k, t; SELECT '' AS "xxx", * FROM J1_TBL LEFT JOIN J2_TBL USING (i) ORDER BY i, k, t; SELECT '' AS "xxx", * FROM J1_TBL RIGHT OUTER JOIN J2_TBL USING (i); SELECT '' AS "xxx", * FROM J1_TBL RIGHT JOIN J2_TBL USING (i); SELECT '' AS "xxx", * FROM J1_TBL FULL OUTER JOIN J2_TBL USING (i) ORDER BY i, k, t; SELECT '' AS "xxx", * FROM J1_TBL FULL JOIN J2_TBL USING (i) ORDER BY i, k, t; SELECT '' AS "xxx", * FROM J1_TBL LEFT JOIN J2_TBL USING (i) WHERE (k = 1); SELECT '' AS "xxx", * FROM J1_TBL LEFT JOIN J2_TBL USING (i) WHERE (i = 1); -- -- semijoin selectivity for <> -- EXPLAIN ( COSTS OFF ) SELECT * FROM int4_tbl i4, tenk1 a WHERE EXISTS ( SELECT * FROM tenk1 b WHERE a.twothousand = b.twothousand AND a.fivethous <> b.fivethous) AND i4.f1 = a.tenthous; -- -- More complicated constructs -- -- -- Multiway full join -- CREATE TABLE t1 ( name text, n integer ); CREATE TABLE t2 ( name text, n integer ); CREATE TABLE t3 ( name text, n integer ); INSERT INTO t1 VALUES ('bb', 11); INSERT INTO t2 VALUES ('bb', 12); INSERT INTO t2 VALUES ('cc', 22); INSERT INTO t2 VALUES ('ee', 42); INSERT INTO t3 VALUES ('bb', 13); INSERT INTO t3 VALUES ('cc', 23); INSERT INTO t3 VALUES ('dd', 33); SELECT * FROM t1 FULL JOIN t2 USING (name) FULL JOIN t3 USING (name); -- -- Test interactions of join syntax and subqueries -- -- Basic cases (we expect planner to pull up the subquery here) SELECT * FROM ( SELECT * FROM t2) AS s2 INNER JOIN ( SELECT * FROM t3) s3 USING (name); SELECT * FROM ( SELECT * FROM t2) AS s2 LEFT JOIN ( SELECT * FROM t3) s3 USING (name); SELECT * FROM ( SELECT * FROM t2) AS s2 FULL JOIN ( SELECT * FROM t3) s3 USING (name); -- Cases with non-nullable expressions in subquery results; -- make sure these go to null as expected SELECT * FROM ( SELECT name, n AS s2_n, 2 AS s2_2 FROM t2) AS s2 NATURAL INNER JOIN ( SELECT name, n AS s3_n, 3 AS s3_2 FROM t3) s3; SELECT * FROM ( SELECT name, n AS s2_n, 2 AS s2_2 FROM t2) AS s2 NATURAL LEFT JOIN ( SELECT name, n AS s3_n, 3 AS s3_2 FROM t3) s3; SELECT * FROM ( SELECT name, n AS s2_n, 2 AS s2_2 FROM t2) AS s2 NATURAL FULL JOIN ( SELECT name, n AS s3_n, 3 AS s3_2 FROM t3) s3; SELECT * FROM ( SELECT name, n AS s1_n, 1 AS s1_1 FROM t1) AS s1 NATURAL INNER JOIN ( SELECT name, n AS s2_n, 2 AS s2_2 FROM t2) AS s2 NATURAL INNER JOIN ( SELECT name, n AS s3_n, 3 AS s3_2 FROM t3) s3; SELECT * FROM ( SELECT name, n AS s1_n, 1 AS s1_1 FROM t1) AS s1 NATURAL FULL JOIN ( SELECT name, n AS s2_n, 2 AS s2_2 FROM t2) AS s2 NATURAL FULL JOIN ( SELECT name, n AS s3_n, 3 AS s3_2 FROM t3) s3; SELECT * FROM ( SELECT name, n AS s1_n FROM t1) AS s1 NATURAL FULL JOIN ( SELECT * FROM ( SELECT name, n AS s2_n FROM t2) AS s2 NATURAL FULL JOIN ( SELECT name, n AS s3_n FROM t3) AS s3) ss2; SELECT * FROM ( SELECT name, n AS s1_n FROM t1) AS s1 NATURAL FULL JOIN ( SELECT * FROM ( SELECT name, n AS s2_n, 2 AS s2_2 FROM t2) AS s2 NATURAL FULL JOIN ( SELECT name, n AS s3_n FROM t3) AS s3) ss2; -- Constants as join keys can also be problematic SELECT * FROM ( SELECT name, n AS s1_n FROM t1) AS s1 FULL JOIN ( SELECT name, 2 AS s2_n FROM t2) AS s2 ON (s1_n = s2_n); -- Test for propagation of nullability constraints into sub-joins CREATE temp TABLE x ( x1 int, x2 int ); INSERT INTO x VALUES (1, 11); INSERT INTO x VALUES (2, 22); INSERT INTO x VALUES (3, NULL); INSERT INTO x VALUES (4, 44); INSERT INTO x VALUES (5, NULL); CREATE temp TABLE y ( y1 int, y2 int ); INSERT INTO y VALUES (1, 111); INSERT INTO y VALUES (2, 222); INSERT INTO y VALUES (3, 333); INSERT INTO y VALUES (4, NULL); SELECT * FROM x; SELECT * FROM y; SELECT * FROM x LEFT JOIN y ON (x1 = y1 AND x2 IS NOT NULL); SELECT * FROM x LEFT JOIN y ON (x1 = y1 AND y2 IS NOT NULL); SELECT * FROM (x LEFT JOIN y ON (x1 = y1)) LEFT JOIN x xx (xx1, xx2) ON (x1 = xx1); SELECT * FROM (x LEFT JOIN y ON (x1 = y1)) LEFT JOIN x xx (xx1, xx2) ON (x1 = xx1 AND x2 IS NOT NULL); SELECT * FROM (x LEFT JOIN y ON (x1 = y1)) LEFT JOIN x xx (xx1, xx2) ON (x1 = xx1 AND y2 IS NOT NULL); SELECT * FROM (x LEFT JOIN y ON (x1 = y1)) LEFT JOIN x xx (xx1, xx2) ON (x1 = xx1 AND xx2 IS NOT NULL); -- these should NOT give the same answers as above SELECT * FROM (x LEFT JOIN y ON (x1 = y1)) LEFT JOIN x xx (xx1, xx2) ON (x1 = xx1) WHERE (x2 IS NOT NULL); SELECT * FROM (x LEFT JOIN y ON (x1 = y1)) LEFT JOIN x xx (xx1, xx2) ON (x1 = xx1) WHERE (y2 IS NOT NULL); SELECT * FROM (x LEFT JOIN y ON (x1 = y1)) LEFT JOIN x xx (xx1, xx2) ON (x1 = xx1) WHERE (xx2 IS NOT NULL); -- -- regression test: check for bug with propagation of implied equality -- to outside an IN -- SELECT count(*) FROM tenk1 a WHERE unique1 IN ( SELECT unique1 FROM tenk1 b JOIN tenk1 c USING (unique1) WHERE b.unique2 = 42); -- -- regression test: check for failure to generate a plan with multiple -- degenerate IN clauses -- SELECT count(*) FROM tenk1 x WHERE x.unique1 IN ( SELECT a.f1 FROM int4_tbl a, float8_tbl b WHERE a.f1 = b.f1) AND x.unique1 = 0 AND x.unique1 IN ( SELECT aa.f1 FROM int4_tbl aa, float8_tbl bb WHERE aa.f1 = bb.f1); -- try that with GEQO too BEGIN; SET geqo = ON; SET geqo_threshold = 2; SELECT count(*) FROM tenk1 x WHERE x.unique1 IN ( SELECT a.f1 FROM int4_tbl a, float8_tbl b WHERE a.f1 = b.f1) AND x.unique1 = 0 AND x.unique1 IN ( SELECT aa.f1 FROM int4_tbl aa, float8_tbl bb WHERE aa.f1 = bb.f1); ROLLBACK; -- -- regression test: be sure we cope with proven-dummy append rels -- EXPLAIN ( COSTS OFF ) SELECT aa, bb, unique1, unique1 FROM tenk1 RIGHT JOIN b ON aa = unique1 WHERE bb < bb AND bb IS NULL; SELECT aa, bb, unique1, unique1 FROM tenk1 RIGHT JOIN b ON aa = unique1 WHERE bb < bb AND bb IS NULL; -- -- regression test: check handling of empty-FROM subquery underneath outer join -- EXPLAIN ( COSTS OFF ) SELECT * FROM int8_tbl i1 LEFT JOIN (int8_tbl i2 JOIN ( SELECT 123 AS x) ss ON i2.q1 = x) ON i1.q2 = i2.q2 ORDER BY 1, 2; SELECT * FROM int8_tbl i1 LEFT JOIN (int8_tbl i2 JOIN ( SELECT 123 AS x) ss ON i2.q1 = x) ON i1.q2 = i2.q2 ORDER BY 1, 2; -- -- regression test: check a case where join_clause_is_movable_into() gives -- an imprecise result, causing an assertion failure -- SELECT count(*) FROM ( SELECT t3.tenthous AS x1, coalesce(t1.stringu1, t2.stringu1) AS x2 FROM tenk1 t1 LEFT JOIN tenk1 t2 ON t1.unique1 = t2.unique1 JOIN tenk1 t3 ON t1.unique2 = t3.unique2) ss, tenk1 t4, tenk1 t5 WHERE t4.thousand = t5.unique1 AND ss.x1 = t4.tenthous AND ss.x2 = t5.stringu1; -- -- regression test: check a case where we formerly missed including an EC -- enforcement clause because it was expected to be handled at scan level -- EXPLAIN ( COSTS OFF ) SELECT a.f1, b.f1, t.thousand, t.tenthous FROM tenk1 t, ( SELECT sum(f1) + 1 AS f1 FROM int4_tbl i4a) a, ( SELECT sum(f1) AS f1 FROM int4_tbl i4b) b WHERE b.f1 = t.thousand AND a.f1 = b.f1 AND (a.f1 + b.f1 + 999) = t.tenthous; SELECT a.f1, b.f1, t.thousand, t.tenthous FROM tenk1 t, ( SELECT sum(f1) + 1 AS f1 FROM int4_tbl i4a) a, ( SELECT sum(f1) AS f1 FROM int4_tbl i4b) b WHERE b.f1 = t.thousand AND a.f1 = b.f1 AND (a.f1 + b.f1 + 999) = t.tenthous; -- -- check a case where we formerly got confused by conflicting sort orders -- in redundant merge join path keys -- EXPLAIN ( COSTS OFF ) SELECT * FROM j1_tbl FULL JOIN ( SELECT * FROM j2_tbl ORDER BY j2_tbl.i DESC, j2_tbl.k ASC) j2_tbl ON j1_tbl.i = j2_tbl.i AND j1_tbl.i = j2_tbl.k; SELECT * FROM j1_tbl FULL JOIN ( SELECT * FROM j2_tbl ORDER BY j2_tbl.i DESC, j2_tbl.k ASC) j2_tbl ON j1_tbl.i = j2_tbl.i AND j1_tbl.i = j2_tbl.k; -- -- a different check for handling of redundant sort keys in merge joins -- EXPLAIN ( COSTS OFF ) SELECT count(*) FROM ( SELECT * FROM tenk1 x ORDER BY x.thousand, x.twothousand, x.fivethous) x LEFT JOIN ( SELECT * FROM tenk1 y ORDER BY y.unique2) y ON x.thousand = y.unique2 AND x.twothousand = y.hundred AND x.fivethous = y.unique2; SELECT count(*) FROM ( SELECT * FROM tenk1 x ORDER BY x.thousand, x.twothousand, x.fivethous) x LEFT JOIN ( SELECT * FROM tenk1 y ORDER BY y.unique2) y ON x.thousand = y.unique2 AND x.twothousand = y.hundred AND x.fivethous = y.unique2; -- -- Clean up -- DROP TABLE t1; DROP TABLE t2; DROP TABLE t3; DROP TABLE J1_TBL; DROP TABLE J2_TBL; -- Both DELETE and UPDATE allow the specification of additional tables -- to "join" against to determine which rows should be modified. CREATE TEMP TABLE t1 ( a int, b int ); CREATE TEMP TABLE t2 ( a int, b int ); CREATE TEMP TABLE t3 ( x int, y int ); INSERT INTO t1 VALUES (5, 10); INSERT INTO t1 VALUES (15, 20); INSERT INTO t1 VALUES (100, 100); INSERT INTO t1 VALUES (200, 1000); INSERT INTO t2 VALUES (200, 2000); INSERT INTO t3 VALUES (5, 20); INSERT INTO t3 VALUES (6, 7); INSERT INTO t3 VALUES (7, 8); INSERT INTO t3 VALUES (500, 100); DELETE FROM t3 USING t1 table1 WHERE t3.x = table1.a; SELECT * FROM t3; DELETE FROM t3 USING t1 JOIN t2 USING (a) WHERE t3.x > t1.a; SELECT * FROM t3; DELETE FROM t3 USING t3 t3_other WHERE t3.x = t3_other.x AND t3.y = t3_other.y; SELECT * FROM t3; -- Test join against inheritance tree CREATE temp TABLE t2a () INHERITS ( t2 ); INSERT INTO t2a VALUES (200, 2001); SELECT * FROM t1 LEFT JOIN t2 ON (t1.a = t2.a); -- Test matching of column name with wrong alias SELECT t1.x FROM t1 JOIN t3 ON (t1.a = t3.x); -- -- regression test for 8.1 merge right join bug -- CREATE TEMP TABLE tt1 ( tt1_id int4, joincol int4 ); INSERT INTO tt1 VALUES (1, 11); INSERT INTO tt1 VALUES (2, NULL); CREATE TEMP TABLE tt2 ( tt2_id int4, joincol int4 ); INSERT INTO tt2 VALUES (21, 11); INSERT INTO tt2 VALUES (22, 11); SET enable_hashjoin TO OFF; SET enable_nestloop TO OFF; -- these should give the same results SELECT tt1.*, tt2.* FROM tt1 LEFT JOIN tt2 ON tt1.joincol = tt2.joincol; SELECT tt1.*, tt2.* FROM tt2 RIGHT JOIN tt1 ON tt1.joincol = tt2.joincol; RESET enable_hashjoin; RESET enable_nestloop; -- -- regression test for bug #13908 (hash join with skew tuples & nbatch increase) -- SET work_mem TO '64kB'; SET enable_mergejoin TO OFF; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1 a, tenk1 b WHERE a.hundred = b.thousand AND (b.fivethous % 10) < 10; SELECT count(*) FROM tenk1 a, tenk1 b WHERE a.hundred = b.thousand AND (b.fivethous % 10) < 10; RESET work_mem; RESET enable_mergejoin; -- -- regression test for 8.2 bug with improper re-ordering of left joins -- CREATE temp TABLE tt3 ( f1 int, f2 text ); INSERT INTO tt3 SELECT x, repeat('xyzzy', 100) FROM generate_series(1, 10000) x; CREATE INDEX tt3i ON tt3 (f1); ANALYZE tt3; CREATE temp TABLE tt4 ( f1 int ); INSERT INTO tt4 VALUES (0), (1), (9999); ANALYZE tt4; SELECT a.f1 FROM tt4 a LEFT JOIN ( SELECT b.f1 FROM tt3 b LEFT JOIN tt3 c ON (b.f1 = c.f1) WHERE c.f1 IS NULL) AS d ON (a.f1 = d.f1) WHERE d.f1 IS NULL; -- -- regression test for proper handling of outer joins within antijoins -- CREATE temp TABLE tt4x ( c1 int, c2 int, c3 int ); EXPLAIN ( COSTS OFF ) SELECT * FROM tt4x t1 WHERE NOT EXISTS ( SELECT 1 FROM tt4x t2 LEFT JOIN tt4x t3 ON t2.c3 = t3.c1 LEFT JOIN ( SELECT t5.c1 AS c1 FROM tt4x t4 LEFT JOIN tt4x t5 ON t4.c2 = t5.c1) a1 ON t3.c2 = a1.c1 WHERE t1.c1 = t2.c2); -- -- regression test for problems of the sort depicted in bug #3494 -- CREATE temp TABLE tt5 ( f1 int, f2 int ); CREATE temp TABLE tt6 ( f1 int, f2 int ); INSERT INTO tt5 VALUES (1, 10); INSERT INTO tt5 VALUES (1, 11); INSERT INTO tt6 VALUES (1, 9); INSERT INTO tt6 VALUES (1, 2); INSERT INTO tt6 VALUES (2, 9); SELECT * FROM tt5, tt6 WHERE tt5.f1 = tt6.f1 AND tt5.f1 = tt5.f2 - tt6.f2; -- -- regression test for problems of the sort depicted in bug #3588 -- CREATE temp TABLE xx ( pkxx int ); CREATE temp TABLE yy ( pkyy int, pkxx int ); INSERT INTO xx VALUES (1); INSERT INTO xx VALUES (2); INSERT INTO xx VALUES (3); INSERT INTO yy VALUES (101, 1); INSERT INTO yy VALUES (201, 2); INSERT INTO yy VALUES (301, NULL); SELECT yy.pkyy AS yy_pkyy, yy.pkxx AS yy_pkxx, yya.pkyy AS yya_pkyy, xxa.pkxx AS xxa_pkxx, xxb.pkxx AS xxb_pkxx FROM yy LEFT JOIN ( SELECT * FROM yy WHERE pkyy = 101) AS yya ON yy.pkyy = yya.pkyy LEFT JOIN xx xxa ON yya.pkxx = xxa.pkxx LEFT JOIN xx xxb ON coalesce(xxa.pkxx, 1) = xxb.pkxx; -- -- regression test for improper pushing of constants across outer-join clauses -- (as seen in early 8.2.x releases) -- CREATE temp TABLE zt1 ( f1 int PRIMARY KEY ); CREATE temp TABLE zt2 ( f2 int PRIMARY KEY ); CREATE temp TABLE zt3 ( f3 int PRIMARY KEY ); INSERT INTO zt1 VALUES (53); INSERT INTO zt2 VALUES (53); SELECT * FROM zt2 LEFT JOIN zt3 ON (f2 = f3) LEFT JOIN zt1 ON (f3 = f1) WHERE f2 = 53; CREATE temp VIEW zv1 AS SELECT *, 'dummy'::text AS junk FROM zt1; SELECT * FROM zt2 LEFT JOIN zt3 ON (f2 = f3) LEFT JOIN zv1 ON (f3 = f1) WHERE f2 = 53; -- -- regression test for improper extraction of OR indexqual conditions -- (as seen in early 8.3.x releases) -- SELECT a.unique2, a.ten, b.tenthous, b.unique2, b.hundred FROM tenk1 a LEFT JOIN tenk1 b ON a.unique2 = b.tenthous WHERE a.unique1 = 42 AND ((b.unique2 IS NULL AND a.ten = 2) OR b.hundred = 3); -- -- test proper positioning of one-time quals in EXISTS (8.4devel bug) -- PREPARE foo (bool) AS SELECT count(*) FROM tenk1 a LEFT JOIN tenk1 b ON (a.unique2 = b.unique1 AND EXISTS ( SELECT 1 FROM tenk1 c WHERE c.thousand = b.unique2 AND $1)); EXECUTE foo (TRUE); EXECUTE foo (FALSE); -- -- test for sane behavior with noncanonical merge clauses, per bug #4926 -- BEGIN; SET enable_mergejoin = 1; SET enable_hashjoin = 0; SET enable_nestloop = 0; CREATE temp TABLE a ( i integer ); CREATE temp TABLE b ( x integer, y integer ); SELECT * FROM a LEFT JOIN b ON i = x AND i = y AND x = i; ROLLBACK; -- -- test handling of merge clauses using record_ops -- BEGIN; CREATE TYPE mycomptype AS ( id int, v bigint ); CREATE temp TABLE tidv ( idv mycomptype ); CREATE INDEX ON tidv (idv); EXPLAIN ( COSTS OFF ) SELECT a.idv, b.idv FROM tidv a, tidv b WHERE a.idv = b.idv; SET enable_mergejoin = 0; EXPLAIN ( COSTS OFF ) SELECT a.idv, b.idv FROM tidv a, tidv b WHERE a.idv = b.idv; ROLLBACK; -- -- test NULL behavior of whole-row Vars, per bug #5025 -- SELECT t1.q2, count(t2.*) FROM int8_tbl t1 LEFT JOIN int8_tbl t2 ON (t1.q2 = t2.q1) GROUP BY t1.q2 ORDER BY 1; SELECT t1.q2, count(t2.*) FROM int8_tbl t1 LEFT JOIN ( SELECT * FROM int8_tbl) t2 ON (t1.q2 = t2.q1) GROUP BY t1.q2 ORDER BY 1; SELECT t1.q2, count(t2.*) FROM int8_tbl t1 LEFT JOIN ( SELECT * FROM int8_tbl offset 0) t2 ON (t1.q2 = t2.q1) GROUP BY t1.q2 ORDER BY 1; SELECT t1.q2, count(t2.*) FROM int8_tbl t1 LEFT JOIN ( SELECT q1, CASE WHEN q2 = 1 THEN 1 ELSE q2 END AS q2 FROM int8_tbl) t2 ON (t1.q2 = t2.q1) GROUP BY t1.q2 ORDER BY 1; -- -- test incorrect failure to NULL pulled-up subexpressions -- BEGIN; CREATE temp TABLE a ( code char NOT NULL, CONSTRAINT a_pk PRIMARY KEY (code) ); CREATE temp TABLE b ( a char NOT NULL, num integer NOT NULL, CONSTRAINT b_pk PRIMARY KEY (a, num) ); CREATE temp TABLE c ( name char NOT NULL, a char, CONSTRAINT c_pk PRIMARY KEY (name) ); INSERT INTO a (code) VALUES ('p'); INSERT INTO a (code) VALUES ('q'); INSERT INTO b (a, num) VALUES ('p', 1); INSERT INTO b (a, num) VALUES ('p', 2); INSERT INTO c (name, a) VALUES ('A', 'p'); INSERT INTO c (name, a) VALUES ('B', 'q'); INSERT INTO c (name, a) VALUES ('C', NULL); SELECT c.name, ss.code, ss.b_cnt, ss.const FROM c LEFT JOIN ( SELECT a.code, coalesce(b_grp.cnt, 0) AS b_cnt, - 1 AS const FROM a LEFT JOIN ( SELECT count(1) AS cnt, b.a FROM b GROUP BY b.a) AS b_grp ON a.code = b_grp.a) AS ss ON (c.a = ss.code) ORDER BY c.name; ROLLBACK; -- -- test incorrect handling of placeholders that only appear in targetlists, -- per bug #6154 -- SELECT * FROM ( SELECT 1 AS key1) sub1 LEFT JOIN ( SELECT sub3.key3, sub4.value2, COALESCE(sub4.value2, 66) AS value3 FROM ( SELECT 1 AS key3) sub3 LEFT JOIN ( SELECT sub5.key5, COALESCE(sub6.value1, 1) AS value2 FROM ( SELECT 1 AS key5) sub5 LEFT JOIN ( SELECT 2 AS key6, 42 AS value1) sub6 ON sub5.key5 = sub6.key6) sub4 ON sub4.key5 = sub3.key3) sub2 ON sub1.key1 = sub2.key3; -- test the path using join aliases, too SELECT * FROM ( SELECT 1 AS key1) sub1 LEFT JOIN ( SELECT sub3.key3, value2, COALESCE(value2, 66) AS value3 FROM ( SELECT 1 AS key3) sub3 LEFT JOIN ( SELECT sub5.key5, COALESCE(sub6.value1, 1) AS value2 FROM ( SELECT 1 AS key5) sub5 LEFT JOIN ( SELECT 2 AS key6, 42 AS value1) sub6 ON sub5.key5 = sub6.key6) sub4 ON sub4.key5 = sub3.key3) sub2 ON sub1.key1 = sub2.key3; -- -- test case where a PlaceHolderVar is used as a nestloop parameter -- EXPLAIN ( COSTS OFF ) SELECT qq, unique1 FROM ( SELECT COALESCE(q1, 0) AS qq FROM int8_tbl a) AS ss1 FULL OUTER JOIN ( SELECT COALESCE(q2, - 1) AS qq FROM int8_tbl b) AS ss2 USING (qq) INNER JOIN tenk1 c ON qq = unique2; SELECT qq, unique1 FROM ( SELECT COALESCE(q1, 0) AS qq FROM int8_tbl a) AS ss1 FULL OUTER JOIN ( SELECT COALESCE(q2, - 1) AS qq FROM int8_tbl b) AS ss2 USING (qq) INNER JOIN tenk1 c ON qq = unique2; -- -- nested nestloops can require nested PlaceHolderVars -- CREATE temp TABLE nt1 ( id int PRIMARY KEY, a1 boolean, a2 boolean ); CREATE temp TABLE nt2 ( id int PRIMARY KEY, nt1_id int, b1 boolean, b2 boolean, FOREIGN KEY (nt1_id) REFERENCES nt1 (id) ); CREATE temp TABLE nt3 ( id int PRIMARY KEY, nt2_id int, c1 boolean, FOREIGN KEY (nt2_id) REFERENCES nt2 (id) ); INSERT INTO nt1 VALUES (1, TRUE, TRUE); INSERT INTO nt1 VALUES (2, TRUE, FALSE); INSERT INTO nt1 VALUES (3, FALSE, FALSE); INSERT INTO nt2 VALUES (1, 1, TRUE, TRUE); INSERT INTO nt2 VALUES (2, 2, TRUE, FALSE); INSERT INTO nt2 VALUES (3, 3, FALSE, FALSE); INSERT INTO nt3 VALUES (1, 1, TRUE); INSERT INTO nt3 VALUES (2, 2, FALSE); INSERT INTO nt3 VALUES (3, 3, TRUE); EXPLAIN ( COSTS OFF ) SELECT nt3.id FROM nt3 AS nt3 LEFT JOIN ( SELECT nt2.*, (nt2.b1 AND ss1.a3) AS b3 FROM nt2 AS nt2 LEFT JOIN ( SELECT nt1.*, (nt1.id IS NOT NULL) AS a3 FROM nt1) AS ss1 ON ss1.id = nt2.nt1_id) AS ss2 ON ss2.id = nt3.nt2_id WHERE nt3.id = 1 AND ss2.b3; SELECT nt3.id FROM nt3 AS nt3 LEFT JOIN ( SELECT nt2.*, (nt2.b1 AND ss1.a3) AS b3 FROM nt2 AS nt2 LEFT JOIN ( SELECT nt1.*, (nt1.id IS NOT NULL) AS a3 FROM nt1) AS ss1 ON ss1.id = nt2.nt1_id) AS ss2 ON ss2.id = nt3.nt2_id WHERE nt3.id = 1 AND ss2.b3; -- -- test case where a PlaceHolderVar is propagated into a subquery -- EXPLAIN ( COSTS OFF ) SELECT * FROM int8_tbl t1 LEFT JOIN ( SELECT q1 AS x, 42 AS y FROM int8_tbl t2) ss ON t1.q2 = ss.x WHERE 1 = ( SELECT 1 FROM int8_tbl t3 WHERE ss.y IS NOT NULL LIMIT 1) ORDER BY 1, 2; SELECT * FROM int8_tbl t1 LEFT JOIN ( SELECT q1 AS x, 42 AS y FROM int8_tbl t2) ss ON t1.q2 = ss.x WHERE 1 = ( SELECT 1 FROM int8_tbl t3 WHERE ss.y IS NOT NULL LIMIT 1) ORDER BY 1, 2; -- -- test the corner cases FULL JOIN ON TRUE and FULL JOIN ON FALSE -- SELECT * FROM int4_tbl a FULL JOIN int4_tbl b ON TRUE; SELECT * FROM int4_tbl a FULL JOIN int4_tbl b ON FALSE; -- -- test for ability to use a cartesian join when necessary -- EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1 JOIN int4_tbl ON f1 = twothousand, int4(sin(1)) q1, int4(sin(0)) q2 WHERE q1 = thousand OR q2 = thousand; EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1 JOIN int4_tbl ON f1 = twothousand, int4(sin(1)) q1, int4(sin(0)) q2 WHERE thousand = (q1 + q2); -- -- test ability to generate a suitable plan for a star-schema query -- EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1, int8_tbl a, int8_tbl b WHERE thousand = a.q1 AND tenthous = b.q1 AND a.q2 = 1 AND b.q2 = 2; -- -- test a corner case in which we shouldn't apply the star-schema optimization -- EXPLAIN ( COSTS OFF ) SELECT t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 FROM tenk1 t1 INNER JOIN int4_tbl i1 LEFT JOIN ( SELECT v1.x2, v2.y1, 11 AS d1 FROM ( SELECT 1, 0 FROM onerow) v1 (x1, x2) LEFT JOIN ( SELECT 3, 1 FROM onerow) v2 (y1, y2) ON v1.x1 = v2.y2) subq1 ON (i1.f1 = subq1.x2) ON (t1.unique2 = subq1.d1) LEFT JOIN tenk1 t2 ON (subq1.y1 = t2.unique1) WHERE t1.unique2 < 42 AND t1.stringu1 > t2.stringu2; SELECT t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 FROM tenk1 t1 INNER JOIN int4_tbl i1 LEFT JOIN ( SELECT v1.x2, v2.y1, 11 AS d1 FROM ( SELECT 1, 0 FROM onerow) v1 (x1, x2) LEFT JOIN ( SELECT 3, 1 FROM onerow) v2 (y1, y2) ON v1.x1 = v2.y2) subq1 ON (i1.f1 = subq1.x2) ON (t1.unique2 = subq1.d1) LEFT JOIN tenk1 t2 ON (subq1.y1 = t2.unique1) WHERE t1.unique2 < 42 AND t1.stringu1 > t2.stringu2; -- variant that isn't quite a star-schema case SELECT ss1.d1 FROM tenk1 AS t1 INNER JOIN tenk1 AS t2 ON t1.tenthous = t2.ten INNER JOIN int8_tbl AS i8 LEFT JOIN int4_tbl AS i4 INNER JOIN ( SELECT 64::information_schema.cardinal_number AS d1 FROM tenk1 t3, LATERAL ( SELECT abs(t3.unique1) + random()) ss0 (x) WHERE t3.fivethous < 0) AS ss1 ON i4.f1 = ss1.d1 ON i8.q1 = i4.f1 ON t1.tenthous = ss1.d1 WHERE t1.unique1 < i4.f1; -- this variant is foldable by the remove-useless-RESULT-RTEs code EXPLAIN ( COSTS OFF ) SELECT t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 FROM tenk1 t1 INNER JOIN int4_tbl i1 LEFT JOIN ( SELECT v1.x2, v2.y1, 11 AS d1 FROM ( VALUES (1, 0)) v1 (x1, x2) LEFT JOIN ( VALUES (3, 1)) v2 (y1, y2) ON v1.x1 = v2.y2) subq1 ON (i1.f1 = subq1.x2) ON (t1.unique2 = subq1.d1) LEFT JOIN tenk1 t2 ON (subq1.y1 = t2.unique1) WHERE t1.unique2 < 42 AND t1.stringu1 > t2.stringu2; SELECT t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 FROM tenk1 t1 INNER JOIN int4_tbl i1 LEFT JOIN ( SELECT v1.x2, v2.y1, 11 AS d1 FROM ( VALUES (1, 0)) v1 (x1, x2) LEFT JOIN ( VALUES (3, 1)) v2 (y1, y2) ON v1.x1 = v2.y2) subq1 ON (i1.f1 = subq1.x2) ON (t1.unique2 = subq1.d1) LEFT JOIN tenk1 t2 ON (subq1.y1 = t2.unique1) WHERE t1.unique2 < 42 AND t1.stringu1 > t2.stringu2; -- -- test extraction of restriction OR clauses from join OR clause -- (we used to only do this for indexable clauses) -- EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1 a JOIN tenk1 b ON (a.unique1 = 1 AND b.unique1 = 2) OR (a.unique2 = 3 AND b.hundred = 4); EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1 a JOIN tenk1 b ON (a.unique1 = 1 AND b.unique1 = 2) OR (a.unique2 = 3 AND b.ten = 4); EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1 a JOIN tenk1 b ON (a.unique1 = 1 AND b.unique1 = 2) OR ((a.unique2 = 3 OR a.unique2 = 7) AND b.hundred = 4); -- -- test placement of movable quals in a parameterized join tree -- EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1 t1 LEFT JOIN (tenk1 t2 JOIN tenk1 t3 ON t2.thousand = t3.unique2) ON t1.hundred = t2.hundred AND t1.ten = t3.ten WHERE t1.unique1 = 1; EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1 t1 LEFT JOIN (tenk1 t2 JOIN tenk1 t3 ON t2.thousand = t3.unique2) ON t1.hundred = t2.hundred AND t1.ten + t2.ten = t3.ten WHERE t1.unique1 = 1; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1 a JOIN tenk1 b ON a.unique1 = b.unique2 LEFT JOIN tenk1 c ON a.unique2 = b.unique1 AND c.thousand = a.thousand JOIN int4_tbl ON b.thousand = f1; SELECT count(*) FROM tenk1 a JOIN tenk1 b ON a.unique1 = b.unique2 LEFT JOIN tenk1 c ON a.unique2 = b.unique1 AND c.thousand = a.thousand JOIN int4_tbl ON b.thousand = f1; EXPLAIN ( COSTS OFF ) SELECT b.unique1 FROM tenk1 a JOIN tenk1 b ON a.unique1 = b.unique2 LEFT JOIN tenk1 c ON b.unique1 = 42 AND c.thousand = a.thousand JOIN int4_tbl i1 ON b.thousand = f1 RIGHT JOIN int4_tbl i2 ON i2.f1 = b.tenthous ORDER BY 1; SELECT b.unique1 FROM tenk1 a JOIN tenk1 b ON a.unique1 = b.unique2 LEFT JOIN tenk1 c ON b.unique1 = 42 AND c.thousand = a.thousand JOIN int4_tbl i1 ON b.thousand = f1 RIGHT JOIN int4_tbl i2 ON i2.f1 = b.tenthous ORDER BY 1; EXPLAIN ( COSTS OFF ) SELECT * FROM ( SELECT unique1, q1, coalesce(unique1, - 1) + q1 AS fault FROM int8_tbl LEFT JOIN tenk1 ON (q2 = unique2)) ss WHERE fault = 122 ORDER BY fault; SELECT * FROM ( SELECT unique1, q1, coalesce(unique1, - 1) + q1 AS fault FROM int8_tbl LEFT JOIN tenk1 ON (q2 = unique2)) ss WHERE fault = 122 ORDER BY fault; EXPLAIN ( COSTS OFF ) SELECT * FROM ( VALUES (1, ARRAY[10, 20]), (2, ARRAY[20, 30])) AS v1 (v1x, v1ys) LEFT JOIN ( VALUES (1, 10), (2, 20)) AS v2 (v2x, v2y) ON v2x = v1x LEFT JOIN unnest(v1ys) AS u1 (u1y) ON u1y = v2y; SELECT * FROM ( VALUES (1, ARRAY[10, 20]), (2, ARRAY[20, 30])) AS v1 (v1x, v1ys) LEFT JOIN ( VALUES (1, 10), (2, 20)) AS v2 (v2x, v2y) ON v2x = v1x LEFT JOIN unnest(v1ys) AS u1 (u1y) ON u1y = v2y; -- -- test handling of potential equivalence clauses above outer joins -- EXPLAIN ( COSTS OFF ) SELECT q1, unique2, thousand, hundred FROM int8_tbl a LEFT JOIN tenk1 b ON q1 = unique2 WHERE coalesce(thousand, 123) = q1 AND q1 = coalesce(hundred, 123); SELECT q1, unique2, thousand, hundred FROM int8_tbl a LEFT JOIN tenk1 b ON q1 = unique2 WHERE coalesce(thousand, 123) = q1 AND q1 = coalesce(hundred, 123); EXPLAIN ( COSTS OFF ) SELECT f1, unique2, CASE WHEN unique2 IS NULL THEN f1 ELSE 0 END FROM int4_tbl a LEFT JOIN tenk1 b ON f1 = unique2 WHERE ( CASE WHEN unique2 IS NULL THEN f1 ELSE 0 END) = 0; SELECT f1, unique2, CASE WHEN unique2 IS NULL THEN f1 ELSE 0 END FROM int4_tbl a LEFT JOIN tenk1 b ON f1 = unique2 WHERE ( CASE WHEN unique2 IS NULL THEN f1 ELSE 0 END) = 0; -- -- another case with equivalence clauses above outer joins (bug #8591) -- EXPLAIN ( COSTS OFF ) SELECT a.unique1, b.unique1, c.unique1, coalesce(b.twothousand, a.twothousand) FROM tenk1 a LEFT JOIN tenk1 b ON b.thousand = a.unique1 LEFT JOIN tenk1 c ON c.unique2 = coalesce(b.twothousand, a.twothousand) WHERE a.unique2 < 10 AND coalesce(b.twothousand, a.twothousand) = 44; SELECT a.unique1, b.unique1, c.unique1, coalesce(b.twothousand, a.twothousand) FROM tenk1 a LEFT JOIN tenk1 b ON b.thousand = a.unique1 LEFT JOIN tenk1 c ON c.unique2 = coalesce(b.twothousand, a.twothousand) WHERE a.unique2 < 10 AND coalesce(b.twothousand, a.twothousand) = 44; -- -- check handling of join aliases when flattening multiple levels of subquery -- EXPLAIN ( VERBOSE, COSTS OFF ) SELECT foo1.join_key AS foo1_id, foo3.join_key AS foo3_id, bug_field FROM ( VALUES (0), (1)) foo1 (join_key) LEFT JOIN ( SELECT join_key, bug_field FROM ( SELECT ss1.join_key, ss1.bug_field FROM ( SELECT f1 AS join_key, 666 AS bug_field FROM int4_tbl i1) ss1) foo2 LEFT JOIN ( SELECT unique2 AS join_key FROM tenk1 i2) ss2 USING (join_key)) foo3 USING (join_key); SELECT foo1.join_key AS foo1_id, foo3.join_key AS foo3_id, bug_field FROM ( VALUES (0), (1)) foo1 (join_key) LEFT JOIN ( SELECT join_key, bug_field FROM ( SELECT ss1.join_key, ss1.bug_field FROM ( SELECT f1 AS join_key, 666 AS bug_field FROM int4_tbl i1) ss1) foo2 LEFT JOIN ( SELECT unique2 AS join_key FROM tenk1 i2) ss2 USING (join_key)) foo3 USING (join_key); -- -- test successful handling of nested outer joins with degenerate join quals -- EXPLAIN ( VERBOSE, COSTS OFF ) SELECT t1.* FROM text_tbl t1 LEFT JOIN ( SELECT *, '***'::text AS d1 FROM int8_tbl i8b1) b1 LEFT JOIN int8_tbl i8 LEFT JOIN ( SELECT *, NULL::int AS d2 FROM int8_tbl i8b2) b2 ON (i8.q1 = b2.q1) ON (b2.d2 = b1.q2) ON (t1.f1 = b1.d1) LEFT JOIN int4_tbl i4 ON (i8.q2 = i4.f1); SELECT t1.* FROM text_tbl t1 LEFT JOIN ( SELECT *, '***'::text AS d1 FROM int8_tbl i8b1) b1 LEFT JOIN int8_tbl i8 LEFT JOIN ( SELECT *, NULL::int AS d2 FROM int8_tbl i8b2) b2 ON (i8.q1 = b2.q1) ON (b2.d2 = b1.q2) ON (t1.f1 = b1.d1) LEFT JOIN int4_tbl i4 ON (i8.q2 = i4.f1); EXPLAIN ( VERBOSE, COSTS OFF ) SELECT t1.* FROM text_tbl t1 LEFT JOIN ( SELECT *, '***'::text AS d1 FROM int8_tbl i8b1) b1 LEFT JOIN int8_tbl i8 LEFT JOIN ( SELECT *, NULL::int AS d2 FROM int8_tbl i8b2, int4_tbl i4b2) b2 ON (i8.q1 = b2.q1) ON (b2.d2 = b1.q2) ON (t1.f1 = b1.d1) LEFT JOIN int4_tbl i4 ON (i8.q2 = i4.f1); SELECT t1.* FROM text_tbl t1 LEFT JOIN ( SELECT *, '***'::text AS d1 FROM int8_tbl i8b1) b1 LEFT JOIN int8_tbl i8 LEFT JOIN ( SELECT *, NULL::int AS d2 FROM int8_tbl i8b2, int4_tbl i4b2) b2 ON (i8.q1 = b2.q1) ON (b2.d2 = b1.q2) ON (t1.f1 = b1.d1) LEFT JOIN int4_tbl i4 ON (i8.q2 = i4.f1); EXPLAIN ( VERBOSE, COSTS OFF ) SELECT t1.* FROM text_tbl t1 LEFT JOIN ( SELECT *, '***'::text AS d1 FROM int8_tbl i8b1) b1 LEFT JOIN int8_tbl i8 LEFT JOIN ( SELECT *, NULL::int AS d2 FROM int8_tbl i8b2, int4_tbl i4b2 WHERE q1 = f1) b2 ON (i8.q1 = b2.q1) ON (b2.d2 = b1.q2) ON (t1.f1 = b1.d1) LEFT JOIN int4_tbl i4 ON (i8.q2 = i4.f1); SELECT t1.* FROM text_tbl t1 LEFT JOIN ( SELECT *, '***'::text AS d1 FROM int8_tbl i8b1) b1 LEFT JOIN int8_tbl i8 LEFT JOIN ( SELECT *, NULL::int AS d2 FROM int8_tbl i8b2, int4_tbl i4b2 WHERE q1 = f1) b2 ON (i8.q1 = b2.q1) ON (b2.d2 = b1.q2) ON (t1.f1 = b1.d1) LEFT JOIN int4_tbl i4 ON (i8.q2 = i4.f1); EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM text_tbl t1 INNER JOIN int8_tbl i8 ON i8.q2 = 456 RIGHT JOIN text_tbl t2 ON t1.f1 = 'doh!' LEFT JOIN int4_tbl i4 ON i8.q1 = i4.f1; SELECT * FROM text_tbl t1 INNER JOIN int8_tbl i8 ON i8.q2 = 456 RIGHT JOIN text_tbl t2 ON t1.f1 = 'doh!' LEFT JOIN int4_tbl i4 ON i8.q1 = i4.f1; -- -- test for appropriate join order in the presence of lateral references -- EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM text_tbl t1 LEFT JOIN int8_tbl i8 ON i8.q2 = 123, LATERAL ( SELECT i8.q1, t2.f1 FROM text_tbl t2 LIMIT 1) AS ss WHERE t1.f1 = ss.f1; SELECT * FROM text_tbl t1 LEFT JOIN int8_tbl i8 ON i8.q2 = 123, LATERAL ( SELECT i8.q1, t2.f1 FROM text_tbl t2 LIMIT 1) AS ss WHERE t1.f1 = ss.f1; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM text_tbl t1 LEFT JOIN int8_tbl i8 ON i8.q2 = 123, LATERAL ( SELECT i8.q1, t2.f1 FROM text_tbl t2 LIMIT 1) AS ss1, LATERAL ( SELECT ss1.* FROM text_tbl t3 LIMIT 1) AS ss2 WHERE t1.f1 = ss2.f1; SELECT * FROM text_tbl t1 LEFT JOIN int8_tbl i8 ON i8.q2 = 123, LATERAL ( SELECT i8.q1, t2.f1 FROM text_tbl t2 LIMIT 1) AS ss1, LATERAL ( SELECT ss1.* FROM text_tbl t3 LIMIT 1) AS ss2 WHERE t1.f1 = ss2.f1; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT 1 FROM text_tbl AS tt1 INNER JOIN text_tbl AS tt2 ON (tt1.f1 = 'foo') LEFT JOIN text_tbl AS tt3 ON (tt3.f1 = 'foo') LEFT JOIN text_tbl AS tt4 ON (tt3.f1 = tt4.f1), LATERAL ( SELECT tt4.f1 AS c0 FROM text_tbl AS tt5 LIMIT 1) AS ss1 WHERE tt1.f1 = ss1.c0; SELECT 1 FROM text_tbl AS tt1 INNER JOIN text_tbl AS tt2 ON (tt1.f1 = 'foo') LEFT JOIN text_tbl AS tt3 ON (tt3.f1 = 'foo') LEFT JOIN text_tbl AS tt4 ON (tt3.f1 = tt4.f1), LATERAL ( SELECT tt4.f1 AS c0 FROM text_tbl AS tt5 LIMIT 1) AS ss1 WHERE tt1.f1 = ss1.c0; -- -- check a case in which a PlaceHolderVar forces join order -- EXPLAIN ( VERBOSE, COSTS OFF ) SELECT ss2.* FROM int4_tbl i41 LEFT JOIN int8_tbl i8 JOIN ( SELECT i42.f1 AS c1, i43.f1 AS c2, 42 AS c3 FROM int4_tbl i42, int4_tbl i43) ss1 ON i8.q1 = ss1.c2 ON i41.f1 = ss1.c1, LATERAL ( SELECT i41.*, i8.*, ss1.* FROM text_tbl LIMIT 1) ss2 WHERE ss1.c2 = 0; SELECT ss2.* FROM int4_tbl i41 LEFT JOIN int8_tbl i8 JOIN ( SELECT i42.f1 AS c1, i43.f1 AS c2, 42 AS c3 FROM int4_tbl i42, int4_tbl i43) ss1 ON i8.q1 = ss1.c2 ON i41.f1 = ss1.c1, LATERAL ( SELECT i41.*, i8.*, ss1.* FROM text_tbl LIMIT 1) ss2 WHERE ss1.c2 = 0; -- -- test successful handling of full join underneath left join (bug #14105) -- EXPLAIN ( COSTS OFF ) SELECT * FROM ( SELECT 1 AS id) AS xx LEFT JOIN (tenk1 AS a1 FULL JOIN ( SELECT 1 AS id) AS yy ON (a1.unique1 = yy.id)) ON (xx.id = coalesce(yy.id)); SELECT * FROM ( SELECT 1 AS id) AS xx LEFT JOIN (tenk1 AS a1 FULL JOIN ( SELECT 1 AS id) AS yy ON (a1.unique1 = yy.id)) ON (xx.id = coalesce(yy.id)); -- -- test ability to push constants through outer join clauses -- EXPLAIN ( COSTS OFF ) SELECT * FROM int4_tbl a LEFT JOIN tenk1 b ON f1 = unique2 WHERE f1 = 0; EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1 a FULL JOIN tenk1 b USING (unique2) WHERE unique2 = 42; -- -- test that quals attached to an outer join have correct semantics, -- specifically that they don't re-use expressions computed below the join; -- we force a mergejoin so that coalesce(b.q1, 1) appears as a join input -- SET enable_hashjoin TO OFF; SET enable_nestloop TO OFF; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT a.q2, b.q1 FROM int8_tbl a LEFT JOIN int8_tbl b ON a.q2 = coalesce(b.q1, 1) WHERE coalesce(b.q1, 1) > 0; SELECT a.q2, b.q1 FROM int8_tbl a LEFT JOIN int8_tbl b ON a.q2 = coalesce(b.q1, 1) WHERE coalesce(b.q1, 1) > 0; RESET enable_hashjoin; RESET enable_nestloop; -- -- test join removal -- BEGIN; CREATE TEMP TABLE a ( id int PRIMARY KEY, b_id int ); CREATE TEMP TABLE b ( id int PRIMARY KEY, c_id int ); CREATE TEMP TABLE c ( id int PRIMARY KEY ); CREATE TEMP TABLE d ( a int, b int ); INSERT INTO a VALUES (0, 0), (1, NULL); INSERT INTO b VALUES (0, 0), (1, NULL); INSERT INTO c VALUES (0), (1); INSERT INTO d VALUES (1, 3), (2, 2), (3, 1); -- all three cases should be optimizable into a simple seqscan EXPLAIN ( COSTS OFF ) SELECT a.* FROM a LEFT JOIN b ON a.b_id = b.id; EXPLAIN ( COSTS OFF ) SELECT b.* FROM b LEFT JOIN c ON b.c_id = c.id; EXPLAIN ( COSTS OFF ) SELECT a.* FROM a LEFT JOIN (b LEFT JOIN c ON b.c_id = c.id) ON (a.b_id = b.id); -- check optimization of outer join within another special join EXPLAIN ( COSTS OFF ) SELECT id FROM a WHERE id IN ( SELECT b.id FROM b LEFT JOIN c ON b.id = c.id); -- check that join removal works for a left join when joining a subquery -- that is guaranteed to be unique by its GROUP BY clause EXPLAIN ( COSTS OFF ) SELECT d.* FROM d LEFT JOIN ( SELECT * FROM b GROUP BY b.id, b.c_id) s ON d.a = s.id AND d.b = s.c_id; -- similarly, but keying off a DISTINCT clause EXPLAIN ( COSTS OFF ) SELECT d.* FROM d LEFT JOIN ( SELECT DISTINCT * FROM b) s ON d.a = s.id AND d.b = s.c_id; -- join removal is not possible when the GROUP BY contains a column that is -- not in the join condition. (Note: as of 9.6, we notice that b.id is a -- primary key and so drop b.c_id from the GROUP BY of the resulting plan; -- but this happens too late for join removal in the outer plan level.) EXPLAIN ( COSTS OFF ) SELECT d.* FROM d LEFT JOIN ( SELECT * FROM b GROUP BY b.id, b.c_id) s ON d.a = s.id; -- similarly, but keying off a DISTINCT clause EXPLAIN ( COSTS OFF ) SELECT d.* FROM d LEFT JOIN ( SELECT DISTINCT * FROM b) s ON d.a = s.id; -- check join removal works when uniqueness of the join condition is enforced -- by a UNION EXPLAIN ( COSTS OFF ) SELECT d.* FROM d LEFT JOIN ( SELECT id FROM a UNION SELECT id FROM b) s ON d.a = s.id; -- check join removal with a cross-type comparison operator EXPLAIN ( COSTS OFF ) SELECT i8.* FROM int8_tbl i8 LEFT JOIN ( SELECT f1 FROM int4_tbl GROUP BY f1) i4 ON i8.q1 = i4.f1; -- check join removal with lateral references EXPLAIN ( COSTS OFF ) SELECT 1 FROM ( SELECT a.id FROM a LEFT JOIN b ON a.b_id = b.id) q, LATERAL generate_series(1, q.id) gs (i) WHERE q.id = gs.i; ROLLBACK; CREATE temp TABLE parent ( k int PRIMARY KEY, pd int ); CREATE temp TABLE child ( k int UNIQUE, cd int ); INSERT INTO parent VALUES (1, 10), (2, 20), (3, 30); INSERT INTO child VALUES (1, 100), (4, 400); -- this case is optimizable SELECT p.* FROM parent p LEFT JOIN child c ON (p.k = c.k); EXPLAIN ( COSTS OFF ) SELECT p.* FROM parent p LEFT JOIN child c ON (p.k = c.k); -- this case is not SELECT p.*, linked FROM parent p LEFT JOIN ( SELECT c.*, TRUE AS linked FROM child c) AS ss ON (p.k = ss.k); EXPLAIN ( COSTS OFF ) SELECT p.*, linked FROM parent p LEFT JOIN ( SELECT c.*, TRUE AS linked FROM child c) AS ss ON (p.k = ss.k); -- check for a 9.0rc1 bug: join removal breaks pseudoconstant qual handling SELECT p.* FROM parent p LEFT JOIN child c ON (p.k = c.k) WHERE p.k = 1 AND p.k = 2; EXPLAIN ( COSTS OFF ) SELECT p.* FROM parent p LEFT JOIN child c ON (p.k = c.k) WHERE p.k = 1 AND p.k = 2; SELECT p.* FROM (parent p LEFT JOIN child c ON (p.k = c.k)) JOIN parent x ON p.k = x.k WHERE p.k = 1 AND p.k = 2; EXPLAIN ( COSTS OFF ) SELECT p.* FROM (parent p LEFT JOIN child c ON (p.k = c.k)) JOIN parent x ON p.k = x.k WHERE p.k = 1 AND p.k = 2; -- bug 5255: this is not optimizable by join removal BEGIN; CREATE TEMP TABLE a ( id int PRIMARY KEY ); CREATE TEMP TABLE b ( id int PRIMARY KEY, a_id int ); INSERT INTO a VALUES (0), (1); INSERT INTO b VALUES (0, 0), (1, NULL); SELECT * FROM b LEFT JOIN a ON (b.a_id = a.id) WHERE (a.id IS NULL OR a.id > 0); SELECT b.* FROM b LEFT JOIN a ON (b.a_id = a.id) WHERE (a.id IS NULL OR a.id > 0); ROLLBACK; -- another join removal bug: this is not optimizable, either BEGIN; CREATE temp TABLE innertab ( id int8 PRIMARY KEY, dat1 int8 ); INSERT INTO innertab VALUES (123, 42); SELECT * FROM ( SELECT 1 AS x) ss1 LEFT JOIN ( SELECT q1, q2, COALESCE(dat1, q1) AS y FROM int8_tbl LEFT JOIN innertab ON q2 = id) ss2 ON TRUE; ROLLBACK; -- another join removal bug: we must clean up correctly when removing a PHV BEGIN; CREATE temp TABLE uniquetbl ( f1 text UNIQUE ); EXPLAIN ( COSTS OFF ) SELECT t1.* FROM uniquetbl AS t1 LEFT JOIN ( SELECT *, '***'::text AS d1 FROM uniquetbl) t2 ON t1.f1 = t2.f1 LEFT JOIN uniquetbl t3 ON t2.d1 = t3.f1; EXPLAIN ( COSTS OFF ) SELECT t0.* FROM text_tbl t0 LEFT JOIN ( SELECT CASE t1.ten WHEN 0 THEN 'doh!'::text ELSE NULL::text END AS case1, t1.stringu2 FROM tenk1 t1 JOIN int4_tbl i4 ON i4.f1 = t1.unique2 LEFT JOIN uniquetbl u1 ON u1.f1 = t1.string4) ss ON t0.f1 = ss.case1 WHERE ss.stringu2 !~* ss.case1; SELECT t0.* FROM text_tbl t0 LEFT JOIN ( SELECT CASE t1.ten WHEN 0 THEN 'doh!'::text ELSE NULL::text END AS case1, t1.stringu2 FROM tenk1 t1 JOIN int4_tbl i4 ON i4.f1 = t1.unique2 LEFT JOIN uniquetbl u1 ON u1.f1 = t1.string4) ss ON t0.f1 = ss.case1 WHERE ss.stringu2 !~* ss.case1; ROLLBACK; -- bug #8444: we've historically allowed duplicate aliases within aliased JOINs SELECT * FROM int8_tbl x JOIN (int4_tbl x CROSS JOIN int4_tbl y) j ON q1 = f1; -- error SELECT * FROM int8_tbl x JOIN (int4_tbl x CROSS JOIN int4_tbl y) j ON q1 = y.f1; -- error SELECT * FROM int8_tbl x JOIN (int4_tbl x CROSS JOIN int4_tbl y (ff)) j ON q1 = f1; -- ok -- -- Test hints given on incorrect column references are useful -- SELECT t1.uunique1 FROM tenk1 t1 JOIN tenk2 t2 ON t1.two = t2.two; -- error, prefer "t1" suggestion SELECT t2.uunique1 FROM tenk1 t1 JOIN tenk2 t2 ON t1.two = t2.two; -- error, prefer "t2" suggestion SELECT uunique1 FROM tenk1 t1 JOIN tenk2 t2 ON t1.two = t2.two; -- error, suggest both at once -- -- Take care to reference the correct RTE -- SELECT atts.relid::regclass, s.* FROM pg_stats s JOIN pg_attribute a ON s.attname = a.attname AND s.tablename = a.attrelid::regclass::text JOIN ( SELECT unnest(indkey) attnum, indexrelid FROM pg_index i) atts ON atts.attnum = a.attnum WHERE schemaname != 'pg_catalog'; -- -- Test LATERAL -- SELECT unique2, x.* FROM tenk1 a, LATERAL ( SELECT * FROM int4_tbl b WHERE f1 = a.unique1) x; EXPLAIN ( COSTS OFF ) SELECT unique2, x.* FROM tenk1 a, LATERAL ( SELECT * FROM int4_tbl b WHERE f1 = a.unique1) x; SELECT unique2, x.* FROM int4_tbl x, LATERAL ( SELECT unique2 FROM tenk1 WHERE f1 = unique1) ss; EXPLAIN ( COSTS OFF ) SELECT unique2, x.* FROM int4_tbl x, LATERAL ( SELECT unique2 FROM tenk1 WHERE f1 = unique1) ss; EXPLAIN ( COSTS OFF ) SELECT unique2, x.* FROM int4_tbl x CROSS JOIN LATERAL ( SELECT unique2 FROM tenk1 WHERE f1 = unique1) ss; SELECT unique2, x.* FROM int4_tbl x LEFT JOIN LATERAL ( SELECT unique1, unique2 FROM tenk1 WHERE f1 = unique1) ss ON TRUE; EXPLAIN ( COSTS OFF ) SELECT unique2, x.* FROM int4_tbl x LEFT JOIN LATERAL ( SELECT unique1, unique2 FROM tenk1 WHERE f1 = unique1) ss ON TRUE; -- check scoping of lateral versus parent references -- the first of these should return int8_tbl.q2, the second int8_tbl.q1 SELECT *, ( SELECT r FROM ( SELECT q1 AS q2) x, ( SELECT q2 AS r) y) FROM int8_tbl; SELECT *, ( SELECT r FROM ( SELECT q1 AS q2) x, LATERAL ( SELECT q2 AS r) y) FROM int8_tbl; -- lateral with function in FROM SELECT count(*) FROM tenk1 a, LATERAL generate_series(1, two) g; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1 a, LATERAL generate_series(1, two) g; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1 a CROSS JOIN LATERAL generate_series(1, two) g; -- don't need the explicit LATERAL keyword for functions EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1 a, generate_series(1, two) g; -- lateral with UNION ALL subselect EXPLAIN ( COSTS OFF ) SELECT * FROM generate_series(100, 200) g, LATERAL ( SELECT * FROM int8_tbl a WHERE g = q1 UNION ALL SELECT * FROM int8_tbl b WHERE g = q2) ss; SELECT * FROM generate_series(100, 200) g, LATERAL ( SELECT * FROM int8_tbl a WHERE g = q1 UNION ALL SELECT * FROM int8_tbl b WHERE g = q2) ss; -- lateral with VALUES EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1 a, tenk1 b JOIN LATERAL ( VALUES (a.unique1)) ss (x) ON b.unique2 = ss.x; SELECT count(*) FROM tenk1 a, tenk1 b JOIN LATERAL ( VALUES (a.unique1)) ss (x) ON b.unique2 = ss.x; -- lateral with VALUES, no flattening possible EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1 a, tenk1 b JOIN LATERAL ( VALUES (a.unique1), (- 1)) ss (x) ON b.unique2 = ss.x; SELECT count(*) FROM tenk1 a, tenk1 b JOIN LATERAL ( VALUES (a.unique1), (- 1)) ss (x) ON b.unique2 = ss.x; -- lateral injecting a strange outer join condition EXPLAIN ( COSTS OFF ) SELECT * FROM int8_tbl a, int8_tbl x LEFT JOIN LATERAL ( SELECT a.q1 FROM int4_tbl y) ss (z) ON x.q2 = ss.z ORDER BY a.q1, a.q2, x.q1, x.q2, ss.z; SELECT * FROM int8_tbl a, int8_tbl x LEFT JOIN LATERAL ( SELECT a.q1 FROM int4_tbl y) ss (z) ON x.q2 = ss.z ORDER BY a.q1, a.q2, x.q1, x.q2, ss.z; -- lateral reference to a join alias variable SELECT * FROM ( SELECT f1 / 2 AS x FROM int4_tbl) ss1 JOIN int4_tbl i4 ON x = f1, LATERAL ( SELECT x) ss2 (y); SELECT * FROM ( SELECT f1 AS x FROM int4_tbl) ss1 JOIN int4_tbl i4 ON x = f1, LATERAL ( VALUES (x)) ss2 (y); SELECT * FROM (( SELECT f1 / 2 AS x FROM int4_tbl) ss1 JOIN int4_tbl i4 ON x = f1) j, LATERAL ( SELECT x) ss2 (y); -- lateral references requiring pullup SELECT * FROM ( VALUES (1)) x (lb), LATERAL generate_series(lb, 4) x4; SELECT * FROM ( SELECT f1 / 1000000000 FROM int4_tbl) x (lb), LATERAL generate_series(lb, 4) x4; SELECT * FROM ( VALUES (1)) x (lb), LATERAL ( VALUES (lb)) y (lbcopy); SELECT * FROM ( VALUES (1)) x (lb), LATERAL ( SELECT lb FROM int4_tbl) y (lbcopy); SELECT * FROM int8_tbl x LEFT JOIN ( SELECT q1, coalesce(q2, 0) q2 FROM int8_tbl) y ON x.q2 = y.q1, LATERAL ( VALUES (x.q1, y.q1, y.q2)) v (xq1, yq1, yq2); SELECT * FROM int8_tbl x LEFT JOIN ( SELECT q1, coalesce(q2, 0) q2 FROM int8_tbl) y ON x.q2 = y.q1, LATERAL ( SELECT x.q1, y.q1, y.q2) v (xq1, yq1, yq2); SELECT x.* FROM int8_tbl x LEFT JOIN ( SELECT q1, coalesce(q2, 0) q2 FROM int8_tbl) y ON x.q2 = y.q1, LATERAL ( SELECT x.q1, y.q1, y.q2) v (xq1, yq1, yq2); SELECT v.* FROM (int8_tbl x LEFT JOIN ( SELECT q1, coalesce(q2, 0) q2 FROM int8_tbl) y ON x.q2 = y.q1) LEFT JOIN int4_tbl z ON z.f1 = x.q2, LATERAL ( SELECT x.q1, y.q1 UNION ALL SELECT x.q2, y.q2) v (vx, vy); SELECT v.* FROM (int8_tbl x LEFT JOIN ( SELECT q1, ( SELECT coalesce(q2, 0)) q2 FROM int8_tbl) y ON x.q2 = y.q1) LEFT JOIN int4_tbl z ON z.f1 = x.q2, LATERAL ( SELECT x.q1, y.q1 UNION ALL SELECT x.q2, y.q2) v (vx, vy); SELECT v.* FROM (int8_tbl x LEFT JOIN ( SELECT q1, ( SELECT coalesce(q2, 0)) q2 FROM int8_tbl) y ON x.q2 = y.q1) LEFT JOIN int4_tbl z ON z.f1 = x.q2, LATERAL ( SELECT x.q1, y.q1 FROM onerow UNION ALL SELECT x.q2, y.q2 FROM onerow) v (vx, vy); EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM int8_tbl a LEFT JOIN LATERAL ( SELECT *, a.q2 AS x FROM int8_tbl b) ss ON a.q2 = ss.q1; SELECT * FROM int8_tbl a LEFT JOIN LATERAL ( SELECT *, a.q2 AS x FROM int8_tbl b) ss ON a.q2 = ss.q1; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM int8_tbl a LEFT JOIN LATERAL ( SELECT *, coalesce(a.q2, 42) AS x FROM int8_tbl b) ss ON a.q2 = ss.q1; SELECT * FROM int8_tbl a LEFT JOIN LATERAL ( SELECT *, coalesce(a.q2, 42) AS x FROM int8_tbl b) ss ON a.q2 = ss.q1; -- lateral can result in join conditions appearing below their -- real semantic level EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM int4_tbl i LEFT JOIN LATERAL ( SELECT * FROM int2_tbl j WHERE i.f1 = j.f1) k ON TRUE; SELECT * FROM int4_tbl i LEFT JOIN LATERAL ( SELECT * FROM int2_tbl j WHERE i.f1 = j.f1) k ON TRUE; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM int4_tbl i LEFT JOIN LATERAL ( SELECT coalesce(i) FROM int2_tbl j WHERE i.f1 = j.f1) k ON TRUE; SELECT * FROM int4_tbl i LEFT JOIN LATERAL ( SELECT coalesce(i) FROM int2_tbl j WHERE i.f1 = j.f1) k ON TRUE; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM int4_tbl a, LATERAL ( SELECT * FROM int4_tbl b LEFT JOIN int8_tbl c ON (b.f1 = q1 AND a.f1 = q2)) ss; SELECT * FROM int4_tbl a, LATERAL ( SELECT * FROM int4_tbl b LEFT JOIN int8_tbl c ON (b.f1 = q1 AND a.f1 = q2)) ss; -- lateral reference in a PlaceHolderVar evaluated at join level EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM int8_tbl a LEFT JOIN LATERAL ( SELECT b.q1 AS bq1, c.q1 AS cq1, least (a.q1, b.q1, c.q1) FROM int8_tbl b CROSS JOIN int8_tbl c) ss ON a.q2 = ss.bq1; SELECT * FROM int8_tbl a LEFT JOIN LATERAL ( SELECT b.q1 AS bq1, c.q1 AS cq1, least (a.q1, b.q1, c.q1) FROM int8_tbl b CROSS JOIN int8_tbl c) ss ON a.q2 = ss.bq1; -- case requiring nested PlaceHolderVars EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM int8_tbl c LEFT JOIN (int8_tbl a LEFT JOIN ( SELECT q1, coalesce(q2, 42) AS x FROM int8_tbl b) ss1 ON a.q2 = ss1.q1 CROSS JOIN LATERAL ( SELECT q1, coalesce(ss1.x, q2) AS y FROM int8_tbl d) ss2) ON c.q2 = ss2.q1, LATERAL ( SELECT ss2.y offset 0) ss3; -- case that breaks the old ph_may_need optimization EXPLAIN ( VERBOSE, COSTS OFF ) SELECT c.*, a.*, ss1.q1, ss2.q1, ss3.* FROM int8_tbl c LEFT JOIN (int8_tbl a LEFT JOIN ( SELECT q1, coalesce(q2, f1) AS x FROM int8_tbl b, int4_tbl b2 WHERE q1 < f1) ss1 ON a.q2 = ss1.q1 CROSS JOIN LATERAL ( SELECT q1, coalesce(ss1.x, q2) AS y FROM int8_tbl d) ss2) ON c.q2 = ss2.q1, LATERAL ( SELECT * FROM int4_tbl i WHERE ss2.y > f1) ss3; -- check processing of postponed quals (bug #9041) EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM ( SELECT 1 AS x offset 0) x CROSS JOIN ( SELECT 2 AS y offset 0) y LEFT JOIN LATERAL ( SELECT * FROM ( SELECT 3 AS z offset 0) z WHERE z.z = x.x) zz ON zz.z = y.y; -- check dummy rels with lateral references (bug #15694) EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM int8_tbl i8 LEFT JOIN LATERAL ( SELECT *, i8.q2 FROM int4_tbl WHERE FALSE) ss ON TRUE; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM int8_tbl i8 LEFT JOIN LATERAL ( SELECT *, i8.q2 FROM int4_tbl i1, int4_tbl i2 WHERE FALSE) ss ON TRUE; -- check handling of nested appendrels inside LATERAL SELECT * FROM (( SELECT 2 AS v) UNION ALL ( SELECT 3 AS v)) AS q1 CROSS JOIN LATERAL (( SELECT * FROM (( SELECT 4 AS v) UNION ALL ( SELECT 5 AS v)) AS q3) UNION ALL ( SELECT q1.v)) AS q2; -- check we don't try to do a unique-ified semijoin with LATERAL EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM ( VALUES (0, 9998), (1, 1000)) v (id, x), LATERAL ( SELECT f1 FROM int4_tbl WHERE f1 = ANY ( SELECT unique1 FROM tenk1 WHERE unique2 = v.x offset 0)) ss; SELECT * FROM ( VALUES (0, 9998), (1, 1000)) v (id, x), LATERAL ( SELECT f1 FROM int4_tbl WHERE f1 = ANY ( SELECT unique1 FROM tenk1 WHERE unique2 = v.x offset 0)) ss; -- check proper extParam/allParam handling (this isn't exactly a LATERAL issue, -- but we can make the test case much more compact with LATERAL) EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM ( VALUES (0), (1)) v (id), LATERAL ( SELECT * FROM int8_tbl t1, LATERAL ( SELECT * FROM ( SELECT * FROM int8_tbl t2 WHERE q1 = ANY ( SELECT q2 FROM int8_tbl t3 WHERE q2 = ( SELECT greatest (t1.q1, t2.q2)) AND ( SELECT v.id = 0)) offset 0) ss2) ss WHERE t1.q1 = ss.q2) ss0; SELECT * FROM ( VALUES (0), (1)) v (id), LATERAL ( SELECT * FROM int8_tbl t1, LATERAL ( SELECT * FROM ( SELECT * FROM int8_tbl t2 WHERE q1 = ANY ( SELECT q2 FROM int8_tbl t3 WHERE q2 = ( SELECT greatest (t1.q1, t2.q2)) AND ( SELECT v.id = 0)) offset 0) ss2) ss WHERE t1.q1 = ss.q2) ss0; -- test some error cases where LATERAL should have been used but wasn't SELECT f1, g FROM int4_tbl a, ( SELECT f1 AS g) ss; SELECT f1, g FROM int4_tbl a, ( SELECT a.f1 AS g) ss; SELECT f1, g FROM int4_tbl a CROSS JOIN ( SELECT f1 AS g) ss; SELECT f1, g FROM int4_tbl a CROSS JOIN ( SELECT a.f1 AS g) ss; -- SQL:2008 says the left table is in scope but illegal to access here SELECT f1, g FROM int4_tbl a RIGHT JOIN LATERAL generate_series(0, a.f1) g ON TRUE; SELECT f1, g FROM int4_tbl a FULL JOIN LATERAL generate_series(0, a.f1) g ON TRUE; -- check we complain about ambiguous table references SELECT * FROM int8_tbl x CROSS JOIN (int4_tbl x CROSS JOIN LATERAL ( SELECT x.f1) ss); -- LATERAL can be used to put an aggregate into the FROM clause of its query SELECT 1 FROM tenk1 a, LATERAL ( SELECT max(a.unique1) FROM int4_tbl b) ss; -- check behavior of LATERAL in UPDATE/DELETE CREATE temp TABLE xx1 AS SELECT f1 AS x1, - f1 AS x2 FROM int4_tbl; -- error, can't do this: UPDATE xx1 SET x2 = f1 FROM ( SELECT * FROM int4_tbl WHERE f1 = x1) ss; UPDATE xx1 SET x2 = f1 FROM ( SELECT * FROM int4_tbl WHERE f1 = xx1.x1) ss; -- can't do it even with LATERAL: UPDATE xx1 SET x2 = f1 FROM LATERAL ( SELECT * FROM int4_tbl WHERE f1 = x1) ss; -- we might in future allow something like this, but for now it's an error: UPDATE xx1 SET x2 = f1 FROM xx1, LATERAL ( SELECT * FROM int4_tbl WHERE f1 = x1) ss; -- also errors: DELETE FROM xx1 USING ( SELECT * FROM int4_tbl WHERE f1 = x1) ss; DELETE FROM xx1 USING ( SELECT * FROM int4_tbl WHERE f1 = xx1.x1) ss; DELETE FROM xx1 USING LATERAL ( SELECT * FROM int4_tbl WHERE f1 = x1) ss; -- -- test LATERAL reference propagation down a multi-level inheritance hierarchy -- produced for a multi-level partitioned table hierarchy. -- CREATE TABLE join_pt1 ( a int, b int, c varchar ) PARTITION BY RANGE (a); CREATE TABLE join_pt1p1 PARTITION OF join_pt1 FOR VALUES FROM (0) TO (100) PARTITION BY RANGE (b); CREATE TABLE join_pt1p2 PARTITION OF join_pt1 FOR VALUES FROM (100) TO (200); CREATE TABLE join_pt1p1p1 PARTITION OF join_pt1p1 FOR VALUES FROM (0) TO (100); INSERT INTO join_pt1 VALUES (1, 1, 'x'), (101, 101, 'y'); CREATE TABLE join_ut1 ( a int, b int, c varchar ); INSERT INTO join_ut1 VALUES (101, 101, 'y'), (2, 2, 'z'); EXPLAIN ( VERBOSE, COSTS OFF ) SELECT t1.b, ss.phv FROM join_ut1 t1 LEFT JOIN LATERAL ( SELECT t2.a AS t2a, t3.a t3a, least (t1.a, t2.a, t3.a) phv FROM join_pt1 t2 JOIN join_ut1 t3 ON t2.a = t3.b) ss ON t1.a = ss.t2a ORDER BY t1.a; SELECT t1.b, ss.phv FROM join_ut1 t1 LEFT JOIN LATERAL ( SELECT t2.a AS t2a, t3.a t3a, least (t1.a, t2.a, t3.a) phv FROM join_pt1 t2 JOIN join_ut1 t3 ON t2.a = t3.b) ss ON t1.a = ss.t2a ORDER BY t1.a; DROP TABLE join_pt1; DROP TABLE join_ut1; -- -- test that foreign key join estimation performs sanely for outer joins -- BEGIN; CREATE TABLE fkest ( a int, b int, c int UNIQUE, PRIMARY KEY (a, b) ); CREATE TABLE fkest1 ( a int, b int, PRIMARY KEY (a, b) ); INSERT INTO fkest SELECT x / 10, x % 10, x FROM generate_series(1, 1000) x; INSERT INTO fkest1 SELECT x / 10, x % 10 FROM generate_series(1, 1000) x; ALTER TABLE fkest1 ADD CONSTRAINT fkest1_a_b_fkey FOREIGN KEY (a, b) REFERENCES fkest; ANALYZE fkest; ANALYZE fkest1; EXPLAIN ( COSTS OFF ) SELECT * FROM fkest f LEFT JOIN fkest1 f1 ON f.a = f1.a AND f.b = f1.b LEFT JOIN fkest1 f2 ON f.a = f2.a AND f.b = f2.b LEFT JOIN fkest1 f3 ON f.a = f3.a AND f.b = f3.b WHERE f.c = 1; ROLLBACK; -- -- test planner's ability to mark joins as unique -- CREATE TABLE j1 ( id int PRIMARY KEY ); CREATE TABLE j2 ( id int PRIMARY KEY ); CREATE TABLE j3 ( id int ); INSERT INTO j1 VALUES (1), (2), (3); INSERT INTO j2 VALUES (1), (2), (3); INSERT INTO j3 VALUES (1), (1); ANALYZE j1; ANALYZE j2; ANALYZE j3; -- ensure join is properly marked as unique EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM j1 INNER JOIN j2 ON j1.id = j2.id; -- ensure join is not unique when not an equi-join EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM j1 INNER JOIN j2 ON j1.id > j2.id; -- ensure non-unique rel is not chosen as inner EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM j1 INNER JOIN j3 ON j1.id = j3.id; -- ensure left join is marked as unique EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM j1 LEFT JOIN j2 ON j1.id = j2.id; -- ensure right join is marked as unique EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM j1 RIGHT JOIN j2 ON j1.id = j2.id; -- ensure full join is marked as unique EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM j1 FULL JOIN j2 ON j1.id = j2.id; -- a clauseless (cross) join can't be unique EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM j1 CROSS JOIN j2; -- ensure a natural join is marked as unique EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM j1 NATURAL JOIN j2; -- ensure a distinct clause allows the inner to become unique EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM j1 INNER JOIN ( SELECT DISTINCT id FROM j3) j3 ON j1.id = j3.id; -- ensure group by clause allows the inner to become unique EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM j1 INNER JOIN ( SELECT id FROM j3 GROUP BY id) j3 ON j1.id = j3.id; DROP TABLE j1; DROP TABLE j2; DROP TABLE j3; -- test more complex permutations of unique joins CREATE TABLE j1 ( id1 int, id2 int, PRIMARY KEY (id1, id2) ); CREATE TABLE j2 ( id1 int, id2 int, PRIMARY KEY (id1, id2) ); CREATE TABLE j3 ( id1 int, id2 int, PRIMARY KEY (id1, id2) ); INSERT INTO j1 VALUES (1, 1), (1, 2); INSERT INTO j2 VALUES (1, 1); INSERT INTO j3 VALUES (1, 1); ANALYZE j1; ANALYZE j2; ANALYZE j3; -- ensure there's no unique join when not all columns which are part of the -- unique index are seen in the join clause EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM j1 INNER JOIN j2 ON j1.id1 = j2.id1; -- ensure proper unique detection with multiple join quals EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM j1 INNER JOIN j2 ON j1.id1 = j2.id1 AND j1.id2 = j2.id2; -- ensure we don't detect the join to be unique when quals are not part of the -- join condition EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM j1 INNER JOIN j2 ON j1.id1 = j2.id1 WHERE j1.id2 = 1; -- as above, but for left joins. EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM j1 LEFT JOIN j2 ON j1.id1 = j2.id1 WHERE j1.id2 = 1; -- validate logic in merge joins which skips mark and restore. -- it should only do this if all quals which were used to detect the unique -- are present as join quals, and not plain quals. SET enable_nestloop TO 0; SET enable_hashjoin TO 0; SET enable_sort TO 0; -- create indexes that will be preferred over the PKs to perform the join CREATE INDEX j1_id1_idx ON j1 (id1) WHERE id1 % 1000 = 1; CREATE INDEX j2_id1_idx ON j2 (id1) WHERE id1 % 1000 = 1; -- need an additional row in j2, if we want j2_id1_idx to be preferred INSERT INTO j2 VALUES (1, 2); ANALYZE j2; EXPLAIN ( COSTS OFF ) SELECT * FROM j1 INNER JOIN j2 ON j1.id1 = j2.id1 AND j1.id2 = j2.id2 WHERE j1.id1 % 1000 = 1 AND j2.id1 % 1000 = 1; SELECT * FROM j1 INNER JOIN j2 ON j1.id1 = j2.id1 AND j1.id2 = j2.id2 WHERE j1.id1 % 1000 = 1 AND j2.id1 % 1000 = 1; RESET enable_nestloop; RESET enable_hashjoin; RESET enable_sort; DROP TABLE j1; DROP TABLE j2; DROP TABLE j3; -- check that semijoin inner is not seen as unique for a portion of the outerrel EXPLAIN ( VERBOSE, COSTS OFF ) SELECT t1.unique1, t2.hundred FROM onek t1, tenk1 t2 WHERE EXISTS ( SELECT 1 FROM tenk1 t3 WHERE t3.thousand = t1.unique1 AND t3.tenthous = t2.hundred) AND t1.unique1 < 1; -- ... unless it actually is unique CREATE TABLE j3 AS SELECT unique1, tenthous FROM onek; VACUUM ANALYZE j3; CREATE UNIQUE INDEX ON j3 (unique1, tenthous); EXPLAIN ( VERBOSE, COSTS OFF ) SELECT t1.unique1, t2.hundred FROM onek t1, tenk1 t2 WHERE EXISTS ( SELECT 1 FROM j3 WHERE j3.unique1 = t1.unique1 AND j3.tenthous = t2.hundred) AND t1.unique1 < 1; DROP TABLE j3; pgFormatter-4.2/t/pg-test-files/expected/join_hash.sql000066400000000000000000000462121361326045100230710ustar00rootroot00000000000000-- -- exercises for the hash join code -- BEGIN; SET local min_parallel_table_scan_size = 0; SET local parallel_setup_cost = 0; -- Extract bucket and batch counts from an explain analyze plan. In -- general we can't make assertions about how many batches (or -- buckets) will be required because it can vary, but we can in some -- special cases and we can check for growth. CREATE OR REPLACE FUNCTION find_hash (node json) RETURNS json LANGUAGE plpgsql AS $$ DECLARE x json; child json; BEGIN IF node ->> 'Node Type' = 'Hash' THEN RETURN node; ELSE FOR child IN SELECT json_array_elements(node -> 'Plans') LOOP x := find_hash (child); IF x IS NOT NULL THEN RETURN x; END IF; END LOOP; RETURN NULL; END IF; END; $$; CREATE OR REPLACE FUNCTION hash_join_batches (query text) RETURNS TABLE ( original int, final int) LANGUAGE plpgsql AS $$ DECLARE whole_plan json; hash_node json; BEGIN FOR whole_plan IN EXECUTE 'explain (analyze, format ''json'') ' || query LOOP hash_node := find_hash (json_extract_path(whole_plan, '0', 'Plan')); original := hash_node ->> 'Original Hash Batches'; final := hash_node ->> 'Hash Batches'; RETURN NEXT; END LOOP; END; $$; -- Make a simple relation with well distributed keys and correctly -- estimated size. CREATE TABLE simple AS SELECT generate_series(1, 20000) AS id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; ALTER TABLE simple SET (parallel_workers = 2); ANALYZE simple; -- Make a relation whose size we will under-estimate. We want stats -- to say 1000 rows, but actually there are 20,000 rows. CREATE TABLE bigger_than_it_looks AS SELECT generate_series(1, 20000) AS id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; ALTER TABLE bigger_than_it_looks SET (autovacuum_enabled = 'false'); ALTER TABLE bigger_than_it_looks SET (parallel_workers = 2); ANALYZE bigger_than_it_looks; UPDATE pg_class SET reltuples = 1000 WHERE relname = 'bigger_than_it_looks'; -- Make a relation whose size we underestimate and that also has a -- kind of skew that breaks our batching scheme. We want stats to say -- 2 rows, but actually there are 20,000 rows with the same key. CREATE TABLE extremely_skewed ( id int, t text ); ALTER TABLE extremely_skewed SET (autovacuum_enabled = 'false'); ALTER TABLE extremely_skewed SET (parallel_workers = 2); ANALYZE extremely_skewed; INSERT INTO extremely_skewed SELECT 42 AS id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' FROM generate_series(1, 20000); UPDATE pg_class SET reltuples = 2, relpages = pg_relation_size('extremely_skewed') / 8192 WHERE relname = 'extremely_skewed'; -- Make a relation with a couple of enormous tuples. CREATE TABLE wide AS SELECT generate_series(1, 2) AS id, rpad('', 320000, 'x') AS t; ALTER TABLE wide SET (parallel_workers = 2); -- The "optimal" case: the hash table fits in memory; we plan for 1 -- batch, we stick to that number, and peak memory usage stays within -- our work_mem budget -- non-parallel SAVEPOINT settings; SET local max_parallel_workers_per_gather = 0; SET local work_mem = '4MB'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM simple r JOIN simple s USING (id); SELECT count(*) FROM simple r JOIN simple s USING (id); SELECT original > 1 AS initially_multibatch, final > original AS increased_batches FROM hash_join_batches ($$ SELECT count(*) FROM simple r JOIN simple s USING (id); $$); ROLLBACK TO settings; -- parallel with parallel-oblivious hash join SAVEPOINT settings; SET local max_parallel_workers_per_gather = 2; SET local work_mem = '4MB'; SET local enable_parallel_hash = OFF; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM simple r JOIN simple s USING (id); SELECT count(*) FROM simple r JOIN simple s USING (id); SELECT original > 1 AS initially_multibatch, final > original AS increased_batches FROM hash_join_batches ($$ SELECT count(*) FROM simple r JOIN simple s USING (id); $$); ROLLBACK TO settings; -- parallel with parallel-aware hash join SAVEPOINT settings; SET local max_parallel_workers_per_gather = 2; SET local work_mem = '4MB'; SET local enable_parallel_hash = ON; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM simple r JOIN simple s USING (id); SELECT count(*) FROM simple r JOIN simple s USING (id); SELECT original > 1 AS initially_multibatch, final > original AS increased_batches FROM hash_join_batches ($$ SELECT count(*) FROM simple r JOIN simple s USING (id); $$); ROLLBACK TO settings; -- The "good" case: batches required, but we plan the right number; we -- plan for some number of batches, and we stick to that number, and -- peak memory usage says within our work_mem budget -- non-parallel SAVEPOINT settings; SET local max_parallel_workers_per_gather = 0; SET local work_mem = '128kB'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM simple r JOIN simple s USING (id); SELECT count(*) FROM simple r JOIN simple s USING (id); SELECT original > 1 AS initially_multibatch, final > original AS increased_batches FROM hash_join_batches ($$ SELECT count(*) FROM simple r JOIN simple s USING (id); $$); ROLLBACK TO settings; -- parallel with parallel-oblivious hash join SAVEPOINT settings; SET local max_parallel_workers_per_gather = 2; SET local work_mem = '128kB'; SET local enable_parallel_hash = OFF; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM simple r JOIN simple s USING (id); SELECT count(*) FROM simple r JOIN simple s USING (id); SELECT original > 1 AS initially_multibatch, final > original AS increased_batches FROM hash_join_batches ($$ SELECT count(*) FROM simple r JOIN simple s USING (id); $$); ROLLBACK TO settings; -- parallel with parallel-aware hash join SAVEPOINT settings; SET local max_parallel_workers_per_gather = 2; SET local work_mem = '192kB'; SET local enable_parallel_hash = ON; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM simple r JOIN simple s USING (id); SELECT count(*) FROM simple r JOIN simple s USING (id); SELECT original > 1 AS initially_multibatch, final > original AS increased_batches FROM hash_join_batches ($$ SELECT count(*) FROM simple r JOIN simple s USING (id); $$); ROLLBACK TO settings; -- The "bad" case: during execution we need to increase number of -- batches; in this case we plan for 1 batch, and increase at least a -- couple of times, and peak memory usage stays within our work_mem -- budget -- non-parallel SAVEPOINT settings; SET local max_parallel_workers_per_gather = 0; SET local work_mem = '128kB'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); SELECT count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); SELECT original > 1 AS initially_multibatch, final > original AS increased_batches FROM hash_join_batches ($$ SELECT count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); $$); ROLLBACK TO settings; -- parallel with parallel-oblivious hash join SAVEPOINT settings; SET local max_parallel_workers_per_gather = 2; SET local work_mem = '128kB'; SET local enable_parallel_hash = OFF; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); SELECT count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); SELECT original > 1 AS initially_multibatch, final > original AS increased_batches FROM hash_join_batches ($$ SELECT count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); $$); ROLLBACK TO settings; -- parallel with parallel-aware hash join SAVEPOINT settings; SET local max_parallel_workers_per_gather = 1; SET local work_mem = '192kB'; SET local enable_parallel_hash = ON; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); SELECT count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); SELECT original > 1 AS initially_multibatch, final > original AS increased_batches FROM hash_join_batches ($$ SELECT count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); $$); ROLLBACK TO settings; -- The "ugly" case: increasing the number of batches during execution -- doesn't help, so stop trying to fit in work_mem and hope for the -- best; in this case we plan for 1 batch, increases just once and -- then stop increasing because that didn't help at all, so we blow -- right through the work_mem budget and hope for the best... -- non-parallel SAVEPOINT settings; SET local max_parallel_workers_per_gather = 0; SET local work_mem = '128kB'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM simple r JOIN extremely_skewed s USING (id); SELECT count(*) FROM simple r JOIN extremely_skewed s USING (id); SELECT * FROM hash_join_batches ($$ SELECT count(*) FROM simple r JOIN extremely_skewed s USING (id); $$); ROLLBACK TO settings; -- parallel with parallel-oblivious hash join SAVEPOINT settings; SET local max_parallel_workers_per_gather = 2; SET local work_mem = '128kB'; SET local enable_parallel_hash = OFF; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM simple r JOIN extremely_skewed s USING (id); SELECT count(*) FROM simple r JOIN extremely_skewed s USING (id); SELECT * FROM hash_join_batches ($$ SELECT count(*) FROM simple r JOIN extremely_skewed s USING (id); $$); ROLLBACK TO settings; -- parallel with parallel-aware hash join SAVEPOINT settings; SET local max_parallel_workers_per_gather = 1; SET local work_mem = '128kB'; SET local enable_parallel_hash = ON; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM simple r JOIN extremely_skewed s USING (id); SELECT count(*) FROM simple r JOIN extremely_skewed s USING (id); SELECT * FROM hash_join_batches ($$ SELECT count(*) FROM simple r JOIN extremely_skewed s USING (id); $$); ROLLBACK TO settings; -- A couple of other hash join tests unrelated to work_mem management. -- Check that EXPLAIN ANALYZE has data even if the leader doesn't participate SAVEPOINT settings; SET local max_parallel_workers_per_gather = 2; SET local work_mem = '4MB'; SET local parallel_leader_participation = OFF; SELECT * FROM hash_join_batches ($$ SELECT count(*) FROM simple r JOIN simple s USING (id); $$); ROLLBACK TO settings; -- Exercise rescans. We'll turn off parallel_leader_participation so -- that we can check that instrumentation comes back correctly. CREATE TABLE join_foo AS SELECT generate_series(1, 3) AS id, 'xxxxx'::text AS t; ALTER TABLE join_foo SET (parallel_workers = 0); CREATE TABLE join_bar AS SELECT generate_series(1, 10000) AS id, 'xxxxx'::text AS t; ALTER TABLE join_bar SET (parallel_workers = 2); -- multi-batch with rescan, parallel-oblivious SAVEPOINT settings; SET enable_parallel_hash = OFF; SET parallel_leader_participation = OFF; SET min_parallel_table_scan_size = 0; SET parallel_setup_cost = 0; SET parallel_tuple_cost = 0; SET max_parallel_workers_per_gather = 2; SET enable_material = OFF; SET enable_mergejoin = OFF; SET work_mem = '64kB'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM join_foo LEFT JOIN ( SELECT b1.id, b1.t FROM join_bar b1 JOIN join_bar b2 USING (id)) ss ON join_foo.id < ss.id + 1 AND join_foo.id > ss.id - 1; SELECT count(*) FROM join_foo LEFT JOIN ( SELECT b1.id, b1.t FROM join_bar b1 JOIN join_bar b2 USING (id)) ss ON join_foo.id < ss.id + 1 AND join_foo.id > ss.id - 1; SELECT final > 1 AS multibatch FROM hash_join_batches ($$ SELECT count(*) FROM join_foo LEFT JOIN ( SELECT b1.id, b1.t FROM join_bar b1 JOIN join_bar b2 USING (id)) ss ON join_foo.id < ss.id + 1 AND join_foo.id > ss.id - 1; $$); ROLLBACK TO settings; -- single-batch with rescan, parallel-oblivious SAVEPOINT settings; SET enable_parallel_hash = OFF; SET parallel_leader_participation = OFF; SET min_parallel_table_scan_size = 0; SET parallel_setup_cost = 0; SET parallel_tuple_cost = 0; SET max_parallel_workers_per_gather = 2; SET enable_material = OFF; SET enable_mergejoin = OFF; SET work_mem = '4MB'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM join_foo LEFT JOIN ( SELECT b1.id, b1.t FROM join_bar b1 JOIN join_bar b2 USING (id)) ss ON join_foo.id < ss.id + 1 AND join_foo.id > ss.id - 1; SELECT count(*) FROM join_foo LEFT JOIN ( SELECT b1.id, b1.t FROM join_bar b1 JOIN join_bar b2 USING (id)) ss ON join_foo.id < ss.id + 1 AND join_foo.id > ss.id - 1; SELECT final > 1 AS multibatch FROM hash_join_batches ($$ SELECT count(*) FROM join_foo LEFT JOIN ( SELECT b1.id, b1.t FROM join_bar b1 JOIN join_bar b2 USING (id)) ss ON join_foo.id < ss.id + 1 AND join_foo.id > ss.id - 1; $$); ROLLBACK TO settings; -- multi-batch with rescan, parallel-aware SAVEPOINT settings; SET enable_parallel_hash = ON; SET parallel_leader_participation = OFF; SET min_parallel_table_scan_size = 0; SET parallel_setup_cost = 0; SET parallel_tuple_cost = 0; SET max_parallel_workers_per_gather = 2; SET enable_material = OFF; SET enable_mergejoin = OFF; SET work_mem = '64kB'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM join_foo LEFT JOIN ( SELECT b1.id, b1.t FROM join_bar b1 JOIN join_bar b2 USING (id)) ss ON join_foo.id < ss.id + 1 AND join_foo.id > ss.id - 1; SELECT count(*) FROM join_foo LEFT JOIN ( SELECT b1.id, b1.t FROM join_bar b1 JOIN join_bar b2 USING (id)) ss ON join_foo.id < ss.id + 1 AND join_foo.id > ss.id - 1; SELECT final > 1 AS multibatch FROM hash_join_batches ($$ SELECT count(*) FROM join_foo LEFT JOIN ( SELECT b1.id, b1.t FROM join_bar b1 JOIN join_bar b2 USING (id)) ss ON join_foo.id < ss.id + 1 AND join_foo.id > ss.id - 1; $$); ROLLBACK TO settings; -- single-batch with rescan, parallel-aware SAVEPOINT settings; SET enable_parallel_hash = ON; SET parallel_leader_participation = OFF; SET min_parallel_table_scan_size = 0; SET parallel_setup_cost = 0; SET parallel_tuple_cost = 0; SET max_parallel_workers_per_gather = 2; SET enable_material = OFF; SET enable_mergejoin = OFF; SET work_mem = '4MB'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM join_foo LEFT JOIN ( SELECT b1.id, b1.t FROM join_bar b1 JOIN join_bar b2 USING (id)) ss ON join_foo.id < ss.id + 1 AND join_foo.id > ss.id - 1; SELECT count(*) FROM join_foo LEFT JOIN ( SELECT b1.id, b1.t FROM join_bar b1 JOIN join_bar b2 USING (id)) ss ON join_foo.id < ss.id + 1 AND join_foo.id > ss.id - 1; SELECT final > 1 AS multibatch FROM hash_join_batches ($$ SELECT count(*) FROM join_foo LEFT JOIN ( SELECT b1.id, b1.t FROM join_bar b1 JOIN join_bar b2 USING (id)) ss ON join_foo.id < ss.id + 1 AND join_foo.id > ss.id - 1; $$); ROLLBACK TO settings; -- A full outer join where every record is matched. -- non-parallel SAVEPOINT settings; SET local max_parallel_workers_per_gather = 0; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM simple r FULL OUTER JOIN simple s USING (id); SELECT count(*) FROM simple r FULL OUTER JOIN simple s USING (id); ROLLBACK TO settings; -- parallelism not possible with parallel-oblivious outer hash join SAVEPOINT settings; SET local max_parallel_workers_per_gather = 2; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM simple r FULL OUTER JOIN simple s USING (id); SELECT count(*) FROM simple r FULL OUTER JOIN simple s USING (id); ROLLBACK TO settings; -- An full outer join where every record is not matched. -- non-parallel SAVEPOINT settings; SET local max_parallel_workers_per_gather = 0; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM simple r FULL OUTER JOIN simple s ON (r.id = 0 - s.id); SELECT count(*) FROM simple r FULL OUTER JOIN simple s ON (r.id = 0 - s.id); ROLLBACK TO settings; -- parallelism not possible with parallel-oblivious outer hash join SAVEPOINT settings; SET local max_parallel_workers_per_gather = 2; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM simple r FULL OUTER JOIN simple s ON (r.id = 0 - s.id); SELECT count(*) FROM simple r FULL OUTER JOIN simple s ON (r.id = 0 - s.id); ROLLBACK TO settings; -- exercise special code paths for huge tuples (note use of non-strict -- expression and left join required to get the detoasted tuple into -- the hash table) -- parallel with parallel-aware hash join (hits ExecParallelHashLoadTuple and -- sts_puttuple oversized tuple cases because it's multi-batch) SAVEPOINT settings; SET max_parallel_workers_per_gather = 2; SET enable_parallel_hash = ON; SET work_mem = '128kB'; EXPLAIN ( COSTS OFF ) SELECT length(max(s.t)) FROM wide LEFT JOIN ( SELECT id, coalesce(t, '') || '' AS t FROM wide) s USING (id); SELECT length(max(s.t)) FROM wide LEFT JOIN ( SELECT id, coalesce(t, '') || '' AS t FROM wide) s USING (id); SELECT final > 1 AS multibatch FROM hash_join_batches ($$ SELECT length(max(s.t)) FROM wide LEFT JOIN ( SELECT id, coalesce(t, '') || '' AS t FROM wide) s USING (id); $$); ROLLBACK TO settings; ROLLBACK; pgFormatter-4.2/t/pg-test-files/expected/json.sql000066400000000000000000001127531361326045100221040ustar00rootroot00000000000000-- Strings. SELECT '""'::json; -- OK. SELECT $$ '' $$::json; -- ERROR, single quotes are not allowed SELECT '"abc"'::json; -- OK SELECT '"abc'::json; -- ERROR, quotes not closed SELECT '"abc def"'::json; -- ERROR, unescaped newline in string constant SELECT '"\n\"\\"'::json; -- OK, legal escapes SELECT '"\v"'::json; -- ERROR, not a valid JSON escape -- see json_encoding test for input with unicode escapes -- Numbers. SELECT '1'::json; -- OK SELECT '0'::json; -- OK SELECT '01'::json; -- ERROR, not valid according to JSON spec SELECT '0.1'::json; -- OK SELECT '9223372036854775808'::json; -- OK, even though it's too large for int8 SELECT '1e100'::json; -- OK SELECT '1.3e100'::json; -- OK SELECT '1f2'::json; -- ERROR SELECT '0.x1'::json; -- ERROR SELECT '1.3ex100'::json; -- ERROR -- Arrays. SELECT '[]'::json; -- OK SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::json; -- OK SELECT '[1,2]'::json; -- OK SELECT '[1,2,]'::json; -- ERROR, trailing comma SELECT '[1,2'::json; -- ERROR, no closing bracket SELECT '[1,[2]'::json; -- ERROR, no closing bracket -- Objects. SELECT '{}'::json; -- OK SELECT '{"abc"}'::json; -- ERROR, no value SELECT '{"abc":1}'::json; -- OK SELECT '{1:"abc"}'::json; -- ERROR, keys must be strings SELECT '{"abc",1}'::json; -- ERROR, wrong separator SELECT '{"abc"=1}'::json; -- ERROR, totally wrong separator SELECT '{"abc"::1}'::json; -- ERROR, another wrong separator SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::json; -- OK SELECT '{"abc":1:2}'::json; -- ERROR, colon in wrong spot SELECT '{"abc":1,3}'::json; -- ERROR, no value -- Recursion. SET max_stack_depth = '100kB'; SELECT repeat('[', 10000)::json; SELECT repeat('{"a":', 10000)::json; RESET max_stack_depth; -- Miscellaneous stuff. SELECT 'true'::json; -- OK SELECT 'false'::json; -- OK SELECT 'null'::json; -- OK SELECT ' true '::json; -- OK, even with extra whitespace SELECT 'true false'::json; -- ERROR, too many values SELECT 'true, false'::json; -- ERROR, too many values SELECT 'truf'::json; -- ERROR, not a keyword SELECT 'trues'::json; -- ERROR, not a keyword SELECT ''::json; -- ERROR, no value SELECT ' '::json; -- ERROR, no value --constructors -- array_to_json SELECT array_to_json(ARRAY ( SELECT 1 AS a)); SELECT array_to_json(array_agg(q), FALSE) FROM ( SELECT x AS b, x * 2 AS c FROM generate_series(1, 3) x) q; SELECT array_to_json(array_agg(q), TRUE) FROM ( SELECT x AS b, x * 2 AS c FROM generate_series(1, 3) x) q; SELECT array_to_json(array_agg(q), FALSE) FROM ( SELECT $$ a$$ || x AS b, y AS c, ARRAY[ROW (x.*, ARRAY[1, 2, 3]), ROW (y.*, ARRAY[4, 5, 6])] AS z FROM generate_series(1, 2) x, generate_series(4, 5) y) q; SELECT array_to_json(array_agg(x), FALSE) FROM generate_series(5, 10) x; SELECT array_to_json('{{1,5},{99,100}}'::int[]); -- row_to_json SELECT row_to_json(ROW (1, 'foo')); SELECT row_to_json(q) FROM ( SELECT $$ a$$ || x AS b, y AS c, ARRAY[ROW (x.*, ARRAY[1, 2, 3]), ROW (y.*, ARRAY[4, 5, 6])] AS z FROM generate_series(1, 2) x, generate_series(4, 5) y) q; SELECT row_to_json(q, TRUE) FROM ( SELECT $$ a$$ || x AS b, y AS c, ARRAY[ROW (x.*, ARRAY[1, 2, 3]), ROW (y.*, ARRAY[4, 5, 6])] AS z FROM generate_series(1, 2) x, generate_series(4, 5) y) q; CREATE TEMP TABLE ROWS AS SELECT x, 'txt' || x AS y FROM generate_series(1, 3) AS x; SELECT row_to_json(q, TRUE) FROM ROWS q; SELECT row_to_json(ROW (( SELECT array_agg(x) AS d FROM generate_series(5, 10) x)), FALSE); -- anyarray column SELECT to_json(histogram_bounds) histogram_bounds FROM pg_stats WHERE attname = 'tmplname' AND tablename = 'pg_pltemplate'; -- to_json, timestamps SELECT to_json(timestamp '2014-05-28 12:22:35.614298'); BEGIN; SET LOCAL time zone 10.5; SELECT to_json(timestamptz '2014-05-28 12:22:35.614298-04'); SET LOCAL time zone - 8; SELECT to_json(timestamptz '2014-05-28 12:22:35.614298-04'); COMMIT; SELECT to_json(date '2014-05-28'); SELECT to_json(date 'Infinity'); SELECT to_json(date '-Infinity'); SELECT to_json(timestamp 'Infinity'); SELECT to_json(timestamp '-Infinity'); SELECT to_json(timestamptz 'Infinity'); SELECT to_json(timestamptz '-Infinity'); --json_agg SELECT json_agg(q) FROM ( SELECT $$ a$$ || x AS b, y AS c, ARRAY[ROW (x.*, ARRAY[1, 2, 3]), ROW (y.*, ARRAY[4, 5, 6])] AS z FROM generate_series(1, 2) x, generate_series(4, 5) y) q; SELECT json_agg(q ORDER BY x, y) FROM ROWS q; UPDATE ROWS SET x = NULL WHERE x = 1; SELECT json_agg(q ORDER BY x NULLS FIRST, y) FROM ROWS q; -- non-numeric output SELECT row_to_json(q) FROM ( SELECT 'NaN'::float8 AS "float8field") q; SELECT row_to_json(q) FROM ( SELECT 'Infinity'::float8 AS "float8field") q; SELECT row_to_json(q) FROM ( SELECT '-Infinity'::float8 AS "float8field") q; -- json input SELECT row_to_json(q) FROM ( SELECT '{"a":1,"b": [2,3,4,"d","e","f"],"c":{"p":1,"q":2}}'::json AS "jsonfield") q; -- json extraction functions CREATE TEMP TABLE test_json ( json_type text, test_json json ); INSERT INTO test_json VALUES ('scalar', '"a scalar"'), ('array', '["zero", "one","two",null,"four","five", [1,2,3],{"f1":9}]'), ('object', '{"field1":"val1","field2":"val2","field3":null, "field4": 4, "field5": [1,2,3], "field6": {"f1":9}}'); SELECT test_json -> 'x' FROM test_json WHERE json_type = 'scalar'; SELECT test_json -> 'x' FROM test_json WHERE json_type = 'array'; SELECT test_json -> 'x' FROM test_json WHERE json_type = 'object'; SELECT test_json -> 'field2' FROM test_json WHERE json_type = 'object'; SELECT test_json ->> 'field2' FROM test_json WHERE json_type = 'object'; SELECT test_json -> 2 FROM test_json WHERE json_type = 'scalar'; SELECT test_json -> 2 FROM test_json WHERE json_type = 'array'; SELECT test_json -> - 1 FROM test_json WHERE json_type = 'array'; SELECT test_json -> 2 FROM test_json WHERE json_type = 'object'; SELECT test_json ->> 2 FROM test_json WHERE json_type = 'array'; SELECT test_json ->> 6 FROM test_json WHERE json_type = 'array'; SELECT test_json ->> 7 FROM test_json WHERE json_type = 'array'; SELECT test_json ->> 'field4' FROM test_json WHERE json_type = 'object'; SELECT test_json ->> 'field5' FROM test_json WHERE json_type = 'object'; SELECT test_json ->> 'field6' FROM test_json WHERE json_type = 'object'; SELECT json_object_keys(test_json) FROM test_json WHERE json_type = 'scalar'; SELECT json_object_keys(test_json) FROM test_json WHERE json_type = 'array'; SELECT json_object_keys(test_json) FROM test_json WHERE json_type = 'object'; -- test extending object_keys resultset - initial resultset size is 256 SELECT count(*) FROM ( SELECT json_object_keys(json_object(array_agg(g))) FROM ( SELECT unnest(ARRAY['f' || n, n::text]) AS g FROM generate_series(1, 300) AS n) x) y; -- nulls SELECT (test_json -> 'field3') IS NULL AS expect_false FROM test_json WHERE json_type = 'object'; SELECT (test_json ->> 'field3') IS NULL AS expect_true FROM test_json WHERE json_type = 'object'; SELECT (test_json -> 3) IS NULL AS expect_false FROM test_json WHERE json_type = 'array'; SELECT (test_json ->> 3) IS NULL AS expect_true FROM test_json WHERE json_type = 'array'; -- corner cases SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::json -> NULL::text; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::json -> NULL::int; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::json -> 1; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::json -> - 1; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::json -> 'z'; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::json -> ''; SELECT '[{"b": "c"}, {"b": "cc"}]'::json -> 1; SELECT '[{"b": "c"}, {"b": "cc"}]'::json -> 3; SELECT '[{"b": "c"}, {"b": "cc"}]'::json -> 'z'; SELECT '{"a": "c", "b": null}'::json -> 'b'; SELECT '"foo"'::json -> 1; SELECT '"foo"'::json -> 'z'; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::json ->> NULL::text; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::json ->> NULL::int; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::json ->> 1; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::json ->> 'z'; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::json ->> ''; SELECT '[{"b": "c"}, {"b": "cc"}]'::json ->> 1; SELECT '[{"b": "c"}, {"b": "cc"}]'::json ->> 3; SELECT '[{"b": "c"}, {"b": "cc"}]'::json ->> 'z'; SELECT '{"a": "c", "b": null}'::json ->> 'b'; SELECT '"foo"'::json ->> 1; SELECT '"foo"'::json ->> 'z'; -- array length SELECT json_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]'); SELECT json_array_length('[]'); SELECT json_array_length('{"f1":1,"f2":[5,6]}'); SELECT json_array_length('4'); -- each SELECT json_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}'); SELECT * FROM json_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q; SELECT json_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}'); SELECT * FROM json_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q; -- extract_path, extract_path_as_text SELECT json_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}', 'f4', 'f6'); SELECT json_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}', 'f2'); SELECT json_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}', 'f2', 0::text); SELECT json_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}', 'f2', 1::text); SELECT json_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}', 'f4', 'f6'); SELECT json_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}', 'f2'); SELECT json_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}', 'f2', 0::text); SELECT json_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}', 'f2', 1::text); -- extract_path nulls SELECT json_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}', 'f4', 'f5') IS NULL AS expect_false; SELECT json_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}', 'f4', 'f5') IS NULL AS expect_true; SELECT json_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}', 'f4', '3') IS NULL AS expect_false; SELECT json_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}', 'f4', '3') IS NULL AS expect_true; -- extract_path operators SELECT '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json #> ARRAY['f4', 'f6']; SELECT '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json #> ARRAY['f2']; SELECT '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::json #> ARRAY['f2', '0']; SELECT '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::json #> ARRAY['f2', '1']; SELECT '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json #>> ARRAY['f4', 'f6']; SELECT '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json #>> ARRAY['f2']; SELECT '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::json #>> ARRAY['f2', '0']; SELECT '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::json #>> ARRAY['f2', '1']; -- corner cases for same SELECT '{"a": {"b":{"c": "foo"}}}'::json #> '{}'; SELECT '[1,2,3]'::json #> '{}'; SELECT '"foo"'::json #> '{}'; SELECT '42'::json #> '{}'; SELECT 'null'::json #> '{}'; SELECT '{"a": {"b":{"c": "foo"}}}'::json #> ARRAY['a']; SELECT '{"a": {"b":{"c": "foo"}}}'::json #> ARRAY['a', NULL]; SELECT '{"a": {"b":{"c": "foo"}}}'::json #> ARRAY['a', '']; SELECT '{"a": {"b":{"c": "foo"}}}'::json #> ARRAY['a', 'b']; SELECT '{"a": {"b":{"c": "foo"}}}'::json #> ARRAY['a', 'b', 'c']; SELECT '{"a": {"b":{"c": "foo"}}}'::json #> ARRAY['a', 'b', 'c', 'd']; SELECT '{"a": {"b":{"c": "foo"}}}'::json #> ARRAY['a', 'z', 'c']; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::json #> ARRAY['a', '1', 'b']; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::json #> ARRAY['a', 'z', 'b']; SELECT '[{"b": "c"}, {"b": "cc"}]'::json #> ARRAY['1', 'b']; SELECT '[{"b": "c"}, {"b": "cc"}]'::json #> ARRAY['z', 'b']; SELECT '[{"b": "c"}, {"b": null}]'::json #> ARRAY['1', 'b']; SELECT '"foo"'::json #> ARRAY['z']; SELECT '42'::json #> ARRAY['f2']; SELECT '42'::json #> ARRAY['0']; SELECT '{"a": {"b":{"c": "foo"}}}'::json #>> '{}'; SELECT '[1,2,3]'::json #>> '{}'; SELECT '"foo"'::json #>> '{}'; SELECT '42'::json #>> '{}'; SELECT 'null'::json #>> '{}'; SELECT '{"a": {"b":{"c": "foo"}}}'::json #>> ARRAY['a']; SELECT '{"a": {"b":{"c": "foo"}}}'::json #>> ARRAY['a', NULL]; SELECT '{"a": {"b":{"c": "foo"}}}'::json #>> ARRAY['a', '']; SELECT '{"a": {"b":{"c": "foo"}}}'::json #>> ARRAY['a', 'b']; SELECT '{"a": {"b":{"c": "foo"}}}'::json #>> ARRAY['a', 'b', 'c']; SELECT '{"a": {"b":{"c": "foo"}}}'::json #>> ARRAY['a', 'b', 'c', 'd']; SELECT '{"a": {"b":{"c": "foo"}}}'::json #>> ARRAY['a', 'z', 'c']; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::json #>> ARRAY['a', '1', 'b']; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::json #>> ARRAY['a', 'z', 'b']; SELECT '[{"b": "c"}, {"b": "cc"}]'::json #>> ARRAY['1', 'b']; SELECT '[{"b": "c"}, {"b": "cc"}]'::json #>> ARRAY['z', 'b']; SELECT '[{"b": "c"}, {"b": null}]'::json #>> ARRAY['1', 'b']; SELECT '"foo"'::json #>> ARRAY['z']; SELECT '42'::json #>> ARRAY['f2']; SELECT '42'::json #>> ARRAY['0']; -- array_elements SELECT json_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false,"stringy"]'); SELECT * FROM json_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false,"stringy"]') q; SELECT json_array_elements_text('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false,"stringy"]'); SELECT * FROM json_array_elements_text('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false,"stringy"]') q; -- populate_record CREATE TYPE jpop AS ( a text, b int, c timestamp ); CREATE DOMAIN js_int_not_null AS int NOT NULL; CREATE DOMAIN js_int_array_1d AS int[] CHECK (array_length(VALUE, 1) = 3); CREATE DOMAIN js_int_array_2d AS int[][] CHECK (array_length(VALUE, 2) = 3); CREATE TYPE j_unordered_pair AS ( x int, y int ); CREATE DOMAIN j_ordered_pair AS j_unordered_pair CHECK ((value).x <= (value).y); CREATE TYPE jsrec AS ( i int, ia _int4, ia1 int[], ia2 int[][], ia3 int[][][], ia1d js_int_array_1d, ia2d js_int_array_2d, t text, ta text[], c char ( 10), ca char(10)[], ts timestamp, js json, jsb jsonb, jsa json[], rec jpop, reca jpop[]); CREATE TYPE jsrec_i_not_null AS ( i js_int_not_null ); SELECT * FROM json_populate_record(NULL::jpop, '{"a":"blurfl","x":43.2}') q; SELECT * FROM json_populate_record(ROW ('x', 3, '2012-12-31 15:30:56')::jpop, '{"a":"blurfl","x":43.2}') q; SELECT * FROM json_populate_record(NULL::jpop, '{"a":"blurfl","x":43.2}') q; SELECT * FROM json_populate_record(ROW ('x', 3, '2012-12-31 15:30:56')::jpop, '{"a":"blurfl","x":43.2}') q; SELECT * FROM json_populate_record(NULL::jpop, '{"a":[100,200,false],"x":43.2}') q; SELECT * FROM json_populate_record(ROW ('x', 3, '2012-12-31 15:30:56')::jpop, '{"a":[100,200,false],"x":43.2}') q; SELECT * FROM json_populate_record(ROW ('x', 3, '2012-12-31 15:30:56')::jpop, '{"c":[100,200,false],"x":43.2}') q; SELECT * FROM json_populate_record(ROW ('x', 3, '2012-12-31 15:30:56')::jpop, '{}') q; SELECT i FROM json_populate_record(NULL::jsrec_i_not_null, '{"x": 43.2}') q; SELECT i FROM json_populate_record(NULL::jsrec_i_not_null, '{"i": null}') q; SELECT i FROM json_populate_record(NULL::jsrec_i_not_null, '{"i": 12345}') q; SELECT ia FROM json_populate_record(NULL::jsrec, '{"ia": null}') q; SELECT ia FROM json_populate_record(NULL::jsrec, '{"ia": 123}') q; SELECT ia FROM json_populate_record(NULL::jsrec, '{"ia": [1, "2", null, 4]}') q; SELECT ia FROM json_populate_record(NULL::jsrec, '{"ia": [[1, 2], [3, 4]]}') q; SELECT ia FROM json_populate_record(NULL::jsrec, '{"ia": [[1], 2]}') q; SELECT ia FROM json_populate_record(NULL::jsrec, '{"ia": [[1], [2, 3]]}') q; SELECT ia FROM json_populate_record(NULL::jsrec, '{"ia": "{1,2,3}"}') q; SELECT ia1 FROM json_populate_record(NULL::jsrec, '{"ia1": null}') q; SELECT ia1 FROM json_populate_record(NULL::jsrec, '{"ia1": 123}') q; SELECT ia1 FROM json_populate_record(NULL::jsrec, '{"ia1": [1, "2", null, 4]}') q; SELECT ia1 FROM json_populate_record(NULL::jsrec, '{"ia1": [[1, 2, 3]]}') q; SELECT ia1d FROM json_populate_record(NULL::jsrec, '{"ia1d": null}') q; SELECT ia1d FROM json_populate_record(NULL::jsrec, '{"ia1d": 123}') q; SELECT ia1d FROM json_populate_record(NULL::jsrec, '{"ia1d": [1, "2", null, 4]}') q; SELECT ia1d FROM json_populate_record(NULL::jsrec, '{"ia1d": [1, "2", null]}') q; SELECT ia2 FROM json_populate_record(NULL::jsrec, '{"ia2": [1, "2", null, 4]}') q; SELECT ia2 FROM json_populate_record(NULL::jsrec, '{"ia2": [[1, 2], [null, 4]]}') q; SELECT ia2 FROM json_populate_record(NULL::jsrec, '{"ia2": [[], []]}') q; SELECT ia2 FROM json_populate_record(NULL::jsrec, '{"ia2": [[1, 2], [3]]}') q; SELECT ia2 FROM json_populate_record(NULL::jsrec, '{"ia2": [[1, 2], 3, 4]}') q; SELECT ia2d FROM json_populate_record(NULL::jsrec, '{"ia2d": [[1, "2"], [null, 4]]}') q; SELECT ia2d FROM json_populate_record(NULL::jsrec, '{"ia2d": [[1, "2", 3], [null, 5, 6]]}') q; SELECT ia3 FROM json_populate_record(NULL::jsrec, '{"ia3": [1, "2", null, 4]}') q; SELECT ia3 FROM json_populate_record(NULL::jsrec, '{"ia3": [[1, 2], [null, 4]]}') q; SELECT ia3 FROM json_populate_record(NULL::jsrec, '{"ia3": [ [[], []], [[], []], [[], []] ]}') q; SELECT ia3 FROM json_populate_record(NULL::jsrec, '{"ia3": [ [[1, 2]], [[3, 4]] ]}') q; SELECT ia3 FROM json_populate_record(NULL::jsrec, '{"ia3": [ [[1, 2], [3, 4]], [[5, 6], [7, 8]] ]}') q; SELECT ia3 FROM json_populate_record(NULL::jsrec, '{"ia3": [ [[1, 2], [3, 4]], [[5, 6], [7, 8], [9, 10]] ]}') q; SELECT ta FROM json_populate_record(NULL::jsrec, '{"ta": null}') q; SELECT ta FROM json_populate_record(NULL::jsrec, '{"ta": 123}') q; SELECT ta FROM json_populate_record(NULL::jsrec, '{"ta": [1, "2", null, 4]}') q; SELECT ta FROM json_populate_record(NULL::jsrec, '{"ta": [[1, 2, 3], {"k": "v"}]}') q; SELECT c FROM json_populate_record(NULL::jsrec, '{"c": null}') q; SELECT c FROM json_populate_record(NULL::jsrec, '{"c": "aaa"}') q; SELECT c FROM json_populate_record(NULL::jsrec, '{"c": "aaaaaaaaaa"}') q; SELECT c FROM json_populate_record(NULL::jsrec, '{"c": "aaaaaaaaaaaaa"}') q; SELECT ca FROM json_populate_record(NULL::jsrec, '{"ca": null}') q; SELECT ca FROM json_populate_record(NULL::jsrec, '{"ca": 123}') q; SELECT ca FROM json_populate_record(NULL::jsrec, '{"ca": [1, "2", null, 4]}') q; SELECT ca FROM json_populate_record(NULL::jsrec, '{"ca": ["aaaaaaaaaaaaaaaa"]}') q; SELECT ca FROM json_populate_record(NULL::jsrec, '{"ca": [[1, 2, 3], {"k": "v"}]}') q; SELECT js FROM json_populate_record(NULL::jsrec, '{"js": null}') q; SELECT js FROM json_populate_record(NULL::jsrec, '{"js": true}') q; SELECT js FROM json_populate_record(NULL::jsrec, '{"js": 123.45}') q; SELECT js FROM json_populate_record(NULL::jsrec, '{"js": "123.45"}') q; SELECT js FROM json_populate_record(NULL::jsrec, '{"js": "abc"}') q; SELECT js FROM json_populate_record(NULL::jsrec, '{"js": [123, "123", null, {"key": "value"}]}') q; SELECT js FROM json_populate_record(NULL::jsrec, '{"js": {"a": "bbb", "b": null, "c": 123.45}}') q; SELECT jsb FROM json_populate_record(NULL::jsrec, '{"jsb": null}') q; SELECT jsb FROM json_populate_record(NULL::jsrec, '{"jsb": true}') q; SELECT jsb FROM json_populate_record(NULL::jsrec, '{"jsb": 123.45}') q; SELECT jsb FROM json_populate_record(NULL::jsrec, '{"jsb": "123.45"}') q; SELECT jsb FROM json_populate_record(NULL::jsrec, '{"jsb": "abc"}') q; SELECT jsb FROM json_populate_record(NULL::jsrec, '{"jsb": [123, "123", null, {"key": "value"}]}') q; SELECT jsb FROM json_populate_record(NULL::jsrec, '{"jsb": {"a": "bbb", "b": null, "c": 123.45}}') q; SELECT jsa FROM json_populate_record(NULL::jsrec, '{"jsa": null}') q; SELECT jsa FROM json_populate_record(NULL::jsrec, '{"jsa": 123}') q; SELECT jsa FROM json_populate_record(NULL::jsrec, '{"jsa": [1, "2", null, 4]}') q; SELECT jsa FROM json_populate_record(NULL::jsrec, '{"jsa": ["aaa", null, [1, 2, "3", {}], { "k" : "v" }]}') q; SELECT rec FROM json_populate_record(NULL::jsrec, '{"rec": 123}') q; SELECT rec FROM json_populate_record(NULL::jsrec, '{"rec": [1, 2]}') q; SELECT rec FROM json_populate_record(NULL::jsrec, '{"rec": {"a": "abc", "c": "01.02.2003", "x": 43.2}}') q; SELECT rec FROM json_populate_record(NULL::jsrec, '{"rec": "(abc,42,01.02.2003)"}') q; SELECT reca FROM json_populate_record(NULL::jsrec, '{"reca": 123}') q; SELECT reca FROM json_populate_record(NULL::jsrec, '{"reca": [1, 2]}') q; SELECT reca FROM json_populate_record(NULL::jsrec, '{"reca": [{"a": "abc", "b": 456}, null, {"c": "01.02.2003", "x": 43.2}]}') q; SELECT reca FROM json_populate_record(NULL::jsrec, '{"reca": ["(abc,42,01.02.2003)"]}') q; SELECT reca FROM json_populate_record(NULL::jsrec, '{"reca": "{\"(abc,42,01.02.2003)\"}"}') q; SELECT rec FROM json_populate_record(ROW (NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ROW ('x', 3, '2012-12-31 15:30:56')::jpop, NULL)::jsrec, '{"rec": {"a": "abc", "c": "01.02.2003", "x": 43.2}}') q; -- anonymous record type SELECT json_populate_record(NULL::record, '{"x": 0, "y": 1}'); SELECT json_populate_record(ROW (1, 2), '{"f1": 0, "f2": 1}'); -- composite domain SELECT json_populate_record(NULL::j_ordered_pair, '{"x": 0, "y": 1}'); SELECT json_populate_record(ROW (1, 2)::j_ordered_pair, '{"x": 0}'); SELECT json_populate_record(ROW (1, 2)::j_ordered_pair, '{"x": 1, "y": 0}'); -- populate_recordset SELECT * FROM json_populate_recordset(NULL::jpop, '[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM json_populate_recordset(ROW ('def', 99, NULL)::jpop, '[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM json_populate_recordset(NULL::jpop, '[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM json_populate_recordset(ROW ('def', 99, NULL)::jpop, '[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM json_populate_recordset(ROW ('def', 99, NULL)::jpop, '[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM json_populate_recordset(ROW ('def', 99, NULL)::jpop, '[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q; CREATE TYPE jpop2 AS ( a int, b json, c int, d int ); SELECT * FROM json_populate_recordset(NULL::jpop2, '[{"a":2,"c":3,"b":{"z":4},"d":6}]') q; SELECT * FROM json_populate_recordset(NULL::jpop, '[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM json_populate_recordset(ROW ('def', 99, NULL)::jpop, '[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM json_populate_recordset(ROW ('def', 99, NULL)::jpop, '[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q; -- anonymous record type SELECT json_populate_recordset(NULL::record, '[{"x": 0, "y": 1}]'); SELECT json_populate_recordset(ROW (1, 2), '[{"f1": 0, "f2": 1}]'); SELECT i, json_populate_recordset(ROW (i, 50), '[{"f1":"42"},{"f2":"43"}]') FROM ( VALUES (1), (2)) v (i); -- empty array is a corner case SELECT json_populate_recordset(NULL::record, '[]'); SELECT json_populate_recordset(ROW (1, 2), '[]'); SELECT * FROM json_populate_recordset(NULL::jpop, '[]') q; -- composite domain SELECT json_populate_recordset(NULL::j_ordered_pair, '[{"x": 0, "y": 1}]'); SELECT json_populate_recordset(ROW (1, 2)::j_ordered_pair, '[{"x": 0}, {"y": 3}]'); SELECT json_populate_recordset(ROW (1, 2)::j_ordered_pair, '[{"x": 1, "y": 0}]'); -- negative cases where the wrong record type is supplied SELECT * FROM json_populate_recordset(ROW (0::int), '[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text); SELECT * FROM json_populate_recordset(ROW (0::int, 0::int), '[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text); SELECT * FROM json_populate_recordset(ROW (0::int, 0::int, 0::int), '[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text); SELECT * FROM json_populate_recordset(ROW (1000000000::int, 50::int), '[{"b":"2"},{"a":"3"}]') q (a text, b text); -- test type info caching in json_populate_record() CREATE TEMP TABLE jspoptest ( js json ); INSERT INTO jspoptest SELECT '{ "jsa": [1, "2", null, 4], "rec": {"a": "abc", "c": "01.02.2003", "x": 43.2}, "reca": [{"a": "abc", "b": 456}, null, {"c": "01.02.2003", "x": 43.2}] }'::json FROM generate_series(1, 3); SELECT (json_populate_record(NULL::jsrec, js)).* FROM jspoptest; DROP TYPE jsrec; DROP TYPE jsrec_i_not_null; DROP DOMAIN js_int_not_null; DROP DOMAIN js_int_array_1d; DROP DOMAIN js_int_array_2d; DROP DOMAIN j_ordered_pair; DROP TYPE j_unordered_pair; --json_typeof() function SELECT value, json_typeof(value) FROM ( VALUES (json '123.4'), (json '-1'), (json '"foo"'), (json 'true'), (json 'false'), (json 'null'), (json '[1, 2, 3]'), (json '[]'), (json '{"x":"foo", "y":123}'), (json '{}'), (NULL::json)) AS data (value); -- json_build_array, json_build_object, json_object_agg SELECT json_build_array('a', 1, 'b', 1.2, 'c', TRUE, 'd', NULL, 'e', json '{"x": 3, "y": [1,2,3]}'); SELECT json_build_array('a', NULL); -- ok SELECT json_build_array(VARIADIC NULL::text[]); -- ok SELECT json_build_array(VARIADIC '{}'::text[]); -- ok SELECT json_build_array(VARIADIC '{a,b,c}'::text[]); -- ok SELECT json_build_array(VARIADIC ARRAY['a', NULL]::text[]); -- ok SELECT json_build_array(VARIADIC '{1,2,3,4}'::text[]); -- ok SELECT json_build_array(VARIADIC '{1,2,3,4}'::int[]); -- ok SELECT json_build_array(VARIADIC '{{1,4},{2,5},{3,6}}'::int[][]); -- ok SELECT json_build_object('a', 1, 'b', 1.2, 'c', TRUE, 'd', NULL, 'e', json '{"x": 3, "y": [1,2,3]}'); SELECT json_build_object('a', json_build_object('b', FALSE, 'c', 99), 'd', json_build_object('e', ARRAY[9, 8, 7]::int[], 'f', ( SELECT row_to_json(r) FROM ( SELECT relkind, oid::regclass AS name FROM pg_class WHERE relname = 'pg_class') r))); SELECT json_build_object('{a,b,c}'::text[]); -- error SELECT json_build_object('{a,b,c}'::text[], '{d,e,f}'::text[]); -- error, key cannot be array SELECT json_build_object('a', 'b', 'c'); -- error SELECT json_build_object(NULL, 'a'); -- error, key cannot be NULL SELECT json_build_object('a', NULL); -- ok SELECT json_build_object(VARIADIC NULL::text[]); -- ok SELECT json_build_object(VARIADIC '{}'::text[]); -- ok SELECT json_build_object(VARIADIC '{a,b,c}'::text[]); -- error SELECT json_build_object(VARIADIC ARRAY['a', NULL]::text[]); -- ok SELECT json_build_object(VARIADIC ARRAY[NULL, 'a']::text[]); -- error, key cannot be NULL SELECT json_build_object(VARIADIC '{1,2,3,4}'::text[]); -- ok SELECT json_build_object(VARIADIC '{1,2,3,4}'::int[]); -- ok SELECT json_build_object(VARIADIC '{{1,4},{2,5},{3,6}}'::int[][]); -- ok -- empty objects/arrays SELECT json_build_array(); SELECT json_build_object(); -- make sure keys are quoted SELECT json_build_object(1, 2); -- keys must be scalar and not null SELECT json_build_object(NULL, 2); SELECT json_build_object(r, 2) FROM ( SELECT 1 AS a, 2 AS b) r; SELECT json_build_object(json '{"a":1,"b":2}', 3); SELECT json_build_object('{1,2,3}'::int[], 3); CREATE TEMP TABLE foo ( serial_num int, name text, type text ); INSERT INTO foo VALUES (847001, 't15', 'GE1043'); INSERT INTO foo VALUES (847002, 't16', 'GE1043'); INSERT INTO foo VALUES (847003, 'sub-alpha', 'GESS90'); SELECT json_build_object('turbines', json_object_agg(serial_num, json_build_object('name', name, 'type', TYPE))) FROM foo; SELECT json_object_agg(name, TYPE) FROM foo; INSERT INTO foo VALUES (999999, NULL, 'bar'); SELECT json_object_agg(name, TYPE) FROM foo; -- json_object -- empty object, one dimension SELECT json_object('{}'); -- empty object, two dimensions SELECT json_object('{}', '{}'); -- one dimension SELECT json_object('{a,1,b,2,3,NULL,"d e f","a b c"}'); -- same but with two dimensions SELECT json_object('{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}'); -- odd number error SELECT json_object('{a,b,c}'); -- one column error SELECT json_object('{{a},{b}}'); -- too many columns error SELECT json_object('{{a,b,c},{b,c,d}}'); -- too many dimensions error SELECT json_object('{{{a,b},{c,d}},{{b,c},{d,e}}}'); --two argument form of json_object SELECT json_object('{a,b,c,"d e f"}', '{1,2,3,"a b c"}'); -- too many dimensions SELECT json_object('{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}', '{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}'); -- mismatched dimensions SELECT json_object('{a,b,c,"d e f",g}', '{1,2,3,"a b c"}'); SELECT json_object('{a,b,c,"d e f"}', '{1,2,3,"a b c",g}'); -- null key error SELECT json_object('{a,b,NULL,"d e f"}', '{1,2,3,"a b c"}'); -- empty key is allowed SELECT json_object('{a,b,"","d e f"}', '{1,2,3,"a b c"}'); -- json_to_record and json_to_recordset SELECT * FROM json_to_record('{"a":1,"b":"foo","c":"bar"}') AS x (a int, b text, d text); SELECT * FROM json_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar","c":true}]') AS x (a int, b text, c boolean); SELECT * FROM json_to_recordset('[{"a":1,"b":{"d":"foo"},"c":true},{"a":2,"c":false,"b":{"d":"bar"}}]') AS x (a int, b json, c boolean); SELECT *, c IS NULL AS c_is_null FROM json_to_record('{"a":1, "b":{"c":16, "d":2}, "x":8, "ca": ["1 2", 3], "ia": [[1,2],[3,4]], "r": {"a": "aaa", "b": 123}}'::json) AS t (a int, b json, c text, x int, ca char(5)[], ia int[][], r jpop); SELECT *, c IS NULL AS c_is_null FROM json_to_recordset('[{"a":1, "b":{"c":16, "d":2}, "x":8}]'::json) AS t (a int, b json, c text, x int); SELECT * FROM json_to_record('{"ia": null}') AS x (ia _int4); SELECT * FROM json_to_record('{"ia": 123}') AS x (ia _int4); SELECT * FROM json_to_record('{"ia": [1, "2", null, 4]}') AS x (ia _int4); SELECT * FROM json_to_record('{"ia": [[1, 2], [3, 4]]}') AS x (ia _int4); SELECT * FROM json_to_record('{"ia": [[1], 2]}') AS x (ia _int4); SELECT * FROM json_to_record('{"ia": [[1], [2, 3]]}') AS x (ia _int4); SELECT * FROM json_to_record('{"ia2": [1, 2, 3]}') AS x (ia2 int[][]); SELECT * FROM json_to_record('{"ia2": [[1, 2], [3, 4]]}') AS x (ia2 int4[][]); SELECT * FROM json_to_record('{"ia2": [[[1], [2], [3]]]}') AS x (ia2 int4[][]); -- json_strip_nulls SELECT json_strip_nulls (NULL); SELECT json_strip_nulls ('1'); SELECT json_strip_nulls ('"a string"'); SELECT json_strip_nulls ('null'); SELECT json_strip_nulls ('[1,2,null,3,4]'); SELECT json_strip_nulls ('{"a":1,"b":null,"c":[2,null,3],"d":{"e":4,"f":null}}'); SELECT json_strip_nulls ('[1,{"a":1,"b":null,"c":2},3]'); -- an empty object is not null and should not be stripped SELECT json_strip_nulls ('{"a": {"b": null, "c": null}, "d": {} }'); -- json to tsvector SELECT to_tsvector('{"a": "aaa bbb ddd ccc", "b": ["eee fff ggg"], "c": {"d": "hhh iii"}}'::json); -- json to tsvector with config SELECT to_tsvector('simple', '{"a": "aaa bbb ddd ccc", "b": ["eee fff ggg"], "c": {"d": "hhh iii"}}'::json); -- json to tsvector with stop words SELECT to_tsvector('english', '{"a": "aaa in bbb ddd ccc", "b": ["the eee fff ggg"], "c": {"d": "hhh. iii"}}'::json); -- json to tsvector with numeric values SELECT to_tsvector('english', '{"a": "aaa in bbb ddd ccc", "b": 123, "c": 456}'::json); -- json_to_tsvector SELECT json_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"all"'); SELECT json_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"key"'); SELECT json_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"string"'); SELECT json_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"numeric"'); SELECT json_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"boolean"'); SELECT json_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '["string", "numeric"]'); SELECT json_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"all"'); SELECT json_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"key"'); SELECT json_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"string"'); SELECT json_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"numeric"'); SELECT json_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"boolean"'); SELECT json_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '["string", "numeric"]'); -- to_tsvector corner cases SELECT to_tsvector('""'::json); SELECT to_tsvector('{}'::json); SELECT to_tsvector('[]'::json); SELECT to_tsvector('null'::json); -- json_to_tsvector corner cases SELECT json_to_tsvector ('""'::json, '"all"'); SELECT json_to_tsvector ('{}'::json, '"all"'); SELECT json_to_tsvector ('[]'::json, '"all"'); SELECT json_to_tsvector ('null'::json, '"all"'); SELECT json_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '""'); SELECT json_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '{}'); SELECT json_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '[]'); SELECT json_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, 'null'); SELECT json_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '["all", null]'); -- ts_headline for json SELECT ts_headline('{"a": "aaa bbb", "b": {"c": "ccc ddd fff", "c1": "ccc1 ddd1"}, "d": ["ggg hhh", "iii jjj"]}'::json, tsquery ('bbb & ddd & hhh')); SELECT ts_headline('english', '{"a": "aaa bbb", "b": {"c": "ccc ddd fff"}, "d": ["ggg hhh", "iii jjj"]}'::json, tsquery ('bbb & ddd & hhh')); SELECT ts_headline('{"a": "aaa bbb", "b": {"c": "ccc ddd fff", "c1": "ccc1 ddd1"}, "d": ["ggg hhh", "iii jjj"]}'::json, tsquery ('bbb & ddd & hhh'), 'StartSel = <, StopSel = >'); SELECT ts_headline('english', '{"a": "aaa bbb", "b": {"c": "ccc ddd fff", "c1": "ccc1 ddd1"}, "d": ["ggg hhh", "iii jjj"]}'::json, tsquery ('bbb & ddd & hhh'), 'StartSel = <, StopSel = >'); -- corner cases for ts_headline with json SELECT ts_headline('null'::json, tsquery ('aaa & bbb')); SELECT ts_headline('{}'::json, tsquery ('aaa & bbb')); SELECT ts_headline('[]'::json, tsquery ('aaa & bbb')); pgFormatter-4.2/t/pg-test-files/expected/json_encoding.sql000066400000000000000000000064461361326045100237530ustar00rootroot00000000000000-- encoding-sensitive tests for json and jsonb -- first json -- basic unicode input SELECT '"\u"'::json; -- ERROR, incomplete escape SELECT '"\u00"'::json; -- ERROR, incomplete escape SELECT '"\u000g"'::json; -- ERROR, g is not a hex digit SELECT '"\u0000"'::json; -- OK, legal escape SELECT '"\uaBcD"'::json; -- OK, uppercase and lower case both OK -- handling of unicode surrogate pairs SELECT json '{ "a": "\ud83d\ude04\ud83d\udc36" }' -> 'a' AS correct_in_utf8; SELECT json '{ "a": "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row SELECT json '{ "a": "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order SELECT json '{ "a": "\ud83dX" }' -> 'a'; -- orphan high surrogate SELECT json '{ "a": "\ude04X" }' -> 'a'; -- orphan low surrogate --handling of simple unicode escapes SELECT json '{ "a": "the Copyright \u00a9 sign" }' AS correct_in_utf8; SELECT json '{ "a": "dollar \u0024 character" }' AS correct_everywhere; SELECT json '{ "a": "dollar \\u0024 character" }' AS not_an_escape; SELECT json '{ "a": "null \u0000 escape" }' AS not_unescaped; SELECT json '{ "a": "null \\u0000 escape" }' AS not_an_escape; SELECT json '{ "a": "the Copyright \u00a9 sign" }' ->> 'a' AS correct_in_utf8; SELECT json '{ "a": "dollar \u0024 character" }' ->> 'a' AS correct_everywhere; SELECT json '{ "a": "dollar \\u0024 character" }' ->> 'a' AS not_an_escape; SELECT json '{ "a": "null \u0000 escape" }' ->> 'a' AS fails; SELECT json '{ "a": "null \\u0000 escape" }' ->> 'a' AS not_an_escape; -- then jsonb -- basic unicode input SELECT '"\u"'::jsonb; -- ERROR, incomplete escape SELECT '"\u00"'::jsonb; -- ERROR, incomplete escape SELECT '"\u000g"'::jsonb; -- ERROR, g is not a hex digit SELECT '"\u0045"'::jsonb; -- OK, legal escape SELECT '"\u0000"'::jsonb; -- ERROR, we don't support U+0000 -- use octet_length here so we don't get an odd unicode char in the -- output SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK -- handling of unicode surrogate pairs SELECT octet_length((jsonb '{ "a": "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text) AS correct_in_utf8; SELECT jsonb '{ "a": "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row SELECT jsonb '{ "a": "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order SELECT jsonb '{ "a": "\ud83dX" }' -> 'a'; -- orphan high surrogate SELECT jsonb '{ "a": "\ude04X" }' -> 'a'; -- orphan low surrogate -- handling of simple unicode escapes SELECT jsonb '{ "a": "the Copyright \u00a9 sign" }' AS correct_in_utf8; SELECT jsonb '{ "a": "dollar \u0024 character" }' AS correct_everywhere; SELECT jsonb '{ "a": "dollar \\u0024 character" }' AS not_an_escape; SELECT jsonb '{ "a": "null \u0000 escape" }' AS fails; SELECT jsonb '{ "a": "null \\u0000 escape" }' AS not_an_escape; SELECT jsonb '{ "a": "the Copyright \u00a9 sign" }' ->> 'a' AS correct_in_utf8; SELECT jsonb '{ "a": "dollar \u0024 character" }' ->> 'a' AS correct_everywhere; SELECT jsonb '{ "a": "dollar \\u0024 character" }' ->> 'a' AS not_an_escape; SELECT jsonb '{ "a": "null \u0000 escape" }' ->> 'a' AS fails; SELECT jsonb '{ "a": "null \\u0000 escape" }' ->> 'a' AS not_an_escape; pgFormatter-4.2/t/pg-test-files/expected/jsonb.sql000066400000000000000000002070731361326045100222460ustar00rootroot00000000000000-- Strings. SELECT '""'::jsonb; -- OK. SELECT $$ '' $$::jsonb; -- ERROR, single quotes are not allowed SELECT '"abc"'::jsonb; -- OK SELECT '"abc'::jsonb; -- ERROR, quotes not closed SELECT '"abc def"'::jsonb; -- ERROR, unescaped newline in string constant SELECT '"\n\"\\"'::jsonb; -- OK, legal escapes SELECT '"\v"'::jsonb; -- ERROR, not a valid JSON escape -- see json_encoding test for input with unicode escapes -- Numbers. SELECT '1'::jsonb; -- OK SELECT '0'::jsonb; -- OK SELECT '01'::jsonb; -- ERROR, not valid according to JSON spec SELECT '0.1'::jsonb; -- OK SELECT '9223372036854775808'::jsonb; -- OK, even though it's too large for int8 SELECT '1e100'::jsonb; -- OK SELECT '1.3e100'::jsonb; -- OK SELECT '1f2'::jsonb; -- ERROR SELECT '0.x1'::jsonb; -- ERROR SELECT '1.3ex100'::jsonb; -- ERROR -- Arrays. SELECT '[]'::jsonb; -- OK SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb; -- OK SELECT '[1,2]'::jsonb; -- OK SELECT '[1,2,]'::jsonb; -- ERROR, trailing comma SELECT '[1,2'::jsonb; -- ERROR, no closing bracket SELECT '[1,[2]'::jsonb; -- ERROR, no closing bracket -- Objects. SELECT '{}'::jsonb; -- OK SELECT '{"abc"}'::jsonb; -- ERROR, no value SELECT '{"abc":1}'::jsonb; -- OK SELECT '{1:"abc"}'::jsonb; -- ERROR, keys must be strings SELECT '{"abc",1}'::jsonb; -- ERROR, wrong separator SELECT '{"abc"=1}'::jsonb; -- ERROR, totally wrong separator SELECT '{"abc"::1}'::jsonb; -- ERROR, another wrong separator SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK SELECT '{"abc":1:2}'::jsonb; -- ERROR, colon in wrong spot SELECT '{"abc":1,3}'::jsonb; -- ERROR, no value -- Recursion. SET max_stack_depth = '100kB'; SELECT repeat('[', 10000)::jsonb; SELECT repeat('{"a":', 10000)::jsonb; RESET max_stack_depth; -- Miscellaneous stuff. SELECT 'true'::jsonb; -- OK SELECT 'false'::jsonb; -- OK SELECT 'null'::jsonb; -- OK SELECT ' true '::jsonb; -- OK, even with extra whitespace SELECT 'true false'::jsonb; -- ERROR, too many values SELECT 'true, false'::jsonb; -- ERROR, too many values SELECT 'truf'::jsonb; -- ERROR, not a keyword SELECT 'trues'::jsonb; -- ERROR, not a keyword SELECT ''::jsonb; -- ERROR, no value SELECT ' '::jsonb; -- ERROR, no value -- make sure jsonb is passed through json generators without being escaped SELECT array_to_json(ARRAY[jsonb '{"a":1}', jsonb '{"b":[2,3]}']); -- anyarray column SELECT to_jsonb (histogram_bounds) histogram_bounds FROM pg_stats WHERE attname = 'tmplname' AND tablename = 'pg_pltemplate'; -- to_jsonb, timestamps SELECT to_jsonb (timestamp '2014-05-28 12:22:35.614298'); BEGIN; SET LOCAL time zone 10.5; SELECT to_jsonb (timestamptz '2014-05-28 12:22:35.614298-04'); SET LOCAL time zone - 8; SELECT to_jsonb (timestamptz '2014-05-28 12:22:35.614298-04'); COMMIT; SELECT to_jsonb (date '2014-05-28'); SELECT to_jsonb (date 'Infinity'); SELECT to_jsonb (date '-Infinity'); SELECT to_jsonb (timestamp 'Infinity'); SELECT to_jsonb (timestamp '-Infinity'); SELECT to_jsonb (timestamptz 'Infinity'); SELECT to_jsonb (timestamptz '-Infinity'); --jsonb_agg CREATE TEMP TABLE ROWS AS SELECT x, 'txt' || x AS y FROM generate_series(1, 3) AS x; SELECT jsonb_agg(q) FROM ( SELECT $$ a$$ || x AS b, y AS c, ARRAY[ROW (x.*, ARRAY[1, 2, 3]), ROW (y.*, ARRAY[4, 5, 6])] AS z FROM generate_series(1, 2) x, generate_series(4, 5) y) q; SELECT jsonb_agg(q ORDER BY x, y) FROM ROWS q; UPDATE ROWS SET x = NULL WHERE x = 1; SELECT jsonb_agg(q ORDER BY x NULLS FIRST, y) FROM ROWS q; -- jsonb extraction functions CREATE TEMP TABLE test_jsonb ( json_type text, test_json jsonb ); INSERT INTO test_jsonb VALUES ('scalar', '"a scalar"'), ('array', '["zero", "one","two",null,"four","five", [1,2,3],{"f1":9}]'), ('object', '{"field1":"val1","field2":"val2","field3":null, "field4": 4, "field5": [1,2,3], "field6": {"f1":9}}'); SELECT test_json -> 'x' FROM test_jsonb WHERE json_type = 'scalar'; SELECT test_json -> 'x' FROM test_jsonb WHERE json_type = 'array'; SELECT test_json -> 'x' FROM test_jsonb WHERE json_type = 'object'; SELECT test_json -> 'field2' FROM test_jsonb WHERE json_type = 'object'; SELECT test_json ->> 'field2' FROM test_jsonb WHERE json_type = 'scalar'; SELECT test_json ->> 'field2' FROM test_jsonb WHERE json_type = 'array'; SELECT test_json ->> 'field2' FROM test_jsonb WHERE json_type = 'object'; SELECT test_json -> 2 FROM test_jsonb WHERE json_type = 'scalar'; SELECT test_json -> 2 FROM test_jsonb WHERE json_type = 'array'; SELECT test_json -> 9 FROM test_jsonb WHERE json_type = 'array'; SELECT test_json -> 2 FROM test_jsonb WHERE json_type = 'object'; SELECT test_json ->> 6 FROM test_jsonb WHERE json_type = 'array'; SELECT test_json ->> 7 FROM test_jsonb WHERE json_type = 'array'; SELECT test_json ->> 'field4' FROM test_jsonb WHERE json_type = 'object'; SELECT test_json ->> 'field5' FROM test_jsonb WHERE json_type = 'object'; SELECT test_json ->> 'field6' FROM test_jsonb WHERE json_type = 'object'; SELECT test_json ->> 2 FROM test_jsonb WHERE json_type = 'scalar'; SELECT test_json ->> 2 FROM test_jsonb WHERE json_type = 'array'; SELECT test_json ->> 2 FROM test_jsonb WHERE json_type = 'object'; SELECT jsonb_object_keys(test_json) FROM test_jsonb WHERE json_type = 'scalar'; SELECT jsonb_object_keys(test_json) FROM test_jsonb WHERE json_type = 'array'; SELECT jsonb_object_keys(test_json) FROM test_jsonb WHERE json_type = 'object'; -- nulls SELECT (test_json -> 'field3') IS NULL AS expect_false FROM test_jsonb WHERE json_type = 'object'; SELECT (test_json ->> 'field3') IS NULL AS expect_true FROM test_jsonb WHERE json_type = 'object'; SELECT (test_json -> 3) IS NULL AS expect_false FROM test_jsonb WHERE json_type = 'array'; SELECT (test_json ->> 3) IS NULL AS expect_true FROM test_jsonb WHERE json_type = 'array'; -- corner cases SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb -> NULL::text; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb -> NULL::int; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb -> 1; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb -> 'z'; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb -> ''; SELECT '[{"b": "c"}, {"b": "cc"}]'::jsonb -> 1; SELECT '[{"b": "c"}, {"b": "cc"}]'::jsonb -> 3; SELECT '[{"b": "c"}, {"b": "cc"}]'::jsonb -> 'z'; SELECT '{"a": "c", "b": null}'::jsonb -> 'b'; SELECT '"foo"'::jsonb -> 1; SELECT '"foo"'::jsonb -> 'z'; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb ->> NULL::text; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb ->> NULL::int; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb ->> 1; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb ->> 'z'; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb ->> ''; SELECT '[{"b": "c"}, {"b": "cc"}]'::jsonb ->> 1; SELECT '[{"b": "c"}, {"b": "cc"}]'::jsonb ->> 3; SELECT '[{"b": "c"}, {"b": "cc"}]'::jsonb ->> 'z'; SELECT '{"a": "c", "b": null}'::jsonb ->> 'b'; SELECT '"foo"'::jsonb ->> 1; SELECT '"foo"'::jsonb ->> 'z'; -- equality and inequality SELECT '{"x":"y"}'::jsonb = '{"x":"y"}'::jsonb; SELECT '{"x":"y"}'::jsonb = '{"x":"z"}'::jsonb; SELECT '{"x":"y"}'::jsonb <> '{"x":"y"}'::jsonb; SELECT '{"x":"y"}'::jsonb <> '{"x":"z"}'::jsonb; -- containment SELECT jsonb_contains ('{"a":"b", "b":1, "c":null}', '{"a":"b"}'); SELECT jsonb_contains ('{"a":"b", "b":1, "c":null}', '{"a":"b", "c":null}'); SELECT jsonb_contains ('{"a":"b", "b":1, "c":null}', '{"a":"b", "g":null}'); SELECT jsonb_contains ('{"a":"b", "b":1, "c":null}', '{"g":null}'); SELECT jsonb_contains ('{"a":"b", "b":1, "c":null}', '{"a":"c"}'); SELECT jsonb_contains ('{"a":"b", "b":1, "c":null}', '{"a":"b"}'); SELECT jsonb_contains ('{"a":"b", "b":1, "c":null}', '{"a":"b", "c":"q"}'); SELECT '{"a":"b", "b":1, "c":null}'::jsonb @> '{"a":"b"}'; SELECT '{"a":"b", "b":1, "c":null}'::jsonb @> '{"a":"b", "c":null}'; SELECT '{"a":"b", "b":1, "c":null}'::jsonb @> '{"a":"b", "g":null}'; SELECT '{"a":"b", "b":1, "c":null}'::jsonb @> '{"g":null}'; SELECT '{"a":"b", "b":1, "c":null}'::jsonb @> '{"a":"c"}'; SELECT '{"a":"b", "b":1, "c":null}'::jsonb @> '{"a":"b"}'; SELECT '{"a":"b", "b":1, "c":null}'::jsonb @> '{"a":"b", "c":"q"}'; SELECT '[1,2]'::jsonb @> '[1,2,2]'::jsonb; SELECT '[1,1,2]'::jsonb @> '[1,2,2]'::jsonb; SELECT '[[1,2]]'::jsonb @> '[[1,2,2]]'::jsonb; SELECT '[1,2,2]'::jsonb <@ '[1,2]'::jsonb; SELECT '[1,2,2]'::jsonb <@ '[1,1,2]'::jsonb; SELECT '[[1,2,2]]'::jsonb <@ '[[1,2]]'::jsonb; SELECT jsonb_contained ('{"a":"b"}', '{"a":"b", "b":1, "c":null}'); SELECT jsonb_contained ('{"a":"b", "c":null}', '{"a":"b", "b":1, "c":null}'); SELECT jsonb_contained ('{"a":"b", "g":null}', '{"a":"b", "b":1, "c":null}'); SELECT jsonb_contained ('{"g":null}', '{"a":"b", "b":1, "c":null}'); SELECT jsonb_contained ('{"a":"c"}', '{"a":"b", "b":1, "c":null}'); SELECT jsonb_contained ('{"a":"b"}', '{"a":"b", "b":1, "c":null}'); SELECT jsonb_contained ('{"a":"b", "c":"q"}', '{"a":"b", "b":1, "c":null}'); SELECT '{"a":"b"}'::jsonb <@ '{"a":"b", "b":1, "c":null}'; SELECT '{"a":"b", "c":null}'::jsonb <@ '{"a":"b", "b":1, "c":null}'; SELECT '{"a":"b", "g":null}'::jsonb <@ '{"a":"b", "b":1, "c":null}'; SELECT '{"g":null}'::jsonb <@ '{"a":"b", "b":1, "c":null}'; SELECT '{"a":"c"}'::jsonb <@ '{"a":"b", "b":1, "c":null}'; SELECT '{"a":"b"}'::jsonb <@ '{"a":"b", "b":1, "c":null}'; SELECT '{"a":"b", "c":"q"}'::jsonb <@ '{"a":"b", "b":1, "c":null}'; -- Raw scalar may contain another raw scalar, array may contain a raw scalar SELECT '[5]'::jsonb @> '[5]'; SELECT '5'::jsonb @> '5'; SELECT '[5]'::jsonb @> '5'; -- But a raw scalar cannot contain an array SELECT '5'::jsonb @> '[5]'; -- In general, one thing should always contain itself. Test array containment: SELECT '["9", ["7", "3"], 1]'::jsonb @> '["9", ["7", "3"], 1]'::jsonb; SELECT '["9", ["7", "3"], ["1"]]'::jsonb @> '["9", ["7", "3"], ["1"]]'::jsonb; -- array containment string matching confusion bug SELECT '{ "name": "Bob", "tags": [ "enim", "qui"]}'::jsonb @> '{"tags":["qu"]}'; -- array length SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]'); SELECT jsonb_array_length('[]'); SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}'); SELECT jsonb_array_length('4'); -- each SELECT jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}'); SELECT jsonb_each('{"a":{"b":"c","c":"b","1":"first"},"b":[1,2],"c":"cc","1":"first","n":null}'::jsonb) AS q; SELECT * FROM jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q; SELECT * FROM jsonb_each('{"a":{"b":"c","c":"b","1":"first"},"b":[1,2],"c":"cc","1":"first","n":null}'::jsonb) AS q; SELECT jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}'); SELECT jsonb_each_text('{"a":{"b":"c","c":"b","1":"first"},"b":[1,2],"c":"cc","1":"first","n":null}'::jsonb) AS q; SELECT * FROM jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q; SELECT * FROM jsonb_each_text('{"a":{"b":"c","c":"b","1":"first"},"b":[1,2],"c":"cc","1":"first","n":null}'::jsonb) AS q; -- exists SELECT jsonb_exists ('{"a":null, "b":"qq"}', 'a'); SELECT jsonb_exists ('{"a":null, "b":"qq"}', 'b'); SELECT jsonb_exists ('{"a":null, "b":"qq"}', 'c'); SELECT jsonb_exists ('{"a":"null", "b":"qq"}', 'a'); SELECT jsonb '{"a":null, "b":"qq"}' ? 'a'; SELECT jsonb '{"a":null, "b":"qq"}' ? 'b'; SELECT jsonb '{"a":null, "b":"qq"}' ? 'c'; SELECT jsonb '{"a":"null", "b":"qq"}' ? 'a'; -- array exists - array elements should behave as keys SELECT count(*) FROM testjsonb WHERE j -> 'array' ? 'bar'; -- type sensitive array exists - should return no rows (since "exists" only -- matches strings that are either object keys or array elements) SELECT count(*) FROM testjsonb WHERE j -> 'array' ? '5'::text; -- However, a raw scalar is *contained* within the array SELECT count(*) FROM testjsonb WHERE j -> 'array' @> '5'::jsonb; SELECT jsonb_exists_any ('{"a":null, "b":"qq"}', ARRAY['a', 'b']); SELECT jsonb_exists_any ('{"a":null, "b":"qq"}', ARRAY['b', 'a']); SELECT jsonb_exists_any ('{"a":null, "b":"qq"}', ARRAY['c', 'a']); SELECT jsonb_exists_any ('{"a":null, "b":"qq"}', ARRAY['c', 'd']); SELECT jsonb_exists_any ('{"a":null, "b":"qq"}', '{}'::text[]); SELECT jsonb '{"a":null, "b":"qq"}' ? | ARRAY['a', 'b']; SELECT jsonb '{"a":null, "b":"qq"}' ? | ARRAY['b', 'a']; SELECT jsonb '{"a":null, "b":"qq"}' ? | ARRAY['c', 'a']; SELECT jsonb '{"a":null, "b":"qq"}' ? | ARRAY['c', 'd']; SELECT jsonb '{"a":null, "b":"qq"}' ? | '{}'::text[]; SELECT jsonb_exists_all ('{"a":null, "b":"qq"}', ARRAY['a', 'b']); SELECT jsonb_exists_all ('{"a":null, "b":"qq"}', ARRAY['b', 'a']); SELECT jsonb_exists_all ('{"a":null, "b":"qq"}', ARRAY['c', 'a']); SELECT jsonb_exists_all ('{"a":null, "b":"qq"}', ARRAY['c', 'd']); SELECT jsonb_exists_all ('{"a":null, "b":"qq"}', '{}'::text[]); SELECT jsonb '{"a":null, "b":"qq"}' ?& ARRAY['a', 'b']; SELECT jsonb '{"a":null, "b":"qq"}' ?& ARRAY['b', 'a']; SELECT jsonb '{"a":null, "b":"qq"}' ?& ARRAY['c', 'a']; SELECT jsonb '{"a":null, "b":"qq"}' ?& ARRAY['c', 'd']; SELECT jsonb '{"a":null, "b":"qq"}' ?& ARRAY['a', 'a', 'b', 'b', 'b']; SELECT jsonb '{"a":null, "b":"qq"}' ?& '{}'::text[]; -- typeof SELECT jsonb_typeof('{}') AS object; SELECT jsonb_typeof('{"c":3,"p":"o"}') AS object; SELECT jsonb_typeof('[]') AS ARRAY; SELECT jsonb_typeof('["a", 1]') AS ARRAY; SELECT jsonb_typeof('null') AS "null"; SELECT jsonb_typeof('1') AS number; SELECT jsonb_typeof('-1') AS number; SELECT jsonb_typeof('1.0') AS number; SELECT jsonb_typeof('1e2') AS number; SELECT jsonb_typeof('-1.0') AS number; SELECT jsonb_typeof('true') AS boolean; SELECT jsonb_typeof('false') AS boolean; SELECT jsonb_typeof('"hello"') AS string; SELECT jsonb_typeof('"true"') AS string; SELECT jsonb_typeof('"1.0"') AS string; -- jsonb_build_array, jsonb_build_object, jsonb_object_agg SELECT jsonb_build_array ('a', 1, 'b', 1.2, 'c', TRUE, 'd', NULL, 'e', json '{"x": 3, "y": [1,2,3]}'); SELECT jsonb_build_array ('a', NULL); -- ok SELECT jsonb_build_array (VARIADIC NULL::text[]); -- ok SELECT jsonb_build_array (VARIADIC '{}'::text[]); -- ok SELECT jsonb_build_array (VARIADIC '{a,b,c}'::text[]); -- ok SELECT jsonb_build_array (VARIADIC ARRAY['a', NULL]::text[]); -- ok SELECT jsonb_build_array (VARIADIC '{1,2,3,4}'::text[]); -- ok SELECT jsonb_build_array (VARIADIC '{1,2,3,4}'::int[]); -- ok SELECT jsonb_build_array (VARIADIC '{{1,4},{2,5},{3,6}}'::int[][]); -- ok SELECT jsonb_build_object('a', 1, 'b', 1.2, 'c', TRUE, 'd', NULL, 'e', json '{"x": 3, "y": [1,2,3]}'); SELECT jsonb_build_object('a', jsonb_build_object('b', FALSE, 'c', 99), 'd', jsonb_build_object('e', ARRAY[9, 8, 7]::int[], 'f', ( SELECT row_to_json(r) FROM ( SELECT relkind, oid::regclass AS name FROM pg_class WHERE relname = 'pg_class') r))); SELECT jsonb_build_object('{a,b,c}'::text[]); -- error SELECT jsonb_build_object('{a,b,c}'::text[], '{d,e,f}'::text[]); -- error, key cannot be array SELECT jsonb_build_object('a', 'b', 'c'); -- error SELECT jsonb_build_object(NULL, 'a'); -- error, key cannot be NULL SELECT jsonb_build_object('a', NULL); -- ok SELECT jsonb_build_object(VARIADIC NULL::text[]); -- ok SELECT jsonb_build_object(VARIADIC '{}'::text[]); -- ok SELECT jsonb_build_object(VARIADIC '{a,b,c}'::text[]); -- error SELECT jsonb_build_object(VARIADIC ARRAY['a', NULL]::text[]); -- ok SELECT jsonb_build_object(VARIADIC ARRAY[NULL, 'a']::text[]); -- error, key cannot be NULL SELECT jsonb_build_object(VARIADIC '{1,2,3,4}'::text[]); -- ok SELECT jsonb_build_object(VARIADIC '{1,2,3,4}'::int[]); -- ok SELECT jsonb_build_object(VARIADIC '{{1,4},{2,5},{3,6}}'::int[][]); -- ok -- empty objects/arrays SELECT jsonb_build_array (); SELECT jsonb_build_object(); -- make sure keys are quoted SELECT jsonb_build_object(1, 2); -- keys must be scalar and not null SELECT jsonb_build_object(NULL, 2); SELECT jsonb_build_object(r, 2) FROM ( SELECT 1 AS a, 2 AS b) r; SELECT jsonb_build_object(json '{"a":1,"b":2}', 3); SELECT jsonb_build_object('{1,2,3}'::int[], 3); -- handling of NULL values SELECT jsonb_object_agg(1, NULL::jsonb); SELECT jsonb_object_agg(NULL, '{"a":1}'); CREATE TEMP TABLE foo ( serial_num int, name text, type text ); INSERT INTO foo VALUES (847001, 't15', 'GE1043'); INSERT INTO foo VALUES (847002, 't16', 'GE1043'); INSERT INTO foo VALUES (847003, 'sub-alpha', 'GESS90'); SELECT jsonb_build_object('turbines', jsonb_object_agg(serial_num, jsonb_build_object('name', name, 'type', TYPE))) FROM foo; SELECT jsonb_object_agg(name, TYPE) FROM foo; INSERT INTO foo VALUES (999999, NULL, 'bar'); SELECT jsonb_object_agg(name, TYPE) FROM foo; -- jsonb_object -- empty object, one dimension SELECT jsonb_object ('{}'); -- empty object, two dimensions SELECT jsonb_object ('{}', '{}'); -- one dimension SELECT jsonb_object ('{a,1,b,2,3,NULL,"d e f","a b c"}'); -- same but with two dimensions SELECT jsonb_object ('{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}'); -- odd number error SELECT jsonb_object ('{a,b,c}'); -- one column error SELECT jsonb_object ('{{a},{b}}'); -- too many columns error SELECT jsonb_object ('{{a,b,c},{b,c,d}}'); -- too many dimensions error SELECT jsonb_object ('{{{a,b},{c,d}},{{b,c},{d,e}}}'); --two argument form of jsonb_object SELECT jsonb_object ('{a,b,c,"d e f"}', '{1,2,3,"a b c"}'); -- too many dimensions SELECT jsonb_object ('{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}', '{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}'); -- mismatched dimensions SELECT jsonb_object ('{a,b,c,"d e f",g}', '{1,2,3,"a b c"}'); SELECT jsonb_object ('{a,b,c,"d e f"}', '{1,2,3,"a b c",g}'); -- null key error SELECT jsonb_object ('{a,b,NULL,"d e f"}', '{1,2,3,"a b c"}'); -- empty key is allowed SELECT jsonb_object ('{a,b,"","d e f"}', '{1,2,3,"a b c"}'); -- extract_path, extract_path_as_text SELECT jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}', 'f4', 'f6'); SELECT jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}', 'f2'); SELECT jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}', 'f2', 0::text); SELECT jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}', 'f2', 1::text); SELECT jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}', 'f4', 'f6'); SELECT jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}', 'f2'); SELECT jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}', 'f2', 0::text); SELECT jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}', 'f2', 1::text); -- extract_path nulls SELECT jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}', 'f4', 'f5') IS NULL AS expect_false; SELECT jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}', 'f4', 'f5') IS NULL AS expect_true; SELECT jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}', 'f4', '3') IS NULL AS expect_false; SELECT jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}', 'f4', '3') IS NULL AS expect_true; -- extract_path operators SELECT '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb #> ARRAY['f4', 'f6']; SELECT '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb #> ARRAY['f2']; SELECT '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb #> ARRAY['f2', '0']; SELECT '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb #> ARRAY['f2', '1']; SELECT '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb #>> ARRAY['f4', 'f6']; SELECT '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb #>> ARRAY['f2']; SELECT '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb #>> ARRAY['f2', '0']; SELECT '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb #>> ARRAY['f2', '1']; -- corner cases for same SELECT '{"a": {"b":{"c": "foo"}}}'::jsonb #> '{}'; SELECT '[1,2,3]'::jsonb #> '{}'; SELECT '"foo"'::jsonb #> '{}'; SELECT '42'::jsonb #> '{}'; SELECT 'null'::jsonb #> '{}'; SELECT '{"a": {"b":{"c": "foo"}}}'::jsonb #> ARRAY['a']; SELECT '{"a": {"b":{"c": "foo"}}}'::jsonb #> ARRAY['a', NULL]; SELECT '{"a": {"b":{"c": "foo"}}}'::jsonb #> ARRAY['a', '']; SELECT '{"a": {"b":{"c": "foo"}}}'::jsonb #> ARRAY['a', 'b']; SELECT '{"a": {"b":{"c": "foo"}}}'::jsonb #> ARRAY['a', 'b', 'c']; SELECT '{"a": {"b":{"c": "foo"}}}'::jsonb #> ARRAY['a', 'b', 'c', 'd']; SELECT '{"a": {"b":{"c": "foo"}}}'::jsonb #> ARRAY['a', 'z', 'c']; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb #> ARRAY['a', '1', 'b']; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb #> ARRAY['a', 'z', 'b']; SELECT '[{"b": "c"}, {"b": "cc"}]'::jsonb #> ARRAY['1', 'b']; SELECT '[{"b": "c"}, {"b": "cc"}]'::jsonb #> ARRAY['z', 'b']; SELECT '[{"b": "c"}, {"b": null}]'::jsonb #> ARRAY['1', 'b']; SELECT '"foo"'::jsonb #> ARRAY['z']; SELECT '42'::jsonb #> ARRAY['f2']; SELECT '42'::jsonb #> ARRAY['0']; SELECT '{"a": {"b":{"c": "foo"}}}'::jsonb #>> '{}'; SELECT '[1,2,3]'::jsonb #>> '{}'; SELECT '"foo"'::jsonb #>> '{}'; SELECT '42'::jsonb #>> '{}'; SELECT 'null'::jsonb #>> '{}'; SELECT '{"a": {"b":{"c": "foo"}}}'::jsonb #>> ARRAY['a']; SELECT '{"a": {"b":{"c": "foo"}}}'::jsonb #>> ARRAY['a', NULL]; SELECT '{"a": {"b":{"c": "foo"}}}'::jsonb #>> ARRAY['a', '']; SELECT '{"a": {"b":{"c": "foo"}}}'::jsonb #>> ARRAY['a', 'b']; SELECT '{"a": {"b":{"c": "foo"}}}'::jsonb #>> ARRAY['a', 'b', 'c']; SELECT '{"a": {"b":{"c": "foo"}}}'::jsonb #>> ARRAY['a', 'b', 'c', 'd']; SELECT '{"a": {"b":{"c": "foo"}}}'::jsonb #>> ARRAY['a', 'z', 'c']; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb #>> ARRAY['a', '1', 'b']; SELECT '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb #>> ARRAY['a', 'z', 'b']; SELECT '[{"b": "c"}, {"b": "cc"}]'::jsonb #>> ARRAY['1', 'b']; SELECT '[{"b": "c"}, {"b": "cc"}]'::jsonb #>> ARRAY['z', 'b']; SELECT '[{"b": "c"}, {"b": null}]'::jsonb #>> ARRAY['1', 'b']; SELECT '"foo"'::jsonb #>> ARRAY['z']; SELECT '42'::jsonb #>> ARRAY['f2']; SELECT '42'::jsonb #>> ARRAY['0']; -- array_elements SELECT jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]'); SELECT * FROM jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q; SELECT jsonb_array_elements_text('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false,"stringy"]'); SELECT * FROM jsonb_array_elements_text('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false,"stringy"]') q; -- populate_record CREATE TYPE jbpop AS ( a text, b int, c timestamp ); CREATE DOMAIN jsb_int_not_null AS int NOT NULL; CREATE DOMAIN jsb_int_array_1d AS int[] CHECK (array_length(VALUE, 1) = 3); CREATE DOMAIN jsb_int_array_2d AS int[][] CHECK (array_length(VALUE, 2) = 3); CREATE TYPE jb_unordered_pair AS ( x int, y int ); CREATE DOMAIN jb_ordered_pair AS jb_unordered_pair CHECK ((value).x <= (value).y); CREATE TYPE jsbrec AS ( i int, ia _int4, ia1 int[], ia2 int[][], ia3 int[][][], ia1d jsb_int_array_1d, ia2d jsb_int_array_2d, t text, ta text[], c char ( 10), ca char(10)[], ts timestamp, js json, jsb jsonb, jsa json[], rec jbpop, reca jbpop[]); CREATE TYPE jsbrec_i_not_null AS ( i jsb_int_not_null ); SELECT * FROM jsonb_populate_record(NULL::jbpop, '{"a":"blurfl","x":43.2}') q; SELECT * FROM jsonb_populate_record(ROW ('x', 3, '2012-12-31 15:30:56')::jbpop, '{"a":"blurfl","x":43.2}') q; SELECT * FROM jsonb_populate_record(NULL::jbpop, '{"a":"blurfl","x":43.2}') q; SELECT * FROM jsonb_populate_record(ROW ('x', 3, '2012-12-31 15:30:56')::jbpop, '{"a":"blurfl","x":43.2}') q; SELECT * FROM jsonb_populate_record(NULL::jbpop, '{"a":[100,200,false],"x":43.2}') q; SELECT * FROM jsonb_populate_record(ROW ('x', 3, '2012-12-31 15:30:56')::jbpop, '{"a":[100,200,false],"x":43.2}') q; SELECT * FROM jsonb_populate_record(ROW ('x', 3, '2012-12-31 15:30:56')::jbpop, '{"c":[100,200,false],"x":43.2}') q; SELECT * FROM jsonb_populate_record(ROW ('x', 3, '2012-12-31 15:30:56')::jbpop, '{}') q; SELECT i FROM jsonb_populate_record(NULL::jsbrec_i_not_null, '{"x": 43.2}') q; SELECT i FROM jsonb_populate_record(NULL::jsbrec_i_not_null, '{"i": null}') q; SELECT i FROM jsonb_populate_record(NULL::jsbrec_i_not_null, '{"i": 12345}') q; SELECT ia FROM jsonb_populate_record(NULL::jsbrec, '{"ia": null}') q; SELECT ia FROM jsonb_populate_record(NULL::jsbrec, '{"ia": 123}') q; SELECT ia FROM jsonb_populate_record(NULL::jsbrec, '{"ia": [1, "2", null, 4]}') q; SELECT ia FROM jsonb_populate_record(NULL::jsbrec, '{"ia": [[1, 2], [3, 4]]}') q; SELECT ia FROM jsonb_populate_record(NULL::jsbrec, '{"ia": [[1], 2]}') q; SELECT ia FROM jsonb_populate_record(NULL::jsbrec, '{"ia": [[1], [2, 3]]}') q; SELECT ia FROM jsonb_populate_record(NULL::jsbrec, '{"ia": "{1,2,3}"}') q; SELECT ia1 FROM jsonb_populate_record(NULL::jsbrec, '{"ia1": null}') q; SELECT ia1 FROM jsonb_populate_record(NULL::jsbrec, '{"ia1": 123}') q; SELECT ia1 FROM jsonb_populate_record(NULL::jsbrec, '{"ia1": [1, "2", null, 4]}') q; SELECT ia1 FROM jsonb_populate_record(NULL::jsbrec, '{"ia1": [[1, 2, 3]]}') q; SELECT ia1d FROM jsonb_populate_record(NULL::jsbrec, '{"ia1d": null}') q; SELECT ia1d FROM jsonb_populate_record(NULL::jsbrec, '{"ia1d": 123}') q; SELECT ia1d FROM jsonb_populate_record(NULL::jsbrec, '{"ia1d": [1, "2", null, 4]}') q; SELECT ia1d FROM jsonb_populate_record(NULL::jsbrec, '{"ia1d": [1, "2", null]}') q; SELECT ia2 FROM jsonb_populate_record(NULL::jsbrec, '{"ia2": [1, "2", null, 4]}') q; SELECT ia2 FROM jsonb_populate_record(NULL::jsbrec, '{"ia2": [[1, 2], [null, 4]]}') q; SELECT ia2 FROM jsonb_populate_record(NULL::jsbrec, '{"ia2": [[], []]}') q; SELECT ia2 FROM jsonb_populate_record(NULL::jsbrec, '{"ia2": [[1, 2], [3]]}') q; SELECT ia2 FROM jsonb_populate_record(NULL::jsbrec, '{"ia2": [[1, 2], 3, 4]}') q; SELECT ia2d FROM jsonb_populate_record(NULL::jsbrec, '{"ia2d": [[1, "2"], [null, 4]]}') q; SELECT ia2d FROM jsonb_populate_record(NULL::jsbrec, '{"ia2d": [[1, "2", 3], [null, 5, 6]]}') q; SELECT ia3 FROM jsonb_populate_record(NULL::jsbrec, '{"ia3": [1, "2", null, 4]}') q; SELECT ia3 FROM jsonb_populate_record(NULL::jsbrec, '{"ia3": [[1, 2], [null, 4]]}') q; SELECT ia3 FROM jsonb_populate_record(NULL::jsbrec, '{"ia3": [ [[], []], [[], []], [[], []] ]}') q; SELECT ia3 FROM jsonb_populate_record(NULL::jsbrec, '{"ia3": [ [[1, 2]], [[3, 4]] ]}') q; SELECT ia3 FROM jsonb_populate_record(NULL::jsbrec, '{"ia3": [ [[1, 2], [3, 4]], [[5, 6], [7, 8]] ]}') q; SELECT ia3 FROM jsonb_populate_record(NULL::jsbrec, '{"ia3": [ [[1, 2], [3, 4]], [[5, 6], [7, 8], [9, 10]] ]}') q; SELECT ta FROM jsonb_populate_record(NULL::jsbrec, '{"ta": null}') q; SELECT ta FROM jsonb_populate_record(NULL::jsbrec, '{"ta": 123}') q; SELECT ta FROM jsonb_populate_record(NULL::jsbrec, '{"ta": [1, "2", null, 4]}') q; SELECT ta FROM jsonb_populate_record(NULL::jsbrec, '{"ta": [[1, 2, 3], {"k": "v"}]}') q; SELECT c FROM jsonb_populate_record(NULL::jsbrec, '{"c": null}') q; SELECT c FROM jsonb_populate_record(NULL::jsbrec, '{"c": "aaa"}') q; SELECT c FROM jsonb_populate_record(NULL::jsbrec, '{"c": "aaaaaaaaaa"}') q; SELECT c FROM jsonb_populate_record(NULL::jsbrec, '{"c": "aaaaaaaaaaaaa"}') q; SELECT ca FROM jsonb_populate_record(NULL::jsbrec, '{"ca": null}') q; SELECT ca FROM jsonb_populate_record(NULL::jsbrec, '{"ca": 123}') q; SELECT ca FROM jsonb_populate_record(NULL::jsbrec, '{"ca": [1, "2", null, 4]}') q; SELECT ca FROM jsonb_populate_record(NULL::jsbrec, '{"ca": ["aaaaaaaaaaaaaaaa"]}') q; SELECT ca FROM jsonb_populate_record(NULL::jsbrec, '{"ca": [[1, 2, 3], {"k": "v"}]}') q; SELECT js FROM jsonb_populate_record(NULL::jsbrec, '{"js": null}') q; SELECT js FROM jsonb_populate_record(NULL::jsbrec, '{"js": true}') q; SELECT js FROM jsonb_populate_record(NULL::jsbrec, '{"js": 123.45}') q; SELECT js FROM jsonb_populate_record(NULL::jsbrec, '{"js": "123.45"}') q; SELECT js FROM jsonb_populate_record(NULL::jsbrec, '{"js": "abc"}') q; SELECT js FROM jsonb_populate_record(NULL::jsbrec, '{"js": [123, "123", null, {"key": "value"}]}') q; SELECT js FROM jsonb_populate_record(NULL::jsbrec, '{"js": {"a": "bbb", "b": null, "c": 123.45}}') q; SELECT jsb FROM jsonb_populate_record(NULL::jsbrec, '{"jsb": null}') q; SELECT jsb FROM jsonb_populate_record(NULL::jsbrec, '{"jsb": true}') q; SELECT jsb FROM jsonb_populate_record(NULL::jsbrec, '{"jsb": 123.45}') q; SELECT jsb FROM jsonb_populate_record(NULL::jsbrec, '{"jsb": "123.45"}') q; SELECT jsb FROM jsonb_populate_record(NULL::jsbrec, '{"jsb": "abc"}') q; SELECT jsb FROM jsonb_populate_record(NULL::jsbrec, '{"jsb": [123, "123", null, {"key": "value"}]}') q; SELECT jsb FROM jsonb_populate_record(NULL::jsbrec, '{"jsb": {"a": "bbb", "b": null, "c": 123.45}}') q; SELECT jsa FROM jsonb_populate_record(NULL::jsbrec, '{"jsa": null}') q; SELECT jsa FROM jsonb_populate_record(NULL::jsbrec, '{"jsa": 123}') q; SELECT jsa FROM jsonb_populate_record(NULL::jsbrec, '{"jsa": [1, "2", null, 4]}') q; SELECT jsa FROM jsonb_populate_record(NULL::jsbrec, '{"jsa": ["aaa", null, [1, 2, "3", {}], { "k" : "v" }]}') q; SELECT rec FROM jsonb_populate_record(NULL::jsbrec, '{"rec": 123}') q; SELECT rec FROM jsonb_populate_record(NULL::jsbrec, '{"rec": [1, 2]}') q; SELECT rec FROM jsonb_populate_record(NULL::jsbrec, '{"rec": {"a": "abc", "c": "01.02.2003", "x": 43.2}}') q; SELECT rec FROM jsonb_populate_record(NULL::jsbrec, '{"rec": "(abc,42,01.02.2003)"}') q; SELECT reca FROM jsonb_populate_record(NULL::jsbrec, '{"reca": 123}') q; SELECT reca FROM jsonb_populate_record(NULL::jsbrec, '{"reca": [1, 2]}') q; SELECT reca FROM jsonb_populate_record(NULL::jsbrec, '{"reca": [{"a": "abc", "b": 456}, null, {"c": "01.02.2003", "x": 43.2}]}') q; SELECT reca FROM jsonb_populate_record(NULL::jsbrec, '{"reca": ["(abc,42,01.02.2003)"]}') q; SELECT reca FROM jsonb_populate_record(NULL::jsbrec, '{"reca": "{\"(abc,42,01.02.2003)\"}"}') q; SELECT rec FROM jsonb_populate_record(ROW (NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ROW ('x', 3, '2012-12-31 15:30:56')::jbpop, NULL)::jsbrec, '{"rec": {"a": "abc", "c": "01.02.2003", "x": 43.2}}') q; -- anonymous record type SELECT jsonb_populate_record(NULL::record, '{"x": 0, "y": 1}'); SELECT jsonb_populate_record(ROW (1, 2), '{"f1": 0, "f2": 1}'); -- composite domain SELECT jsonb_populate_record(NULL::jb_ordered_pair, '{"x": 0, "y": 1}'); SELECT jsonb_populate_record(ROW (1, 2)::jb_ordered_pair, '{"x": 0}'); SELECT jsonb_populate_record(ROW (1, 2)::jb_ordered_pair, '{"x": 1, "y": 0}'); -- populate_recordset SELECT * FROM jsonb_populate_recordset(NULL::jbpop, '[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM jsonb_populate_recordset(ROW ('def', 99, NULL)::jbpop, '[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM jsonb_populate_recordset(NULL::jbpop, '[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM jsonb_populate_recordset(ROW ('def', 99, NULL)::jbpop, '[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM jsonb_populate_recordset(ROW ('def', 99, NULL)::jbpop, '[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM jsonb_populate_recordset(ROW ('def', 99, NULL)::jbpop, '[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM jsonb_populate_recordset(NULL::jbpop, '[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM jsonb_populate_recordset(ROW ('def', 99, NULL)::jbpop, '[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM jsonb_populate_recordset(ROW ('def', 99, NULL)::jbpop, '[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q; -- anonymous record type SELECT jsonb_populate_recordset(NULL::record, '[{"x": 0, "y": 1}]'); SELECT jsonb_populate_recordset(ROW (1, 2), '[{"f1": 0, "f2": 1}]'); SELECT i, jsonb_populate_recordset(ROW (i, 50), '[{"f1":"42"},{"f2":"43"}]') FROM ( VALUES (1), (2)) v (i); -- empty array is a corner case SELECT jsonb_populate_recordset(NULL::record, '[]'); SELECT jsonb_populate_recordset(ROW (1, 2), '[]'); SELECT * FROM jsonb_populate_recordset(NULL::jbpop, '[]') q; -- composite domain SELECT jsonb_populate_recordset(NULL::jb_ordered_pair, '[{"x": 0, "y": 1}]'); SELECT jsonb_populate_recordset(ROW (1, 2)::jb_ordered_pair, '[{"x": 0}, {"y": 3}]'); SELECT jsonb_populate_recordset(ROW (1, 2)::jb_ordered_pair, '[{"x": 1, "y": 0}]'); -- negative cases where the wrong record type is supplied SELECT * FROM jsonb_populate_recordset(ROW (0::int), '[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text); SELECT * FROM jsonb_populate_recordset(ROW (0::int, 0::int), '[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text); SELECT * FROM jsonb_populate_recordset(ROW (0::int, 0::int, 0::int), '[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text); SELECT * FROM jsonb_populate_recordset(ROW (1000000000::int, 50::int), '[{"b":"2"},{"a":"3"}]') q (a text, b text); -- jsonb_to_record and jsonb_to_recordset SELECT * FROM jsonb_to_record('{"a":1,"b":"foo","c":"bar"}') AS x (a int, b text, d text); SELECT * FROM jsonb_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar","c":true}]') AS x (a int, b text, c boolean); SELECT *, c IS NULL AS c_is_null FROM jsonb_to_record('{"a":1, "b":{"c":16, "d":2}, "x":8, "ca": ["1 2", 3], "ia": [[1,2],[3,4]], "r": {"a": "aaa", "b": 123}}'::jsonb) AS t (a int, b jsonb, c text, x int, ca char(5)[], ia int[][], r jbpop); SELECT *, c IS NULL AS c_is_null FROM jsonb_to_recordset('[{"a":1, "b":{"c":16, "d":2}, "x":8}]'::jsonb) AS t (a int, b jsonb, c text, x int); SELECT * FROM jsonb_to_record('{"ia": null}') AS x (ia _int4); SELECT * FROM jsonb_to_record('{"ia": 123}') AS x (ia _int4); SELECT * FROM jsonb_to_record('{"ia": [1, "2", null, 4]}') AS x (ia _int4); SELECT * FROM jsonb_to_record('{"ia": [[1, 2], [3, 4]]}') AS x (ia _int4); SELECT * FROM jsonb_to_record('{"ia": [[1], 2]}') AS x (ia _int4); SELECT * FROM jsonb_to_record('{"ia": [[1], [2, 3]]}') AS x (ia _int4); SELECT * FROM jsonb_to_record('{"ia2": [1, 2, 3]}') AS x (ia2 int[][]); SELECT * FROM jsonb_to_record('{"ia2": [[1, 2], [3, 4]]}') AS x (ia2 int4[][]); SELECT * FROM jsonb_to_record('{"ia2": [[[1], [2], [3]]]}') AS x (ia2 int4[][]); -- test type info caching in jsonb_populate_record() CREATE TEMP TABLE jsbpoptest ( js jsonb ); INSERT INTO jsbpoptest SELECT '{ "jsa": [1, "2", null, 4], "rec": {"a": "abc", "c": "01.02.2003", "x": 43.2}, "reca": [{"a": "abc", "b": 456}, null, {"c": "01.02.2003", "x": 43.2}] }'::jsonb FROM generate_series(1, 3); SELECT (jsonb_populate_record(NULL::jsbrec, js)).* FROM jsbpoptest; DROP TYPE jsbrec; DROP TYPE jsbrec_i_not_null; DROP DOMAIN jsb_int_not_null; DROP DOMAIN jsb_int_array_1d; DROP DOMAIN jsb_int_array_2d; DROP DOMAIN jb_ordered_pair; DROP TYPE jb_unordered_pair; -- indexing SELECT count(*) FROM testjsonb WHERE j @> '{"wait":null}'; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":"CC"}'; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":"CC", "public":true}'; SELECT count(*) FROM testjsonb WHERE j @> '{"age":25}'; SELECT count(*) FROM testjsonb WHERE j @> '{"age":25.0}'; SELECT count(*) FROM testjsonb WHERE j ? 'public'; SELECT count(*) FROM testjsonb WHERE j ? 'bar'; SELECT count(*) FROM testjsonb WHERE j ? | ARRAY['public', 'disabled']; SELECT count(*) FROM testjsonb WHERE j ?& ARRAY['public', 'disabled']; SELECT count(*) FROM testjsonb WHERE j @@ '$.wait == null'; SELECT count(*) FROM testjsonb WHERE j @@ '"CC" == $.wait'; SELECT count(*) FROM testjsonb WHERE j @@ '$.wait == "CC" && true == $.public'; SELECT count(*) FROM testjsonb WHERE j @@ '$.age == 25'; SELECT count(*) FROM testjsonb WHERE j @@ '$.age == 25.0'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($)'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.public)'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.bar)'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.public) || exists($.disabled)'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.public) && exists($.disabled)'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.wait ? (@ == null)'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.wait ? ("CC" == @)'; SELECT count(*) FROM testjsonb WHERE j @ ? '$ ? (@.wait == "CC" && true == @.public)'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.age ? (@ == 25)'; SELECT count(*) FROM testjsonb WHERE j @ ? '$ ? (@.age == 25.0)'; SELECT count(*) FROM testjsonb WHERE j @ ? '$'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.public'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.bar'; CREATE INDEX jidx ON testjsonb USING gin (j); SET enable_seqscan = OFF; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":null}'; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":"CC"}'; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":"CC", "public":true}'; SELECT count(*) FROM testjsonb WHERE j @> '{"age":25}'; SELECT count(*) FROM testjsonb WHERE j @> '{"age":25.0}'; SELECT count(*) FROM testjsonb WHERE j @> '{"array":["foo"]}'; SELECT count(*) FROM testjsonb WHERE j @> '{"array":["bar"]}'; -- exercise GIN_SEARCH_MODE_ALL SELECT count(*) FROM testjsonb WHERE j @> '{}'; SELECT count(*) FROM testjsonb WHERE j ? 'public'; SELECT count(*) FROM testjsonb WHERE j ? 'bar'; SELECT count(*) FROM testjsonb WHERE j ? | ARRAY['public', 'disabled']; SELECT count(*) FROM testjsonb WHERE j ?& ARRAY['public', 'disabled']; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM testjsonb WHERE j @@ '$.wait == null'; SELECT count(*) FROM testjsonb WHERE j @@ '$.wait == null'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($ ? (@.wait == null))'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.wait ? (@ == null))'; SELECT count(*) FROM testjsonb WHERE j @@ '"CC" == $.wait'; SELECT count(*) FROM testjsonb WHERE j @@ '$.wait == "CC" && true == $.public'; SELECT count(*) FROM testjsonb WHERE j @@ '$.age == 25'; SELECT count(*) FROM testjsonb WHERE j @@ '$.age == 25.0'; SELECT count(*) FROM testjsonb WHERE j @@ '$.array[*] == "foo"'; SELECT count(*) FROM testjsonb WHERE j @@ '$.array[*] == "bar"'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($ ? (@.array[*] == "bar"))'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.array ? (@[*] == "bar"))'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.array[*] ? (@ == "bar"))'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($)'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.public)'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.bar)'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.public) || exists($.disabled)'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.public) && exists($.disabled)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM testjsonb WHERE j @ ? '$.wait ? (@ == null)'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.wait ? (@ == null)'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.wait ? ("CC" == @)'; SELECT count(*) FROM testjsonb WHERE j @ ? '$ ? (@.wait == "CC" && true == @.public)'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.age ? (@ == 25)'; SELECT count(*) FROM testjsonb WHERE j @ ? '$ ? (@.age == 25.0)'; SELECT count(*) FROM testjsonb WHERE j @ ? '$ ? (@.array[*] == "bar")'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.array ? (@[*] == "bar")'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.array[*] ? (@ == "bar")'; SELECT count(*) FROM testjsonb WHERE j @ ? '$'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.public'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.bar'; -- array exists - array elements should behave as keys (for GIN index scans too) CREATE INDEX jidx_array ON testjsonb USING gin ((j -> 'array')); SELECT count(*) FROM testjsonb WHERE j -> 'array' ? 'bar'; -- type sensitive array exists - should return no rows (since "exists" only -- matches strings that are either object keys or array elements) SELECT count(*) FROM testjsonb WHERE j -> 'array' ? '5'::text; -- However, a raw scalar is *contained* within the array SELECT count(*) FROM testjsonb WHERE j -> 'array' @> '5'::jsonb; RESET enable_seqscan; SELECT count(*) FROM ( SELECT (jsonb_each(j)).KEY FROM testjsonb) AS wow; SELECT KEY, count(*) FROM ( SELECT (jsonb_each(j)).KEY FROM testjsonb) AS wow GROUP BY KEY ORDER BY count DESC, KEY; -- sort/hash SELECT count(DISTINCT j) FROM testjsonb; SET enable_hashagg = OFF; SELECT count(*) FROM ( SELECT j FROM ( SELECT * FROM testjsonb UNION ALL SELECT * FROM testjsonb) js GROUP BY j) js2; SET enable_hashagg = ON; SET enable_sort = OFF; SELECT count(*) FROM ( SELECT j FROM ( SELECT * FROM testjsonb UNION ALL SELECT * FROM testjsonb) js GROUP BY j) js2; SELECT DISTINCT * FROM ( VALUES (jsonb '{}' || ''::text), ('{}')) v (j); SET enable_sort = ON; RESET enable_hashagg; RESET enable_sort; DROP INDEX jidx; DROP INDEX jidx_array; -- btree CREATE INDEX jidx ON testjsonb USING btree (j); SET enable_seqscan = OFF; SELECT count(*) FROM testjsonb WHERE j > '{"p":1}'; SELECT count(*) FROM testjsonb WHERE j = '{"pos":98, "line":371, "node":"CBA", "indexed":true}'; --gin path opclass DROP INDEX jidx; CREATE INDEX jidx ON testjsonb USING gin (j jsonb_path_ops); SET enable_seqscan = OFF; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":null}'; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":"CC"}'; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":"CC", "public":true}'; SELECT count(*) FROM testjsonb WHERE j @> '{"age":25}'; SELECT count(*) FROM testjsonb WHERE j @> '{"age":25.0}'; -- exercise GIN_SEARCH_MODE_ALL SELECT count(*) FROM testjsonb WHERE j @> '{}'; SELECT count(*) FROM testjsonb WHERE j @@ '$.wait == null'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($ ? (@.wait == null))'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.wait ? (@ == null))'; SELECT count(*) FROM testjsonb WHERE j @@ '"CC" == $.wait'; SELECT count(*) FROM testjsonb WHERE j @@ '$.wait == "CC" && true == $.public'; SELECT count(*) FROM testjsonb WHERE j @@ '$.age == 25'; SELECT count(*) FROM testjsonb WHERE j @@ '$.age == 25.0'; SELECT count(*) FROM testjsonb WHERE j @@ '$.array[*] == "foo"'; SELECT count(*) FROM testjsonb WHERE j @@ '$.array[*] == "bar"'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($ ? (@.array[*] == "bar"))'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.array ? (@[*] == "bar"))'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.array[*] ? (@ == "bar"))'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($)'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM testjsonb WHERE j @ ? '$.wait ? (@ == null)'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.wait ? (@ == null)'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.wait ? ("CC" == @)'; SELECT count(*) FROM testjsonb WHERE j @ ? '$ ? (@.wait == "CC" && true == @.public)'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.age ? (@ == 25)'; SELECT count(*) FROM testjsonb WHERE j @ ? '$ ? (@.age == 25.0)'; SELECT count(*) FROM testjsonb WHERE j @ ? '$ ? (@.array[*] == "bar")'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.array ? (@[*] == "bar")'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.array[*] ? (@ == "bar")'; SELECT count(*) FROM testjsonb WHERE j @ ? '$'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.public'; SELECT count(*) FROM testjsonb WHERE j @ ? '$.bar'; RESET enable_seqscan; DROP INDEX jidx; -- nested tests SELECT '{"ff":{"a":12,"b":16}}'::jsonb; SELECT '{"ff":{"a":12,"b":16},"qq":123}'::jsonb; SELECT '{"aa":["a","aaa"],"qq":{"a":12,"b":16,"c":["c1","c2"],"d":{"d1":"d1","d2":"d2","d1":"d3"}}}'::jsonb; SELECT '{"aa":["a","aaa"],"qq":{"a":"12","b":"16","c":["c1","c2"],"d":{"d1":"d1","d2":"d2"}}}'::jsonb; SELECT '{"aa":["a","aaa"],"qq":{"a":"12","b":"16","c":["c1","c2",["c3"],{"c4":4}],"d":{"d1":"d1","d2":"d2"}}}'::jsonb; SELECT '{"ff":["a","aaa"]}'::jsonb; SELECT '{"ff":{"a":12,"b":16},"qq":123,"x":[1,2],"Y":null}'::jsonb -> 'ff', '{"ff":{"a":12,"b":16},"qq":123,"x":[1,2],"Y":null}'::jsonb -> 'qq', ('{"ff":{"a":12,"b":16},"qq":123,"x":[1,2],"Y":null}'::jsonb -> 'Y') IS NULL AS f, ('{"ff":{"a":12,"b":16},"qq":123,"x":[1,2],"Y":null}'::jsonb ->> 'Y') IS NULL AS t, '{"ff":{"a":12,"b":16},"qq":123,"x":[1,2],"Y":null}'::jsonb -> 'x'; -- nested containment SELECT '{"a":[1,2],"c":"b"}'::jsonb @> '{"a":[1,2]}'; SELECT '{"a":[2,1],"c":"b"}'::jsonb @> '{"a":[1,2]}'; SELECT '{"a":{"1":2},"c":"b"}'::jsonb @> '{"a":[1,2]}'; SELECT '{"a":{"2":1},"c":"b"}'::jsonb @> '{"a":[1,2]}'; SELECT '{"a":{"1":2},"c":"b"}'::jsonb @> '{"a":{"1":2}}'; SELECT '{"a":{"2":1},"c":"b"}'::jsonb @> '{"a":{"1":2}}'; SELECT '["a","b"]'::jsonb @> '["a","b","c","b"]'; SELECT '["a","b","c","b"]'::jsonb @> '["a","b"]'; SELECT '["a","b","c",[1,2]]'::jsonb @> '["a",[1,2]]'; SELECT '["a","b","c",[1,2]]'::jsonb @> '["b",[1,2]]'; SELECT '{"a":[1,2],"c":"b"}'::jsonb @> '{"a":[1]}'; SELECT '{"a":[1,2],"c":"b"}'::jsonb @> '{"a":[2]}'; SELECT '{"a":[1,2],"c":"b"}'::jsonb @> '{"a":[3]}'; SELECT '{"a":[1,2,{"c":3,"x":4}],"c":"b"}'::jsonb @> '{"a":[{"c":3}]}'; SELECT '{"a":[1,2,{"c":3,"x":4}],"c":"b"}'::jsonb @> '{"a":[{"x":4}]}'; SELECT '{"a":[1,2,{"c":3,"x":4}],"c":"b"}'::jsonb @> '{"a":[{"x":4},3]}'; SELECT '{"a":[1,2,{"c":3,"x":4}],"c":"b"}'::jsonb @> '{"a":[{"x":4},1]}'; -- check some corner cases for indexed nested containment (bug #13756) CREATE temp TABLE nestjsonb ( j jsonb ); INSERT INTO nestjsonb (j) VALUES ('{"a":[["b",{"x":1}],["b",{"x":2}]],"c":3}'); INSERT INTO nestjsonb (j) VALUES ('[[14,2,3]]'); INSERT INTO nestjsonb (j) VALUES ('[1,[14,2,3]]'); CREATE INDEX ON nestjsonb USING gin (j jsonb_path_ops); SET enable_seqscan = ON; SET enable_bitmapscan = OFF; SELECT * FROM nestjsonb WHERE j @> '{"a":[[{"x":2}]]}'::jsonb; SELECT * FROM nestjsonb WHERE j @> '{"c":3}'; SELECT * FROM nestjsonb WHERE j @> '[[14]]'; SET enable_seqscan = OFF; SET enable_bitmapscan = ON; SELECT * FROM nestjsonb WHERE j @> '{"a":[[{"x":2}]]}'::jsonb; SELECT * FROM nestjsonb WHERE j @> '{"c":3}'; SELECT * FROM nestjsonb WHERE j @> '[[14]]'; RESET enable_seqscan; RESET enable_bitmapscan; -- nested object field / array index lookup SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 'n'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 'a'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 'b'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 'c'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 'd'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 'd' -> '1'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 'e'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 0; --expecting error SELECT '["a","b","c",[1,2],null]'::jsonb -> 0; SELECT '["a","b","c",[1,2],null]'::jsonb -> 1; SELECT '["a","b","c",[1,2],null]'::jsonb -> 2; SELECT '["a","b","c",[1,2],null]'::jsonb -> 3; SELECT '["a","b","c",[1,2],null]'::jsonb -> 3 -> 1; SELECT '["a","b","c",[1,2],null]'::jsonb -> 4; SELECT '["a","b","c",[1,2],null]'::jsonb -> 5; SELECT '["a","b","c",[1,2],null]'::jsonb -> - 1; SELECT '["a","b","c",[1,2],null]'::jsonb -> - 5; SELECT '["a","b","c",[1,2],null]'::jsonb -> - 6; --nested path extraction SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{0}'; SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{a}'; SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{c}'; SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{c,0}'; SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{c,1}'; SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{c,2}'; SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{c,3}'; SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{c,-1}'; SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{c,-3}'; SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{c,-4}'; SELECT '[0,1,2,[3,4],{"5":"five"}]'::jsonb #> '{0}'; SELECT '[0,1,2,[3,4],{"5":"five"}]'::jsonb #> '{3}'; SELECT '[0,1,2,[3,4],{"5":"five"}]'::jsonb #> '{4}'; SELECT '[0,1,2,[3,4],{"5":"five"}]'::jsonb #> '{4,5}'; --nested exists SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb ? 'n'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb ? 'a'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb ? 'b'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb ? 'c'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb ? 'd'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb ? 'e'; -- jsonb_strip_nulls SELECT jsonb_strip_nulls (NULL); SELECT jsonb_strip_nulls ('1'); SELECT jsonb_strip_nulls ('"a string"'); SELECT jsonb_strip_nulls ('null'); SELECT jsonb_strip_nulls ('[1,2,null,3,4]'); SELECT jsonb_strip_nulls ('{"a":1,"b":null,"c":[2,null,3],"d":{"e":4,"f":null}}'); SELECT jsonb_strip_nulls ('[1,{"a":1,"b":null,"c":2},3]'); -- an empty object is not null and should not be stripped SELECT jsonb_strip_nulls ('{"a": {"b": null, "c": null}, "d": {} }'); SELECT jsonb_pretty('{"a": "test", "b": [1, 2, 3], "c": "test3", "d":{"dd": "test4", "dd2":{"ddd": "test5"}}}'); SELECT jsonb_pretty('[{"f1":1,"f2":null},2,null,[[{"x":true},6,7],8],3]'); SELECT jsonb_pretty('{"a":["b", "c"], "d": {"e":"f"}}'); SELECT jsonb_concat ('{"d": "test", "a": [1, 2]}', '{"g": "test2", "c": {"c1":1, "c2":2}}'); SELECT '{"aa":1 , "b":2, "cq":3}'::jsonb || '{"cq":"l", "b":"g", "fg":false}'; SELECT '{"aa":1 , "b":2, "cq":3}'::jsonb || '{"aq":"l"}'; SELECT '{"aa":1 , "b":2, "cq":3}'::jsonb || '{"aa":"l"}'; SELECT '{"aa":1 , "b":2, "cq":3}'::jsonb || '{}'; SELECT '["a", "b"]'::jsonb || '["c"]'; SELECT '["a", "b"]'::jsonb || '["c", "d"]'; SELECT '["c"]' || '["a", "b"]'::jsonb; SELECT '["a", "b"]'::jsonb || '"c"'; SELECT '"c"' || '["a", "b"]'::jsonb; SELECT '[]'::jsonb || '["a"]'::jsonb; SELECT '[]'::jsonb || '"a"'::jsonb; SELECT '"b"'::jsonb || '"a"'::jsonb; SELECT '{}'::jsonb || '{"a":"b"}'::jsonb; SELECT '[]'::jsonb || '{"a":"b"}'::jsonb; SELECT '{"a":"b"}'::jsonb || '[]'::jsonb; SELECT '"a"'::jsonb || '{"a":1}'; SELECT '{"a":1}' || '"a"'::jsonb; SELECT '["a", "b"]'::jsonb || '{"c":1}'; SELECT '{"c": 1}'::jsonb || '["a", "b"]'; SELECT '{}'::jsonb || '{"cq":"l", "b":"g", "fg":false}'; SELECT pg_column_size('{}'::jsonb || '{}'::jsonb) = pg_column_size('{}'::jsonb); SELECT pg_column_size('{"aa":1}'::jsonb || '{"b":2}'::jsonb) = pg_column_size('{"aa":1, "b":2}'::jsonb); SELECT pg_column_size('{"aa":1, "b":2}'::jsonb || '{}'::jsonb) = pg_column_size('{"aa":1, "b":2}'::jsonb); SELECT pg_column_size('{}'::jsonb || '{"aa":1, "b":2}'::jsonb) = pg_column_size('{"aa":1, "b":2}'::jsonb); SELECT jsonb_delete ('{"a":1 , "b":2, "c":3}'::jsonb, 'a'); SELECT jsonb_delete ('{"a":null , "b":2, "c":3}'::jsonb, 'a'); SELECT jsonb_delete ('{"a":1 , "b":2, "c":3}'::jsonb, 'b'); SELECT jsonb_delete ('{"a":1 , "b":2, "c":3}'::jsonb, 'c'); SELECT jsonb_delete ('{"a":1 , "b":2, "c":3}'::jsonb, 'd'); SELECT '{"a":1 , "b":2, "c":3}'::jsonb - 'a'; SELECT '{"a":null , "b":2, "c":3}'::jsonb - 'a'; SELECT '{"a":1 , "b":2, "c":3}'::jsonb - 'b'; SELECT '{"a":1 , "b":2, "c":3}'::jsonb - 'c'; SELECT '{"a":1 , "b":2, "c":3}'::jsonb - 'd'; SELECT pg_column_size('{"a":1 , "b":2, "c":3}'::jsonb - 'b') = pg_column_size('{"a":1, "b":2}'::jsonb); SELECT '["a","b","c"]'::jsonb - 3; SELECT '["a","b","c"]'::jsonb - 2; SELECT '["a","b","c"]'::jsonb - 1; SELECT '["a","b","c"]'::jsonb - 0; SELECT '["a","b","c"]'::jsonb - - 1; SELECT '["a","b","c"]'::jsonb - - 2; SELECT '["a","b","c"]'::jsonb - - 3; SELECT '["a","b","c"]'::jsonb - - 4; SELECT '{"a":1 , "b":2, "c":3}'::jsonb - '{b}'::text[]; SELECT '{"a":1 , "b":2, "c":3}'::jsonb - '{c,b}'::text[]; SELECT '{"a":1 , "b":2, "c":3}'::jsonb - '{}'::text[]; SELECT jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '[1,2,3]'); SELECT jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '[1,2,3]'); SELECT jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '[1,2,3]'); SELECT jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '[1,2,3]'); SELECT jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '{"1": 2}'); SELECT jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"1": 2}'); SELECT jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '{"1": 2}'); SELECT jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '{"1": 2}'); SELECT jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '"test"'); SELECT jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"f": "test"}'); SELECT jsonb_delete_path ('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}', '{n}'); SELECT jsonb_delete_path ('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}', '{b,-1}'); SELECT jsonb_delete_path ('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}', '{d,1,0}'); SELECT '{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb # - '{n}'; SELECT '{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb # - '{b,-1}'; SELECT '{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb # - '{b,-1e}'; -- invalid array subscript SELECT '{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb # - '{d,1,0}'; -- empty structure and error conditions for delete and replace SELECT '"a"'::jsonb - 'a'; -- error SELECT '{}'::jsonb - 'a'; SELECT '[]'::jsonb - 'a'; SELECT '"a"'::jsonb - 1; -- error SELECT '{}'::jsonb - 1; -- error SELECT '[]'::jsonb - 1; SELECT '"a"'::jsonb # - '{a}'; -- error SELECT '{}'::jsonb # - '{a}'; SELECT '[]'::jsonb # - '{a}'; SELECT jsonb_set('"a"', '{a}', '"b"'); --error SELECT jsonb_set('{}', '{a}', '"b"', FALSE); SELECT jsonb_set('[]', '{1}', '"b"', FALSE); SELECT jsonb_set('[{"f1":1,"f2":null},2,null,3]', '{0}', '[2,3,4]', FALSE); -- jsonb_set adding instead of replacing -- prepend to array SELECT jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}', '{b,-33}', '{"foo":123}'); -- append to array SELECT jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}', '{b,33}', '{"foo":123}'); -- check nesting levels addition SELECT jsonb_set('{"a":1,"b":[4,5,[0,1,2],6,7],"c":{"d":4}}', '{b,2,33}', '{"foo":123}'); -- add new key SELECT jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}', '{c,e}', '{"foo":123}'); -- adding doesn't do anything if elements before last aren't present SELECT jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}', '{x,-33}', '{"foo":123}'); SELECT jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}', '{x,y}', '{"foo":123}'); -- add to empty object SELECT jsonb_set('{}', '{x}', '{"foo":123}'); --add to empty array SELECT jsonb_set('[]', '{0}', '{"foo":123}'); SELECT jsonb_set('[]', '{99}', '{"foo":123}'); SELECT jsonb_set('[]', '{-99}', '{"foo":123}'); SELECT jsonb_set('{"a": [1, 2, 3]}', '{a, non_integer}', '"new_value"'); SELECT jsonb_set('{"a": {"b": [1, 2, 3]}}', '{a, b, non_integer}', '"new_value"'); SELECT jsonb_set('{"a": {"b": [1, 2, 3]}}', '{a, b, NULL}', '"new_value"'); -- jsonb_insert SELECT jsonb_insert ('{"a": [0,1,2]}', '{a, 1}', '"new_value"'); SELECT jsonb_insert ('{"a": [0,1,2]}', '{a, 1}', '"new_value"', TRUE); SELECT jsonb_insert ('{"a": {"b": {"c": [0, 1, "test1", "test2"]}}}', '{a, b, c, 2}', '"new_value"'); SELECT jsonb_insert ('{"a": {"b": {"c": [0, 1, "test1", "test2"]}}}', '{a, b, c, 2}', '"new_value"', TRUE); SELECT jsonb_insert ('{"a": [0,1,2]}', '{a, 1}', '{"b": "value"}'); SELECT jsonb_insert ('{"a": [0,1,2]}', '{a, 1}', '["value1", "value2"]'); -- edge cases SELECT jsonb_insert ('{"a": [0,1,2]}', '{a, 0}', '"new_value"'); SELECT jsonb_insert ('{"a": [0,1,2]}', '{a, 0}', '"new_value"', TRUE); SELECT jsonb_insert ('{"a": [0,1,2]}', '{a, 2}', '"new_value"'); SELECT jsonb_insert ('{"a": [0,1,2]}', '{a, 2}', '"new_value"', TRUE); SELECT jsonb_insert ('{"a": [0,1,2]}', '{a, -1}', '"new_value"'); SELECT jsonb_insert ('{"a": [0,1,2]}', '{a, -1}', '"new_value"', TRUE); SELECT jsonb_insert ('[]', '{1}', '"new_value"'); SELECT jsonb_insert ('[]', '{1}', '"new_value"', TRUE); SELECT jsonb_insert ('{"a": []}', '{a, 1}', '"new_value"'); SELECT jsonb_insert ('{"a": []}', '{a, 1}', '"new_value"', TRUE); SELECT jsonb_insert ('{"a": [0,1,2]}', '{a, 10}', '"new_value"'); SELECT jsonb_insert ('{"a": [0,1,2]}', '{a, -10}', '"new_value"'); -- jsonb_insert should be able to insert new value for objects, but not to replace SELECT jsonb_insert ('{"a": {"b": "value"}}', '{a, c}', '"new_value"'); SELECT jsonb_insert ('{"a": {"b": "value"}}', '{a, c}', '"new_value"', TRUE); SELECT jsonb_insert ('{"a": {"b": "value"}}', '{a, b}', '"new_value"'); SELECT jsonb_insert ('{"a": {"b": "value"}}', '{a, b}', '"new_value"', TRUE); -- jsonb to tsvector SELECT to_tsvector('{"a": "aaa bbb ddd ccc", "b": ["eee fff ggg"], "c": {"d": "hhh iii"}}'::jsonb); -- jsonb to tsvector with config SELECT to_tsvector('simple', '{"a": "aaa bbb ddd ccc", "b": ["eee fff ggg"], "c": {"d": "hhh iii"}}'::jsonb); -- jsonb to tsvector with stop words SELECT to_tsvector('english', '{"a": "aaa in bbb ddd ccc", "b": ["the eee fff ggg"], "c": {"d": "hhh. iii"}}'::jsonb); -- jsonb to tsvector with numeric values SELECT to_tsvector('english', '{"a": "aaa in bbb ddd ccc", "b": 123, "c": 456}'::jsonb); -- jsonb_to_tsvector SELECT jsonb_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"all"'); SELECT jsonb_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"key"'); SELECT jsonb_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"string"'); SELECT jsonb_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"numeric"'); SELECT jsonb_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"boolean"'); SELECT jsonb_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '["string", "numeric"]'); SELECT jsonb_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"all"'); SELECT jsonb_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"key"'); SELECT jsonb_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"string"'); SELECT jsonb_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"numeric"'); SELECT jsonb_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"boolean"'); SELECT jsonb_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '["string", "numeric"]'); -- to_tsvector corner cases SELECT to_tsvector('""'::jsonb); SELECT to_tsvector('{}'::jsonb); SELECT to_tsvector('[]'::jsonb); SELECT to_tsvector('null'::jsonb); -- jsonb_to_tsvector corner cases SELECT jsonb_to_tsvector ('""'::jsonb, '"all"'); SELECT jsonb_to_tsvector ('{}'::jsonb, '"all"'); SELECT jsonb_to_tsvector ('[]'::jsonb, '"all"'); SELECT jsonb_to_tsvector ('null'::jsonb, '"all"'); SELECT jsonb_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '""'); SELECT jsonb_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '{}'); SELECT jsonb_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '[]'); SELECT jsonb_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, 'null'); SELECT jsonb_to_tsvector ('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '["all", null]'); -- ts_headline for jsonb SELECT ts_headline('{"a": "aaa bbb", "b": {"c": "ccc ddd fff", "c1": "ccc1 ddd1"}, "d": ["ggg hhh", "iii jjj"]}'::jsonb, tsquery ('bbb & ddd & hhh')); SELECT ts_headline('english', '{"a": "aaa bbb", "b": {"c": "ccc ddd fff"}, "d": ["ggg hhh", "iii jjj"]}'::jsonb, tsquery ('bbb & ddd & hhh')); SELECT ts_headline('{"a": "aaa bbb", "b": {"c": "ccc ddd fff", "c1": "ccc1 ddd1"}, "d": ["ggg hhh", "iii jjj"]}'::jsonb, tsquery ('bbb & ddd & hhh'), 'StartSel = <, StopSel = >'); SELECT ts_headline('english', '{"a": "aaa bbb", "b": {"c": "ccc ddd fff", "c1": "ccc1 ddd1"}, "d": ["ggg hhh", "iii jjj"]}'::jsonb, tsquery ('bbb & ddd & hhh'), 'StartSel = <, StopSel = >'); -- corner cases for ts_headline with jsonb SELECT ts_headline('null'::jsonb, tsquery ('aaa & bbb')); SELECT ts_headline('{}'::jsonb, tsquery ('aaa & bbb')); SELECT ts_headline('[]'::jsonb, tsquery ('aaa & bbb')); -- casts SELECT 'true'::jsonb::bool; SELECT '[]'::jsonb::bool; SELECT '1.0'::jsonb::float; SELECT '[1.0]'::jsonb::float; SELECT '12345'::jsonb::int4; SELECT '"hello"'::jsonb::int4; SELECT '12345'::jsonb::numeric; SELECT '{}'::jsonb::numeric; SELECT '12345.05'::jsonb::numeric; SELECT '12345.05'::jsonb::float4; SELECT '12345.05'::jsonb::float8; SELECT '12345.05'::jsonb::int2; SELECT '12345.05'::jsonb::int4; SELECT '12345.05'::jsonb::int8; SELECT '12345.0000000000000000000000000000000000000000000005'::jsonb::numeric; SELECT '12345.0000000000000000000000000000000000000000000005'::jsonb::float4; SELECT '12345.0000000000000000000000000000000000000000000005'::jsonb::float8; SELECT '12345.0000000000000000000000000000000000000000000005'::jsonb::int2; SELECT '12345.0000000000000000000000000000000000000000000005'::jsonb::int4; SELECT '12345.0000000000000000000000000000000000000000000005'::jsonb::int8; pgFormatter-4.2/t/pg-test-files/expected/jsonb_jsonpath.sql000066400000000000000000000567301361326045100241560ustar00rootroot00000000000000SELECT jsonb '{"a": 12}' @ ? '$'; SELECT jsonb '{"a": 12}' @ ? '1'; SELECT jsonb '{"a": 12}' @ ? '$.a.b'; SELECT jsonb '{"a": 12}' @ ? '$.b'; SELECT jsonb '{"a": 12}' @ ? '$.a + 2'; SELECT jsonb '{"a": 12}' @ ? '$.b + 2'; SELECT jsonb '{"a": {"a": 12}}' @ ? '$.a.a'; SELECT jsonb '{"a": {"a": 12}}' @ ? '$.*.a'; SELECT jsonb '{"b": {"a": 12}}' @ ? '$.*.a'; SELECT jsonb '{"b": {"a": 12}}' @ ? '$.*.b'; SELECT jsonb '{"b": {"a": 12}}' @ ? 'strict $.*.b'; SELECT jsonb '{}' @ ? '$.*'; SELECT jsonb '{"a": 1}' @ ? '$.*'; SELECT jsonb '{"a": {"b": 1}}' @ ? 'lax $.**{1}'; SELECT jsonb '{"a": {"b": 1}}' @ ? 'lax $.**{2}'; SELECT jsonb '{"a": {"b": 1}}' @ ? 'lax $.**{3}'; SELECT jsonb '[]' @ ? '$[*]'; SELECT jsonb '[1]' @ ? '$[*]'; SELECT jsonb '[1]' @ ? '$[1]'; SELECT jsonb '[1]' @ ? 'strict $[1]'; SELECT jsonb_path_query('[1]', 'strict $[1]'); SELECT jsonb_path_query('[1]', 'strict $[1]', silent => TRUE); SELECT jsonb '[1]' @ ? 'lax $[10000000000000000]'; SELECT jsonb '[1]' @ ? 'strict $[10000000000000000]'; SELECT jsonb_path_query('[1]', 'lax $[10000000000000000]'); SELECT jsonb_path_query('[1]', 'strict $[10000000000000000]'); SELECT jsonb '[1]' @ ? '$[0]'; SELECT jsonb '[1]' @ ? '$[0.3]'; SELECT jsonb '[1]' @ ? '$[0.5]'; SELECT jsonb '[1]' @ ? '$[0.9]'; SELECT jsonb '[1]' @ ? '$[1.2]'; SELECT jsonb '[1]' @ ? 'strict $[1.2]'; SELECT jsonb '{"a": [1,2,3], "b": [3,4,5]}' @ ? '$ ? (@.a[*] > @.b[*])'; SELECT jsonb '{"a": [1,2,3], "b": [3,4,5]}' @ ? '$ ? (@.a[*] >= @.b[*])'; SELECT jsonb '{"a": [1,2,3], "b": [3,4,"5"]}' @ ? '$ ? (@.a[*] >= @.b[*])'; SELECT jsonb '{"a": [1,2,3], "b": [3,4,"5"]}' @ ? 'strict $ ? (@.a[*] >= @.b[*])'; SELECT jsonb '{"a": [1,2,3], "b": [3,4,null]}' @ ? '$ ? (@.a[*] >= @.b[*])'; SELECT jsonb '1' @ ? '$ ? ((@ == "1") is unknown)'; SELECT jsonb '1' @ ? '$ ? ((@ == 1) is unknown)'; SELECT jsonb '[{"a": 1}, {"a": 2}]' @ ? '$[0 to 1] ? (@.a > 1)'; SELECT jsonb_path_exists ('[{"a": 1}, {"a": 2}, 3]', 'lax $[*].a', silent => FALSE); SELECT jsonb_path_exists ('[{"a": 1}, {"a": 2}, 3]', 'lax $[*].a', silent => TRUE); SELECT jsonb_path_exists ('[{"a": 1}, {"a": 2}, 3]', 'strict $[*].a', silent => FALSE); SELECT jsonb_path_exists ('[{"a": 1}, {"a": 2}, 3]', 'strict $[*].a', silent => TRUE); SELECT jsonb_path_query('1', 'lax $.a'); SELECT jsonb_path_query('1', 'strict $.a'); SELECT jsonb_path_query('1', 'strict $.*'); SELECT jsonb_path_query('1', 'strict $.a', silent => TRUE); SELECT jsonb_path_query('1', 'strict $.*', silent => TRUE); SELECT jsonb_path_query('[]', 'lax $.a'); SELECT jsonb_path_query('[]', 'strict $.a'); SELECT jsonb_path_query('[]', 'strict $.a', silent => TRUE); SELECT jsonb_path_query('{}', 'lax $.a'); SELECT jsonb_path_query('{}', 'strict $.a'); SELECT jsonb_path_query('{}', 'strict $.a', silent => TRUE); SELECT jsonb_path_query('1', 'strict $[1]'); SELECT jsonb_path_query('1', 'strict $[*]'); SELECT jsonb_path_query('[]', 'strict $[1]'); SELECT jsonb_path_query('[]', 'strict $["a"]'); SELECT jsonb_path_query('1', 'strict $[1]', silent => TRUE); SELECT jsonb_path_query('1', 'strict $[*]', silent => TRUE); SELECT jsonb_path_query('[]', 'strict $[1]', silent => TRUE); SELECT jsonb_path_query('[]', 'strict $["a"]', silent => TRUE); SELECT jsonb_path_query('{"a": 12, "b": {"a": 13}}', '$.a'); SELECT jsonb_path_query('{"a": 12, "b": {"a": 13}}', '$.b'); SELECT jsonb_path_query('{"a": 12, "b": {"a": 13}}', '$.*'); SELECT jsonb_path_query('{"a": 12, "b": {"a": 13}}', 'lax $.*.a'); SELECT jsonb_path_query('[12, {"a": 13}, {"b": 14}]', 'lax $[*].a'); SELECT jsonb_path_query('[12, {"a": 13}, {"b": 14}]', 'lax $[*].*'); SELECT jsonb_path_query('[12, {"a": 13}, {"b": 14}]', 'lax $[0].a'); SELECT jsonb_path_query('[12, {"a": 13}, {"b": 14}]', 'lax $[1].a'); SELECT jsonb_path_query('[12, {"a": 13}, {"b": 14}]', 'lax $[2].a'); SELECT jsonb_path_query('[12, {"a": 13}, {"b": 14}]', 'lax $[0,1].a'); SELECT jsonb_path_query('[12, {"a": 13}, {"b": 14}]', 'lax $[0 to 10].a'); SELECT jsonb_path_query('[12, {"a": 13}, {"b": 14}]', 'lax $[0 to 10 / 0].a'); SELECT jsonb_path_query('[12, {"a": 13}, {"b": 14}, "ccc", true]', '$[2.5 - 1 to $.size() - 2]'); SELECT jsonb_path_query('1', 'lax $[0]'); SELECT jsonb_path_query('1', 'lax $[*]'); SELECT jsonb_path_query('[1]', 'lax $[0]'); SELECT jsonb_path_query('[1]', 'lax $[*]'); SELECT jsonb_path_query('[1,2,3]', 'lax $[*]'); SELECT jsonb_path_query('[1,2,3]', 'strict $[*].a'); SELECT jsonb_path_query('[1,2,3]', 'strict $[*].a', silent => TRUE); SELECT jsonb_path_query('[]', '$[last]'); SELECT jsonb_path_query('[]', '$[last ? (exists(last))]'); SELECT jsonb_path_query('[]', 'strict $[last]'); SELECT jsonb_path_query('[]', 'strict $[last]', silent => TRUE); SELECT jsonb_path_query('[1]', '$[last]'); SELECT jsonb_path_query('[1,2,3]', '$[last]'); SELECT jsonb_path_query('[1,2,3]', '$[last - 1]'); SELECT jsonb_path_query('[1,2,3]', '$[last ? (@.type() == "number")]'); SELECT jsonb_path_query('[1,2,3]', '$[last ? (@.type() == "string")]'); SELECT jsonb_path_query('[1,2,3]', '$[last ? (@.type() == "string")]', silent => TRUE); SELECT * FROM jsonb_path_query('{"a": 10}', '$'); SELECT * FROM jsonb_path_query('{"a": 10}', '$ ? (@.a < $value)'); SELECT * FROM jsonb_path_query('{"a": 10}', '$ ? (@.a < $value)', '1'); SELECT * FROM jsonb_path_query('{"a": 10}', '$ ? (@.a < $value)', '[{"value" : 13}]'); SELECT * FROM jsonb_path_query('{"a": 10}', '$ ? (@.a < $value)', '{"value" : 13}'); SELECT * FROM jsonb_path_query('{"a": 10}', '$ ? (@.a < $value)', '{"value" : 8}'); SELECT * FROM jsonb_path_query('{"a": 10}', '$.a ? (@ < $value)', '{"value" : 13}'); SELECT * FROM jsonb_path_query('[10,11,12,13,14,15]', '$[*] ? (@ < $value)', '{"value" : 13}'); SELECT * FROM jsonb_path_query('[10,11,12,13,14,15]', '$[0,1] ? (@ < $x.value)', '{"x": {"value" : 13}}'); SELECT * FROM jsonb_path_query('[10,11,12,13,14,15]', '$[0 to 2] ? (@ < $value)', '{"value" : 15}'); SELECT * FROM jsonb_path_query('[1,"1",2,"2",null]', '$[*] ? (@ == "1")'); SELECT * FROM jsonb_path_query('[1,"1",2,"2",null]', '$[*] ? (@ == $value)', '{"value" : "1"}'); SELECT * FROM jsonb_path_query('[1,"1",2,"2",null]', '$[*] ? (@ == $value)', '{"value" : null}'); SELECT * FROM jsonb_path_query('[1, "2", null]', '$[*] ? (@ != null)'); SELECT * FROM jsonb_path_query('[1, "2", null]', '$[*] ? (@ == null)'); SELECT * FROM jsonb_path_query('{}', '$ ? (@ == @)'); SELECT * FROM jsonb_path_query('[]', 'strict $ ? (@ == @)'); SELECT jsonb_path_query('{"a": {"b": 1}}', 'lax $.**'); SELECT jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{0}'); SELECT jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{0 to last}'); SELECT jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{1}'); SELECT jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{1 to last}'); SELECT jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{2}'); SELECT jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{2 to last}'); SELECT jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{3 to last}'); SELECT jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{last}'); SELECT jsonb_path_query('{"a": {"b": 1}}', 'lax $.**.b ? (@ > 0)'); SELECT jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{0}.b ? (@ > 0)'); SELECT jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{1}.b ? (@ > 0)'); SELECT jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{0 to last}.b ? (@ > 0)'); SELECT jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{1 to last}.b ? (@ > 0)'); SELECT jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{1 to 2}.b ? (@ > 0)'); SELECT jsonb_path_query('{"a": {"c": {"b": 1}}}', 'lax $.**.b ? (@ > 0)'); SELECT jsonb_path_query('{"a": {"c": {"b": 1}}}', 'lax $.**{0}.b ? (@ > 0)'); SELECT jsonb_path_query('{"a": {"c": {"b": 1}}}', 'lax $.**{1}.b ? (@ > 0)'); SELECT jsonb_path_query('{"a": {"c": {"b": 1}}}', 'lax $.**{0 to last}.b ? (@ > 0)'); SELECT jsonb_path_query('{"a": {"c": {"b": 1}}}', 'lax $.**{1 to last}.b ? (@ > 0)'); SELECT jsonb_path_query('{"a": {"c": {"b": 1}}}', 'lax $.**{1 to 2}.b ? (@ > 0)'); SELECT jsonb_path_query('{"a": {"c": {"b": 1}}}', 'lax $.**{2 to 3}.b ? (@ > 0)'); SELECT jsonb '{"a": {"b": 1}}' @ ? '$.**.b ? ( @ > 0)'; SELECT jsonb '{"a": {"b": 1}}' @ ? '$.**{0}.b ? ( @ > 0)'; SELECT jsonb '{"a": {"b": 1}}' @ ? '$.**{1}.b ? ( @ > 0)'; SELECT jsonb '{"a": {"b": 1}}' @ ? '$.**{0 to last}.b ? ( @ > 0)'; SELECT jsonb '{"a": {"b": 1}}' @ ? '$.**{1 to last}.b ? ( @ > 0)'; SELECT jsonb '{"a": {"b": 1}}' @ ? '$.**{1 to 2}.b ? ( @ > 0)'; SELECT jsonb '{"a": {"c": {"b": 1}}}' @ ? '$.**.b ? ( @ > 0)'; SELECT jsonb '{"a": {"c": {"b": 1}}}' @ ? '$.**{0}.b ? ( @ > 0)'; SELECT jsonb '{"a": {"c": {"b": 1}}}' @ ? '$.**{1}.b ? ( @ > 0)'; SELECT jsonb '{"a": {"c": {"b": 1}}}' @ ? '$.**{0 to last}.b ? ( @ > 0)'; SELECT jsonb '{"a": {"c": {"b": 1}}}' @ ? '$.**{1 to last}.b ? ( @ > 0)'; SELECT jsonb '{"a": {"c": {"b": 1}}}' @ ? '$.**{1 to 2}.b ? ( @ > 0)'; SELECT jsonb '{"a": {"c": {"b": 1}}}' @ ? '$.**{2 to 3}.b ? ( @ > 0)'; SELECT jsonb_path_query('{"g": {"x": 2}}', '$.g ? (exists (@.x))'); SELECT jsonb_path_query('{"g": {"x": 2}}', '$.g ? (exists (@.y))'); SELECT jsonb_path_query('{"g": {"x": 2}}', '$.g ? (exists (@.x ? (@ >= 2) ))'); SELECT jsonb_path_query('{"g": [{"x": 2}, {"y": 3}]}', 'lax $.g ? (exists (@.x))'); SELECT jsonb_path_query('{"g": [{"x": 2}, {"y": 3}]}', 'lax $.g ? (exists (@.x + "3"))'); SELECT jsonb_path_query('{"g": [{"x": 2}, {"y": 3}]}', 'lax $.g ? ((exists (@.x + "3")) is unknown)'); SELECT jsonb_path_query('{"g": [{"x": 2}, {"y": 3}]}', 'strict $.g[*] ? (exists (@.x))'); SELECT jsonb_path_query('{"g": [{"x": 2}, {"y": 3}]}', 'strict $.g[*] ? ((exists (@.x)) is unknown)'); SELECT jsonb_path_query('{"g": [{"x": 2}, {"y": 3}]}', 'strict $.g ? (exists (@[*].x))'); SELECT jsonb_path_query('{"g": [{"x": 2}, {"y": 3}]}', 'strict $.g ? ((exists (@[*].x)) is unknown)'); --test ternary logic SELECT x, y, jsonb_path_query('[true, false, null]', '$[*] ? (@ == true && ($x == true && $y == true) || @ == false && !($x == true && $y == true) || @ == null && ($x == true && $y == true) is unknown)', jsonb_build_object('x', x, 'y', y)) AS "x && y" FROM ( VALUES (jsonb 'true'), ('false'), ('"null"')) x (x), ( VALUES (jsonb 'true'), ('false'), ('"null"')) y (y); SELECT x, y, jsonb_path_query('[true, false, null]', '$[*] ? (@ == true && ($x == true || $y == true) || @ == false && !($x == true || $y == true) || @ == null && ($x == true || $y == true) is unknown)', jsonb_build_object('x', x, 'y', y)) AS "x || y" FROM ( VALUES (jsonb 'true'), ('false'), ('"null"')) x (x), ( VALUES (jsonb 'true'), ('false'), ('"null"')) y (y); SELECT jsonb '{"a": 1, "b":1}' @ ? '$ ? (@.a == @.b)'; SELECT jsonb '{"c": {"a": 1, "b":1}}' @ ? '$ ? (@.a == @.b)'; SELECT jsonb '{"c": {"a": 1, "b":1}}' @ ? '$.c ? (@.a == @.b)'; SELECT jsonb '{"c": {"a": 1, "b":1}}' @ ? '$.c ? ($.c.a == @.b)'; SELECT jsonb '{"c": {"a": 1, "b":1}}' @ ? '$.* ? (@.a == @.b)'; SELECT jsonb '{"a": 1, "b":1}' @ ? '$.** ? (@.a == @.b)'; SELECT jsonb '{"c": {"a": 1, "b":1}}' @ ? '$.** ? (@.a == @.b)'; SELECT jsonb_path_query('{"c": {"a": 2, "b":1}}', '$.** ? (@.a == 1 + 1)'); SELECT jsonb_path_query('{"c": {"a": 2, "b":1}}', '$.** ? (@.a == (1 + 1))'); SELECT jsonb_path_query('{"c": {"a": 2, "b":1}}', '$.** ? (@.a == @.b + 1)'); SELECT jsonb_path_query('{"c": {"a": 2, "b":1}}', '$.** ? (@.a == (@.b + 1))'); SELECT jsonb '{"c": {"a": -1, "b":1}}' @ ? '$.** ? (@.a == - 1)'; SELECT jsonb '{"c": {"a": -1, "b":1}}' @ ? '$.** ? (@.a == -1)'; SELECT jsonb '{"c": {"a": -1, "b":1}}' @ ? '$.** ? (@.a == -@.b)'; SELECT jsonb '{"c": {"a": -1, "b":1}}' @ ? '$.** ? (@.a == - @.b)'; SELECT jsonb '{"c": {"a": 0, "b":1}}' @ ? '$.** ? (@.a == 1 - @.b)'; SELECT jsonb '{"c": {"a": 2, "b":1}}' @ ? '$.** ? (@.a == 1 - - @.b)'; SELECT jsonb '{"c": {"a": 0, "b":1}}' @ ? '$.** ? (@.a == 1 - +@.b)'; SELECT jsonb '[1,2,3]' @ ? '$ ? (+@[*] > +2)'; SELECT jsonb '[1,2,3]' @ ? '$ ? (+@[*] > +3)'; SELECT jsonb '[1,2,3]' @ ? '$ ? (-@[*] < -2)'; SELECT jsonb '[1,2,3]' @ ? '$ ? (-@[*] < -3)'; SELECT jsonb '1' @ ? '$ ? ($ > 0)'; -- arithmetic errors SELECT jsonb_path_query('[1,2,0,3]', '$[*] ? (2 / @ > 0)'); SELECT jsonb_path_query('[1,2,0,3]', '$[*] ? ((2 / @ > 0) is unknown)'); SELECT jsonb_path_query('0', '1 / $'); SELECT jsonb_path_query('0', '1 / $ + 2'); SELECT jsonb_path_query('0', '-(3 + 1 % $)'); SELECT jsonb_path_query('1', '$ + "2"'); SELECT jsonb_path_query('[1, 2]', '3 * $'); SELECT jsonb_path_query('"a"', '-$'); SELECT jsonb_path_query('[1,"2",3]', '+$'); SELECT jsonb_path_query('1', '$ + "2"', silent => TRUE); SELECT jsonb_path_query('[1, 2]', '3 * $', silent => TRUE); SELECT jsonb_path_query('"a"', '-$', silent => TRUE); SELECT jsonb_path_query('[1,"2",3]', '+$', silent => TRUE); SELECT jsonb '["1",2,0,3]' @ ? '-$[*]'; SELECT jsonb '[1,"2",0,3]' @ ? '-$[*]'; SELECT jsonb '["1",2,0,3]' @ ? 'strict -$[*]'; SELECT jsonb '[1,"2",0,3]' @ ? 'strict -$[*]'; -- unwrapping of operator arguments in lax mode SELECT jsonb_path_query('{"a": [2]}', 'lax $.a * 3'); SELECT jsonb_path_query('{"a": [2]}', 'lax $.a + 3'); SELECT jsonb_path_query('{"a": [2, 3, 4]}', 'lax -$.a'); -- should fail SELECT jsonb_path_query('{"a": [1, 2]}', 'lax $.a * 3'); SELECT jsonb_path_query('{"a": [1, 2]}', 'lax $.a * 3', silent => TRUE); -- extension: boolean expressions SELECT jsonb_path_query('2', '$ > 1'); SELECT jsonb_path_query('2', '$ <= 1'); SELECT jsonb_path_query('2', '$ == "2"'); SELECT jsonb '2' @ ? '$ == "2"'; SELECT jsonb '2' @@ '$ > 1'; SELECT jsonb '2' @@ '$ <= 1'; SELECT jsonb '2' @@ '$ == "2"'; SELECT jsonb '2' @@ '1'; SELECT jsonb '{}' @@ '$'; SELECT jsonb '[]' @@ '$'; SELECT jsonb '[1,2,3]' @@ '$[*]'; SELECT jsonb '[]' @@ '$[*]'; SELECT jsonb_path_match ('[[1, true], [2, false]]', 'strict $[*] ? (@[0] > $x) [1]', '{"x": 1}'); SELECT jsonb_path_match ('[[1, true], [2, false]]', 'strict $[*] ? (@[0] < $x) [1]', '{"x": 2}'); SELECT jsonb_path_match ('[{"a": 1}, {"a": 2}, 3]', 'lax exists($[*].a)', silent => FALSE); SELECT jsonb_path_match ('[{"a": 1}, {"a": 2}, 3]', 'lax exists($[*].a)', silent => TRUE); SELECT jsonb_path_match ('[{"a": 1}, {"a": 2}, 3]', 'strict exists($[*].a)', silent => FALSE); SELECT jsonb_path_match ('[{"a": 1}, {"a": 2}, 3]', 'strict exists($[*].a)', silent => TRUE); SELECT jsonb_path_query('[null,1,true,"a",[],{}]', '$.type()'); SELECT jsonb_path_query('[null,1,true,"a",[],{}]', 'lax $.type()'); SELECT jsonb_path_query('[null,1,true,"a",[],{}]', '$[*].type()'); SELECT jsonb_path_query('null', 'null.type()'); SELECT jsonb_path_query('null', 'true.type()'); SELECT jsonb_path_query('null', '(123).type()'); SELECT jsonb_path_query('null', '"123".type()'); SELECT jsonb_path_query('{"a": 2}', '($.a - 5).abs() + 10'); SELECT jsonb_path_query('{"a": 2.5}', '-($.a * $.a).floor() % 4.3'); SELECT jsonb_path_query('[1, 2, 3]', '($[*] > 2) ? (@ == true)'); SELECT jsonb_path_query('[1, 2, 3]', '($[*] > 3).type()'); SELECT jsonb_path_query('[1, 2, 3]', '($[*].a > 3).type()'); SELECT jsonb_path_query('[1, 2, 3]', 'strict ($[*].a > 3).type()'); SELECT jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'strict $[*].size()'); SELECT jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'strict $[*].size()', silent => TRUE); SELECT jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'lax $[*].size()'); SELECT jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].abs()'); SELECT jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].floor()'); SELECT jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].ceiling()'); SELECT jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].ceiling().abs()'); SELECT jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].ceiling().abs().type()'); SELECT jsonb_path_query('[{},1]', '$[*].keyvalue()'); SELECT jsonb_path_query('[{},1]', '$[*].keyvalue()', silent => TRUE); SELECT jsonb_path_query('{}', '$.keyvalue()'); SELECT jsonb_path_query('{"a": 1, "b": [1, 2], "c": {"a": "bbb"}}', '$.keyvalue()'); SELECT jsonb_path_query('[{"a": 1, "b": [1, 2]}, {"c": {"a": "bbb"}}]', '$[*].keyvalue()'); SELECT jsonb_path_query('[{"a": 1, "b": [1, 2]}, {"c": {"a": "bbb"}}]', 'strict $.keyvalue()'); SELECT jsonb_path_query('[{"a": 1, "b": [1, 2]}, {"c": {"a": "bbb"}}]', 'lax $.keyvalue()'); SELECT jsonb_path_query('[{"a": 1, "b": [1, 2]}, {"c": {"a": "bbb"}}]', 'strict $.keyvalue().a'); SELECT jsonb '{"a": 1, "b": [1, 2]}' @ ? 'lax $.keyvalue()'; SELECT jsonb '{"a": 1, "b": [1, 2]}' @ ? 'lax $.keyvalue().key'; SELECT jsonb_path_query('null', '$.double()'); SELECT jsonb_path_query('true', '$.double()'); SELECT jsonb_path_query('null', '$.double()', silent => TRUE); SELECT jsonb_path_query('true', '$.double()', silent => TRUE); SELECT jsonb_path_query('[]', '$.double()'); SELECT jsonb_path_query('[]', 'strict $.double()'); SELECT jsonb_path_query('{}', '$.double()'); SELECT jsonb_path_query('[]', 'strict $.double()', silent => TRUE); SELECT jsonb_path_query('{}', '$.double()', silent => TRUE); SELECT jsonb_path_query('1.23', '$.double()'); SELECT jsonb_path_query('"1.23"', '$.double()'); SELECT jsonb_path_query('"1.23aaa"', '$.double()'); SELECT jsonb_path_query('"nan"', '$.double()'); SELECT jsonb_path_query('"NaN"', '$.double()'); SELECT jsonb_path_query('"inf"', '$.double()'); SELECT jsonb_path_query('"-inf"', '$.double()'); SELECT jsonb_path_query('"inf"', '$.double()', silent => TRUE); SELECT jsonb_path_query('"-inf"', '$.double()', silent => TRUE); SELECT jsonb_path_query('{}', '$.abs()'); SELECT jsonb_path_query('true', '$.floor()'); SELECT jsonb_path_query('"1.2"', '$.ceiling()'); SELECT jsonb_path_query('{}', '$.abs()', silent => TRUE); SELECT jsonb_path_query('true', '$.floor()', silent => TRUE); SELECT jsonb_path_query('"1.2"', '$.ceiling()', silent => TRUE); SELECT jsonb_path_query('["", "a", "abc", "abcabc"]', '$[*] ? (@ starts with "abc")'); SELECT jsonb_path_query('["", "a", "abc", "abcabc"]', 'strict $ ? (@[*] starts with "abc")'); SELECT jsonb_path_query('["", "a", "abd", "abdabc"]', 'strict $ ? (@[*] starts with "abc")'); SELECT jsonb_path_query('["abc", "abcabc", null, 1]', 'strict $ ? (@[*] starts with "abc")'); SELECT jsonb_path_query('["abc", "abcabc", null, 1]', 'strict $ ? ((@[*] starts with "abc") is unknown)'); SELECT jsonb_path_query('[[null, 1, "abc", "abcabc"]]', 'lax $ ? (@[*] starts with "abc")'); SELECT jsonb_path_query('[[null, 1, "abd", "abdabc"]]', 'lax $ ? ((@[*] starts with "abc") is unknown)'); SELECT jsonb_path_query('[null, 1, "abd", "abdabc"]', 'lax $[*] ? ((@ starts with "abc") is unknown)'); SELECT jsonb_path_query('[null, 1, "abc", "abd", "aBdC", "abdacb", "adc\nabc", "babc"]', 'lax $[*] ? (@ like_regex "^ab.*c")'); SELECT jsonb_path_query('[null, 1, "abc", "abd", "aBdC", "abdacb", "adc\nabc", "babc"]', 'lax $[*] ? (@ like_regex "^a b.* c " flag "ix")'); SELECT jsonb_path_query('[null, 1, "abc", "abd", "aBdC", "abdacb", "adc\nabc", "babc"]', 'lax $[*] ? (@ like_regex "^ab.*c" flag "m")'); SELECT jsonb_path_query('[null, 1, "abc", "abd", "aBdC", "abdacb", "adc\nabc", "babc"]', 'lax $[*] ? (@ like_regex "^ab.*c" flag "s")'); -- jsonpath operators SELECT jsonb_path_query('[{"a": 1}, {"a": 2}]', '$[*]'); SELECT jsonb_path_query('[{"a": 1}, {"a": 2}]', '$[*] ? (@.a > 10)'); SELECT jsonb_path_query_array ('[{"a": 1}, {"a": 2}, {}]', 'strict $[*].a'); SELECT jsonb_path_query_array ('[{"a": 1}, {"a": 2}]', '$[*].a'); SELECT jsonb_path_query_array ('[{"a": 1}, {"a": 2}]', '$[*].a ? (@ == 1)'); SELECT jsonb_path_query_array ('[{"a": 1}, {"a": 2}]', '$[*].a ? (@ > 10)'); SELECT jsonb_path_query_array ('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*].a ? (@ > $min && @ < $max)', vars => '{"min": 1, "max": 4}'); SELECT jsonb_path_query_array ('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*].a ? (@ > $min && @ < $max)', vars => '{"min": 3, "max": 4}'); SELECT jsonb_path_query_first ('[{"a": 1}, {"a": 2}, {}]', 'strict $[*].a'); SELECT jsonb_path_query_first ('[{"a": 1}, {"a": 2}, {}]', 'strict $[*].a', silent => TRUE); SELECT jsonb_path_query_first ('[{"a": 1}, {"a": 2}]', '$[*].a'); SELECT jsonb_path_query_first ('[{"a": 1}, {"a": 2}]', '$[*].a ? (@ == 1)'); SELECT jsonb_path_query_first ('[{"a": 1}, {"a": 2}]', '$[*].a ? (@ > 10)'); SELECT jsonb_path_query_first ('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*].a ? (@ > $min && @ < $max)', vars => '{"min": 1, "max": 4}'); SELECT jsonb_path_query_first ('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*].a ? (@ > $min && @ < $max)', vars => '{"min": 3, "max": 4}'); SELECT jsonb '[{"a": 1}, {"a": 2}]' @ ? '$[*].a ? (@ > 1)'; SELECT jsonb '[{"a": 1}, {"a": 2}]' @ ? '$[*] ? (@.a > 2)'; SELECT jsonb_path_exists ('[{"a": 1}, {"a": 2}]', '$[*].a ? (@ > 1)'); SELECT jsonb_path_exists ('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*] ? (@.a > $min && @.a < $max)', vars => '{"min": 1, "max": 4}'); SELECT jsonb_path_exists ('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*] ? (@.a > $min && @.a < $max)', vars => '{"min": 3, "max": 4}'); SELECT jsonb_path_match ('true', '$', silent => FALSE); SELECT jsonb_path_match ('false', '$', silent => FALSE); SELECT jsonb_path_match ('null', '$', silent => FALSE); SELECT jsonb_path_match ('1', '$', silent => TRUE); SELECT jsonb_path_match ('1', '$', silent => FALSE); SELECT jsonb_path_match ('"a"', '$', silent => FALSE); SELECT jsonb_path_match ('{}', '$', silent => FALSE); SELECT jsonb_path_match ('[true]', '$', silent => FALSE); SELECT jsonb_path_match ('{}', 'lax $.a', silent => FALSE); SELECT jsonb_path_match ('{}', 'strict $.a', silent => FALSE); SELECT jsonb_path_match ('{}', 'strict $.a', silent => TRUE); SELECT jsonb_path_match ('[true, true]', '$[*]', silent => FALSE); SELECT jsonb '[{"a": 1}, {"a": 2}]' @@ '$[*].a > 1'; SELECT jsonb '[{"a": 1}, {"a": 2}]' @@ '$[*].a > 2'; SELECT jsonb_path_match ('[{"a": 1}, {"a": 2}]', '$[*].a > 1'); pgFormatter-4.2/t/pg-test-files/expected/jsonpath.sql000066400000000000000000000150401361326045100227500ustar00rootroot00000000000000--jsonpath io SELECT ''::jsonpath; SELECT '$'::jsonpath; SELECT 'strict $'::jsonpath; SELECT 'lax $'::jsonpath; SELECT '$.a'::jsonpath; SELECT '$.a.v'::jsonpath; SELECT '$.a.*'::jsonpath; SELECT '$.*[*]'::jsonpath; SELECT '$.a[*]'::jsonpath; SELECT '$.a[*][*]'::jsonpath; SELECT '$[*]'::jsonpath; SELECT '$[0]'::jsonpath; SELECT '$[*][0]'::jsonpath; SELECT '$[*].a'::jsonpath; SELECT '$[*][0].a.b'::jsonpath; SELECT '$.a.**.b'::jsonpath; SELECT '$.a.**{2}.b'::jsonpath; SELECT '$.a.**{2 to 2}.b'::jsonpath; SELECT '$.a.**{2 to 5}.b'::jsonpath; SELECT '$.a.**{0 to 5}.b'::jsonpath; SELECT '$.a.**{5 to last}.b'::jsonpath; SELECT '$.a.**{last}.b'::jsonpath; SELECT '$.a.**{last to 5}.b'::jsonpath; SELECT '$+1'::jsonpath; SELECT '$-1'::jsonpath; SELECT '$--+1'::jsonpath; SELECT '$.a/+-1'::jsonpath; SELECT '1 * 2 + 4 % -3 != false'::jsonpath; SELECT '"\b\f\r\n\t\v\"\'' "'::jsonpath; select '''\b\f\r\n\t\v\"\''\\'''::jsonpath; select '" x50 u0067 u 53 u 051 u 00004C "'::jsonpath; select '''\x50\u0067\u{53}\u{051}\u{00004C}'''::jsonpath; select '$.foo\x50\u0067\u{53}\u{051}\u{00004C}\t\"bar'::jsonpath; select '$.g ? ($.a == 1)'::jsonpath; select '$.g ? (@ == 1)'::jsonpath; select '$.g ? (@.a == 1)'::jsonpath; select '$.g ? (@.a == 1 || @.a == 4)'::jsonpath; select '$.g ? (@.a == 1 && @.a == 4)'::jsonpath; select '$.g ? (@.a == 1 || @.a == 4 && @.b == 7)'::jsonpath; select '$.g ? (@.a == 1 || !(@.a == 4) && @.b == 7)'::jsonpath; select '$.g ? (@.a == 1 || !(@.x >= 123 || @.a == 4) && @.b == 7)'::jsonpath; select '$.g ? (@.x >= @[*]?(@.a > " abc "))'::jsonpath; select '$.g ? ((@.x >= 123 || @.a == 4) is unknown)'::jsonpath; select '$.g ? (exists (@.x))'::jsonpath; select '$.g ? (exists (@.x ? (@ == 14)))'::jsonpath; select '$.g ? ((@.x >= 123 || @.a == 4) && exists (@.x ? (@ == 14)))'::jsonpath; select '$.g ? (+@.x >= +-(+@.a + 2))'::jsonpath; select '$a'::jsonpath; select '$a.b'::jsonpath; select '$a[*]'::jsonpath; select '$.g ? (@.zip == $zip)'::jsonpath; select '$.a[1,2, 3 to 16]'::jsonpath; select '$.a[$a + 1, ($b[*]) to -($[0] * 2)]'::jsonpath; select '$.a[$.a.size() - 3]'::jsonpath; select 'last'::jsonpath; select '" LAST "'::jsonpath; select '$.last'::jsonpath; select '$ ? (last > 0)'::jsonpath; select '$[last]'::jsonpath; select '$[$[0] ? (last > 0)]'::jsonpath; select 'null.type()'::jsonpath; select '1.type()'::jsonpath; select '(1).type()'::jsonpath; select '1.2.type()'::jsonpath; select '" aaa ".type()'::jsonpath; select 'true.type()'::jsonpath; select '$.double().floor().ceiling().abs()'::jsonpath; select '$.keyvalue().key'::jsonpath; select '$ ? (@ starts with " abc ")'::jsonpath; select '$ ? (@ starts with $var)'::jsonpath; select '$ ? (@ like_regex " (invalid pattern ")'::jsonpath; select '$ ? (@ like_regex " pattern ")'::jsonpath; select '$ ? (@ like_regex " pattern " flag "")'::jsonpath; select '$ ? (@ like_regex " pattern " flag " i ")'::jsonpath; select '$ ? (@ like_regex " pattern " flag " IS ")'::jsonpath; select '$ ? (@ like_regex " pattern " flag " isim ")'::jsonpath; select '$ ? (@ like_regex " pattern " flag " xsms ")'::jsonpath; select '$ ? (@ like_regex " pattern " flag " a) '::jsonpath; select ' $ < 1 '::jsonpath; select ' ($ < 1) || $.a.b <= $ x '::jsonpath; select ' @ + 1 '::jsonpath; select ' ($).a.b '::jsonpath; select ' ($.a.b).c.d '::jsonpath; select ' ($.a.b + - $.x.y).c.d '::jsonpath; select ' (- + $.a.b).c.d '::jsonpath; select ' 1 + ($.a.b + 2).c.d '::jsonpath; select ' 1 + ($.a.b > 2).c.d '::jsonpath; select ' ($) '::jsonpath; select ' (($)) '::jsonpath; select ' ((($ + 1)).a + ((2)).b ? ((((@ > 1)) || (EXISTS (@.c))))) '::jsonpath; select ' $ ? (@.a < 1) '::jsonpath; select ' $ ? (@.a < - 1) '::jsonpath; select ' $ ? (@.a < + 1) '::jsonpath; select ' $ ? (@.a <.1) '::jsonpath; select ' $ ? (@.a < -.1) '::jsonpath; select ' $ ? (@.a < +.1) '::jsonpath; select ' $ ? (@.a < 0.1) '::jsonpath; select ' $ ? (@.a < - 0.1) '::jsonpath; select ' $ ? (@.a < + 0.1) '::jsonpath; select ' $ ? (@.a < 10.1) '::jsonpath; select ' $ ? (@.a < - 10.1) '::jsonpath; select ' $ ? (@.a < + 10.1) '::jsonpath; select ' $ ? (@.a < 1e1) '::jsonpath; select ' $ ? (@.a < - 1e1) '::jsonpath; select ' $ ? (@.a < + 1e1) '::jsonpath; select ' $ ? (@.a <.1e1) '::jsonpath; select ' $ ? (@.a < -.1e1) '::jsonpath; select ' $ ? (@.a < +.1e1) '::jsonpath; select ' $ ? (@.a < 0.1e1) '::jsonpath; select ' $ ? (@.a < - 0.1e1) '::jsonpath; select ' $ ? (@.a < + 0.1e1) '::jsonpath; select ' $ ? (@.a < 10.1e1) '::jsonpath; select ' $ ? (@.a < - 10.1e1) '::jsonpath; select ' $ ? (@.a < + 10.1e1) '::jsonpath; select ' $ ? (@.a < 1e - 1) '::jsonpath; select ' $ ? (@.a < - 1e - 1) '::jsonpath; select ' $ ? (@.a < + 1e - 1) '::jsonpath; select ' $ ? (@.a <.1e - 1) '::jsonpath; select ' $ ? (@.a < -.1e - 1) '::jsonpath; select ' $ ? (@.a < +.1e - 1) '::jsonpath; select ' $ ? (@.a < 0.1e - 1) '::jsonpath; select ' $ ? (@.a < - 0.1e - 1) '::jsonpath; select ' $ ? (@.a < + 0.1e - 1) '::jsonpath; select ' $ ? (@.a < 10.1e - 1) '::jsonpath; select ' $ ? (@.a < - 10.1e - 1) '::jsonpath; select ' $ ? (@.a < + 10.1e - 1) '::jsonpath; select ' $ ? (@.a < 1e + 1) '::jsonpath; select ' $ ? (@.a < - 1e + 1) '::jsonpath; select ' $ ? (@.a < + 1e + 1) '::jsonpath; select ' $ ? (@.a <.1e + 1) '::jsonpath; select ' $ ? (@.a < -.1e + 1) '::jsonpath; select ' $ ? (@.a < +.1e + 1) '::jsonpath; select ' $ ? (@.a < 0.1e + 1) '::jsonpath; select ' $ ? (@.a < - 0.1e + 1) '::jsonpath; select ' $ ? (@.a < + 0.1e + 1) '::jsonpath; select ' $ ? (@.a < 10.1e + 1) '::jsonpath; select ' $ ? (@.a < - 10.1e + 1) '::jsonpath; select ' $ ? (@.a < + 10.1e + 1) '::jsonpath; select ' 0 '::jsonpath; select ' 00 '::jsonpath; select ' 0.0 '::jsonpath; select ' 0.000 '::jsonpath; select ' 0.000e1 '::jsonpath; select ' 0.000e2 '::jsonpath; select ' 0.000e3 '::jsonpath; select ' 0.0010 '::jsonpath; select ' 0.0010e - 1 '::jsonpath; select ' 0.0010e + 1 '::jsonpath; select ' 0.0010e + 2 '::jsonpath; select ' 1e '::jsonpath; select ' 1.e '::jsonpath; select ' 1.2e '::jsonpath; select ' 1.2.e '::jsonpath; select ' (1.2).e '::jsonpath; select ' 1e3 '::jsonpath; select ' 1.e3 '::jsonpath; select ' 1.e3.e '::jsonpath; select ' 1.e3.e4 '::jsonpath; select ' 1.2e3 '::jsonpath; select ' 1.2.e3 '::jsonpath; select ' (1.2).e3 '::jsonpath; select ' 1..e '::jsonpath; select ' 1..e3 '::jsonpath; select ' (1.).e '::jsonpath; select ' (1.).e3::jsonpath; pgFormatter-4.2/t/pg-test-files/expected/jsonpath_encoding.sql000066400000000000000000000066121361326045100246230ustar00rootroot00000000000000-- encoding-sensitive tests for jsonpath -- checks for double-quoted values -- basic unicode input SELECT '"\u"'::jsonpath; -- ERROR, incomplete escape SELECT '"\u00"'::jsonpath; -- ERROR, incomplete escape SELECT '"\u000g"'::jsonpath; -- ERROR, g is not a hex digit SELECT '"\u0000"'::jsonpath; -- OK, legal escape SELECT '"\uaBcD"'::jsonpath; -- OK, uppercase and lower case both OK -- handling of unicode surrogate pairs SELECT '"\ud83d\ude04\ud83d\udc36"'::jsonpath AS correct_in_utf8; SELECT '"\ud83d\ud83d"'::jsonpath; -- 2 high surrogates in a row SELECT '"\ude04\ud83d"'::jsonpath; -- surrogates in wrong order SELECT '"\ud83dX"'::jsonpath; -- orphan high surrogate SELECT '"\ude04X"'::jsonpath; -- orphan low surrogate --handling of simple unicode escapes SELECT '"the Copyright \u00a9 sign"'::jsonpath AS correct_in_utf8; SELECT '"dollar \u0024 character"'::jsonpath AS correct_everywhere; SELECT '"dollar \\u0024 character"'::jsonpath AS not_an_escape; SELECT '"null \u0000 escape"'::jsonpath AS not_unescaped; SELECT '"null \\u0000 escape"'::jsonpath AS not_an_escape; -- checks for single-quoted values -- basic unicode input SELECT E'\'\u\''::jsonpath; -- ERROR, incomplete escape SELECT E'\'\u00\''::jsonpath; -- ERROR, incomplete escape SELECT E'\'\u000g\''::jsonpath; -- ERROR, g is not a hex digit SELECT E'\'\u0000\''::jsonpath; -- OK, legal escape SELECT E'\'\uaBcD\''::jsonpath; -- OK, uppercase and lower case both OK -- handling of unicode surrogate pairs SELECT E'\'\ud83d\ude04\ud83d\udc36\''::jsonpath AS correct_in_utf8; SELECT E'\'\ud83d\ud83d\''::jsonpath; -- 2 high surrogates in a row SELECT E'\'\ude04\ud83d\''::jsonpath; -- surrogates in wrong order SELECT E'\'\ud83dX\''::jsonpath; -- orphan high surrogate SELECT E'\'\ude04X\''::jsonpath; -- orphan low surrogate --handling of simple unicode escapes SELECT E'\'the Copyright \u00a9 sign\''::jsonpath AS correct_in_utf8; SELECT E'\'dollar \u0024 character\''::jsonpath AS correct_everywhere; SELECT E'\'dollar \\u0024 character\''::jsonpath AS not_an_escape; SELECT E'\'null \u0000 escape\''::jsonpath AS not_unescaped; SELECT E'\'null \\u0000 escape\''::jsonpath AS not_an_escape; -- checks for quoted key names -- basic unicode input SELECT '$."\u"'::jsonpath; -- ERROR, incomplete escape SELECT '$."\u00"'::jsonpath; -- ERROR, incomplete escape SELECT '$."\u000g"'::jsonpath; -- ERROR, g is not a hex digit SELECT '$."\u0000"'::jsonpath; -- OK, legal escape SELECT '$."\uaBcD"'::jsonpath; -- OK, uppercase and lower case both OK -- handling of unicode surrogate pairs SELECT '$."\ud83d\ude04\ud83d\udc36"'::jsonpath AS correct_in_utf8; SELECT '$."\ud83d\ud83d"'::jsonpath; -- 2 high surrogates in a row SELECT '$."\ude04\ud83d"'::jsonpath; -- surrogates in wrong order SELECT '$."\ud83dX"'::jsonpath; -- orphan high surrogate SELECT '$."\ude04X"'::jsonpath; -- orphan low surrogate --handling of simple unicode escapes SELECT '$."the Copyright \u00a9 sign"'::jsonpath AS correct_in_utf8; SELECT '$."dollar \u0024 character"'::jsonpath AS correct_everywhere; SELECT '$."dollar \\u0024 character"'::jsonpath AS not_an_escape; SELECT '$."null \u0000 escape"'::jsonpath AS not_unescaped; SELECT '$."null \\u0000 escape"'::jsonpath AS not_an_escape; pgFormatter-4.2/t/pg-test-files/expected/limit.sql000066400000000000000000000120051361326045100222360ustar00rootroot00000000000000-- -- LIMIT -- Check the LIMIT/OFFSET feature of SELECT -- SELECT ''::text AS two, unique1, unique2, stringu1 FROM onek WHERE unique1 > 50 ORDER BY unique1 LIMIT 2; SELECT ''::text AS five, unique1, unique2, stringu1 FROM onek WHERE unique1 > 60 ORDER BY unique1 LIMIT 5; SELECT ''::text AS two, unique1, unique2, stringu1 FROM onek WHERE unique1 > 60 AND unique1 < 63 ORDER BY unique1 LIMIT 5; SELECT ''::text AS three, unique1, unique2, stringu1 FROM onek WHERE unique1 > 100 ORDER BY unique1 LIMIT 3 OFFSET 20; SELECT ''::text AS zero, unique1, unique2, stringu1 FROM onek WHERE unique1 < 50 ORDER BY unique1 DESC LIMIT 8 OFFSET 99; SELECT ''::text AS eleven, unique1, unique2, stringu1 FROM onek WHERE unique1 < 50 ORDER BY unique1 DESC LIMIT 20 OFFSET 39; SELECT ''::text AS ten, unique1, unique2, stringu1 FROM onek ORDER BY unique1 OFFSET 990; SELECT ''::text AS five, unique1, unique2, stringu1 FROM onek ORDER BY unique1 OFFSET 990 LIMIT 5; SELECT ''::text AS five, unique1, unique2, stringu1 FROM onek ORDER BY unique1 LIMIT 5 OFFSET 900; -- Test null limit and offset. The planner would discard a simple null -- constant, so to ensure executor is exercised, do this: SELECT * FROM int8_tbl LIMIT ( CASE WHEN random() < 0.5 THEN NULL::bigint END); SELECT * FROM int8_tbl offset ( CASE WHEN random() < 0.5 THEN NULL::bigint END); -- Test assorted cases involving backwards fetch from a LIMIT plan node BEGIN; DECLARE c1 CURSOR FOR SELECT * FROM int8_tbl LIMIT 10; FETCH ALL IN c1; FETCH 1 IN c1; FETCH BACKWARD 1 IN c1; FETCH BACKWARD ALL IN c1; FETCH BACKWARD 1 IN c1; FETCH ALL IN c1; DECLARE c2 CURSOR FOR SELECT * FROM int8_tbl LIMIT 3; FETCH ALL IN c2; FETCH 1 IN c2; FETCH BACKWARD 1 IN c2; FETCH BACKWARD ALL IN c2; FETCH BACKWARD 1 IN c2; FETCH ALL IN c2; DECLARE c3 CURSOR FOR SELECT * FROM int8_tbl offset 3; FETCH ALL IN c3; FETCH 1 IN c3; FETCH BACKWARD 1 IN c3; FETCH BACKWARD ALL IN c3; FETCH BACKWARD 1 IN c3; FETCH ALL IN c3; DECLARE c4 CURSOR FOR SELECT * FROM int8_tbl offset 10; FETCH ALL IN c4; FETCH 1 IN c4; FETCH BACKWARD 1 IN c4; FETCH BACKWARD ALL IN c4; FETCH BACKWARD 1 IN c4; FETCH ALL IN c4; ROLLBACK; -- Stress test for variable LIMIT in conjunction with bounded-heap sorting SELECT ( SELECT n FROM ( VALUES (1)) AS x, ( SELECT n FROM generate_series(1, 10) AS n ORDER BY n LIMIT 1 OFFSET s - 1) AS y) AS z FROM generate_series(1, 10) AS s; -- -- Test behavior of volatile and set-returning functions in conjunction -- with ORDER BY and LIMIT. -- CREATE temp SEQUENCE testseq; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT unique1, unique2, nextval('testseq') FROM tenk1 ORDER BY unique2 LIMIT 10; SELECT unique1, unique2, nextval('testseq') FROM tenk1 ORDER BY unique2 LIMIT 10; SELECT currval('testseq'); EXPLAIN ( VERBOSE, COSTS OFF ) SELECT unique1, unique2, nextval('testseq') FROM tenk1 ORDER BY tenthous LIMIT 10; SELECT unique1, unique2, nextval('testseq') FROM tenk1 ORDER BY tenthous LIMIT 10; SELECT currval('testseq'); EXPLAIN ( VERBOSE, COSTS OFF ) SELECT unique1, unique2, generate_series(1, 10) FROM tenk1 ORDER BY unique2 LIMIT 7; SELECT unique1, unique2, generate_series(1, 10) FROM tenk1 ORDER BY unique2 LIMIT 7; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT unique1, unique2, generate_series(1, 10) FROM tenk1 ORDER BY tenthous LIMIT 7; SELECT unique1, unique2, generate_series(1, 10) FROM tenk1 ORDER BY tenthous LIMIT 7; -- use of random() is to keep planner from folding the expressions together EXPLAIN ( VERBOSE, COSTS OFF ) SELECT generate_series(0, 2) AS s1, generate_series((random() *.1)::int, 2) AS s2; SELECT generate_series(0, 2) AS s1, generate_series((random() *.1)::int, 2) AS s2; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT generate_series(0, 2) AS s1, generate_series((random() *.1)::int, 2) AS s2 ORDER BY s2 DESC; SELECT generate_series(0, 2) AS s1, generate_series((random() *.1)::int, 2) AS s2 ORDER BY s2 DESC; -- test for failure to set all aggregates' aggtranstype EXPLAIN ( VERBOSE, COSTS OFF ) SELECT sum(tenthous) AS s1, sum(tenthous) + random() * 0 AS s2 FROM tenk1 GROUP BY thousand ORDER BY thousand LIMIT 3; SELECT sum(tenthous) AS s1, sum(tenthous) + random() * 0 AS s2 FROM tenk1 GROUP BY thousand ORDER BY thousand LIMIT 3; pgFormatter-4.2/t/pg-test-files/expected/line.sql000066400000000000000000000026731361326045100220610ustar00rootroot00000000000000-- -- LINE -- Infinite lines -- --DROP TABLE LINE_TBL; CREATE TABLE LINE_TBL ( s line ); INSERT INTO LINE_TBL VALUES ('{0,-1,5}'); -- A == 0 INSERT INTO LINE_TBL VALUES ('{1,0,5}'); -- B == 0 INSERT INTO LINE_TBL VALUES ('{0,3,0}'); -- A == C == 0 INSERT INTO LINE_TBL VALUES (' (0,0), (6,6)'); INSERT INTO LINE_TBL VALUES ('10,-10 ,-5,-4'); INSERT INTO LINE_TBL VALUES ('[-1e6,2e2,3e5, -4e1]'); INSERT INTO LINE_TBL VALUES ('{3,NaN,5}'); INSERT INTO LINE_TBL VALUES ('{NaN,NaN,NaN}'); -- horizontal INSERT INTO LINE_TBL VALUES ('[(1,3),(2,3)]'); -- vertical INSERT INTO LINE_TBL VALUES (line(point '(3,1)', point '(3,2)')); -- bad values for parser testing INSERT INTO LINE_TBL VALUES ('{}'); INSERT INTO LINE_TBL VALUES ('{0'); INSERT INTO LINE_TBL VALUES ('{0,0}'); INSERT INTO LINE_TBL VALUES ('{0,0,1'); INSERT INTO LINE_TBL VALUES ('{0,0,1}'); INSERT INTO LINE_TBL VALUES ('{0,0,1} x'); INSERT INTO LINE_TBL VALUES ('(3asdf,2 ,3,4r2)'); INSERT INTO LINE_TBL VALUES ('[1,2,3, 4'); INSERT INTO LINE_TBL VALUES ('[(,2),(3,4)]'); INSERT INTO LINE_TBL VALUES ('[(1,2),(3,4)'); INSERT INTO LINE_TBL VALUES ('[(1,2),(1,2)]'); INSERT INTO LINE_TBL VALUES (line(point '(1,0)', point '(1,0)')); SELECT * FROM LINE_TBL; SELECT '{nan, 1, nan}'::line = '{nan, 1, nan}'::line AS true, '{nan, 1, nan}'::line = '{nan, 2, nan}'::line AS false; pgFormatter-4.2/t/pg-test-files/expected/lock.sql000066400000000000000000000115611361326045100220560ustar00rootroot00000000000000-- -- Test the LOCK statement -- -- Setup CREATE SCHEMA lock_schema1; SET search_path = lock_schema1; CREATE TABLE lock_tbl1 ( a bigint ); CREATE TABLE lock_tbl1a ( a bigint ); CREATE VIEW lock_view1 AS SELECT * FROM lock_tbl1; CREATE VIEW lock_view2 (a, b) AS SELECT * FROM lock_tbl1, lock_tbl1a; CREATE VIEW lock_view3 AS SELECT * FROM lock_view2; CREATE VIEW lock_view4 AS SELECT ( SELECT a FROM lock_tbl1a LIMIT 1) FROM lock_tbl1; CREATE VIEW lock_view5 AS SELECT * FROM lock_tbl1 WHERE a IN ( SELECT * FROM lock_tbl1a); CREATE VIEW lock_view6 AS SELECT * FROM ( SELECT * FROM lock_tbl1) sub; CREATE ROLE regress_rol_lock1; ALTER ROLE regress_rol_lock1 SET search_path = lock_schema1; GRANT USAGE ON SCHEMA lock_schema1 TO regress_rol_lock1; -- Try all valid lock options; also try omitting the optional TABLE keyword. BEGIN TRANSACTION; LOCK TABLE lock_tbl1 IN ACCESS SHARE MODE; LOCK lock_tbl1 IN ROW SHARE MODE; LOCK TABLE lock_tbl1 IN ROW EXCLUSIVE MODE; LOCK TABLE lock_tbl1 IN SHARE UPDATE EXCLUSIVE MODE; LOCK TABLE lock_tbl1 IN SHARE MODE; LOCK lock_tbl1 IN SHARE ROW EXCLUSIVE MODE; LOCK TABLE lock_tbl1 IN EXCLUSIVE MODE; LOCK TABLE lock_tbl1 IN ACCESS EXCLUSIVE MODE; ROLLBACK; -- Try using NOWAIT along with valid options. BEGIN TRANSACTION; LOCK TABLE lock_tbl1 IN ACCESS SHARE MODE NOWAIT; LOCK TABLE lock_tbl1 IN ROW SHARE MODE NOWAIT; LOCK TABLE lock_tbl1 IN ROW EXCLUSIVE MODE NOWAIT; LOCK TABLE lock_tbl1 IN SHARE UPDATE EXCLUSIVE MODE NOWAIT; LOCK TABLE lock_tbl1 IN SHARE MODE NOWAIT; LOCK TABLE lock_tbl1 IN SHARE ROW EXCLUSIVE MODE NOWAIT; LOCK TABLE lock_tbl1 IN EXCLUSIVE MODE NOWAIT; LOCK TABLE lock_tbl1 IN ACCESS EXCLUSIVE MODE NOWAIT; ROLLBACK; -- Verify that we can lock views. BEGIN TRANSACTION; LOCK TABLE lock_view1 IN EXCLUSIVE MODE; -- lock_view1 and lock_tbl1 are locked. SELECT relname FROM pg_locks l, pg_class c WHERE l.relation = c.oid AND relname LIKE '%lock_%' AND mode = 'ExclusiveLock' ORDER BY relname; ROLLBACK; BEGIN TRANSACTION; LOCK TABLE lock_view2 IN EXCLUSIVE MODE; -- lock_view1, lock_tbl1, and lock_tbl1a are locked. SELECT relname FROM pg_locks l, pg_class c WHERE l.relation = c.oid AND relname LIKE '%lock_%' AND mode = 'ExclusiveLock' ORDER BY relname; ROLLBACK; BEGIN TRANSACTION; LOCK TABLE lock_view3 IN EXCLUSIVE MODE; -- lock_view3, lock_view2, lock_tbl1, and lock_tbl1a are locked recursively. SELECT relname FROM pg_locks l, pg_class c WHERE l.relation = c.oid AND relname LIKE '%lock_%' AND mode = 'ExclusiveLock' ORDER BY relname; ROLLBACK; BEGIN TRANSACTION; LOCK TABLE lock_view4 IN EXCLUSIVE MODE; -- lock_view4, lock_tbl1, and lock_tbl1a are locked. SELECT relname FROM pg_locks l, pg_class c WHERE l.relation = c.oid AND relname LIKE '%lock_%' AND mode = 'ExclusiveLock' ORDER BY relname; ROLLBACK; BEGIN TRANSACTION; LOCK TABLE lock_view5 IN EXCLUSIVE MODE; -- lock_view5, lock_tbl1, and lock_tbl1a are locked. SELECT relname FROM pg_locks l, pg_class c WHERE l.relation = c.oid AND relname LIKE '%lock_%' AND mode = 'ExclusiveLock' ORDER BY relname; ROLLBACK; BEGIN TRANSACTION; LOCK TABLE lock_view6 IN EXCLUSIVE MODE; -- lock_view6 an lock_tbl1 are locked. SELECT relname FROM pg_locks l, pg_class c WHERE l.relation = c.oid AND relname LIKE '%lock_%' AND mode = 'ExclusiveLock' ORDER BY relname; ROLLBACK; -- detecting infinite recursions in view definitions CREATE OR REPLACE VIEW lock_view2 AS SELECT * FROM lock_view3; BEGIN TRANSACTION; LOCK TABLE lock_view2 IN EXCLUSIVE MODE; ROLLBACK; CREATE VIEW lock_view7 AS SELECT * FROM lock_view2; BEGIN TRANSACTION; LOCK TABLE lock_view7 IN EXCLUSIVE MODE; ROLLBACK; -- Verify that we can lock a table with inheritance children. CREATE TABLE lock_tbl2 ( b bigint ) INHERITS ( lock_tbl1 ); CREATE TABLE lock_tbl3 () INHERITS ( lock_tbl2 ); BEGIN TRANSACTION; LOCK TABLE lock_tbl1 * IN ACCESS EXCLUSIVE MODE; ROLLBACK; -- Verify that we can't lock a child table just because we have permission -- on the parent, but that we can lock the parent only. GRANT UPDATE ON TABLE lock_tbl1 TO regress_rol_lock1; SET ROLE regress_rol_lock1; BEGIN; LOCK TABLE lock_tbl1 * IN ACCESS EXCLUSIVE MODE; ROLLBACK; BEGIN; LOCK TABLE ONLY lock_tbl1; ROLLBACK; RESET ROLE; -- -- Clean up -- DROP VIEW lock_view7; DROP VIEW lock_view6; DROP VIEW lock_view5; DROP VIEW lock_view4; DROP VIEW lock_view3 CASCADE; DROP VIEW lock_view1; DROP TABLE lock_tbl3; DROP TABLE lock_tbl2; DROP TABLE lock_tbl1; DROP TABLE lock_tbl1a; DROP SCHEMA lock_schema1 CASCADE; DROP ROLE regress_rol_lock1; -- atomic ops tests RESET search_path; SELECT test_atomic_ops (); pgFormatter-4.2/t/pg-test-files/expected/lseg.sql000066400000000000000000000015251361326045100220570ustar00rootroot00000000000000-- -- LSEG -- Line segments -- --DROP TABLE LSEG_TBL; CREATE TABLE LSEG_TBL ( s lseg ); INSERT INTO LSEG_TBL VALUES ('[(1,2),(3,4)]'); INSERT INTO LSEG_TBL VALUES ('(0,0),(6,6)'); INSERT INTO LSEG_TBL VALUES ('10,-10 ,-3,-4'); INSERT INTO LSEG_TBL VALUES ('[-1e6,2e2,3e5, -4e1]'); INSERT INTO LSEG_TBL VALUES (lseg(point(11, 22), point(33, 44))); INSERT INTO LSEG_TBL VALUES ('[(-10,2),(-10,3)]'); -- vertical INSERT INTO LSEG_TBL VALUES ('[(0,-20),(30,-20)]'); -- horizontal INSERT INTO LSEG_TBL VALUES ('[(NaN,1),(NaN,90)]'); -- NaN -- bad values for parser testing INSERT INTO LSEG_TBL VALUES ('(3asdf,2 ,3,4r2)'); INSERT INTO LSEG_TBL VALUES ('[1,2,3, 4'); INSERT INTO LSEG_TBL VALUES ('[(,2),(3,4)]'); INSERT INTO LSEG_TBL VALUES ('[(1,2),(3,4)'); SELECT * FROM LSEG_TBL; pgFormatter-4.2/t/pg-test-files/expected/macaddr.sql000066400000000000000000000040151361326045100225150ustar00rootroot00000000000000-- -- macaddr -- CREATE TABLE macaddr_data ( a int, b macaddr ); INSERT INTO macaddr_data VALUES (1, '08:00:2b:01:02:03'); INSERT INTO macaddr_data VALUES (2, '08-00-2b-01-02-03'); INSERT INTO macaddr_data VALUES (3, '08002b:010203'); INSERT INTO macaddr_data VALUES (4, '08002b-010203'); INSERT INTO macaddr_data VALUES (5, '0800.2b01.0203'); INSERT INTO macaddr_data VALUES (6, '0800-2b01-0203'); INSERT INTO macaddr_data VALUES (7, '08002b010203'); INSERT INTO macaddr_data VALUES (8, '0800:2b01:0203'); -- invalid INSERT INTO macaddr_data VALUES (9, 'not even close'); -- invalid INSERT INTO macaddr_data VALUES (10, '08:00:2b:01:02:04'); INSERT INTO macaddr_data VALUES (11, '08:00:2b:01:02:02'); INSERT INTO macaddr_data VALUES (12, '08:00:2a:01:02:03'); INSERT INTO macaddr_data VALUES (13, '08:00:2c:01:02:03'); INSERT INTO macaddr_data VALUES (14, '08:00:2a:01:02:04'); SELECT * FROM macaddr_data; CREATE INDEX macaddr_data_btree ON macaddr_data USING btree (b); CREATE INDEX macaddr_data_hash ON macaddr_data USING HASH (b); SELECT a, b, trunc(b) FROM macaddr_data ORDER BY 2, 1; SELECT b < '08:00:2b:01:02:04' FROM macaddr_data WHERE a = 1; -- true SELECT b > '08:00:2b:01:02:04' FROM macaddr_data WHERE a = 1; -- false SELECT b > '08:00:2b:01:02:03' FROM macaddr_data WHERE a = 1; -- false SELECT b <= '08:00:2b:01:02:04' FROM macaddr_data WHERE a = 1; -- true SELECT b >= '08:00:2b:01:02:04' FROM macaddr_data WHERE a = 1; -- false SELECT b = '08:00:2b:01:02:03' FROM macaddr_data WHERE a = 1; -- true SELECT b <> '08:00:2b:01:02:04' FROM macaddr_data WHERE a = 1; -- true SELECT b <> '08:00:2b:01:02:03' FROM macaddr_data WHERE a = 1; -- false SELECT ~ b FROM macaddr_data; SELECT b & '00:00:00:ff:ff:ff' FROM macaddr_data; SELECT b | '01:02:03:04:05:06' FROM macaddr_data; DROP TABLE macaddr_data; pgFormatter-4.2/t/pg-test-files/expected/macaddr8.sql000066400000000000000000000112061361326045100226050ustar00rootroot00000000000000-- -- macaddr8 -- -- test various cases of valid and invalid input -- valid SELECT '08:00:2b:01:02:03 '::macaddr8; SELECT ' 08:00:2b:01:02:03 '::macaddr8; SELECT ' 08:00:2b:01:02:03'::macaddr8; SELECT '08:00:2b:01:02:03:04:05 '::macaddr8; SELECT ' 08:00:2b:01:02:03:04:05 '::macaddr8; SELECT ' 08:00:2b:01:02:03:04:05'::macaddr8; SELECT '123 08:00:2b:01:02:03'::macaddr8; -- invalid SELECT '08:00:2b:01:02:03 123'::macaddr8; -- invalid SELECT '123 08:00:2b:01:02:03:04:05'::macaddr8; -- invalid SELECT '08:00:2b:01:02:03:04:05 123'::macaddr8; -- invalid SELECT '08:00:2b:01:02:03:04:05:06:07'::macaddr8; -- invalid SELECT '08-00-2b-01-02-03-04-05-06-07'::macaddr8; -- invalid SELECT '08002b:01020304050607'::macaddr8; -- invalid SELECT '08002b01020304050607'::macaddr8; -- invalid SELECT '0z002b0102030405'::macaddr8; -- invalid SELECT '08002b010203xyza'::macaddr8; -- invalid SELECT '08:00-2b:01:02:03:04:05'::macaddr8; -- invalid SELECT '08:00-2b:01:02:03:04:05'::macaddr8; -- invalid SELECT '08:00:2b:01.02:03:04:05'::macaddr8; -- invalid SELECT '08:00:2b:01.02:03:04:05'::macaddr8; -- invalid -- test converting a MAC address to modified EUI-64 for inclusion -- in an ipv6 address SELECT macaddr8_set7bit ('00:08:2b:01:02:03'::macaddr8); CREATE TABLE macaddr8_data ( a int, b macaddr8 ); INSERT INTO macaddr8_data VALUES (1, '08:00:2b:01:02:03'); INSERT INTO macaddr8_data VALUES (2, '08-00-2b-01-02-03'); INSERT INTO macaddr8_data VALUES (3, '08002b:010203'); INSERT INTO macaddr8_data VALUES (4, '08002b-010203'); INSERT INTO macaddr8_data VALUES (5, '0800.2b01.0203'); INSERT INTO macaddr8_data VALUES (6, '0800-2b01-0203'); INSERT INTO macaddr8_data VALUES (7, '08002b010203'); INSERT INTO macaddr8_data VALUES (8, '0800:2b01:0203'); INSERT INTO macaddr8_data VALUES (9, 'not even close'); -- invalid INSERT INTO macaddr8_data VALUES (10, '08:00:2b:01:02:04'); INSERT INTO macaddr8_data VALUES (11, '08:00:2b:01:02:02'); INSERT INTO macaddr8_data VALUES (12, '08:00:2a:01:02:03'); INSERT INTO macaddr8_data VALUES (13, '08:00:2c:01:02:03'); INSERT INTO macaddr8_data VALUES (14, '08:00:2a:01:02:04'); INSERT INTO macaddr8_data VALUES (15, '08:00:2b:01:02:03:04:05'); INSERT INTO macaddr8_data VALUES (16, '08-00-2b-01-02-03-04-05'); INSERT INTO macaddr8_data VALUES (17, '08002b:0102030405'); INSERT INTO macaddr8_data VALUES (18, '08002b-0102030405'); INSERT INTO macaddr8_data VALUES (19, '0800.2b01.0203.0405'); INSERT INTO macaddr8_data VALUES (20, '08002b01:02030405'); INSERT INTO macaddr8_data VALUES (21, '08002b0102030405'); SELECT * FROM macaddr8_data ORDER BY 1; CREATE INDEX macaddr8_data_btree ON macaddr8_data USING btree (b); CREATE INDEX macaddr8_data_hash ON macaddr8_data USING HASH (b); SELECT a, b, trunc(b) FROM macaddr8_data ORDER BY 2, 1; SELECT b < '08:00:2b:01:02:04' FROM macaddr8_data WHERE a = 1; -- true SELECT b > '08:00:2b:ff:fe:01:02:04' FROM macaddr8_data WHERE a = 1; -- false SELECT b > '08:00:2b:ff:fe:01:02:03' FROM macaddr8_data WHERE a = 1; -- false SELECT b::macaddr <= '08:00:2b:01:02:04' FROM macaddr8_data WHERE a = 1; -- true SELECT b::macaddr >= '08:00:2b:01:02:04' FROM macaddr8_data WHERE a = 1; -- false SELECT b = '08:00:2b:ff:fe:01:02:03' FROM macaddr8_data WHERE a = 1; -- true SELECT b::macaddr <> '08:00:2b:01:02:04'::macaddr FROM macaddr8_data WHERE a = 1; -- true SELECT b::macaddr <> '08:00:2b:01:02:03'::macaddr FROM macaddr8_data WHERE a = 1; -- false SELECT b < '08:00:2b:01:02:03:04:06' FROM macaddr8_data WHERE a = 15; -- true SELECT b > '08:00:2b:01:02:03:04:06' FROM macaddr8_data WHERE a = 15; -- false SELECT b > '08:00:2b:01:02:03:04:05' FROM macaddr8_data WHERE a = 15; -- false SELECT b <= '08:00:2b:01:02:03:04:06' FROM macaddr8_data WHERE a = 15; -- true SELECT b >= '08:00:2b:01:02:03:04:06' FROM macaddr8_data WHERE a = 15; -- false SELECT b = '08:00:2b:01:02:03:04:05' FROM macaddr8_data WHERE a = 15; -- true SELECT b <> '08:00:2b:01:02:03:04:06' FROM macaddr8_data WHERE a = 15; -- true SELECT b <> '08:00:2b:01:02:03:04:05' FROM macaddr8_data WHERE a = 15; -- false SELECT ~ b FROM macaddr8_data; SELECT b & '00:00:00:ff:ff:ff' FROM macaddr8_data; SELECT b | '01:02:03:04:05:06' FROM macaddr8_data; DROP TABLE macaddr8_data; pgFormatter-4.2/t/pg-test-files/expected/matview.sql000066400000000000000000000254001361326045100225770ustar00rootroot00000000000000-- create a table to use as a basis for views and materialized views in various combinations CREATE TABLE mvtest_t ( id int NOT NULL PRIMARY KEY, type text NOT NULL, amt numeric NOT NULL ); INSERT INTO mvtest_t VALUES (1, 'x', 2), (2, 'x', 3), (3, 'y', 5), (4, 'y', 7), (5, 'z', 11); -- we want a view based on the table, too, since views present additional challenges CREATE VIEW mvtest_tv AS SELECT TYPE, sum(amt) AS totamt FROM mvtest_t GROUP BY TYPE; SELECT * FROM mvtest_tv ORDER BY TYPE; -- create a materialized view with no data, and confirm correct behavior EXPLAIN ( COSTS OFF ) CREATE MATERIALIZED VIEW mvtest_tm AS SELECT TYPE, sum(amt) AS totamt FROM mvtest_t GROUP BY TYPE WITH NO DATA; CREATE MATERIALIZED VIEW mvtest_tm AS SELECT TYPE, sum(amt) AS totamt FROM mvtest_t GROUP BY TYPE WITH NO DATA; SELECT relispopulated FROM pg_class WHERE oid = 'mvtest_tm'::regclass; SELECT * FROM mvtest_tm ORDER BY TYPE; REFRESH MATERIALIZED VIEW mvtest_tm; SELECT relispopulated FROM pg_class WHERE oid = 'mvtest_tm'::regclass; CREATE UNIQUE INDEX mvtest_tm_type ON mvtest_tm (TYPE); SELECT * FROM mvtest_tm ORDER BY TYPE; -- create various views EXPLAIN ( COSTS OFF ) CREATE MATERIALIZED VIEW mvtest_tvm AS SELECT * FROM mvtest_tv ORDER BY TYPE; CREATE MATERIALIZED VIEW mvtest_tvm AS SELECT * FROM mvtest_tv ORDER BY TYPE; SELECT * FROM mvtest_tvm; CREATE MATERIALIZED VIEW mvtest_tmm AS SELECT sum(totamt) AS grandtot FROM mvtest_tm; CREATE MATERIALIZED VIEW mvtest_tvmm AS SELECT sum(totamt) AS grandtot FROM mvtest_tvm; CREATE UNIQUE INDEX mvtest_tvmm_expr ON mvtest_tvmm ((grandtot > 0)); CREATE UNIQUE INDEX mvtest_tvmm_pred ON mvtest_tvmm (grandtot) WHERE grandtot < 0; CREATE VIEW mvtest_tvv AS SELECT sum(totamt) AS grandtot FROM mvtest_tv; EXPLAIN ( COSTS OFF ) CREATE MATERIALIZED VIEW mvtest_tvvm AS SELECT * FROM mvtest_tvv; CREATE MATERIALIZED VIEW mvtest_tvvm AS SELECT * FROM mvtest_tvv; CREATE VIEW mvtest_tvvmv AS SELECT * FROM mvtest_tvvm; CREATE MATERIALIZED VIEW mvtest_bb AS SELECT * FROM mvtest_tvvmv; CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); -- check that plans seem reasonable \d+ mvtest_tvm \d+ mvtest_tvm \d+ mvtest_tvvm \d+ mvtest_bb -- test schema behavior CREATE SCHEMA mvtest_mvschema; ALTER MATERIALIZED VIEW mvtest_tvm SET SCHEMA mvtest_mvschema; \d+ mvtest_tvm \d+ mvtest_tvmm SET search_path = mvtest_mvschema, public; \d+ mvtest_tvm -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); -- confirm pre- and post-refresh contents of fairly simple materialized views SELECT * FROM mvtest_tm ORDER BY TYPE; SELECT * FROM mvtest_tvm ORDER BY TYPE; REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_tm; REFRESH MATERIALIZED VIEW mvtest_tvm; SELECT * FROM mvtest_tm ORDER BY TYPE; SELECT * FROM mvtest_tvm ORDER BY TYPE; RESET search_path; -- confirm pre- and post-refresh contents of nested materialized views EXPLAIN ( COSTS OFF ) SELECT * FROM mvtest_tmm; EXPLAIN ( COSTS OFF ) SELECT * FROM mvtest_tvmm; EXPLAIN ( COSTS OFF ) SELECT * FROM mvtest_tvvm; SELECT * FROM mvtest_tmm; SELECT * FROM mvtest_tvmm; SELECT * FROM mvtest_tvvm; REFRESH MATERIALIZED VIEW mvtest_tmm; REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_tvmm; REFRESH MATERIALIZED VIEW mvtest_tvmm; REFRESH MATERIALIZED VIEW mvtest_tvvm; EXPLAIN ( COSTS OFF ) SELECT * FROM mvtest_tmm; EXPLAIN ( COSTS OFF ) SELECT * FROM mvtest_tvmm; EXPLAIN ( COSTS OFF ) SELECT * FROM mvtest_tvvm; SELECT * FROM mvtest_tmm; SELECT * FROM mvtest_tvmm; SELECT * FROM mvtest_tvvm; -- test diemv when the mv does not exist DROP MATERIALIZED VIEW IF EXISTS no_such_mv; -- make sure invalid combination of options is prohibited REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_tvmm WITH NO DATA; -- no tuple locks on materialized views SELECT * FROM mvtest_tvvm FOR SHARE; -- test join of mv and view SELECT TYPE, m.totamt AS mtot, v.totamt AS vtot FROM mvtest_tm m LEFT JOIN mvtest_tv v USING (TYPE) ORDER BY TYPE; -- make sure that dependencies are reported properly when they block the drop DROP TABLE mvtest_t; -- make sure dependencies are dropped and reported -- and make sure that transactional behavior is correct on rollback -- incidentally leaving some interesting materialized views for pg_dump testing BEGIN; DROP TABLE mvtest_t CASCADE; ROLLBACK; -- some additional tests not using base tables CREATE VIEW mvtest_vt1 AS SELECT 1 moo; CREATE VIEW mvtest_vt2 AS SELECT moo, 2 * moo FROM mvtest_vt1 UNION ALL SELECT moo, 3 * moo FROM mvtest_vt1; \d+ mvtest_vt2 CREATE MATERIALIZED VIEW mv_test2 AS SELECT moo, 2 * moo FROM mvtest_vt2 UNION ALL SELECT moo, 3 * moo FROM mvtest_vt2; \d+ mv_test2 CREATE MATERIALIZED VIEW mv_test3 AS SELECT * FROM mv_test2 WHERE moo = 12345; SELECT relispopulated FROM pg_class WHERE oid = 'mv_test3'::regclass; DROP VIEW mvtest_vt1 CASCADE; -- test that duplicate values on unique index prevent refresh CREATE TABLE mvtest_foo ( a, b ) AS VALUES ( 1, 10 ); CREATE MATERIALIZED VIEW mvtest_mv AS SELECT * FROM mvtest_foo; CREATE UNIQUE INDEX ON mvtest_mv (a); INSERT INTO mvtest_foo SELECT * FROM mvtest_foo; REFRESH MATERIALIZED VIEW mvtest_mv; REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_mv; DROP TABLE mvtest_foo CASCADE; -- make sure that all columns covered by unique indexes works CREATE TABLE mvtest_foo ( a, b, c ) AS VALUES ( 1, 2, 3 ); CREATE MATERIALIZED VIEW mvtest_mv AS SELECT * FROM mvtest_foo; CREATE UNIQUE INDEX ON mvtest_mv (a); CREATE UNIQUE INDEX ON mvtest_mv (b); CREATE UNIQUE INDEX ON mvtest_mv (c); INSERT INTO mvtest_foo VALUES (2, 3, 4); INSERT INTO mvtest_foo VALUES (3, 4, 5); REFRESH MATERIALIZED VIEW mvtest_mv; REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_mv; DROP TABLE mvtest_foo CASCADE; -- allow subquery to reference unpopulated matview if WITH NO DATA is specified CREATE MATERIALIZED VIEW mvtest_mv1 AS SELECT 1 AS col1 WITH NO DATA; CREATE MATERIALIZED VIEW mvtest_mv2 AS SELECT * FROM mvtest_mv1 WHERE col1 = ( SELECT LEAST (col1) FROM mvtest_mv1 ) WITH NO DATA; DROP MATERIALIZED VIEW mvtest_mv1 CASCADE; -- make sure that types with unusual equality tests work CREATE TABLE mvtest_boxes ( id serial PRIMARY KEY, b box ); INSERT INTO mvtest_boxes (b) VALUES ('(32,32),(31,31)'), ('(2.0000004,2.0000004),(1,1)'), ('(1.9999996,1.9999996),(1,1)'); CREATE MATERIALIZED VIEW mvtest_boxmv AS SELECT * FROM mvtest_boxes; CREATE UNIQUE INDEX mvtest_boxmv_id ON mvtest_boxmv (id); UPDATE mvtest_boxes SET b = '(2,2),(1,1)' WHERE id = 2; REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_boxmv; SELECT * FROM mvtest_boxmv ORDER BY id; DROP TABLE mvtest_boxes CASCADE; -- make sure that column names are handled correctly CREATE TABLE mvtest_v ( i int, j int ); CREATE MATERIALIZED VIEW mvtest_mv_v (ii, jj, kk) AS SELECT i, j FROM mvtest_v; -- error CREATE MATERIALIZED VIEW mvtest_mv_v (ii, jj) AS SELECT i, j FROM mvtest_v; -- ok CREATE MATERIALIZED VIEW mvtest_mv_v_2 (ii) AS SELECT i, j FROM mvtest_v; -- ok CREATE MATERIALIZED VIEW mvtest_mv_v_3 (ii, jj, kk) AS SELECT i, j FROM mvtest_v WITH NO DATA; -- error CREATE MATERIALIZED VIEW mvtest_mv_v_3 (ii, jj) AS SELECT i, j FROM mvtest_v WITH NO DATA; -- ok CREATE MATERIALIZED VIEW mvtest_mv_v_4 (ii) AS SELECT i, j FROM mvtest_v WITH NO DATA; -- ok ALTER TABLE mvtest_v RENAME COLUMN i TO x; INSERT INTO mvtest_v VALUES (1, 2); CREATE UNIQUE INDEX mvtest_mv_v_ii ON mvtest_mv_v (ii); REFRESH MATERIALIZED VIEW mvtest_mv_v; UPDATE mvtest_v SET j = 3 WHERE x = 1; REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_mv_v; REFRESH MATERIALIZED VIEW mvtest_mv_v_2; REFRESH MATERIALIZED VIEW mvtest_mv_v_3; REFRESH MATERIALIZED VIEW mvtest_mv_v_4; SELECT * FROM mvtest_v; SELECT * FROM mvtest_mv_v; SELECT * FROM mvtest_mv_v_2; SELECT * FROM mvtest_mv_v_3; SELECT * FROM mvtest_mv_v_4; DROP TABLE mvtest_v CASCADE; -- Check that unknown literals are converted to "text" in CREATE MATVIEW, -- so that we don't end up with unknown-type columns. CREATE MATERIALIZED VIEW mv_unspecified_types AS SELECT 42 AS i, 42.5 AS num, 'foo' AS u, 'foo'::unknown AS u2, NULL AS n; \d+ mv_unspecified_types SELECT * FROM mv_unspecified_types; DROP MATERIALIZED VIEW mv_unspecified_types; -- make sure that create WITH NO DATA does not plan the query (bug #13907) CREATE MATERIALIZED VIEW mvtest_error AS SELECT 1 / 0 AS x; -- fail CREATE MATERIALIZED VIEW mvtest_error AS SELECT 1 / 0 AS x WITH NO data; REFRESH MATERIALIZED VIEW mvtest_error; -- fail here DROP MATERIALIZED VIEW mvtest_error; -- make sure that matview rows can be referenced as source rows (bug #9398) CREATE TABLE mvtest_v AS SELECT generate_series(1, 10) AS a; CREATE MATERIALIZED VIEW mvtest_mv_v AS SELECT a FROM mvtest_v WHERE a <= 5; DELETE FROM mvtest_v WHERE EXISTS ( SELECT * FROM mvtest_mv_v WHERE mvtest_mv_v.a = mvtest_v.a); SELECT * FROM mvtest_v; SELECT * FROM mvtest_mv_v; DROP TABLE mvtest_v CASCADE; -- make sure running as superuser works when MV owned by another role (bug #11208) CREATE ROLE regress_user_mvtest; SET ROLE regress_user_mvtest; CREATE TABLE mvtest_foo_data AS SELECT i, md5(random()::text) FROM generate_series(1, 10) i; CREATE MATERIALIZED VIEW mvtest_mv_foo AS SELECT * FROM mvtest_foo_data; CREATE MATERIALIZED VIEW mvtest_mv_foo AS SELECT * FROM mvtest_foo_data; CREATE MATERIALIZED VIEW IF NOT EXISTS mvtest_mv_foo AS SELECT * FROM mvtest_foo_data; CREATE UNIQUE INDEX ON mvtest_mv_foo (i); RESET ROLE; REFRESH MATERIALIZED VIEW mvtest_mv_foo; REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_mv_foo; DROP OWNED BY regress_user_mvtest CASCADE; DROP ROLE regress_user_mvtest; -- make sure that create WITH NO DATA works via SPI BEGIN; CREATE FUNCTION mvtest_func () RETURNS void AS $$ BEGIN CREATE MATERIALIZED VIEW mvtest1 AS SELECT 1 AS x; CREATE MATERIALIZED VIEW mvtest2 AS SELECT 1 AS x WITH NO DATA; END; $$ LANGUAGE plpgsql; SELECT mvtest_func (); SELECT * FROM mvtest1; SELECT * FROM mvtest2; ROLLBACK; pgFormatter-4.2/t/pg-test-files/expected/misc_functions.sql000066400000000000000000000051671361326045100241560ustar00rootroot00000000000000-- -- num_nulls() -- SELECT num_nonnulls (NULL); SELECT num_nonnulls ('1'); SELECT num_nonnulls (NULL::text); SELECT num_nonnulls (NULL::text, NULL::int); SELECT num_nonnulls (1, 2, NULL::text, NULL::point, '', int8 '9', 1.0 / NULL); SELECT num_nonnulls (VARIADIC '{1,2,NULL,3}'::int[]); SELECT num_nonnulls (VARIADIC '{"1","2","3","4"}'::text[]); SELECT num_nonnulls (VARIADIC ARRAY ( SELECT CASE WHEN i <> 40 THEN i END FROM generate_series(1, 100) i)); SELECT num_nulls (NULL); SELECT num_nulls ('1'); SELECT num_nulls (NULL::text); SELECT num_nulls (NULL::text, NULL::int); SELECT num_nulls (1, 2, NULL::text, NULL::point, '', int8 '9', 1.0 / NULL); SELECT num_nulls (VARIADIC '{1,2,NULL,3}'::int[]); SELECT num_nulls (VARIADIC '{"1","2","3","4"}'::text[]); SELECT num_nulls (VARIADIC ARRAY ( SELECT CASE WHEN i <> 40 THEN i END FROM generate_series(1, 100) i)); -- special cases SELECT num_nonnulls (VARIADIC NULL::text[]); SELECT num_nonnulls (VARIADIC '{}'::int[]); SELECT num_nulls (VARIADIC NULL::text[]); SELECT num_nulls (VARIADIC '{}'::int[]); -- should fail, one or more arguments is required SELECT num_nonnulls (); SELECT num_nulls (); -- -- Test adding a support function to a subject function -- CREATE FUNCTION my_int_eq (int, int) RETURNS bool LANGUAGE internal STRICT IMMUTABLE PARALLEL SAFE AS $$ int4eq$$; -- By default, planner does not think that's selective EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1 a JOIN tenk1 b ON a.unique1 = b.unique1 WHERE my_int_eq (a.unique2, 42); -- With support function that knows it's int4eq, we get a different plan ALTER FUNCTION my_int_eq (int, int) SUPPORT test_support_func; EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1 a JOIN tenk1 b ON a.unique1 = b.unique1 WHERE my_int_eq (a.unique2, 42); -- Also test non-default rowcount estimate CREATE FUNCTION my_gen_series (int, int) RETURNS SETOF integer LANGUAGE internal STRICT IMMUTABLE PARALLEL SAFE AS $$ generate_series_int4$$ SUPPORT test_support_func; EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1 a JOIN my_gen_series (1, 1000) g ON a.unique1 = g; EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1 a JOIN my_gen_series (1, 10) g ON a.unique1 = g; pgFormatter-4.2/t/pg-test-files/expected/misc_sanity.sql000066400000000000000000000104411361326045100234440ustar00rootroot00000000000000-- -- MISC_SANITY -- Sanity checks for common errors in making system tables that don't fit -- comfortably into either opr_sanity or type_sanity. -- -- Every test failure in this file should be closely inspected. -- The description of the failing test should be read carefully before -- adjusting the expected output. In most cases, the queries should -- not find *any* matching entries. -- -- NB: run this test early, because some later tests create bogus entries. -- **************** pg_depend **************** -- Look for illegal values in pg_depend fields. -- classid/objid can be zero, but only in 'p' entries SELECT * FROM pg_depend AS d1 WHERE refclassid = 0 OR refobjid = 0 OR deptype NOT IN ('a', 'e', 'i', 'n', 'p') OR (deptype != 'p' AND (classid = 0 OR objid = 0)) OR (deptype = 'p' AND (classid != 0 OR objid != 0 OR objsubid != 0)); -- **************** pg_shdepend **************** -- Look for illegal values in pg_shdepend fields. -- classid/objid can be zero, but only in 'p' entries SELECT * FROM pg_shdepend AS d1 WHERE refclassid = 0 OR refobjid = 0 OR deptype NOT IN ('a', 'o', 'p', 'r') OR (deptype != 'p' AND (classid = 0 OR objid = 0)) OR (deptype = 'p' AND (dbid != 0 OR classid != 0 OR objid != 0 OR objsubid != 0)); -- Check each OID-containing system catalog to see if its lowest-numbered OID -- is pinned. If not, and if that OID was generated during initdb, then -- perhaps initdb forgot to scan that catalog for pinnable entries. -- Generally, it's okay for a catalog to be listed in the output of this -- test if that catalog is scanned by initdb.c's setup_depend() function; -- whatever OID the test is complaining about must have been added later -- in initdb, where it intentionally isn't pinned. Legitimate exceptions -- to that rule are listed in the comments in setup_depend(). DO $$ DECLARE relnm text; reloid oid; shared bool; lowoid oid; pinned bool; BEGIN FOR relnm, reloid, shared IN SELECT relname, oid, relisshared FROM pg_class WHERE EXISTS ( SELECT * FROM pg_attribute WHERE attrelid = pg_class.oid AND attname = 'oid') AND relkind = 'r' AND oid < 16384 ORDER BY 1 LOOP EXECUTE 'select min(oid) from ' || relnm INTO lowoid; CONTINUE WHEN lowoid IS NULL OR lowoid >= 16384; IF shared THEN pinned := EXISTS ( SELECT 1 FROM pg_shdepend WHERE refclassid = reloid AND refobjid = lowoid AND deptype = 'p'); ELSE pinned := EXISTS ( SELECT 1 FROM pg_depend WHERE refclassid = reloid AND refobjid = lowoid AND deptype = 'p'); END IF; IF NOT pinned THEN RAISE notice '% contains unpinned initdb-created object(s)', relnm; END IF; END LOOP; end$$; -- **************** pg_class **************** -- Look for system tables with varlena columns but no toast table. All -- system tables with toastable columns should have toast tables, with -- the following exceptions: -- 1. pg_class, pg_attribute, and pg_index, due to fear of recursive -- dependencies as toast tables depend on them. -- 2. pg_largeobject and pg_largeobject_metadata. Large object catalogs -- and toast tables are mutually exclusive and large object data is handled -- as user data by pg_upgrade, which would cause failures. SELECT relname, attname, atttypid::regtype FROM pg_class c JOIN pg_attribute a ON c.oid = attrelid WHERE c.oid < 16384 AND reltoastrelid = 0 AND relkind = 'r' AND attstorage != 'p' ORDER BY 1, 2; pgFormatter-4.2/t/pg-test-files/expected/money.sql000066400000000000000000000102511361326045100222500ustar00rootroot00000000000000-- -- MONEY -- CREATE TABLE money_data ( m money ); INSERT INTO money_data VALUES ('123'); SELECT * FROM money_data; SELECT m + '123' FROM money_data; SELECT m + '123.45' FROM money_data; SELECT m - '123.45' FROM money_data; SELECT m / '2'::money FROM money_data; SELECT m * 2 FROM money_data; SELECT 2 * m FROM money_data; SELECT m / 2 FROM money_data; SELECT m * 2::int2 FROM money_data; SELECT 2::int2 * m FROM money_data; SELECT m / 2::int2 FROM money_data; SELECT m * 2::int8 FROM money_data; SELECT 2::int8 * m FROM money_data; SELECT m / 2::int8 FROM money_data; SELECT m * 2::float8 FROM money_data; SELECT 2::float8 * m FROM money_data; SELECT m / 2::float8 FROM money_data; SELECT m * 2::float4 FROM money_data; SELECT 2::float4 * m FROM money_data; SELECT m / 2::float4 FROM money_data; -- All true SELECT m = '$123.00' FROM money_data; SELECT m != '$124.00' FROM money_data; SELECT m <= '$123.00' FROM money_data; SELECT m >= '$123.00' FROM money_data; SELECT m < '$124.00' FROM money_data; SELECT m > '$122.00' FROM money_data; -- All false SELECT m = '$123.01' FROM money_data; SELECT m != '$123.00' FROM money_data; SELECT m <= '$122.99' FROM money_data; SELECT m >= '$123.01' FROM money_data; SELECT m > '$124.00' FROM money_data; SELECT m < '$122.00' FROM money_data; SELECT cashlarger(m, '$124.00') FROM money_data; SELECT cashsmaller(m, '$124.00') FROM money_data; SELECT cash_words(m) FROM money_data; SELECT cash_words(m + '1.23') FROM money_data; DELETE FROM money_data; INSERT INTO money_data VALUES ('$123.45'); SELECT * FROM money_data; DELETE FROM money_data; INSERT INTO money_data VALUES ('$123.451'); SELECT * FROM money_data; DELETE FROM money_data; INSERT INTO money_data VALUES ('$123.454'); SELECT * FROM money_data; DELETE FROM money_data; INSERT INTO money_data VALUES ('$123.455'); SELECT * FROM money_data; DELETE FROM money_data; INSERT INTO money_data VALUES ('$123.456'); SELECT * FROM money_data; DELETE FROM money_data; INSERT INTO money_data VALUES ('$123.459'); SELECT * FROM money_data; -- input checks SELECT '1234567890'::money; SELECT '12345678901234567'::money; SELECT '123456789012345678'::money; SELECT '9223372036854775807'::money; SELECT '-12345'::money; SELECT '-1234567890'::money; SELECT '-12345678901234567'::money; SELECT '-123456789012345678'::money; SELECT '-9223372036854775808'::money; -- special characters SELECT '(1)'::money; SELECT '($123,456.78)'::money; -- documented minimums and maximums SELECT '-92233720368547758.08'::money; SELECT '92233720368547758.07'::money; SELECT '-92233720368547758.09'::money; SELECT '92233720368547758.08'::money; -- rounding SELECT '-92233720368547758.085'::money; SELECT '92233720368547758.075'::money; -- rounding vs. truncation in division SELECT '878.08'::money / 11::float8; SELECT '878.08'::money / 11::float4; SELECT '878.08'::money / 11::bigint; SELECT '878.08'::money / 11::int; SELECT '878.08'::money / 11::smallint; -- check for precision loss in division SELECT '90000000000000099.00'::money / 10::bigint; SELECT '90000000000000099.00'::money / 10::int; SELECT '90000000000000099.00'::money / 10::smallint; -- Cast int4/int8/numeric to money SELECT 1234567890::money; SELECT 12345678901234567::money; SELECT (- 12345)::money; SELECT (- 1234567890)::money; SELECT (- 12345678901234567)::money; SELECT 1234567890::int4::money; SELECT 12345678901234567::int8::money; SELECT 12345678901234567::numeric::money; SELECT (- 1234567890)::int4::money; SELECT (- 12345678901234567)::int8::money; SELECT (- 12345678901234567)::numeric::money; -- Cast from money SELECT '12345678901234567'::money::numeric; SELECT '-12345678901234567'::money::numeric; pgFormatter-4.2/t/pg-test-files/expected/name.sql000066400000000000000000000074541361326045100220540ustar00rootroot00000000000000-- -- NAME -- all inputs are silently truncated at NAMEDATALEN-1 (63) characters -- -- fixed-length by reference SELECT name 'name string' = name 'name string' AS "True"; SELECT name 'name string' = name 'name string ' AS "False"; -- -- -- CREATE TABLE NAME_TBL ( f1 name ); INSERT INTO NAME_TBL (f1) VALUES ('1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR'); INSERT INTO NAME_TBL (f1) VALUES ('1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqr'); INSERT INTO NAME_TBL (f1) VALUES ('asdfghjkl;'); INSERT INTO NAME_TBL (f1) VALUES ('343f%2a'); INSERT INTO NAME_TBL (f1) VALUES ('d34aaasdf'); INSERT INTO NAME_TBL (f1) VALUES (''); INSERT INTO NAME_TBL (f1) VALUES ('1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ'); SELECT '' AS seven, * FROM NAME_TBL; SELECT '' AS six, c.f1 FROM NAME_TBL c WHERE c.f1 <> '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR'; SELECT '' AS one, c.f1 FROM NAME_TBL c WHERE c.f1 = '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR'; SELECT '' AS three, c.f1 FROM NAME_TBL c WHERE c.f1 < '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR'; SELECT '' AS four, c.f1 FROM NAME_TBL c WHERE c.f1 <= '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR'; SELECT '' AS three, c.f1 FROM NAME_TBL c WHERE c.f1 > '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR'; SELECT '' AS four, c.f1 FROM NAME_TBL c WHERE c.f1 >= '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR'; SELECT '' AS seven, c.f1 FROM NAME_TBL c WHERE c.f1 ~ '.*'; SELECT '' AS zero, c.f1 FROM NAME_TBL c WHERE c.f1 !~ '.*'; SELECT '' AS three, c.f1 FROM NAME_TBL c WHERE c.f1 ~ '[0-9]'; SELECT '' AS two, c.f1 FROM NAME_TBL c WHERE c.f1 ~ '.*asdf.*'; DROP TABLE NAME_TBL; DO $$ DECLARE r text[]; BEGIN r := parse_ident ('Schemax.Tabley'); RAISE NOTICE '%', format('%I.%I', r[1], r[2]); r := parse_ident ('"SchemaX"."TableY"'); RAISE NOTICE '%', format('%I.%I', r[1], r[2]); END; $$; SELECT parse_ident ('foo.boo'); SELECT parse_ident ('foo.boo[]'); -- should fail SELECT parse_ident ('foo.boo[]', STRICT => FALSE); -- ok -- should fail SELECT parse_ident (' '); SELECT parse_ident (' .aaa'); SELECT parse_ident (' aaa . '); SELECT parse_ident ('aaa.a%b'); SELECT parse_ident (E'X\rXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'); SELECT length(a[1]), length(a[2]) FROM parse_ident ('"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy') AS a; SELECT parse_ident (' first . " second " ." third ". " ' || repeat('x', 66) || '"'); SELECT parse_ident (' first . " second " ." third ". " ' || repeat('x', 66) || '"')::name[]; SELECT parse_ident (E'"c".X XXXX\002XXXXXX'); SELECT parse_ident ('1020'); SELECT parse_ident ('10.20'); SELECT parse_ident ('.'); SELECT parse_ident ('.1020'); SELECT parse_ident ('xxx.1020'); pgFormatter-4.2/t/pg-test-files/expected/namespace.sql000066400000000000000000000030371361326045100230610ustar00rootroot00000000000000-- -- Regression tests for schemas (namespaces) -- CREATE SCHEMA test_ns_schema_1 CREATE UNIQUE INDEX abc_a_idx ON abc ( a) CREATE VIEW abc_view AS SELECT a + 1 AS a, b + 1 AS b FROM abc CREATE TABLE abc ( a serial, b int UNIQUE ); -- verify that the objects were created SELECT COUNT(*) FROM pg_class WHERE relnamespace = ( SELECT oid FROM pg_namespace WHERE nspname = 'test_ns_schema_1'); INSERT INTO test_ns_schema_1.abc DEFAULT VALUES; INSERT INTO test_ns_schema_1.abc DEFAULT VALUES; INSERT INTO test_ns_schema_1.abc DEFAULT VALUES; SELECT * FROM test_ns_schema_1.abc; SELECT * FROM test_ns_schema_1.abc_view; ALTER SCHEMA test_ns_schema_1 RENAME TO test_ns_schema_renamed; SELECT COUNT(*) FROM pg_class WHERE relnamespace = ( SELECT oid FROM pg_namespace WHERE nspname = 'test_ns_schema_1'); -- test IF NOT EXISTS cases CREATE SCHEMA test_ns_schema_renamed; -- fail, already exists CREATE SCHEMA IF NOT EXISTS test_ns_schema_renamed; -- ok with notice CREATE SCHEMA IF NOT EXISTS test_ns_schema_renamed -- fail, disallowed CREATE TABLE abc ( a serial, b int UNIQUE ); DROP SCHEMA test_ns_schema_renamed CASCADE; -- verify that the objects were dropped SELECT COUNT(*) FROM pg_class WHERE relnamespace = ( SELECT oid FROM pg_namespace WHERE nspname = 'test_ns_schema_renamed'); pgFormatter-4.2/t/pg-test-files/expected/numeric.sql000066400000000000000000001426511361326045100225750ustar00rootroot00000000000000-- -- NUMERIC -- CREATE TABLE num_data ( id int4, val numeric(210, 10) ); CREATE TABLE num_exp_add ( id1 int4, id2 int4, expected numeric(210, 10) ); CREATE TABLE num_exp_sub ( id1 int4, id2 int4, expected numeric(210, 10) ); CREATE TABLE num_exp_div ( id1 int4, id2 int4, expected numeric(210, 10) ); CREATE TABLE num_exp_mul ( id1 int4, id2 int4, expected numeric(210, 10) ); CREATE TABLE num_exp_sqrt ( id int4, expected numeric(210, 10) ); CREATE TABLE num_exp_ln ( id int4, expected numeric(210, 10) ); CREATE TABLE num_exp_log10 ( id int4, expected numeric(210, 10) ); CREATE TABLE num_exp_power_10_ln ( id int4, expected numeric(210, 10) ); CREATE TABLE num_result ( id1 int4, id2 int4, result numeric(210, 10) ); -- ****************************** -- * The following EXPECTED results are computed by bc(1) -- * with a scale of 200 -- ****************************** BEGIN TRANSACTION; INSERT INTO num_exp_add VALUES (0, 0, '0'); INSERT INTO num_exp_sub VALUES (0, 0, '0'); INSERT INTO num_exp_mul VALUES (0, 0, '0'); INSERT INTO num_exp_div VALUES (0, 0, 'NaN'); INSERT INTO num_exp_add VALUES (0, 1, '0'); INSERT INTO num_exp_sub VALUES (0, 1, '0'); INSERT INTO num_exp_mul VALUES (0, 1, '0'); INSERT INTO num_exp_div VALUES (0, 1, 'NaN'); INSERT INTO num_exp_add VALUES (0, 2, '-34338492.215397047'); INSERT INTO num_exp_sub VALUES (0, 2, '34338492.215397047'); INSERT INTO num_exp_mul VALUES (0, 2, '0'); INSERT INTO num_exp_div VALUES (0, 2, '0'); INSERT INTO num_exp_add VALUES (0, 3, '4.31'); INSERT INTO num_exp_sub VALUES (0, 3, '-4.31'); INSERT INTO num_exp_mul VALUES (0, 3, '0'); INSERT INTO num_exp_div VALUES (0, 3, '0'); INSERT INTO num_exp_add VALUES (0, 4, '7799461.4119'); INSERT INTO num_exp_sub VALUES (0, 4, '-7799461.4119'); INSERT INTO num_exp_mul VALUES (0, 4, '0'); INSERT INTO num_exp_div VALUES (0, 4, '0'); INSERT INTO num_exp_add VALUES (0, 5, '16397.038491'); INSERT INTO num_exp_sub VALUES (0, 5, '-16397.038491'); INSERT INTO num_exp_mul VALUES (0, 5, '0'); INSERT INTO num_exp_div VALUES (0, 5, '0'); INSERT INTO num_exp_add VALUES (0, 6, '93901.57763026'); INSERT INTO num_exp_sub VALUES (0, 6, '-93901.57763026'); INSERT INTO num_exp_mul VALUES (0, 6, '0'); INSERT INTO num_exp_div VALUES (0, 6, '0'); INSERT INTO num_exp_add VALUES (0, 7, '-83028485'); INSERT INTO num_exp_sub VALUES (0, 7, '83028485'); INSERT INTO num_exp_mul VALUES (0, 7, '0'); INSERT INTO num_exp_div VALUES (0, 7, '0'); INSERT INTO num_exp_add VALUES (0, 8, '74881'); INSERT INTO num_exp_sub VALUES (0, 8, '-74881'); INSERT INTO num_exp_mul VALUES (0, 8, '0'); INSERT INTO num_exp_div VALUES (0, 8, '0'); INSERT INTO num_exp_add VALUES (0, 9, '-24926804.045047420'); INSERT INTO num_exp_sub VALUES (0, 9, '24926804.045047420'); INSERT INTO num_exp_mul VALUES (0, 9, '0'); INSERT INTO num_exp_div VALUES (0, 9, '0'); INSERT INTO num_exp_add VALUES (1, 0, '0'); INSERT INTO num_exp_sub VALUES (1, 0, '0'); INSERT INTO num_exp_mul VALUES (1, 0, '0'); INSERT INTO num_exp_div VALUES (1, 0, 'NaN'); INSERT INTO num_exp_add VALUES (1, 1, '0'); INSERT INTO num_exp_sub VALUES (1, 1, '0'); INSERT INTO num_exp_mul VALUES (1, 1, '0'); INSERT INTO num_exp_div VALUES (1, 1, 'NaN'); INSERT INTO num_exp_add VALUES (1, 2, '-34338492.215397047'); INSERT INTO num_exp_sub VALUES (1, 2, '34338492.215397047'); INSERT INTO num_exp_mul VALUES (1, 2, '0'); INSERT INTO num_exp_div VALUES (1, 2, '0'); INSERT INTO num_exp_add VALUES (1, 3, '4.31'); INSERT INTO num_exp_sub VALUES (1, 3, '-4.31'); INSERT INTO num_exp_mul VALUES (1, 3, '0'); INSERT INTO num_exp_div VALUES (1, 3, '0'); INSERT INTO num_exp_add VALUES (1, 4, '7799461.4119'); INSERT INTO num_exp_sub VALUES (1, 4, '-7799461.4119'); INSERT INTO num_exp_mul VALUES (1, 4, '0'); INSERT INTO num_exp_div VALUES (1, 4, '0'); INSERT INTO num_exp_add VALUES (1, 5, '16397.038491'); INSERT INTO num_exp_sub VALUES (1, 5, '-16397.038491'); INSERT INTO num_exp_mul VALUES (1, 5, '0'); INSERT INTO num_exp_div VALUES (1, 5, '0'); INSERT INTO num_exp_add VALUES (1, 6, '93901.57763026'); INSERT INTO num_exp_sub VALUES (1, 6, '-93901.57763026'); INSERT INTO num_exp_mul VALUES (1, 6, '0'); INSERT INTO num_exp_div VALUES (1, 6, '0'); INSERT INTO num_exp_add VALUES (1, 7, '-83028485'); INSERT INTO num_exp_sub VALUES (1, 7, '83028485'); INSERT INTO num_exp_mul VALUES (1, 7, '0'); INSERT INTO num_exp_div VALUES (1, 7, '0'); INSERT INTO num_exp_add VALUES (1, 8, '74881'); INSERT INTO num_exp_sub VALUES (1, 8, '-74881'); INSERT INTO num_exp_mul VALUES (1, 8, '0'); INSERT INTO num_exp_div VALUES (1, 8, '0'); INSERT INTO num_exp_add VALUES (1, 9, '-24926804.045047420'); INSERT INTO num_exp_sub VALUES (1, 9, '24926804.045047420'); INSERT INTO num_exp_mul VALUES (1, 9, '0'); INSERT INTO num_exp_div VALUES (1, 9, '0'); INSERT INTO num_exp_add VALUES (2, 0, '-34338492.215397047'); INSERT INTO num_exp_sub VALUES (2, 0, '-34338492.215397047'); INSERT INTO num_exp_mul VALUES (2, 0, '0'); INSERT INTO num_exp_div VALUES (2, 0, 'NaN'); INSERT INTO num_exp_add VALUES (2, 1, '-34338492.215397047'); INSERT INTO num_exp_sub VALUES (2, 1, '-34338492.215397047'); INSERT INTO num_exp_mul VALUES (2, 1, '0'); INSERT INTO num_exp_div VALUES (2, 1, 'NaN'); INSERT INTO num_exp_add VALUES (2, 2, '-68676984.430794094'); INSERT INTO num_exp_sub VALUES (2, 2, '0'); INSERT INTO num_exp_mul VALUES (2, 2, '1179132047626883.596862135856320209'); INSERT INTO num_exp_div VALUES (2, 2, '1.00000000000000000000'); INSERT INTO num_exp_add VALUES (2, 3, '-34338487.905397047'); INSERT INTO num_exp_sub VALUES (2, 3, '-34338496.525397047'); INSERT INTO num_exp_mul VALUES (2, 3, '-147998901.44836127257'); INSERT INTO num_exp_div VALUES (2, 3, '-7967167.56737750510440835266'); INSERT INTO num_exp_add VALUES (2, 4, '-26539030.803497047'); INSERT INTO num_exp_sub VALUES (2, 4, '-42137953.627297047'); INSERT INTO num_exp_mul VALUES (2, 4, '-267821744976817.8111137106593'); INSERT INTO num_exp_div VALUES (2, 4, '-4.40267480046830116685'); INSERT INTO num_exp_add VALUES (2, 5, '-34322095.176906047'); INSERT INTO num_exp_sub VALUES (2, 5, '-34354889.253888047'); INSERT INTO num_exp_mul VALUES (2, 5, '-563049578578.769242506736077'); INSERT INTO num_exp_div VALUES (2, 5, '-2094.18866914563535496429'); INSERT INTO num_exp_add VALUES (2, 6, '-34244590.637766787'); INSERT INTO num_exp_sub VALUES (2, 6, '-34432393.793027307'); INSERT INTO num_exp_mul VALUES (2, 6, '-3224438592470.18449811926184222'); INSERT INTO num_exp_div VALUES (2, 6, '-365.68599891479766440940'); INSERT INTO num_exp_add VALUES (2, 7, '-117366977.215397047'); INSERT INTO num_exp_sub VALUES (2, 7, '48689992.784602953'); INSERT INTO num_exp_mul VALUES (2, 7, '2851072985828710.485883795'); INSERT INTO num_exp_div VALUES (2, 7, '.41357483778485235518'); INSERT INTO num_exp_add VALUES (2, 8, '-34263611.215397047'); INSERT INTO num_exp_sub VALUES (2, 8, '-34413373.215397047'); INSERT INTO num_exp_mul VALUES (2, 8, '-2571300635581.146276407'); INSERT INTO num_exp_div VALUES (2, 8, '-458.57416721727870888476'); INSERT INTO num_exp_add VALUES (2, 9, '-59265296.260444467'); INSERT INTO num_exp_sub VALUES (2, 9, '-9411688.170349627'); INSERT INTO num_exp_mul VALUES (2, 9, '855948866655588.453741509242968740'); INSERT INTO num_exp_div VALUES (2, 9, '1.37757299946438931811'); INSERT INTO num_exp_add VALUES (3, 0, '4.31'); INSERT INTO num_exp_sub VALUES (3, 0, '4.31'); INSERT INTO num_exp_mul VALUES (3, 0, '0'); INSERT INTO num_exp_div VALUES (3, 0, 'NaN'); INSERT INTO num_exp_add VALUES (3, 1, '4.31'); INSERT INTO num_exp_sub VALUES (3, 1, '4.31'); INSERT INTO num_exp_mul VALUES (3, 1, '0'); INSERT INTO num_exp_div VALUES (3, 1, 'NaN'); INSERT INTO num_exp_add VALUES (3, 2, '-34338487.905397047'); INSERT INTO num_exp_sub VALUES (3, 2, '34338496.525397047'); INSERT INTO num_exp_mul VALUES (3, 2, '-147998901.44836127257'); INSERT INTO num_exp_div VALUES (3, 2, '-.00000012551512084352'); INSERT INTO num_exp_add VALUES (3, 3, '8.62'); INSERT INTO num_exp_sub VALUES (3, 3, '0'); INSERT INTO num_exp_mul VALUES (3, 3, '18.5761'); INSERT INTO num_exp_div VALUES (3, 3, '1.00000000000000000000'); INSERT INTO num_exp_add VALUES (3, 4, '7799465.7219'); INSERT INTO num_exp_sub VALUES (3, 4, '-7799457.1019'); INSERT INTO num_exp_mul VALUES (3, 4, '33615678.685289'); INSERT INTO num_exp_div VALUES (3, 4, '.00000055260225961552'); INSERT INTO num_exp_add VALUES (3, 5, '16401.348491'); INSERT INTO num_exp_sub VALUES (3, 5, '-16392.728491'); INSERT INTO num_exp_mul VALUES (3, 5, '70671.23589621'); INSERT INTO num_exp_div VALUES (3, 5, '.00026285234387695504'); INSERT INTO num_exp_add VALUES (3, 6, '93905.88763026'); INSERT INTO num_exp_sub VALUES (3, 6, '-93897.26763026'); INSERT INTO num_exp_mul VALUES (3, 6, '404715.7995864206'); INSERT INTO num_exp_div VALUES (3, 6, '.00004589912234457595'); INSERT INTO num_exp_add VALUES (3, 7, '-83028480.69'); INSERT INTO num_exp_sub VALUES (3, 7, '83028489.31'); INSERT INTO num_exp_mul VALUES (3, 7, '-357852770.35'); INSERT INTO num_exp_div VALUES (3, 7, '-.00000005190989574240'); INSERT INTO num_exp_add VALUES (3, 8, '74885.31'); INSERT INTO num_exp_sub VALUES (3, 8, '-74876.69'); INSERT INTO num_exp_mul VALUES (3, 8, '322737.11'); INSERT INTO num_exp_div VALUES (3, 8, '.00005755799201399553'); INSERT INTO num_exp_add VALUES (3, 9, '-24926799.735047420'); INSERT INTO num_exp_sub VALUES (3, 9, '24926808.355047420'); INSERT INTO num_exp_mul VALUES (3, 9, '-107434525.43415438020'); INSERT INTO num_exp_div VALUES (3, 9, '-.00000017290624149854'); INSERT INTO num_exp_add VALUES (4, 0, '7799461.4119'); INSERT INTO num_exp_sub VALUES (4, 0, '7799461.4119'); INSERT INTO num_exp_mul VALUES (4, 0, '0'); INSERT INTO num_exp_div VALUES (4, 0, 'NaN'); INSERT INTO num_exp_add VALUES (4, 1, '7799461.4119'); INSERT INTO num_exp_sub VALUES (4, 1, '7799461.4119'); INSERT INTO num_exp_mul VALUES (4, 1, '0'); INSERT INTO num_exp_div VALUES (4, 1, 'NaN'); INSERT INTO num_exp_add VALUES (4, 2, '-26539030.803497047'); INSERT INTO num_exp_sub VALUES (4, 2, '42137953.627297047'); INSERT INTO num_exp_mul VALUES (4, 2, '-267821744976817.8111137106593'); INSERT INTO num_exp_div VALUES (4, 2, '-.22713465002993920385'); INSERT INTO num_exp_add VALUES (4, 3, '7799465.7219'); INSERT INTO num_exp_sub VALUES (4, 3, '7799457.1019'); INSERT INTO num_exp_mul VALUES (4, 3, '33615678.685289'); INSERT INTO num_exp_div VALUES (4, 3, '1809619.81714617169373549883'); INSERT INTO num_exp_add VALUES (4, 4, '15598922.8238'); INSERT INTO num_exp_sub VALUES (4, 4, '0'); INSERT INTO num_exp_mul VALUES (4, 4, '60831598315717.14146161'); INSERT INTO num_exp_div VALUES (4, 4, '1.00000000000000000000'); INSERT INTO num_exp_add VALUES (4, 5, '7815858.450391'); INSERT INTO num_exp_sub VALUES (4, 5, '7783064.373409'); INSERT INTO num_exp_mul VALUES (4, 5, '127888068979.9935054429'); INSERT INTO num_exp_div VALUES (4, 5, '475.66281046305802686061'); INSERT INTO num_exp_add VALUES (4, 6, '7893362.98953026'); INSERT INTO num_exp_sub VALUES (4, 6, '7705559.83426974'); INSERT INTO num_exp_mul VALUES (4, 6, '732381731243.745115764094'); INSERT INTO num_exp_div VALUES (4, 6, '83.05996138436129499606'); INSERT INTO num_exp_add VALUES (4, 7, '-75229023.5881'); INSERT INTO num_exp_sub VALUES (4, 7, '90827946.4119'); INSERT INTO num_exp_mul VALUES (4, 7, '-647577464846017.9715'); INSERT INTO num_exp_div VALUES (4, 7, '-.09393717604145131637'); INSERT INTO num_exp_add VALUES (4, 8, '7874342.4119'); INSERT INTO num_exp_sub VALUES (4, 8, '7724580.4119'); INSERT INTO num_exp_mul VALUES (4, 8, '584031469984.4839'); INSERT INTO num_exp_div VALUES (4, 8, '104.15808298366741897143'); INSERT INTO num_exp_add VALUES (4, 9, '-17127342.633147420'); INSERT INTO num_exp_sub VALUES (4, 9, '32726265.456947420'); INSERT INTO num_exp_mul VALUES (4, 9, '-194415646271340.1815956522980'); INSERT INTO num_exp_div VALUES (4, 9, '-.31289456112403769409'); INSERT INTO num_exp_add VALUES (5, 0, '16397.038491'); INSERT INTO num_exp_sub VALUES (5, 0, '16397.038491'); INSERT INTO num_exp_mul VALUES (5, 0, '0'); INSERT INTO num_exp_div VALUES (5, 0, 'NaN'); INSERT INTO num_exp_add VALUES (5, 1, '16397.038491'); INSERT INTO num_exp_sub VALUES (5, 1, '16397.038491'); INSERT INTO num_exp_mul VALUES (5, 1, '0'); INSERT INTO num_exp_div VALUES (5, 1, 'NaN'); INSERT INTO num_exp_add VALUES (5, 2, '-34322095.176906047'); INSERT INTO num_exp_sub VALUES (5, 2, '34354889.253888047'); INSERT INTO num_exp_mul VALUES (5, 2, '-563049578578.769242506736077'); INSERT INTO num_exp_div VALUES (5, 2, '-.00047751189505192446'); INSERT INTO num_exp_add VALUES (5, 3, '16401.348491'); INSERT INTO num_exp_sub VALUES (5, 3, '16392.728491'); INSERT INTO num_exp_mul VALUES (5, 3, '70671.23589621'); INSERT INTO num_exp_div VALUES (5, 3, '3804.41728329466357308584'); INSERT INTO num_exp_add VALUES (5, 4, '7815858.450391'); INSERT INTO num_exp_sub VALUES (5, 4, '-7783064.373409'); INSERT INTO num_exp_mul VALUES (5, 4, '127888068979.9935054429'); INSERT INTO num_exp_div VALUES (5, 4, '.00210232958726897192'); INSERT INTO num_exp_add VALUES (5, 5, '32794.076982'); INSERT INTO num_exp_sub VALUES (5, 5, '0'); INSERT INTO num_exp_mul VALUES (5, 5, '268862871.275335557081'); INSERT INTO num_exp_div VALUES (5, 5, '1.00000000000000000000'); INSERT INTO num_exp_add VALUES (5, 6, '110298.61612126'); INSERT INTO num_exp_sub VALUES (5, 6, '-77504.53913926'); INSERT INTO num_exp_mul VALUES (5, 6, '1539707782.76899778633766'); INSERT INTO num_exp_div VALUES (5, 6, '.17461941433576102689'); INSERT INTO num_exp_add VALUES (5, 7, '-83012087.961509'); INSERT INTO num_exp_sub VALUES (5, 7, '83044882.038491'); INSERT INTO num_exp_mul VALUES (5, 7, '-1361421264394.416135'); INSERT INTO num_exp_div VALUES (5, 7, '-.00019748690453643710'); INSERT INTO num_exp_add VALUES (5, 8, '91278.038491'); INSERT INTO num_exp_sub VALUES (5, 8, '-58483.961509'); INSERT INTO num_exp_mul VALUES (5, 8, '1227826639.244571'); INSERT INTO num_exp_div VALUES (5, 8, '.21897461960978085228'); INSERT INTO num_exp_add VALUES (5, 9, '-24910407.006556420'); INSERT INTO num_exp_sub VALUES (5, 9, '24943201.083538420'); INSERT INTO num_exp_mul VALUES (5, 9, '-408725765384.257043660243220'); INSERT INTO num_exp_div VALUES (5, 9, '-.00065780749354660427'); INSERT INTO num_exp_add VALUES (6, 0, '93901.57763026'); INSERT INTO num_exp_sub VALUES (6, 0, '93901.57763026'); INSERT INTO num_exp_mul VALUES (6, 0, '0'); INSERT INTO num_exp_div VALUES (6, 0, 'NaN'); INSERT INTO num_exp_add VALUES (6, 1, '93901.57763026'); INSERT INTO num_exp_sub VALUES (6, 1, '93901.57763026'); INSERT INTO num_exp_mul VALUES (6, 1, '0'); INSERT INTO num_exp_div VALUES (6, 1, 'NaN'); INSERT INTO num_exp_add VALUES (6, 2, '-34244590.637766787'); INSERT INTO num_exp_sub VALUES (6, 2, '34432393.793027307'); INSERT INTO num_exp_mul VALUES (6, 2, '-3224438592470.18449811926184222'); INSERT INTO num_exp_div VALUES (6, 2, '-.00273458651128995823'); INSERT INTO num_exp_add VALUES (6, 3, '93905.88763026'); INSERT INTO num_exp_sub VALUES (6, 3, '93897.26763026'); INSERT INTO num_exp_mul VALUES (6, 3, '404715.7995864206'); INSERT INTO num_exp_div VALUES (6, 3, '21786.90896293735498839907'); INSERT INTO num_exp_add VALUES (6, 4, '7893362.98953026'); INSERT INTO num_exp_sub VALUES (6, 4, '-7705559.83426974'); INSERT INTO num_exp_mul VALUES (6, 4, '732381731243.745115764094'); INSERT INTO num_exp_div VALUES (6, 4, '.01203949512295682469'); INSERT INTO num_exp_add VALUES (6, 5, '110298.61612126'); INSERT INTO num_exp_sub VALUES (6, 5, '77504.53913926'); INSERT INTO num_exp_mul VALUES (6, 5, '1539707782.76899778633766'); INSERT INTO num_exp_div VALUES (6, 5, '5.72674008674192359679'); INSERT INTO num_exp_add VALUES (6, 6, '187803.15526052'); INSERT INTO num_exp_sub VALUES (6, 6, '0'); INSERT INTO num_exp_mul VALUES (6, 6, '8817506281.4517452372676676'); INSERT INTO num_exp_div VALUES (6, 6, '1.00000000000000000000'); INSERT INTO num_exp_add VALUES (6, 7, '-82934583.42236974'); INSERT INTO num_exp_sub VALUES (6, 7, '83122386.57763026'); INSERT INTO num_exp_mul VALUES (6, 7, '-7796505729750.37795610'); INSERT INTO num_exp_div VALUES (6, 7, '-.00113095617281538980'); INSERT INTO num_exp_add VALUES (6, 8, '168782.57763026'); INSERT INTO num_exp_sub VALUES (6, 8, '19020.57763026'); INSERT INTO num_exp_mul VALUES (6, 8, '7031444034.53149906'); INSERT INTO num_exp_div VALUES (6, 8, '1.25401073209839612184'); INSERT INTO num_exp_add VALUES (6, 9, '-24832902.467417160'); INSERT INTO num_exp_sub VALUES (6, 9, '25020705.622677680'); INSERT INTO num_exp_mul VALUES (6, 9, '-2340666225110.29929521292692920'); INSERT INTO num_exp_div VALUES (6, 9, '-.00376709254265256789'); INSERT INTO num_exp_add VALUES (7, 0, '-83028485'); INSERT INTO num_exp_sub VALUES (7, 0, '-83028485'); INSERT INTO num_exp_mul VALUES (7, 0, '0'); INSERT INTO num_exp_div VALUES (7, 0, 'NaN'); INSERT INTO num_exp_add VALUES (7, 1, '-83028485'); INSERT INTO num_exp_sub VALUES (7, 1, '-83028485'); INSERT INTO num_exp_mul VALUES (7, 1, '0'); INSERT INTO num_exp_div VALUES (7, 1, 'NaN'); INSERT INTO num_exp_add VALUES (7, 2, '-117366977.215397047'); INSERT INTO num_exp_sub VALUES (7, 2, '-48689992.784602953'); INSERT INTO num_exp_mul VALUES (7, 2, '2851072985828710.485883795'); INSERT INTO num_exp_div VALUES (7, 2, '2.41794207151503385700'); INSERT INTO num_exp_add VALUES (7, 3, '-83028480.69'); INSERT INTO num_exp_sub VALUES (7, 3, '-83028489.31'); INSERT INTO num_exp_mul VALUES (7, 3, '-357852770.35'); INSERT INTO num_exp_div VALUES (7, 3, '-19264149.65197215777262180974'); INSERT INTO num_exp_add VALUES (7, 4, '-75229023.5881'); INSERT INTO num_exp_sub VALUES (7, 4, '-90827946.4119'); INSERT INTO num_exp_mul VALUES (7, 4, '-647577464846017.9715'); INSERT INTO num_exp_div VALUES (7, 4, '-10.64541262725136247686'); INSERT INTO num_exp_add VALUES (7, 5, '-83012087.961509'); INSERT INTO num_exp_sub VALUES (7, 5, '-83044882.038491'); INSERT INTO num_exp_mul VALUES (7, 5, '-1361421264394.416135'); INSERT INTO num_exp_div VALUES (7, 5, '-5063.62688881730941836574'); INSERT INTO num_exp_add VALUES (7, 6, '-82934583.42236974'); INSERT INTO num_exp_sub VALUES (7, 6, '-83122386.57763026'); INSERT INTO num_exp_mul VALUES (7, 6, '-7796505729750.37795610'); INSERT INTO num_exp_div VALUES (7, 6, '-884.20756174009028770294'); INSERT INTO num_exp_add VALUES (7, 7, '-166056970'); INSERT INTO num_exp_sub VALUES (7, 7, '0'); INSERT INTO num_exp_mul VALUES (7, 7, '6893729321395225'); INSERT INTO num_exp_div VALUES (7, 7, '1.00000000000000000000'); INSERT INTO num_exp_add VALUES (7, 8, '-82953604'); INSERT INTO num_exp_sub VALUES (7, 8, '-83103366'); INSERT INTO num_exp_mul VALUES (7, 8, '-6217255985285'); INSERT INTO num_exp_div VALUES (7, 8, '-1108.80577182462841041118'); INSERT INTO num_exp_add VALUES (7, 9, '-107955289.045047420'); INSERT INTO num_exp_sub VALUES (7, 9, '-58101680.954952580'); INSERT INTO num_exp_mul VALUES (7, 9, '2069634775752159.035758700'); INSERT INTO num_exp_div VALUES (7, 9, '3.33089171198810413382'); INSERT INTO num_exp_add VALUES (8, 0, '74881'); INSERT INTO num_exp_sub VALUES (8, 0, '74881'); INSERT INTO num_exp_mul VALUES (8, 0, '0'); INSERT INTO num_exp_div VALUES (8, 0, 'NaN'); INSERT INTO num_exp_add VALUES (8, 1, '74881'); INSERT INTO num_exp_sub VALUES (8, 1, '74881'); INSERT INTO num_exp_mul VALUES (8, 1, '0'); INSERT INTO num_exp_div VALUES (8, 1, 'NaN'); INSERT INTO num_exp_add VALUES (8, 2, '-34263611.215397047'); INSERT INTO num_exp_sub VALUES (8, 2, '34413373.215397047'); INSERT INTO num_exp_mul VALUES (8, 2, '-2571300635581.146276407'); INSERT INTO num_exp_div VALUES (8, 2, '-.00218067233500788615'); INSERT INTO num_exp_add VALUES (8, 3, '74885.31'); INSERT INTO num_exp_sub VALUES (8, 3, '74876.69'); INSERT INTO num_exp_mul VALUES (8, 3, '322737.11'); INSERT INTO num_exp_div VALUES (8, 3, '17373.78190255220417633410'); INSERT INTO num_exp_add VALUES (8, 4, '7874342.4119'); INSERT INTO num_exp_sub VALUES (8, 4, '-7724580.4119'); INSERT INTO num_exp_mul VALUES (8, 4, '584031469984.4839'); INSERT INTO num_exp_div VALUES (8, 4, '.00960079113741758956'); INSERT INTO num_exp_add VALUES (8, 5, '91278.038491'); INSERT INTO num_exp_sub VALUES (8, 5, '58483.961509'); INSERT INTO num_exp_mul VALUES (8, 5, '1227826639.244571'); INSERT INTO num_exp_div VALUES (8, 5, '4.56673929509287019456'); INSERT INTO num_exp_add VALUES (8, 6, '168782.57763026'); INSERT INTO num_exp_sub VALUES (8, 6, '-19020.57763026'); INSERT INTO num_exp_mul VALUES (8, 6, '7031444034.53149906'); INSERT INTO num_exp_div VALUES (8, 6, '.79744134113322314424'); INSERT INTO num_exp_add VALUES (8, 7, '-82953604'); INSERT INTO num_exp_sub VALUES (8, 7, '83103366'); INSERT INTO num_exp_mul VALUES (8, 7, '-6217255985285'); INSERT INTO num_exp_div VALUES (8, 7, '-.00090187120721280172'); INSERT INTO num_exp_add VALUES (8, 8, '149762'); INSERT INTO num_exp_sub VALUES (8, 8, '0'); INSERT INTO num_exp_mul VALUES (8, 8, '5607164161'); INSERT INTO num_exp_div VALUES (8, 8, '1.00000000000000000000'); INSERT INTO num_exp_add VALUES (8, 9, '-24851923.045047420'); INSERT INTO num_exp_sub VALUES (8, 9, '25001685.045047420'); INSERT INTO num_exp_mul VALUES (8, 9, '-1866544013697.195857020'); INSERT INTO num_exp_div VALUES (8, 9, '-.00300403532938582735'); INSERT INTO num_exp_add VALUES (9, 0, '-24926804.045047420'); INSERT INTO num_exp_sub VALUES (9, 0, '-24926804.045047420'); INSERT INTO num_exp_mul VALUES (9, 0, '0'); INSERT INTO num_exp_div VALUES (9, 0, 'NaN'); INSERT INTO num_exp_add VALUES (9, 1, '-24926804.045047420'); INSERT INTO num_exp_sub VALUES (9, 1, '-24926804.045047420'); INSERT INTO num_exp_mul VALUES (9, 1, '0'); INSERT INTO num_exp_div VALUES (9, 1, 'NaN'); INSERT INTO num_exp_add VALUES (9, 2, '-59265296.260444467'); INSERT INTO num_exp_sub VALUES (9, 2, '9411688.170349627'); INSERT INTO num_exp_mul VALUES (9, 2, '855948866655588.453741509242968740'); INSERT INTO num_exp_div VALUES (9, 2, '.72591434384152961526'); INSERT INTO num_exp_add VALUES (9, 3, '-24926799.735047420'); INSERT INTO num_exp_sub VALUES (9, 3, '-24926808.355047420'); INSERT INTO num_exp_mul VALUES (9, 3, '-107434525.43415438020'); INSERT INTO num_exp_div VALUES (9, 3, '-5783481.21694835730858468677'); INSERT INTO num_exp_add VALUES (9, 4, '-17127342.633147420'); INSERT INTO num_exp_sub VALUES (9, 4, '-32726265.456947420'); INSERT INTO num_exp_mul VALUES (9, 4, '-194415646271340.1815956522980'); INSERT INTO num_exp_div VALUES (9, 4, '-3.19596478892958416484'); INSERT INTO num_exp_add VALUES (9, 5, '-24910407.006556420'); INSERT INTO num_exp_sub VALUES (9, 5, '-24943201.083538420'); INSERT INTO num_exp_mul VALUES (9, 5, '-408725765384.257043660243220'); INSERT INTO num_exp_div VALUES (9, 5, '-1520.20159364322004505807'); INSERT INTO num_exp_add VALUES (9, 6, '-24832902.467417160'); INSERT INTO num_exp_sub VALUES (9, 6, '-25020705.622677680'); INSERT INTO num_exp_mul VALUES (9, 6, '-2340666225110.29929521292692920'); INSERT INTO num_exp_div VALUES (9, 6, '-265.45671195426965751280'); INSERT INTO num_exp_add VALUES (9, 7, '-107955289.045047420'); INSERT INTO num_exp_sub VALUES (9, 7, '58101680.954952580'); INSERT INTO num_exp_mul VALUES (9, 7, '2069634775752159.035758700'); INSERT INTO num_exp_div VALUES (9, 7, '.30021990699995814689'); INSERT INTO num_exp_add VALUES (9, 8, '-24851923.045047420'); INSERT INTO num_exp_sub VALUES (9, 8, '-25001685.045047420'); INSERT INTO num_exp_mul VALUES (9, 8, '-1866544013697.195857020'); INSERT INTO num_exp_div VALUES (9, 8, '-332.88556569820675471748'); INSERT INTO num_exp_add VALUES (9, 9, '-49853608.090094840'); INSERT INTO num_exp_sub VALUES (9, 9, '0'); INSERT INTO num_exp_mul VALUES (9, 9, '621345559900192.420120630048656400'); INSERT INTO num_exp_div VALUES (9, 9, '1.00000000000000000000'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_exp_sqrt VALUES (0, '0'); INSERT INTO num_exp_sqrt VALUES (1, '0'); INSERT INTO num_exp_sqrt VALUES (2, '5859.90547836712524903505'); INSERT INTO num_exp_sqrt VALUES (3, '2.07605394920266944396'); INSERT INTO num_exp_sqrt VALUES (4, '2792.75158435189147418923'); INSERT INTO num_exp_sqrt VALUES (5, '128.05092147657509145473'); INSERT INTO num_exp_sqrt VALUES (6, '306.43364311096782703406'); INSERT INTO num_exp_sqrt VALUES (7, '9111.99676251039939975230'); INSERT INTO num_exp_sqrt VALUES (8, '273.64392922189960397542'); INSERT INTO num_exp_sqrt VALUES (9, '4992.67503899937593364766'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_exp_ln VALUES (0, 'NaN'); INSERT INTO num_exp_ln VALUES (1, 'NaN'); INSERT INTO num_exp_ln VALUES (2, '17.35177750493897715514'); INSERT INTO num_exp_ln VALUES (3, '1.46093790411565641971'); INSERT INTO num_exp_ln VALUES (4, '15.86956523951936572464'); INSERT INTO num_exp_ln VALUES (5, '9.70485601768871834038'); INSERT INTO num_exp_ln VALUES (6, '11.45000246622944403127'); INSERT INTO num_exp_ln VALUES (7, '18.23469429965478772991'); INSERT INTO num_exp_ln VALUES (8, '11.22365546576315513668'); INSERT INTO num_exp_ln VALUES (9, '17.03145425013166006962'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_exp_log10 VALUES (0, 'NaN'); INSERT INTO num_exp_log10 VALUES (1, 'NaN'); INSERT INTO num_exp_log10 VALUES (2, '7.53578122160797276459'); INSERT INTO num_exp_log10 VALUES (3, '.63447727016073160075'); INSERT INTO num_exp_log10 VALUES (4, '6.89206461372691743345'); INSERT INTO num_exp_log10 VALUES (5, '4.21476541614777768626'); INSERT INTO num_exp_log10 VALUES (6, '4.97267288886207207671'); INSERT INTO num_exp_log10 VALUES (7, '7.91922711353275546914'); INSERT INTO num_exp_log10 VALUES (8, '4.87437163556421004138'); INSERT INTO num_exp_log10 VALUES (9, '7.39666659961986567059'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_exp_power_10_ln VALUES (0, 'NaN'); INSERT INTO num_exp_power_10_ln VALUES (1, 'NaN'); INSERT INTO num_exp_power_10_ln VALUES (2, '224790267919917955.13261618583642653184'); INSERT INTO num_exp_power_10_ln VALUES (3, '28.90266599445155957393'); INSERT INTO num_exp_power_10_ln VALUES (4, '7405685069594999.07733999469386277636'); INSERT INTO num_exp_power_10_ln VALUES (5, '5068226527.32127265408584640098'); INSERT INTO num_exp_power_10_ln VALUES (6, '281839893606.99372343357047819067'); INSERT INTO num_exp_power_10_ln VALUES (7, '1716699575118597095.42330819910640247627'); INSERT INTO num_exp_power_10_ln VALUES (8, '167361463828.07491320069016125952'); INSERT INTO num_exp_power_10_ln VALUES (9, '107511333880052007.04141124673540337457'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_data VALUES (0, '0'); INSERT INTO num_data VALUES (1, '0'); INSERT INTO num_data VALUES (2, '-34338492.215397047'); INSERT INTO num_data VALUES (3, '4.31'); INSERT INTO num_data VALUES (4, '7799461.4119'); INSERT INTO num_data VALUES (5, '16397.038491'); INSERT INTO num_data VALUES (6, '93901.57763026'); INSERT INTO num_data VALUES (7, '-83028485'); INSERT INTO num_data VALUES (8, '74881'); INSERT INTO num_data VALUES (9, '-24926804.045047420'); COMMIT TRANSACTION; -- ****************************** -- * Create indices for faster checks -- ****************************** CREATE UNIQUE INDEX num_exp_add_idx ON num_exp_add (id1, id2); CREATE UNIQUE INDEX num_exp_sub_idx ON num_exp_sub (id1, id2); CREATE UNIQUE INDEX num_exp_div_idx ON num_exp_div (id1, id2); CREATE UNIQUE INDEX num_exp_mul_idx ON num_exp_mul (id1, id2); CREATE UNIQUE INDEX num_exp_sqrt_idx ON num_exp_sqrt (id); CREATE UNIQUE INDEX num_exp_ln_idx ON num_exp_ln (id); CREATE UNIQUE INDEX num_exp_log10_idx ON num_exp_log10 (id); CREATE UNIQUE INDEX num_exp_power_10_ln_idx ON num_exp_power_10_ln (id); VACUUM ANALYZE num_exp_add; VACUUM ANALYZE num_exp_sub; VACUUM ANALYZE num_exp_div; VACUUM ANALYZE num_exp_mul; VACUUM ANALYZE num_exp_sqrt; VACUUM ANALYZE num_exp_ln; VACUUM ANALYZE num_exp_log10; VACUUM ANALYZE num_exp_power_10_ln; -- ****************************** -- * Now check the behaviour of the NUMERIC type -- ****************************** -- ****************************** -- * Addition check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, t1.val + t2.val FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, t2.expected FROM num_result t1, num_exp_add t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != t2.expected; DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, round(t1.val + t2.val, 10) FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, round(t2.expected, 10) AS expected FROM num_result t1, num_exp_add t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != round(t2.expected, 10); -- ****************************** -- * Subtraction check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, t1.val - t2.val FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, t2.expected FROM num_result t1, num_exp_sub t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != t2.expected; DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, round(t1.val - t2.val, 40) FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, round(t2.expected, 40) FROM num_result t1, num_exp_sub t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != round(t2.expected, 40); -- ****************************** -- * Multiply check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, t1.val * t2.val FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, t2.expected FROM num_result t1, num_exp_mul t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != t2.expected; DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, round(t1.val * t2.val, 30) FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, round(t2.expected, 30) AS expected FROM num_result t1, num_exp_mul t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != round(t2.expected, 30); -- ****************************** -- * Division check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, t1.val / t2.val FROM num_data t1, num_data t2 WHERE t2.val != '0.0'; SELECT t1.id1, t1.id2, t1.result, t2.expected FROM num_result t1, num_exp_div t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != t2.expected; DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, round(t1.val / t2.val, 80) FROM num_data t1, num_data t2 WHERE t2.val != '0.0'; SELECT t1.id1, t1.id2, t1.result, round(t2.expected, 80) AS expected FROM num_result t1, num_exp_div t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != round(t2.expected, 80); -- ****************************** -- * Square root check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT id, 0, SQRT(ABS(val)) FROM num_data; SELECT t1.id1, t1.result, t2.expected FROM num_result t1, num_exp_sqrt t2 WHERE t1.id1 = t2.id AND t1.result != t2.expected; -- ****************************** -- * Natural logarithm check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT id, 0, LN(ABS(val)) FROM num_data WHERE val != '0.0'; SELECT t1.id1, t1.result, t2.expected FROM num_result t1, num_exp_ln t2 WHERE t1.id1 = t2.id AND t1.result != t2.expected; -- ****************************** -- * Logarithm base 10 check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT id, 0, LOG(numeric '10', ABS(val)) FROM num_data WHERE val != '0.0'; SELECT t1.id1, t1.result, t2.expected FROM num_result t1, num_exp_log10 t2 WHERE t1.id1 = t2.id AND t1.result != t2.expected; -- ****************************** -- * POWER(10, LN(value)) check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT id, 0, POWER(numeric '10', LN(ABS(round(val, 200)))) FROM num_data WHERE val != '0.0'; SELECT t1.id1, t1.result, t2.expected FROM num_result t1, num_exp_power_10_ln t2 WHERE t1.id1 = t2.id AND t1.result != t2.expected; -- ****************************** -- * miscellaneous checks for things that have been broken in the past... -- ****************************** -- numeric AVG used to fail on some platforms SELECT AVG(val) FROM num_data; SELECT STDDEV(val) FROM num_data; SELECT VARIANCE(val) FROM num_data; -- Check for appropriate rounding and overflow CREATE TABLE fract_only ( id int, val numeric(4, 4) ); INSERT INTO fract_only VALUES (1, '0.0'); INSERT INTO fract_only VALUES (2, '0.1'); INSERT INTO fract_only VALUES (3, '1.0'); -- should fail INSERT INTO fract_only VALUES (4, '-0.9999'); INSERT INTO fract_only VALUES (5, '0.99994'); INSERT INTO fract_only VALUES (6, '0.99995'); -- should fail INSERT INTO fract_only VALUES (7, '0.00001'); INSERT INTO fract_only VALUES (8, '0.00017'); SELECT * FROM fract_only; DROP TABLE fract_only; -- Check inf/nan conversion behavior SELECT 'NaN'::float8::numeric; SELECT 'Infinity'::float8::numeric; SELECT '-Infinity'::float8::numeric; SELECT 'NaN'::float4::numeric; SELECT 'Infinity'::float4::numeric; SELECT '-Infinity'::float4::numeric; -- Simple check that ceil(), floor(), and round() work correctly CREATE TABLE ceil_floor_round ( a numeric ); INSERT INTO ceil_floor_round VALUES ('-5.5'); INSERT INTO ceil_floor_round VALUES ('-5.499999'); INSERT INTO ceil_floor_round VALUES ('9.5'); INSERT INTO ceil_floor_round VALUES ('9.4999999'); INSERT INTO ceil_floor_round VALUES ('0.0'); INSERT INTO ceil_floor_round VALUES ('0.0000001'); INSERT INTO ceil_floor_round VALUES ('-0.000001'); SELECT a, ceil(a), ceiling(a), floor(a), round(a) FROM ceil_floor_round; DROP TABLE ceil_floor_round; -- Check rounding, it should round ties away from zero. SELECT i AS pow, round((- 2.5 * 10 ^ i)::numeric, - i), round((- 1.5 * 10 ^ i)::numeric, - i), round((- 0.5 * 10 ^ i)::numeric, - i), round((0.5 * 10 ^ i)::numeric, - i), round((1.5 * 10 ^ i)::numeric, - i), round((2.5 * 10 ^ i)::numeric, - i) FROM generate_series(- 5, 5) AS t (i); -- Testing for width_bucket(). For convenience, we test both the -- numeric and float8 versions of the function in this file. -- errors SELECT width_bucket(5.0, 3.0, 4.0, 0); SELECT width_bucket(5.0, 3.0, 4.0, - 5); SELECT width_bucket(3.5, 3.0, 3.0, 888); SELECT width_bucket(5.0::float8, 3.0::float8, 4.0::float8, 0); SELECT width_bucket(5.0::float8, 3.0::float8, 4.0::float8, - 5); SELECT width_bucket(3.5::float8, 3.0::float8, 3.0::float8, 888); SELECT width_bucket('NaN', 3.0, 4.0, 888); SELECT width_bucket(0::float8, 'NaN', 4.0::float8, 888); -- normal operation CREATE TABLE width_bucket_test ( operand_num numeric, operand_f8 float8 ); UPDATE width_bucket_test SET operand_f8 = operand_num::float8; SELECT operand_num, width_bucket(operand_num, 0, 10, 5) AS wb_1, width_bucket(operand_f8, 0, 10, 5) AS wb_1f, width_bucket(operand_num, 10, 0, 5) AS wb_2, width_bucket(operand_f8, 10, 0, 5) AS wb_2f, width_bucket(operand_num, 2, 8, 4) AS wb_3, width_bucket(operand_f8, 2, 8, 4) AS wb_3f, width_bucket(operand_num, 5.0, 5.5, 20) AS wb_4, width_bucket(operand_f8, 5.0, 5.5, 20) AS wb_4f, width_bucket(operand_num, - 25, 25, 10) AS wb_5, width_bucket(operand_f8, - 25, 25, 10) AS wb_5f FROM width_bucket_test; -- for float8 only, check positive and negative infinity: we require -- finite bucket bounds, but allow an infinite operand SELECT width_bucket(0.0::float8, 'Infinity'::float8, 5, 10); -- error SELECT width_bucket(0.0::float8, 5, '-Infinity'::float8, 20); -- error SELECT width_bucket('Infinity'::float8, 1, 10, 10), width_bucket('-Infinity'::float8, 1, 10, 10); DROP TABLE width_bucket_test; -- TO_CHAR() -- SELECT '' AS to_char_1, to_char(val, '9G999G999G999G999G999') FROM num_data; SELECT '' AS to_char_2, to_char(val, '9G999G999G999G999G999D999G999G999G999G999') FROM num_data; SELECT '' AS to_char_3, to_char(val, '9999999999999999.999999999999999PR') FROM num_data; SELECT '' AS to_char_4, to_char(val, '9999999999999999.999999999999999S') FROM num_data; SELECT '' AS to_char_5, to_char(val, 'MI9999999999999999.999999999999999') FROM num_data; SELECT '' AS to_char_6, to_char(val, 'FMS9999999999999999.999999999999999') FROM num_data; SELECT '' AS to_char_7, to_char(val, 'FM9999999999999999.999999999999999THPR') FROM num_data; SELECT '' AS to_char_8, to_char(val, 'SG9999999999999999.999999999999999th') FROM num_data; SELECT '' AS to_char_9, to_char(val, '0999999999999999.999999999999999') FROM num_data; SELECT '' AS to_char_10, to_char(val, 'S0999999999999999.999999999999999') FROM num_data; SELECT '' AS to_char_11, to_char(val, 'FM0999999999999999.999999999999999') FROM num_data; SELECT '' AS to_char_12, to_char(val, 'FM9999999999999999.099999999999999') FROM num_data; SELECT '' AS to_char_13, to_char(val, 'FM9999999999990999.990999999999999') FROM num_data; SELECT '' AS to_char_14, to_char(val, 'FM0999999999999999.999909999999999') FROM num_data; SELECT '' AS to_char_15, to_char(val, 'FM9999999990999999.099999999999999') FROM num_data; SELECT '' AS to_char_16, to_char(val, 'L9999999999999999.099999999999999') FROM num_data; SELECT '' AS to_char_17, to_char(val, 'FM9999999999999999.99999999999999') FROM num_data; SELECT '' AS to_char_18, to_char(val, 'S 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 . 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9') FROM num_data; SELECT '' AS to_char_19, to_char(val, 'FMS 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 . 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9') FROM num_data; SELECT '' AS to_char_20, to_char(val, E'99999 "text" 9999 "9999" 999 "\\"text between quote marks\\"" 9999') FROM num_data; SELECT '' AS to_char_21, to_char(val, '999999SG9999999999') FROM num_data; SELECT '' AS to_char_22, to_char(val, 'FM9999999999999999.999999999999999') FROM num_data; SELECT '' AS to_char_23, to_char(val, '9.999EEEE') FROM num_data; SELECT '' AS to_char_24, to_char('100'::numeric, 'FM999.9'); SELECT '' AS to_char_25, to_char('100'::numeric, 'FM999.'); SELECT '' AS to_char_26, to_char('100'::numeric, 'FM999'); -- Check parsing of literal text in a format string SELECT '' AS to_char_27, to_char('100'::numeric, 'foo999'); SELECT '' AS to_char_28, to_char('100'::numeric, 'f\oo999'); SELECT '' AS to_char_29, to_char('100'::numeric, 'f\\oo999'); SELECT '' AS to_char_30, to_char('100'::numeric, 'f\"oo999'); SELECT '' AS to_char_31, to_char('100'::numeric, 'f\\"oo999'); SELECT '' AS to_char_32, to_char('100'::numeric, 'f"ool"999'); SELECT '' AS to_char_33, to_char('100'::numeric, 'f"\ool"999'); SELECT '' AS to_char_34, to_char('100'::numeric, 'f"\\ool"999'); SELECT '' AS to_char_35, to_char('100'::numeric, 'f"ool\"999'); SELECT '' AS to_char_36, to_char('100'::numeric, 'f"ool\\"999'); -- TO_NUMBER() -- SET lc_numeric = 'C'; SELECT '' AS to_number_1, to_number('-34,338,492', '99G999G999'); SELECT '' AS to_number_2, to_number('-34,338,492.654,878', '99G999G999D999G999'); SELECT '' AS to_number_3, to_number('<564646.654564>', '999999.999999PR'); SELECT '' AS to_number_4, to_number('0.00001-', '9.999999S'); SELECT '' AS to_number_5, to_number('5.01-', 'FM9.999999S'); SELECT '' AS to_number_5, to_number('5.01-', 'FM9.999999MI'); SELECT '' AS to_number_7, to_number('5 4 4 4 4 8 . 7 8', '9 9 9 9 9 9 . 9 9'); SELECT '' AS to_number_8, to_number('.01', 'FM9.99'); SELECT '' AS to_number_9, to_number('.0', '99999999.99999999'); SELECT '' AS to_number_10, to_number('0', '99.99'); SELECT '' AS to_number_11, to_number('.-01', 'S99.99'); SELECT '' AS to_number_12, to_number('.01-', '99.99S'); SELECT '' AS to_number_13, to_number(' . 0 1-', ' 9 9 . 9 9 S'); SELECT '' AS to_number_14, to_number('34,50', '999,99'); SELECT '' AS to_number_15, to_number('123,000', '999G'); SELECT '' AS to_number_16, to_number('123456', '999G999'); SELECT '' AS to_number_17, to_number('$1234.56', 'L9,999.99'); SELECT '' AS to_number_18, to_number('$1234.56', 'L99,999.99'); SELECT '' AS to_number_19, to_number('$1,234.56', 'L99,999.99'); SELECT '' AS to_number_20, to_number('1234.56', 'L99,999.99'); SELECT '' AS to_number_21, to_number('1,234.56', 'L99,999.99'); SELECT '' AS to_number_22, to_number('42nd', '99th'); RESET lc_numeric; -- -- Input syntax -- CREATE TABLE num_input_test ( n1 numeric ); -- good inputs INSERT INTO num_input_test (n1) VALUES (' 123'); INSERT INTO num_input_test (n1) VALUES (' 3245874 '); INSERT INTO num_input_test (n1) VALUES (' -93853'); INSERT INTO num_input_test (n1) VALUES ('555.50'); INSERT INTO num_input_test (n1) VALUES ('-555.50'); INSERT INTO num_input_test (n1) VALUES ('NaN '); INSERT INTO num_input_test (n1) VALUES (' nan'); -- bad inputs INSERT INTO num_input_test (n1) VALUES (' '); INSERT INTO num_input_test (n1) VALUES (' 1234 %'); INSERT INTO num_input_test (n1) VALUES ('xyz'); INSERT INTO num_input_test (n1) VALUES ('- 1234'); INSERT INTO num_input_test (n1) VALUES ('5 . 0'); INSERT INTO num_input_test (n1) VALUES ('5. 0 '); INSERT INTO num_input_test (n1) VALUES (''); INSERT INTO num_input_test (n1) VALUES (' N aN '); SELECT * FROM num_input_test; -- -- Test some corner cases for multiplication -- SELECT 4790999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999; SELECT 4789999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999; SELECT 4770999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999; SELECT 4769999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999; -- -- Test some corner cases for division -- SELECT 999999999999999999999::numeric / 1000000000000000000000; SELECT div(999999999999999999999::numeric, 1000000000000000000000); SELECT mod(999999999999999999999::numeric, 1000000000000000000000); SELECT div(- 9999999999999999999999::numeric, 1000000000000000000000); SELECT mod(- 9999999999999999999999::numeric, 1000000000000000000000); SELECT div(- 9999999999999999999999::numeric, 1000000000000000000000) * 1000000000000000000000 + mod(- 9999999999999999999999::numeric, 1000000000000000000000); SELECT mod(70.0, 70); SELECT div(70.0, 70); SELECT 70.0 / 70; SELECT 12345678901234567890 % 123; SELECT 12345678901234567890 / 123; SELECT div(12345678901234567890, 123); SELECT div(12345678901234567890, 123) * 123 + 12345678901234567890 % 123; -- -- Test code path for raising to integer powers -- SELECT 10.0 ^ - 2147483648 AS rounds_to_zero; SELECT 10.0 ^ - 2147483647 AS rounds_to_zero; SELECT 10.0 ^ 2147483647 AS overflows; SELECT 117743296169.0 ^ 1000000000 AS overflows; -- cases that used to return inaccurate results SELECT 3.789 ^ 21; SELECT 3.789 ^ 35; SELECT 1.2 ^ 345; SELECT 0.12 ^ (- 20); -- cases that used to error out SELECT 0.12 ^ (- 25); SELECT 0.5678 ^ (- 85); -- -- Tests for raising to non-integer powers -- -- special cases SELECT 0.0 ^ 0.0; SELECT (- 12.34) ^ 0.0; SELECT 12.34 ^ 0.0; SELECT 0.0 ^ 12.34; -- NaNs SELECT 'NaN'::numeric ^ 'NaN'::numeric; SELECT 'NaN'::numeric ^ 0; SELECT 'NaN'::numeric ^ 1; SELECT 0 ^ 'NaN'::numeric; SELECT 1 ^ 'NaN'::numeric; -- invalid inputs SELECT 0.0 ^ (- 12.34); SELECT (- 12.34) ^ 1.2; -- cases that used to generate inaccurate results SELECT 32.1 ^ 9.8; SELECT 32.1 ^ (- 9.8); SELECT 12.3 ^ 45.6; SELECT 12.3 ^ (- 45.6); -- big test SELECT 1.234 ^ 5678; -- -- Tests for EXP() -- -- special cases SELECT exp(0.0); SELECT exp(1.0); SELECT exp(1.0::numeric(71, 70)); -- cases that used to generate inaccurate results SELECT exp(32.999); SELECT exp(- 32.999); SELECT exp(123.456); SELECT exp(- 123.456); -- big test SELECT exp(1234.5678); -- -- Tests for generate_series -- SELECT * FROM generate_series(0.0::numeric, 4.0::numeric); SELECT * FROM generate_series(0.1::numeric, 4.0::numeric, 1.3::numeric); SELECT * FROM generate_series(4.0::numeric, - 1.5::numeric, - 2.2::numeric); -- Trigger errors SELECT * FROM generate_series(- 100::numeric, 100::numeric, 0::numeric); SELECT * FROM generate_series(- 100::numeric, 100::numeric, 'nan'::numeric); SELECT * FROM generate_series('nan'::numeric, 100::numeric, 10::numeric); SELECT * FROM generate_series(0::numeric, 'nan'::numeric, 10::numeric); -- Checks maximum, output is truncated SELECT (i / (10::numeric ^ 131071))::numeric(1, 0) FROM generate_series(6 * (10::numeric ^ 131071), 9 * (10::numeric ^ 131071), 10::numeric ^ 131071) AS a (i); -- Check usage with variables SELECT * FROM generate_series(1::numeric, 3::numeric) i, generate_series(i, 3) j; SELECT * FROM generate_series(1::numeric, 3::numeric) i, generate_series(1, i) j; SELECT * FROM generate_series(1::numeric, 3::numeric) i, generate_series(1, 5, i) j; -- -- Tests for LN() -- -- Invalid inputs SELECT ln(- 12.34); SELECT ln(0.0); -- Some random tests SELECT ln(1.2345678e - 28); SELECT ln(0.0456789); SELECT ln(0.349873948359354029493948309745709580730482050975); SELECT ln(0.99949452); SELECT ln(1.00049687395); SELECT ln(1234.567890123456789); SELECT ln(5.80397490724e5); SELECT ln(9.342536355e34); -- -- Tests for LOG() (base 10) -- -- invalid inputs SELECT log(- 12.34); SELECT log(0.0); -- some random tests SELECT log(1.234567e - 89); SELECT log(3.4634998359873254962349856073435545); SELECT log(9.999999999999999999); SELECT log(10.00000000000000000); SELECT log(10.00000000000000001); SELECT log(590489.45235237); -- -- Tests for LOG() (arbitrary base) -- -- invalid inputs SELECT log(- 12.34, 56.78); SELECT log(- 12.34, - 56.78); SELECT log(12.34, - 56.78); SELECT log(0.0, 12.34); SELECT log(12.34, 0.0); SELECT log(1.0, 12.34); -- some random tests SELECT log(1.23e - 89, 6.4689e45); SELECT log(0.99923, 4.58934e34); SELECT log(1.000016, 8.452010e18); SELECT log(3.1954752e47, 9.4792021e - 73); -- -- Tests for scale() -- SELECT scale (numeric 'NaN'); SELECT scale (NULL::numeric); SELECT scale (1.12); SELECT scale (0); SELECT scale (0.00); SELECT scale (1.12345); SELECT scale (110123.12475871856128); SELECT scale (- 1123.12471856128); SELECT scale (- 13.000000000000000); -- -- Tests for SUM() -- -- cases that need carry propagation SELECT SUM(9999::numeric) FROM generate_series(1, 100000); SELECT SUM((- 9999)::numeric) FROM generate_series(1, 100000); pgFormatter-4.2/t/pg-test-files/expected/numeric_big.sql000066400000000000000000011214531361326045100234140ustar00rootroot00000000000000-- ****************************** -- * Test suite for the Postgres NUMERIC data type -- ****************************** -- Must drop tables created by short numeric test. DROP TABLE num_data; DROP TABLE num_exp_add; DROP TABLE num_exp_sub; DROP TABLE num_exp_div; DROP TABLE num_exp_mul; DROP TABLE num_exp_sqrt; DROP TABLE num_exp_ln; DROP TABLE num_exp_log10; DROP TABLE num_exp_power_10_ln; DROP TABLE num_result; CREATE TABLE num_data ( id int4, val numeric(1000, 800) ); CREATE TABLE num_exp_add ( id1 int4, id2 int4, expected numeric(1000, 800) ); CREATE TABLE num_exp_sub ( id1 int4, id2 int4, expected numeric(1000, 800) ); CREATE TABLE num_exp_div ( id1 int4, id2 int4, expected numeric(1000, 800) ); CREATE TABLE num_exp_mul ( id1 int4, id2 int4, expected numeric(1000, 800) ); CREATE TABLE num_exp_sqrt ( id int4, expected numeric(1000, 800) ); CREATE TABLE num_exp_ln ( id int4, expected numeric(1000, 800) ); CREATE TABLE num_exp_log10 ( id int4, expected numeric(1000, 800) ); CREATE TABLE num_exp_power_10_ln ( id int4, expected numeric(1000, 800) ); CREATE TABLE num_result ( id1 int4, id2 int4, result numeric(1000, 800) ); -- ****************************** -- * The following EXPECTED results are computed by bc(1) -- * with a scale of 1000 -- ****************************** BEGIN TRANSACTION; INSERT INTO num_exp_add VALUES (0, 0, '0'); INSERT INTO num_exp_sub VALUES (0, 0, '0'); INSERT INTO num_exp_mul VALUES (0, 0, '0'); INSERT INTO num_exp_div VALUES (0, 0, 'NaN'); INSERT INTO num_exp_add VALUES (0, 1, '85243.39540024977626076239847863600785982737155858270959890014613035727868293618673807776733416230953723818527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_sub VALUES (0, 1, '-85243.39540024977626076239847863600785982737155858270959890014613035727868293618673807776733416230953723818527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (0, 1, '0'); INSERT INTO num_exp_div VALUES (0, 1, '0'); INSERT INTO num_exp_add VALUES (0, 2, '-994877526002806872754342148749241.04353023451866590387054959174736129501310680280823383331007646306243540953499740615246583399296334239109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_sub VALUES (0, 2, '994877526002806872754342148749241.04353023451866590387054959174736129501310680280823383331007646306243540953499740615246583399296334239109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_mul VALUES (0, 2, '0'); INSERT INTO num_exp_div VALUES (0, 2, '0'); INSERT INTO num_exp_add VALUES (0, 3, '-60302029489319384367663884408085757480.2322712404088283093870869198708849258097125725036189625900174440196153781995220721511009855207486224837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (0, 3, '60302029489319384367663884408085757480.2322712404088283093870869198708849258097125725036189625900174440196153781995220721511009855207486224837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (0, 3, '0'); INSERT INTO num_exp_div VALUES (0, 3, '0'); INSERT INTO num_exp_add VALUES (0, 4, '5329378275943663322215245.24931765987630429629836382184742348353920297283690739124220773955591340709935970062776650204659187764581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (0, 4, '-5329378275943663322215245.24931765987630429629836382184742348353920297283690739124220773955591340709935970062776650204659187764581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (0, 4, '0'); INSERT INTO num_exp_div VALUES (0, 4, '0'); INSERT INTO num_exp_add VALUES (0, 5, '-652755630.43456071828048833552593835051449845484289562110789582081210403487973096161149072377955192388469356112505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (0, 5, '652755630.43456071828048833552593835051449845484289562110789582081210403487973096161149072377955192388469356112505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (0, 5, '0'); INSERT INTO num_exp_div VALUES (0, 5, '0'); INSERT INTO num_exp_add VALUES (0, 6, '.0469370721950711508944806393077762204079964905145503836835397203739563036579760026190241480514409364'); INSERT INTO num_exp_sub VALUES (0, 6, '-.0469370721950711508944806393077762204079964905145503836835397203739563036579760026190241480514409364'); INSERT INTO num_exp_mul VALUES (0, 6, '0'); INSERT INTO num_exp_div VALUES (0, 6, '0'); INSERT INTO num_exp_add VALUES (0, 7, '-818934540071845742'); INSERT INTO num_exp_sub VALUES (0, 7, '818934540071845742'); INSERT INTO num_exp_mul VALUES (0, 7, '0'); INSERT INTO num_exp_div VALUES (0, 7, '0'); INSERT INTO num_exp_add VALUES (0, 8, '8496986223.64063724353165506167988570717591150432282238795309964705925283285060558038824227595710139960766584401003765241409149793494330798800'); INSERT INTO num_exp_sub VALUES (0, 8, '-8496986223.64063724353165506167988570717591150432282238795309964705925283285060558038824227595710139960766584401003765241409149793494330798800'); INSERT INTO num_exp_mul VALUES (0, 8, '0'); INSERT INTO num_exp_div VALUES (0, 8, '0'); INSERT INTO num_exp_add VALUES (0, 9, '54863480.34685027005508022756223282084742813020271603840941647350440860843570182437301045468670059279379903480024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_sub VALUES (0, 9, '-54863480.34685027005508022756223282084742813020271603840941647350440860843570182437301045468670059279379903480024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_mul VALUES (0, 9, '0'); INSERT INTO num_exp_div VALUES (0, 9, '0'); INSERT INTO num_exp_add VALUES (1, 0, '85243.39540024977626076239847863600785982737155858270959890014613035727868293618673807776733416230953723818527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_sub VALUES (1, 0, '85243.39540024977626076239847863600785982737155858270959890014613035727868293618673807776733416230953723818527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (1, 0, '0'); INSERT INTO num_exp_div VALUES (1, 0, 'NaN'); INSERT INTO num_exp_add VALUES (1, 1, '170486.79080049955252152479695727201571965474311716541919780029226071455736587237347615553466832461907447637054203186991790701615551214692555785671028648640897898741246882118067609728317430043806625387779037980513762118868084887015059202190301421555269486602797852927777567694581746398790609996101506730430853942556475840126871131898407356048450541232591147357021858041662012293323494543567675306406079659294204054863522259037763051870433216859794083051717080761509518250300466106939998045710070'); INSERT INTO num_exp_sub VALUES (1, 1, '0'); INSERT INTO num_exp_mul VALUES (1, 1, '7266436459.363324713115467666113895787027372854351303425444968800459979742082292257107107767894843498525848597439323325297125474674300428669958003640228730876886174255457103020291514229439701871032118057857763809224712818579091741996335014138185389554630910658876423205103697147288306070059640369158894028731728589073730895396494400175420670713113234800826523252075036892246807434088405522834549449664122407363485486902219500109237667016524913027290777216477989904700729228025571098410870506256758678625928245828210775042611512394316804583459576285681159178280400209217948833631961377519855502763611693070238579591463373484424582723121059964236704135695706864890193388054537703767833595331866551990460050750959493829603581882430597105627056085260296454181999581594565113210481151487049158699087454047624433576922179904629'); INSERT INTO num_exp_div VALUES (1, 1, '1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); INSERT INTO num_exp_add VALUES (1, 2, '-994877526002806872754342148663997.64812998474240514147207095573950146764154822009863493316394610578375247334825932838513167168342610420582834742950389452212867974756590355021495169819086060202117180229196935525386766373096687306110481009743118940565957556492470398904849289222365256698601073536111216152709126800604695001949246634784573028721762079936564434050796321975774729383704426321489070979168993853338252728216162346796960170352897972568238870481118474064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_sub VALUES (1, 2, '994877526002806872754342148834484.43893048429492666626902822775522112238466538551783273345620682034111834572173548391979999630250058057637037929942180153828419189449146140692523818459983958943364062347264545253704196416903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (1, 2, '-84806738323879544552397401815149740513.8505875535743013876823142649666132764556588225959336097903898464616542203793600590311980154402068027051522932586050753865288419084437796768749509032177577451738712965496693249429231838833655025794915864261585848007162358912070811805298210095333433397862313304655108809804359760907473898420016370058274978588765092161529583480924554820756527238472641797198545539410039895140087686344382628317530286295498797849942258314364503000942821309916954725689781458590617068629906894951122301020797266469357701283289275708774593896770378558232444454118891917258610753077932026885574920166837998049508644891327208474213193224700658584824407382455480657734911543930195324144216374573825'); INSERT INTO num_exp_div VALUES (1, 2, '-.000000000000000000000000000085682300757901809257711279577127388124986344391495296640171942990079130291883279872719240502687189411421655284515420074848478500192127657883342858267913417679786356766341637336955924836847768457039175660279784295612167899455618405343686908907695358239088351870495830739180518509859269437015797489301844593920484927630172344269378248455657186218762679357609204333669024237648538465053048724383898528808961206696787294681884412485427843796696788390072124570957047672341581447744981862017791206857428430183366004980966398716823512288330174863890117558744630102020144500158878244146399686532935435591262767487823942606452349972401012308378888947381934278131785907155692007064636085000405504866631011593239041758448995933095907216863744502344014999804306234830774259496097549717476344048'); INSERT INTO num_exp_add VALUES (1, 3, '-60302029489319384367663884408085672236.83687099063256754698860828386302509843815398979402006244388708674093244201278399438376682321121138429850885935540924586964982855913223221441591310211730902799041126800414795030815514254713522692405212716783388698431088814919226444677188004928663343696636297536500970117716818423689175692808344185016908913828066250587407384563498516598672584120143890364303296142744031320345312431817858545326010704685255237541162931904446804064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_sub VALUES (1, 3, '60302029489319384367663884408085842723.62767149018508907178556555587874475318127115521321786273614780129829831438626014991843514783028586066905089122532715288580534070605779007112619958852628801540288008918482404759132944298520148080184250697297150817299173701934285646867489426483932830299434150464278537812298564822479785688909850915447762856384542090714278516461905872647123125352735037721325154184406043613668806975385533851732090363979459292404685190942209855935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (1, 3, '-5140349743195574373979577554212527512597024.162480344833040409158673429491690439298506850052285119390701002577176786023622062742050099464897084793357329597395417632908812044304066963549928478520702505283307379218587635434673128958824348493758429380623577527186462464399974242800361134191519694694139153279582776168995426125926314513926640766117733774558011741611075336271613675760116784769700605008122422944290652448956922432960815546502965310676913079866511016221573557684245901002643719965652152439520727383305120298495304784052489867651462175349450610643411043707261107569691076730261762793560088893354750383257372118118753366377402045596735023445172252225346164608897913115394905485106225627590643805003075069931177395059698550161546962768768895596088478488887530518018212441345360153523733317120037436403475909117998647781920105313938836144009539683'); INSERT INTO num_exp_div VALUES (1, 3, '-.000000000000000000000000000000001413607404628860353773457807436398753936801768769045711604884548436548520368932184112069166807060840219636509423284498981041814526856251281381511288768719259120481595036745286884246627534964287523188738499223075292690431699417313258943941279343383979626641848305343592679057491670166887054819766294147341982669243114259272404203080347707713358471397866402657818267495050115642987782080912962056565478445923456884713049272637646637760989004917643369240372476411912794578381690666695711891846833983534126217706309741885844723208036219144146342212915129560758201609824034610223907791643110990898577049488934294259106725414517181607988173722432655731491050637087261030314548853334338835938120502930424813699221083197863303458179445322810087784892821862085562891180364134284641396475'); INSERT INTO num_exp_add VALUES (1, 4, '5329378275943663322300488.64471790965256505869684245785528331091076155554650629138833809683459634328609777839510066435612911583108717191216693735823717997111970662575497378762952496582183738308720094529950793570383580785385569873278068217936841324404119828637880370718028782103860007754579779716996004352284614661690063919125301052941328989181561787543541920734755989452320799185700078241880935083616978140555713297241612718277766918005268951861880490889884082730841740604517529391011862694381726143520658746305661338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (1, 4, '-5329378275943663322130001.85391741010004353389988518583956365616764439012730849109607738227723047091262162286043233973705463946054514004224903034208166782419414876904468730122054597840936856190652484801633363526576955397606531892764306099068756437389060626447578949162759295501062154826802212022414257953494004665588557188694447110384853149054690655645134564686305448219729651828678220200218922790293483596988037990835533058983562863141746692824117439019450865871047657552800448629502344444081260036580660700595591338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (1, 4, '454294299613767152878025320780.534199313974295807138790763501115780294529340799108297697573066187975311338382917022391830256203305238757334106943821060545424417350991354829668286194840925251162479496893943917530660694097932059166013476064988623431110002057735318529554555260199417935495388243829261809007709919225000608711536928171687251088217591210419208480251102484043683131687013687838713055660405381318396419588727500715930145098362997142075433472039319292466570912777345841400769387321465602989947078951135489852486382469990409873227894248208197179481868230244584527040573428134962626267135732247029762468417273891700661832893497067151409134724061246612631376075173287264787886064622106855886785805818642123776489793586531950438285720668411465570116161790343538663297713926678759640594912243360541590368666922379919514826022141331900181'); INSERT INTO num_exp_div VALUES (1, 4, '.000000000000000000015994998100440878014888861029956505927201309704413242103407885948184870841766875212766910686894450511886242468216220470061916924303252919423028993720180330014505454865704155281502763018913215741264982350384245753394656021401865680441649920273268554396350483440173848850052788410943178207336328451359951614056237100465802151856198860908371340425459435127133071447273887829397881221098443685586506647314622864702873235212396755866459409263439958011711379929751157260020133239574261188528305921244365838405372320186907437842180388704854605498842516581811515413843298370501194935797268161171428747542997504369133579105180311662221854071962295818264211400101689450830279979372422749150894553349570063000769685274875561760334738424509532610467832951796852051505383374693614022043010735004494395190'); INSERT INTO num_exp_add VALUES (1, 5, '-652670387.03916046850422757312745971450663862747133703839829692066597367760104802542475264601221776157515632293978442027199108085723617181683235487266149426304575903892721468296143475297345699313102262188759506518376019936160961709578829069446312051432780603656651983414612264636232727512091101057374054475214114364113300402823059519499217878746766275164739724770556122895799337810694888119810524986616938847385753562624139431982468828696587199570410008890188532132652095915565323400735066310142303225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (1, 5, '652840873.82996096805674909792441698652235828221445420381749472095823439215841389779822880154688608619423079931032645214190898787339168396375791272937178074945473802633968350414211085025663129356908887576538544498889782055029046596593888271636613472988050090259449836342389832330814473910881711053475561205644968306669776242949930651397625234795216816397330872127577980937461350104018382663378200293023018506679957617487661691020231880567020416430204091941905612894161614165865789507675064355852373225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (1, 5, '-55643106304872.575994253221940844841058071061962511162776681458310912066379595519265546225338405882027547140476045378015935579066580347282075024392379464189067155567624835346798806677988850250198082355055954078446421075165109896091047534711081616362392995575466807084807876544560268050611445006601394735810211678919646667455478469014906335433468365011768049600750224822391684377238242162320161552720449713229523135506671063115436813348612986916614320012995541575293478341408982118538094438068036422562665160411591652618670802973618768526197813319204816293073794413317669922144705633308090832805914096147659820167569140291210526520361556881576175809360614782817717579318298657744021133210954279487777567785280633309576696708168342539425395482429923273623865667723482418178781573723597156804085501875735112311466228778929147929'); INSERT INTO num_exp_div VALUES (1, 5, '-.000130590057635351941758745900947472461593749814351229292370661147301124533787181489468804246182606762727711479707901680546780430454163647774077629503207962424213266902732555945190365467801995495570282501722505521485829885605904543846887348545254658726343578684749830307120625129857380290225370772763609458975555029415082569247186899112975387051141777417911244576134390940441209829852154391377911942082738699481875795620569383196133124499983396562167632007454221121465745085962247988140942672429187053671899537331280701003778040796615094903602095098880716919238394057384949891444700347825726273725378453454782330181608182747900774711384845635284701538541452235224216112380245660177463043471814071809869894647262285332580556739424040615194137651616350340752691170045698234853734471923738591898290468792787543896'); INSERT INTO num_exp_add VALUES (1, 6, '85243.44233732197133191329295927531563604777955507322414928382967007765263923984471408038635831036097817458527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_sub VALUES (1, 6, '85243.34846317758118961150399799670008360696356209219504851646259063690472663252876207514831001425809630178527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (1, 6, '4001.075404054519813215296429095020391062109905613738157927030437221793757373268325953178030040276107574363822832168160758728653712686313134828282109532831190239521843808940611025488601517574653932032236616573457735900045655665690517797280666732780030171712864961531623060353548802466577910774711998056232872212688464691036260746751992072745518373073825852119460094113694393273456369345499434994672730920070410547163082189385645712866100999708173472360864669110044660667614583576570496399103026286828660558854973376227247132815728164629722965145778698957093136175449225024685874279280018547740'); INSERT INTO num_exp_div VALUES (1, 6, '1816120.848909727306817960620941575637231136442992819290405125420545200026620306446043740992108329883383706060582482495616151605111275635501481354526017831484915013545483361715432312183101964395505340188909970344423950565285639911521082834494088840596716495422427543520536844348040681236845850482165744696068209384509064196671206362539077218412355776790921130042376467606683622970728503408501481791356294886150690067651815776445750760428874351556866105285911902433352126498951242195408782804314174041618879250740246352525074791310920062276490422853700893340860452528740673590486626464460321410814395342850270921486724297414692313177440726749004398703147904603937755702369682956482832074779404350351752662820773690162594400557957241676636030332988289683112176900913522668426137377289536793838959751008646843014106876005'); INSERT INTO num_exp_add VALUES (1, 7, '-818934540071760498.60459975022373923760152136399214017262844141729040109985386964272131706381326192223266583769046276181472898406504104649192224392653722107164485675679551050629376558940966195135841284978096687306110481009743118940565957556492470398904849289222365256698601073536111216152709126800604695001949246634784573028721762079936564434050796321975774729383704426321489070979168993853338252728216162346796960170352897972568238870481118474064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_sub VALUES (1, 7, '818934540071930985.39540024977626076239847863600785982737155858270959890014613035727868293618673807776733416230953723818527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (1, 7, '-69808760806266041400340.70700818693892852138813934414383886494691670042143650609934777814995087699409404201920249076407981012095999320858479644760715204999741683528746097757549835956359129287002171391961763797857794730120426599135099619822532290339000466211195776337667123320942107370731349851576864242697412616810236323676004067839744992733887503405311090677026008324895177587064547630828026123718296429295638934384446325302964896473296829265805737112709269803814942537657996725913938408781715328945194948010970'); INSERT INTO num_exp_div VALUES (1, 7, '-.000000000000104090609479936344103210175655521317012597986331111866307697262848964666360492361638117930801818899121383806224630563676018240181412174154250663423230239912527388431901852952893943812666142740182651125508583527237123596541789628675379232473721293630968882045044077795828674268595016625198802475186587918019739056755398151182369187670251750080227679555002307777300392769289647975058449905106584837938556260801229545589323224752038795423164214112897202147313792076165011373139219134850954217300915326944185918762838321705825423789073869940092569940135329697980600082436317664012683589681419530904283106912171330819469065141821685734295058255484933744156717782754922568796985634397878149984177882018261742637463462647452140104146195353696596211873925359508622779658904411330975862442989437933211964821'); INSERT INTO num_exp_add VALUES (1, 8, '8497071467.03603749330791582407836434318377133169438097066269854720538319012928851657498035372443556191720308219530866834905045144302106406146277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_sub VALUES (1, 8, '-8496900980.24523699375539429928140707116805167695126380524350074691312247557192264420150419818976723729812860582476663647913254442686555191453722107164485675679551050629376558940966195135841284978096687306110481009743118940565957556492470398904849289222365256698601073536111216152709126800604695001949246634784573028721762079936564434050796321975774729383704426321489070979168993853338252728216162346796960170352897972568238870481118474064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_mul VALUES (1, 8, '724311956372274.0135050255361637906710330203036651743488213007179039756514944640108625580172737414192938789413338554327986697518463087452612658955180411327002900979574347739956600177846996063741787205122007268468674386396156638261992679442768654367111433834151087792255469957061758837789341439211010331332174981459471333376067541234901538285101103690622656631026001337239036711179989456674399137008584021283568040818388709554256523118702728176420022080138548890713013682480239784198421500241995499841675772793497485550923152267616622892846304530712344886979674416990935007952941652591352603797627920865960622077762568060903908151958000'); INSERT INTO num_exp_div VALUES (1, 8, '.000010032191786198542900505683562217892317481076466949299850809276743457759270150820565375820388277409258249926696079166209409657808406245382887790534127749833677458375931047385994887406206232330491317602830654688957983804698568410728278089250379255157030886262396950539100566975000094268415749476738358914633948867977798590927055566888255636132486899287919515638902721543629183577900872078173883974905921239149419877613723476347774771230668479296621531969573505480695490386225866950545725121902534610730154727385072738079149623798073810167706094070842646222833137345669922898403368997676634709281456818189049718956207208697021706186341405575300648248555331280690778367620868775005181264547924615247991795542738868003191757946979714250339430363902549866892041102771965653407197094250270379367437342632741280710'); INSERT INTO num_exp_add VALUES (1, 9, '54948723.74225051983134098996071145685528795757427462111901537365053896571438476055974853245403475510333627298551845046116291696445177112567064282766115207407461565363967417615506303416694032848457927390574251904212425813072768882213388082765916956736282110801611726537663292922699021333445658549608928179155685881583228490235606377831724593358583903616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_sub VALUES (1, 9, '-54778236.95145002027881946516375418483956830283115745569981757335827825115701888818627237691936643048426179661497641859124500994829625897874508497095086558766563666622720535497438693688376602804651302002795213923698663694204683995198328880575615535181012624198813873609885725228117274934655048553507421448724831939026752650108735245933317237310133362383704426321489070979168993853338252728216162346796960170352897972568238870481118474064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_mul VALUES (1, 9, '4676749348240.390309875431213992853550297086049749814750492488995108783145961719774217441193547534210468967573344456866203963659951312519988497979489304488948342258375915152429008993288817366720647491166024151209542534474867042837694499222928509320280684557676243780452100132238968233413333851595648146954975713386711764268506890884764704949969602122157394714663532141060559896359465918874990769222345665160127552795532197771168442486088776803398878354288847069602460071745966589164282641033852314335279121191855487126430176047553895892632834940595958394834437871886013513058514896870683979585091413977173250824451205330441299000850618134248917380244749589254309567551846327349592529960432446947239714236828401206843011440433362544797025114476612133622499094287321570559088587999417440664282418005102546343020409520421747216'); INSERT INTO num_exp_div VALUES (1, 9, '.001553736563217204408368240901181555234014339476186598647410198373122572205209277343865051610898136462487966496673511261433286284257044548634547569923035899634327495195510767312478861719221916387940027268721306540663743713345337497285507595251328382906111997524508729275471287648008479480805967901972481289402930660848950039779707354469389216931774094174326513465502460315792834278614886136688161679443873815113442220055827192996984074129528034845339130162104547166079591654852164993577408422015514100323825529286511720963047269483211930770803479398243069649400360625259869765138545866815758888670363356947311319523139395191102286838888146829667276592755438606664644975648828848738708349790766370694194763606850690923803984129157519048493985198591771429264967247245289970213262206709011468289046840862597010969'); INSERT INTO num_exp_add VALUES (2, 0, '-994877526002806872754342148749241.04353023451866590387054959174736129501310680280823383331007646306243540953499740615246583399296334239109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_sub VALUES (2, 0, '-994877526002806872754342148749241.04353023451866590387054959174736129501310680280823383331007646306243540953499740615246583399296334239109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_mul VALUES (2, 0, '0'); INSERT INTO num_exp_div VALUES (2, 0, 'NaN'); INSERT INTO num_exp_add VALUES (2, 1, '-994877526002806872754342148663997.64812998474240514147207095573950146764154822009863493316394610578375247334825932838513167168342610420582834742950389452212867974756590355021495169819086060202117180229196935525386766373096687306110481009743118940565957556492470398904849289222365256698601073536111216152709126800604695001949246634784573028721762079936564434050796321975774729383704426321489070979168993853338252728216162346796960170352897972568238870481118474064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_sub VALUES (2, 1, '-994877526002806872754342148834484.43893048429492666626902822775522112238466538551783273345620682034111834572173548391979999630250058057637037929942180153828419189449146140692523818459983958943364062347264545253704196416903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (2, 1, '-84806738323879544552397401815149740513.8505875535743013876823142649666132764556588225959336097903898464616542203793600590311980154402068027051522932586050753865288419084437796768749509032177577451738712965496693249429231838833655025794915864261585848007162358912070811805298210095333433397862313304655108809804359760907473898420016370058274978588765092161529583480924554820756527238472641797198545539410039895140087686344382628317530286295498797849942258314364503000942821309916954725689781458590617068629906894951122301020797266469357701283289275708774593896770378558232444454118891917258610753077932026885574920166837998049508644891327208474213193224700658584824407382455480657734911543930195324144216374573825'); INSERT INTO num_exp_div VALUES (2, 1, '-11671021799770914903865020509.301561107153561058074179843542446420696517132461554451075945807420674211966679216615407057626541711186781735967334896541890595771915856783008831770988426637435694856170266346306640678577376310547806764332837625966429200996250687908930748245035578756314083608655163891041399241377675534416837659335561005203219889972336214863417948542956735403991871098341470996860469878038840964359144637726669728240650066795729910649523281308716277906908340457162235831526838308777581569974551673352306004330423694524256415657620427590352277556907586751621496248973165690360552007637570957980230685679819820147036159174977086193494572117089582758015847544798464543446227632367713941117001423437766840744488426025388612316819120660814681298624293065972395923651314350558006567251033289878238407790871784676348196394482477767774'); INSERT INTO num_exp_add VALUES (2, 2, '-1989755052005613745508684297498482.08706046903733180774109918349472259002621360561646766662015292612487081906999481230493166798592668478219872672892569606041287164205736495714018988279070019145481242576461480779090962790'); INSERT INTO num_exp_sub VALUES (2, 2, '0'); INSERT INTO num_exp_mul VALUES (2, 2, '989781291745465665243281323944996915810556285052564220274237162526.1617859904902612197894543199389468971679632139059029459520163585971122643624316475417489000981872666677202334180945949860058384424993911721081868337499377890298636260338063268639283065887210924895929155083478140340889209440025415565915964293989840603863813531303253038823629712989041722072693449251635519992922148998556112923060331794396659338057474019846675262291146025'); INSERT INTO num_exp_div VALUES (2, 2, '1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); INSERT INTO num_exp_add VALUES (2, 3, '-60303024366845387174536638750234506721.2758014749274942132576365116182462208228193753118527959000939070820507877345194783035668195137119648748792386548310474079340204536236936213411512867171486174240518914767934028451971067161683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (2, 3, '60301034611793381560791130065937008239.1887410058901624055165373281235236307966057696953851292799409809571799686645246659986351515277852800926805119259053513475211488115663286642009614039264484259692394657121785950542874788161683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (2, 3, '59993133911282372667149627097418449223835595194300848703012380022306762.154418449236691515146061305380465061074531890529497774836941002526095632166401249277270674802626154774328055399254982998368191676630276960361274433270795772477146870294928855773172789856196219950097157391050424577381777627004101100872747943673762087675405200265837631665464736842180920496158545887039337399558993437594084473932658319914390365451919627956823980800124880375978662052111797881386060353490432427832058851094210488804887183034572364751639107535041308434932952695103493677600969712634416241541391613699710826602011076372592299807609658979777598672141389319098817824624950794758296679318319299142035'); INSERT INTO num_exp_div VALUES (2, 3, '.000016498242835741013709859217005931279826178662180173096568520102488480129191427472581644597420895622947234184547373944996197105916093347103336318249582032230903680989710242610024298937774441533502282949127537125997753002819456724709929935850697744632904111143787011103837624936502324835260843148595669524694347566421203164808527739207590986975750648112133699756328511947175496694080071202064255118777680958612315513441989609682655431197367166056616661045712867189326408877133865572680407329449150282415810958772293869902662884761202424695742898573841869524376684740249281181605067345203479719345061595919652192297531638467223956758315591610733251562492794891852151639643060692698365496208796638230566761231611376199140556503620471090364900792180618741355091923808605890415081571900697282725022629812561702118'); INSERT INTO num_exp_add VALUES (2, 4, '-994877520673428596810678826533995.79421257464236160757218576989993781147390382997132644206786872350652200243563770552469933194637146474528320738725486418004701192337175478117026439697031462361180324038544450723753402846519731908503949116978812841497201119103409772457270340059605961197538918709309004130294868847110690336360689446090125918336908930881873778405661757289469281163974774492810850778950071063044769131228124355961427111369335109426492177657001035045332525699055300921341010989742896430768506909949340276549373661076950964959025967328861569387160956730002517417236732463510495205173523163676450203614971844583064927040066684531931069310935516821795449174271052747559395296525950219449541557191520903507653089998307641491381797101485104546410643'); INSERT INTO num_exp_sub VALUES (2, 4, '-994877531332185148698005470964486.29284789439497020016891341359478477855230977564514122455228420261834881663435710678023233603955522003691551934167083188036585971868561017596992548582038556784300918537917030055337559943480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (2, 4, '-5302078674303935968062773235453828254014583744527466365136.236414807326868572353809920518232561005161225922028750078608989965741402418802255050636954800114792425419735155504035469350521800895164087027043476055514245942961100610551646034472084954313670284875310691807937254054948742125729353864014122131419164449567115006621212424805182687707372956385102095255735458593389920872596796806885847543910224476727171570873698525606016990229936284811067826588349092841322512643043008589065847223683467371925773023109720951609815041012521485326120380123169545818055967455575736140138663815073081494226676896278654189873597341203197903408668523514375373841493189836809506003729379742035629498519683885268256481104619815130659628225053833297766479068686119691010593208135616363994230674606991733148502293102108193522604968743948323130517040609601859735899914987426089053869350663'); INSERT INTO num_exp_div VALUES (2, 4, '-186677971.517539861245390308778107722315862721823627804195528485535806132067679059453022306691281662574091826898288146790399178357754908901382135796783067563944022498807930452234032896817601590728156392188660701355670595952594500812333935362955625137944589981298793332621503315902294100258945995827423279442031218510259915311555745581797315793010762585658196457363672908315687720174516274528662385172326028870945153551774300419158584379602045442200523311437013776079979639415633358878239012925000523542907592866797199229858272764668664323316251874027468128770456766875866492004650352654523634716923150212263912760225390093339729495231675627059805624175587380165509763048913150826017167286786277908970769297060278191518730887417202276531151575412404467497036737825989088867451153485938272367300939127313445244028528055624'); INSERT INTO num_exp_add VALUES (2, 5, '-994877526002806872754342801504871.47809095279915423939648794226185974985600242391612965412218049794216637114648812993201775787765690351615479957141288239552036371132381627958673244764559862836085530643408020551049895730005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (2, 5, '-994877526002806872754341495993610.60896951623817756834461124123286284017021118170033801249797242818270444792350668237291391010826978126604392715751281366489250793073354867755345743514510156309395711933053460228041067059994425117350974491367099004404995846913641329458537237789584653041949090121498951516476399288513593944575192159570458664608461677113504914551578443229008454218964701550932948083369656042643364608405637360180021322967144409944099438498649645368196191999692949583952927486593144959284443545794934667002661774373364219852712996869245745722896071593910890197478196462961042627387162830776094709087748993678069776845437889735782063'); INSERT INTO num_exp_mul VALUES (2, 5, '649411906691138274293985410502516861224852.2323455192714410716272307781034189160865613770320102043319541634113746032638191509585045862973333645830298922352816245477556264222094036953195419857712804755170632292914187367964994214922001758104594052499795564860466055599417895782179851297585155129541589802249540436678824225950907268084876110445460948679383611117263673106597132046331719468816839434908155684738864149955129235751738204036443603521478609787295079710078973503970964790273461142497259987849074597264522099648376356902360358310245001183020992360260836105404118742418040965190000718736837422434593694808973939805954329718232693154128543253581495885789333274488461716809104532693754070810202831113003978085636579574171344721710232931261731022478029314435363413498991740750878099825781577297965642009156858479681236085226911858782115'); INSERT INTO num_exp_div VALUES (2, 5, '1524119409495532727030986.638577103454261465522025182901477334004986357902177024959076085490119358611626688213654669281670407680244740174673394111775678935383154847014211641601227316639834450258566053805263858706381900273201146454036688771735398324537667996974210741719621449948660517037619359095556637235980122706739013220201060795557114248610410815988952748489854367480813823114296393315170621979351958306734282429929421779129764262568942699813166237466796852578307944635545174715298176546980314973426586923195248536376403319094417073026382024413817222396402299695717290716014320518777088811749776114378145110676170242861393274018655137797545194817703831240390631723050378397773341835222892981773205967439339460305257986693600088957772328044922955990976285151896366292514128607363007421484320868718566256882080399264346243272770200676'); INSERT INTO num_exp_add VALUES (2, 6, '-994877526002806872754342148749240.99659316232359475297606895243958507460511031229368344962653674268847910587702140353344168594152240599109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_sub VALUES (2, 6, '-994877526002806872754342148749241.09046730671373705476503023105513751542110329332278421699361618343639171319297340877148998204440427879109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_mul VALUES (2, 6, '-46696638263247522384986521136500.479312417066793299922708112595886608370451213741279484136907754744903470430131032928908162742687359367826808123516519335458861613010646992354378739165872253762686683966945711430182491860196341344982195078000259063231136011430995647812149294224699587849791008794261026932467933475782780'); INSERT INTO num_exp_div VALUES (2, 6, '-21195986018643887410662481595901800.342199657994285865579781485758715114242459388977583220756870314514884887803267837816669111279417861218648323488364513921592045485003563036021370174294475403630933854767386355037781881144701319212711655881277140183173924089814927297045029394618083349813549439341772734606115369911736164723942330187830605893993276674913563980890459604886172701331890746621222114280438198802989678877404376001410627722336243835841751052795437979198996482216031399073597399901975686733315751292369326904428230195579137225651689857057115970784985439417129044974524632220457594191305254649113470116960582543784928547885740020507755033347968928034294570497118410435615856155184563329718831512839630769097935523279881940380220955993456451396417879773380305142918906742431812580562496634831735169817705720949712410595406012323294829461'); INSERT INTO num_exp_add VALUES (2, 7, '-994877526002807691688882220594983.04353023451866590387054959174736129501310680280823383331007646306243540953499740615246583399296334239109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_sub VALUES (2, 7, '-994877526002806053819802076903499.04353023451866590387054959174736129501310680280823383331007646306243540953499740615246583399296334239109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_mul VALUES (2, 7, '814739569184924399102711674444306584731316176345067.39834031417849342571224916231092924046722938910652929295271097903377854123984307101079073134405782275535446337229706620713104545454319555885847481531722101704765783025789147453570970090'); INSERT INTO num_exp_div VALUES (2, 7, '1214843772391778.127361407585140553741220126410637250571020684739034685508176000812180032686291124045768750332493129822580347351032145964983629059968936201592138368806173099130176852606440296388856520582890650384142745607345709716826703676313341953999327129144154152914234659001555055379537780751567782847296067128932113870102563522810980359433259696591977617184951677390423898232135100000764121508662830515405980450892222598485287609657612482190264517684867291774820716746063133066053446257163185646067618679478975882247893469409405379034723543061767846895135644429012095930584952053545016706315299076691015196261253199176743281648949731423486208098120903720124071047872917636988241710583721537777321338769039241700203546247947405745989053846970910400831817998342969657501678430211657755864160072525313889413731419647001970593'); INSERT INTO num_exp_add VALUES (2, 8, '-994877526002806872754333651763017.40289299098701084219066388457144979069028441485513418625082363021182982914675513019536443438529749838106171095037135009526312783302868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_sub VALUES (2, 8, '-994877526002806872754350645735464.68416747805032096555043529892327279933592919076133348036932929591304098992323968210956723360062918640113701577855434596514974380902868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_mul VALUES (2, 8, '-8453460632655529853033389979024265783461224.3195241893307807116624750282852146303290708492834695194274289713076935297734670940696121761483641291930931061232942894577813178566088927221374036301485916497770984757492912292002695944367308880163698595015497307574177176409203214324418237020500352652934909632442547242092296504047310806151851207329042221920888326000'); INSERT INTO num_exp_div VALUES (2, 8, '-117085929036205907700251.219065234073336548829793284434494573185718678644093751558890746941383215425734761534822966779511801033216479269605150574332107020180872343673157350081102818832254463561564431056604957702984438484261858890324442581609284935850435611342611117035589511568432559140282381526487115307554496353616929034919886387903446436924514812698404129456069856633480965357915969548215985452939172313964007318881987188665231550330515412104367728617802960792164260429920719961650164518261501571220901151359208484337831586551714193024143212288426326740373893030225940355268499071669300664200888186064836443459131985786957267268845966279576380786883200277187591448294590370986026461176853573555996139940001165172158855197070946665074838360933025833716166930231164328918316437195201546383664484983447934244744303265471044295601062898'); INSERT INTO num_exp_add VALUES (2, 9, '-994877526002806872754342093885760.69667996446358567630831677089993316481039076439881735980566785462673358516198695146576524119916430759085192883825888457383242076882081857926408611052522393579396644731758241837010163568445385303315267086044455246361273561294141518329233754041352632499787199926225490924591851865949646448441825186059741089695009429827829188117479084665641367'); INSERT INTO num_exp_sub VALUES (2, 9, '-994877526002806872754342203612721.39038050457374613143278241259478942521582284121765030681448507149813723390800786083916642678676237719134679789066681148658045087323654637787610377226547625566084597844703238942080799221554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_mul VALUES (2, 9, '-54582443595378013373024060492546032003692.4875677735896411267274323339692558458420972958075073392126734000341372096298914875892612108329218081214550050039133117695428196702128258481789017059073444323729583900855712795086447886053552786449313809589992185978097430132940882612817775035217244553616977182049775786664446683332098226841743818600819221587510039430478859412452506872131851471967577741190323481953867845129745440745526578327709351120432530702446916035797432129052518980799424635406993848916727957825620638983706180841278402925286540375225365057191075559133035'); INSERT INTO num_exp_div VALUES (2, 9, '-18133693300409132895168796.074616314168631402221003009151140409826855230810646429042722071403306917323628118792142878282108022292754325022530103525285999179488507720688317761243448898240836430183645778132937666952111134601563043980164547020295727057908447220163534134835130866457657964382363853570827467081988390359191484798677813656413640874450449802233520570178139244957518604566383671867773821069602665918688868868894979351219381089954104823746091972754649316823714354000113723793845707472924569647945844436702275724514171940901057842455729977729388911537391920702753167125695758365521631000334183494148229356487592577177344247694925635113222720411958290166668659311154664393442690740373285505786584987609789805525300762074682544164213490532272590665630428583216403362629445153016404037983825555019274338559686335405719430737559715778'); INSERT INTO num_exp_add VALUES (3, 0, '-60302029489319384367663884408085757480.2322712404088283093870869198708849258097125725036189625900174440196153781995220721511009855207486224837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (3, 0, '-60302029489319384367663884408085757480.2322712404088283093870869198708849258097125725036189625900174440196153781995220721511009855207486224837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (3, 0, '0'); INSERT INTO num_exp_div VALUES (3, 0, 'NaN'); INSERT INTO num_exp_add VALUES (3, 1, '-60302029489319384367663884408085672236.83687099063256754698860828386302509843815398979402006244388708674093244201278399438376682321121138429850885935540924586964982855913223221441591310211730902799041126800414795030815514254713522692405212716783388698431088814919226444677188004928663343696636297536500970117716818423689175692808344185016908913828066250587407384563498516598672584120143890364303296142744031320345312431817858545326010704685255237541162931904446804064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_sub VALUES (3, 1, '-60302029489319384367663884408085842723.62767149018508907178556555587874475318127115521321786273614780129829831438626014991843514783028586066905089122532715288580534070605779007112619958852628801540288008918482404759132944298520148080184250697297150817299173701934285646867489426483932830299434150464278537812298564822479785688909850915447762856384542090714278516461905872647123125352735037721325154184406043613668806975385533851732090363979459292404685190942209855935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (3, 1, '-5140349743195574373979577554212527512597024.162480344833040409158673429491690439298506850052285119390701002577176786023622062742050099464897084793357329597395417632908812044304066963549928478520702505283307379218587635434673128958824348493758429380623577527186462464399974242800361134191519694694139153279582776168995426125926314513926640766117733774558011741611075336271613675760116784769700605008122422944290652448956922432960815546502965310676913079866511016221573557684245901002643719965652152439520727383305120298495304784052489867651462175349450610643411043707261107569691076730261762793560088893354750383257372118118753366377402045596735023445172252225346164608897913115394905485106225627590643805003075069931177395059698550161546962768768895596088478488887530518018212441345360153523733317120037436403475909117998647781920105313938836144009539683'); INSERT INTO num_exp_div VALUES (3, 1, '-707409990019504668223608170643582.082425157530076679823177950190511141917761066423266390864536360056345386873500583953954967225431526056199231768143978526582904071798714789552447782850723926323452633811653766838064983821149041415149067433978085927687765773012158659685363079191901396502099956189371719135315616249471739677995520904113581848295732911534266040260836644379296158092198514963023001686666281725991605685524015227112003429486755206848316731257322742428352116058878710728614841247581716185886403744830796740424927494009978599974431617064012221450054532987372285996679180090592706458366967534834069977644215413076082570497451654516268857039718730203921980307096740864747006176117071983875364434497517026142488015705391255750729200497229031250705777282987863242056223584453312226818451807347197583925624299372040413470456696588043062815'); INSERT INTO num_exp_add VALUES (3, 2, '-60303024366845387174536638750234506721.2758014749274942132576365116182462208228193753118527959000939070820507877345194783035668195137119648748792386548310474079340204536236936213411512867171486174240518914767934028451971067161683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (3, 2, '-60301034611793381560791130065937008239.1887410058901624055165373281235236307966057696953851292799409809571799686645246659986351515277852800926805119259053513475211488115663286642009614039264484259692394657121785950542874788161683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (3, 2, '59993133911282372667149627097418449223835595194300848703012380022306762.154418449236691515146061305380465061074531890529497774836941002526095632166401249277270674802626154774328055399254982998368191676630276960361274433270795772477146870294928855773172789856196219950097157391050424577381777627004101100872747943673762087675405200265837631665464736842180920496158545887039337399558993437594084473932658319914390365451919627956823980800124880375978662052111797881386060353490432427832058851094210488804887183034572364751639107535041308434932952695103493677600969712634416241541391613699710826602011076372592299807609658979777598672141389319098817824624950794758296679318319299142035'); INSERT INTO num_exp_div VALUES (3, 2, '60612.515523995516156897729403721504966784736064970538891936016753206905080265887046037910122269129293912171105589512464185386239562077778499936203155976336284324712221812806801062157592930664021782540155687632208890794166119782594464410498356083266087045927038416810562596141871858142749062925965665039981381277808608946877852933015970874447235220989360704166270479475802673572039541121473138382812420076284458769543418652217394352637294823914346726065145538710933281768776286965107974980550163605068693568717671571780028113969794125200592691656568731359981803586296135840575095063824258761205175762907549288801963550628589530419118771779395037240198270853609924445368393952404606326559485235840170339343865253618184271158932135392539396160392488927771488269959497352568205940636180870805982484030168838833607478593'); INSERT INTO num_exp_add VALUES (3, 3, '-120604058978638768735327768816171514960.4645424808176566187741738397417698516194251450072379251800348880392307563990441443022019710414972449675597505807363987554551692651900222855421126906435970433932913571889719978994845855323367077258946341408053951573026251685351209154467743141259617399607044800077950793001538324616896138171819510046467177021260834130168590102540438924579570947287892808562845032715007493401411940720339239705810106866471452994584812284665666'); INSERT INTO num_exp_sub VALUES (3, 3, '0'); INSERT INTO num_exp_mul VALUES (3, 3, '3636334760530744652235488357607657374520053530993537920755375319352615385278.023608692512217812784472508939511216316773023870624171279878340621219698109986095090336065266376220109007718694455520948311677863167090936408887147442375455695868593092154861636486745490748828207939155392396090682312136290864359484540126174821846208064763823279315343506148025281475729723686566174395516982893064510403581479746673749128344955124070957545815390178764940816628194640888255387443237798761377617383817511745005525149990207764725040109364671749403389999498572538135588695345112358160274671918953118753964073105250116426665508214894805722798842017943220605600452911496071424281587802689830031742105619630787641205011894680546049982654601956546154572720177337696285354350903475239411654436042931409507429892682706228354459580412759920815932840348933425754970917910500027837428631661182510071352138858'); INSERT INTO num_exp_div VALUES (3, 3, '1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); INSERT INTO num_exp_add VALUES (3, 4, '-60302029489314054989387940744763542234.98295358053252401308872309802346144227050959966671157134780970446370197110016237152333448347415674483796371931316021552756816073493808344537122580089676304958104270609762310229182150728136567294798680824019082599362332377530165818229609055765904048195574142709698758095302560470195171027219786996322461803443213101532716728918363951912367135900414238535625075942525108530051828834829820554490477645701692374399416239080329365045332525699055300921341010989742896430768506909949340276549373661076950964959025967328861569387160956730002517417236732463510495205173523163676450203614971844583064927040066684531931069310935516821795449174271052747559395296525950219449541557191520903507653089998307641491381797101485104546410643'); INSERT INTO num_exp_sub VALUES (3, 4, '-60302029489324713745939828071407972725.48158890028513260568545074171830840934891554534052635383222518357552878529888177277886748756734050012959603126757618322788700853025193884017088688974683399381224865109134889560766307825097103477790782590061456916367930139323346273315068375646692125800496305291080749834712822775973790354498408104142209966769395239768969172107040437333428573572464689550003374384624966403962290572373571842567623422963022155546431883766327294954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (3, 4, '-321372325955692885069615337209737469749246561535004445508427591.072860243358366933071485495726715620133686420023451450292996945184959542770492705998350644739298629407567812798540119555932604687814429669592481327761428042980782672136901602006622227365754036664912989085940235439697789102358431343119457114603363936544931303133371137532006899162833369543279729021228901466728220729625107362063321334489394782322741444425117731922691457341543446841167138481424319752111748042440994701571955325673470021626946676976482516292402239416632497972073915818846704053624707839813514171497746804751780741682011937606462260710753056669269928580460921188286249923152921382198282201761171043384698319895970192114563900025573490442674225227682235790590616707857188385274186584856872573669591460447105688151281208238908470285147895678001948902280493477604361481216667716971590499226735103039'); INSERT INTO num_exp_div VALUES (3, 4, '-11315021446594.877643290091276308982961654569173523687151347727612592478433578066762912541361898899908505997444632820107356713116459078630334224890355872486337973552333755378190316811715776951317058334754704988120078733912131691682869448731717816749620336196719541702138949084375907248656748314375183301372633028246109596775255074617515860012417935744433243071057057560464360663978361945666099558526069794464437818864063206829678640156992474597480916575712563493776637239091589972373682399519931569163592317107392231951775499293572134702843085474656152913351183535194499521618027894129537558509428098859715020703897463518891082573242502356303078754574312965093639182648263511466558336912294702019648266054331227425119096294871153811412169351624751542166779635702042223762951850816568617453355571302500885410532963789364822647'); INSERT INTO num_exp_add VALUES (3, 5, '-60302029489319384367663884408738513110.66683195868931664491302527038538338065260819361151478340212147889934633981101279593065290940544218360883531149731823374304151252289014494378769385157204705433009477214625880056478643611622410268943757215673170753460135411513114716313801477916713433956086133878890802448531292334570886746283905390661877220497842493537338035961123751393889400517474762491881277080205381424363695095196058838349029211365212855028824622924678684631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (3, 5, '-60302029489319384367663884407433001849.79771052212833997386114856935638647096681695139572314177791340913988441658803134837154906163605506135872443908341816501241365674229987734175441883907154998906319658504271319733469814941611260503645706198407368762270127105340397375230875953495882740039984314121888705481484090911598074635434289709802794549714765847764347865064280637851906308955404165593747173246944693509650424312007333558709071857299501674917023499921977975368196191999692949583952927486593144959284443545794934667002661774373364219852712996869245745722896071593910890197478196462961042627387162830776094709087748993678069776845437889735782063'); INSERT INTO num_exp_mul VALUES (3, 5, '39362489275784146262776411377472433635883331946.794473520543457442955620133347015506556162839462623905489255080102447195050109095701660164272430316804466254467810714209179752718730906325952685817112992943656292503112803950215110778476301809440329937774061163668461957943313261962261081942055908935814323069621279128270849852239727888939033546870208376394878842958202403235309372240005941467570230067124830916866857395233038346727879951123599893174252558078732888910139309038957525961212820831321973219557165558911222848692996406741318948607549825343491479728117062814094258484536263158005174429922237853707635743736923521032098496725445243775790161216159399180889906705265012270270348146530113428221072591696851818281866095288773371414866822270689959827332258348570976075184933893434327278299820594014788148344260948638847457822697682605612771344335201258128'); INSERT INTO num_exp_div VALUES (3, 5, '92380711368470856513514428781.033155715252174277753317877861994356621252232374386687048394529670637693505779282500567256835271428113529026462111032257747830329068594622091282098767000694818101994264352932243278144124687156236926607422077479412495979777588932692081795130282128890441931602671468684153168580234070246201722180460130467506344034452687371838907269162119534950946217165384250603250357360223255177692065141037447374172264943732616165429783010079281851748804739433821308362193703012671569249508710820679009084891198169587484117171861141580870066764275087111843275285564262902405980617569581840831518012986031156042600391943605532635833608358301306456966765206853910579231447150839538731157206153540873916893579943906851149770881336811951119112558311734171557608362620988555075663589827484854016702489324791126228380209309587206299'); INSERT INTO num_exp_add VALUES (3, 6, '-60302029489319384367663884408085757480.1853341682137571584926062805631087054017160819890685789064777236456590745415460695320768374693076860837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (3, 6, '-60302029489319384367663884408085757480.2792083126038994602815675591786611462177090630181693462735571643935716818574980747701251335721895588837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (3, 6, '-2830400711649493468815157129316992649.40542786074520931471973065281957756940496588853021620372179463538053123396140685749478530925306163968207226329985017644835203709485594362663495728106061878665324856417118064730721101615473194292620972173690618491026470353143141125614124440035267592258385099934706896692953497971326605145704135723011753705907329979207428661473172503098296622281647255008204864404416199384701720347319806375450632245634238172654086373193251877533131784268854289406126119630708578053354762596511353053106459297339360827562281168219966099848212'); INSERT INTO num_exp_div VALUES (3, 6, '-1284742031601444539630782308463065726620.121021225455596762466053504195700643301310745151565435123335541550963124666304408503436412726848834604336377169205828654564329888653766451656774534718709065521243637375270687684572524302099749018591530352756390467862377335526634920857924031482455373589053524922608255779040656019538392173139295812160325688504210040741075388404155144782519528791757450256668977268409265390016721724966592135644698341754332845002439113523127047593325646484654291494607100188094186116001064043796216982681807318598789324900462932294782971663150070521334398542559480877366424630693734132836518604260869235580641521264976411493166969530737254118968281271908306432918913600567757535151861421384835424322504855607676315840963696944683182767935565256136130185809101891760917733694553800748568697830680328155128016670099315391685422333'); INSERT INTO num_exp_add VALUES (3, 7, '-60302029489319384368482818948157603222.2322712404088283093870869198708849258097125725036189625900174440196153781995220721511009855207486224837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (3, 7, '-60302029489319384366844949868013911738.2322712404088283093870869198708849258097125725036189625900174440196153781995220721511009855207486224837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (3, 7, '49383414785234649002982046297226894664526726187218771083.0993243619030008310875293647868815940421844461627295157812843657782639833900543200310573708100000958929315945039020410482966753145208427035917753919085618457760620513481628641658765820294863970581642745379331727722585319163262763708386199720411053619449096019862596221607526610103408936214184850115071874430846697061554769773328338028749631552202705583855831155461651414320570061181212214810086436100771547030013079997847086'); INSERT INTO num_exp_div VALUES (3, 7, '73634737013325927185.787791148221519354461791539553527545166847382784629235192342551464898036004011575416717008403527685470842765455409054592207142526523023201841973047779202013398235864494503216973882479116841765663948294836180515686647139678530220909072497288527276378202532400736141014848907023234659020093073127450778982904578906877634654521825977382116752537063128793631412296206704078569268566614023846282524151679028060869175439188773864994186109445961525301841201265289707928211114515861536069733921800160245586536759625418951427346236213019358749196674633237197452976517130405065120577692737021174118093373953642724512531935525024447977867020930500433287279183436509990047372809400167546185096048971157700858970777301410692908939206693154161335335755844997198191427289546263182822280127912118140820265025555165337881999926'); INSERT INTO num_exp_add VALUES (3, 8, '-60302029489319384367663884399588771256.5916339968771732477072012126949734214868901845505193155307646111690097978112797961939995859130827784737422228762767014427842766445950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (3, 8, '-60302029489319384367663884416582743703.8729084839404833710669726270467964301325349604567186096492702768702209585877643481082023851284144664938175277044596973126708926205950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (3, 8, '-512385513828318260570283740065493064477880918352.732624553690077857674083796435724202494963885926573907185100543184828131859183999195040110586155435203949963570735841632689374488877298209082579317039061893012560130258753218955057387206477423088065663401594359617882154814262843273526859406265633827109554791772242178864873774889091687515990672487380368975556580539271333144212685871370972163560839446696514092637412587953506052848750866803569213269271165856310101244342151576488190595936869490659700946174362872797854591188391982770203203644172999264143929484089237665313698600170041324566984832357000400'); INSERT INTO num_exp_div VALUES (3, 8, '-7096872691348467943606706217.907270287823269424282176534343841939501231816905820949045946136373255017076943323578903040918266385724756894003692978391468202345397178445216069294845721607024056189567609414049207292919519881725733381453217071918292453682942046440563446278374996563501512335133749731529362537349288419883140401056747081065947774593869673146309163791076953204291951821124894409171722911526435445719071769008713367057971351892550570642991097981458696464929009464411568672010548002196406312721789582428747564855324072212842315229302959908665089850886951261233852165624100634055045684536311382452553544676139507899503993644452161529145849579200003677255968757773363970434791501820320494192909660871475590637419913907191608957830524390049664686282439567943053924245852983990958276537000732363895444894582579142752920882750130052682'); INSERT INTO num_exp_add VALUES (3, 9, '-60302029489319384367663884408030893999.8854209703537480818248540990234567956069965340942024890856088355839135538265116174644003927269495876835324407641642359213535695803871472434650475144516723617632059130297610134243891145006222068960999879308472500422640481972089756410157246974765071949782242392661524488959954348903412713930092273629207697480131360047867213863018127928853922173643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (3, 9, '-60302029489319384367663884408140620960.5791215104639085369493197407183130560124286109130354360944260524553172025725325268378015783145476572840273098165721628341015996848028750420770651761919246816300854441592109844750954710317145008297946462099581451150385769713261452744310496166494545449824802407416426304041583975713483424241727236417259479541129474082301376239522310995725648773643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (3, 9, '-3308379209762459471107480259839508279070920437.883503980178028214343751083865562028455061662673132221930429904398963590401793045470444301883103141901787466923883803951815572606105617157736442670792467625964359169270739534412932791178258858918086886061702512427989129732248215348301444245772127142869263635282888226326427510486246184233225114523636171202034558843515894542952126988613018789833835507734620046994907453602573865012044120483116345444810078666601100257620969379968264504287700045822481492526688635364586344704730579892342786173395802035361824932075736340405960099542224953439044947229246847140957298841482874444906129049023002897135347878048572628834749795298712449864571996898774444932083319581439741625832405434317985988163261591679157437224404970927012111196724239860528859217322132733404472897289'); INSERT INTO num_exp_div VALUES (3, 9, '-1099128766678422054524173986658.839339966689456265703816212189145237878729886466041806078542573981227645802109969871638687985985845489422516004202630099080709709893022100481258818112345013009059633421290241583864468453396484606925071369550998772875840640325758308835852391176503689677263605949075815552026731067384737231681068134099746550363063940273625924224721503126912810251607546172009765059506591787282558727077669973711491157840340631805422942099954647016059576777054339588421998882440726473698513560202030309804089250300097589174314677765341104767702983421063649104691583044460507666600260994707192787133590502137391691330098102374713996115782701417107878938473243874299874872852713499024851414757892169376458916467621226859152075901273014182163212783658933754507272478777304254191033562324994395916168496097385872331012258027431094381'); INSERT INTO num_exp_add VALUES (4, 0, '5329378275943663322215245.24931765987630429629836382184742348353920297283690739124220773955591340709935970062776650204659187764581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (4, 0, '5329378275943663322215245.24931765987630429629836382184742348353920297283690739124220773955591340709935970062776650204659187764581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (4, 0, '0'); INSERT INTO num_exp_div VALUES (4, 0, 'NaN'); INSERT INTO num_exp_add VALUES (4, 1, '5329378275943663322300488.64471790965256505869684245785528331091076155554650629138833809683459634328609777839510066435612911583108717191216693735823717997111970662575497378762952496582183738308720094529950793570383580785385569873278068217936841324404119828637880370718028782103860007754579779716996004352284614661690063919125301052941328989181561787543541920734755989452320799185700078241880935083616978140555713297241612718277766918005268951861880490889884082730841740604517529391011862694381726143520658746305661338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (4, 1, '5329378275943663322130001.85391741010004353389988518583956365616764439012730849109607738227723047091262162286043233973705463946054514004224903034208166782419414876904468730122054597840936856190652484801633363526576955397606531892764306099068756437389060626447578949162759295501062154826802212022414257953494004665588557188694447110384853149054690655645134564686305448219729651828678220200218922790293483596988037990835533058983562863141746692824117439019450865871047657552800448629502344444081260036580660700595591338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (4, 1, '454294299613767152878025320780.534199313974295807138790763501115780294529340799108297697573066187975311338382917022391830256203305238757334106943821060545424417350991354829668286194840925251162479496893943917530660694097932059166013476064988623431110002057735318529554555260199417935495388243829261809007709919225000608711536928171687251088217591210419208480251102484043683131687013687838713055660405381318396419588727500715930145098362997142075433472039319292466570912777345841400769387321465602989947078951135489852486382469990409873227894248208197179481868230244584527040573428134962626267135732247029762468417273891700661832893497067151409134724061246612631376075173287264787886064622106855886785805818642123776489793586531950438285720668411465570116161790343538663297713926678759640594912243360541590368666922379919514826022141331900181'); INSERT INTO num_exp_div VALUES (4, 1, '62519544780217042176.800424689664850775296526267109332647921183817056683200043718160298562843864918741523494444361916531159341418970534833628106062976341639276761669219281771109561175175033739624472497927501467465456946098280878993371659461957361369508794842102784763955539708800574418468150309301129490186416766691183270872711413796386178009615777589066235359283212636467980113350635181915492452697347977967985810294150853782607014649150457138118264698071689065469752702524632313088938504181640435324554007553994564705401249228914199354821595855823113730697333390936834057091883654016371107974899726642500486005445063301647520527084320363513388355471718583708935211830796440056542408492723718088396437530207347815505844074508948817594746824098278470533148171941442049323578854023683167934569551595335539887777638716651319134577441'); INSERT INTO num_exp_add VALUES (4, 2, '-994877520673428596810678826533995.79421257464236160757218576989993781147390382997132644206786872350652200243563770552469933194637146474528320738725486418004701192337175478117026439697031462361180324038544450723753402846519731908503949116978812841497201119103409772457270340059605961197538918709309004130294868847110690336360689446090125918336908930881873778405661757289469281163974774492810850778950071063044769131228124355961427111369335109426492177657001035045332525699055300921341010989742896430768506909949340276549373661076950964959025967328861569387160956730002517417236732463510495205173523163676450203614971844583064927040066684531931069310935516821795449174271052747559395296525950219449541557191520903507653089998307641491381797101485104546410643'); INSERT INTO num_exp_sub VALUES (4, 2, '994877531332185148698005470964486.29284789439497020016891341359478477855230977564514122455228420261834881663435710678023233603955522003691551934167083188036585971868561017596992548582038556784300918537917030055337559943480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (4, 2, '-5302078674303935968062773235453828254014583744527466365136.236414807326868572353809920518232561005161225922028750078608989965741402418802255050636954800114792425419735155504035469350521800895164087027043476055514245942961100610551646034472084954313670284875310691807937254054948742125729353864014122131419164449567115006621212424805182687707372956385102095255735458593389920872596796806885847543910224476727171570873698525606016990229936284811067826588349092841322512643043008589065847223683467371925773023109720951609815041012521485326120380123169545818055967455575736140138663815073081494226676896278654189873597341203197903408668523514375373841493189836809506003729379742035629498519683885268256481104619815130659628225053833297766479068686119691010593208135616363994230674606991733148502293102108193522604968743948323130517040609601859735899914987426089053869350663'); INSERT INTO num_exp_div VALUES (4, 2, '-.000000005356818439105666775800262590702859770599410113087721172791624002387236505438218124867814437523686300450045582100868990117124343222534568799037421944272316277130975314766456260710406160143182498931595199129228915695802952695510723443157825968340043198200740606202264287904755124946591110599335909404657109057432686191440989434662797205973563889238804413861126260401987949920244286377128599413927273444061572120561496904543200956508673923547626768641271397088562966176629018606103663605145666976048261236691866387601532424530473754175270500777679603569715192364542901360534980926452487443629100484491344001509360344122933911316486556042277769848194790964257060927912344609376571637126617813506411190014141992988288983968823792971270853369317867326071952900448455162898476163801382836761898292684175721846'); INSERT INTO num_exp_add VALUES (4, 3, '-60302029489314054989387940744763542234.98295358053252401308872309802346144227050959966671157134780970446370197110016237152333448347415674483796371931316021552756816073493808344537122580089676304958104270609762310229182150728136567294798680824019082599362332377530165818229609055765904048195574142709698758095302560470195171027219786996322461803443213101532716728918363951912367135900414238535625075942525108530051828834829820554490477645701692374399416239080329365045332525699055300921341010989742896430768506909949340276549373661076950964959025967328861569387160956730002517417236732463510495205173523163676450203614971844583064927040066684531931069310935516821795449174271052747559395296525950219449541557191520903507653089998307641491381797101485104546410643'); INSERT INTO num_exp_sub VALUES (4, 3, '60302029489324713745939828071407972725.48158890028513260568545074171830840934891554534052635383222518357552878529888177277886748756734050012959603126757618322788700853025193884017088688974683399381224865109134889560766307825097103477790782590061456916367930139323346273315068375646692125800496305291080749834712822775973790354498408104142209966769395239768969172107040437333428573572464689550003374384624966403962290572373571842567623422963022155546431883766327294954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (4, 3, '-321372325955692885069615337209737469749246561535004445508427591.072860243358366933071485495726715620133686420023451450292996945184959542770492705998350644739298629407567812798540119555932604687814429669592481327761428042980782672136901602006622227365754036664912989085940235439697789102358431343119457114603363936544931303133371137532006899162833369543279729021228901466728220729625107362063321334489394782322741444425117731922691457341543446841167138481424319752111748042440994701571955325673470021626946676976482516292402239416632497972073915818846704053624707839813514171497746804751780741682011937606462260710753056669269928580460921188286249923152921382198282201761171043384698319895970192114563900025573490442674225227682235790590616707857188385274186584856872573669591460447105688151281208238908470285147895678001948902280493477604361481216667716971590499226735103039'); INSERT INTO num_exp_div VALUES (4, 3, '-.000000000000088378091435340426596348183959201660680284222502095357746364378698792730669202270228092348823133529449019715406417264278615046537007844589547485282959556860316942508808911542109265489435572674031608663747132688980867386885961271358592278360097086532747883342438036287136994589308551796702164612609710942175900921197001888540314760352113821737014875886635147123114456910985089625906448913621495025509697742196814421833448856595853403450682101743559369637786458968714240975228615283970739279506239628546165569688434254286341567486905374255702980370754235630955328837646999003123103831262789115646588779721625156078607919060762857866951417867378220773543985422722165221371084387943737083254760594128718841665355053236168688218864433967871311858292181233490194833547273501436630325295640020916257836404'); INSERT INTO num_exp_add VALUES (4, 4, '10658756551887326644430490.49863531975260859259672764369484696707840594567381478248441547911182681419871940125553300409318375529163231195441596770031884779531385539479966108885007094423120594499372579331584157096960536182992101766042374317005597761793180455085459319880788077604922162581381991739410262305778619327278621107819748163326182138236252443188676485421061437672050451014378298442099857873910461737543751288077145777261329781147015644685997929909334948601889398157317978020514207138462986180101319446901252677846098070081948065342276861225678086539994965165526535072979009589652953672647099592770056310833870145919866630936137861378128966356409101651457894504881209406948099561100916885616958192984693820003384717017236405797029790907178714'); INSERT INTO num_exp_sub VALUES (4, 4, '0'); INSERT INTO num_exp_mul VALUES (4, 4, '28402272808100253242547006276715304015308580784958.804614276533085644370816876160290159450291717634111299841065255625515058118012211808741402904995080624675460593676923639082981788732031193774047612589113654423166826140872334380708795266307037944059108148612979119729408762532396036043629484049508789880964586236575769826806092391573178899640321403656891487586452524427223891405519836671312830183895761747460911777623703557946796784873885800089025388390522992806365773290733075927321101736155663727528284512100509273076328103465333687228713897893434161293693971954442699482857938492961830350598789444266860160794913830991304996676299650460125000959751177037694425217989910261807246272771711816326991282202653917488360776928533800529297474279497910326579608191975246060946079639658615178160271122713225105861574160788280907842327681375920919676063500116492292319'); INSERT INTO num_exp_div VALUES (4, 4, '1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); INSERT INTO num_exp_add VALUES (4, 5, '5329378275943662669459614.81475694159581596077242547133292502869630735172901157043010370467618244548786897684821457816189831652076071977025794948484549600736179389638319303817478693948215387894509009504287664213474693208847025374388286162907794727810231557001266897729978691844410171412189947386181530441402903608214502713480332746271552746231631136145916685939539173054989927058122097304419584979598595477177513004218594211597809300517607260841648610322863666300637648662611916496850248528515936635845594390453288113296413254893687029540384176335735114863908372780241463999450547422213639667099644505472777149095004849805371205203850993689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (4, 5, '5329378275943663974970875.68387837815679263182430217236192193838209859394480321205431177443564436871085042440731842593128543877087159218415801821547335178795206149841646805067528400474905206604863569827296492883485842974145076391654088154097803033982948898084192422150809385760511991169192044353228731864375715719064118394339415417054629392004621307042759799481522264617060523956256201137680272894311866260366238283858551565663520480629408383844349319586471282301251749494706061523663958609947049544255725056447964564549684815188261035801892684889942971676086592385285071073528462167439314005547455087297279161738865296114495425732286867689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (4, 5, '-3478781676337858247983014311182511.567538638808357215203593479841446379226774481291286361639429856698999485760647422501864626078375852610019829111004807806660731243672830787729048847342063218718651165150612717759770504648306347926061960607388621011846314969634048226452709389995594961695723139571002939804473057725442880410434039783304583526414509590532906062732322732569475349107437896717416548237633532805602064623969799081086996320156575550896200848758685986331692388099427314008504506503745527468550106879602399030419569897808150076298414568875477195447656904373310322813412927463518325927626891046356679526447117311923853482118502868148386882363449163182892615259995945992014431502761210899772725227648729095696228388558331052524469604046072203605897109629560683446827492904111565278516043939137760721315953500281379039771826554155511347152'); INSERT INTO num_exp_div VALUES (4, 5, '-8164430956184510.184223536017248184022252663660196916321116266103608317725855237211273642694947892658721606226082017525816544904635887836163201565923338826779819876742736219975639586566502584026349778499211535661173597356253186281116862244165796632756909578140184577853088376334255860281874385669242675881761388233070861374295536603371778669602656670852115614651462552069294889723058758969660566508798011830996965570446030123780674316363670374970480994905368006454513642480180066435609577311074332150098288374616437489163254821095377348025470309665651059603665062887597814064136313866690824972464351274062540825405003954064175728198182815347642172934453828192850870808373638597839434504241236228591053696481146252072190903430582534862988719805163692697482513169856291048966811374872266165034373412719593685881972700171726777938'); INSERT INTO num_exp_add VALUES (4, 6, '5329378275943663322215245.29625473207137544719284446115519970394719946335145777492574745992986971075733570324679065009803281404581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (4, 6, '5329378275943663322215245.20238058768123314540388318253964726313120648232235700755866801918195710344138369800874235399515094124581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (4, 6, '250145412892811547138949.592621291590152419206270097656346630226508074074623894951308487425470437268130465956063593951784820669318897182831355375451719125809800516979013437732298382708070979871283132689492336823087794373113039154669229889503700598930220858275174342776478898670277868700384853696009897221747924643343353942154528501454689084608965009561564638167714973711022212547096732831847202912862290958304510651828842182545311077713664465815992616213663619529378061133917572474298028065850515876361609671565914027186063801852554353160801534696062207299890867876199323530337336273950892723090754719547285920090419070001019943385293110663922226230169381423410428577990604776655422105400452217085311617728003688836185608912367677734364834577573255789160419371322775733777518997638403409000055707558465286469808848200141192627396502735'); INSERT INTO num_exp_div VALUES (4, 6, '113543048739697485358574290.758354267447744932153707340542459183720907885610125346262898114677742971240785031722334497858930434531517077525413654346644836353208132641713415396062580605566225794048569430676355036264762949452090151450855446984773994337170590068740235544320694721909983307239491151139099779296496785240814600627140543144068640768857707110930453204162312973998304574796413938461971472337040811785231390930046688391955000749644938061585377150632133417156866197053052425576957646564943278156977176976876921235395711611898108821587442609611001702344783440618040704066809035404237786023075676374788819144406909313755996914145273176359246052899650387182222905558751208368173052381982668563471143298720677965028880626152749773712037769548408324298835212547215352657271696665387200792785056233953536347605130973626194099064678842085'); INSERT INTO num_exp_add VALUES (4, 7, '5329377457009123250369503.24931765987630429629836382184742348353920297283690739124220773955591340709935970062776650204659187764581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (4, 7, '5329379094878203394060987.24931765987630429629836382184742348353920297283690739124220773955591340709935970062776650204659187764581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (4, 7, '-4364411947278810125327066890819882483326918.05664098958260550284395870948992407314161088028674246708928421994893923699743452802989464864039994566042797942433140378990308345483670828497915478397481687305406460330009319949623844175096007381662809083363069100235985794575399268709260901964834244796150883807308976949196661411035264619638771824190014274817662519438658481432363824187693821267613212631153175155634316128036152465184903927860719447693468054624663668062006049759837326188252927823612718163916100588143128358998656306593393889422386501730237442526450419990376323903182669190482615734972147533221144682538647497701130447816148459762464395194383090936159579764712919396391813914821973715879062992249315474841639591907249142779103650773383644785606333916967894'); INSERT INTO num_exp_div VALUES (4, 7, '-6507697.520580964829176145824902679560705744817573189143227837387224410616222039115571544850095278317993922427931439719549137387753697989249394347047436951117850128104928719365703899136632100669607126357491484781141296021264049762417528697619931558728863308905257358126654378784709213859234056696519305650316810797382293500878834933984458810656133463638442959750083607649924453935287420620424368291770694630751828333903156364366745210911640207075765008558904788350844410055253643515389003711759818446776538393914018427075074171758415188027562645239606914126802490579848138218395145734902830046359100742374008993296019987093605275289913663224324033923096998194326249508491872193747944673057257521552387923218450155737056841633810711295424578984452176016198348344913655301417872189073133147510027427530833694019910340299'); INSERT INTO num_exp_add VALUES (4, 8, '5329378275943671819201468.88995490340795935797824952902333498786202536079000703830146057240651898748760197658486790165425772165585380839129948178510273188565692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (4, 8, '5329378275943654825229021.60868041634464923461847811467151197921638058488380774418295490670530782671111742467066510243892603363577850356311648591521611590965692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (4, 8, '45283653791262997781451381354094822.762732909505051438036873220502792213670540454778361182993875916509061144859281577740137081988678361247725064336120451090222456518107029158304937620179032477664627949959143233370320432203497828243297406462513350790251761540074946469824444452248386782451723637769289822576372357189700319768797708375563651655860093365309717823602754924352327588945034832436331911584742966378275504545736896430718939807674966738116698454215555860047859161126694019895490767779791933882712567492115664113775047192011252893773389940988533801360010782816196288710063568554147458866942816721046004257953642508395867837127678980002737669139369781058046396738606563716339660654364541530532834806205571191828994250708412638796240377704994928921528330863683630622922959130920715261879547446054261914770022377059156125037157979236658010950'); INSERT INTO num_exp_div VALUES (4, 8, '627208063620965.397582272040628872773601055303353339700043792111288801181637510303989399395425313995651311362368773096988861977687484912995632130587762386590996099363383976320342247076516604162469063709298438133327434461462906199160715395064249299615054970359309619951777972710299484596875999967582794277241285253106817446259313281064844416249524876385699646393555435017820686376877981018047574348711991428666249794623006175739581915209218834701034964043360823844816042368184094857692062884223864639972005010863342567608351008172649209459933114800143792514183138995700133608613158857147417653998048890116531052767737435620558349226865105888201598712435680481803901906613772821370519525404423549161696526405320391828194356063547089626322474164332505209233143121068245585662919687001395119229263995765376465304715643388771609446'); INSERT INTO num_exp_add VALUES (4, 9, '5329378275943663377078725.59616792993138452386059664269485161374191901124632386474661634799161523147237015531446709484039091244606359050341194730653343894986479159670583937529516163204904273806158788218327396375034882788180783796976731912141525319602448709213495905899041406302673881364465504945113279286939663215197485367850132991968081639290297033476859158044889351836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (4, 9, '5329378275943663267351764.90246738982122406873613100099999535333648693442749091773779913112021158272634924594106590925279284284556872145100402039378540884544906379809382171355490931218216320693213791113256760721925653394811317969065642404864072442190731745871963413981746671302248281216916486794296983018838956112081135739969615171358100498945955409711817327376172085836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (4, 9, '292388240303165948041827159734686.255558469787242316676287235194652580157149226950109397295920730296960145548003120827363226435916209781396711693581454960342091452830648929118261388933297036933167543189308061917640517578583521401267417187854611829815212778183983326568586118831109538377828156118900313778053576483381085207892754728937946691892849474364477434665960112125254104966566712906532318984871145605839506991591027939136026602051635433295687547552796828217859648186757719639965988287173297286034098497871707197092627676226053609131138590878743560287292934815277894463305001278326023708395571840850120055316276256138004565442099731931051413153564744766098053176049414330146267604802971221161572130161432525297614616942172815141372973870720928125699420370428856022295499447755488148545048400795053604349570217878099721865670458104653570360'); INSERT INTO num_exp_div VALUES (4, 9, '97138902640718538.241246716463110895614166618530828908023040947887095196830690221211560526562522274118188963051412359798837957512805692731972838989047910709158995922699598619854907969493232150042212406549916252602794415099066259707018021422154933830674786488990033885447289593742424717170197810316367637885248684134204152352748803532396210051700193575105804898183523770153431536054848843504020390623875664696278263569145547515663340450903772852615789980257449146000410036925975898331113013857953289990299253584950458042598491897496393582249411290555264437893099880371008957017323366523688894303458743415715114628052487518110654201696604914159777300997374156315186315524817636714210119873791848535246674326877611945112249137224923201544452904111118569299934059002046318394345055859769572070097973298522564724884895879226870720839'); INSERT INTO num_exp_add VALUES (5, 0, '-652755630.43456071828048833552593835051449845484289562110789582081210403487973096161149072377955192388469356112505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (5, 0, '-652755630.43456071828048833552593835051449845484289562110789582081210403487973096161149072377955192388469356112505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (5, 0, '0'); INSERT INTO num_exp_div VALUES (5, 0, 'NaN'); INSERT INTO num_exp_add VALUES (5, 1, '-652670387.03916046850422757312745971450663862747133703839829692066597367760104802542475264601221776157515632293978442027199108085723617181683235487266149426304575903892721468296143475297345699313102262188759506518376019936160961709578829069446312051432780603656651983414612264636232727512091101057374054475214114364113300402823059519499217878746766275164739724770556122895799337810694888119810524986616938847385753562624139431982468828696587199570410008890188532132652095915565323400735066310142303225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (5, 1, '-652840873.82996096805674909792441698652235828221445420381749472095823439215841389779822880154688608619423079931032645214190898787339168396375791272937178074945473802633968350414211085025663129356908887576538544498889782055029046596593888271636613472988050090259449836342389832330814473910881711053475561205644968306669776242949930651397625234795216816397330872127577980937461350104018382663378200293023018506679957617487661691020231880567020416430204091941905612894161614165865789507675064355852373225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (5, 1, '-55643106304872.575994253221940844841058071061962511162776681458310912066379595519265546225338405882027547140476045378015935579066580347282075024392379464189067155567624835346798806677988850250198082355055954078446421075165109896091047534711081616362392995575466807084807876544560268050611445006601394735810211678919646667455478469014906335433468365011768049600750224822391684377238242162320161552720449713229523135506671063115436813348612986916614320012995541575293478341408982118538094438068036422562665160411591652618670802973618768526197813319204816293073794413317669922144705633308090832805914096147659820167569140291210526520361556881576175809360614782817717579318298657744021133210954279487777567785280633309576696708168342539425395482429923273623865667723482418178781573723597156804085501875735112311466228778929147929'); INSERT INTO num_exp_div VALUES (5, 1, '-7657.550797567691019915353529993301413746369700087741672762343206271266232635965032053368224472333368713006346867984576168784127503674579531243603836945595880917241997606783133673324236134063757452734295148763280059050480246827193380861494669624151921824660313516974440913733511526807313019192263170823268678149435664224184903925632177789052038092611394447709922076676981043877747276056677801802695466205531230350209787298926245402046182150996849906836743231861317120171583577624262765589605263477198809166390259128339127005924586833372241946051704497188891325715185091060185547236923494393813210904033520844572880475265306843414506359253445517738473745552980984097762509546161690823646176501838559393690565709795724159196133663168004773260451322595899506776323262195323943138344537866088159583331807728944620284996'); INSERT INTO num_exp_add VALUES (5, 2, '-994877526002806872754342801504871.47809095279915423939648794226185974985600242391612965412218049794216637114648812993201775787765690351615479957141288239552036371132381627958673244764559862836085530643408020551049895730005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (5, 2, '994877526002806872754341495993610.60896951623817756834461124123286284017021118170033801249797242818270444792350668237291391010826978126604392715751281366489250793073354867755345743514510156309395711933053460228041067059994425117350974491367099004404995846913641329458537237789584653041949090121498951516476399288513593944575192159570458664608461677113504914551578443229008454218964701550932948083369656042643364608405637360180021322967144409944099438498649645368196191999692949583952927486593144959284443545794934667002661774373364219852712996869245745722896071593910890197478196462961042627387162830776094709087748993678069776845437889735782063'); INSERT INTO num_exp_mul VALUES (5, 2, '649411906691138274293985410502516861224852.2323455192714410716272307781034189160865613770320102043319541634113746032638191509585045862973333645830298922352816245477556264222094036953195419857712804755170632292914187367964994214922001758104594052499795564860466055599417895782179851297585155129541589802249540436678824225950907268084876110445460948679383611117263673106597132046331719468816839434908155684738864149955129235751738204036443603521478609787295079710078973503970964790273461142497259987849074597264522099648376356902360358310245001183020992360260836105404118742418040965190000718736837422434593694808973939805954329718232693154128543253581495885789333274488461716809104532693754070810202831113003978085636579574171344721710232931261731022478029314435363413498991740750878099825781577297965642009156858479681236085226911858782115'); INSERT INTO num_exp_div VALUES (5, 2, '.000000000000000000000000656116570506105776235076334177868550033347254561166417969910286926369599900073757929714260350320362090452092025380232792749476245042480546813848702351830607516880397305138543526307608094143028291193163613755680419049060162928958489964834941920423432354996040147818253087783193280640282263490705632002572757216731766513434035163528102590524432221718194164133959630768718395847710529339782880381264265894322494716854757290930538739000043383104085867828258790010654331660516512156519838978751447311068903958136482041673109857552178367614498426226323001399275980281507353231821022591045797658991388304873240910526149138339658220844723880158150606035181559877351791752701872877147074033569061408920725522180134133183999181370354585872214368766629114773129541658653693832843354053701079334077'); INSERT INTO num_exp_add VALUES (5, 3, '-60302029489319384367663884408738513110.66683195868931664491302527038538338065260819361151478340212147889934633981101279593065290940544218360883531149731823374304151252289014494378769385157204705433009477214625880056478643611622410268943757215673170753460135411513114716313801477916713433956086133878890802448531292334570886746283905390661877220497842493537338035961123751393889400517474762491881277080205381424363695095196058838349029211365212855028824622924678684631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (5, 3, '60302029489319384367663884407433001849.79771052212833997386114856935638647096681695139572314177791340913988441658803134837154906163605506135872443908341816501241365674229987734175441883907154998906319658504271319733469814941611260503645706198407368762270127105340397375230875953495882740039984314121888705481484090911598074635434289709802794549714765847764347865064280637851906308955404165593747173246944693509650424312007333558709071857299501674917023499921977975368196191999692949583952927486593144959284443545794934667002661774373364219852712996869245745722896071593910890197478196462961042627387162830776094709087748993678069776845437889735782063'); INSERT INTO num_exp_mul VALUES (5, 3, '39362489275784146262776411377472433635883331946.794473520543457442955620133347015506556162839462623905489255080102447195050109095701660164272430316804466254467810714209179752718730906325952685817112992943656292503112803950215110778476301809440329937774061163668461957943313261962261081942055908935814323069621279128270849852239727888939033546870208376394878842958202403235309372240005941467570230067124830916866857395233038346727879951123599893174252558078732888910139309038957525961212820831321973219557165558911222848692996406741318948607549825343491479728117062814094258484536263158005174429922237853707635743736923521032098496725445243775790161216159399180889906705265012270270348146530113428221072591696851818281866095288773371414866822270689959827332258348570976075184933893434327278299820594014788148344260948638847457822697682605612771344335201258128'); INSERT INTO num_exp_div VALUES (5, 3, '.000000000000000000000000000010824770508763323320533297369674519056450544793568147911931789010432012750062661590994728968589403602468229106206242395792957238667714358401601098858606386995096923432407249369639633268143022787987190106724545750803196130511146323174462918572423414631798141263222875752767731279138952850500369328934959764805948568471324562210715908420467881411844098258193571194910997918428786213948547748701831331312040839544355427357749520227124858111324859160114175254197992204974033767300989488517391063188153561391320190653403747521648794370679322504188364455328709488846777004202196382575648619395139553279192346251133156445942281048959845827006761160755031086836046398020850814350246219929303018051720203943879538087954853996826539712240458022307680912400297508925714946398031304516583939283'); INSERT INTO num_exp_add VALUES (5, 4, '5329378275943662669459614.81475694159581596077242547133292502869630735172901157043010370467618244548786897684821457816189831652076071977025794948484549600736179389638319303817478693948215387894509009504287664213474693208847025374388286162907794727810231557001266897729978691844410171412189947386181530441402903608214502713480332746271552746231631136145916685939539173054989927058122097304419584979598595477177513004218594211597809300517607260841648610322863666300637648662611916496850248528515936635845594390453288113296413254893687029540384176335735114863908372780241463999450547422213639667099644505472777149095004849805371205203850993689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (5, 4, '-5329378275943663974970875.68387837815679263182430217236192193838209859394480321205431177443564436871085042440731842593128543877087159218415801821547335178795206149841646805067528400474905206604863569827296492883485842974145076391654088154097803033982948898084192422150809385760511991169192044353228731864375715719064118394339415417054629392004621307042759799481522264617060523956256201137680272894311866260366238283858551565663520480629408383844349319586471282301251749494706061523663958609947049544255725056447964564549684815188261035801892684889942971676086592385285071073528462167439314005547455087297279161738865296114495425732286867689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (5, 4, '-3478781676337858247983014311182511.567538638808357215203593479841446379226774481291286361639429856698999485760647422501864626078375852610019829111004807806660731243672830787729048847342063218718651165150612717759770504648306347926061960607388621011846314969634048226452709389995594961695723139571002939804473057725442880410434039783304583526414509590532906062732322732569475349107437896717416548237633532805602064623969799081086996320156575550896200848758685986331692388099427314008504506503745527468550106879602399030419569897808150076298414568875477195447656904373310322813412927463518325927626891046356679526447117311923853482118502868148386882363449163182892615259995945992014431502761210899772725227648729095696228388558331052524469604046072203605897109629560683446827492904111565278516043939137760721315953500281379039771826554155511347152'); INSERT INTO num_exp_div VALUES (5, 4, '-.000000000000000122482510461124748279475400009367345900846466958806966807399903713411658400733717078392550780910604704603123670767210550800752620037863340961255721285160854785449315208955654408132775022766783343331151895973970395232686910362226184006990485313002943710214511418310741271074710741339586430026286272098156531835438969774325517509155992092194349661122678547097423264670055720422496527272118788005921590521726691666219504214087867030003203385360001614199656989667055583749577099440092378355805901262289841168751608673297446473709956390142112843400255748161809121986096092991616144443486023218404881798896685413932215981950393130292001833627899480153863300557853617312991880655905907971211246077450786084079040513198340644157868678782195341316027563717617074364438885981635394382733697473265872796207'); INSERT INTO num_exp_add VALUES (5, 5, '-1305511260.86912143656097667105187670102899690968579124221579164162420806975946192322298144755910384776938712225011087241390006873062785578059026760203327501250049706526689818710354560323008828670011149765298051017265801991190008306172717341082925524420830693916101819757002096967047201422972812110849615680859082670783076645772990170896843113541983091562070596898134103833260687914713270783188725279639957354065711180111801123002700709263607616000614100832094145026813710081431112908410130665994676451253271560294574006261508508554207856812178219605043607074077914745225674338447810581824502012643860446309124220528435874'); INSERT INTO num_exp_sub VALUES (5, 5, '0'); INSERT INTO num_exp_mul VALUES (5, 5, '426089913064020811.057708378200224487694731586862745370027417544052374884336177893807736467646454486029424673621605232432043672119510371547153895504456723242262639262542904151307250842477327375961936454637964429999741717244285121019840463692418987118402683746281993192269229200465080358289645050337976214115902915692028162689089167194843185708212911364017271332623359100711545479273675423617018342297822477514128997410642005300368966199980354369928371655155437291469427189561877718971914040675572136507472590254222870537216617260612835805368361975725573009455402822669103118872235140158440342063571894152305875004532651814592458133460160514384171804043127771746596286988679698684698755896736275307574630777027620558428909546664763675431701332632828281070572045822129984625797185173815273651376003614106277727279230096226977335510'); INSERT INTO num_exp_div VALUES (5, 5, '1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); INSERT INTO num_exp_add VALUES (5, 6, '-652755630.38762364608541718463145771120672223443489913059334543712856431450577465795351472116052777583325262472505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (5, 6, '-652755630.48149779047555948642041898982227467525089211162244620449564375525368726526946672639857607193613449752505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (5, 6, '-30638438.151446159804025029882398388155309149089870990062944469684482366692824338098201222171115395923414887930224163525189097571163687285244255335505387733673499447610577050114902372990462064696637481657064525319516004273769831260452832960893174173254560250804003884280384718123289136453955482855362019158401218620018346500189769819687260476334734259702665316562988639223597110627626759216850014150105605927773639897638043177685498804811787888811168524202700283461266793154726325540776914500415140842975457394524215869103737379109516024460317825645645301237375972914247141703084877141866316168268901439172491577729880760950895760711857112463508064820414904611059588717092145484656103798852859978690742216940980929562068'); INSERT INTO num_exp_div VALUES (5, 6, '-13907037655.047994416383638650569341223199042786813441967582376077478024677494832069402897226848055043557486983268019376307288565911231748501636517992289743940159005664424461285010295150828744259113760652210086696250085454819340987566229400805422509198052317518991183515696724846560872057916862620762789778660622787735923967096950195583369113574365386627110408307941105082873469072519133330718161987781080307947247163619814890462416622144825161521790673339279047700672881113718394727610096366361422482794458375587355933614201638489194194834709433413694420512869179976485096875057742460003147602405353823942488343056906912173170809084207937229591627643451380735179767199816663168139837088183577975769442341678933576388936845704303859241320794255052627716474860113993958556604381707826493168941926878481079724185426298004604'); INSERT INTO num_exp_add VALUES (5, 7, '-818934540724601372.43456071828048833552593835051449845484289562110789582081210403487973096161149072377955192388469356112505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (5, 7, '818934539419090111.56543928171951166447406164948550154515710437889210417918789596512026903838850927622044807611530643887494456379304996563468607210970486619898336249374975146736655090644822719838495585664994425117350974491367099004404995846913641329458537237789584653041949090121498951516476399288513593944575192159570458664608461677113504914551578443229008454218964701550932948083369656042643364608405637360180021322967144409944099438498649645368196191999692949583952927486593144959284443545794934667002661774373364219852712996869245745722896071593910890197478196462961042627387162830776094709087748993678069776845437889735782063'); INSERT INTO num_exp_mul VALUES (5, 7, '534564131989234694540350103.27821462973515555648644772098605028371173048154132108733819196629002548296868548691993248746628993380136454426833349407578676005545111508293942736555269938962058196496152360848131645787941032968937794930046928523006455386861100809286408671908320322523368135203881520526880998279355848280412933152306299256343179622513731096363088094541514890135766460631462465021694553063366717467560655272004461368865264059368514271105464855575429914212085797297268595943955105608543373940035636033207568676745293499106348500559628723682588033431457023964317090780615020801564861497990103549650624438425421690193862533733474254'); INSERT INTO num_exp_div VALUES (5, 7, '.000000000797079129642393611556079160915147221153735075943759104977169600937534508973732991117540626046659124172765761873705978811124901421049332579161931652390647472911517923131800238903184679028518657818755558526885018755394697157094867449047655737107085020874974955627907737126958129710597811740696534189608639914753884882702680512272194316887744972931453458445314561564591875764930680945589486999586667912816485821717403892703364322658245615895415781719033810595358092343690359557942948213374234065052300866661453767599465059289920067095083062096458980564265691295895672503728815182981118876144075942348853666085714846210822847053889733510154276933759200630639642310562242207518883342516103725757482864105340008709446643820864294556778969997115586027866760708448174502158738150605938364482719960251612464993'); INSERT INTO num_exp_add VALUES (5, 8, '7844230593.20607652525116672615394735666141304947992676684520382624714879797087461877675155217754947572297228288498221620714146356962938009770486619898336249374975146736655090644822719838495585664994425117350974491367099004404995846913641329458537237789584653041949090121498951516476399288513593944575192159570458664608461677113504914551578443229008454218964701550932948083369656042643364608405637360180021322967144409944099438498649645368196191999692949583952927486593144959284443545794934667002661774373364219852712996869245745722896071593910890197478196462961042627387162830776094709087748993678069776845437889735782063'); INSERT INTO num_exp_sub VALUES (5, 8, '-9149741854.07519796181214339720582405769040995916571800906099546787135686773033654199973299973665332349235940513509308862104153230025723587829513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (5, 8, '-5546455599206321494.0676583421119904300307105296377723816472192007866147764761501865875232824814135783697976183493106885436876081315217834621720906478074798596116645640251460842350553806256223963023430631066024389364515688765194373161385579258482225808660340732705687558150699172147896486727530192499184101617379930846663835628510376484675411350654979679181852179924386290069790336316958202582966248703889464308649631486542724072047294216362186036638115240070658004553260251510288423749333873893917690832829128021808383128393431810674177390352413548658782609064839524756041501835115152819802758773711821322162752064589750295542985780512921839490040396053737870038534216948323935020460307350020911362024271167085905714873548388570602799432705061561572854498075600'); INSERT INTO num_exp_div VALUES (5, 8, '-.076822018213756690975099471985461347542955923191183223634407380481978143225129486622351714276452369661632980197282261508936298649901018470846144321441236073683990324039849865750139470288565622579952182053792815638469841531577235191276257498209844422440366423136595067535337374223115507557306455001792362506235886189722508617024948653046102060677266555476719102193278190540414934812073355995577639986512222998268934000209944414236509139290657402937840986061987219441410741189615344050459067454369371094189930607834375561948483494321255500497786795636801854613881105643003358210407867114145806225724880370339074242480071595684502491827709175732777776915682786771730423733673667248186336046898260378049328204094804755195626798951644386924178161926128482002518979482630732440619051262620098544265763306253807191182'); INSERT INTO num_exp_add VALUES (5, 9, '-597892150.08771044822540810796370552966707032464017958269847934730769542644402913723848026909285133109089452632480800168074607090893991283808726990171062867538012237270000932798704781608969096508450960185964292594677356241956277714380500188870696516251767979457838109804726539408115452577436052503866633026489282425086547752714324273565900641436632912781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (5, 9, '-707619110.78141098833556856308817117136192658504561165951731229431651264331543278598450117846625251667849259592530287073315399782168794294250299770032264633712037469256688885911649778714039732161560189579333758422588445749233730591792217152212229008169062714458263709952275557558931748845536759606982982654369800245696528893058665897330942472105350178781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (5, 9, '-35812445701642379.972368737320206275515144213236752803936806738624588812089615098329765811617509505790110909629109400553415312470540217508070421816878544125783329593128638405659896184248784794258084116406472768709113030915308410565617764394827427154923321461158387012978726512246146545834669665093228316853342805604075936530371665576147966721599968786161939347726656168798065647411457701453987215491345496003650288850096338695703984042549594979897253521041581573388369367579323607093487743440894765114619634001789457486407909224339065748496715380572175183589195611952939575073075140094901024063428239223964510824958346570603142906309198033196987949067156046076497974760641964978711558209708743776024313916111738542765749928287600981397080809041007714387564206594515733287925008053261840295560398311905155157989225181164097547541'); INSERT INTO num_exp_div VALUES (5, 9, '-11.897816658873986795664687519069203701902563457968097729876034796143085813450454323128600602495745166997629078984618283588337379184733369491549230343315369634754204412939757136108898254582353378508832611703989221079986765793923635928759179573599208612516427628403686659479459867527627014558600521732194240404211484706621458983727740143568799713006127585168144158660566534382037451913967363675002134687952374080694449905223371627606557311710348820900963340884001770733452314715448053233208783321215998063958966729954113843581448912079950334969908657535514847005768455377990262943747367245613296497099716892292154137652893990339292671106003657659470243633112063075297194691349631518467702876183897580432003030164590920118726657290102377710611324297862045849839571689192181090062958059281673245670440852080202548743'); INSERT INTO num_exp_add VALUES (6, 0, '.0469370721950711508944806393077762204079964905145503836835397203739563036579760026190241480514409364'); INSERT INTO num_exp_sub VALUES (6, 0, '.0469370721950711508944806393077762204079964905145503836835397203739563036579760026190241480514409364'); INSERT INTO num_exp_mul VALUES (6, 0, '0'); INSERT INTO num_exp_div VALUES (6, 0, 'NaN'); INSERT INTO num_exp_add VALUES (6, 1, '85243.44233732197133191329295927531563604777955507322414928382967007765263923984471408038635831036097817458527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_sub VALUES (6, 1, '-85243.34846317758118961150399799670008360696356209219504851646259063690472663252876207514831001425809630178527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (6, 1, '4001.075404054519813215296429095020391062109905613738157927030437221793757373268325953178030040276107574363822832168160758728653712686313134828282109532831190239521843808940611025488601517574653932032236616573457735900045655665690517797280666732780030171712864961531623060353548802466577910774711998056232872212688464691036260746751992072745518373073825852119460094113694393273456369345499434994672730920070410547163082189385645712866100999708173472360864669110044660667614583576570496399103026286828660558854973376227247132815728164629722965145778698957093136175449225024685874279280018547740'); INSERT INTO num_exp_div VALUES (6, 1, '.000000550624150700285432940805295709861455424264970126953321538967550091614148982212874391026630805836518138806917934859138493583812313778188030836027246840794439412443826640206464415527687555214009725107630387889854278497875708390050387195108441635824296563108288712340902423706104029452615686971019125750530034798026103476074158922893374911891438688457439945897348811702908216883650280617098402133628688982793791562476980709924382381505517834196446365877784931355599480881104446907801805570471686295270927836995181422963320376948188855989986414581755633425437161760674162177776773597848142496583128607548351599750592863590334617838124741567654525843413232313914310487355539260264225486180000012813397807525203822863232682089295055713257835007742845010741137213301116647610033909062369843750685396196342928455'); INSERT INTO num_exp_add VALUES (6, 2, '-994877526002806872754342148749240.99659316232359475297606895243958507460511031229368344962653674268847910587702140353344168594152240599109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_sub VALUES (6, 2, '994877526002806872754342148749241.09046730671373705476503023105513751542110329332278421699361618343639171319297340877148998204440427879109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_mul VALUES (6, 2, '-46696638263247522384986521136500.479312417066793299922708112595886608370451213741279484136907754744903470430131032928908162742687359367826808123516519335458861613010646992354378739165872253762686683966945711430182491860196341344982195078000259063231136011430995647812149294224699587849791008794261026932467933475782780'); INSERT INTO num_exp_div VALUES (6, 2, '-.000000000000000000000000000000000047178744084866106587600962473825168237820701199970144691815329658682341685812472535816245052671243808078367856957579485152424914481414614360809698177236664771558713606961423658442962083541733004775309314926918118528217478256885324362912426275407382550929085958089798861918760121727491366034496581249711153289495601712583077918760003840368008056353090552282274780428335438032908213783490070198414584291402513547386013689752310173492320159738977752795528725029134841933604057954874523842273790958618375118974623107241366036640538085329921129023905888674299774726871808862832797230915933851225308164365269753526489223540580759951230801125605963901491073619448437890841032149898629231552019804656219062534881074125995130202820302133432951999011667568746004715268323913437054078537'); INSERT INTO num_exp_add VALUES (6, 3, '-60302029489319384367663884408085757480.1853341682137571584926062805631087054017160819890685789064777236456590745415460695320768374693076860837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (6, 3, '60302029489319384367663884408085757480.2792083126038994602815675591786611462177090630181693462735571643935716818574980747701251335721895588837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (6, 3, '-2830400711649493468815157129316992649.40542786074520931471973065281957756940496588853021620372179463538053123396140685749478530925306163968207226329985017644835203709485594362663495728106061878665324856417118064730721101615473194292620972173690618491026470353143141125614124440035267592258385099934706896692953497971326605145704135723011753705907329979207428661473172503098296622281647255008204864404416199384701720347319806375450632245634238172654086373193251877533131784268854289406126119630708578053354762596511353053106459297339360827562281168219966099848212'); INSERT INTO num_exp_div VALUES (6, 3, '-.000000000000000000000000000000000000000778366376597400971124059102619954214055884926284646546105035591052258074563706355894551049631537984053410850060739107742208523938741961208742831871056600773325053133977559789796700130019975964192371715826863472981072974742704091801166438465082519558956925444635729210849210496466189037623555622901738570979273502405907969114110345815802999687171113749364073269902319653450479463404003706147915064100959774312307195946966281098140229199529866429134937742584938255441169541436021827079647129394362379406256722903991353136733939395366152312959281905058592776286736536360235356737359904478313225848562436632109470589310799000750518904145312512621838935796912993778920622238202744037977772169066929474233952081158212174549695244127987299282384885288897893503991509410567351494'); INSERT INTO num_exp_add VALUES (6, 4, '5329378275943663322215245.29625473207137544719284446115519970394719946335145777492574745992986971075733570324679065009803281404581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (6, 4, '-5329378275943663322215245.20238058768123314540388318253964726313120648232235700755866801918195710344138369800874235399515094124581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (6, 4, '250145412892811547138949.592621291590152419206270097656346630226508074074623894951308487425470437268130465956063593951784820669318897182831355375451719125809800516979013437732298382708070979871283132689492336823087794373113039154669229889503700598930220858275174342776478898670277868700384853696009897221747924643343353942154528501454689084608965009561564638167714973711022212547096732831847202912862290958304510651828842182545311077713664465815992616213663619529378061133917572474298028065850515876361609671565914027186063801852554353160801534696062207299890867876199323530337336273950892723090754719547285920090419070001019943385293110663922226230169381423410428577990604776655422105400452217085311617728003688836185608912367677734364834577573255789160419371322775733777518997638403409000055707558465286469808848200141192627396502735'); INSERT INTO num_exp_div VALUES (6, 4, '.000000000000000000000000008807232244507937251856465017967626593430084223212999583902527587737263981869382895220711835510154989851222501080395520249593128253795609198666884523792646863341248402687314509176781281863891589925961900674092953408613128961234166906173266411035009516545964362406728942021813644419154548354247112601793685146960840364604115937119024575638240439041250900118977183124605578660115160551830946251713350556181960983267689939549506518185340972020820080460565392359379680036788592213479105831301723237102710863182596413567756605711230290883888612188805367801369264231165178487334557824054205160222371548005742602736713668548450400926514169967213301919971189065307721110805424950794015852531342286935114651278691214233054575660712537044810163930633456573860895791198853393107188289695511873068'); INSERT INTO num_exp_add VALUES (6, 5, '-652755630.38762364608541718463145771120672223443489913059334543712856431450577465795351472116052777583325262472505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (6, 5, '652755630.48149779047555948642041898982227467525089211162244620449564375525368726526946672639857607193613449752505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (6, 5, '-30638438.151446159804025029882398388155309149089870990062944469684482366692824338098201222171115395923414887930224163525189097571163687285244255335505387733673499447610577050114902372990462064696637481657064525319516004273769831260452832960893174173254560250804003884280384718123289136453955482855362019158401218620018346500189769819687260476334734259702665316562988639223597110627626759216850014150105605927773639897638043177685498804811787888811168524202700283461266793154726325540776914500415140842975457394524215869103737379109516024460317825645645301237375972914247141703084877141866316168268901439172491577729880760950895760711857112463508064820414904611059588717092145484656103798852859978690742216940980929562068'); INSERT INTO num_exp_div VALUES (6, 5, '-.000000000071906039575366987930696117572143566208825430801491864851999044659045681114433294052065377679745375399878664822361548237094424148992770296383642432040129230180142339557437679166815114510467763288057917694948929009212876391059413439647163295629904270262780935228234994930653489111444964446097124407804311494588517082748514970905563707392765567625639455978464081409330528324962333492925267647686759704415549221137291475247571296491073010175087298752769122449499990102435819414671847617062560524758344361194566796343756743243766853291113852464023843527189221162680613675369708907935197867458588904367993736363321133720345058432019986643353417257503619558797249295232894674255060861358071309619524800424087896023710729815248847792174290644245138831518072176198607255346603270853333176255533974364728342822'); INSERT INTO num_exp_add VALUES (6, 6, '.0938741443901423017889612786155524408159929810291007673670794407479126073159520052380482961028818728'); INSERT INTO num_exp_sub VALUES (6, 6, '0'); INSERT INTO num_exp_mul VALUES (6, 6, '.00220308874624532134736695825088747995945783791378828770826401323533973395137378460250799184832278118133622563295093909508983301127615815865216895482784469538070133388154961402881325731054433770884496'); INSERT INTO num_exp_div VALUES (6, 6, '1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); INSERT INTO num_exp_add VALUES (6, 7, '-818934540071845741.9530629278049288491055193606922237795920035094854496163164602796260436963420239973809758519485590636'); INSERT INTO num_exp_sub VALUES (6, 7, '818934540071845742.0469370721950711508944806393077762204079964905145503836835397203739563036579760026190241480514409364'); INSERT INTO num_exp_mul VALUES (6, 7, '-38438389630389612.0042045464692275627184627672063157323631169405883031379129843031477339360597564128205768842448328088'); INSERT INTO num_exp_div VALUES (6, 7, '-.000000000000000000057314803440765029050667129936880528769333499793237773980613524885506515999851858649385968476426313207429914995755091541422893944525222307473169425244462149015717526718376299808423552027796204632286454853167559026787019718806449038446612978917236245943248168920696452018925986743620392955122431521581268518101342690974749463089739042586011924590503136498488946387508310209984849243014542648765897536338824721211252335866349509669538308454367849024503312249951727948786393404944555844863805495937835281927012430439403132382055464307180153473189842433614777883826783689904293115204700185380661601223693428304020047393499702811581067120117405280772944184877279069842269329959037186324135435468322336398566440055479142909170224780318371473684868152271947368867666706912563225912012901437076773416'); INSERT INTO num_exp_add VALUES (6, 8, '8496986223.68757431572672621257436634648368772473081887846765003074279255322456188404621827857612554765910678041003765241409149793494330798800'); INSERT INTO num_exp_sub VALUES (6, 8, '-8496986223.59370017133658391078540506786813528391482589743854926337571311247664927673026627333807725155622490761003765241409149793494330798800'); INSERT INTO num_exp_mul VALUES (6, 8, '398823655.819545574205652791249227663407026876411660299394659390409794761643751582473390322547798567169668246138880832642141417531427935520467563318363116897177899262525720710134129529640376020947774470933902793259531840625444267816319963200'); INSERT INTO num_exp_div VALUES (6, 8, '.000000000005523967081937952184172713994498918048454262874017009201501812494019618863622631634736130436187167745347383745890248619882896153083428308074678908731005176810208100004498415662458272149380846809398637385270265351808328466537502823071145089961996689711299405627596294988646826454676198092260759424935699382655736524042353938814268760468122584678267125994645166955751211397353140569987758938572953312303398024147927938612934833827734142292697389251052485981023756760420972614486278837214553818521196182883489483756785207650821722660455451660719560529693418375773124813290305501923899840247103166971466167032437598057958226806335324315214908788839919408525748236713611579486768218564733151121028172253396652755590051310396973181595992981076269789287489208817712754098019817792758730835341151711523474207'); INSERT INTO num_exp_add VALUES (6, 9, '54863480.39378734225015137845671346015520435061071252892396685718794832880965812803098645730572474084523997120024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_sub VALUES (6, 9, '-54863480.29991319786000907666775218153965190979471954789486608982086888806174552071503445206767644474235809840024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_mul VALUES (6, 9, '2575131.137912978352131546639620215541477987701194164886305951830806120142596646541302305984776928560906754259789485960991272272782091464270104432109904222200473616116525297615725803495463468272171161659654385929185160689572943852767523792651123455283534072794326647404332228203001469884016996499768656263775233430922446983838511590562929268821678518640501686017030536100955531423152839988008496919169395159653034847677470665418765966542111749439412'); INSERT INTO num_exp_div VALUES (6, 9, '.000000000855524875533453524582534418967571681572635027972658867593464437484123442242521660317156546196609749230372398872487667521984251509483676665788527375343148382604836976332389890799079878151841905152004537926201190193814594954194044560537664560344224646197027029681984683465852110060077865421064400958821808374370779297676624123638191407441015008434084079839721156870032377372497814037418047056438760664237367081226979226606227037631073946209105678283624370820396871058367779887709720661001099338250009251834581804647326512873792849059661525874160414378459696930831877643599421297749483849526695657467708603491876916749718079725746259119898269814551222336219537198318796277931946529242436502235147453584237994498566122973953203597470078105606906752099294162422474758048436539653041606499637623370030079916'); INSERT INTO num_exp_add VALUES (7, 0, '-818934540071845742'); INSERT INTO num_exp_sub VALUES (7, 0, '-818934540071845742'); INSERT INTO num_exp_mul VALUES (7, 0, '0'); INSERT INTO num_exp_div VALUES (7, 0, 'NaN'); INSERT INTO num_exp_add VALUES (7, 1, '-818934540071760498.60459975022373923760152136399214017262844141729040109985386964272131706381326192223266583769046276181472898406504104649192224392653722107164485675679551050629376558940966195135841284978096687306110481009743118940565957556492470398904849289222365256698601073536111216152709126800604695001949246634784573028721762079936564434050796321975774729383704426321489070979168993853338252728216162346796960170352897972568238870481118474064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_sub VALUES (7, 1, '-818934540071930985.39540024977626076239847863600785982737155858270959890014613035727868293618673807776733416230953723818527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (7, 1, '-69808760806266041400340.70700818693892852138813934414383886494691670042143650609934777814995087699409404201920249076407981012095999320858479644760715204999741683528746097757549835956359129287002171391961763797857794730120426599135099619822532290339000466211195776337667123320942107370731349851576864242697412616810236323676004067839744992733887503405311090677026008324895177587064547630828026123718296429295638934384446325302964896473296829265805737112709269803814942537657996725913938408781715328945194948010970'); INSERT INTO num_exp_div VALUES (7, 1, '-9607014551997.140858001442365669993007297071681832468350855627077185145567261170534005832165603932891201648027598773639089125980996652005412450490063683624648655909636499261774535015914730479401090227915382926027949990128880284298688443593909017437720828163877690126019616194376778317148693270900349151496295698078575648169637635898560612738481294674167553369445426793073304518646116539082953755973571046622684332425840412198776081251646424875405772676893185726872613804612566569794177506268399878105117763696990094108960076591684779180089885283939385808214239337829666227427148603057941899878123459708920227867371285837642561064461118016739395972994827327543594846953341750907541716807985738518071480209106185726125017342997283356926976052909493074301401955202616191210810331245427141945840542129607439703255628683506772979'); INSERT INTO num_exp_add VALUES (7, 2, '-994877526002807691688882220594983.04353023451866590387054959174736129501310680280823383331007646306243540953499740615246583399296334239109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_sub VALUES (7, 2, '994877526002806053819802076903499.04353023451866590387054959174736129501310680280823383331007646306243540953499740615246583399296334239109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_mul VALUES (7, 2, '814739569184924399102711674444306584731316176345067.39834031417849342571224916231092924046722938910652929295271097903377854123984307101079073134405782275535446337229706620713104545454319555885847481531722101704765783025789147453570970090'); INSERT INTO num_exp_div VALUES (7, 2, '.000000000000000823151110229758332661330617426417726331211894330147399760458555778324097596176117291103184653828305857999638466183347321835058943563347767579219763002258622507889760416640758842509635599414768344140175277742935564567127659688612699366182158030839083982896107176174766408199870924563237827899202849733606842856491701660599599211106794572237923985121475458446997860253437578966578617985764298513928307852082168209458400544457824307270777530312648199364084272310536024283945598340590403612752287693234647719354745060851129534452514828239800716088248915975054881011343555492596002595181046121935660176097475159074973635534016835214952415720717896518544064238656360099884889450237541254761746029507300068198731306211736696956568648033834554273602524147075895460874922913883751452403825099444642503437'); INSERT INTO num_exp_add VALUES (7, 3, '-60302029489319384368482818948157603222.2322712404088283093870869198708849258097125725036189625900174440196153781995220721511009855207486224837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (7, 3, '60302029489319384366844949868013911738.2322712404088283093870869198708849258097125725036189625900174440196153781995220721511009855207486224837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (7, 3, '49383414785234649002982046297226894664526726187218771083.0993243619030008310875293647868815940421844461627295157812843657782639833900543200310573708100000958929315945039020410482966753145208427035917753919085618457760620513481628641658765820294863970581642745379331727722585319163262763708386199720411053619449096019862596221607526610103408936214184850115071874430846697061554769773328338028749631552202705583855831155461651414320570061181212214810086436100771547030013079997847086'); INSERT INTO num_exp_div VALUES (7, 3, '.000000000000000000013580546907080371873577430837141172674171921610919544849037647398734065712983603204704663262116138799357430947986241590690589753181299773842880079777640016786921825609617596862828930939366173224366864448436461306602680780407912534492687474933386043505172346330210659476505435994582446405414027199938970759003336829722057241708213838318628292667946636226143164221380503228191376939596663443230082698085439531600756771639601022064620204571458766303985028143400866776954225590745596639602613498355332049777798367675438365442468743270334407716567057368347458892075084694158566383133325959042076573734408841629149903649365079563374278550978052491499304166424686842598833319515705663176855033865872333988551611996194856472662292344160194821687681312501127516922809221030420253714666026321243515830'); INSERT INTO num_exp_add VALUES (7, 4, '5329377457009123250369503.24931765987630429629836382184742348353920297283690739124220773955591340709935970062776650204659187764581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (7, 4, '-5329379094878203394060987.24931765987630429629836382184742348353920297283690739124220773955591340709935970062776650204659187764581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (7, 4, '-4364411947278810125327066890819882483326918.05664098958260550284395870948992407314161088028674246708928421994893923699743452802989464864039994566042797942433140378990308345483670828497915478397481687305406460330009319949623844175096007381662809083363069100235985794575399268709260901964834244796150883807308976949196661411035264619638771824190014274817662519438658481432363824187693821267613212631153175155634316128036152465184903927860719447693468054624663668062006049759837326188252927823612718163916100588143128358998656306593393889422386501730237442526450419990376323903182669190482615734972147533221144682538647497701130447816148459762464395194383090936159579764712919396391813914821973715879062992249315474841639591907249142779103650773383644785606333916967894'); INSERT INTO num_exp_div VALUES (7, 4, '-.000000153664179510102140733858340480800294287837601105047285453457000254577644933901525444082336054243749405512900867540483190494113677173628646221933766421338612376123824684592850465460156248403574333545090544920568230979754949827013129083778435107488003838746926270955224758508832133483591156567868631938590248213604979638895901933775098150684618378235712437137852195098700137765601802898366867034641606131280434771339920637353140131159441790904703083143627590062236537714415872864218260252838432414759890832271190606933534662897006726154587341385852258168335058931957995901987808602365467861573344491265289043037273815504867254228957776127752540924854546837197432384563153608878864912196453587628891285275067452280357349897203095502806923463147414086919014592380804424300739713935051357374227246098303140106'); INSERT INTO num_exp_add VALUES (7, 5, '-818934540724601372.43456071828048833552593835051449845484289562110789582081210403487973096161149072377955192388469356112505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (7, 5, '-818934539419090111.56543928171951166447406164948550154515710437889210417918789596512026903838850927622044807611530643887494456379304996563468607210970486619898336249374975146736655090644822719838495585664994425117350974491367099004404995846913641329458537237789584653041949090121498951516476399288513593944575192159570458664608461677113504914551578443229008454218964701550932948083369656042643364608405637360180021322967144409944099438498649645368196191999692949583952927486593144959284443545794934667002661774373364219852712996869245745722896071593910890197478196462961042627387162830776094709087748993678069776845437889735782063'); INSERT INTO num_exp_mul VALUES (7, 5, '534564131989234694540350103.27821462973515555648644772098605028371173048154132108733819196629002548296868548691993248746628993380136454426833349407578676005545111508293942736555269938962058196496152360848131645787941032968937794930046928523006455386861100809286408671908320322523368135203881520526880998279355848280412933152306299256343179622513731096363088094541514890135766460631462465021694553063366717467560655272004461368865264059368514271105464855575429914212085797297268595943955105608543373940035636033207568676745293499106348500559628723682588033431457023964317090780615020801564861497990103549650624438425421690193862533733474254'); INSERT INTO num_exp_div VALUES (7, 5, '1254580584.048971438599349046867230181719371038956756285986415773300837165755558702217197735811549684202279755101552533605390208155708695952004683670878589028717509749282693444655857296902117478518511492735290086040573521482737598395369632843374456793385511847676556826348943588519880411018079886373631771830925920986588708409208527042927229627786932908015502292313887561198156623702404977221789649731458241770690830680067801377815840764873662400590343236662968218256211697981048576328148435241545372543075051594952109757428031762469834781538302930957095080167901199455226976113347018972534334210416375400979738414416582588689496706548495076287263281908191770792203069614447622517839588243746755480572371988630084226963919158931419126724681617069720048557166545204944250492282054791996953359013543036918134163144772567093'); INSERT INTO num_exp_add VALUES (7, 6, '-818934540071845741.9530629278049288491055193606922237795920035094854496163164602796260436963420239973809758519485590636'); INSERT INTO num_exp_sub VALUES (7, 6, '-818934540071845742.0469370721950711508944806393077762204079964905145503836835397203739563036579760026190241480514409364'); INSERT INTO num_exp_mul VALUES (7, 6, '-38438389630389612.0042045464692275627184627672063157323631169405883031379129843031477339360597564128205768842448328088'); INSERT INTO num_exp_div VALUES (7, 6, '-17447499423661151023.558342555162228919125358089491573318627107322332520978657843895009110781773496490472817700487707134216424855867015781267287628022535529641238372370292374146871103236048507252055787621394728096799222976387108688980537900309311204203302960751747509648304056939321473462375648710590981564101023812800603438271190184064874290215309040519813024962909469701968804925443161094255632624090623433640078421818321246597728308302979223833487133268472455479442002005374793705431817866798804822885690193667521606781156962792120052947767160957903073698536973292205899421787948529970837601521657406211962967291912148632072929662185840265855612193255596825032457033402506154930851214421895488796227471490998190312007513478459049382774782886773158311656817014322925167278223360446454868236479549745612973293185989975394307678926'); INSERT INTO num_exp_add VALUES (7, 7, '-1637869080143691484'); INSERT INTO num_exp_sub VALUES (7, 7, '0'); INSERT INTO num_exp_mul VALUES (7, 7, '670653780922685519356619170643530564'); INSERT INTO num_exp_div VALUES (7, 7, '1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); INSERT INTO num_exp_add VALUES (7, 8, '-818934531574859518.35936275646834493832011429282408849567717761204690035294074716714939441961175772404289860039233415598996234758590850206505669201200'); INSERT INTO num_exp_sub VALUES (7, 8, '-818934548568831965.64063724353165506167988570717591150432282238795309964705925283285060558038824227595710139960766584401003765241409149793494330798800'); INSERT INTO num_exp_mul VALUES (7, 8, '-6958475505053954666339703437.48985528725312694198056665033448258303533387675711770743843194274181580881296671866212320171337132096489224277825857521033238709600'); INSERT INTO num_exp_div VALUES (7, 8, '-96379412.478435590945480884955616049873645089637121682284625533034225619945532704111492738646389632607594293500930307222576571876059094206480673293295865214240456906965855425738072430281475736130342229749511650392658808510082775031098547507966544723255869156056349218776847523349173551313282283869146710349521487706884633419341568648959204688757523312579312713453540395840470692533267158388401676533369105590789036132185107859069994833345453200014884023709597817280132465224778002071890368479648934317322270613208789859930618055792958996389145963056607200020526949699302565905917600478429628844015684879886549766473809801710003649193772354147104446894109928903223843036925147624639466770660174828940577089095480826473544099693433597812637069287644606693066736302793687011165899362920686114156254982709172925265118077531'); INSERT INTO num_exp_add VALUES (7, 9, '-818934540016982261.65314972994491977243776717915257186979728396159058352649559139156429817562698954531329940720620096519975256547379603654362598494779213610069399116912987384006656023443527501447464682173445385303315267086044455246361273561294141518329233754041352632499787199926225490924591851865949646448441825186059741089695009429827829188117479084665641367'); INSERT INTO num_exp_sub VALUES (7, 9, '-818934540126709222.34685027005508022756223282084742813020271603840941647350440860843570182437301045468670059279379903480024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_mul VALUES (7, 9, '-44929599044588573810654775.83678007633232843418115790847152455559258007804727916986432256198687661496804050903769496933400455947645400628259699874770581538122521805603947464462448454681701547899144129061961394870320463199545502030106801911915987309444301341575451240764927967432593181449618816978119423290767783843864768557371257918447461479570164065303599994081990686'); INSERT INTO num_exp_div VALUES (7, 9, '-14926769772.797708334489652004325241753714626257641081061212878627972973992233480868793527325656854681817156284203427388055525855608883067129036717726368707982450450575794623567027457808927082390474261155500697096284790656757163047499531247323702909360444831707029353441147768321257650234732286165724178549576948957405037843360446785505536809409054071975214796532504678683693402401018726571884721963641317944453797513145055081061680091585467186975354801535734149952115333241283186621720677488342266420359417174224757781125498130120775969091933838082305123652811689513300403051544682523761263183781206840940347226802620226164265210810994106136738030959199259066517106713585343004140573604437146025585149934286364795122716971496775012412420105368351774715982565252533025207453326002101655121126631180162560463548157187175671'); INSERT INTO num_exp_add VALUES (8, 0, '8496986223.64063724353165506167988570717591150432282238795309964705925283285060558038824227595710139960766584401003765241409149793494330798800'); INSERT INTO num_exp_sub VALUES (8, 0, '8496986223.64063724353165506167988570717591150432282238795309964705925283285060558038824227595710139960766584401003765241409149793494330798800'); INSERT INTO num_exp_mul VALUES (8, 0, '0'); INSERT INTO num_exp_div VALUES (8, 0, 'NaN'); INSERT INTO num_exp_add VALUES (8, 1, '8497071467.03603749330791582407836434318377133169438097066269854720538319012928851657498035372443556191720308219530866834905045144302106406146277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_sub VALUES (8, 1, '8496900980.24523699375539429928140707116805167695126380524350074691312247557192264420150419818976723729812860582476663647913254442686555191453722107164485675679551050629376558940966195135841284978096687306110481009743118940565957556492470398904849289222365256698601073536111216152709126800604695001949246634784573028721762079936564434050796321975774729383704426321489070979168993853338252728216162346796960170352897972568238870481118474064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_mul VALUES (8, 1, '724311956372274.0135050255361637906710330203036651743488213007179039756514944640108625580172737414192938789413338554327986697518463087452612658955180411327002900979574347739956600177846996063741787205122007268468674386396156638261992679442768654367111433834151087792255469957061758837789341439211010331332174981459471333376067541234901538285101103690622656631026001337239036711179989456674399137008584021283568040818388709554256523118702728176420022080138548890713013682480239784198421500241995499841675772793497485550923152267616622892846304530712344886979674416990935007952941652591352603797627920865960622077762568060903908151958000'); INSERT INTO num_exp_div VALUES (8, 1, '99679.115123747637190903598543851248555278745675862923884476564848911494649941770503156134872464666625927195645517181131678518619856156844072856993813601495176097972982587061507650426363887871820112714099226501603733968262566093655417466145183587899155614471697804006772915054739361437054029183182533671508695646413074668188590846200362324428338974890534273352188276373478524543505805545661569395314989170104140776362043880099775594658817242753124957385625811310332354760117110779649164022618274859298031549851269619167173746259018497289174255201452265070501056913033329291819570027877856677145579673495987354805150868813877928857472561883332547900866904764950837506993759536410161752469488392566682723027340638271076406246129989851281210810196699482980833204884400423019400653089825859983062096326294783573417554749'); INSERT INTO num_exp_add VALUES (8, 2, '-994877526002806872754333651763017.40289299098701084219066388457144979069028441485513418625082363021182982914675513019536443438529749838106171095037135009526312783302868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_sub VALUES (8, 2, '994877526002806872754350645735464.68416747805032096555043529892327279933592919076133348036932929591304098992323968210956723360062918640113701577855434596514974380902868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_mul VALUES (8, 2, '-8453460632655529853033389979024265783461224.3195241893307807116624750282852146303290708492834695194274289713076935297734670940696121761483641291930931061232942894577813178566088927221374036301485916497770984757492912292002695944367308880163698595015497307574177176409203214324418237020500352652934909632442547242092296504047310806151851207329042221920888326000'); INSERT INTO num_exp_div VALUES (8, 2, '-.000000000000000000000008540735921314463871578184793632135730756619558669911183806487803411545406462244216408739432325839683804021466133071768612386706692296158696852363349481716813410857655324486448455846562309041306880675446880859847445987588059144788756984750993583865748280824370754934966494724951583311563735533173023858438364336214213295786266815116844775733072416507474834701984381586060478606371028156925222726225495235702395502085206072985373035972506738983640539009567237336002073370431753469632428303255926718930619221521257726366850472572830063284204851204189447233044832163423057501488364913539948261528280564870049935369825245920984413480757133585498984374354957754078525161296201228031555280486615145365039415418251448980923331334883673792135893857917681235883506783408111446970710546686739582471'); INSERT INTO num_exp_add VALUES (8, 3, '-60302029489319384367663884399588771256.5916339968771732477072012126949734214868901845505193155307646111690097978112797961939995859130827784737422228762767014427842766445950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (8, 3, '60302029489319384367663884416582743703.8729084839404833710669726270467964301325349604567186096492702768702209585877643481082023851284144664938175277044596973126708926205950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (8, 3, '-512385513828318260570283740065493064477880918352.732624553690077857674083796435724202494963885926573907185100543184828131859183999195040110586155435203949963570735841632689374488877298209082579317039061893012560130258753218955057387206477423088065663401594359617882154814262843273526859406265633827109554791772242178864873774889091687515990672487380368975556580539271333144212685871370972163560839446696514092637412587953506052848750866803569213269271165856310101244342151576488190595936869490659700946174362872797854591188391982770203203644172999264143929484089237665313698600170041324566984832357000400'); INSERT INTO num_exp_div VALUES (8, 3, '-.000000000000000000000000000140907135225782279761112255989433531718277338909398600029580768021365259747075253760824424092983497958717844671162530550507041138147836569244869107757945370200122955794509365120853536859837243314494576053441804831018954867623755033888264275704547752628348151132333655667171970175829826792355986148522268067032057293494927558322394395160508723637192234110428953945018965078022622950949911124494740703606109543716688008516750321047603009424529696862953094999450658951089435460411028678817795100630449046993274191915359520936265372754315076684798942557329584282177053819106884196674660057281227248874819417305259132106690385871316407455034281900110779740008476645291647094776093567400422266906817555937149628005629880142615126571231411138926043531449659320501743591992888328328980526602'); INSERT INTO num_exp_add VALUES (8, 4, '5329378275943671819201468.88995490340795935797824952902333498786202536079000703830146057240651898748760197658486790165425772165585380839129948178510273188565692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (8, 4, '-5329378275943654825229021.60868041634464923461847811467151197921638058488380774418295490670530782671111742467066510243892603363577850356311648591521611590965692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (8, 4, '45283653791262997781451381354094822.762732909505051438036873220502792213670540454778361182993875916509061144859281577740137081988678361247725064336120451090222456518107029158304937620179032477664627949959143233370320432203497828243297406462513350790251761540074946469824444452248386782451723637769289822576372357189700319768797708375563651655860093365309717823602754924352327588945034832436331911584742966378275504545736896430718939807674966738116698454215555860047859161126694019895490767779791933882712567492115664113775047192011252893773389940988533801360010782816196288710063568554147458866942816721046004257953642508395867837127678980002737669139369781058046396738606563716339660654364541530532834806205571191828994250708412638796240377704994928921528330863683630622922959130920715261879547446054261914770022377059156125037157979236658010950'); INSERT INTO num_exp_div VALUES (8, 4, '.000000000000001594367257057971052149628499448029056279649281098852958322409409919964709324200796473211884339143791758566019217634542932882694487712398244322522748736692741288668885362384266615527166964187404128216235057387796054457728789109537338988453837993084016408244895452291151218602815057669592284587317035387004942691671916981967449109983992675125005085762403043329820872839739877674121174083273716295673230993049263574856197011389828478636779342320299895806297835595427859271617831720398457416685435560152182883615601663820189195644140652141180949257192740185075408019971747810015931542757445763460947106918998459997631117642552273815713467150465548031203738878873114842844016176922502916339025283749846225376341878386377192605865913018132981323065698049618379727531925408677611856682983907951667054819'); INSERT INTO num_exp_add VALUES (8, 5, '7844230593.20607652525116672615394735666141304947992676684520382624714879797087461877675155217754947572297228288498221620714146356962938009770486619898336249374975146736655090644822719838495585664994425117350974491367099004404995846913641329458537237789584653041949090121498951516476399288513593944575192159570458664608461677113504914551578443229008454218964701550932948083369656042643364608405637360180021322967144409944099438498649645368196191999692949583952927486593144959284443545794934667002661774373364219852712996869245745722896071593910890197478196462961042627387162830776094709087748993678069776845437889735782063'); INSERT INTO num_exp_sub VALUES (8, 5, '9149741854.07519796181214339720582405769040995916571800906099546787135686773033654199973299973665332349235940513509308862104153230025723587829513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (8, 5, '-5546455599206321494.0676583421119904300307105296377723816472192007866147764761501865875232824814135783697976183493106885436876081315217834621720906478074798596116645640251460842350553806256223963023430631066024389364515688765194373161385579258482225808660340732705687558150699172147896486727530192499184101617379930846663835628510376484675411350654979679181852179924386290069790336316958202582966248703889464308649631486542724072047294216362186036638115240070658004553260251510288423749333873893917690832829128021808383128393431810674177390352413548658782609064839524756041501835115152819802758773711821322162752064589750295542985780512921839490040396053737870038534216948323935020460307350020911362024271167085905714873548388570602799432705061561572854498075600'); INSERT INTO num_exp_div VALUES (8, 5, '-13.017101389051085341042057308965769356145255575582875626848796382322826525772114256699384710400140437710569924703769685567402446691691210934185000959063158239023412379691360587119206695513775971704926722817528818197919265145207032750407924774510773427697188520818450702875142190949766251178733262143962213111236591970766836685919581025629742334704854852196126735685421250263035895756028805974153787560164935038227108975229771590754808331856162035119882347418116049174638416621093907738608991987582465865527947015457540650512339263071898410531735438556948115098562123055444965056347091625748703503220861221718449714020622377233272042277814766996198081939221253025243417993701684007826177845003391944496774674489538520354606358872276671998045196738090133576377830721671972381371985771591052597345572374064920279182'); INSERT INTO num_exp_add VALUES (8, 6, '8496986223.68757431572672621257436634648368772473081887846765003074279255322456188404621827857612554765910678041003765241409149793494330798800'); INSERT INTO num_exp_sub VALUES (8, 6, '8496986223.59370017133658391078540506786813528391482589743854926337571311247664927673026627333807725155622490761003765241409149793494330798800'); INSERT INTO num_exp_mul VALUES (8, 6, '398823655.819545574205652791249227663407026876411660299394659390409794761643751582473390322547798567169668246138880832642141417531427935520467563318363116897177899262525720710134129529640376020947774470933902793259531840625444267816319963200'); INSERT INTO num_exp_div VALUES (8, 6, '181029319177.110996740664566780784253502559986936959009611748146099327460471609593148344991059106574612143724330935988823134137686051475120980257829276671900076859337187540608483895641504622910361858962883971613675309676443079313179200981488761707281247447120551917205792352229666049191991270809865110506639390610910481490688182068719005593641339338678014189749279508731647492051879768743158839680867283217578754666643688259810863605002821607490100820241093473083445658378988069593782353275713240897038366242558466047071334385431080003439842348547427066389352198560236731403235927478177780757802759046212921140424771887928786549573201311120885052685761195784207710933764480136690216943336587118385525047554334029388869436622866247240903231799829259264158812528305210833683370536416861544931420820452512390255774498188962903'); INSERT INTO num_exp_add VALUES (8, 7, '-818934531574859518.35936275646834493832011429282408849567717761204690035294074716714939441961175772404289860039233415598996234758590850206505669201200'); INSERT INTO num_exp_sub VALUES (8, 7, '818934548568831965.64063724353165506167988570717591150432282238795309964705925283285060558038824227595710139960766584401003765241409149793494330798800'); INSERT INTO num_exp_mul VALUES (8, 7, '-6958475505053954666339703437.48985528725312694198056665033448258303533387675711770743843194274181580881296671866212320171337132096489224277825857521033238709600'); INSERT INTO num_exp_div VALUES (8, 7, '-.000000010375659845651632013446652385870617923988120764298690164486716047614260682259722116360931978511176121353975789418625836899338225571166376573732227571704071000348895791547943896682585450808398324252224265156214259224488248639550967292466343168350213394398101712526534464002532408445204630441167137710565437434313424987517531891145368203998329086865151248833625645567863740298397742783405267970015165358620026813812552194344790169289440822038223606218360105618852154152168496637886434061050281055613760360200323363465925493033734895631921307644481639236601187225135325401868178006133838932915485272554505684060229409404902185944047523033315868230944723282246159741659387362889777495094736963530708159604929268812778894177095572578862150793098548829744006499229853198046828954650334595737117597239208825268'); INSERT INTO num_exp_add VALUES (8, 8, '16993972447.28127448706331012335977141435182300864564477590619929411850566570121116077648455191420279921533168802007530482818299586988661597600'); INSERT INTO num_exp_sub VALUES (8, 8, '0'); INSERT INTO num_exp_mul VALUES (8, 8, '72198774884738777393.8687539247642452953425155400068591498151280875559609979248583367700231031634872342122563819478919600402159024059794279536786611373504966204744811722007869415559012475160471227957857756325962941799428857291371597146319816910515366298862558849452235442246081440000'); INSERT INTO num_exp_div VALUES (8, 8, '1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); INSERT INTO num_exp_add VALUES (8, 9, '8551849703.98748751358673528924211852802333963452553842636251612056366144128630740476125273064380199240146487881028508694029546139131732304020786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_sub VALUES (8, 9, '8442122743.29378697347657483411765288632848337412010634954368317355484422441490375601523182127040080681386680920979021788788753447856929293579213610069399116912987384006656023443527501447464682173445385303315267086044455246361273561294141518329233754041352632499787199926225490924591851865949646448441825186059741089695009429827829188117479084665641367'); INSERT INTO num_exp_mul VALUES (8, 9, '466174236688165594.9218054325256670866060556227711696100465581464881295978997280335378678072434776702952026828137140986670189756965420183565968027969700090735690246176791371115610886533930223141650377886909408268207750238603105232560663571044993507074695683027062426288270199495225881785499139012931143826099668999261931834700467395442768201666740663642498098541516326470052372008385656719236306238735524802875519713512894448940917708118676095378518264553310312628830009314653641136566040400'); INSERT INTO num_exp_div VALUES (8, 9, '154.875085756903716715488911525453064308758123952566428258639786597308109810869086867746263482721081985848551254298524280231489145092826397833394044637104667137816928932471315095067524966582810436282901424423215992139000153713476369887383242289102867530775908269805285313842050961754114751975054515055089553180717444020378611767296609130477264722612784088270193199394531972594028420402254831778715196248487757266330454269044609134602570688339750190391651801546906342796660819535014295618246236706572780627362908121159003488810140236665846928586992082180006454824311789091323774002510945263351862712964422865623934112293184149374573706760114682326698881257123280119140924775171374360283137569618025005229268057970275164869735173660958715166148344076027212231446680947914004346760896298312286730627916684448923824769'); INSERT INTO num_exp_add VALUES (9, 0, '54863480.34685027005508022756223282084742813020271603840941647350440860843570182437301045468670059279379903480024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_sub VALUES (9, 0, '54863480.34685027005508022756223282084742813020271603840941647350440860843570182437301045468670059279379903480024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_mul VALUES (9, 0, '0'); INSERT INTO num_exp_div VALUES (9, 0, 'NaN'); INSERT INTO num_exp_add VALUES (9, 1, '54948723.74225051983134098996071145685528795757427462111901537365053896571438476055974853245403475510333627298551845046116291696445177112567064282766115207407461565363967417615506303416694032848457927390574251904212425813072768882213388082765916956736282110801611726537663292922699021333445658549608928179155685881583228490235606377831724593358583903616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_sub VALUES (9, 1, '54778236.95145002027881946516375418483956830283115745569981757335827825115701888818627237691936643048426179661497641859124500994829625897874508497095086558766563666622720535497438693688376602804651302002795213923698663694204683995198328880575615535181012624198813873609885725228117274934655048553507421448724831939026752650108735245933317237310133362383704426321489070979168993853338252728216162346796960170352897972568238870481118474064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_mul VALUES (9, 1, '4676749348240.390309875431213992853550297086049749814750492488995108783145961719774217441193547534210468967573344456866203963659951312519988497979489304488948342258375915152429008993288817366720647491166024151209542534474867042837694499222928509320280684557676243780452100132238968233413333851595648146954975713386711764268506890884764704949969602122157394714663532141060559896359465918874990769222345665160127552795532197771168442486088776803398878354288847069602460071745966589164282641033852314335279121191855487126430176047553895892632834940595958394834437871886013513058514896870683979585091413977173250824451205330441299000850618134248917380244749589254309567551846327349592529960432446947239714236828401206843011440433362544797025114476612133622499094287321570559088587999417440664282418005102546343020409520421747216'); INSERT INTO num_exp_div VALUES (9, 1, '643.609749344751131516972294140174556703217311736700045690413622699888869645595256683013323517984528456698303984909359393772036036540901870537096836621035845014213031549051156299974682317824766457362427063305495772666640279328909129870227828460705733995380145417663304348663705694070309475835826101153850359826502235923289787750107778906593010060115662191620280031872002110849782776325630424918493602259707267214006217268630948545349980430128422952869610116216278256812581821942763705098526140427280008360043829906543029486315209818099697988089748683904695870401517598840185535891464842870210715421728852789815860153472208176465166954851895457846723102438114697692610933532992841803219018495137378534010155991355251803548866919409031477821173935696065078362044927492034445482457329200246282082707380974745411383781'); INSERT INTO num_exp_add VALUES (9, 2, '-994877526002806872754342093885760.69667996446358567630831677089993316481039076439881735980566785462673358516198695146576524119916430759085192883825888457383242076882081857926408611052522393579396644731758241837010163568445385303315267086044455246361273561294141518329233754041352632499787199926225490924591851865949646448441825186059741089695009429827829188117479084665641367'); INSERT INTO num_exp_sub VALUES (9, 2, '994877526002806872754342203612721.39038050457374613143278241259478942521582284121765030681448507149813723390800786083916642678676237719134679789066681148658045087323654637787610377226547625566084597844703238942080799221554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_mul VALUES (9, 2, '-54582443595378013373024060492546032003692.4875677735896411267274323339692558458420972958075073392126734000341372096298914875892612108329218081214550050039133117695428196702128258481789017059073444323729583900855712795086447886053552786449313809589992185978097430132940882612817775035217244553616977182049775786664446683332098226841743818600819221587510039430478859412452506872131851471967577741190323481953867845129745440745526578327709351120432530702446916035797432129052518980799424635406993848916727957825620638983706180841278402925286540375225365057191075559133035'); INSERT INTO num_exp_div VALUES (9, 2, '-.000000000000000000000000055145964114074763360265614481666934002579974728749248345352023099030383962250681574081874554842623852433135871821620640200582985140388676650602814646133317791813938390695683843848260103199745295436998313216878337673674660966362155480524935736646623766057029148471463569162153009963312016563281545776175277904913263614668092319707343286073000287493274965714031678784835459999763925833141049057636632430975424499618419962303087175237320046300285962065818926167792812657620724550768858763098967149546312995222223400007044549870620849992226072041407997925405957501929449911416474388622107825120486594723448780503829317691081601820425151593487431389373265285594626753418140874747955925763163132984655078996173911578832035721963554569605730262976354029623260224710106409129114204296314733036'); INSERT INTO num_exp_add VALUES (9, 3, '-60302029489319384367663884408030893999.8854209703537480818248540990234567956069965340942024890856088355839135538265116174644003927269495876835324407641642359213535695803871472434650475144516723617632059130297610134243891145006222068960999879308472500422640481972089756410157246974765071949782242392661524488959954348903412713930092273629207697480131360047867213863018127928853922173643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (9, 3, '60302029489319384367663884408140620960.5791215104639085369493197407183130560124286109130354360944260524553172025725325268378015783145476572840273098165721628341015996848028750420770651761919246816300854441592109844750954710317145008297946462099581451150385769713261452744310496166494545449824802407416426304041583975713483424241727236417259479541129474082301376239522310995725648773643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (9, 3, '-3308379209762459471107480259839508279070920437.883503980178028214343751083865562028455061662673132221930429904398963590401793045470444301883103141901787466923883803951815572606105617157736442670792467625964359169270739534412932791178258858918086886061702512427989129732248215348301444245772127142869263635282888226326427510486246184233225114523636171202034558843515894542952126988613018789833835507734620046994907453602573865012044120483116345444810078666601100257620969379968264504287700045822481492526688635364586344704730579892342786173395802035361824932075736340405960099542224953439044947229246847140957298841482874444906129049023002897135347878048572628834749795298712449864571996898774444932083319581439741625832405434317985988163261591679157437224404970927012111196724239860528859217322132733404472897289'); INSERT INTO num_exp_div VALUES (9, 3, '-.000000000000000000000000000000909811507365065002714756487495210579371808512079908127938523896001746219475805196061435010714649189975968123072269549018826343830061696154665503565341929634172463095299662727352635590451263034658630449260378893723785917860125051787451512267088404686342938118993621396641623525252649748977992770709930435013456855344203854749977414354164157192885125263071636468941596567220391082793700307461350484216679632552883058303710297475827456761138832914743429330069022439380297715971317819244718196187172770061156794130040674050533617155253444764036426045091327368023602807193742585178432544430741520636125146531502042579276206322507516332917325631822606079220413965396706334639331097621824106950192993127113903265025719013680733760540930122186345919977470628988674677630636632053583144327'); INSERT INTO num_exp_add VALUES (9, 4, '5329378275943663377078725.59616792993138452386059664269485161374191901124632386474661634799161523147237015531446709484039091244606359050341194730653343894986479159670583937529516163204904273806158788218327396375034882788180783796976731912141525319602448709213495905899041406302673881364465504945113279286939663215197485367850132991968081639290297033476859158044889351836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (9, 4, '-5329378275943663267351764.90246738982122406873613100099999535333648693442749091773779913112021158272634924594106590925279284284556872145100402039378540884544906379809382171355490931218216320693213791113256760721925653394811317969065642404864072442190731745871963413981746671302248281216916486794296983018838956112081135739969615171358100498945955409711817327376172085836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (9, 4, '292388240303165948041827159734686.255558469787242316676287235194652580157149226950109397295920730296960145548003120827363226435916209781396711693581454960342091452830648929118261388933297036933167543189308061917640517578583521401267417187854611829815212778183983326568586118831109538377828156118900313778053576483381085207892754728937946691892849474364477434665960112125254104966566712906532318984871145605839506991591027939136026602051635433295687547552796828217859648186757719639965988287173297286034098497871707197092627676226053609131138590878743560287292934815277894463305001278326023708395571840850120055316276256138004565442099731931051413153564744766098053176049414330146267604802971221161572130161432525297614616942172815141372973870720928125699420370428856022295499447755488148545048400795053604349570217878099721865670458104653570360'); INSERT INTO num_exp_div VALUES (9, 4, '.000000000000000010294536718194523982241053267404812827031741197656209184880073175960433631103885281961037127283726462743623757855378209281373475473018922090781553213750339001555832360656399849031527008437303091226051008068950896796359518673740801770866360774945096397034708173365378527676779736929035450380795854046109380272505550244458858231227568118355064007614608452292270378691774826689216790090661497154742954386244856792006376222923780801296832612827123778915598893970651480451509706836620045721191411824060983487064555397842027454385628620582036592315345973096405447742002746762099231557054678593446667904250189208490698468539396733604833688133512716508825505666644390119877423938820483653319376926639295680552194966870285838815705038244628263602997511842285889300557188773128635554621378148419364876651'); INSERT INTO num_exp_add VALUES (9, 5, '-597892150.08771044822540810796370552966707032464017958269847934730769542644402913723848026909285133109089452632480800168074607090893991283808726990171062867538012237270000932798704781608969096508450960185964292594677356241956277714380500188870696516251767979457838109804726539408115452577436052503866633026489282425086547752714324273565900641436632912781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (9, 5, '707619110.78141098833556856308817117136192658504561165951731229431651264331543278598450117846625251667849259592530287073315399782168794294250299770032264633712037469256688885911649778714039732161560189579333758422588445749233730591792217152212229008169062714458263709952275557558931748845536759606982982654369800245696528893058665897330942472105350178781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (9, 5, '-35812445701642379.972368737320206275515144213236752803936806738624588812089615098329765811617509505790110909629109400553415312470540217508070421816878544125783329593128638405659896184248784794258084116406472768709113030915308410565617764394827427154923321461158387012978726512246146545834669665093228316853342805604075936530371665576147966721599968786161939347726656168798065647411457701453987215491345496003650288850096338695703984042549594979897253521041581573388369367579323607093487743440894765114619634001789457486407909224339065748496715380572175183589195611952939575073075140094901024063428239223964510824958346570603142906309198033196987949067156046076497974760641964978711558209708743776024313916111738542765749928287600981397080809041007714387564206594515733287925008053261840295560398311905155157989225181164097547541'); INSERT INTO num_exp_div VALUES (9, 5, '-.084049034261605466896663277055600903951276881294745183935726262038673990196778002490449355450474227878560465916800470848046625257516764244432096856845087412397406701521972651300484716852035267197801389708234913163750232707469240634303111868882057393120649919262424619226282082184091177505826009374043368623853156698509808569378758387708910629731005691079770517679511879694426434724918004419953301426679939010592502325130576915399009756468717124460489039474155719834555522581553817856854607844133431854471292027873672356863673617090151801474016666978499651970627896504709551656249007718965259502928591648533670568214972768900993459927860068104745163979267716597907297073374689384723943955361288974065531322408839914599555769945298758102515352082822617428033648130099822033393662643586331479103933840387663729387'); INSERT INTO num_exp_add VALUES (9, 6, '54863480.39378734225015137845671346015520435061071252892396685718794832880965812803098645730572474084523997120024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_sub VALUES (9, 6, '54863480.29991319786000907666775218153965190979471954789486608982086888806174552071503445206767644474235809840024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_mul VALUES (9, 6, '2575131.137912978352131546639620215541477987701194164886305951830806120142596646541302305984776928560906754259789485960991272272782091464270104432109904222200473616116525297615725803495463468272171161659654385929185160689572943852767523792651123455283534072794326647404332228203001469884016996499768656263775233430922446983838511590562929268821678518640501686017030536100955531423152839988008496919169395159653034847677470665418765966542111749439412'); INSERT INTO num_exp_div VALUES (9, 6, '1168873084.346566233232746391559830634361431940000227460271861554316197556566224118756340501278103405856646766537018954185964066240457859194626558143313125824412559635129130086906976028635444060218797992547370132082916380788496584864016645155338102476357490305222392452114945853620686975383081427840791892729407194179236897452655907829255937027286698570784397487382242990326347080472574546312522326038419753951437799831430690304084087684303035538181812523230890783372773953961677974396907303758903934808035747944477277528267001070234880092255363221274303820343225415479126819937070570562654065195009839593938440374000473302075568746771126391307584779249330981594640387657042725725493800876630516005713789705652827210295338592985225924959199657729900181287069808881130884115897407246324220524401243575641227725030779990490'); INSERT INTO num_exp_add VALUES (9, 7, '-818934540016982261.65314972994491977243776717915257186979728396159058352649559139156429817562698954531329940720620096519975256547379603654362598494779213610069399116912987384006656023443527501447464682173445385303315267086044455246361273561294141518329233754041352632499787199926225490924591851865949646448441825186059741089695009429827829188117479084665641367'); INSERT INTO num_exp_sub VALUES (9, 7, '818934540126709222.34685027005508022756223282084742813020271603840941647350440860843570182437301045468670059279379903480024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_mul VALUES (9, 7, '-44929599044588573810654775.83678007633232843418115790847152455559258007804727916986432256198687661496804050903769496933400455947645400628259699874770581538122521805603947464462448454681701547899144129061961394870320463199545502030106801911915987309444301341575451240764927967432593181449618816978119423290767783843864768557371257918447461479570164065303599994081990686'); INSERT INTO num_exp_div VALUES (9, 7, '-.000000000066993731076524206362744068866774567920404984046399050881532938231826344009126898802592302273719505485084766150904380671495128604515800845609713368334606489445184535043833069145643553083555507533900955661105251251918425885537513359541698046533092111969478225528665278023069818968531644884466229545497943710817187632203193468836772459599856856811131193744272314519908999458320275710240994009061040198159739169960258978462113813370513611735006229733329565083659159456172425715216475781507996483885669437855000029758892126410922067202159414570164537031153818197618428471046051340835826664787585016361564969663413176434498159140395476980277574789931364078570781760777773379636490084338326576889857824344578398580499610233575273027387501809967324874264742269453420400624883982643066864175851881870402856698'); INSERT INTO num_exp_add VALUES (9, 8, '8551849703.98748751358673528924211852802333963452553842636251612056366144128630740476125273064380199240146487881028508694029546139131732304020786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_sub VALUES (9, 8, '-8442122743.29378697347657483411765288632848337412010634954368317355484422441490375601523182127040080681386680920979021788788753447856929293579213610069399116912987384006656023443527501447464682173445385303315267086044455246361273561294141518329233754041352632499787199926225490924591851865949646448441825186059741089695009429827829188117479084665641367'); INSERT INTO num_exp_mul VALUES (9, 8, '466174236688165594.9218054325256670866060556227711696100465581464881295978997280335378678072434776702952026828137140986670189756965420183565968027969700090735690246176791371115610886533930223141650377886909408268207750238603105232560663571044993507074695683027062426288270199495225881785499139012931143826099668999261931834700467395442768201666740663642498098541516326470052372008385656719236306238735524802875519713512894448940917708118676095378518264553310312628830009314653641136566040400'); INSERT INTO num_exp_div VALUES (9, 8, '.006456816440893715330247418029019114736889626790871612141686117271826070935285769018710680035004320626745647926106882508048159628931624522666638442625219959259156539178378186912871506893482633695438850964052285542425753626455183282159259999492971992739484319464700978750304962671213318202670228197968646486740006148091321740497272644910882302412140576608739962605210964504469426861972705740810533465451230811358870068391007718532021526225893542801514255726272411690175555142385382688220121052891017808391607717500701760375927811435030512071347521837090721052128992926357375527600337655573639413811262412492632491693179011503973930804928749370652038245414768103001067902012962988384812280453070895781287237746786414435546976395632454474312533482077585837153357017362048554313154580576238549196250793055676215164'); INSERT INTO num_exp_add VALUES (9, 9, '109726960.69370054011016045512446564169485626040543207681883294700881721687140364874602090937340118558759806960049486905240792691274803010441572779861201766174025231986687953112944997105070635653109229393369465827911089507277452877411716963341532491917294735000425600147549018150816296268100707103116349627880517820609981140344341623765041830668717266'); INSERT INTO num_exp_sub VALUES (9, 9, '0'); INSERT INTO num_exp_mul VALUES (9, 9, '3010001475769225.8286280957637941018500905354415197182850820227163907782811814730309044010416886791014702373809932926301368137684091094408663914110947072451332976891128659038142954192986392936981664792370678656287232795203974766040821110221158579481177539669363513848425151485663431478439528936592701070340012569297177488556353760756495238304538439278682066056721729656193616571456456325016960870401748115848423105783116854283646624807603476682295234280408938557209608025246638166902335016025467565869375885610813662767004038102486303756741615124814580306266901273803721191779461890468156043551004644728343579032524687612403663816107770451694666844862368101122025340182510019516924578414085461628689'); INSERT INTO num_exp_div VALUES (9, 9, '1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_exp_sqrt VALUES (0, '0'); INSERT INTO num_exp_sqrt VALUES (1, '291.964716019333021494947753821238960905461614737525349376826064492714634914263808902604580614735501799528494357560837535773816469841426747889103714048646989532842972129124080559131220979335403729022278994440514872845756198274805589586120535745968205107562348427941379641465378272611453955517402598409789621997041856848783989993820946766177453801729783316269310186191833995557234577548740940419224137195404391193633808203715191863638616433190672511651125299379882126530500870287424768024674231651229908224729856278167033444719242144302972892419034855417126978468296581589282861879645409909873113678361180607775255758820910366926076380306290306477790931129670172989289536405788838857428768869345763784112862591549008321546447442552533919976570125718481191724503352619626562352280522949665158335559389298720990302071'); INSERT INTO num_exp_sqrt VALUES (2, '31541679188064906.712574384704440356216787857626740375004266523720148374188511622980520374202725176835435173058936870163875556102907654264048353814040480579464700545975346621546520503928314632418705230212623378642743044255181848913683862360044189531298446109955034944189751302497670367665492719604026161836224535961347218522748523360100432275693829501972749859329753224444694962089604095212784768854310289429208671271394086829270986183171968944659703708706544668326267327938226750760690620258967209626420981505237183055363540806281098871221581265173394406715458619627534396065960117454160969749739483126059760636526242783235685190739315590041294766649891987044641492234243404608847939002062827210734973778130441825067858641461599799772535304379732674727995848518807202053316225824685704785148921785964036119338754973714515974054'); INSERT INTO num_exp_sqrt VALUES (3, '7765438138915239878.949520541017683429203286303188179443533225547096446554008374834292278237558244698868300666061834105683999048386497322007336816482648302911579331582895326423063492240235074387242190187374869842856897538718280497895072291181675294000739548676781615025944675912072664211455701112700937190832332966000160156597821149428032612782336278939437593991008833233156511435294360065004167893309428565243314846456225604669764879344135321428948841659419438769652686215993544390780212859309497190065178705035652106614050448518931820975038314187040226298661787490226917902356569717171481159691409131778764973037046501816919243659681416263730519167614043077472097520207347950292377914586524327206547377189493301153212000966249655331053184913579513686655963686155890934436604123384536027235444923674128269748280097789270784333442'); INSERT INTO num_exp_sqrt VALUES (4, '2308544622905.016172868282330339228589083058636874526727829838244942341440716909466939214393597311710652963849541394758298277969240038668406494621950956862959196896847352631445328917063551082418729435554972200530109505384839391233286173517804321019323644218483570886304028175359854335870835404627608254205407525763332087823548640923282031978903399118139052814618531713327991857575390136755426466065839913887477577516426991104516201265995293600539957187007068885368699949673989051443005684755994465547159213587471972139403333249259808344536605314911144950465968669770276463111776581675944967401948957460097365849699783091843609965345747287667911324039374314413430490112443463386381631812537639503425989372084906324702158112088898424705684574998783112519152403201231176840068666882123684602080460378627639651465436618032671756'); INSERT INTO num_exp_sqrt VALUES (5, '25549.082770905117529972076915050747181125832857399138345044265535151111965091602789684342996759657333588444489085160336703294705499665424408218434077722506748278242942379566431768762487954917389137120540138359870652558814224523699917122023018717544160579704907452934297025088008618627873220397030397424422097405152321366495319708580932627092620533785271831833326130796638935296720064431288560292191928489034307645738331451165431755179025359993690642194334018457793169983249853388987495489562746304107188105521296156525984787815685365255240654972150342496329030279439124533240114879332406941960563154881888172285475336782757262639979527682925214971861707635327995621436598536743180180978457735632181738067997521785965451385630326464388080990200265186437768409003553910194212076755448477164192901658547251079126833187'); INSERT INTO num_exp_sqrt VALUES (6, '.216649653115510782473161631235601739254284877523828136703593069337209747459679979369185882839688430004369697316986054374456779366220242645866798278985273820408495361607183119980716020227424205519727777568954933592987351750339481522149106749713967143685591960510946511796062486795368200503801097611436787402191532618456991115230272084771674098613479989808680789347124789253499967359190605681912854639520917409710307182238065185749856554472717209097115325999946728168357936779767099041518574001682560265549916593333117469681763348860131760281253987626822958726920016922608371657319505153308390495179319529587670415367205193280809809356733443291197315823747505896510820272670040485083775482983378341120809542502350385555577946098824446199419354197416933858522419312733314383889554606932774046771497129486979593226'); INSERT INTO num_exp_sqrt VALUES (7, '904950020.759072496304165474991957396337281699986101765045213964054286624338102141970514306010139529492299343393832200631760194440206005974547202512275476562767685193838576516154915404389465528270010938533075930081897392863141132529694804621418663424569202655893682412466871297412964570322984865326770090075582481194532433411398133265643849129084449161396724635797324126396071308557057830046688990212282866035593809633839882468628249964862932050189148498591642162462777480125024786829078066012617362076651920045684345679767223337287825546294839320770903419463644110383560050404456170063805115223954191445548226706113970164823214416171441655706141596091717118495955441099867737827763335880891937222647408575142200256804313345924443344596462585960919126827045197885802122062165934504665811115031150357820196176799560314653'); INSERT INTO num_exp_sqrt VALUES (8, '92179.098626752893864900181023972781406074846653380680747862421481598042923358730531575438403865501429843141967819802251116774924400485954931201776260931315313253827346015775662310076094882239170765060649024538403329505426563390044695320714825481746233901773893996663258170360232639353378395244461670781152793416950717050461856097473105730100523010642696332151571372764781034028324977128554099993021459338419164426784774496292405945103200724413639660488309795423335142455569853549710795692020963174011003447023610692365550245567840477105794884132665155376243735213346877116105595296043532605899184658904822980397411096930267453332143879534914237169761039374689145860503772331147367757318826885494994339695470190886515765452545019167989882527248872835783707554463866334705735781549392895480816605355996057201589681125'); INSERT INTO num_exp_sqrt VALUES (9, '7406.988615277484686670011157489572203134420118818648711986549881046321377798441006745317356200279801348355202517703531020643333388857073977704009782384103170022716610432579974132111487533733493986910583223121269323909760573942980360508642443245341392335557152177332615977623338526935953706604224108508582338123915133189529507760875123300397933931420500010248194253078118618381590347297853307090813639981736227771834732256867579490224181748450683295253634852775448770576585177080941820456051588076218688792321741398867304684922665590162004919486643750098085197190000638539994723704724550600891137853975703823903659121582583388450687255538838161486019214242094423895463814933532217776443473765708693285683261505695170847285063013324823850724236845500162436661946026097459146424122412596018946436589967013641971183281'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_exp_ln VALUES (0, 'NaN'); INSERT INTO num_exp_ln VALUES (1, '11.353265918833698201334218522735144514838241118349715803442713722607336732214173255618762341321138898556011520430414052782971985419141860417968593746833898952016980791997105866598425597066404919489902082738711038276194174786383758877067916049129476352925010880025206629976454341252818402788928939407784629386362069592202090897264194883276572978998896242281239126931595483958092059051047739223830394259082355969005503976135238921488192773135287876801394308064862257453262299764712613486466254696464150007113953810688169396432889052881763511661127351872408811370081346456019961324265446884877073712053408327408917588393884214304220369626106333713688792094943405258431214313197283237071070354654837081449831786573831004911008790533179001070424813584405346221388686999574752038655226138085374176702005198770598232862'); INSERT INTO num_exp_ln VALUES (2, '75.980172429959420723484178622920965327708652620924912610122049843800380131746381968266727388919414524075492921510147435877107720844487333947572033626887969846858337336557672107987074468763307953130616555202495401302128216460637786993535376622372745654109623249396257174895352222213037880060756992073605135503615371392439827458529942230210514752764526895030759481226199720092008002458654297737883219558685499445394647863430593136350562417924068100891680398878483362058595716232013516337079804607378041880078724811071904523716775991447489914128580100888252698281559809224785596795038122963619830942475652745611551345360922016753939774272970008770647516790944335173711498988149783075646985898883858697162003144539047532603946093022417842140993960433780913606807466518632121884254341907122163281927271483110212890483'); INSERT INTO num_exp_ln VALUES (3, '86.992429107491709045555322727377654177072455841678650084144967727028762699430180506209786297136121512625728883607972513154010138109866327600596617277403558404624813332464431424791338402731178416819791932126837396086742033973404980654712734845137075562739300866280737071167943367603243180515859476717635339619107593771719314284984269343476343816253634799874584843436046260962736006310389088154751401911743739429257286834178656182340416539923956100441369280015412718483971113838923221170027312390404790743389872757674342133486652087007983701950040432125562287337697971646750563062524010514537132255605131615248097901911480464339325353279118429890601202554448469387179349495284716473293965884844451619766312048304583068386805927433174443889441171878078987788018564357316138422561213329104267180509029624308926098065'); INSERT INTO num_exp_ln VALUES (4, '56.935276817066740776567329017240462885579486075188456418197311631774373422196025180114152248099799048545382060930401786002025479108787121595516444894009593031141335985913019897883627990503003577804436730367402618412514152465206336556967419434371593632864308139215157721913158949066717186782560422199668568894551013785702491365073449320535603830475158258853167712460432995074161536886421366716995573365924430692151761737886552457036412140640821310927642146210426044265504978418405684030862182425702683702307323138985481047994648222224089112998195621687911787785594701557252468626097576375468916953563766801336922479861708649876362257086586679701715813254414915314296890025577780265459584203893089574567331742100451277992780400302806430264717887468808962517029442262560742822875484362427192693300423729233467613910'); INSERT INTO num_exp_ln VALUES (5, '20.296713391219923821414834924710998522858242536565236229645868008008504475111229451635162536658197320282791428572861452713483981402773630985812066048575864982038046409484905688236579134672910905547858248343712686247795669280482288748331949478864729205285910525962001251260319741279139167559906461672936902355959755164523720443059989357054368460911050707727029320725144824995614445423492687177126412520389766864793826362309254124276325522276592246655562770110024099522184080118637524912964002223613671995639705240767929562023556724031894855094820328152633412077228479168557819219970917880393852962560319397442566813746504969336443969816954424715197797253670026862362130664772772977978222813915593329422557592316429203293264572088112274848838446633519530653849595288125585730314673691986554304725866754516304420665'); INSERT INTO num_exp_ln VALUES (6, '-3.058947463851998053084898503420969773173569760507671013593014983772013099601022840164736581595033399273677583253456908293015637115395777673836877852797643436458673662566205707359569792482081945396989472318998080581824382006377064185813936544714612287417301161454496258176319380348780934551188852900784476213986897306897793456700682073399936398243222895442594762628402487110466705108765286617060826203345783502301472192906817785365563881556293576463515218574477264521950513789471494214626744754200844840310516235570475410854073969787604451971790833680742315518808178608136598148628107328076871698598743664423452623124027059698038466681488746505289551548778131621576387262707147068500249466398507704796800459013580425992071957391417767257856002976954566094297724379688683375704613872658653366052459242767328235849'); INSERT INTO num_exp_ln VALUES (7, '41.246780548917246608934265057073076900048579756649769602488660179351587788197892095257027979113051775079905924990472069951828742350559917110289416201523653941731339141666097617614477426376799479821365070373247490598890520285155435501242427296281987676879064510605563522117334502131946383957407685328562874307957108543536378261847119286989184256009392692140821396916222386573424618796707564187152459973446833193743614720624765332006827171872712331032607870580880807058576154429597725560836582655488602546786785520452359711161305828045237044625934404295366273012300148250900116489718279757540843657039519736455668388572899273464839528462223812926410544976290646668870192676914370659142463304861500879195867873346447316374869974900582948166687948531910220128160490935170837209017355954301127162240133341813847180541'); INSERT INTO num_exp_ln VALUES (8, '22.862977375646110045361670561177818139082238721442691850491173190000619222046296383571431877856442345505931635735363450488731186880557789439424987680284612480261693386095598289519783790826332183796775862215503493910816035128476952347072320869461206895223935484838130924268616681347949695029657753251443811448783435000569829291535036468240771401957519222523032235686030017496209956550934543164421459898155836108824017735809352580723262896259290484291175350770265895317482371895188221452083719817251845416195168686335127805092334984596224320638378502008767433534450949989322562311171685891891122105437154553106840103473941148230953978989145470651955269817951560544095229079088083494695756914405635176899994279484466773598435268700064279990885608144109747858515514066444373797446449729058958270758597627587968112958'); INSERT INTO num_exp_ln VALUES (9, '17.820358481980064387183481028572263407130633079314879566896470101569251997264841660326428805413719418277889123643557369421967068805165885825106611310020187894256310674762734896979157570968168599492401269694048046876387337971177513661006711375440365724346137980004810780215236524986274043416621637509807126148966029923572853117418545426960105154053049098579812135003711132897895016476695223444397389521434633067499404903493027304737402519428197015899833229473322655155458942323004249812974150129789653469524573801259946118454333405580647485894435301530550214095993989552176497867244278699359917247910082169086524111229983698975613609318418313798992088206507831757327320958918656453341769110558376097374227592021075267882222057385413453949580066342977546145482215220982989992069525148522710254796105001938615214263'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_exp_log10 VALUES (0, 'NaN'); INSERT INTO num_exp_log10 VALUES (1, '4.930660740129727276654889314296515979425461685461970306647398411855044094312185293195497201658739777714943974003690119189101973212927970410047992001003936259467465542044528955416040460487922970233600641954269411521809500203864460110903973264337093883907933081597350982496469748131390809569321256206859934619579029279954574676601709408712255490686948453752571699579252140062805776361984468580258289509013081691778727372026090522694670379557247829136504595898935235926069699309392675806881162434168418505908116911054206058735257796918687777716036307205415038158583184624809880157060625643069601549803887864772092583549388533013233603450097615537162442973385137488450178790573546382354482351187412256794374383453695483855501587939419102008302408157959291557415763034668013452188944554607063362933134950906875499201'); INSERT INTO num_exp_log10 VALUES (2, '32.997769620388965086774969704518222090258389987679691893351902336370051104718852164011301929506188893338106627980171059175447833290713847317665944354651476245003161501753612545484635275306181777040447675475670149066399611203341262105766118892586541910243351018829302798733989560900125591073082441126709911019648451232244139674063434385451279378543163944005973452562993913383659295688375546058256196254319767218634546732685705517341998116744642480938405113447415486950667007645850519659606476727681944251201236366198374488204017630268083077471516734133869728427050843306716313813724061560369884508660845630727190444623729815564381063131729592825825486515070406390371638817503915214206586939112681762984038333298146999891250107667687034785493312416966635780188163871680959873288697497561452228182734430749066579749'); INSERT INTO num_exp_log10 VALUES (3, '37.780331928743475574895606142114739140772838801045013007323050327909196792739138159615327729728110344767302636436234256468332011934881494997184865617793179255006442447189720642997935223133982347184994174261506212652322213673745795726283311685835974151422721233207287206894148660531800622455957268888702309499182978182878524951883775154983702898237404558813230370364953160102391101897560104513279410610948028599674950811462114131673380477843456965645417025376374320207504913806546872166094337441573669261285052323206348035827948287081776955945081345131570610652073053464020209215624179904586956137079321655773178387441622685682721151900601340680061607114354850640946256225260430676099781727317540719923791064452012925902993317349390523278687089530234444415688602090547516647302454865526291471706301790881694022223'); INSERT INTO num_exp_log10 VALUES (4, '24.726676547286224970759328746582840552419566534667446425423046931401641497155587075591229106937829957279943690528061985864558314570189069764367933957499905044566413640017549478921384160584906257607957223101377816440084188042395098536074479064548620374152344954289432050971466476174493306432228880930006524504974367146536665170956555486181410864034862861231267121149652317599303804477688621597163730470970207231328339082779056152481480926452142005969020950341307977091850953883445808399574256295803245530993204179747743812544604144379381347499056545148243304041538981954204310612049423688645476667184129189153715486929216331980316967699254518020077226689317148303152585009031597809279387172427408557115400021035692880631275593381822805377317270568779655383061987766693697518921188619814204902583361096973421134004'); INSERT INTO num_exp_log10 VALUES (5, '8.814750626578650238811431417807018895270298639823442501111235973209197727215795256506525221092818797578008152140054383421240180435087611869193019443372556081555311825248667278358330916098378127100899126895012782320751838528480712942601038190627182482614147263228588284866661508052724762701223357327343090598060805245853527435948381893458352744679795853650453594546267600486696643924152372736774331080527157374379043696696647158270918245668579680394279565181670004245143555617589138267976417280970718829942998800499312890580011246294669585429723974582350357991472101919333996770115834067969654217063942059882195268353998096891812525364797586486311202350700339609637274043915687880562465121559531284337603363356183320193656553931871200575467929714875483123706358278876389849119105053294688326141759401230994901405'); INSERT INTO num_exp_log10 VALUES (6, '-1.328484003982869642690619298690906747763234110040562640557173509402512757735587333095924652711056556491908059708986413635120656426593745303715671199761364516107844087845783714418487426723538440387069985879601248897538855843115404484229652166941838283489828419407478748732927617251897244190697443966424660881366993754577233476597163021768156814527570512834684713730559883782625870597080940193303268818336816535968869931456641949301731046034660616615392129109391145214470757259042172416816936479713743188047425796931722546185493217275537303458837771965375448968719169174136287532752370175863826715450565025635651343928205805494319778539652563499901671319955144823432132740582617949774638538594081514904904341299199113721131520557004571803778698005652464301037962272085633628653321081368256925971558076970172779715'); INSERT INTO num_exp_log10 VALUES (7, '17.913249188669140643510654105014358282516966474257460687880559542190804665566625978925406311113121982595279826214959603627387555578965653325278444455875162277940655989601428868642914577248262147833499137348602966573601719040813549936948178463592211685237720748377879836890106515699728652218324794927458352954247096536337594789471529493944292143186953509162522579060020018226817623648563806559917579317916242706559131476179714031602207057714677845347616752450567251644277767418397621490301286115159509360375419599968738067461569666699939732107480135216621373057421990702923042287910730395998082514702629760389192370666675364405730936537832803383367187639209534697198515928978064543150195911463663617683085348965065679311986715357338675515370634753254774665197233934933271954463040729779956682570415317734489164385'); INSERT INTO num_exp_log10 VALUES (8, '9.929264914121995501917993119394933531225401243275938207624866270551448544301376913376130982251708700134720886862945040266148728213253651323129942781577143957084726727561987639140151337848818195806259935747329665025823709044567138449084349729747202164413995795609659711723455165142329822773177102845804114214340046404641970845707372809306219463962664551623665322610139794354769767829380018857313559373283673392337954610346290037758389035140213224696023751541663171574697035012610534455189013755134090933979479069288110010954211669067225249755249337768792642303351914884187159646984708862430789018895140670365476746734456807215043628059581947593694929159076346249490593187993386780521089745819640214783614157516171005086731241769146397577246387886107367648843380733370112546792442909347322732196805316614555689762'); INSERT INTO num_exp_log10 VALUES (9, '7.739283354261751283625223433456284905560931805428759681411970457812279544250432389511382263439324085689734710188041049046660480575958686859942980599595036769090747781359217248301544587434077376812293034848418204834388504169166350770257248896025815531248627658465029806509131631454856186387892627989218208026727504548130018922325585619738185507999433763118148418722504204066578294826264005398891049629199412773138457218976050467479292777172717500219850781664314597312411301296201533610562886229900497272268364496763758868455934979903774531992886483396489868888731578355541611359130188566524240259770918423445785338175040098706500034487703124623745259139247432324145633151895802637182446905097253961951018926565652497920605819785424451050191604602898777804133717341512568151920576684198443843944721398831404081859'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_exp_power_10_ln VALUES (0, 'NaN'); INSERT INTO num_exp_power_10_ln VALUES (1, '225561990715.277245515991117670624124484084762557459065170589803293759247930753528436379932442146759103295277479258327642314622036941865221478746258727236601688778946696303277607709407496616423493315166963938393760548678730128692212077086588682984700837334554241405763691119669847463520746595280034536307041368063462023793177898200220207765205127584303464304601759554817607633012272490650155253979182893585119965271975927569080191838676053084168631217591768468344106219831174026139608715965691941366334940196517120885214887008671956523579678156919416435031020452971977153991139145404842034138317592877675821045409772456977018293365238179815614004574330200783530118851005077771478448804470170641452481992602803877112958872108069738434946694089025321283178188028224338756015337492913115267362635647236447601252924834642796058'); INSERT INTO num_exp_power_10_ln VALUES (2, '9553718264533556311125292459627965006385666643531070061102266984368939757379.536714147420215784125170401370065894858487440153494392538261078415409784085960333028254155527328359894197540839556987826344995348426293585457768226283066583722499658006242709930685932246087653832230889613022921575445199055131152661556678809191264086381976922223866204038615136758192929883317207903579770917317641181652055458721731297347443662717939116561947785705140374908203404860090658919334137955075887697259604047657534191202566335372150375993361370075961180728155127447781364264047857624746079509591666068708743260905728661917791822925979235918475633100283148558978385583805341715868143937062092264994833222352433299015979561976964779350640064096690062929265992966564232453102431600199173711947391200249130712039686700111791790265309426741120465259677894665532560198051256215915373145226284270408649736509'); INSERT INTO num_exp_power_10_ln VALUES (3, '982718444846268846508445482774217796844461660819285525931206164100817251856409365450682.362683768066405322653747385034480250394145008573806022660379219602846285813744865438912887625784087005970975437905783802114553690522787857272953842288090141945268495451006273685577260054069522075046955466204804067271437138871789034722069934693546671607506851844248427950939791205412350536883779850165603116191193657054604569586553874805856647223849267039531773072343908345333155562072887754900969504551717514980465801806565999410206735831440712124661645970935112535081991606671600328471264697018198676317466846450405861359235297846597981143547119390922405594115478086038680663368675222949247096131378724350715530605691796680604309063173515781378545860473572389718345696107553363715518601596249508215455106779522851210398208919496668879040223859884166805448827948087400426315425231119801173387715922086154065273'); INSERT INTO num_exp_power_10_ln VALUES (4, '861542720105376650266753999919217194383259935058507531116.774511336660822591851369622743235084609149542494189385785321912210129989390054947787009383210009523204976629456268332186620016067379702483800883493431423160815760933380418976582725913410929214462739708321325884209636272001805871036779154087677637129248122540412937033791526383240502286607736226090213753913654673523613612439527815137888202973659987501649474772884055648603290154867585312925699571949539600328906295652872654314913539778815035321695215634102441494403825526533235061083947035338872599854931230001361227174477274708230470794066733245241594719912710139298949856243576688344051439047966427547889756037265151798639614843866387316916203238068277912991427278268083231579195846744438643659745041780103653332041031419793815914447232121937821142169172566753399257291244398531365781832297786941359729799400'); INSERT INTO num_exp_power_10_ln VALUES (5, '198021976607570296508.271597639984889464620426933601643322058775615235389194561064983706229795978402690473201671702614911129095149240715527556855309177671128442458698638704394974473956869419481315262823632891676087912529523219333012290621046361106033860210270638559271706082115529424772192777643046125905852037759566224116373416253787241195450409652089019290072319861181399387753223422998872180810295299831487867222464355713552301775702554189470264147325049133532522718679336524769566984150923939420759804463781082299907043016120177416779442865059261387111806785876531152192378576258351599534512031062777609734092707165605364139201322351960602280089186180302246827234844736393745487324460438448807241887783263546165171099497316415863122023114646876909575845860402164818094500541234974716577550807551946414081410743197768993152975501'); INSERT INTO num_exp_power_10_ln VALUES (6, '.000873076977206566818052116526263730226812004454463281371489634779519089200224205946321120805055212090024554381349223642352209212670470260295303361873760972918129853308169576675500721645609379420329169271088810484607337679253503247351324049221970104335289487989027621978310506220905131150125321713385148268584530413680037620544212746920563790371941626294733473967065607791756894237438288480748407449237446113996117912144587258434808327522518688617394025018756570740098795745692805352377041347367240475846033282850136270250633825482156304826383360291164928049344226886150285595932088884965511963310715773499733217615863523253012606066583814112265708693122563204149232245895551314975524172504103194858904869273185785182598234060315036187756490539352752560361560286717869643902435677448962235275054804452967413005'); INSERT INTO num_exp_power_10_ln VALUES (7, '176514565873872717825163931126806100435750.096278384530154766967061948052237623936423931849868926020451465515367348890410352640552194499619062823622476972850692557798609619250753020363520533767813563613425606228355802781302735485038377521515850536680425059519814786118919994914180918228654298075183514200191737597656810036850772127169441661576862538643715648802139886576391427423689320082366572297580054381937437005879583216745596935643579262248665490169331304003204939561361718554509909313409421397022626924406091551900222555950699170864234411017062042057683304265485826061096835531732950909546314722726990314852356462874701181085379772134121978510387397276859318242238150439474660772561390798432890789762504242822787017140808209820627435991445529404692793744568204608385843245177656436105160780897472099970336514833257055017279707999437302548655364559'); INSERT INTO num_exp_power_10_ln VALUES (8, '72941951052009383458167.300747500436981484566111756088702608000390737594784514635592222758882092500858797317505303492923829092720870826490477962201959426813271424853341826896270963213736922458746003100613943600855942721319226948714369219316345322636075285343544788982588956431405042577296229122673590336976893594798942025893296105815818487227300314490440902574022885833779324177053242170024559675073866612316965636832258283516275906085642459351367507561963945012828379111856700009391438637054015804558386733558956649061672420804826896303889067785497738203077050774825608647969196321506624991188638449047860249367840775936911749905927108478444112230174584693363226143549933224252679398881354887872642908328737917862751077365602631600279486028043329404269490375935308156815477700961014566228692743960491745353377403533037122586797765130'); INSERT INTO num_exp_power_10_ln VALUES (9, '661239032819374816.097553651299556484820492272269662685578275493609248662925676004753503494252951243895572437264999063878330704584509915845096232798927524470286655554736724913758600775591269525423912692080421094644542553026831758426157681271572808657664918053119324646138457659418857926209701677786068580819823633713337632456905824562235373422309621872998037966404189020165296080436871220718574009921789858751384547836431858428729570977259373272041837411903005303672798845573379758630607982213326716018594073712340609488043353995410508475153538231445235003980586600882223782814368245305160648543466496726973755388826656879616734762068443462618454921858705377028522664844761719759342490380417060255776725333319537746890406213693117052223545525717132695297770810635066731941724108167146710297146989770382041617889670713111888375717'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_data VALUES (0, '0'); INSERT INTO num_data VALUES (1, '85243.39540024977626076239847863600785982737155858270959890014613035727868293618673807776733416230953723818527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_data VALUES (2, '-994877526002806872754342148749241.04353023451866590387054959174736129501310680280823383331007646306243540953499740615246583399296334239109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_data VALUES (3, '-60302029489319384367663884408085757480.2322712404088283093870869198708849258097125725036189625900174440196153781995220721511009855207486224837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_data VALUES (4, '5329378275943663322215245.24931765987630429629836382184742348353920297283690739124220773955591340709935970062776650204659187764581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_data VALUES (5, '-652755630.43456071828048833552593835051449845484289562110789582081210403487973096161149072377955192388469356112505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_data VALUES (6, '0.0469370721950711508944806393077762204079964905145503836835397203739563036579760026190241480514409364'); INSERT INTO num_data VALUES (7, '-818934540071845742'); INSERT INTO num_data VALUES (8, '8496986223.64063724353165506167988570717591150432282238795309964705925283285060558038824227595710139960766584401003765241409149793494330798800'); INSERT INTO num_data VALUES (9, '054863480.34685027005508022756223282084742813020271603840941647350440860843570182437301045468670059279379903480024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); COMMIT TRANSACTION; -- ****************************** -- * Create indices for faster checks -- ****************************** CREATE UNIQUE INDEX num_exp_add_idx ON num_exp_add (id1, id2); CREATE UNIQUE INDEX num_exp_sub_idx ON num_exp_sub (id1, id2); CREATE UNIQUE INDEX num_exp_div_idx ON num_exp_div (id1, id2); CREATE UNIQUE INDEX num_exp_mul_idx ON num_exp_mul (id1, id2); CREATE UNIQUE INDEX num_exp_sqrt_idx ON num_exp_sqrt (id); CREATE UNIQUE INDEX num_exp_ln_idx ON num_exp_ln (id); CREATE UNIQUE INDEX num_exp_log10_idx ON num_exp_log10 (id); CREATE UNIQUE INDEX num_exp_power_10_ln_idx ON num_exp_power_10_ln (id); VACUUM ANALYZE num_exp_add; VACUUM ANALYZE num_exp_sub; VACUUM ANALYZE num_exp_div; VACUUM ANALYZE num_exp_mul; VACUUM ANALYZE num_exp_sqrt; VACUUM ANALYZE num_exp_ln; VACUUM ANALYZE num_exp_log10; VACUUM ANALYZE num_exp_power_10_ln; -- ****************************** -- * Now check the behaviour of the NUMERIC type -- ****************************** -- ****************************** -- * Addition check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, t1.val + t2.val FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, t2.expected FROM num_result t1, num_exp_add t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != t2.expected; DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, round(t1.val + t2.val, 10) FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, round(t2.expected, 10) AS expected FROM num_result t1, num_exp_add t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != round(t2.expected, 10); -- ****************************** -- * Subtraction check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, t1.val - t2.val FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, t2.expected FROM num_result t1, num_exp_sub t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != t2.expected; DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, round(t1.val - t2.val, 40) FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, round(t2.expected, 40) FROM num_result t1, num_exp_sub t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != round(t2.expected, 40); -- ****************************** -- * Multiply check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, t1.val * t2.val FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, t2.expected FROM num_result t1, num_exp_mul t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != t2.expected; DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, round(t1.val * t2.val, 30) FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, round(t2.expected, 30) AS expected FROM num_result t1, num_exp_mul t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != round(t2.expected, 30); -- ****************************** -- * Division check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, t1.val / t2.val FROM num_data t1, num_data t2 WHERE t2.val != '0.0'; SELECT t1.id1, t1.id2, t1.result, t2.expected FROM num_result t1, num_exp_div t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != t2.expected; DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, round(t1.val / t2.val, 80) FROM num_data t1, num_data t2 WHERE t2.val != '0.0'; SELECT t1.id1, t1.id2, t1.result, round(t2.expected, 80) AS expected FROM num_result t1, num_exp_div t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != round(t2.expected, 80); -- ****************************** -- * Square root check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT id, 0, SQRT(ABS(val)) FROM num_data; SELECT t1.id1, t1.result, t2.expected FROM num_result t1, num_exp_sqrt t2 WHERE t1.id1 = t2.id AND t1.result != t2.expected; -- ****************************** -- * Natural logarithm check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT id, 0, LN(ABS(val)) FROM num_data WHERE val != '0.0'; SELECT t1.id1, t1.result, t2.expected FROM num_result t1, num_exp_ln t2 WHERE t1.id1 = t2.id AND t1.result != t2.expected; -- ****************************** -- * Logarithm base 10 check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT id, 0, LOG('10'::numeric, ABS(val)) FROM num_data WHERE val != '0.0'; SELECT t1.id1, t1.result, t2.expected FROM num_result t1, num_exp_log10 t2 WHERE t1.id1 = t2.id AND t1.result != t2.expected; -- ****************************** -- * POW(10, LN(value)) check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT id, 0, POW(numeric '10', LN(ABS(round(val, 1000)))) FROM num_data WHERE val != '0.0'; SELECT t1.id1, t1.result, t2.expected FROM num_result t1, num_exp_power_10_ln t2 WHERE t1.id1 = t2.id AND t1.result != t2.expected; -- -- Test code path for raising to integer powers -- -- base less than 1 -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of POW(): -- -- for p in {-20..20} -- do -- b="0.084738" -- r=$(bc -ql <<< "scale=500 ; $b^$p" | head -n 1) -- echo "($b, $p, $r)," -- done WITH t ( b, p, bc_result ) AS ( VALUES (0.084738, - 20, 2744326694304960114888.7859130502035257), (0.084738, - 19, 232548755422013710215.4459407000481464), (0.084738, - 18, 19705716436950597776.2364581230406798), (0.084738, - 17, 1669822999434319754.3627249884302211), (0.084738, - 16, 141497461326065387.3451885900696001), (0.084738, - 15, 11990211877848128.7928565907453178), (0.084738, - 14, 1016026574105094.7376490817865767), (0.084738, - 13, 86096059836517.5178789078924309), (0.084738, - 12, 7295607918426.8214300228969888), (0.084738, - 11, 618215223791.6519943372802450), (0.084738, - 10, 52386321633.6570066961524534), (0.084738, - 9, 4439112122.5928274334185666), (0.084738, - 8, 376161483.0442710110530225), (0.084738, - 7, 31875171.7502054369346110), (0.084738, - 6, 2701038.3037689083149651), (0.084738, - 5, 228880.5837847697527935), (0.084738, - 4, 19394.8829087538193122), (0.084738, - 3, 1643.4835879219811409), (0.084738, - 2, 139.2655122733328379), (0.084738, - 1, 11.8010809790176780), (0.084738, 0, 1), (0.084738, 1,.084738), (0.084738, 2,.007180528644), (0.084738, 3,.0006084636362353), (0.084738, 4,.0000515599916073), (0.084738, 5,.0000043690905688), (0.084738, 6,.0000003702279966), (0.084738, 7,.0000000313723800), (0.084738, 8,.0000000026584327), (0.084738, 9,.0000000002252703), (0.084738, 10,.0000000000190890), (0.084738, 11,.0000000000016176), (0.084738, 12,.0000000000001371), (0.084738, 13,.0000000000000116), (0.084738, 14,.0000000000000010), (0.084738, 15,.0000000000000001), (0.084738, 16,.0000000000000000), (0.084738, 17,.0000000000000000), (0.084738, 18,.0000000000000000), (0.084738, 19,.0000000000000000), (0.084738, 20,.0000000000000000)) SELECT b, p, bc_result, b ^ p AS power, b ^ p - bc_result AS diff FROM t; -- base greater than 1 -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of POW(): -- -- for p in {-20..20} -- do -- b="37.821637" -- r=$(bc -ql <<< "scale=500 ; $b^$p" | head -n 1) -- echo "($b, $p, $r)," -- done WITH t ( b, p, bc_result ) AS ( VALUES (37.821637, - 20,.0000000000000000), (37.821637, - 19,.0000000000000000), (37.821637, - 18,.0000000000000000), (37.821637, - 17,.0000000000000000), (37.821637, - 16,.0000000000000000), (37.821637, - 15,.0000000000000000), (37.821637, - 14,.0000000000000000), (37.821637, - 13,.0000000000000000), (37.821637, - 12,.0000000000000000), (37.821637, - 11,.0000000000000000), (37.821637, - 10,.0000000000000002), (37.821637, - 9,.0000000000000063), (37.821637, - 8,.0000000000002388), (37.821637, - 7,.0000000000090327), (37.821637, - 6,.0000000003416316), (37.821637, - 5,.0000000129210673), (37.821637, - 4,.0000004886959182), (37.821637, - 3,.0000184832796213), (37.821637, - 2,.0006990678924066), (37.821637, - 1,.0264398920649574), (37.821637, 0, 1), (37.821637, 1, 37.821637), (37.821637, 2, 1430.476225359769), (37.821637, 3, 54102.9525326873775219), (37.821637, 4, 2046262.2313195326271135), (37.821637, 5, 77392987.3197773940323425), (37.821637, 6, 2927129472.7542235178972258), (37.821637, 7, 110708828370.5116321107718772), (37.821637, 8, 4187189119324.7924539711577286), (37.821637, 9, 158366346921451.9852944363360812), (37.821637, 10, 5989674486279224.5007355092228730), (37.821637, 11, 226539294168214309.7083246628376531), (37.821637, 12, 8568086950266418559.9938312759931069), (37.821637, 13, 324059074417413536066.1494087598581043), (37.821637, 14, 12256444679171401239980.3109258799733927), (37.821637, 15, 463558801566202198479885.2069857662592280), (37.821637, 16, 17532552720991931019508170.1002855156233684), (37.821637, 17, 663109844696719094948877928.0672523682648687), (37.821637, 18, 25079899837245684700124994552.6717306599041850), (37.821637, 19, 948562867640665366544581398598.1275771806665398), (37.821637, 20, 35876200451584291931921101974730.6901038166532866)) SELECT b, p, bc_result, b ^ p AS power, b ^ p - bc_result AS diff FROM t; -- -- Tests for raising to non-integer powers -- -- base less than 1 -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of POW(): -- -- for n in {-20..20} -- do -- b="0.06933247" -- p="$n.342987" -- r=$(bc -ql <<< "scale=500 ; e($p*l($b))" | head -n 1) -- echo "($b, $p, $r)," -- done WITH t ( b, p, bc_result ) AS ( VALUES (0.06933247, - 20.342987, 379149253615977128356318.39406340), (0.06933247, - 19.342987, 26287354251852125772450.59436685), (0.06933247, - 18.342987, 1822567200045909954554.65766042), (0.06933247, - 17.342987, 126363085720167050546.86216560), (0.06933247, - 16.342987, 8761064849800910427.02880469), (0.06933247, - 15.342987, 607426265866876128.15466179), (0.06933247, - 14.342987, 42114363355427213.14899924), (0.06933247, - 13.342987, 2919892833909256.59283660), (0.06933247, - 12.342987, 202443382310228.51544515), (0.06933247, - 11.342987, 14035899730722.44924025), (0.06933247, - 10.342987, 973143597003.32229028), (0.06933247, - 9.342987, 67470449244.92493259), (0.06933247, - 8.342987, 4677892898.16028054), (0.06933247, - 7.342987, 324329869.02491071), (0.06933247, - 6.342987, 22486590.914273551), (0.06933247, - 5.342987, 1559050.8899661435), (0.06933247, - 4.342987, 108092.84905705095), (0.06933247, - 3.342987, 7494.3442144625131), (0.06933247, - 2.342987, 519.60139541889576), (0.06933247, - 1.342987, 36.025248159838727), (0.06933247, 0.342987,.40036522320023350), (0.06933247, 1.342987,.02775830982657349), (0.06933247, 2.342987,.001924552183301612), (0.06933247, 3.342987,.0001334339565121935), (0.06933247, 4.342987,.000009251305786862961), (0.06933247, 5.342987,.0000006414158809285026), (0.06933247, 6.342987,.00000004447094732199898), (0.06933247, 7.342987,.000000003083280621074075), (0.06933247, 8.342987,.0000000002137714611621997), (0.06933247, 9.342987,.00000000001482130341788437), (0.06933247, 10.342987,.000000000001027597574581366), (0.06933247, 11.342987,.00000000000007124587801173530), (0.06933247, 12.342987,.000000000000004939652699872298), (0.06933247, 13.342987,.0000000000000003424783226243151), (0.06933247, 14.342987,.00000000000000002374486802900065), (0.06933247, 15.342987,.000000000000000001646290350274646), (0.06933247, 16.342987,.0000000000000000001141413763217064), (0.06933247, 17.342987,.000000000000000000007913703549583420), (0.06933247, 18.342987,.0000000000000000000005486766139403860), (0.06933247, 19.342987,.00000000000000000000003804110487572339), (0.06933247, 20.342987,.000000000000000000000002637483762562946)) SELECT b, p, bc_result, b ^ p AS power, b ^ p - bc_result AS diff FROM t; -- base greater than 1 -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of POW(): -- -- for n in {-20..20} -- do -- b="27.234987" -- p="$n.230957" -- r=$(bc -ql <<< "scale=500 ; e($p*l($b))" | head -n 1) -- echo "($b, $p, $r)," -- done WITH t ( b, p, bc_result ) AS ( VALUES (27.234987, - 20.230957,.000000000000000000000000000009247064512095633), (27.234987, - 19.230957,.0000000000000000000000000002518436817750859), (27.234987, - 18.230957,.000000000000000000000000006858959399176602), (27.234987, - 17.230957,.0000000000000000000000001868036700701026), (27.234987, - 16.230957,.000000000000000000000005087595525911532), (27.234987, - 15.230957,.0000000000000000000001385605980094587), (27.234987, - 14.230957,.000000000000000000003773696085499835), (27.234987, - 13.230957,.0000000000000000001027765638305389), (27.234987, - 12.230957,.000000000000000002799118379829397), (27.234987, - 11.230957,.00000000000000007623395268611469), (27.234987, - 10.230957,.000000000000002076230710364949), (27.234987, - 9.230957,.00000000000005654611640579014), (27.234987, - 8.230957,.000000000001540032745212181), (27.234987, - 7.230957,.00000000004194277179542807), (27.234987, - 6.230957,.000000001142310844592450), (27.234987, - 5.230957,.00000003111082100243440), (27.234987, - 4.230957,.0000008473028055606278), (27.234987, - 3.230957,.00002307628089450723), (27.234987, - 2.230957,.0006284822101702527), (27.234987, - 1.230957,.01711670482371810), (27.234987, 0.230957, 2.1451253063142300), (27.234987, 1.230957, 58.422459830839071), (27.234987, 2.230957, 1591.1349340009243), (27.234987, 3.230957, 43334.539242761031), (27.234987, 4.230957, 1180215.6129275865), (27.234987, 5.230957, 32143156.875279851), (27.234987, 6.230957, 875418459.63720737), (27.234987, 7.230957, 23842010367.779367), (27.234987, 8.230957, 649336842420.336290), (27.234987, 9.230957, 17684680461938.907402), (27.234987, 10.230957, 481642042480060.137900), (27.234987, 11.230957, 13117514765597885.614921), (27.234987, 12.230957, 357255344113366461.949871), (27.234987, 13.230957, 9729844652608062117.440722), (27.234987, 14.230957, 264992192625800087863.690528), (27.234987, 15.230957, 7217058921265161257566.469315), (27.234987, 16.230957, 196556505898890690402726.443417), (27.234987, 17.230957, 5353213882921711267539279.451015), (27.234987, 18.230957, 145794710509592328389185797.837767), (27.234987, 19.230957, 3970717045397510438979206144.696206), (27.234987, 20.230957, 108142427112079606637962972621.121293)) SELECT b, p, bc_result, b ^ p AS power, b ^ p - bc_result AS diff FROM t; -- Inputs close to overflow -- -- bc(1) results computed with a scale of 2700 and truncated to 4 decimal -- places. WITH t ( b, p, bc_result ) AS ( VALUES (0.12, - 2829.8369, 58463948950011752465280493160293790845494328939320966633018493248607815580903065923369555885857984675501574162389726507612128133630191173383130639968378879506624785786843501848666498440326970769604109017960864573408272864266102690849952650095786874354625921641729880352858506454246180842452983243549491658464046163869265572232996388827878976066830374513768599285647145439771472435206769249126377164951470622827631950210853282324510655982757098065657709137845327135766013147354253426364240746381620690117663724329288646510198895137275207992825719846135857839292915100523542874885080351683587865157015032404901182924720371819942957083390475846809517968191151435281268695782594904484795360890092607679215675240583291240729468370895035823777914792823688291214492607109455017754453939895630226174304357121900605689015734289765672740769194115142607443713769825894380064727556869268488695795705030158832909348803019429370973064732712469794182891757241046263341655894972953512257981661670321890336672832647028099324621932563236459127918144141230217523147304565594514812518826936144181257723061181656522095236928347413997136815409159361412494284201481609684892562646522086577634100783077813105675590737823924220663206479031113753135119759722725207724879578900186075841393115040465401462266086907464970054073340036852442184414587772177753008511913377364966775792477387717262694468450099866775550614257191941835797445874557362115814601886902749237439492398087966544817154173072811937702110580330775581851211123491341435883319798273456296794954514173820352334127081705706502510709179711510240917772628308487366740741280043704807717608366220401933596364641284631036907635403895053036499618723044314773148779735006542501244942039455169872946018271985844759209768927953340447524637670938413827595013338859796135512187473850161303598087634723542727044978083220970836296653305188470017342167913572166172051819741354902582606590658382067039498769674611071582171914886494269818475850690414812481252963932223686078322390396586222238852602472958831686564971334200490182175112490433364675164900946902818404704835106260174052265784055642968397240262737313737007322288203637798365320295080314524864099419556398713380156353062937736280885716820226469419928595465390700629307079710611273715705695938635644841913194091407807776191951797748706106000922803167645881087385311847268311361092838264814899353459146959869764278464187826798546290981492648723002412475976344071283321798061003719251864595518596639432393032991023409676558943539937377229130132816883146259468718344018277257037013406135980469482324577407154032999045733141275895.3432), (1.2, 32908.8896, 58463467728170833376633133695001863276259293590926929026251227859007891876739460057725441400966420577009060860805883032969522911803372870882799865787473726926215148161529632590083389287080925059682489116446754279752928005457087175157581627230586554364417068189211136840990661174760199073702207450133797324318403866058202372178813998850887986769280847189341565507156189065295823921162851958925352114220880236114784962150135485415106748467247897246441194126125699204912883449386043559785865023459356275014504597646990160571664166410683323036984805434677654413174177920726210827006973855410386789516533036723888687725436216478665958434776205940192130053647653715221076841771578099896259902368829351569726536927952661429685419815305418450230567773264738536471211804481206474781470237730069753206249915908804615495060673071058534441654604668770343616386612119048579369195201590008082689834456232255266932976831478404670192731621439902738547169253818323045451045749609624500171633897705543164388470746657118050314064066768449450440405619135824055131398727045420324382226572368236570500391463795989258779677208133531636928003546809249007993065200108076924439703799231711400266122025052209803513232429907231051873161206025860851056337427740362763618748092029386371493898291580557004812947013231371383576580415676519066503391905962989205397824064923920045371823949776899815750413244195402085917098964452866825666226141169411712884994564949174271056284898570445214367063763956186792886147126466387576513166370247576466566827375268334148320298849218878848928271566491769458471357076035396330179659440244425914213309776100351793665960978678576150833311810944729586040624059867137538839913141142139636023129691775489034134511666020819676247950267220131499463010350308195762769192775344260909521732256844149916046793599150786757764962585268686580124987490115873389726527572428003433405659445349155536369077209682951123806333170190998931670309088422483075609203671527331975811507450670132060984691061148836994322505371265263690017938762760088575875666254883673433331627055180154954694693433502522592907190906966067656027637884202418119121728966267936832338377284832958974299187166554160783467156478554899314000348357280306042140481751668215838656488457943830180819301102535170705017482946779698265096226184239631924271857062033454725540956591929965181603262502135610768915716020374362368495244256420143645126927013882334008435586481691725030031204304273292938132599127402133470745819213047706793887965197191137237066440328777206799072470374264316425913530947082957300047105685634407092811630672103242089966046839626911122.7149)) SELECT b, p, bc_result, b ^ p AS power, b ^ p - bc_result AS diff FROM t; -- -- Tests for EXP() -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of EXP(): -- -- for n in {-20..20} -- do -- x="$n.29837" -- r=$(bc -ql <<< "scale=500 ; e($x)" | head -n 1) -- echo "($x, $r)," -- done WITH t ( x, bc_result ) AS ( VALUES (- 20.29837,.000000001529431101152222), (- 19.29837,.000000004157424770142192), (- 18.29837,.00000001130105220586304), (- 17.29837,.00000003071944485366452), (- 16.29837,.00000008350410872606600), (- 15.29837,.0000002269877013517336), (- 14.29837,.0000006170165438681061), (- 13.29837,.000001677224859055276), (- 12.29837,.000004559169856609741), (- 11.29837,.00001239310857408049), (- 10.29837,.00003368796183504298), (- 9.29837,.00009157337449401917), (- 8.29837,.0002489222398577673), (- 7.29837,.0006766408013046928), (- 6.29837,.001839300394580514), (- 5.29837,.004999736839665763), (- 4.29837,.01359069379834070), (- 3.29837,.03694333598818056), (- 2.29837,.1004223988993283), (- 1.29837,.2729763820983097), (0.29837, 1.3476603299656679), (1.29837, 3.6633205858807959), (2.29837, 9.9579377804197108), (3.29837, 27.068481317440698), (4.29837, 73.579760889182206), (5.29837, 200.01052696742555), (6.29837, 543.68498095607070), (7.29837, 1477.8890041389891), (8.29837, 4017.3188244304487), (9.29837, 10920.204759575742), (10.29837, 29684.194161006717), (11.29837, 80690.005580314652), (12.29837, 219338.17590722828), (13.29837, 596222.97785597218), (14.29837, 1620702.0864156289), (15.29837, 4405525.0308492653), (16.29837, 11975458.636179032), (17.29837, 32552671.598188404), (18.29837, 88487335.673150406), (19.29837, 240533516.60908059), (20.29837, 653837887.33381570)) SELECT x, bc_result, exp(x), exp(x) - bc_result AS diff FROM t; -- -- Tests for LN() -- -- input very small -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of LN(): -- -- for p in {1..40} -- do -- l=$(bc -ql <<< "scale=500 ; l(10^-$p)" | head -n 1) -- echo "('1.0e-$p', $l)," -- done WITH t ( x, bc_result ) AS ( VALUES ('1.0e-1', - 2.3025850929940457), ('1.0e-2', - 4.6051701859880914), ('1.0e-3', - 6.9077552789821371), ('1.0e-4', - 9.2103403719761827), ('1.0e-5', - 11.512925464970228), ('1.0e-6', - 13.815510557964274), ('1.0e-7', - 16.118095650958320), ('1.0e-8', - 18.420680743952365), ('1.0e-9', - 20.723265836946411), ('1.0e-10', - 23.025850929940457), ('1.0e-11', - 25.328436022934503), ('1.0e-12', - 27.631021115928548), ('1.0e-13', - 29.933606208922594), ('1.0e-14', - 32.236191301916640), ('1.0e-15', - 34.5387763949106853), ('1.0e-16', - 36.84136148790473094), ('1.0e-17', - 39.143946580898776628), ('1.0e-18', - 41.4465316738928223123), ('1.0e-19', - 43.74911676688686799634), ('1.0e-20', - 46.051701859880913680360), ('1.0e-21', - 48.3542869528749593643778), ('1.0e-22', - 50.65687204586900504839581), ('1.0e-23', - 52.959457138863050732413803), ('1.0e-24', - 55.2620422318570964164317949), ('1.0e-25', - 57.56462732485114210044978637), ('1.0e-26', - 59.867212417845187784467777822), ('1.0e-27', - 62.1697975108392334684857692765), ('1.0e-28', - 64.47238260383327915250376073116), ('1.0e-29', - 66.774967696827324836521752185847), ('1.0e-30', - 69.0775527898213705205397436405309), ('1.0e-31', - 71.38013788281541620455773509521529), ('1.0e-32', - 73.682722975809461888575726549899655), ('1.0e-33', - 75.9853080688035075725937180045840189), ('1.0e-34', - 78.28789316179755325661170945926838306), ('1.0e-35', - 80.590478254791598940629700913952747266), ('1.0e-36', - 82.8930633477856446246476923686371114736), ('1.0e-37', - 85.19564844077969030866568382332147568124), ('1.0e-38', - 87.498233533773735992683675278005839888842), ('1.0e-39', - 89.8008186267677816767016667326902040964430), ('1.0e-40', - 92.10340371976182736071965818737456830404406)) SELECT x, bc_result, ln(x::numeric), ln(x::numeric) - bc_result AS diff FROM t; -- input very close to but smaller than 1 -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of LN(): -- -- for p in {1..40} -- do -- l=$(bc -ql <<< "scale=500 ; l(1-10^-$p)" | head -n 1) -- echo "('1.0e-$p', $l)," -- done WITH t ( x, bc_result ) AS ( VALUES ('1.0e-1', -.10536051565782630), ('1.0e-2', -.010050335853501441), ('1.0e-3', -.0010005003335835335), ('1.0e-4', -.00010000500033335834), ('1.0e-5', -.000010000050000333336), ('1.0e-6', -.0000010000005000003333), ('1.0e-7', -.00000010000000500000033), ('1.0e-8', -.000000010000000050000000), ('1.0e-9', -.0000000010000000005000000), ('1.0e-10', -.00000000010000000000500000), ('1.0e-11', -.000000000010000000000050000), ('1.0e-12', -.0000000000010000000000005000), ('1.0e-13', -.00000000000010000000000000500), ('1.0e-14', -.000000000000010000000000000050), ('1.0e-15', -.0000000000000010000000000000005), ('1.0e-16', -.00000000000000010000000000000001), ('1.0e-17', -.000000000000000010000000000000000), ('1.0e-18', -.0000000000000000010000000000000000), ('1.0e-19', -.00000000000000000010000000000000000), ('1.0e-20', -.000000000000000000010000000000000000), ('1.0e-21', -.0000000000000000000010000000000000000), ('1.0e-22', -.00000000000000000000010000000000000000), ('1.0e-23', -.000000000000000000000010000000000000000), ('1.0e-24', -.0000000000000000000000010000000000000000), ('1.0e-25', -.00000000000000000000000010000000000000000), ('1.0e-26', -.000000000000000000000000010000000000000000), ('1.0e-27', -.0000000000000000000000000010000000000000000), ('1.0e-28', -.00000000000000000000000000010000000000000000), ('1.0e-29', -.000000000000000000000000000010000000000000000), ('1.0e-30', -.0000000000000000000000000000010000000000000000), ('1.0e-31', -.00000000000000000000000000000010000000000000000), ('1.0e-32', -.000000000000000000000000000000010000000000000000), ('1.0e-33', -.0000000000000000000000000000000010000000000000000), ('1.0e-34', -.00000000000000000000000000000000010000000000000000), ('1.0e-35', -.000000000000000000000000000000000010000000000000000), ('1.0e-36', -.0000000000000000000000000000000000010000000000000000), ('1.0e-37', -.00000000000000000000000000000000000010000000000000000), ('1.0e-38', -.000000000000000000000000000000000000010000000000000000), ('1.0e-39', -.0000000000000000000000000000000000000010000000000000000), ('1.0e-40', -.00000000000000000000000000000000000000010000000000000000)) SELECT '1-' || x, bc_result, ln(1.0 - x::numeric), ln(1.0 - x::numeric) - bc_result AS diff FROM t; -- input very close to but larger than 1 -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of LN(): -- -- for p in {1..40} -- do -- l=$(bc -ql <<< "scale=500 ; l(1+10^-$p)" | head -n 1) -- echo "('1.0e-$p', $l)," -- done WITH t ( x, bc_result ) AS ( VALUES ('1.0e-1',.09531017980432486), ('1.0e-2',.009950330853168083), ('1.0e-3',.0009995003330835332), ('1.0e-4',.00009999500033330834), ('1.0e-5',.000009999950000333331), ('1.0e-6',.0000009999995000003333), ('1.0e-7',.00000009999999500000033), ('1.0e-8',.000000009999999950000000), ('1.0e-9',.0000000009999999995000000), ('1.0e-10',.00000000009999999999500000), ('1.0e-11',.000000000009999999999950000), ('1.0e-12',.0000000000009999999999995000), ('1.0e-13',.00000000000009999999999999500), ('1.0e-14',.000000000000009999999999999950), ('1.0e-15',.0000000000000009999999999999995), ('1.0e-16',.00000000000000010000000000000000), ('1.0e-17',.000000000000000010000000000000000), ('1.0e-18',.0000000000000000010000000000000000), ('1.0e-19',.00000000000000000010000000000000000), ('1.0e-20',.000000000000000000010000000000000000), ('1.0e-21',.0000000000000000000010000000000000000), ('1.0e-22',.00000000000000000000010000000000000000), ('1.0e-23',.000000000000000000000010000000000000000), ('1.0e-24',.0000000000000000000000010000000000000000), ('1.0e-25',.00000000000000000000000010000000000000000), ('1.0e-26',.000000000000000000000000010000000000000000), ('1.0e-27',.0000000000000000000000000010000000000000000), ('1.0e-28',.00000000000000000000000000010000000000000000), ('1.0e-29',.000000000000000000000000000010000000000000000), ('1.0e-30',.0000000000000000000000000000010000000000000000), ('1.0e-31',.00000000000000000000000000000010000000000000000), ('1.0e-32',.000000000000000000000000000000010000000000000000), ('1.0e-33',.0000000000000000000000000000000010000000000000000), ('1.0e-34',.00000000000000000000000000000000010000000000000000), ('1.0e-35',.000000000000000000000000000000000010000000000000000), ('1.0e-36',.0000000000000000000000000000000000010000000000000000), ('1.0e-37',.00000000000000000000000000000000000010000000000000000), ('1.0e-38',.000000000000000000000000000000000000010000000000000000), ('1.0e-39',.0000000000000000000000000000000000000010000000000000000), ('1.0e-40',.00000000000000000000000000000000000000010000000000000000)) SELECT '1+' || x, bc_result, ln(1.0 + x::numeric), ln(1.0 + x::numeric) - bc_result AS diff FROM t; -- input very large -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of LN(): -- -- for p in {1..40} -- do -- l=$(bc -ql <<< "scale=500 ; l(10^$p)" | head -n 1) -- echo "('1.0e$p', $l)," -- done WITH t ( x, bc_result ) AS ( VALUES ('1.0e1', 2.3025850929940457), ('1.0e2', 4.6051701859880914), ('1.0e3', 6.9077552789821371), ('1.0e4', 9.2103403719761827), ('1.0e5', 11.512925464970228), ('1.0e6', 13.815510557964274), ('1.0e7', 16.118095650958320), ('1.0e8', 18.420680743952365), ('1.0e9', 20.723265836946411), ('1.0e10', 23.025850929940457), ('1.0e11', 25.328436022934503), ('1.0e12', 27.631021115928548), ('1.0e13', 29.933606208922594), ('1.0e14', 32.236191301916640), ('1.0e15', 34.538776394910685), ('1.0e16', 36.841361487904731), ('1.0e17', 39.143946580898777), ('1.0e18', 41.446531673892822), ('1.0e19', 43.749116766886868), ('1.0e20', 46.051701859880914), ('1.0e21', 48.354286952874959), ('1.0e22', 50.656872045869005), ('1.0e23', 52.959457138863051), ('1.0e24', 55.262042231857096), ('1.0e25', 57.564627324851142), ('1.0e26', 59.867212417845188), ('1.0e27', 62.169797510839233), ('1.0e28', 64.472382603833279), ('1.0e29', 66.774967696827325), ('1.0e30', 69.077552789821371), ('1.0e31', 71.380137882815416), ('1.0e32', 73.682722975809462), ('1.0e33', 75.985308068803508), ('1.0e34', 78.287893161797553), ('1.0e35', 80.590478254791599), ('1.0e36', 82.893063347785645), ('1.0e37', 85.195648440779690), ('1.0e38', 87.498233533773736), ('1.0e39', 89.800818626767782), ('1.0e40', 92.103403719761827)) SELECT x, bc_result, ln(x::numeric), ln(x::numeric) - bc_result AS diff FROM t; -- input huge -- -- bc(1) results computed with a scale of 1000 and truncated using the script -- below, and then rounded by hand to match the precision of LN(): -- -- for p in {1..10} -- do -- l=$(bc -ql <<< "scale=1000 ; l(10^${p}00)" | head -n 1) -- echo "('1.0e${p}00', $l)," -- done WITH t ( x, bc_result ) AS ( VALUES ('1.0e100', 230.25850929940457), ('1.0e200', 460.51701859880914), ('1.0e300', 690.77552789821371), ('1.0e400', 921.03403719761827), ('1.0e500', 1151.2925464970228), ('1.0e600', 1381.5510557964274), ('1.0e700', 1611.8095650958320), ('1.0e800', 1842.0680743952365), ('1.0e900', 2072.3265836946411), ('1.0e1000', 2302.5850929940457)) SELECT x, bc_result, ln(x::numeric), ln(x::numeric) - bc_result AS diff FROM t; -- -- Tests for LOG() (base 10) -- -- input very small, exact result known WITH t ( x ) AS ( SELECT '1e-' || n FROM generate_series(1, 100) g (n)) SELECT x, log(x::numeric) FROM t; -- input very small, non-exact results -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of LN(): -- -- for p in {1..50..7} -- do -- for d in {9..1..3} -- do -- l=$(bc -ql <<< "scale=500 ; l($d*10^-$p) / l(10)" | head -n 1) -- echo "('${d}.0e-$p', $l)," -- done -- done WITH t ( x, bc_result ) AS ( VALUES ('9.0e-1', -.04575749056067513), ('6.0e-1', -.2218487496163564), ('3.0e-1', -.5228787452803376), ('9.0e-8', - 7.045757490560675), ('6.0e-8', - 7.221848749616356), ('3.0e-8', - 7.522878745280338), ('9.0e-15', - 14.0457574905606751), ('6.0e-15', - 14.2218487496163564), ('3.0e-15', - 14.5228787452803376), ('9.0e-22', - 21.04575749056067512540994), ('6.0e-22', - 21.22184874961635636749123), ('3.0e-22', - 21.52287874528033756270497), ('9.0e-29', - 28.045757490560675125409944193490), ('6.0e-29', - 28.221848749616356367491233202020), ('3.0e-29', - 28.522878745280337562704972096745), ('9.0e-36', - 35.0457574905606751254099441934897693816), ('6.0e-36', - 35.2218487496163563674912332020203916640), ('3.0e-36', - 35.5228787452803375627049720967448846908), ('9.0e-43', - 42.04575749056067512540994419348976938159974227), ('6.0e-43', - 42.22184874961635636749123320202039166403168125), ('3.0e-43', - 42.52287874528033756270497209674488469079987114), ('9.0e-50', - 49.045757490560675125409944193489769381599742271618608), ('6.0e-50', - 49.221848749616356367491233202020391664031681254347196), ('3.0e-50', - 49.522878745280337562704972096744884690799871135809304)) SELECT x, bc_result, log(x::numeric), log(x::numeric) - bc_result AS diff FROM t; -- input very close to but smaller than 1 -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of LN(): -- -- for p in {1..40..7} -- do -- for d in {9..1..3} -- do -- l=$(bc -ql <<< "scale=500 ; l(1-$d*10^-$p) / l(10)" | head -n 1) -- echo "('${d}.0e-$p', $l)," -- done -- done WITH t ( x, bc_result ) AS ( VALUES ('9.0e-1', - 1.0000000000000000), ('6.0e-1', -.3979400086720376), ('3.0e-1', -.1549019599857432), ('9.0e-8', -.000000039086505130185422), ('6.0e-8', -.000000026057669695925208), ('3.0e-8', -.000000013028834652530076), ('9.0e-15', -.0000000000000039086503371292840), ('6.0e-15', -.0000000000000026057668914195188), ('3.0e-15', -.0000000000000013028834457097574), ('9.0e-22', -.00000000000000000000039086503371292664), ('6.0e-22', -.00000000000000000000026057668914195110), ('3.0e-22', -.00000000000000000000013028834457097555), ('9.0e-29', -.000000000000000000000000000039086503371292664), ('6.0e-29', -.000000000000000000000000000026057668914195110), ('3.0e-29', -.000000000000000000000000000013028834457097555), ('9.0e-36', -.0000000000000000000000000000000000039086503371292664), ('6.0e-36', -.0000000000000000000000000000000000026057668914195110), ('3.0e-36', -.0000000000000000000000000000000000013028834457097555)) SELECT '1-' || x, bc_result, log(1.0 - x::numeric), log(1.0 - x::numeric) - bc_result AS diff FROM t; -- input very close to but larger than 1 -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of LN(): -- -- for p in {1..40..7} -- do -- for d in {9..1..3} -- do -- l=$(bc -ql <<< "scale=500 ; l(1+$d*10^-$p) / l(10)" | head -n 1) -- echo "('${d}.0e-$p', $l)," -- done -- done WITH t ( x, bc_result ) AS ( VALUES ('9.0e-1',.2787536009528290), ('6.0e-1',.2041199826559248), ('3.0e-1',.1139433523068368), ('9.0e-8',.000000039086501612400118), ('6.0e-8',.000000026057668132465074), ('3.0e-8',.000000013028834261665042), ('9.0e-15',.0000000000000039086503371292489), ('6.0e-15',.0000000000000026057668914195031), ('3.0e-15',.0000000000000013028834457097535), ('9.0e-22',.00000000000000000000039086503371292664), ('6.0e-22',.00000000000000000000026057668914195110), ('3.0e-22',.00000000000000000000013028834457097555), ('9.0e-29',.000000000000000000000000000039086503371292664), ('6.0e-29',.000000000000000000000000000026057668914195110), ('3.0e-29',.000000000000000000000000000013028834457097555), ('9.0e-36',.0000000000000000000000000000000000039086503371292664), ('6.0e-36',.0000000000000000000000000000000000026057668914195110), ('3.0e-36',.0000000000000000000000000000000000013028834457097555)) SELECT '1+' || x, bc_result, log(1.0 + x::numeric), log(1.0 + x::numeric) - bc_result AS diff FROM t; -- input very large, exact result known WITH t ( x ) AS ( SELECT '1e' || n FROM generate_series(1, 100) g (n)) SELECT x, log(x::numeric) FROM t; -- input very large, non-exact results -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of LN(): -- -- for p in {10..50..7} -- do -- for d in {2..9..3} -- do -- l=$(bc -ql <<< "scale=500 ; l($d*10^$p) / l(10)" | head -n 1) -- echo "('${d}.0e$p', $l)," -- done -- done WITH t ( x, bc_result ) AS ( VALUES ('2.0e10', 10.301029995663981), ('5.0e10', 10.698970004336019), ('8.0e10', 10.903089986991944), ('2.0e17', 17.301029995663981), ('5.0e17', 17.698970004336019), ('8.0e17', 17.903089986991944), ('2.0e24', 24.301029995663981), ('5.0e24', 24.698970004336019), ('8.0e24', 24.903089986991944), ('2.0e31', 31.301029995663981), ('5.0e31', 31.698970004336019), ('8.0e31', 31.903089986991944), ('2.0e38', 38.301029995663981), ('5.0e38', 38.698970004336019), ('8.0e38', 38.903089986991944), ('2.0e45', 45.30102999566398), ('5.0e45', 45.69897000433602), ('8.0e45', 45.90308998699194)) SELECT x, bc_result, log(x::numeric), log(x::numeric) - bc_result AS diff FROM t; pgFormatter-4.2/t/pg-test-files/expected/numerology.sql000066400000000000000000000045031361326045100233240ustar00rootroot00000000000000-- -- NUMEROLOGY -- Test various combinations of numeric types and functions. -- -- -- Test implicit type conversions -- This fails for Postgres v6.1 (and earlier?) -- so let's try explicit conversions for now - tgl 97/05/07 -- CREATE TABLE TEMP_FLOAT ( f1 FLOAT8 ); INSERT INTO TEMP_FLOAT (f1) SELECT float8(f1) FROM INT4_TBL; INSERT INTO TEMP_FLOAT (f1) SELECT float8(f1) FROM INT2_TBL; SELECT '' AS ten, f1 FROM TEMP_FLOAT ORDER BY f1; -- int4 CREATE TABLE TEMP_INT4 ( f1 int4 ); INSERT INTO TEMP_INT4 (f1) SELECT int4(f1) FROM FLOAT8_TBL WHERE (f1 > - 2147483647) AND (f1 < 2147483647); INSERT INTO TEMP_INT4 (f1) SELECT int4(f1) FROM INT2_TBL; SELECT '' AS nine, f1 FROM TEMP_INT4 ORDER BY f1; -- int2 CREATE TABLE TEMP_INT2 ( f1 int2 ); INSERT INTO TEMP_INT2 (f1) SELECT int2(f1) FROM FLOAT8_TBL WHERE (f1 >= - 32767) AND (f1 <= 32767); INSERT INTO TEMP_INT2 (f1) SELECT int2(f1) FROM INT4_TBL WHERE (f1 >= - 32767) AND (f1 <= 32767); SELECT '' AS five, f1 FROM TEMP_INT2 ORDER BY f1; -- -- Group-by combinations -- CREATE TABLE TEMP_GROUP ( f1 int4, f2 int4, f3 FLOAT8 ); INSERT INTO TEMP_GROUP SELECT 1, (- i.f1), (- f.f1) FROM INT4_TBL i, FLOAT8_TBL f; INSERT INTO TEMP_GROUP SELECT 2, i.f1, f.f1 FROM INT4_TBL i, FLOAT8_TBL f; SELECT DISTINCT f1 AS two FROM TEMP_GROUP ORDER BY 1; SELECT f1 AS two, max(f3) AS max_float, min(f3) AS min_float FROM TEMP_GROUP GROUP BY f1 ORDER BY two, max_float, min_float; -- GROUP BY a result column name is not legal per SQL92, but we accept it -- anyway (if the name is not the name of any column exposed by FROM). SELECT f1 AS two, max(f3) AS max_float, min(f3) AS min_float FROM TEMP_GROUP GROUP BY two ORDER BY two, max_float, min_float; SELECT f1 AS two, (max(f3) + 1) AS max_plus_1, (min(f3) - 1) AS min_minus_1 FROM TEMP_GROUP GROUP BY f1 ORDER BY two, min_minus_1; SELECT f1 AS two, max(f2) + min(f2) AS max_plus_min, min(f3) - 1 AS min_minus_1 FROM TEMP_GROUP GROUP BY f1 ORDER BY two, min_minus_1; DROP TABLE TEMP_INT2; DROP TABLE TEMP_INT4; DROP TABLE TEMP_FLOAT; DROP TABLE TEMP_GROUP; pgFormatter-4.2/t/pg-test-files/expected/object_address.sql000066400000000000000000000250001361326045100240720ustar00rootroot00000000000000-- -- Test for pg_get_object_address -- -- Clean up in case a prior regression run failed SET client_min_messages TO 'warning'; DROP ROLE IF EXISTS regress_addr_user; RESET client_min_messages; CREATE USER regress_addr_user; -- Test generic object addressing/identification functions CREATE SCHEMA addr_nsp; SET search_path TO 'addr_nsp'; CREATE FOREIGN DATA WRAPPER addr_fdw; CREATE SERVER addr_fserv FOREIGN DATA WRAPPER addr_fdw; CREATE TEXT SEARCH DICTIONARY addr_ts_dict ( TEMPLATE = simple ); CREATE TEXT SEARCH CONFIGURATION addr_ts_conf ( COPY = english ); CREATE TEXT SEARCH TEMPLATE addr_ts_temp ( lexize = dsimple_lexize ); CREATE TEXT SEARCH PARSER addr_ts_prs ( START = prsd_start, gettoken = prsd_nexttoken, END = prsd_end, lextypes = prsd_lextype ); CREATE TABLE addr_nsp.gentable ( a serial PRIMARY KEY CONSTRAINT a_chk CHECK (a > 0), b text DEFAULT 'hello' ); CREATE TABLE addr_nsp.parttable ( a int PRIMARY KEY ) PARTITION BY RANGE (a); CREATE VIEW addr_nsp.genview AS SELECT * FROM addr_nsp.gentable; CREATE MATERIALIZED VIEW addr_nsp.genmatview AS SELECT * FROM addr_nsp.gentable; CREATE TYPE addr_nsp.gencomptype AS ( a int ); CREATE TYPE addr_nsp.genenum AS ENUM ( 'one', 'two' ); CREATE FOREIGN TABLE addr_nsp.genftable ( a int) SERVER addr_fserv; CREATE AGGREGATE addr_nsp.genaggr (int4) ( SFUNC = int4pl, STYPE = int4 ); CREATE DOMAIN addr_nsp.gendomain AS int4 CONSTRAINT domconstr CHECK (value > 0); CREATE FUNCTION addr_nsp.trig () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN END; $$; CREATE TRIGGER t BEFORE INSERT ON addr_nsp.gentable FOR EACH ROW EXECUTE PROCEDURE addr_nsp.trig (); CREATE POLICY genpol ON addr_nsp.gentable; CREATE PROCEDURE addr_nsp.proc (int4) LANGUAGE SQL AS $$ $$; CREATE SERVER "integer" FOREIGN DATA WRAPPER addr_fdw; CREATE USER MAPPING FOR regress_addr_user SERVER "integer"; ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user IN SCHEMA public GRANT ALL ON TABLES TO regress_addr_user; ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user REVOKE DELETE ON TABLES FROM regress_addr_user; -- this transform would be quite unsafe to leave lying around, -- except that the SQL language pays no attention to transforms: CREATE TRANSFORM FOR int LANGUAGE SQL ( FROM SQL WITH FUNCTION prsd_lextype( internal ), TO SQL WITH FUNCTION int4recv( internal)); CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable; CREATE SUBSCRIPTION addr_sub CONNECTION '' PUBLICATION bar WITH ( connect = FALSE, slot_name = NONE ); CREATE STATISTICS addr_nsp.gentable_stat ON a, b FROM addr_nsp.gentable; -- test some error cases SELECT pg_get_object_address('stone', '{}', '{}'); SELECT pg_get_object_address('table', '{}', '{}'); SELECT pg_get_object_address('table', '{NULL}', '{}'); -- unrecognized object types DO $$ DECLARE objtype text; BEGIN FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'), ('toast table column'), ('view column'), ('materialized view column') LOOP BEGIN PERFORM pg_get_object_address(objtype, '{one}', '{}'); EXCEPTION WHEN invalid_parameter_value THEN RAISE WARNING 'error for %: %', objtype, sqlerrm; END; END LOOP; END; $$; -- miscellaneous other errors SELECT * FROM pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}'); SELECT * FROM pg_get_object_address('operator of access method', '{btree,integer_ops,99}', '{int4,int4}'); SELECT * FROM pg_get_object_address('function of access method', '{btree,integer_ops,1}', '{int4,bool}'); SELECT * FROM pg_get_object_address('function of access method', '{btree,integer_ops,99}', '{int4,int4}'); DO $$ DECLARE objtype text; names text[]; args text[]; BEGIN FOR objtype IN VALUES ('table'), ('index'), ('sequence'), ('view'), ('materialized view'), ('foreign table'), ('table column'), ('foreign table column'), ('aggregate'), ('function'), ('procedure'), ('type'), ('cast'), ('table constraint'), ('domain constraint'), ('conversion'), ('default value'), ('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'), ('text search parser'), ('text search dictionary'), ('text search template'), ('text search configuration'), ('policy'), ('user mapping'), ('default acl'), ('transform'), ('operator of access method'), ('function of access method'), ('publication relation') LOOP FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}') LOOP FOR args IN VALUES ('{}'), ('{integer}') LOOP BEGIN PERFORM pg_get_object_address(objtype, names, args); EXCEPTION WHEN OTHERS THEN RAISE WARNING 'error for %,%,%: %', objtype, names, args, sqlerrm; END; END LOOP; END LOOP; END LOOP; END; $$; -- these object types cannot be qualified names SELECT pg_get_object_address('language', '{one}', '{}'); SELECT pg_get_object_address('language', '{one,two}', '{}'); SELECT pg_get_object_address('large object', '{123}', '{}'); SELECT pg_get_object_address('large object', '{123,456}', '{}'); SELECT pg_get_object_address('large object', '{blargh}', '{}'); SELECT pg_get_object_address('schema', '{one}', '{}'); SELECT pg_get_object_address('schema', '{one,two}', '{}'); SELECT pg_get_object_address('role', '{one}', '{}'); SELECT pg_get_object_address('role', '{one,two}', '{}'); SELECT pg_get_object_address('database', '{one}', '{}'); SELECT pg_get_object_address('database', '{one,two}', '{}'); SELECT pg_get_object_address('tablespace', '{one}', '{}'); SELECT pg_get_object_address('tablespace', '{one,two}', '{}'); SELECT pg_get_object_address('foreign-data wrapper', '{one}', '{}'); SELECT pg_get_object_address('foreign-data wrapper', '{one,two}', '{}'); SELECT pg_get_object_address('server', '{one}', '{}'); SELECT pg_get_object_address('server', '{one,two}', '{}'); SELECT pg_get_object_address('extension', '{one}', '{}'); SELECT pg_get_object_address('extension', '{one,two}', '{}'); SELECT pg_get_object_address('event trigger', '{one}', '{}'); SELECT pg_get_object_address('event trigger', '{one,two}', '{}'); SELECT pg_get_object_address('access method', '{one}', '{}'); SELECT pg_get_object_address('access method', '{one,two}', '{}'); SELECT pg_get_object_address('publication', '{one}', '{}'); SELECT pg_get_object_address('publication', '{one,two}', '{}'); SELECT pg_get_object_address('subscription', '{one}', '{}'); SELECT pg_get_object_address('subscription', '{one,two}', '{}'); -- test successful cases WITH objects ( TYPE, name, args ) AS ( VALUES ('table', '{addr_nsp, gentable}'::text[], '{}'::text[]), ('table', '{addr_nsp, parttable}'::text[], '{}'::text[]), ('index', '{addr_nsp, gentable_pkey}', '{}'), ('index', '{addr_nsp, parttable_pkey}', '{}'), ('sequence', '{addr_nsp, gentable_a_seq}', '{}'), -- toast table ('view', '{addr_nsp, genview}', '{}'), ('materialized view', '{addr_nsp, genmatview}', '{}'), ('foreign table', '{addr_nsp, genftable}', '{}'), ('table column', '{addr_nsp, gentable, b}', '{}'), ('foreign table column', '{addr_nsp, genftable, a}', '{}'), ('aggregate', '{addr_nsp, genaggr}', '{int4}'), ('function', '{pg_catalog, pg_identify_object}', '{pg_catalog.oid, pg_catalog.oid, int4}'), ('procedure', '{addr_nsp, proc}', '{int4}'), ('type', '{pg_catalog._int4}', '{}'), ('type', '{addr_nsp.gendomain}', '{}'), ('type', '{addr_nsp.gencomptype}', '{}'), ('type', '{addr_nsp.genenum}', '{}'), ('cast', '{int8}', '{int4}'), ('collation', '{default}', '{}'), ('table constraint', '{addr_nsp, gentable, a_chk}', '{}'), ('domain constraint', '{addr_nsp.gendomain}', '{domconstr}'), ('conversion', '{pg_catalog, ascii_to_mic}', '{}'), ('default value', '{addr_nsp, gentable, b}', '{}'), ('language', '{plpgsql}', '{}'), -- large object ('operator', '{+}', '{int4, int4}'), ('operator class', '{btree, int4_ops}', '{}'), ('operator family', '{btree, integer_ops}', '{}'), ('operator of access method', '{btree,integer_ops,1}', '{integer,integer}'), ('function of access method', '{btree,integer_ops,2}', '{integer,integer}'), ('rule', '{addr_nsp, genview, _RETURN}', '{}'), ('trigger', '{addr_nsp, gentable, t}', '{}'), ('schema', '{addr_nsp}', '{}'), ('text search parser', '{addr_ts_prs}', '{}'), ('text search dictionary', '{addr_ts_dict}', '{}'), ('text search template', '{addr_ts_temp}', '{}'), ('text search configuration', '{addr_ts_conf}', '{}'), ('role', '{regress_addr_user}', '{}'), -- database -- tablespace ('foreign-data wrapper', '{addr_fdw}', '{}'), ('server', '{addr_fserv}', '{}'), ('user mapping', '{regress_addr_user}', '{integer}'), ('default acl', '{regress_addr_user,public}', '{r}'), ('default acl', '{regress_addr_user}', '{r}'), -- extension -- event trigger ('policy', '{addr_nsp, gentable, genpol}', '{}'), ('transform', '{int}', '{sql}'), ('access method', '{btree}', '{}'), ('publication', '{addr_pub}', '{}'), ('publication relation', '{addr_nsp, gentable}', '{addr_pub}'), ('subscription', '{addr_sub}', '{}'), ('statistics object', '{addr_nsp, gentable_stat}', '{}')) SELECT (pg_identify_object (addr1.classid, addr1.objid, addr1.objsubid)).*, -- test roundtrip through pg_identify_object_as_address ROW (pg_identify_object (addr1.classid, addr1.objid, addr1.objsubid)) = ROW (pg_identify_object (addr2.classid, addr2.objid, addr2.objsubid)) FROM objects, pg_get_object_address(TYPE, name, args) addr1, pg_identify_object_as_address(classid, objid, objsubid) ioa (typ, nms, args), pg_get_object_address(typ, nms, ioa.args) AS addr2 ORDER BY addr1.classid, addr1.objid, addr1.objsubid; --- --- Cleanup resources --- DROP FOREIGN DATA WRAPPER addr_fdw CASCADE; DROP PUBLICATION addr_pub; DROP SUBSCRIPTION addr_sub; DROP SCHEMA addr_nsp CASCADE; DROP OWNED BY regress_addr_user; DROP USER regress_addr_user; pgFormatter-4.2/t/pg-test-files/expected/oid.sql000066400000000000000000000030051361326045100216730ustar00rootroot00000000000000-- -- OID -- CREATE TABLE OID_TBL ( f1 oid ); INSERT INTO OID_TBL (f1) VALUES ('1234'); INSERT INTO OID_TBL (f1) VALUES ('1235'); INSERT INTO OID_TBL (f1) VALUES ('987'); INSERT INTO OID_TBL (f1) VALUES ('-1040'); INSERT INTO OID_TBL (f1) VALUES ('99999999'); INSERT INTO OID_TBL (f1) VALUES ('5 '); INSERT INTO OID_TBL (f1) VALUES (' 10 '); -- leading/trailing hard tab is also allowed INSERT INTO OID_TBL (f1) VALUES (' 15 '); -- bad inputs INSERT INTO OID_TBL (f1) VALUES (''); INSERT INTO OID_TBL (f1) VALUES (' '); INSERT INTO OID_TBL (f1) VALUES ('asdfasd'); INSERT INTO OID_TBL (f1) VALUES ('99asdfasd'); INSERT INTO OID_TBL (f1) VALUES ('5 d'); INSERT INTO OID_TBL (f1) VALUES (' 5d'); INSERT INTO OID_TBL (f1) VALUES ('5 5'); INSERT INTO OID_TBL (f1) VALUES (' - 500'); INSERT INTO OID_TBL (f1) VALUES ('32958209582039852935'); INSERT INTO OID_TBL (f1) VALUES ('-23582358720398502385'); SELECT '' AS six, * FROM OID_TBL; SELECT '' AS one, o.* FROM OID_TBL o WHERE o.f1 = 1234; SELECT '' AS five, o.* FROM OID_TBL o WHERE o.f1 <> '1234'; SELECT '' AS three, o.* FROM OID_TBL o WHERE o.f1 <= '1234'; SELECT '' AS two, o.* FROM OID_TBL o WHERE o.f1 < '1234'; SELECT '' AS four, o.* FROM OID_TBL o WHERE o.f1 >= '1234'; SELECT '' AS three, o.* FROM OID_TBL o WHERE o.f1 > '1234'; DROP TABLE OID_TBL; pgFormatter-4.2/t/pg-test-files/expected/oidjoins.sql000066400000000000000000001205501361326045100227430ustar00rootroot00000000000000-- -- This is created by pgsql/src/tools/findoidjoins/make_oidjoins_check -- SELECT ctid, aggfnoid FROM pg_catalog.pg_aggregate fk WHERE aggfnoid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggfnoid); SELECT ctid, aggtransfn FROM pg_catalog.pg_aggregate fk WHERE aggtransfn != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggtransfn); SELECT ctid, aggfinalfn FROM pg_catalog.pg_aggregate fk WHERE aggfinalfn != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggfinalfn); SELECT ctid, aggcombinefn FROM pg_catalog.pg_aggregate fk WHERE aggcombinefn != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggcombinefn); SELECT ctid, aggserialfn FROM pg_catalog.pg_aggregate fk WHERE aggserialfn != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggserialfn); SELECT ctid, aggdeserialfn FROM pg_catalog.pg_aggregate fk WHERE aggdeserialfn != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggdeserialfn); SELECT ctid, aggmtransfn FROM pg_catalog.pg_aggregate fk WHERE aggmtransfn != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggmtransfn); SELECT ctid, aggminvtransfn FROM pg_catalog.pg_aggregate fk WHERE aggminvtransfn != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggminvtransfn); SELECT ctid, aggmfinalfn FROM pg_catalog.pg_aggregate fk WHERE aggmfinalfn != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggmfinalfn); SELECT ctid, aggsortop FROM pg_catalog.pg_aggregate fk WHERE aggsortop != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.aggsortop); SELECT ctid, aggtranstype FROM pg_catalog.pg_aggregate fk WHERE aggtranstype != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.aggtranstype); SELECT ctid, aggmtranstype FROM pg_catalog.pg_aggregate fk WHERE aggmtranstype != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.aggmtranstype); SELECT ctid, amhandler FROM pg_catalog.pg_am fk WHERE amhandler != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amhandler); SELECT ctid, amopfamily FROM pg_catalog.pg_amop fk WHERE amopfamily != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_opfamily pk WHERE pk.oid = fk.amopfamily); SELECT ctid, amoplefttype FROM pg_catalog.pg_amop fk WHERE amoplefttype != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.amoplefttype); SELECT ctid, amoprighttype FROM pg_catalog.pg_amop fk WHERE amoprighttype != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.amoprighttype); SELECT ctid, amopopr FROM pg_catalog.pg_amop fk WHERE amopopr != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.amopopr); SELECT ctid, amopmethod FROM pg_catalog.pg_amop fk WHERE amopmethod != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_am pk WHERE pk.oid = fk.amopmethod); SELECT ctid, amopsortfamily FROM pg_catalog.pg_amop fk WHERE amopsortfamily != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_opfamily pk WHERE pk.oid = fk.amopsortfamily); SELECT ctid, amprocfamily FROM pg_catalog.pg_amproc fk WHERE amprocfamily != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_opfamily pk WHERE pk.oid = fk.amprocfamily); SELECT ctid, amproclefttype FROM pg_catalog.pg_amproc fk WHERE amproclefttype != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.amproclefttype); SELECT ctid, amprocrighttype FROM pg_catalog.pg_amproc fk WHERE amprocrighttype != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.amprocrighttype); SELECT ctid, amproc FROM pg_catalog.pg_amproc fk WHERE amproc != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amproc); SELECT ctid, adrelid FROM pg_catalog.pg_attrdef fk WHERE adrelid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.adrelid); SELECT ctid, attrelid FROM pg_catalog.pg_attribute fk WHERE attrelid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.attrelid); SELECT ctid, atttypid FROM pg_catalog.pg_attribute fk WHERE atttypid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.atttypid); SELECT ctid, attcollation FROM pg_catalog.pg_attribute fk WHERE attcollation != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_collation pk WHERE pk.oid = fk.attcollation); SELECT ctid, roleid FROM pg_catalog.pg_auth_members fk WHERE roleid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.roleid); SELECT ctid, member FROM pg_catalog.pg_auth_members fk WHERE member != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.member); SELECT ctid, grantor FROM pg_catalog.pg_auth_members fk WHERE grantor != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.grantor); SELECT ctid, castsource FROM pg_catalog.pg_cast fk WHERE castsource != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.castsource); SELECT ctid, casttarget FROM pg_catalog.pg_cast fk WHERE casttarget != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.casttarget); SELECT ctid, castfunc FROM pg_catalog.pg_cast fk WHERE castfunc != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.castfunc); SELECT ctid, relnamespace FROM pg_catalog.pg_class fk WHERE relnamespace != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.relnamespace); SELECT ctid, reltype FROM pg_catalog.pg_class fk WHERE reltype != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.reltype); SELECT ctid, reloftype FROM pg_catalog.pg_class fk WHERE reloftype != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.reloftype); SELECT ctid, relowner FROM pg_catalog.pg_class fk WHERE relowner != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.relowner); SELECT ctid, relam FROM pg_catalog.pg_class fk WHERE relam != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_am pk WHERE pk.oid = fk.relam); SELECT ctid, reltablespace FROM pg_catalog.pg_class fk WHERE reltablespace != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_tablespace pk WHERE pk.oid = fk.reltablespace); SELECT ctid, reltoastrelid FROM pg_catalog.pg_class fk WHERE reltoastrelid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.reltoastrelid); SELECT ctid, collnamespace FROM pg_catalog.pg_collation fk WHERE collnamespace != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.collnamespace); SELECT ctid, collowner FROM pg_catalog.pg_collation fk WHERE collowner != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.collowner); SELECT ctid, connamespace FROM pg_catalog.pg_constraint fk WHERE connamespace != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.connamespace); SELECT ctid, conrelid FROM pg_catalog.pg_constraint fk WHERE conrelid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.conrelid); SELECT ctid, contypid FROM pg_catalog.pg_constraint fk WHERE contypid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.contypid); SELECT ctid, conindid FROM pg_catalog.pg_constraint fk WHERE conindid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.conindid); SELECT ctid, conparentid FROM pg_catalog.pg_constraint fk WHERE conparentid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_constraint pk WHERE pk.oid = fk.conparentid); SELECT ctid, confrelid FROM pg_catalog.pg_constraint fk WHERE confrelid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.confrelid); SELECT ctid, connamespace FROM pg_catalog.pg_conversion fk WHERE connamespace != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.connamespace); SELECT ctid, conowner FROM pg_catalog.pg_conversion fk WHERE conowner != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.conowner); SELECT ctid, conproc FROM pg_catalog.pg_conversion fk WHERE conproc != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.conproc); SELECT ctid, datdba FROM pg_catalog.pg_database fk WHERE datdba != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.datdba); SELECT ctid, dattablespace FROM pg_catalog.pg_database fk WHERE dattablespace != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_tablespace pk WHERE pk.oid = fk.dattablespace); SELECT ctid, setdatabase FROM pg_catalog.pg_db_role_setting fk WHERE setdatabase != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_database pk WHERE pk.oid = fk.setdatabase); SELECT ctid, classid FROM pg_catalog.pg_depend fk WHERE classid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.classid); SELECT ctid, refclassid FROM pg_catalog.pg_depend fk WHERE refclassid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.refclassid); SELECT ctid, classoid FROM pg_catalog.pg_description fk WHERE classoid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.classoid); SELECT ctid, enumtypid FROM pg_catalog.pg_enum fk WHERE enumtypid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.enumtypid); SELECT ctid, extowner FROM pg_catalog.pg_extension fk WHERE extowner != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.extowner); SELECT ctid, extnamespace FROM pg_catalog.pg_extension fk WHERE extnamespace != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.extnamespace); SELECT ctid, fdwowner FROM pg_catalog.pg_foreign_data_wrapper fk WHERE fdwowner != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.fdwowner); SELECT ctid, srvowner FROM pg_catalog.pg_foreign_server fk WHERE srvowner != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.srvowner); SELECT ctid, srvfdw FROM pg_catalog.pg_foreign_server fk WHERE srvfdw != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_foreign_data_wrapper pk WHERE pk.oid = fk.srvfdw); SELECT ctid, indexrelid FROM pg_catalog.pg_index fk WHERE indexrelid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.indexrelid); SELECT ctid, indrelid FROM pg_catalog.pg_index fk WHERE indrelid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.indrelid); SELECT ctid, inhrelid FROM pg_catalog.pg_inherits fk WHERE inhrelid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.inhrelid); SELECT ctid, inhparent FROM pg_catalog.pg_inherits fk WHERE inhparent != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.inhparent); SELECT ctid, classoid FROM pg_catalog.pg_init_privs fk WHERE classoid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.classoid); SELECT ctid, lanowner FROM pg_catalog.pg_language fk WHERE lanowner != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.lanowner); SELECT ctid, lanplcallfoid FROM pg_catalog.pg_language fk WHERE lanplcallfoid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.lanplcallfoid); SELECT ctid, laninline FROM pg_catalog.pg_language fk WHERE laninline != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.laninline); SELECT ctid, lanvalidator FROM pg_catalog.pg_language fk WHERE lanvalidator != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.lanvalidator); SELECT ctid, loid FROM pg_catalog.pg_largeobject fk WHERE loid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_largeobject_metadata pk WHERE pk.oid = fk.loid); SELECT ctid, lomowner FROM pg_catalog.pg_largeobject_metadata fk WHERE lomowner != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.lomowner); SELECT ctid, nspowner FROM pg_catalog.pg_namespace fk WHERE nspowner != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.nspowner); SELECT ctid, opcmethod FROM pg_catalog.pg_opclass fk WHERE opcmethod != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_am pk WHERE pk.oid = fk.opcmethod); SELECT ctid, opcnamespace FROM pg_catalog.pg_opclass fk WHERE opcnamespace != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.opcnamespace); SELECT ctid, opcowner FROM pg_catalog.pg_opclass fk WHERE opcowner != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.opcowner); SELECT ctid, opcfamily FROM pg_catalog.pg_opclass fk WHERE opcfamily != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_opfamily pk WHERE pk.oid = fk.opcfamily); SELECT ctid, opcintype FROM pg_catalog.pg_opclass fk WHERE opcintype != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.opcintype); SELECT ctid, opckeytype FROM pg_catalog.pg_opclass fk WHERE opckeytype != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.opckeytype); SELECT ctid, oprnamespace FROM pg_catalog.pg_operator fk WHERE oprnamespace != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.oprnamespace); SELECT ctid, oprowner FROM pg_catalog.pg_operator fk WHERE oprowner != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.oprowner); SELECT ctid, oprleft FROM pg_catalog.pg_operator fk WHERE oprleft != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.oprleft); SELECT ctid, oprright FROM pg_catalog.pg_operator fk WHERE oprright != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.oprright); SELECT ctid, oprresult FROM pg_catalog.pg_operator fk WHERE oprresult != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.oprresult); SELECT ctid, oprcom FROM pg_catalog.pg_operator fk WHERE oprcom != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.oprcom); SELECT ctid, oprnegate FROM pg_catalog.pg_operator fk WHERE oprnegate != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.oprnegate); SELECT ctid, oprcode FROM pg_catalog.pg_operator fk WHERE oprcode != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.oprcode); SELECT ctid, oprrest FROM pg_catalog.pg_operator fk WHERE oprrest != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.oprrest); SELECT ctid, oprjoin FROM pg_catalog.pg_operator fk WHERE oprjoin != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.oprjoin); SELECT ctid, opfmethod FROM pg_catalog.pg_opfamily fk WHERE opfmethod != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_am pk WHERE pk.oid = fk.opfmethod); SELECT ctid, opfnamespace FROM pg_catalog.pg_opfamily fk WHERE opfnamespace != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.opfnamespace); SELECT ctid, opfowner FROM pg_catalog.pg_opfamily fk WHERE opfowner != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.opfowner); SELECT ctid, partrelid FROM pg_catalog.pg_partitioned_table fk WHERE partrelid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.partrelid); SELECT ctid, partdefid FROM pg_catalog.pg_partitioned_table fk WHERE partdefid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.partdefid); SELECT ctid, polrelid FROM pg_catalog.pg_policy fk WHERE polrelid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.polrelid); SELECT ctid, pronamespace FROM pg_catalog.pg_proc fk WHERE pronamespace != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.pronamespace); SELECT ctid, proowner FROM pg_catalog.pg_proc fk WHERE proowner != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.proowner); SELECT ctid, prolang FROM pg_catalog.pg_proc fk WHERE prolang != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_language pk WHERE pk.oid = fk.prolang); SELECT ctid, provariadic FROM pg_catalog.pg_proc fk WHERE provariadic != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.provariadic); SELECT ctid, prosupport FROM pg_catalog.pg_proc fk WHERE prosupport != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prosupport); SELECT ctid, prorettype FROM pg_catalog.pg_proc fk WHERE prorettype != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.prorettype); SELECT ctid, rngtypid FROM pg_catalog.pg_range fk WHERE rngtypid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.rngtypid); SELECT ctid, rngsubtype FROM pg_catalog.pg_range fk WHERE rngsubtype != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.rngsubtype); SELECT ctid, rngcollation FROM pg_catalog.pg_range fk WHERE rngcollation != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_collation pk WHERE pk.oid = fk.rngcollation); SELECT ctid, rngsubopc FROM pg_catalog.pg_range fk WHERE rngsubopc != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_opclass pk WHERE pk.oid = fk.rngsubopc); SELECT ctid, rngcanonical FROM pg_catalog.pg_range fk WHERE rngcanonical != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.rngcanonical); SELECT ctid, rngsubdiff FROM pg_catalog.pg_range fk WHERE rngsubdiff != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.rngsubdiff); SELECT ctid, ev_class FROM pg_catalog.pg_rewrite fk WHERE ev_class != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.ev_class); SELECT ctid, seqrelid FROM pg_catalog.pg_sequence fk WHERE seqrelid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.seqrelid); SELECT ctid, seqtypid FROM pg_catalog.pg_sequence fk WHERE seqtypid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.seqtypid); SELECT ctid, refclassid FROM pg_catalog.pg_shdepend fk WHERE refclassid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.refclassid); SELECT ctid, classoid FROM pg_catalog.pg_shdescription fk WHERE classoid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.classoid); SELECT ctid, starelid FROM pg_catalog.pg_statistic fk WHERE starelid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.starelid); SELECT ctid, staop1 FROM pg_catalog.pg_statistic fk WHERE staop1 != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.staop1); SELECT ctid, staop2 FROM pg_catalog.pg_statistic fk WHERE staop2 != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.staop2); SELECT ctid, staop3 FROM pg_catalog.pg_statistic fk WHERE staop3 != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.staop3); SELECT ctid, staop4 FROM pg_catalog.pg_statistic fk WHERE staop4 != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.staop4); SELECT ctid, staop5 FROM pg_catalog.pg_statistic fk WHERE staop5 != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.staop5); SELECT ctid, stxrelid FROM pg_catalog.pg_statistic_ext fk WHERE stxrelid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.stxrelid); SELECT ctid, stxnamespace FROM pg_catalog.pg_statistic_ext fk WHERE stxnamespace != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.stxnamespace); SELECT ctid, stxowner FROM pg_catalog.pg_statistic_ext fk WHERE stxowner != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.stxowner); SELECT ctid, spcowner FROM pg_catalog.pg_tablespace fk WHERE spcowner != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.spcowner); SELECT ctid, trftype FROM pg_catalog.pg_transform fk WHERE trftype != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.trftype); SELECT ctid, trflang FROM pg_catalog.pg_transform fk WHERE trflang != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_language pk WHERE pk.oid = fk.trflang); SELECT ctid, trffromsql FROM pg_catalog.pg_transform fk WHERE trffromsql != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.trffromsql); SELECT ctid, trftosql FROM pg_catalog.pg_transform fk WHERE trftosql != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.trftosql); SELECT ctid, tgrelid FROM pg_catalog.pg_trigger fk WHERE tgrelid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.tgrelid); SELECT ctid, tgfoid FROM pg_catalog.pg_trigger fk WHERE tgfoid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.tgfoid); SELECT ctid, tgconstrrelid FROM pg_catalog.pg_trigger fk WHERE tgconstrrelid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.tgconstrrelid); SELECT ctid, tgconstrindid FROM pg_catalog.pg_trigger fk WHERE tgconstrindid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.tgconstrindid); SELECT ctid, tgconstraint FROM pg_catalog.pg_trigger fk WHERE tgconstraint != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_constraint pk WHERE pk.oid = fk.tgconstraint); SELECT ctid, cfgnamespace FROM pg_catalog.pg_ts_config fk WHERE cfgnamespace != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.cfgnamespace); SELECT ctid, cfgowner FROM pg_catalog.pg_ts_config fk WHERE cfgowner != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.cfgowner); SELECT ctid, cfgparser FROM pg_catalog.pg_ts_config fk WHERE cfgparser != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_ts_parser pk WHERE pk.oid = fk.cfgparser); SELECT ctid, mapcfg FROM pg_catalog.pg_ts_config_map fk WHERE mapcfg != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_ts_config pk WHERE pk.oid = fk.mapcfg); SELECT ctid, mapdict FROM pg_catalog.pg_ts_config_map fk WHERE mapdict != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_ts_dict pk WHERE pk.oid = fk.mapdict); SELECT ctid, dictnamespace FROM pg_catalog.pg_ts_dict fk WHERE dictnamespace != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.dictnamespace); SELECT ctid, dictowner FROM pg_catalog.pg_ts_dict fk WHERE dictowner != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.dictowner); SELECT ctid, dicttemplate FROM pg_catalog.pg_ts_dict fk WHERE dicttemplate != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_ts_template pk WHERE pk.oid = fk.dicttemplate); SELECT ctid, prsnamespace FROM pg_catalog.pg_ts_parser fk WHERE prsnamespace != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.prsnamespace); SELECT ctid, prsstart FROM pg_catalog.pg_ts_parser fk WHERE prsstart != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prsstart); SELECT ctid, prstoken FROM pg_catalog.pg_ts_parser fk WHERE prstoken != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prstoken); SELECT ctid, prsend FROM pg_catalog.pg_ts_parser fk WHERE prsend != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prsend); SELECT ctid, prsheadline FROM pg_catalog.pg_ts_parser fk WHERE prsheadline != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prsheadline); SELECT ctid, prslextype FROM pg_catalog.pg_ts_parser fk WHERE prslextype != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prslextype); SELECT ctid, tmplnamespace FROM pg_catalog.pg_ts_template fk WHERE tmplnamespace != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.tmplnamespace); SELECT ctid, tmplinit FROM pg_catalog.pg_ts_template fk WHERE tmplinit != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.tmplinit); SELECT ctid, tmpllexize FROM pg_catalog.pg_ts_template fk WHERE tmpllexize != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.tmpllexize); SELECT ctid, typnamespace FROM pg_catalog.pg_type fk WHERE typnamespace != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.typnamespace); SELECT ctid, typowner FROM pg_catalog.pg_type fk WHERE typowner != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.typowner); SELECT ctid, typrelid FROM pg_catalog.pg_type fk WHERE typrelid != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.typrelid); SELECT ctid, typelem FROM pg_catalog.pg_type fk WHERE typelem != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.typelem); SELECT ctid, typarray FROM pg_catalog.pg_type fk WHERE typarray != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.typarray); SELECT ctid, typinput FROM pg_catalog.pg_type fk WHERE typinput != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typinput); SELECT ctid, typoutput FROM pg_catalog.pg_type fk WHERE typoutput != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typoutput); SELECT ctid, typreceive FROM pg_catalog.pg_type fk WHERE typreceive != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typreceive); SELECT ctid, typsend FROM pg_catalog.pg_type fk WHERE typsend != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typsend); SELECT ctid, typmodin FROM pg_catalog.pg_type fk WHERE typmodin != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typmodin); SELECT ctid, typmodout FROM pg_catalog.pg_type fk WHERE typmodout != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typmodout); SELECT ctid, typanalyze FROM pg_catalog.pg_type fk WHERE typanalyze != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typanalyze); SELECT ctid, typbasetype FROM pg_catalog.pg_type fk WHERE typbasetype != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.typbasetype); SELECT ctid, typcollation FROM pg_catalog.pg_type fk WHERE typcollation != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_collation pk WHERE pk.oid = fk.typcollation); SELECT ctid, conpfeqop FROM ( SELECT ctid, unnest(conpfeqop) AS conpfeqop FROM pg_catalog.pg_constraint) fk WHERE conpfeqop != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.conpfeqop); SELECT ctid, conppeqop FROM ( SELECT ctid, unnest(conppeqop) AS conppeqop FROM pg_catalog.pg_constraint) fk WHERE conppeqop != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.conppeqop); SELECT ctid, conffeqop FROM ( SELECT ctid, unnest(conffeqop) AS conffeqop FROM pg_catalog.pg_constraint) fk WHERE conffeqop != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.conffeqop); SELECT ctid, conexclop FROM ( SELECT ctid, unnest(conexclop) AS conexclop FROM pg_catalog.pg_constraint) fk WHERE conexclop != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.conexclop); SELECT ctid, proallargtypes FROM ( SELECT ctid, unnest(proallargtypes) AS proallargtypes FROM pg_catalog.pg_proc) fk WHERE proallargtypes != 0 AND NOT EXISTS ( SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.proallargtypes); pgFormatter-4.2/t/pg-test-files/expected/opr_sanity.sql000066400000000000000000001655331361326045100233260ustar00rootroot00000000000000-- -- OPR_SANITY -- Sanity checks for common errors in making operator/procedure system tables: -- pg_operator, pg_proc, pg_cast, pg_conversion, pg_aggregate, pg_am, -- pg_amop, pg_amproc, pg_opclass, pg_opfamily, pg_index. -- -- Every test failure in this file should be closely inspected. -- The description of the failing test should be read carefully before -- adjusting the expected output. In most cases, the queries should -- not find *any* matching entries. -- -- NB: we assume the oidjoins test will have caught any dangling links, -- that is OID or REGPROC fields that are not zero and do not match some -- row in the linked-to table. However, if we want to enforce that a link -- field can't be 0, we have to check it here. -- -- NB: run this test earlier than the create_operator test, because -- that test creates some bogus operators... -- Helper functions to deal with cases where binary-coercible matches are -- allowed. -- This should match IsBinaryCoercible() in parse_coerce.c. -- It doesn't currently know about some cases, notably domains, anyelement, -- anynonarray, anyenum, or record, but it doesn't need to (yet). CREATE FUNCTION binary_coercible (oid, oid) RETURNS bool AS $$ BEGIN IF $1 = $2 THEN RETURN TRUE; END IF; IF EXISTS ( SELECT 1 FROM pg_catalog.pg_cast WHERE castsource = $1 AND casttarget = $2 AND castmethod = 'b' AND castcontext = 'i') THEN RETURN TRUE; END IF; IF $2 = 'pg_catalog.any'::pg_catalog.regtype THEN RETURN TRUE; END IF; IF $2 = 'pg_catalog.anyarray'::pg_catalog.regtype THEN IF EXISTS ( SELECT 1 FROM pg_catalog.pg_type WHERE oid = $1 AND typelem != 0 AND typlen = - 1) THEN RETURN TRUE; END IF; END IF; IF $2 = 'pg_catalog.anyrange'::pg_catalog.regtype THEN IF ( SELECT typtype FROM pg_catalog.pg_type WHERE oid = $1) = 'r' THEN RETURN TRUE; END IF; END IF; RETURN FALSE; END $$ LANGUAGE plpgsql STRICT STABLE; -- This one ignores castcontext, so it will allow cases where an explicit -- (but still binary) cast would be required to convert the input type. -- We don't currently use this for any tests in this file, but it is a -- reasonable alternative definition for some scenarios. CREATE FUNCTION explicitly_binary_coercible (oid, oid) RETURNS bool AS $$ BEGIN IF $1 = $2 THEN RETURN TRUE; END IF; IF EXISTS ( SELECT 1 FROM pg_catalog.pg_cast WHERE castsource = $1 AND casttarget = $2 AND castmethod = 'b') THEN RETURN TRUE; END IF; IF $2 = 'pg_catalog.any'::pg_catalog.regtype THEN RETURN TRUE; END IF; IF $2 = 'pg_catalog.anyarray'::pg_catalog.regtype THEN IF EXISTS ( SELECT 1 FROM pg_catalog.pg_type WHERE oid = $1 AND typelem != 0 AND typlen = - 1) THEN RETURN TRUE; END IF; END IF; IF $2 = 'pg_catalog.anyrange'::pg_catalog.regtype THEN IF ( SELECT typtype FROM pg_catalog.pg_type WHERE oid = $1) = 'r' THEN RETURN TRUE; END IF; END IF; RETURN FALSE; END $$ LANGUAGE plpgsql STRICT STABLE; -- **************** pg_proc **************** -- Look for illegal values in pg_proc fields. SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE p1.prolang = 0 OR p1.prorettype = 0 OR p1.pronargs < 0 OR p1.pronargdefaults < 0 OR p1.pronargdefaults > p1.pronargs OR array_lower(p1.proargtypes, 1) != 0 OR array_upper(p1.proargtypes, 1) != p1.pronargs - 1 OR 0::oid = ANY (p1.proargtypes) OR procost <= 0 OR CASE WHEN proretset THEN prorows <= 0 ELSE prorows != 0 END OR prokind NOT IN ('f', 'a', 'w', 'p') OR provolatile NOT IN ('i', 's', 'v') OR proparallel NOT IN ('s', 'r', 'u'); -- prosrc should never be null or empty SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE prosrc IS NULL OR prosrc = '' OR prosrc = '-'; -- proretset should only be set for normal functions SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE proretset AND prokind != 'f'; -- currently, no built-in functions should be SECURITY DEFINER; -- this might change in future, but there will probably never be many. SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE prosecdef ORDER BY 1; -- pronargdefaults should be 0 iff proargdefaults is null SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE (pronargdefaults <> 0) != (proargdefaults IS NOT NULL); -- probin should be non-empty for C functions, null everywhere else SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE prolang = 13 AND (probin IS NULL OR probin = '' OR probin = '-'); SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE prolang != 13 AND probin IS NOT NULL; -- Look for conflicting proc definitions (same names and input datatypes). -- (This test should be dead code now that we have the unique index -- pg_proc_proname_args_nsp_index, but I'll leave it in anyway.) SELECT p1.oid, p1.proname, p2.oid, p2.proname FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.proname = p2.proname AND p1.pronargs = p2.pronargs AND p1.proargtypes = p2.proargtypes; -- Considering only built-in procs (prolang = 12), look for multiple uses -- of the same internal function (ie, matching prosrc fields). It's OK to -- have several entries with different pronames for the same internal function, -- but conflicts in the number of arguments and other critical items should -- be complained of. (We don't check data types here; see next query.) -- Note: ignore aggregate functions here, since they all point to the same -- dummy built-in function. SELECT p1.oid, p1.proname, p2.oid, p2.proname FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid < p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND (p1.prokind != 'a' OR p2.prokind != 'a') AND (p1.prolang != p2.prolang OR p1.prokind != p2.prokind OR p1.prosecdef != p2.prosecdef OR p1.proleakproof != p2.proleakproof OR p1.proisstrict != p2.proisstrict OR p1.proretset != p2.proretset OR p1.provolatile != p2.provolatile OR p1.pronargs != p2.pronargs); -- Look for uses of different type OIDs in the argument/result type fields -- for different aliases of the same built-in function. -- This indicates that the types are being presumed to be binary-equivalent, -- or that the built-in function is prepared to deal with different types. -- That's not wrong, necessarily, but we make lists of all the types being -- so treated. Note that the expected output of this part of the test will -- need to be modified whenever new pairs of types are made binary-equivalent, -- or when new polymorphic built-in functions are added! -- Note: ignore aggregate functions here, since they all point to the same -- dummy built-in function. Likewise, ignore range constructor functions. SELECT DISTINCT p1.prorettype, p2.prorettype FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND p1.prokind != 'a' AND p2.prokind != 'a' AND p1.prosrc NOT LIKE E'range\\_constructor_' AND p2.prosrc NOT LIKE E'range\\_constructor_' AND (p1.prorettype < p2.prorettype) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0] FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND p1.prokind != 'a' AND p2.prokind != 'a' AND p1.prosrc NOT LIKE E'range\\_constructor_' AND p2.prosrc NOT LIKE E'range\\_constructor_' AND (p1.proargtypes[0] < p2.proargtypes[0]) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1] FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND p1.prokind != 'a' AND p2.prokind != 'a' AND p1.prosrc NOT LIKE E'range\\_constructor_' AND p2.prosrc NOT LIKE E'range\\_constructor_' AND (p1.proargtypes[1] < p2.proargtypes[1]) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2] FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND p1.prokind != 'a' AND p2.prokind != 'a' AND (p1.proargtypes[2] < p2.proargtypes[2]) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[3], p2.proargtypes[3] FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND p1.prokind != 'a' AND p2.prokind != 'a' AND (p1.proargtypes[3] < p2.proargtypes[3]) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[4], p2.proargtypes[4] FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND p1.prokind != 'a' AND p2.prokind != 'a' AND (p1.proargtypes[4] < p2.proargtypes[4]) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[5], p2.proargtypes[5] FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND p1.prokind != 'a' AND p2.prokind != 'a' AND (p1.proargtypes[5] < p2.proargtypes[5]) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[6], p2.proargtypes[6] FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND p1.prokind != 'a' AND p2.prokind != 'a' AND (p1.proargtypes[6] < p2.proargtypes[6]) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[7], p2.proargtypes[7] FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND p1.prokind != 'a' AND p2.prokind != 'a' AND (p1.proargtypes[7] < p2.proargtypes[7]) ORDER BY 1, 2; -- Look for functions that return type "internal" and do not have any -- "internal" argument. Such a function would be a security hole since -- it might be used to call an internal function from an SQL command. -- As of 7.3 this query should find only internal_in, which is safe because -- it always throws an error when called. SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE p1.prorettype = 'internal'::regtype AND NOT 'internal'::regtype = ANY (p1.proargtypes); -- Look for functions that return a polymorphic type and do not have any -- polymorphic argument. Calls of such functions would be unresolvable -- at parse time. As of 9.6 this query should find only some input functions -- and GiST support functions associated with these pseudotypes. SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE p1.prorettype IN ('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype, 'anyenum'::regtype, 'anyrange'::regtype) AND NOT ('anyelement'::regtype = ANY (p1.proargtypes) OR 'anyarray'::regtype = ANY (p1.proargtypes) OR 'anynonarray'::regtype = ANY (p1.proargtypes) OR 'anyenum'::regtype = ANY (p1.proargtypes) OR 'anyrange'::regtype = ANY (p1.proargtypes)) ORDER BY 2; -- Look for functions that accept cstring and are neither datatype input -- functions nor encoding conversion functions. It's almost never a good -- idea to use cstring input for a function meant to be called from SQL; -- text should be used instead, because cstring lacks suitable casts. -- As of 9.6 this query should find only cstring_out and cstring_send. -- However, we must manually exclude shell_in, which might or might not be -- rejected by the EXISTS clause depending on whether there are currently -- any shell types. SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE 'cstring'::regtype = ANY (p1.proargtypes) AND NOT EXISTS ( SELECT 1 FROM pg_type WHERE typinput = p1.oid) AND NOT EXISTS ( SELECT 1 FROM pg_conversion WHERE conproc = p1.oid) AND p1.oid != 'shell_in(cstring)'::regprocedure ORDER BY 1; -- Likewise, look for functions that return cstring and aren't datatype output -- functions nor typmod output functions. -- As of 9.6 this query should find only cstring_in and cstring_recv. -- However, we must manually exclude shell_out. SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE p1.prorettype = 'cstring'::regtype AND NOT EXISTS ( SELECT 1 FROM pg_type WHERE typoutput = p1.oid) AND NOT EXISTS ( SELECT 1 FROM pg_type WHERE typmodout = p1.oid) AND p1.oid != 'shell_out(opaque)'::regprocedure ORDER BY 1; -- Check for length inconsistencies between the various argument-info arrays. SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE proallargtypes IS NOT NULL AND array_length(proallargtypes, 1) < array_length(proargtypes, 1); SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE proargmodes IS NOT NULL AND array_length(proargmodes, 1) < array_length(proargtypes, 1); SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE proargnames IS NOT NULL AND array_length(proargnames, 1) < array_length(proargtypes, 1); SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE proallargtypes IS NOT NULL AND proargmodes IS NOT NULL AND array_length(proallargtypes, 1) <> array_length(proargmodes, 1); SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE proallargtypes IS NOT NULL AND proargnames IS NOT NULL AND array_length(proallargtypes, 1) <> array_length(proargnames, 1); SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE proargmodes IS NOT NULL AND proargnames IS NOT NULL AND array_length(proargmodes, 1) <> array_length(proargnames, 1); -- Check that proallargtypes matches proargtypes SELECT p1.oid, p1.proname, p1.proargtypes, p1.proallargtypes, p1.proargmodes FROM pg_proc AS p1 WHERE proallargtypes IS NOT NULL AND ARRAY ( SELECT unnest(proargtypes)) <> ARRAY ( SELECT proallargtypes[i] FROM generate_series(1, array_length(proallargtypes, 1)) g (i) WHERE proargmodes IS NULL OR proargmodes[i] IN ('i', 'b', 'v')); -- Check for prosupport functions with the wrong signature SELECT p1.oid, p1.proname, p2.oid, p2.proname FROM pg_proc AS p1, pg_proc AS p2 WHERE p2.oid = p1.prosupport AND (p2.prorettype != 'internal'::regtype OR p2.proretset OR p2.pronargs != 1 OR p2.proargtypes[0] != 'internal'::regtype); -- Insist that all built-in pg_proc entries have descriptions SELECT p1.oid, p1.proname FROM pg_proc AS p1 LEFT JOIN pg_description AS d ON p1.tableoid = d.classoid AND p1.oid = d.objoid AND d.objsubid = 0 WHERE d.classoid IS NULL AND p1.oid <= 9999; -- List of built-in leakproof functions -- -- Leakproof functions should only be added after carefully -- scrutinizing all possibly executed codepaths for possible -- information leaks. Don't add functions here unless you know what a -- leakproof function is. If unsure, don't mark it as such. -- temporarily disable fancy output, so catalog changes create less diff noise a \t SELECT p1.oid::regprocedure FROM pg_proc p1 JOIN pg_namespace pn ON pronamespace = pn.oid WHERE nspname = 'pg_catalog' AND proleakproof ORDER BY 1; -- restore normal output mode a \t -- List of functions used by libpq's fe-lobj.c -- -- If the output of this query changes, you probably broke libpq. -- lo_initialize() assumes that there will be at most one match for -- each listed name. SELECT proname, oid FROM pg_catalog.pg_proc WHERE proname IN ('lo_open', 'lo_close', 'lo_creat', 'lo_create', 'lo_unlink', 'lo_lseek', 'lo_lseek64', 'lo_tell', 'lo_tell64', 'lo_truncate', 'lo_truncate64', 'loread', 'lowrite') AND pronamespace = ( SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = 'pg_catalog') ORDER BY 1; -- Check that all immutable functions are marked parallel safe SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE provolatile = 'i' AND proparallel = 'u'; -- **************** pg_cast **************** -- Catch bogus values in pg_cast columns (other than cases detected by -- oidjoins test). SELECT * FROM pg_cast c WHERE castsource = 0 OR casttarget = 0 OR castcontext NOT IN ('e', 'a', 'i') OR castmethod NOT IN ('f', 'b', 'i'); -- Check that castfunc is nonzero only for cast methods that need a function, -- and zero otherwise SELECT * FROM pg_cast c WHERE (castmethod = 'f' AND castfunc = 0) OR (castmethod IN ('b', 'i') AND castfunc <> 0); -- Look for casts to/from the same type that aren't length coercion functions. -- (We assume they are length coercions if they take multiple arguments.) -- Such entries are not necessarily harmful, but they are useless. SELECT * FROM pg_cast c WHERE castsource = casttarget AND castfunc = 0; SELECT c.* FROM pg_cast c, pg_proc p WHERE c.castfunc = p.oid AND p.pronargs < 2 AND castsource = casttarget; -- Look for cast functions that don't have the right signature. The -- argument and result types in pg_proc must be the same as, or binary -- compatible with, what it says in pg_cast. -- As a special case, we allow casts from CHAR(n) that use functions -- declared to take TEXT. This does not pass the binary-coercibility test -- because CHAR(n)-to-TEXT normally invokes rtrim(). However, the results -- are the same, so long as the function is one that ignores trailing blanks. SELECT c.* FROM pg_cast c, pg_proc p WHERE c.castfunc = p.oid AND (p.pronargs < 1 OR p.pronargs > 3 OR NOT (binary_coercible (c.castsource, p.proargtypes[0]) OR (c.castsource = 'character'::regtype AND p.proargtypes[0] = 'text'::regtype)) OR NOT binary_coercible (p.prorettype, c.casttarget)); SELECT c.* FROM pg_cast c, pg_proc p WHERE c.castfunc = p.oid AND ((p.pronargs > 1 AND p.proargtypes[1] != 'int4'::regtype) OR (p.pronargs > 2 AND p.proargtypes[2] != 'bool'::regtype)); -- Look for binary compatible casts that do not have the reverse -- direction registered as well, or where the reverse direction is not -- also binary compatible. This is legal, but usually not intended. -- As of 7.4, this finds the casts from text and varchar to bpchar, because -- those are binary-compatible while the reverse way goes through rtrim(). -- As of 8.2, this finds the cast from cidr to inet, because that is a -- trivial binary coercion while the other way goes through inet_to_cidr(). -- As of 8.3, this finds the casts from xml to text, varchar, and bpchar, -- because those are binary-compatible while the reverse goes through -- texttoxml(), which does an XML syntax check. -- As of 9.1, this finds the cast from pg_node_tree to text, which we -- intentionally do not provide a reverse pathway for. SELECT castsource::regtype, casttarget::regtype, castfunc, castcontext FROM pg_cast c WHERE c.castmethod = 'b' AND NOT EXISTS ( SELECT 1 FROM pg_cast k WHERE k.castmethod = 'b' AND k.castsource = c.casttarget AND k.casttarget = c.castsource); -- **************** pg_conversion **************** -- Look for illegal values in pg_conversion fields. SELECT p1.oid, p1.conname FROM pg_conversion AS p1 WHERE p1.conproc = 0 OR pg_encoding_to_char(conforencoding) = '' OR pg_encoding_to_char(contoencoding) = ''; -- Look for conprocs that don't have the expected signature. SELECT p.oid, p.proname, c.oid, c.conname FROM pg_proc p, pg_conversion c WHERE p.oid = c.conproc AND (p.prorettype != 'void'::regtype OR p.proretset OR p.pronargs != 5 OR p.proargtypes[0] != 'int4'::regtype OR p.proargtypes[1] != 'int4'::regtype OR p.proargtypes[2] != 'cstring'::regtype OR p.proargtypes[3] != 'internal'::regtype OR p.proargtypes[4] != 'int4'::regtype); -- Check for conprocs that don't perform the specific conversion that -- pg_conversion alleges they do, by trying to invoke each conversion -- on some simple ASCII data. (The conproc should throw an error if -- it doesn't accept the encodings that are passed to it.) -- Unfortunately, we can't test non-default conprocs this way, because -- there is no way to ask convert() to invoke them, and we cannot call -- them directly from SQL. But there are no non-default built-in -- conversions anyway. -- (Similarly, this doesn't cope with any search path issues.) SELECT p1.oid, p1.conname FROM pg_conversion AS p1 WHERE condefault AND convert('ABC'::bytea, pg_encoding_to_char(conforencoding), pg_encoding_to_char(contoencoding)) != 'ABC'; -- **************** pg_operator **************** -- Look for illegal values in pg_operator fields. SELECT p1.oid, p1.oprname FROM pg_operator AS p1 WHERE (p1.oprkind != 'b' AND p1.oprkind != 'l' AND p1.oprkind != 'r') OR p1.oprresult = 0 OR p1.oprcode = 0; -- Look for missing or unwanted operand types SELECT p1.oid, p1.oprname FROM pg_operator AS p1 WHERE (p1.oprleft = 0 AND p1.oprkind != 'l') OR (p1.oprleft != 0 AND p1.oprkind = 'l') OR (p1.oprright = 0 AND p1.oprkind != 'r') OR (p1.oprright != 0 AND p1.oprkind = 'r'); -- Look for conflicting operator definitions (same names and input datatypes). SELECT p1.oid, p1.oprcode, p2.oid, p2.oprcode FROM pg_operator AS p1, pg_operator AS p2 WHERE p1.oid != p2.oid AND p1.oprname = p2.oprname AND p1.oprkind = p2.oprkind AND p1.oprleft = p2.oprleft AND p1.oprright = p2.oprright; -- Look for commutative operators that don't commute. -- DEFINITIONAL NOTE: If A.oprcom = B, then x A y has the same result as y B x. -- We expect that B will always say that B.oprcom = A as well; that's not -- inherently essential, but it would be inefficient not to mark it so. SELECT p1.oid, p1.oprcode, p2.oid, p2.oprcode FROM pg_operator AS p1, pg_operator AS p2 WHERE p1.oprcom = p2.oid AND (p1.oprkind != 'b' OR p1.oprleft != p2.oprright OR p1.oprright != p2.oprleft OR p1.oprresult != p2.oprresult OR p1.oid != p2.oprcom); -- Look for negatory operators that don't agree. -- DEFINITIONAL NOTE: If A.oprnegate = B, then both A and B must yield -- boolean results, and (x A y) == ! (x B y), or the equivalent for -- single-operand operators. -- We expect that B will always say that B.oprnegate = A as well; that's not -- inherently essential, but it would be inefficient not to mark it so. -- Also, A and B had better not be the same operator. SELECT p1.oid, p1.oprcode, p2.oid, p2.oprcode FROM pg_operator AS p1, pg_operator AS p2 WHERE p1.oprnegate = p2.oid AND (p1.oprkind != p2.oprkind OR p1.oprleft != p2.oprleft OR p1.oprright != p2.oprright OR p1.oprresult != 'bool'::regtype OR p2.oprresult != 'bool'::regtype OR p1.oid != p2.oprnegate OR p1.oid = p2.oid); -- Make a list of the names of operators that are claimed to be commutator -- pairs. This list will grow over time, but before accepting a new entry -- make sure you didn't link the wrong operators. SELECT DISTINCT o1.oprname AS op1, o2.oprname AS op2 FROM pg_operator o1, pg_operator o2 WHERE o1.oprcom = o2.oid AND o1.oprname <= o2.oprname ORDER BY 1, 2; -- Likewise for negator pairs. SELECT DISTINCT o1.oprname AS op1, o2.oprname AS op2 FROM pg_operator o1, pg_operator o2 WHERE o1.oprnegate = o2.oid AND o1.oprname <= o2.oprname ORDER BY 1, 2; -- A mergejoinable or hashjoinable operator must be binary, must return -- boolean, and must have a commutator (itself, unless it's a cross-type -- operator). SELECT p1.oid, p1.oprname FROM pg_operator AS p1 WHERE (p1.oprcanmerge OR p1.oprcanhash) AND NOT (p1.oprkind = 'b' AND p1.oprresult = 'bool'::regtype AND p1.oprcom != 0); -- What's more, the commutator had better be mergejoinable/hashjoinable too. SELECT p1.oid, p1.oprname, p2.oid, p2.oprname FROM pg_operator AS p1, pg_operator AS p2 WHERE p1.oprcom = p2.oid AND (p1.oprcanmerge != p2.oprcanmerge OR p1.oprcanhash != p2.oprcanhash); -- Mergejoinable operators should appear as equality members of btree index -- opfamilies. SELECT p1.oid, p1.oprname FROM pg_operator AS p1 WHERE p1.oprcanmerge AND NOT EXISTS ( SELECT 1 FROM pg_amop WHERE amopmethod = ( SELECT oid FROM pg_am WHERE amname = 'btree') AND amopopr = p1.oid AND amopstrategy = 3); -- And the converse. SELECT p1.oid, p1.oprname, p.amopfamily FROM pg_operator AS p1, pg_amop p WHERE amopopr = p1.oid AND amopmethod = ( SELECT oid FROM pg_am WHERE amname = 'btree') AND amopstrategy = 3 AND NOT p1.oprcanmerge; -- Hashable operators should appear as members of hash index opfamilies. SELECT p1.oid, p1.oprname FROM pg_operator AS p1 WHERE p1.oprcanhash AND NOT EXISTS ( SELECT 1 FROM pg_amop WHERE amopmethod = ( SELECT oid FROM pg_am WHERE amname = 'hash') AND amopopr = p1.oid AND amopstrategy = 1); -- And the converse. SELECT p1.oid, p1.oprname, p.amopfamily FROM pg_operator AS p1, pg_amop p WHERE amopopr = p1.oid AND amopmethod = ( SELECT oid FROM pg_am WHERE amname = 'hash') AND NOT p1.oprcanhash; -- Check that each operator defined in pg_operator matches its oprcode entry -- in pg_proc. Easiest to do this separately for each oprkind. SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprcode = p2.oid AND p1.oprkind = 'b' AND (p2.pronargs != 2 OR NOT binary_coercible (p2.prorettype, p1.oprresult) OR NOT binary_coercible (p1.oprleft, p2.proargtypes[0]) OR NOT binary_coercible (p1.oprright, p2.proargtypes[1])); SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprcode = p2.oid AND p1.oprkind = 'l' AND (p2.pronargs != 1 OR NOT binary_coercible (p2.prorettype, p1.oprresult) OR NOT binary_coercible (p1.oprright, p2.proargtypes[0]) OR p1.oprleft != 0); SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprcode = p2.oid AND p1.oprkind = 'r' AND (p2.pronargs != 1 OR NOT binary_coercible (p2.prorettype, p1.oprresult) OR NOT binary_coercible (p1.oprleft, p2.proargtypes[0]) OR p1.oprright != 0); -- If the operator is mergejoinable or hashjoinable, its underlying function -- should not be volatile. SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprcode = p2.oid AND (p1.oprcanmerge OR p1.oprcanhash) AND p2.provolatile = 'v'; -- If oprrest is set, the operator must return boolean, -- and it must link to a proc with the right signature -- to be a restriction selectivity estimator. -- The proc signature we want is: float8 proc(internal, oid, internal, int4) SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprrest = p2.oid AND (p1.oprresult != 'bool'::regtype OR p2.prorettype != 'float8'::regtype OR p2.proretset OR p2.pronargs != 4 OR p2.proargtypes[0] != 'internal'::regtype OR p2.proargtypes[1] != 'oid'::regtype OR p2.proargtypes[2] != 'internal'::regtype OR p2.proargtypes[3] != 'int4'::regtype); -- If oprjoin is set, the operator must be a binary boolean op, -- and it must link to a proc with the right signature -- to be a join selectivity estimator. -- The proc signature we want is: float8 proc(internal, oid, internal, int2, internal) -- (Note: the old signature with only 4 args is still allowed, but no core -- estimator should be using it.) SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprjoin = p2.oid AND (p1.oprkind != 'b' OR p1.oprresult != 'bool'::regtype OR p2.prorettype != 'float8'::regtype OR p2.proretset OR p2.pronargs != 5 OR p2.proargtypes[0] != 'internal'::regtype OR p2.proargtypes[1] != 'oid'::regtype OR p2.proargtypes[2] != 'internal'::regtype OR p2.proargtypes[3] != 'int2'::regtype OR p2.proargtypes[4] != 'internal'::regtype); -- Insist that all built-in pg_operator entries have descriptions SELECT p1.oid, p1.oprname FROM pg_operator AS p1 LEFT JOIN pg_description AS d ON p1.tableoid = d.classoid AND p1.oid = d.objoid AND d.objsubid = 0 WHERE d.classoid IS NULL AND p1.oid <= 9999; -- Check that operators' underlying functions have suitable comments, -- namely 'implementation of XXX operator'. (Note: it's not necessary to -- put such comments into pg_proc.dat; initdb will generate them as needed.) -- In some cases involving legacy names for operators, there are multiple -- operators referencing the same pg_proc entry, so ignore operators whose -- comments say they are deprecated. -- We also have a few functions that are both operator support and meant to -- be called directly; those should have comments matching their operator. WITH funcdescs AS ( SELECT p.oid AS p_oid, proname, o.oid AS o_oid, pd.description AS prodesc, 'implementation of ' || oprname || ' operator' AS expecteddesc, od.description AS oprdesc FROM pg_proc p JOIN pg_operator o ON oprcode = p.oid LEFT JOIN pg_description pd ON (pd.objoid = p.oid AND pd.classoid = p.tableoid AND pd.objsubid = 0) LEFT JOIN pg_description od ON (od.objoid = o.oid AND od.classoid = o.tableoid AND od.objsubid = 0) WHERE o.oid <= 9999 ) SELECT * FROM funcdescs WHERE prodesc IS DISTINCT FROM expecteddesc AND oprdesc NOT LIKE 'deprecated%' AND prodesc IS DISTINCT FROM oprdesc; -- Show all the operator-implementation functions that have their own -- comments. This should happen only in cases where the function and -- operator syntaxes are both documented at the user level. -- This should be a pretty short list; it's mostly legacy cases. WITH funcdescs AS ( SELECT p.oid AS p_oid, proname, o.oid AS o_oid, pd.description AS prodesc, 'implementation of ' || oprname || ' operator' AS expecteddesc, od.description AS oprdesc FROM pg_proc p JOIN pg_operator o ON oprcode = p.oid LEFT JOIN pg_description pd ON (pd.objoid = p.oid AND pd.classoid = p.tableoid AND pd.objsubid = 0) LEFT JOIN pg_description od ON (od.objoid = o.oid AND od.classoid = o.tableoid AND od.objsubid = 0) WHERE o.oid <= 9999 ) SELECT p_oid, proname, prodesc FROM funcdescs WHERE prodesc IS DISTINCT FROM expecteddesc AND oprdesc NOT LIKE 'deprecated%' ORDER BY 1; -- Operators that are commutator pairs should have identical volatility -- and leakproofness markings on their implementation functions. SELECT o1.oid, o1.oprcode, o2.oid, o2.oprcode FROM pg_operator AS o1, pg_operator AS o2, pg_proc AS p1, pg_proc AS p2 WHERE o1.oprcom = o2.oid AND p1.oid = o1.oprcode AND p2.oid = o2.oprcode AND (p1.provolatile != p2.provolatile OR p1.proleakproof != p2.proleakproof); -- Likewise for negator pairs. SELECT o1.oid, o1.oprcode, o2.oid, o2.oprcode FROM pg_operator AS o1, pg_operator AS o2, pg_proc AS p1, pg_proc AS p2 WHERE o1.oprnegate = o2.oid AND p1.oid = o1.oprcode AND p2.oid = o2.oprcode AND (p1.provolatile != p2.provolatile OR p1.proleakproof != p2.proleakproof); -- Btree comparison operators' functions should have the same volatility -- and leakproofness markings as the associated comparison support function. -- As of Postgres 12, the only exceptions are that textual equality functions -- are marked leakproof but textual comparison/inequality functions are not. SELECT pp.oid::regprocedure AS proc, pp.provolatile AS vp, pp.proleakproof AS lp, po.oid::regprocedure AS opr, po.provolatile AS vo, po.proleakproof AS lo FROM pg_proc pp, pg_proc po, pg_operator o, pg_amproc ap, pg_amop ao WHERE pp.oid = ap.amproc AND po.oid = o.oprcode AND o.oid = ao.amopopr AND ao.amopmethod = ( SELECT oid FROM pg_am WHERE amname = 'btree') AND ao.amopfamily = ap.amprocfamily AND ao.amoplefttype = ap.amproclefttype AND ao.amoprighttype = ap.amprocrighttype AND ap.amprocnum = 1 AND (pp.provolatile != po.provolatile OR pp.proleakproof != po.proleakproof) ORDER BY 1; -- **************** pg_aggregate **************** -- Look for illegal values in pg_aggregate fields. SELECT ctid, aggfnoid::oid FROM pg_aggregate AS p1 WHERE aggfnoid = 0 OR aggtransfn = 0 OR aggkind NOT IN ('n', 'o', 'h') OR aggnumdirectargs < 0 OR (aggkind = 'n' AND aggnumdirectargs > 0) OR aggfinalmodify NOT IN ('r', 's', 'w') OR aggmfinalmodify NOT IN ('r', 's', 'w') OR aggtranstype = 0 OR aggtransspace < 0 OR aggmtransspace < 0; -- Make sure the matching pg_proc entry is sensible, too. SELECT a.aggfnoid::oid, p.proname FROM pg_aggregate AS a, pg_proc AS p WHERE a.aggfnoid = p.oid AND (p.prokind != 'a' OR p.proretset OR p.pronargs < a.aggnumdirectargs); -- Make sure there are no prokind = PROKIND_AGGREGATE pg_proc entries without matches. SELECT oid, proname FROM pg_proc AS p WHERE p.prokind = 'a' AND NOT EXISTS ( SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid); -- If there is no finalfn then the output type must be the transtype. SELECT a.aggfnoid::oid, p.proname FROM pg_aggregate AS a, pg_proc AS p WHERE a.aggfnoid = p.oid AND a.aggfinalfn = 0 AND p.prorettype != a.aggtranstype; -- Cross-check transfn against its entry in pg_proc. SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr WHERE a.aggfnoid = p.oid AND a.aggtransfn = ptr.oid AND (ptr.proretset OR NOT (ptr.pronargs = CASE WHEN a.aggkind = 'n' THEN p.pronargs + 1 ELSE greatest (p.pronargs - a.aggnumdirectargs, 1) + 1 END) OR NOT binary_coercible (ptr.prorettype, a.aggtranstype) OR NOT binary_coercible (a.aggtranstype, ptr.proargtypes[0]) OR (p.pronargs > 0 AND NOT binary_coercible (p.proargtypes[0], ptr.proargtypes[1])) OR (p.pronargs > 1 AND NOT binary_coercible (p.proargtypes[1], ptr.proargtypes[2])) OR (p.pronargs > 2 AND NOT binary_coercible (p.proargtypes[2], ptr.proargtypes[3])) -- we could carry the check further, but 3 args is enough for now OR (p.pronargs > 3)); -- Cross-check finalfn (if present) against its entry in pg_proc. SELECT a.aggfnoid::oid, p.proname, pfn.oid, pfn.proname FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS pfn WHERE a.aggfnoid = p.oid AND a.aggfinalfn = pfn.oid AND (pfn.proretset OR NOT binary_coercible (pfn.prorettype, p.prorettype) OR NOT binary_coercible (a.aggtranstype, pfn.proargtypes[0]) OR CASE WHEN a.aggfinalextra THEN pfn.pronargs != p.pronargs + 1 ELSE pfn.pronargs != a.aggnumdirectargs + 1 END OR (pfn.pronargs > 1 AND NOT binary_coercible (p.proargtypes[0], pfn.proargtypes[1])) OR (pfn.pronargs > 2 AND NOT binary_coercible (p.proargtypes[1], pfn.proargtypes[2])) OR (pfn.pronargs > 3 AND NOT binary_coercible (p.proargtypes[2], pfn.proargtypes[3])) -- we could carry the check further, but 4 args is enough for now OR (pfn.pronargs > 4)); -- If transfn is strict then either initval should be non-NULL, or -- input type should match transtype so that the first non-null input -- can be assigned as the state value. SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr WHERE a.aggfnoid = p.oid AND a.aggtransfn = ptr.oid AND ptr.proisstrict AND a.agginitval IS NULL AND NOT binary_coercible (p.proargtypes[0], a.aggtranstype); -- Check for inconsistent specifications of moving-aggregate columns. SELECT ctid, aggfnoid::oid FROM pg_aggregate AS p1 WHERE aggmtranstype != 0 AND (aggmtransfn = 0 OR aggminvtransfn = 0); SELECT ctid, aggfnoid::oid FROM pg_aggregate AS p1 WHERE aggmtranstype = 0 AND (aggmtransfn != 0 OR aggminvtransfn != 0 OR aggmfinalfn != 0 OR aggmtransspace != 0 OR aggminitval IS NOT NULL); -- If there is no mfinalfn then the output type must be the mtranstype. SELECT a.aggfnoid::oid, p.proname FROM pg_aggregate AS a, pg_proc AS p WHERE a.aggfnoid = p.oid AND a.aggmtransfn != 0 AND a.aggmfinalfn = 0 AND p.prorettype != a.aggmtranstype; -- Cross-check mtransfn (if present) against its entry in pg_proc. SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr WHERE a.aggfnoid = p.oid AND a.aggmtransfn = ptr.oid AND (ptr.proretset OR NOT (ptr.pronargs = CASE WHEN a.aggkind = 'n' THEN p.pronargs + 1 ELSE greatest (p.pronargs - a.aggnumdirectargs, 1) + 1 END) OR NOT binary_coercible (ptr.prorettype, a.aggmtranstype) OR NOT binary_coercible (a.aggmtranstype, ptr.proargtypes[0]) OR (p.pronargs > 0 AND NOT binary_coercible (p.proargtypes[0], ptr.proargtypes[1])) OR (p.pronargs > 1 AND NOT binary_coercible (p.proargtypes[1], ptr.proargtypes[2])) OR (p.pronargs > 2 AND NOT binary_coercible (p.proargtypes[2], ptr.proargtypes[3])) -- we could carry the check further, but 3 args is enough for now OR (p.pronargs > 3)); -- Cross-check minvtransfn (if present) against its entry in pg_proc. SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr WHERE a.aggfnoid = p.oid AND a.aggminvtransfn = ptr.oid AND (ptr.proretset OR NOT (ptr.pronargs = CASE WHEN a.aggkind = 'n' THEN p.pronargs + 1 ELSE greatest (p.pronargs - a.aggnumdirectargs, 1) + 1 END) OR NOT binary_coercible (ptr.prorettype, a.aggmtranstype) OR NOT binary_coercible (a.aggmtranstype, ptr.proargtypes[0]) OR (p.pronargs > 0 AND NOT binary_coercible (p.proargtypes[0], ptr.proargtypes[1])) OR (p.pronargs > 1 AND NOT binary_coercible (p.proargtypes[1], ptr.proargtypes[2])) OR (p.pronargs > 2 AND NOT binary_coercible (p.proargtypes[2], ptr.proargtypes[3])) -- we could carry the check further, but 3 args is enough for now OR (p.pronargs > 3)); -- Cross-check mfinalfn (if present) against its entry in pg_proc. SELECT a.aggfnoid::oid, p.proname, pfn.oid, pfn.proname FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS pfn WHERE a.aggfnoid = p.oid AND a.aggmfinalfn = pfn.oid AND (pfn.proretset OR NOT binary_coercible (pfn.prorettype, p.prorettype) OR NOT binary_coercible (a.aggmtranstype, pfn.proargtypes[0]) OR CASE WHEN a.aggmfinalextra THEN pfn.pronargs != p.pronargs + 1 ELSE pfn.pronargs != a.aggnumdirectargs + 1 END OR (pfn.pronargs > 1 AND NOT binary_coercible (p.proargtypes[0], pfn.proargtypes[1])) OR (pfn.pronargs > 2 AND NOT binary_coercible (p.proargtypes[1], pfn.proargtypes[2])) OR (pfn.pronargs > 3 AND NOT binary_coercible (p.proargtypes[2], pfn.proargtypes[3])) -- we could carry the check further, but 4 args is enough for now OR (pfn.pronargs > 4)); -- If mtransfn is strict then either minitval should be non-NULL, or -- input type should match mtranstype so that the first non-null input -- can be assigned as the state value. SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr WHERE a.aggfnoid = p.oid AND a.aggmtransfn = ptr.oid AND ptr.proisstrict AND a.aggminitval IS NULL AND NOT binary_coercible (p.proargtypes[0], a.aggmtranstype); -- mtransfn and minvtransfn should have same strictness setting. SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname, iptr.oid, iptr.proname FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr, pg_proc AS iptr WHERE a.aggfnoid = p.oid AND a.aggmtransfn = ptr.oid AND a.aggminvtransfn = iptr.oid AND ptr.proisstrict != iptr.proisstrict; -- Check that all combine functions have signature -- combine(transtype, transtype) returns transtype SELECT a.aggfnoid, p.proname FROM pg_aggregate AS a, pg_proc AS p WHERE a.aggcombinefn = p.oid AND (p.pronargs != 2 OR p.prorettype != p.proargtypes[0] OR p.prorettype != p.proargtypes[1] OR NOT binary_coercible (a.aggtranstype, p.proargtypes[0])); -- Check that no combine function for an INTERNAL transtype is strict. SELECT a.aggfnoid, p.proname FROM pg_aggregate AS a, pg_proc AS p WHERE a.aggcombinefn = p.oid AND a.aggtranstype = 'internal'::regtype AND p.proisstrict; -- serialize/deserialize functions should be specified only for aggregates -- with transtype internal and a combine function, and we should have both -- or neither of them. SELECT aggfnoid, aggtranstype, aggserialfn, aggdeserialfn FROM pg_aggregate WHERE (aggserialfn != 0 OR aggdeserialfn != 0) AND (aggtranstype != 'internal'::regtype OR aggcombinefn = 0 OR aggserialfn = 0 OR aggdeserialfn = 0); -- Check that all serialization functions have signature -- serialize(internal) returns bytea -- Also insist that they be strict; it's wasteful to run them on NULLs. SELECT a.aggfnoid, p.proname FROM pg_aggregate AS a, pg_proc AS p WHERE a.aggserialfn = p.oid AND (p.prorettype != 'bytea'::regtype OR p.pronargs != 1 OR p.proargtypes[0] != 'internal'::regtype OR NOT p.proisstrict); -- Check that all deserialization functions have signature -- deserialize(bytea, internal) returns internal -- Also insist that they be strict; it's wasteful to run them on NULLs. SELECT a.aggfnoid, p.proname FROM pg_aggregate AS a, pg_proc AS p WHERE a.aggdeserialfn = p.oid AND (p.prorettype != 'internal'::regtype OR p.pronargs != 2 OR p.proargtypes[0] != 'bytea'::regtype OR p.proargtypes[1] != 'internal'::regtype OR NOT p.proisstrict); -- Check that aggregates which have the same transition function also have -- the same combine, serialization, and deserialization functions. -- While that isn't strictly necessary, it's fishy if they don't. SELECT a.aggfnoid, a.aggcombinefn, a.aggserialfn, a.aggdeserialfn, b.aggfnoid, b.aggcombinefn, b.aggserialfn, b.aggdeserialfn FROM pg_aggregate a, pg_aggregate b WHERE a.aggfnoid < b.aggfnoid AND a.aggtransfn = b.aggtransfn AND (a.aggcombinefn != b.aggcombinefn OR a.aggserialfn != b.aggserialfn OR a.aggdeserialfn != b.aggdeserialfn); -- Cross-check aggsortop (if present) against pg_operator. -- We expect to find entries for bool_and, bool_or, every, max, and min. SELECT DISTINCT proname, oprname FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid ORDER BY 1, 2; -- Check datatypes match SELECT a.aggfnoid::oid, o.oid FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid AND (oprkind != 'b' OR oprresult != 'boolean'::regtype OR oprleft != p.proargtypes[0] OR oprright != p.proargtypes[0]); -- Check operator is a suitable btree opfamily member SELECT a.aggfnoid::oid, o.oid FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid AND NOT EXISTS ( SELECT 1 FROM pg_amop WHERE amopmethod = ( SELECT oid FROM pg_am WHERE amname = 'btree') AND amopopr = o.oid AND amoplefttype = o.oprleft AND amoprighttype = o.oprright); -- Check correspondence of btree strategies and names SELECT DISTINCT proname, oprname, amopstrategy FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p, pg_amop AS ao WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid AND amopopr = o.oid AND amopmethod = ( SELECT oid FROM pg_am WHERE amname = 'btree') ORDER BY 1, 2; -- Check that there are not aggregates with the same name and different -- numbers of arguments. While not technically wrong, we have a project policy -- to avoid this because it opens the door for confusion in connection with -- ORDER BY: novices frequently put the ORDER BY in the wrong place. -- See the fate of the single-argument form of string_agg() for history. -- (Note: we don't forbid users from creating such aggregates; the policy is -- just to think twice before creating built-in aggregates like this.) -- The only aggregates that should show up here are count(x) and count(*). SELECT p1.oid::regprocedure, p2.oid::regprocedure FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid < p2.oid AND p1.proname = p2.proname AND p1.prokind = 'a' AND p2.prokind = 'a' AND array_dims(p1.proargtypes) != array_dims(p2.proargtypes) ORDER BY 1; -- For the same reason, built-in aggregates with default arguments are no good. SELECT oid, proname FROM pg_proc AS p WHERE prokind = 'a' AND proargdefaults IS NOT NULL; -- For the same reason, we avoid creating built-in variadic aggregates, except -- that variadic ordered-set aggregates are OK (since they have special syntax -- that is not subject to the misplaced ORDER BY issue). SELECT p.oid, proname FROM pg_proc AS p JOIN pg_aggregate AS a ON a.aggfnoid = p.oid WHERE prokind = 'a' AND provariadic != 0 AND a.aggkind = 'n'; -- **************** pg_opfamily **************** -- Look for illegal values in pg_opfamily fields SELECT p1.oid FROM pg_opfamily AS p1 WHERE p1.opfmethod = 0 OR p1.opfnamespace = 0; -- Look for opfamilies having no opclasses. While most validation of -- opfamilies is now handled by AM-specific amvalidate functions, that's -- driven from pg_opclass entries below, so an empty opfamily would not -- get noticed. SELECT oid, opfname FROM pg_opfamily f WHERE NOT EXISTS ( SELECT 1 FROM pg_opclass WHERE opcfamily = f.oid); -- **************** pg_opclass **************** -- Look for illegal values in pg_opclass fields SELECT p1.oid FROM pg_opclass AS p1 WHERE p1.opcmethod = 0 OR p1.opcnamespace = 0 OR p1.opcfamily = 0 OR p1.opcintype = 0; -- opcmethod must match owning opfamily's opfmethod SELECT p1.oid, p2.oid FROM pg_opclass AS p1, pg_opfamily AS p2 WHERE p1.opcfamily = p2.oid AND p1.opcmethod != p2.opfmethod; -- There should not be multiple entries in pg_opclass with opcdefault true -- and the same opcmethod/opcintype combination. SELECT p1.oid, p2.oid FROM pg_opclass AS p1, pg_opclass AS p2 WHERE p1.oid != p2.oid AND p1.opcmethod = p2.opcmethod AND p1.opcintype = p2.opcintype AND p1.opcdefault AND p2.opcdefault; -- Ask access methods to validate opclasses -- (this replaces a lot of SQL-level checks that used to be done in this file) SELECT oid, opcname FROM pg_opclass WHERE NOT amvalidate (oid); -- **************** pg_am **************** -- Look for illegal values in pg_am fields SELECT p1.oid, p1.amname FROM pg_am AS p1 WHERE p1.amhandler = 0; -- Check for index amhandler functions with the wrong signature SELECT p1.oid, p1.amname, p2.oid, p2.proname FROM pg_am AS p1, pg_proc AS p2 WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset OR p2.pronargs != 1 OR p2.proargtypes[0] != 'internal'::regtype); -- Check for table amhandler functions with the wrong signature SELECT p1.oid, p1.amname, p2.oid, p2.proname FROM pg_am AS p1, pg_proc AS p2 WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND (p2.prorettype != 'table_am_handler'::regtype OR p2.proretset OR p2.pronargs != 1 OR p2.proargtypes[0] != 'internal'::regtype); -- **************** pg_amop **************** -- Look for illegal values in pg_amop fields SELECT p1.amopfamily, p1.amopstrategy FROM pg_amop AS p1 WHERE p1.amopfamily = 0 OR p1.amoplefttype = 0 OR p1.amoprighttype = 0 OR p1.amopopr = 0 OR p1.amopmethod = 0 OR p1.amopstrategy < 1; SELECT p1.amopfamily, p1.amopstrategy FROM pg_amop AS p1 WHERE NOT ((p1.amoppurpose = 's' AND p1.amopsortfamily = 0) OR (p1.amoppurpose = 'o' AND p1.amopsortfamily <> 0)); -- amopmethod must match owning opfamily's opfmethod SELECT p1.oid, p2.oid FROM pg_amop AS p1, pg_opfamily AS p2 WHERE p1.amopfamily = p2.oid AND p1.amopmethod != p2.opfmethod; -- Make a list of all the distinct operator names being used in particular -- strategy slots. This is a bit hokey, since the list might need to change -- in future releases, but it's an effective way of spotting mistakes such as -- swapping two operators within a family. SELECT DISTINCT amopmethod, amopstrategy, oprname FROM pg_amop p1 LEFT JOIN pg_operator p2 ON amopopr = p2.oid ORDER BY 1, 2, 3; -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing -- to insist on for all standard datatypes. SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.oprname FROM pg_amop AS p1, pg_operator AS p2 WHERE p1.amopopr = p2.oid AND p1.amoppurpose = 's' AND (p2.oprrest = 0 OR p2.oprjoin = 0); -- Check that each opclass in an opfamily has associated operators, that is -- ones whose oprleft matches opcintype (possibly by coercion). SELECT p1.opcname, p1.opcfamily FROM pg_opclass AS p1 WHERE NOT EXISTS ( SELECT 1 FROM pg_amop AS p2 WHERE p2.amopfamily = p1.opcfamily AND binary_coercible (p1.opcintype, p2.amoplefttype)); -- Check that each operator listed in pg_amop has an associated opclass, -- that is one whose opcintype matches oprleft (possibly by coercion). -- Otherwise the operator is useless because it cannot be matched to an index. -- (In principle it could be useful to list such operators in multiple-datatype -- btree opfamilies, but in practice you'd expect there to be an opclass for -- every datatype the family knows about.) SELECT p1.amopfamily, p1.amopstrategy, p1.amopopr FROM pg_amop AS p1 WHERE NOT EXISTS ( SELECT 1 FROM pg_opclass AS p2 WHERE p2.opcfamily = p1.amopfamily AND binary_coercible (p2.opcintype, p1.amoplefttype)); -- Operators that are primary members of opclasses must be immutable (else -- it suggests that the index ordering isn't fixed). Operators that are -- cross-type members need only be stable, since they are just shorthands -- for index probe queries. SELECT p1.amopfamily, p1.amopopr, p2.oprname, p3.prosrc FROM pg_amop AS p1, pg_operator AS p2, pg_proc AS p3 WHERE p1.amopopr = p2.oid AND p2.oprcode = p3.oid AND p1.amoplefttype = p1.amoprighttype AND p3.provolatile != 'i'; SELECT p1.amopfamily, p1.amopopr, p2.oprname, p3.prosrc FROM pg_amop AS p1, pg_operator AS p2, pg_proc AS p3 WHERE p1.amopopr = p2.oid AND p2.oprcode = p3.oid AND p1.amoplefttype != p1.amoprighttype AND p3.provolatile = 'v'; -- **************** pg_amproc **************** -- Look for illegal values in pg_amproc fields SELECT p1.amprocfamily, p1.amprocnum FROM pg_amproc AS p1 WHERE p1.amprocfamily = 0 OR p1.amproclefttype = 0 OR p1.amprocrighttype = 0 OR p1.amprocnum < 1 OR p1.amproc = 0; -- Support routines that are primary members of opfamilies must be immutable -- (else it suggests that the index ordering isn't fixed). But cross-type -- members need only be stable, since they are just shorthands -- for index probe queries. SELECT p1.amprocfamily, p1.amproc, p2.prosrc FROM pg_amproc AS p1, pg_proc AS p2 WHERE p1.amproc = p2.oid AND p1.amproclefttype = p1.amprocrighttype AND p2.provolatile != 'i'; SELECT p1.amprocfamily, p1.amproc, p2.prosrc FROM pg_amproc AS p1, pg_proc AS p2 WHERE p1.amproc = p2.oid AND p1.amproclefttype != p1.amprocrighttype AND p2.provolatile = 'v'; -- **************** pg_index **************** -- Look for illegal values in pg_index fields. SELECT p1.indexrelid, p1.indrelid FROM pg_index AS p1 WHERE p1.indexrelid = 0 OR p1.indrelid = 0 OR p1.indnatts <= 0 OR p1.indnatts > 32; -- oidvector and int2vector fields should be of length indnatts. SELECT p1.indexrelid, p1.indrelid FROM pg_index AS p1 WHERE array_lower(indkey, 1) != 0 OR array_upper(indkey, 1) != indnatts - 1 OR array_lower(indclass, 1) != 0 OR array_upper(indclass, 1) != indnatts - 1 OR array_lower(indcollation, 1) != 0 OR array_upper(indcollation, 1) != indnatts - 1 OR array_lower(indoption, 1) != 0 OR array_upper(indoption, 1) != indnatts - 1; -- Check that opclasses and collations match the underlying columns. -- (As written, this test ignores expression indexes.) SELECT indexrelid::regclass, indrelid::regclass, attname, atttypid::regtype, opcname FROM ( SELECT indexrelid, indrelid, unnest(indkey) AS ikey, unnest(indclass) AS iclass, unnest(indcollation) AS icoll FROM pg_index) ss, pg_attribute a, pg_opclass opc WHERE a.attrelid = indrelid AND a.attnum = ikey AND opc.oid = iclass AND (NOT binary_coercible (atttypid, opcintype) OR icoll != attcollation); -- For system catalogs, be even tighter: nearly all indexes should be -- exact type matches not binary-coercible matches. At this writing -- the only exception is an OID index on a regproc column. SELECT indexrelid::regclass, indrelid::regclass, attname, atttypid::regtype, opcname FROM ( SELECT indexrelid, indrelid, unnest(indkey) AS ikey, unnest(indclass) AS iclass, unnest(indcollation) AS icoll FROM pg_index WHERE indrelid < 16384) ss, pg_attribute a, pg_opclass opc WHERE a.attrelid = indrelid AND a.attnum = ikey AND opc.oid = iclass AND (opcintype != atttypid OR icoll != attcollation) ORDER BY 1; -- Check for system catalogs with collation-sensitive ordering. This is not -- a representational error in pg_index, but simply wrong catalog design. -- It's bad because we expect to be able to clone template0 and assign the -- copy a different database collation. It would especially not work for -- shared catalogs. SELECT relname, attname, attcollation FROM pg_class c, pg_attribute a WHERE c.oid = attrelid AND c.oid < 16384 AND c.relkind != 'v' AND -- we don't care about columns in views attcollation != 0 AND attcollation != ( SELECT oid FROM pg_collation WHERE collname = 'C'); -- Double-check that collation-sensitive indexes have "C" collation, too. SELECT indexrelid::regclass, indrelid::regclass, iclass, icoll FROM ( SELECT indexrelid, indrelid, unnest(indclass) AS iclass, unnest(indcollation) AS icoll FROM pg_index WHERE indrelid < 16384) ss WHERE icoll != 0 AND icoll != ( SELECT oid FROM pg_collation WHERE collname = 'C'); pgFormatter-4.2/t/pg-test-files/expected/partition_aggregate.sql000066400000000000000000000503041361326045100251430ustar00rootroot00000000000000-- -- PARTITION_AGGREGATE -- Test partitionwise aggregation on partitioned tables -- -- Enable partitionwise aggregate, which by default is disabled. SET enable_partitionwise_aggregate TO TRUE; -- Enable partitionwise join, which by default is disabled. SET enable_partitionwise_join TO TRUE; -- Disable parallel plans. SET max_parallel_workers_per_gather TO 0; -- -- Tests for list partitioned tables. -- CREATE TABLE pagg_tab ( a int, b int, c text, d int ) PARTITION BY LIST (c); CREATE TABLE pagg_tab_p1 PARTITION OF pagg_tab FOR VALUES IN ('0000', '0001', '0002', '0003'); CREATE TABLE pagg_tab_p2 PARTITION OF pagg_tab FOR VALUES IN ('0004', '0005', '0006', '0007'); CREATE TABLE pagg_tab_p3 PARTITION OF pagg_tab FOR VALUES IN ('0008', '0009', '0010', '0011'); INSERT INTO pagg_tab SELECT i % 20, i % 30, to_char(i % 12, 'FM0000'), i % 30 FROM generate_series(0, 2999) i; ANALYZE pagg_tab; -- When GROUP BY clause matches; full aggregation is performed for each partition. EXPLAIN ( COSTS OFF ) SELECT c, sum(a), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY c HAVING avg(d) < 15 ORDER BY 1, 2, 3; SELECT c, sum(a), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY c HAVING avg(d) < 15 ORDER BY 1, 2, 3; -- When GROUP BY clause does not match; partial aggregation is performed for each partition. EXPLAIN ( COSTS OFF ) SELECT a, sum(b), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY a HAVING avg(d) < 15 ORDER BY 1, 2, 3; SELECT a, sum(b), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY a HAVING avg(d) < 15 ORDER BY 1, 2, 3; -- Check with multiple columns in GROUP BY EXPLAIN ( COSTS OFF ) SELECT a, c, count(*) FROM pagg_tab GROUP BY a, c; -- Check with multiple columns in GROUP BY, order in GROUP BY is reversed EXPLAIN ( COSTS OFF ) SELECT a, c, count(*) FROM pagg_tab GROUP BY c, a; -- Check with multiple columns in GROUP BY, order in target-list is reversed EXPLAIN ( COSTS OFF ) SELECT c, a, count(*) FROM pagg_tab GROUP BY a, c; -- Test when input relation for grouping is dummy EXPLAIN ( COSTS OFF ) SELECT c, sum(a) FROM pagg_tab WHERE 1 = 2 GROUP BY c; SELECT c, sum(a) FROM pagg_tab WHERE 1 = 2 GROUP BY c; EXPLAIN ( COSTS OFF ) SELECT c, sum(a) FROM pagg_tab WHERE c = 'x' GROUP BY c; SELECT c, sum(a) FROM pagg_tab WHERE c = 'x' GROUP BY c; -- Test GroupAggregate paths by disabling hash aggregates. SET enable_hashagg TO FALSE; -- When GROUP BY clause matches full aggregation is performed for each partition. EXPLAIN ( COSTS OFF ) SELECT c, sum(a), avg(b), count(*) FROM pagg_tab GROUP BY 1 HAVING avg(d) < 15 ORDER BY 1, 2, 3; SELECT c, sum(a), avg(b), count(*) FROM pagg_tab GROUP BY 1 HAVING avg(d) < 15 ORDER BY 1, 2, 3; -- When GROUP BY clause does not match; partial aggregation is performed for each partition. EXPLAIN ( COSTS OFF ) SELECT a, sum(b), avg(b), count(*) FROM pagg_tab GROUP BY 1 HAVING avg(d) < 15 ORDER BY 1, 2, 3; SELECT a, sum(b), avg(b), count(*) FROM pagg_tab GROUP BY 1 HAVING avg(d) < 15 ORDER BY 1, 2, 3; -- Test partitionwise grouping without any aggregates EXPLAIN ( COSTS OFF ) SELECT c FROM pagg_tab GROUP BY c ORDER BY 1; SELECT c FROM pagg_tab GROUP BY c ORDER BY 1; EXPLAIN ( COSTS OFF ) SELECT a FROM pagg_tab WHERE a < 3 GROUP BY a ORDER BY 1; SELECT a FROM pagg_tab WHERE a < 3 GROUP BY a ORDER BY 1; RESET enable_hashagg; -- ROLLUP, partitionwise aggregation does not apply EXPLAIN ( COSTS OFF ) SELECT c, sum(a) FROM pagg_tab GROUP BY ROLLUP (c) ORDER BY 1, 2; -- ORDERED SET within the aggregate. -- Full aggregation; since all the rows that belong to the same group come -- from the same partition, having an ORDER BY within the aggregate doesn't -- make any difference. EXPLAIN ( COSTS OFF ) SELECT c, sum(b ORDER BY a) FROM pagg_tab GROUP BY c ORDER BY 1, 2; -- Since GROUP BY clause does not match with PARTITION KEY; we need to do -- partial aggregation. However, ORDERED SET are not partial safe and thus -- partitionwise aggregation plan is not generated. EXPLAIN ( COSTS OFF ) SELECT a, sum(b ORDER BY a) FROM pagg_tab GROUP BY a ORDER BY 1, 2; -- JOIN query CREATE TABLE pagg_tab1 ( x int, y int ) PARTITION BY RANGE (x); CREATE TABLE pagg_tab1_p1 PARTITION OF pagg_tab1 FOR VALUES FROM (0) TO (10); CREATE TABLE pagg_tab1_p2 PARTITION OF pagg_tab1 FOR VALUES FROM (10) TO (20); CREATE TABLE pagg_tab1_p3 PARTITION OF pagg_tab1 FOR VALUES FROM (20) TO (30); CREATE TABLE pagg_tab2 ( x int, y int ) PARTITION BY RANGE (y); CREATE TABLE pagg_tab2_p1 PARTITION OF pagg_tab2 FOR VALUES FROM (0) TO (10); CREATE TABLE pagg_tab2_p2 PARTITION OF pagg_tab2 FOR VALUES FROM (10) TO (20); CREATE TABLE pagg_tab2_p3 PARTITION OF pagg_tab2 FOR VALUES FROM (20) TO (30); INSERT INTO pagg_tab1 SELECT i % 30, i % 20 FROM generate_series(0, 299, 2) i; INSERT INTO pagg_tab2 SELECT i % 20, i % 30 FROM generate_series(0, 299, 3) i; ANALYZE pagg_tab1; ANALYZE pagg_tab2; -- When GROUP BY clause matches; full aggregation is performed for each partition. EXPLAIN ( COSTS OFF ) SELECT t1.x, sum(t1.y), count(*) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.x ORDER BY 1, 2, 3; SELECT t1.x, sum(t1.y), count(*) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.x ORDER BY 1, 2, 3; -- Check with whole-row reference; partitionwise aggregation does not apply EXPLAIN ( COSTS OFF ) SELECT t1.x, sum(t1.y), count(t1) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.x ORDER BY 1, 2, 3; SELECT t1.x, sum(t1.y), count(t1) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.x ORDER BY 1, 2, 3; -- GROUP BY having other matching key EXPLAIN ( COSTS OFF ) SELECT t2.y, sum(t1.y), count(*) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t2.y ORDER BY 1, 2, 3; -- When GROUP BY clause does not match; partial aggregation is performed for each partition. -- Also test GroupAggregate paths by disabling hash aggregates. SET enable_hashagg TO FALSE; EXPLAIN ( COSTS OFF ) SELECT t1.y, sum(t1.x), count(*) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.y HAVING avg(t1.x) > 10 ORDER BY 1, 2, 3; SELECT t1.y, sum(t1.x), count(*) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.y HAVING avg(t1.x) > 10 ORDER BY 1, 2, 3; RESET enable_hashagg; -- Check with LEFT/RIGHT/FULL OUTER JOINs which produces NULL values for -- aggregation -- LEFT JOIN, should produce partial partitionwise aggregation plan as -- GROUP BY is on nullable column EXPLAIN ( COSTS OFF ) SELECT b.y, sum(a.y) FROM pagg_tab1 a LEFT JOIN pagg_tab2 b ON a.x = b.y GROUP BY b.y ORDER BY 1 NULLS LAST; SELECT b.y, sum(a.y) FROM pagg_tab1 a LEFT JOIN pagg_tab2 b ON a.x = b.y GROUP BY b.y ORDER BY 1 NULLS LAST; -- RIGHT JOIN, should produce full partitionwise aggregation plan as -- GROUP BY is on non-nullable column EXPLAIN ( COSTS OFF ) SELECT b.y, sum(a.y) FROM pagg_tab1 a RIGHT JOIN pagg_tab2 b ON a.x = b.y GROUP BY b.y ORDER BY 1 NULLS LAST; SELECT b.y, sum(a.y) FROM pagg_tab1 a RIGHT JOIN pagg_tab2 b ON a.x = b.y GROUP BY b.y ORDER BY 1 NULLS LAST; -- FULL JOIN, should produce partial partitionwise aggregation plan as -- GROUP BY is on nullable column EXPLAIN ( COSTS OFF ) SELECT a.x, sum(b.x) FROM pagg_tab1 a FULL OUTER JOIN pagg_tab2 b ON a.x = b.y GROUP BY a.x ORDER BY 1 NULLS LAST; SELECT a.x, sum(b.x) FROM pagg_tab1 a FULL OUTER JOIN pagg_tab2 b ON a.x = b.y GROUP BY a.x ORDER BY 1 NULLS LAST; -- LEFT JOIN, with dummy relation on right side, ideally -- should produce full partitionwise aggregation plan as GROUP BY is on -- non-nullable columns. -- But right now we are unable to do partitionwise join in this case. EXPLAIN ( COSTS OFF ) SELECT a.x, b.y, count(*) FROM ( SELECT * FROM pagg_tab1 WHERE x < 20) a LEFT JOIN ( SELECT * FROM pagg_tab2 WHERE y > 10) b ON a.x = b.y WHERE a.x > 5 OR b.y < 20 GROUP BY a.x, b.y ORDER BY 1, 2; SELECT a.x, b.y, count(*) FROM ( SELECT * FROM pagg_tab1 WHERE x < 20) a LEFT JOIN ( SELECT * FROM pagg_tab2 WHERE y > 10) b ON a.x = b.y WHERE a.x > 5 OR b.y < 20 GROUP BY a.x, b.y ORDER BY 1, 2; -- FULL JOIN, with dummy relations on both sides, ideally -- should produce partial partitionwise aggregation plan as GROUP BY is on -- nullable columns. -- But right now we are unable to do partitionwise join in this case. EXPLAIN ( COSTS OFF ) SELECT a.x, b.y, count(*) FROM ( SELECT * FROM pagg_tab1 WHERE x < 20) a FULL JOIN ( SELECT * FROM pagg_tab2 WHERE y > 10) b ON a.x = b.y WHERE a.x > 5 OR b.y < 20 GROUP BY a.x, b.y ORDER BY 1, 2; SELECT a.x, b.y, count(*) FROM ( SELECT * FROM pagg_tab1 WHERE x < 20) a FULL JOIN ( SELECT * FROM pagg_tab2 WHERE y > 10) b ON a.x = b.y WHERE a.x > 5 OR b.y < 20 GROUP BY a.x, b.y ORDER BY 1, 2; -- Empty join relation because of empty outer side, no partitionwise agg plan EXPLAIN ( COSTS OFF ) SELECT a.x, a.y, count(*) FROM ( SELECT * FROM pagg_tab1 WHERE x = 1 AND x = 2) a LEFT JOIN pagg_tab2 b ON a.x = b.y GROUP BY a.x, a.y ORDER BY 1, 2; SELECT a.x, a.y, count(*) FROM ( SELECT * FROM pagg_tab1 WHERE x = 1 AND x = 2) a LEFT JOIN pagg_tab2 b ON a.x = b.y GROUP BY a.x, a.y ORDER BY 1, 2; -- Partition by multiple columns CREATE TABLE pagg_tab_m ( a int, b int, c int ) PARTITION BY RANGE (a, ((a + b) / 2)); CREATE TABLE pagg_tab_m_p1 PARTITION OF pagg_tab_m FOR VALUES FROM (0, 0) TO (10, 10); CREATE TABLE pagg_tab_m_p2 PARTITION OF pagg_tab_m FOR VALUES FROM (10, 10) TO (20, 20); CREATE TABLE pagg_tab_m_p3 PARTITION OF pagg_tab_m FOR VALUES FROM (20, 20) TO (30, 30); INSERT INTO pagg_tab_m SELECT i % 30, i % 40, i % 50 FROM generate_series(0, 2999) i; ANALYZE pagg_tab_m; -- Partial aggregation as GROUP BY clause does not match with PARTITION KEY EXPLAIN ( COSTS OFF ) SELECT a, sum(b), avg(c), count(*) FROM pagg_tab_m GROUP BY a HAVING avg(c) < 22 ORDER BY 1, 2, 3; SELECT a, sum(b), avg(c), count(*) FROM pagg_tab_m GROUP BY a HAVING avg(c) < 22 ORDER BY 1, 2, 3; -- Full aggregation as GROUP BY clause matches with PARTITION KEY EXPLAIN ( COSTS OFF ) SELECT a, sum(b), avg(c), count(*) FROM pagg_tab_m GROUP BY a, (a + b) / 2 HAVING sum(b) < 50 ORDER BY 1, 2, 3; SELECT a, sum(b), avg(c), count(*) FROM pagg_tab_m GROUP BY a, (a + b) / 2 HAVING sum(b) < 50 ORDER BY 1, 2, 3; -- Full aggregation as PARTITION KEY is part of GROUP BY clause EXPLAIN ( COSTS OFF ) SELECT a, c, sum(b), avg(c), count(*) FROM pagg_tab_m GROUP BY (a + b) / 2, 2, 1 HAVING sum(b) = 50 AND avg(c) > 25 ORDER BY 1, 2, 3; SELECT a, c, sum(b), avg(c), count(*) FROM pagg_tab_m GROUP BY (a + b) / 2, 2, 1 HAVING sum(b) = 50 AND avg(c) > 25 ORDER BY 1, 2, 3; -- Test with multi-level partitioning scheme CREATE TABLE pagg_tab_ml ( a int, b int, c text ) PARTITION BY RANGE (a); CREATE TABLE pagg_tab_ml_p1 PARTITION OF pagg_tab_ml FOR VALUES FROM (0) TO (10); CREATE TABLE pagg_tab_ml_p2 PARTITION OF pagg_tab_ml FOR VALUES FROM (10) TO (20) PARTITION BY LIST (c); CREATE TABLE pagg_tab_ml_p2_s1 PARTITION OF pagg_tab_ml_p2 FOR VALUES IN ('0000', '0001'); CREATE TABLE pagg_tab_ml_p2_s2 PARTITION OF pagg_tab_ml_p2 FOR VALUES IN ('0002', '0003'); -- This level of partitioning has different column positions than the parent CREATE TABLE pagg_tab_ml_p3 ( b int, c text, a int ) PARTITION BY RANGE (b); CREATE TABLE pagg_tab_ml_p3_s1 ( c text, a int, b int ); CREATE TABLE pagg_tab_ml_p3_s2 PARTITION OF pagg_tab_ml_p3 FOR VALUES FROM (5) TO (10); ALTER TABLE pagg_tab_ml_p3 ATTACH PARTITION pagg_tab_ml_p3_s1 FOR VALUES FROM (0) TO (5); ALTER TABLE pagg_tab_ml ATTACH PARTITION pagg_tab_ml_p3 FOR VALUES FROM (20) TO (30); INSERT INTO pagg_tab_ml SELECT i % 30, i % 10, to_char(i % 4, 'FM0000') FROM generate_series(0, 29999) i; ANALYZE pagg_tab_ml; -- For Parallel Append SET max_parallel_workers_per_gather TO 2; -- Full aggregation at level 1 as GROUP BY clause matches with PARTITION KEY -- for level 1 only. For subpartitions, GROUP BY clause does not match with -- PARTITION KEY, but still we do not see a partial aggregation as array_agg() -- is not partial agg safe. EXPLAIN ( COSTS OFF ) SELECT a, sum(b), array_agg(DISTINCT c), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3 ORDER BY 1, 2, 3; SELECT a, sum(b), array_agg(DISTINCT c), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3 ORDER BY 1, 2, 3; -- Without ORDER BY clause, to test Gather at top-most path EXPLAIN ( COSTS OFF ) SELECT a, sum(b), array_agg(DISTINCT c), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3; -- Full aggregation at level 1 as GROUP BY clause matches with PARTITION KEY -- for level 1 only. For subpartitions, GROUP BY clause does not match with -- PARTITION KEY, thus we will have a partial aggregation for them. EXPLAIN ( COSTS OFF ) SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3 ORDER BY 1, 2, 3; SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3 ORDER BY 1, 2, 3; -- Partial aggregation at all levels as GROUP BY clause does not match with -- PARTITION KEY EXPLAIN ( COSTS OFF ) SELECT b, sum(a), count(*) FROM pagg_tab_ml GROUP BY b ORDER BY 1, 2, 3; SELECT b, sum(a), count(*) FROM pagg_tab_ml GROUP BY b HAVING avg(a) < 15 ORDER BY 1, 2, 3; -- Full aggregation at all levels as GROUP BY clause matches with PARTITION KEY EXPLAIN ( COSTS OFF ) SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a, b, c HAVING avg(b) > 7 ORDER BY 1, 2, 3; SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a, b, c HAVING avg(b) > 7 ORDER BY 1, 2, 3; -- Parallelism within partitionwise aggregates SET min_parallel_table_scan_size TO '8kB'; SET parallel_setup_cost TO 0; -- Full aggregation at level 1 as GROUP BY clause matches with PARTITION KEY -- for level 1 only. For subpartitions, GROUP BY clause does not match with -- PARTITION KEY, thus we will have a partial aggregation for them. EXPLAIN ( COSTS OFF ) SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3 ORDER BY 1, 2, 3; SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3 ORDER BY 1, 2, 3; -- Partial aggregation at all levels as GROUP BY clause does not match with -- PARTITION KEY EXPLAIN ( COSTS OFF ) SELECT b, sum(a), count(*) FROM pagg_tab_ml GROUP BY b ORDER BY 1, 2, 3; SELECT b, sum(a), count(*) FROM pagg_tab_ml GROUP BY b HAVING avg(a) < 15 ORDER BY 1, 2, 3; -- Full aggregation at all levels as GROUP BY clause matches with PARTITION KEY EXPLAIN ( COSTS OFF ) SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a, b, c HAVING avg(b) > 7 ORDER BY 1, 2, 3; SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a, b, c HAVING avg(b) > 7 ORDER BY 1, 2, 3; -- Parallelism within partitionwise aggregates (single level) -- Add few parallel setup cost, so that we will see a plan which gathers -- partially created paths even for full aggregation and sticks a single Gather -- followed by finalization step. -- Without this, the cost of doing partial aggregation + Gather + finalization -- for each partition and then Append over it turns out to be same and this -- wins as we add it first. This parallel_setup_cost plays a vital role in -- costing such plans. SET parallel_setup_cost TO 10; CREATE TABLE pagg_tab_para ( x int, y int ) PARTITION BY RANGE (x); CREATE TABLE pagg_tab_para_p1 PARTITION OF pagg_tab_para FOR VALUES FROM (0) TO (10); CREATE TABLE pagg_tab_para_p2 PARTITION OF pagg_tab_para FOR VALUES FROM (10) TO (20); CREATE TABLE pagg_tab_para_p3 PARTITION OF pagg_tab_para FOR VALUES FROM (20) TO (30); INSERT INTO pagg_tab_para SELECT i % 30, i % 20 FROM generate_series(0, 29999) i; ANALYZE pagg_tab_para; -- When GROUP BY clause matches; full aggregation is performed for each partition. EXPLAIN ( COSTS OFF ) SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; -- When GROUP BY clause does not match; partial aggregation is performed for each partition. EXPLAIN ( COSTS OFF ) SELECT y, sum(x), avg(x), count(*) FROM pagg_tab_para GROUP BY y HAVING avg(x) < 12 ORDER BY 1, 2, 3; SELECT y, sum(x), avg(x), count(*) FROM pagg_tab_para GROUP BY y HAVING avg(x) < 12 ORDER BY 1, 2, 3; -- Test when parent can produce parallel paths but not any (or some) of its children ALTER TABLE pagg_tab_para_p1 SET (parallel_workers = 0); ALTER TABLE pagg_tab_para_p3 SET (parallel_workers = 0); ANALYZE pagg_tab_para; EXPLAIN ( COSTS OFF ) SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; ALTER TABLE pagg_tab_para_p2 SET (parallel_workers = 0); ANALYZE pagg_tab_para; EXPLAIN ( COSTS OFF ) SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; -- Reset parallelism parameters to get partitionwise aggregation plan. RESET min_parallel_table_scan_size; RESET parallel_setup_cost; EXPLAIN ( COSTS OFF ) SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; pgFormatter-4.2/t/pg-test-files/expected/partition_info.sql000066400000000000000000000140231361326045100241460ustar00rootroot00000000000000-- -- Tests for functions providing information about partitions -- SELECT * FROM pg_partition_tree (NULL); SELECT * FROM pg_partition_tree (0); SELECT * FROM pg_partition_ancestors (NULL); SELECT * FROM pg_partition_ancestors (0); SELECT pg_partition_root (NULL); SELECT pg_partition_root (0); -- Test table partition trees CREATE TABLE ptif_test ( a int, b int ) PARTITION BY RANGE (a); CREATE TABLE ptif_test0 PARTITION OF ptif_test FOR VALUES FROM (MINVALUE) TO (0) PARTITION BY LIST (b); CREATE TABLE ptif_test01 PARTITION OF ptif_test0 FOR VALUES IN (1); CREATE TABLE ptif_test1 PARTITION OF ptif_test FOR VALUES FROM (0) TO (100) PARTITION BY LIST (b); CREATE TABLE ptif_test11 PARTITION OF ptif_test1 FOR VALUES IN (1); CREATE TABLE ptif_test2 PARTITION OF ptif_test FOR VALUES FROM (100) TO (200); -- This partitioned table should remain with no partitions. CREATE TABLE ptif_test3 PARTITION OF ptif_test FOR VALUES FROM (200) TO (MAXVALUE) PARTITION BY LIST (b); -- Test pg_partition_root for tables SELECT pg_partition_root ('ptif_test'); SELECT pg_partition_root ('ptif_test0'); SELECT pg_partition_root ('ptif_test01'); SELECT pg_partition_root ('ptif_test3'); -- Test index partition tree CREATE INDEX ptif_test_index ON ONLY ptif_test (a); CREATE INDEX ptif_test0_index ON ONLY ptif_test0 (a); ALTER INDEX ptif_test_index ATTACH PARTITION ptif_test0_index; CREATE INDEX ptif_test01_index ON ptif_test01 (a); ALTER INDEX ptif_test0_index ATTACH PARTITION ptif_test01_index; CREATE INDEX ptif_test1_index ON ONLY ptif_test1 (a); ALTER INDEX ptif_test_index ATTACH PARTITION ptif_test1_index; CREATE INDEX ptif_test11_index ON ptif_test11 (a); ALTER INDEX ptif_test1_index ATTACH PARTITION ptif_test11_index; CREATE INDEX ptif_test2_index ON ptif_test2 (a); ALTER INDEX ptif_test_index ATTACH PARTITION ptif_test2_index; CREATE INDEX ptif_test3_index ON ptif_test3 (a); ALTER INDEX ptif_test_index ATTACH PARTITION ptif_test3_index; -- Test pg_partition_root for indexes SELECT pg_partition_root ('ptif_test_index'); SELECT pg_partition_root ('ptif_test0_index'); SELECT pg_partition_root ('ptif_test01_index'); SELECT pg_partition_root ('ptif_test3_index'); -- List all tables members of the tree SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree ('ptif_test'); -- List tables from an intermediate level SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree ('ptif_test0') p JOIN pg_class c ON (p.relid = c.oid); -- List from leaf table SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree ('ptif_test01') p JOIN pg_class c ON (p.relid = c.oid); -- List from partitioned table with no partitions SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree ('ptif_test3') p JOIN pg_class c ON (p.relid = c.oid); -- List all ancestors of root and leaf tables SELECT * FROM pg_partition_ancestors ('ptif_test01'); SELECT * FROM pg_partition_ancestors ('ptif_test'); -- List all members using pg_partition_root with leaf table reference SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree (pg_partition_root ('ptif_test01')) p JOIN pg_class c ON (p.relid = c.oid); -- List all indexes members of the tree SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree ('ptif_test_index'); -- List indexes from an intermediate level SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree ('ptif_test0_index') p JOIN pg_class c ON (p.relid = c.oid); -- List from leaf index SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree ('ptif_test01_index') p JOIN pg_class c ON (p.relid = c.oid); -- List from partitioned index with no partitions SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree ('ptif_test3_index') p JOIN pg_class c ON (p.relid = c.oid); -- List all members using pg_partition_root with leaf index reference SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree (pg_partition_root ('ptif_test01_index')) p JOIN pg_class c ON (p.relid = c.oid); -- List all ancestors of root and leaf indexes SELECT * FROM pg_partition_ancestors ('ptif_test01_index'); SELECT * FROM pg_partition_ancestors ('ptif_test_index'); DROP TABLE ptif_test; -- Table that is not part of any partition tree is not listed. CREATE TABLE ptif_normal_table ( a int ); SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree ('ptif_normal_table'); SELECT * FROM pg_partition_ancestors ('ptif_normal_table'); SELECT pg_partition_root ('ptif_normal_table'); DROP TABLE ptif_normal_table; -- Various partitioning-related functions return empty/NULL if passed relations -- of types that cannot be part of a partition tree; for example, views, -- materialized views, legacy inheritance children or parents, etc. CREATE VIEW ptif_test_view AS SELECT 1; CREATE MATERIALIZED VIEW ptif_test_matview AS SELECT 1; CREATE TABLE ptif_li_parent (); CREATE TABLE ptif_li_child () INHERITS ( ptif_li_parent ); SELECT * FROM pg_partition_tree ('ptif_test_view'); SELECT * FROM pg_partition_tree ('ptif_test_matview'); SELECT * FROM pg_partition_tree ('ptif_li_parent'); SELECT * FROM pg_partition_tree ('ptif_li_child'); SELECT * FROM pg_partition_ancestors ('ptif_test_view'); SELECT * FROM pg_partition_ancestors ('ptif_test_matview'); SELECT * FROM pg_partition_ancestors ('ptif_li_parent'); SELECT * FROM pg_partition_ancestors ('ptif_li_child'); SELECT pg_partition_root ('ptif_test_view'); SELECT pg_partition_root ('ptif_test_matview'); SELECT pg_partition_root ('ptif_li_parent'); SELECT pg_partition_root ('ptif_li_child'); DROP VIEW ptif_test_view; DROP MATERIALIZED VIEW ptif_test_matview; DROP TABLE ptif_li_parent, ptif_li_child; pgFormatter-4.2/t/pg-test-files/expected/partition_join.sql000066400000000000000000000776671361326045100242010ustar00rootroot00000000000000-- -- PARTITION_JOIN -- Test partitionwise join between partitioned tables -- -- Enable partitionwise join, which by default is disabled. SET enable_partitionwise_join TO TRUE; -- -- partitioned by a single column -- CREATE TABLE prt1 ( a int, b int, c varchar ) PARTITION BY RANGE (a); CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250); CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600); CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500); INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 2 = 0; CREATE INDEX iprt1_p1_a ON prt1_p1 (a); CREATE INDEX iprt1_p2_a ON prt1_p2 (a); CREATE INDEX iprt1_p3_a ON prt1_p3 (a); ANALYZE prt1; CREATE TABLE prt2 ( a int, b int, c varchar ) PARTITION BY RANGE (b); CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250); CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500); CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600); INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 3 = 0; CREATE INDEX iprt2_p1_b ON prt2_p1 (b); CREATE INDEX iprt2_p2_b ON prt2_p2 (b); CREATE INDEX iprt2_p3_b ON prt2_p3 (b); ANALYZE prt2; -- inner join EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; -- left outer join, with whole-row reference; partitionwise join does not apply EXPLAIN ( COSTS OFF ) SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; -- right outer join EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b; -- full outer join, with placeholder vars EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM ( SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN ( SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.a OR t2.phv = t2.b ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM ( SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN ( SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.a OR t2.phv = t2.b ORDER BY t1.a, t2.b; -- Join with pruned partitions from joining relations EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.b = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.b = 0 ORDER BY t1.a, t2.b; -- Currently we can't do partitioned join if nullable-side partitions are pruned EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM ( SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN ( SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM ( SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN ( SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; -- Currently we can't do partitioned join if nullable-side partitions are pruned EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM ( SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN ( SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM ( SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN ( SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b; -- Semi-join EXPLAIN ( COSTS OFF ) SELECT t1.* FROM prt1 t1 WHERE t1.a IN ( SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a; SELECT t1.* FROM prt1 t1 WHERE t1.a IN ( SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a; -- Anti-join with aggregates EXPLAIN ( COSTS OFF ) SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS ( SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b); SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS ( SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b); -- lateral reference EXPLAIN ( COSTS OFF ) SELECT * FROM prt1 t1 LEFT JOIN LATERAL ( SELECT t2.a AS t2a, t3.a AS t3a, least (t1.a, t2.a, t3.b) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss ON t1.a = ss.t2a WHERE t1.b = 0 ORDER BY t1.a; SELECT * FROM prt1 t1 LEFT JOIN LATERAL ( SELECT t2.a AS t2a, t3.a AS t3a, least (t1.a, t2.a, t3.b) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss ON t1.a = ss.t2a WHERE t1.b = 0 ORDER BY t1.a; EXPLAIN ( COSTS OFF ) SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL ( SELECT t2.a AS t2a, t3.a AS t3a, t2.b t2b, t2.c t2c, least (t1.a, t2.a, t3.b) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss ON t1.c = ss.t2c WHERE (t1.b + coalesce(ss.t2b, 0)) = 0 ORDER BY t1.a; SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL ( SELECT t2.a AS t2a, t3.a AS t3a, t2.b t2b, t2.c t2c, least (t1.a, t2.a, t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss ON t1.c = ss.t2c WHERE (t1.b + coalesce(ss.t2b, 0)) = 0 ORDER BY t1.a; -- -- partitioned by expression -- CREATE TABLE prt1_e ( a int, b int, c int ) PARTITION BY RANGE (((a + b) / 2)); CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES FROM (0) TO (250); CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES FROM (250) TO (500); CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES FROM (500) TO (600); INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i; CREATE INDEX iprt1_e_p1_ab2 ON prt1_e_p1 (((a + b) / 2)); CREATE INDEX iprt1_e_p2_ab2 ON prt1_e_p2 (((a + b) / 2)); CREATE INDEX iprt1_e_p3_ab2 ON prt1_e_p3 (((a + b) / 2)); ANALYZE prt1_e; CREATE TABLE prt2_e ( a int, b int, c int ) PARTITION BY RANGE (((b + a) / 2)); CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES FROM (0) TO (250); CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES FROM (250) TO (500); CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES FROM (500) TO (600); INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i; ANALYZE prt2_e; EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b) / 2 = (t2.b + t2.a) / 2 AND t1.c = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b) / 2 = (t2.b + t2.a) / 2 AND t1.c = 0 ORDER BY t1.a, t2.b; -- -- N-way join -- EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b) / 2 AND t1.b = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b) / 2 AND t1.b = 0 ORDER BY t1.a, t2.b; EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b) / 2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b) / 2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b) / 2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b) / 2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; -- Cases with non-nullable expressions in subquery results; -- make sure these go to null as expected EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM (( SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN ( SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b)) FULL JOIN ( SELECT 50 phv, * FROM prt1_e WHERE prt1_e.c = 0) t3 ON (t1.a = (t3.a + t3.b) / 2) WHERE t1.a = t1.phv OR t2.b = t2.phv OR (t3.a + t3.b) / 2 = t3.phv ORDER BY t1.a, t2.b, t3.a + t3.b; SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM (( SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN ( SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b)) FULL JOIN ( SELECT 50 phv, * FROM prt1_e WHERE prt1_e.c = 0) t3 ON (t1.a = (t3.a + t3.b) / 2) WHERE t1.a = t1.phv OR t2.b = t2.phv OR (t3.a + t3.b) / 2 = t3.phv ORDER BY t1.a, t2.b, t3.a + t3.b; -- Semi-join EXPLAIN ( COSTS OFF ) SELECT t1.* FROM prt1 t1 WHERE t1.a IN ( SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.a = 0 AND t1.b = (t2.a + t2.b) / 2) AND t1.b = 0 ORDER BY t1.a; SELECT t1.* FROM prt1 t1 WHERE t1.a IN ( SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.a = 0 AND t1.b = (t2.a + t2.b) / 2) AND t1.b = 0 ORDER BY t1.a; EXPLAIN ( COSTS OFF ) SELECT t1.* FROM prt1 t1 WHERE t1.a IN ( SELECT t1.b FROM prt2 t1 WHERE t1.b IN ( SELECT (t1.a + t1.b) / 2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; SELECT t1.* FROM prt1 t1 WHERE t1.a IN ( SELECT t1.b FROM prt2 t1 WHERE t1.b IN ( SELECT (t1.a + t1.b) / 2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; -- test merge joins SET enable_hashjoin TO OFF; SET enable_nestloop TO OFF; EXPLAIN ( COSTS OFF ) SELECT t1.* FROM prt1 t1 WHERE t1.a IN ( SELECT t1.b FROM prt2 t1 WHERE t1.b IN ( SELECT (t1.a + t1.b) / 2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; SELECT t1.* FROM prt1 t1 WHERE t1.a IN ( SELECT t1.b FROM prt2 t1 WHERE t1.b IN ( SELECT (t1.a + t1.b) / 2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b) / 2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b) / 2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; -- MergeAppend on nullable column -- This should generate a partitionwise join, but currently fails to EXPLAIN ( COSTS OFF ) SELECT t1.a, t2.b FROM ( SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN ( SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t2.b FROM ( SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN ( SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; -- merge join when expression with whole-row reference needs to be sorted; -- partitionwise join does not apply EXPLAIN ( COSTS OFF ) SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2.b ORDER BY t1.a; SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2.b ORDER BY t1.a; RESET enable_hashjoin; RESET enable_nestloop; -- -- partitioned by multiple columns -- CREATE TABLE prt1_m ( a int, b int, c int ) PARTITION BY RANGE (a, ((a + b) / 2)); CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES FROM (0, 0) TO (250, 250); CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES FROM (250, 250) TO (500, 500); CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES FROM (500, 500) TO (600, 600); INSERT INTO prt1_m SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i; ANALYZE prt1_m; CREATE TABLE prt2_m ( a int, b int, c int ) PARTITION BY RANGE (((b + a) / 2), b); CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES FROM (0, 0) TO (250, 250); CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES FROM (250, 250) TO (500, 500); CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES FROM (500, 500) TO (600, 600); INSERT INTO prt2_m SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i; ANALYZE prt2_m; EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM ( SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN ( SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a) / 2 AND t2.b = (t1.a + t1.b) / 2) ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM ( SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN ( SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a) / 2 AND t2.b = (t1.a + t1.b) / 2) ORDER BY t1.a, t2.b; -- -- tests for list partitioned tables. -- CREATE TABLE plt1 ( a int, b int, c text ) PARTITION BY LIST (c); CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0003', '0004', '0010'); CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0001', '0005', '0002', '0009'); CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0006', '0007', '0008', '0011'); INSERT INTO plt1 SELECT i, i, to_char(i / 50, 'FM0000') FROM generate_series(0, 599, 2) i; ANALYZE plt1; CREATE TABLE plt2 ( a int, b int, c text ) PARTITION BY LIST (c); CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0000', '0003', '0004', '0010'); CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0001', '0005', '0002', '0009'); CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0006', '0007', '0008', '0011'); INSERT INTO plt2 SELECT i, i, to_char(i / 50, 'FM0000') FROM generate_series(0, 599, 3) i; ANALYZE plt2; -- -- list partitioned by expression -- CREATE TABLE plt1_e ( a int, b int, c text ) PARTITION BY LIST (ltrim(c, 'A')); CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0000', '0003', '0004', '0010'); CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0001', '0005', '0002', '0009'); CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0006', '0007', '0008', '0011'); INSERT INTO plt1_e SELECT i, i, 'A' || to_char(i / 50, 'FM0000') FROM generate_series(0, 599, 2) i; ANALYZE plt1_e; -- test partition matching with N-way join EXPLAIN ( COSTS OFF ) SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; -- joins where one of the relations is proven empty EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a = 1 AND t1.a = 2; EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM ( SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 LEFT JOIN prt2 t2 ON t1.a = t2.b; EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM ( SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b, prt1 t3 WHERE t2.b = t3.a; EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM ( SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b; -- -- tests for hash partitioned tables. -- CREATE TABLE pht1 ( a int, b int, c text ) PARTITION BY HASH (c); CREATE TABLE pht1_p1 PARTITION OF pht1 FOR VALUES WITH (MODULUS 3, REMAINDER 0); CREATE TABLE pht1_p2 PARTITION OF pht1 FOR VALUES WITH (MODULUS 3, REMAINDER 1); CREATE TABLE pht1_p3 PARTITION OF pht1 FOR VALUES WITH (MODULUS 3, REMAINDER 2); INSERT INTO pht1 SELECT i, i, to_char(i / 50, 'FM0000') FROM generate_series(0, 599, 2) i; ANALYZE pht1; CREATE TABLE pht2 ( a int, b int, c text ) PARTITION BY HASH (c); CREATE TABLE pht2_p1 PARTITION OF pht2 FOR VALUES WITH (MODULUS 3, REMAINDER 0); CREATE TABLE pht2_p2 PARTITION OF pht2 FOR VALUES WITH (MODULUS 3, REMAINDER 1); CREATE TABLE pht2_p3 PARTITION OF pht2 FOR VALUES WITH (MODULUS 3, REMAINDER 2); INSERT INTO pht2 SELECT i, i, to_char(i / 50, 'FM0000') FROM generate_series(0, 599, 3) i; ANALYZE pht2; -- -- hash partitioned by expression -- CREATE TABLE pht1_e ( a int, b int, c text ) PARTITION BY HASH (ltrim(c, 'A')); CREATE TABLE pht1_e_p1 PARTITION OF pht1_e FOR VALUES WITH (MODULUS 3, REMAINDER 0); CREATE TABLE pht1_e_p2 PARTITION OF pht1_e FOR VALUES WITH (MODULUS 3, REMAINDER 1); CREATE TABLE pht1_e_p3 PARTITION OF pht1_e FOR VALUES WITH (MODULUS 3, REMAINDER 2); INSERT INTO pht1_e SELECT i, i, 'A' || to_char(i / 50, 'FM0000') FROM generate_series(0, 299, 2) i; ANALYZE pht1_e; -- test partition matching with N-way join EXPLAIN ( COSTS OFF ) SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM pht1 t1, pht2 t2, pht1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM pht1 t1, pht2 t2, pht1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; -- test default partition behavior for range ALTER TABLE prt1 DETACH PARTITION prt1_p3; ALTER TABLE prt1 ATTACH PARTITION prt1_p3 DEFAULT; ANALYZE prt1; ALTER TABLE prt2 DETACH PARTITION prt2_p3; ALTER TABLE prt2 ATTACH PARTITION prt2_p3 DEFAULT; ANALYZE prt2; EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; -- test default partition behavior for list ALTER TABLE plt1 DETACH PARTITION plt1_p3; ALTER TABLE plt1 ATTACH PARTITION plt1_p3 DEFAULT; ANALYZE plt1; ALTER TABLE plt2 DETACH PARTITION plt2_p3; ALTER TABLE plt2 ATTACH PARTITION plt2_p3 DEFAULT; ANALYZE plt2; EXPLAIN ( COSTS OFF ) SELECT avg(t1.a), avg(t2.b), t1.c, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.a % 25 = 0 GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; -- -- multiple levels of partitioning -- CREATE TABLE prt1_l ( a int, b int, c varchar ) PARTITION BY RANGE (a); CREATE TABLE prt1_l_p1 PARTITION OF prt1_l FOR VALUES FROM (0) TO (250); CREATE TABLE prt1_l_p2 PARTITION OF prt1_l FOR VALUES FROM (250) TO (500) PARTITION BY LIST (c); CREATE TABLE prt1_l_p2_p1 PARTITION OF prt1_l_p2 FOR VALUES IN ('0000', '0001'); CREATE TABLE prt1_l_p2_p2 PARTITION OF prt1_l_p2 FOR VALUES IN ('0002', '0003'); CREATE TABLE prt1_l_p3 PARTITION OF prt1_l FOR VALUES FROM (500) TO (600) PARTITION BY RANGE (b); CREATE TABLE prt1_l_p3_p1 PARTITION OF prt1_l_p3 FOR VALUES FROM (0) TO (13); CREATE TABLE prt1_l_p3_p2 PARTITION OF prt1_l_p3 FOR VALUES FROM (13) TO (25); INSERT INTO prt1_l SELECT i, i % 25, to_char(i % 4, 'FM0000') FROM generate_series(0, 599, 2) i; ANALYZE prt1_l; CREATE TABLE prt2_l ( a int, b int, c varchar ) PARTITION BY RANGE (b); CREATE TABLE prt2_l_p1 PARTITION OF prt2_l FOR VALUES FROM (0) TO (250); CREATE TABLE prt2_l_p2 PARTITION OF prt2_l FOR VALUES FROM (250) TO (500) PARTITION BY LIST (c); CREATE TABLE prt2_l_p2_p1 PARTITION OF prt2_l_p2 FOR VALUES IN ('0000', '0001'); CREATE TABLE prt2_l_p2_p2 PARTITION OF prt2_l_p2 FOR VALUES IN ('0002', '0003'); CREATE TABLE prt2_l_p3 PARTITION OF prt2_l FOR VALUES FROM (500) TO (600) PARTITION BY RANGE (a); CREATE TABLE prt2_l_p3_p1 PARTITION OF prt2_l_p3 FOR VALUES FROM (0) TO (13); CREATE TABLE prt2_l_p3_p2 PARTITION OF prt2_l_p3 FOR VALUES FROM (13) TO (25); INSERT INTO prt2_l SELECT i % 25, i, to_char(i % 4, 'FM0000') FROM generate_series(0, 599, 3) i; ANALYZE prt2_l; -- inner join, qual covering only top-level partitions EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; -- left join EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t1.b = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t1.b = 0 ORDER BY t1.a, t2.b; -- right join EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.a = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.a = 0 ORDER BY t1.a, t2.b; -- full join EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM ( SELECT * FROM prt1_l WHERE prt1_l.b = 0) t1 FULL JOIN ( SELECT * FROM prt2_l WHERE prt2_l.a = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM ( SELECT * FROM prt1_l WHERE prt1_l.b = 0) t1 FULL JOIN ( SELECT * FROM prt2_l WHERE prt2_l.a = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b; -- lateral partitionwise join EXPLAIN ( COSTS OFF ) SELECT * FROM prt1_l t1 LEFT JOIN LATERAL ( SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.b AS t3b, least (t1.a, t2.a, t3.b) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.c = t3.c)) ss ON t1.a = ss.t2a AND t1.c = ss.t2c WHERE t1.b = 0 ORDER BY t1.a; SELECT * FROM prt1_l t1 LEFT JOIN LATERAL ( SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.b AS t3b, least (t1.a, t2.a, t3.b) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.c = t3.c)) ss ON t1.a = ss.t2a AND t1.c = ss.t2c WHERE t1.b = 0 ORDER BY t1.a; -- join with one side empty EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM ( SELECT * FROM prt1_l WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c; -- Test case to verify proper handling of subqueries in a partitioned delete. -- The weird-looking lateral join is just there to force creation of a -- nestloop parameter within the subquery, which exposes the problem if the -- planner fails to make multiple copies of the subquery as appropriate. EXPLAIN ( COSTS OFF ) DELETE FROM prt1_l WHERE EXISTS ( SELECT 1 FROM int4_tbl, LATERAL ( SELECT int4_tbl.f1 FROM int8_tbl LIMIT 2) ss WHERE prt1_l.c IS NULL); -- -- negative testcases -- CREATE TABLE prt1_n ( a int, b int, c varchar ) PARTITION BY RANGE (c); CREATE TABLE prt1_n_p1 PARTITION OF prt1_n FOR VALUES FROM ('0000') TO ('0250'); CREATE TABLE prt1_n_p2 PARTITION OF prt1_n FOR VALUES FROM ('0250') TO ('0500'); INSERT INTO prt1_n SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 499, 2) i; ANALYZE prt1_n; CREATE TABLE prt2_n ( a int, b int, c text ) PARTITION BY LIST (c); CREATE TABLE prt2_n_p1 PARTITION OF prt2_n FOR VALUES IN ('0000', '0003', '0004', '0010', '0006', '0007'); CREATE TABLE prt2_n_p2 PARTITION OF prt2_n FOR VALUES IN ('0001', '0005', '0002', '0009', '0008', '0011'); INSERT INTO prt2_n SELECT i, i, to_char(i / 50, 'FM0000') FROM generate_series(0, 599, 2) i; ANALYZE prt2_n; CREATE TABLE prt3_n ( a int, b int, c text ) PARTITION BY LIST (c); CREATE TABLE prt3_n_p1 PARTITION OF prt3_n FOR VALUES IN ('0000', '0004', '0006', '0007'); CREATE TABLE prt3_n_p2 PARTITION OF prt3_n FOR VALUES IN ('0001', '0002', '0008', '0010'); CREATE TABLE prt3_n_p3 PARTITION OF prt3_n FOR VALUES IN ('0003', '0005', '0009', '0011'); INSERT INTO prt2_n SELECT i, i, to_char(i / 50, 'FM0000') FROM generate_series(0, 599, 2) i; ANALYZE prt3_n; CREATE TABLE prt4_n ( a int, b int, c text ) PARTITION BY RANGE (a); CREATE TABLE prt4_n_p1 PARTITION OF prt4_n FOR VALUES FROM (0) TO (300); CREATE TABLE prt4_n_p2 PARTITION OF prt4_n FOR VALUES FROM (300) TO (500); CREATE TABLE prt4_n_p3 PARTITION OF prt4_n FOR VALUES FROM (500) TO (600); INSERT INTO prt4_n SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i; ANALYZE prt4_n; -- partitionwise join can not be applied if the partition ranges differ EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2 WHERE t1.a = t2.a; EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2, prt2 t3 WHERE t1.a = t2.a AND t1.a = t3.b; -- partitionwise join can not be applied if there are no equi-join conditions -- between partition keys EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON (t1.a < t2.b); -- equi-join with join condition on partial keys does not qualify for -- partitionwise join EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a) / 2; -- equi-join between out-of-order partition key columns does not qualify for -- partitionwise join EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = t2.b; -- equi-join between non-key columns does not qualify for partitionwise join EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.c = t2.c; -- partitionwise join can not be applied between tables with different -- partition lists EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 LEFT JOIN prt2_n t2 ON (t1.c = t2.c); EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 JOIN prt2_n t2 ON (t1.c = t2.c) JOIN plt1 t3 ON (t1.c = t3.c); -- partitionwise join can not be applied for a join between list and range -- partitioned table EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 FULL JOIN prt1 t2 ON (t1.c = t2.c); -- partitionwise join can not be applied if only one of joining table has -- default partition ALTER TABLE prt2 DETACH PARTITION prt2_p3; ALTER TABLE prt2 ATTACH PARTITION prt2_p3 FOR VALUES FROM (500) TO (600); ANALYZE prt2; EXPLAIN ( COSTS OFF ) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; pgFormatter-4.2/t/pg-test-files/expected/partition_prune.sql000066400000000000000000001406701361326045100243540ustar00rootroot00000000000000-- -- Test partitioning planner code -- CREATE TABLE lp ( a char ) PARTITION BY LIST (a); CREATE TABLE lp_default PARTITION OF lp DEFAULT; CREATE TABLE lp_ef PARTITION OF lp FOR VALUES IN ('e', 'f'); CREATE TABLE lp_ad PARTITION OF lp FOR VALUES IN ('a', 'd'); CREATE TABLE lp_bc PARTITION OF lp FOR VALUES IN ('b', 'c'); CREATE TABLE lp_g PARTITION OF lp FOR VALUES IN ('g'); CREATE TABLE lp_null PARTITION OF lp FOR VALUES IN (NULL); EXPLAIN ( COSTS OFF ) SELECT * FROM lp; EXPLAIN ( COSTS OFF ) SELECT * FROM lp WHERE a > 'a' AND a < 'd'; EXPLAIN ( COSTS OFF ) SELECT * FROM lp WHERE a > 'a' AND a <= 'd'; EXPLAIN ( COSTS OFF ) SELECT * FROM lp WHERE a = 'a'; EXPLAIN ( COSTS OFF ) SELECT * FROM lp WHERE 'a' = a; /* commuted */ EXPLAIN ( COSTS OFF ) SELECT * FROM lp WHERE a IS NOT NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM lp WHERE a IS NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM lp WHERE a = 'a' OR a = 'c'; EXPLAIN ( COSTS OFF ) SELECT * FROM lp WHERE a IS NOT NULL AND (a = 'a' OR a = 'c'); EXPLAIN ( COSTS OFF ) SELECT * FROM lp WHERE a <> 'g'; EXPLAIN ( COSTS OFF ) SELECT * FROM lp WHERE a <> 'a' AND a <> 'd'; EXPLAIN ( COSTS OFF ) SELECT * FROM lp WHERE a NOT IN ('a', 'd'); -- collation matches the partitioning collation, pruning works CREATE TABLE coll_pruning ( a text COLLATE "C" ) PARTITION BY LIST (a); CREATE TABLE coll_pruning_a PARTITION OF coll_pruning FOR VALUES IN ('a'); CREATE TABLE coll_pruning_b PARTITION OF coll_pruning FOR VALUES IN ('b'); CREATE TABLE coll_pruning_def PARTITION OF coll_pruning DEFAULT; EXPLAIN ( COSTS OFF ) SELECT * FROM coll_pruning WHERE a COLLATE "C" = 'a' COLLATE "C"; -- collation doesn't match the partitioning collation, no pruning occurs EXPLAIN ( COSTS OFF ) SELECT * FROM coll_pruning WHERE a COLLATE "POSIX" = 'a' COLLATE "POSIX"; CREATE TABLE rlp ( a int, b varchar ) PARTITION BY RANGE (a); CREATE TABLE rlp_default PARTITION OF rlp DEFAULT PARTITION BY LIST (a); CREATE TABLE rlp_default_default PARTITION OF rlp_default DEFAULT; CREATE TABLE rlp_default_10 PARTITION OF rlp_default FOR VALUES IN (10); CREATE TABLE rlp_default_30 PARTITION OF rlp_default FOR VALUES IN (30); CREATE TABLE rlp_default_null PARTITION OF rlp_default FOR VALUES IN (NULL); CREATE TABLE rlp1 PARTITION OF rlp FOR VALUES FROM (MINVALUE) TO (1); CREATE TABLE rlp2 PARTITION OF rlp FOR VALUES FROM (1) TO (10); CREATE TABLE rlp3 ( b varchar, a int ) PARTITION BY LIST (b varchar_ops); CREATE TABLE rlp3_default PARTITION OF rlp3 DEFAULT; CREATE TABLE rlp3abcd PARTITION OF rlp3 FOR VALUES IN ('ab', 'cd'); CREATE TABLE rlp3efgh PARTITION OF rlp3 FOR VALUES IN ('ef', 'gh'); CREATE TABLE rlp3nullxy PARTITION OF rlp3 FOR VALUES IN (NULL, 'xy'); ALTER TABLE rlp ATTACH PARTITION rlp3 FOR VALUES FROM (15) TO (20); CREATE TABLE rlp4 PARTITION OF rlp FOR VALUES FROM (20) TO (30) PARTITION BY RANGE (a); CREATE TABLE rlp4_default PARTITION OF rlp4 DEFAULT; CREATE TABLE rlp4_1 PARTITION OF rlp4 FOR VALUES FROM (20) TO (25); CREATE TABLE rlp4_2 PARTITION OF rlp4 FOR VALUES FROM (25) TO (29); CREATE TABLE rlp5 PARTITION OF rlp FOR VALUES FROM (31) TO (MAXVALUE) PARTITION BY RANGE (a); CREATE TABLE rlp5_default PARTITION OF rlp5 DEFAULT; CREATE TABLE rlp5_1 PARTITION OF rlp5 FOR VALUES FROM (31) TO (40); EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a < 1; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE 1 > a; /* commuted */ EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a <= 1; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a = 1; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a = 1::bigint; /* same as above */ EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a = 1::numeric; /* no pruning */ EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a <= 10; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a > 10; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a < 15; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a <= 15; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a > 15 AND b = 'ab'; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a = 16; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a = 16 AND b IN ('not', 'in', 'here'); EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a = 16 AND b < 'ab'; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a = 16 AND b <= 'ab'; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a = 16 AND b IS NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a = 16 AND b IS NOT NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a IS NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a IS NOT NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a > 30; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a = 30; /* only default is scanned */ EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a <= 31; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a = 1 OR a = 7; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a = 1 OR b = 'ab'; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a > 20 AND a < 27; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a = 29; EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a >= 29; -- redundant clauses are eliminated EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a > 1 AND a = 10; /* only default */ EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a > 1 AND a >= 15; /* rlp3 onwards, including default */ EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a = 1 AND a = 3; /* empty */ EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE (a = 1 AND a = 3) OR (a > 1 AND a = 15); -- multi-column keys CREATE TABLE mc3p ( a int, b int, c int ) PARTITION BY RANGE (a, abs(b), c); CREATE TABLE mc3p_default PARTITION OF mc3p DEFAULT; CREATE TABLE mc3p0 PARTITION OF mc3p FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, 1, 1); CREATE TABLE mc3p1 PARTITION OF mc3p FOR VALUES FROM (1, 1, 1) TO (10, 5, 10); CREATE TABLE mc3p2 PARTITION OF mc3p FOR VALUES FROM (10, 5, 10) TO (10, 10, 10); CREATE TABLE mc3p3 PARTITION OF mc3p FOR VALUES FROM (10, 10, 10) TO (10, 10, 20); CREATE TABLE mc3p4 PARTITION OF mc3p FOR VALUES FROM (10, 10, 20) TO (10, MAXVALUE, MAXVALUE); CREATE TABLE mc3p5 PARTITION OF mc3p FOR VALUES FROM (11, 1, 1) TO (20, 10, 10); CREATE TABLE mc3p6 PARTITION OF mc3p FOR VALUES FROM (20, 10, 10) TO (20, 20, 20); CREATE TABLE mc3p7 PARTITION OF mc3p FOR VALUES FROM (20, 20, 20) TO (MAXVALUE, MAXVALUE, MAXVALUE); EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE a = 1; EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE a = 1 AND abs(b) < 1; EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE a = 1 AND abs(b) = 1; EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE a = 1 AND abs(b) = 1 AND c < 8; EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE a = 10 AND abs(b) BETWEEN 5 AND 35; EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE a > 10; EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE a >= 10; EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE a < 10; EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE a <= 10 AND abs(b) < 10; EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE a = 11 AND abs(b) = 0; EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE a = 20 AND abs(b) = 10 AND c = 100; EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE a > 20; EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE a >= 20; EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE (a = 1 AND abs(b) = 1 AND c = 1) OR (a = 10 AND abs(b) = 5 AND c = 10) OR (a > 11 AND a < 20); EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE (a = 1 AND abs(b) = 1 AND c = 1) OR (a = 10 AND abs(b) = 5 AND c = 10) OR (a > 11 AND a < 20) OR a < 1; EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE (a = 1 AND abs(b) = 1 AND c = 1) OR (a = 10 AND abs(b) = 5 AND c = 10) OR (a > 11 AND a < 20) OR a < 1 OR a = 1; EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE a = 1 OR abs(b) = 1 OR c = 1; EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE (a = 1 AND abs(b) = 1) OR (a = 10 AND abs(b) = 10); EXPLAIN ( COSTS OFF ) SELECT * FROM mc3p WHERE (a = 1 AND abs(b) = 1) OR (a = 10 AND abs(b) = 9); -- a simpler multi-column keys case CREATE TABLE mc2p ( a int, b int ) PARTITION BY RANGE (a, b); CREATE TABLE mc2p_default PARTITION OF mc2p DEFAULT; CREATE TABLE mc2p0 PARTITION OF mc2p FOR VALUES FROM (MINVALUE, MINVALUE) TO (1, MINVALUE); CREATE TABLE mc2p1 PARTITION OF mc2p FOR VALUES FROM (1, MINVALUE) TO (1, 1); CREATE TABLE mc2p2 PARTITION OF mc2p FOR VALUES FROM (1, 1) TO (2, MINVALUE); CREATE TABLE mc2p3 PARTITION OF mc2p FOR VALUES FROM (2, MINVALUE) TO (2, 1); CREATE TABLE mc2p4 PARTITION OF mc2p FOR VALUES FROM (2, 1) TO (2, MAXVALUE); CREATE TABLE mc2p5 PARTITION OF mc2p FOR VALUES FROM (2, MAXVALUE) TO (MAXVALUE, MAXVALUE); EXPLAIN ( COSTS OFF ) SELECT * FROM mc2p WHERE a < 2; EXPLAIN ( COSTS OFF ) SELECT * FROM mc2p WHERE a = 2 AND b < 1; EXPLAIN ( COSTS OFF ) SELECT * FROM mc2p WHERE a > 1; EXPLAIN ( COSTS OFF ) SELECT * FROM mc2p WHERE a = 1 AND b > 1; -- all partitions but the default one should be pruned EXPLAIN ( COSTS OFF ) SELECT * FROM mc2p WHERE a = 1 AND b IS NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM mc2p WHERE a IS NULL AND b IS NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM mc2p WHERE a IS NULL AND b = 1; EXPLAIN ( COSTS OFF ) SELECT * FROM mc2p WHERE a IS NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM mc2p WHERE b IS NULL; -- boolean partitioning CREATE TABLE boolpart ( a bool ) PARTITION BY LIST (a); CREATE TABLE boolpart_default PARTITION OF boolpart DEFAULT; CREATE TABLE boolpart_t PARTITION OF boolpart FOR VALUES IN ('true'); CREATE TABLE boolpart_f PARTITION OF boolpart FOR VALUES IN ('false'); EXPLAIN ( COSTS OFF ) SELECT * FROM boolpart WHERE a IN (TRUE, FALSE); EXPLAIN ( COSTS OFF ) SELECT * FROM boolpart WHERE a = FALSE; EXPLAIN ( COSTS OFF ) SELECT * FROM boolpart WHERE NOT a = FALSE; EXPLAIN ( COSTS OFF ) SELECT * FROM boolpart WHERE a IS TRUE OR a IS NOT TRUE; EXPLAIN ( COSTS OFF ) SELECT * FROM boolpart WHERE a IS NOT TRUE; EXPLAIN ( COSTS OFF ) SELECT * FROM boolpart WHERE a IS NOT TRUE AND a IS NOT FALSE; EXPLAIN ( COSTS OFF ) SELECT * FROM boolpart WHERE a IS unknown; EXPLAIN ( COSTS OFF ) SELECT * FROM boolpart WHERE a IS NOT unknown; -- test scalar-to-array operators CREATE TABLE coercepart ( a varchar ) PARTITION BY LIST (a); CREATE TABLE coercepart_ab PARTITION OF coercepart FOR VALUES IN ('ab'); CREATE TABLE coercepart_bc PARTITION OF coercepart FOR VALUES IN ('bc'); CREATE TABLE coercepart_cd PARTITION OF coercepart FOR VALUES IN ('cd'); EXPLAIN ( COSTS OFF ) SELECT * FROM coercepart WHERE a IN ('ab', to_char(125, '999')); EXPLAIN ( COSTS OFF ) SELECT * FROM coercepart WHERE a ~ ANY ('{ab}'); EXPLAIN ( COSTS OFF ) SELECT * FROM coercepart WHERE a !~ ALL ('{ab}'); EXPLAIN ( COSTS OFF ) SELECT * FROM coercepart WHERE a ~ ANY ('{ab,bc}'); EXPLAIN ( COSTS OFF ) SELECT * FROM coercepart WHERE a !~ ALL ('{ab,bc}'); DROP TABLE coercepart; CREATE TABLE part ( a int, b int ) PARTITION BY LIST (a); CREATE TABLE part_p1 PARTITION OF part FOR VALUES IN (- 2, - 1, 0, 1, 2); CREATE TABLE part_p2 PARTITION OF part DEFAULT PARTITION BY RANGE (a); CREATE TABLE part_p2_p1 PARTITION OF part_p2 DEFAULT; INSERT INTO part VALUES (- 1, - 1), (1, 1), (2, NULL), (NULL, - 2), (NULL, NULL); EXPLAIN ( COSTS OFF ) SELECT tableoid::regclass AS part, a, b FROM part WHERE a IS NULL ORDER BY 1, 2, 3; -- -- some more cases -- -- -- pruning for partitioned table appearing inside a sub-query -- -- pruning won't work for mc3p, because some keys are Params EXPLAIN ( COSTS OFF ) SELECT * FROM mc2p t1, LATERAL ( SELECT count(*) FROM mc3p t2 WHERE t2.a = t1.b AND abs(t2.b) = 1 AND t2.c = 1) s WHERE t1.a = 1; -- pruning should work fine, because values for a prefix of keys (a, b) are -- available EXPLAIN ( COSTS OFF ) SELECT * FROM mc2p t1, LATERAL ( SELECT count(*) FROM mc3p t2 WHERE t2.c = t1.b AND abs(t2.b) = 1 AND t2.a = 1) s WHERE t1.a = 1; -- also here, because values for all keys are provided EXPLAIN ( COSTS OFF ) SELECT * FROM mc2p t1, LATERAL ( SELECT count(*) FROM mc3p t2 WHERE t2.a = 1 AND abs(t2.b) = 1 AND t2.c = 1) s WHERE t1.a = 1; -- -- pruning with clauses containing <> operator -- -- doesn't prune range partitions CREATE TABLE rp ( a int ) PARTITION BY RANGE (a); CREATE TABLE rp0 PARTITION OF rp FOR VALUES FROM (MINVALUE) TO (1); CREATE TABLE rp1 PARTITION OF rp FOR VALUES FROM (1) TO (2); CREATE TABLE rp2 PARTITION OF rp FOR VALUES FROM (2) TO (MAXVALUE); EXPLAIN ( COSTS OFF ) SELECT * FROM rp WHERE a <> 1; EXPLAIN ( COSTS OFF ) SELECT * FROM rp WHERE a <> 1 AND a <> 2; -- null partition should be eliminated due to strict <> clause. EXPLAIN ( COSTS OFF ) SELECT * FROM lp WHERE a <> 'a'; -- ensure we detect contradictions in clauses; a can't be NULL and NOT NULL. EXPLAIN ( COSTS OFF ) SELECT * FROM lp WHERE a <> 'a' AND a IS NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM lp WHERE (a <> 'a' AND a <> 'd') OR a IS NULL; -- check that it also works for a partitioned table that's not root, -- which in this case are partitions of rlp that are themselves -- list-partitioned on b EXPLAIN ( COSTS OFF ) SELECT * FROM rlp WHERE a = 15 AND b <> 'ab' AND b <> 'cd' AND b <> 'xy' AND b IS NOT NULL; -- -- different collations for different keys with same expression -- CREATE TABLE coll_pruning_multi ( a text ) PARTITION BY RANGE (substr(a, 1) COLLATE "POSIX", substr(a, 1) COLLATE "C"); CREATE TABLE coll_pruning_multi1 PARTITION OF coll_pruning_multi FOR VALUES FROM ('a', 'a') TO ('a', 'e'); CREATE TABLE coll_pruning_multi2 PARTITION OF coll_pruning_multi FOR VALUES FROM ('a', 'e') TO ('a', 'z'); CREATE TABLE coll_pruning_multi3 PARTITION OF coll_pruning_multi FOR VALUES FROM ('b', 'a') TO ('b', 'e'); -- no pruning, because no value for the leading key EXPLAIN ( COSTS OFF ) SELECT * FROM coll_pruning_multi WHERE substr(a, 1) = 'e' COLLATE "C"; -- pruning, with a value provided for the leading key EXPLAIN ( COSTS OFF ) SELECT * FROM coll_pruning_multi WHERE substr(a, 1) = 'a' COLLATE "POSIX"; -- pruning, with values provided for both keys EXPLAIN ( COSTS OFF ) SELECT * FROM coll_pruning_multi WHERE substr(a, 1) = 'e' COLLATE "C" AND substr(a, 1) = 'a' COLLATE "POSIX"; -- -- LIKE operators don't prune -- CREATE TABLE like_op_noprune ( a text ) PARTITION BY LIST (a); CREATE TABLE like_op_noprune1 PARTITION OF like_op_noprune FOR VALUES IN ('ABC'); CREATE TABLE like_op_noprune2 PARTITION OF like_op_noprune FOR VALUES IN ('BCD'); EXPLAIN ( COSTS OFF ) SELECT * FROM like_op_noprune WHERE a LIKE '%BC'; -- -- tests wherein clause value requires a cross-type comparison function -- CREATE TABLE lparted_by_int2 ( a smallint ) PARTITION BY LIST (a); CREATE TABLE lparted_by_int2_1 PARTITION OF lparted_by_int2 FOR VALUES IN (1); CREATE TABLE lparted_by_int2_16384 PARTITION OF lparted_by_int2 FOR VALUES IN (16384); EXPLAIN ( COSTS OFF ) SELECT * FROM lparted_by_int2 WHERE a = 100000000000000; CREATE TABLE rparted_by_int2 ( a smallint ) PARTITION BY RANGE (a); CREATE TABLE rparted_by_int2_1 PARTITION OF rparted_by_int2 FOR VALUES FROM (1) TO (10); CREATE TABLE rparted_by_int2_16384 PARTITION OF rparted_by_int2 FOR VALUES FROM (10) TO (16384); -- all partitions pruned EXPLAIN ( COSTS OFF ) SELECT * FROM rparted_by_int2 WHERE a > 100000000000000; CREATE TABLE rparted_by_int2_maxvalue PARTITION OF rparted_by_int2 FOR VALUES FROM (16384) TO (MAXVALUE); -- all partitions but rparted_by_int2_maxvalue pruned EXPLAIN ( COSTS OFF ) SELECT * FROM rparted_by_int2 WHERE a > 100000000000000; DROP TABLE lp, coll_pruning, rlp, mc3p, mc2p, boolpart, rp, coll_pruning_multi, like_op_noprune, lparted_by_int2, rparted_by_int2; -- -- Test Partition pruning for HASH partitioning -- -- Use hand-rolled hash functions and operator classes to get predictable -- result on different matchines. See the definitions of -- part_part_test_int4_ops and part_test_text_ops in insert.sql. -- CREATE TABLE hp ( a int, b text ) PARTITION BY HASH (a part_test_int4_ops, b part_test_text_ops); CREATE TABLE hp0 PARTITION OF hp FOR VALUES WITH (MODULUS 4, REMAINDER 0); CREATE TABLE hp3 PARTITION OF hp FOR VALUES WITH (MODULUS 4, REMAINDER 3); CREATE TABLE hp1 PARTITION OF hp FOR VALUES WITH (MODULUS 4, REMAINDER 1); CREATE TABLE hp2 PARTITION OF hp FOR VALUES WITH (MODULUS 4, REMAINDER 2); INSERT INTO hp VALUES (NULL, NULL); INSERT INTO hp VALUES (1, NULL); INSERT INTO hp VALUES (1, 'xxx'); INSERT INTO hp VALUES (NULL, 'xxx'); INSERT INTO hp VALUES (2, 'xxx'); INSERT INTO hp VALUES (1, 'abcde'); SELECT tableoid::regclass, * FROM hp ORDER BY 1; -- partial keys won't prune, nor would non-equality conditions EXPLAIN ( COSTS OFF ) SELECT * FROM hp WHERE a = 1; EXPLAIN ( COSTS OFF ) SELECT * FROM hp WHERE b = 'xxx'; EXPLAIN ( COSTS OFF ) SELECT * FROM hp WHERE a IS NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM hp WHERE b IS NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM hp WHERE a < 1 AND b = 'xxx'; EXPLAIN ( COSTS OFF ) SELECT * FROM hp WHERE a <> 1 AND b = 'yyy'; EXPLAIN ( COSTS OFF ) SELECT * FROM hp WHERE a <> 1 AND b <> 'xxx'; -- pruning should work if either a value or a IS NULL clause is provided for -- each of the keys EXPLAIN ( COSTS OFF ) SELECT * FROM hp WHERE a IS NULL AND b IS NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM hp WHERE a = 1 AND b IS NULL; EXPLAIN ( COSTS OFF ) SELECT * FROM hp WHERE a = 1 AND b = 'xxx'; EXPLAIN ( COSTS OFF ) SELECT * FROM hp WHERE a IS NULL AND b = 'xxx'; EXPLAIN ( COSTS OFF ) SELECT * FROM hp WHERE a = 2 AND b = 'xxx'; EXPLAIN ( COSTS OFF ) SELECT * FROM hp WHERE a = 1 AND b = 'abcde'; EXPLAIN ( COSTS OFF ) SELECT * FROM hp WHERE (a = 1 AND b = 'abcde') OR (a = 2 AND b = 'xxx') OR (a IS NULL AND b IS NULL); DROP TABLE hp; -- -- Test runtime partition pruning -- CREATE TABLE ab ( a int NOT NULL, b int NOT NULL ) PARTITION BY LIST (a); CREATE TABLE ab_a2 PARTITION OF ab FOR VALUES IN (2) PARTITION BY LIST (b); CREATE TABLE ab_a2_b1 PARTITION OF ab_a2 FOR VALUES IN (1); CREATE TABLE ab_a2_b2 PARTITION OF ab_a2 FOR VALUES IN (2); CREATE TABLE ab_a2_b3 PARTITION OF ab_a2 FOR VALUES IN (3); CREATE TABLE ab_a1 PARTITION OF ab FOR VALUES IN (1) PARTITION BY LIST (b); CREATE TABLE ab_a1_b1 PARTITION OF ab_a1 FOR VALUES IN (1); CREATE TABLE ab_a1_b2 PARTITION OF ab_a1 FOR VALUES IN (2); CREATE TABLE ab_a1_b3 PARTITION OF ab_a1 FOR VALUES IN (3); CREATE TABLE ab_a3 PARTITION OF ab FOR VALUES IN (3) PARTITION BY LIST (b); CREATE TABLE ab_a3_b1 PARTITION OF ab_a3 FOR VALUES IN (1); CREATE TABLE ab_a3_b2 PARTITION OF ab_a3 FOR VALUES IN (2); CREATE TABLE ab_a3_b3 PARTITION OF ab_a3 FOR VALUES IN (3); -- Disallow index only scans as concurrent transactions may stop visibility -- bits being set causing "Heap Fetches" to be unstable in the EXPLAIN ANALYZE -- output. SET enable_indexonlyscan = OFF; PREPARE ab_q1 (int, int, int) AS SELECT * FROM ab WHERE a BETWEEN $1 AND $2 AND b <= $3; -- Execute query 5 times to allow choose_custom_plan -- to start considering a generic plan. EXECUTE ab_q1 (1, 8, 3); EXECUTE ab_q1 (1, 8, 3); EXECUTE ab_q1 (1, 8, 3); EXECUTE ab_q1 (1, 8, 3); EXECUTE ab_q1 (1, 8, 3); EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) EXECUTE ab_q1 (2, 2, 3); EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) EXECUTE ab_q1 (1, 2, 3); DEALLOCATE ab_q1; -- Runtime pruning after optimizer pruning PREPARE ab_q1 (int, int) AS SELECT a FROM ab WHERE a BETWEEN $1 AND $2 AND b < 3; -- Execute query 5 times to allow choose_custom_plan -- to start considering a generic plan. EXECUTE ab_q1 (1, 8); EXECUTE ab_q1 (1, 8); EXECUTE ab_q1 (1, 8); EXECUTE ab_q1 (1, 8); EXECUTE ab_q1 (1, 8); EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) EXECUTE ab_q1 (2, 2); EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) EXECUTE ab_q1 (2, 4); -- Ensure a mix of PARAM_EXTERN and PARAM_EXEC Params work together at -- different levels of partitioning. PREPARE ab_q2 (int, int) AS SELECT a FROM ab WHERE a BETWEEN $1 AND $2 AND b < ( SELECT 3); EXECUTE ab_q2 (1, 8); EXECUTE ab_q2 (1, 8); EXECUTE ab_q2 (1, 8); EXECUTE ab_q2 (1, 8); EXECUTE ab_q2 (1, 8); EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) EXECUTE ab_q2 (2, 2); -- As above, but swap the PARAM_EXEC Param to the first partition level PREPARE ab_q3 (int, int) AS SELECT a FROM ab WHERE b BETWEEN $1 AND $2 AND a < ( SELECT 3); EXECUTE ab_q3 (1, 8); EXECUTE ab_q3 (1, 8); EXECUTE ab_q3 (1, 8); EXECUTE ab_q3 (1, 8); EXECUTE ab_q3 (1, 8); EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) EXECUTE ab_q3 (2, 2); -- Test a backwards Append scan CREATE TABLE list_part ( a int ) PARTITION BY LIST (a); CREATE TABLE list_part1 PARTITION OF list_part FOR VALUES IN (1); CREATE TABLE list_part2 PARTITION OF list_part FOR VALUES IN (2); CREATE TABLE list_part3 PARTITION OF list_part FOR VALUES IN (3); CREATE TABLE list_part4 PARTITION OF list_part FOR VALUES IN (4); INSERT INTO list_part SELECT generate_series(1, 4); BEGIN; -- Don't select an actual value out of the table as the order of the Append's -- subnodes may not be stable. DECLARE cur SCROLL CURSOR FOR SELECT 1 FROM list_part WHERE a > ( SELECT 1) AND a < ( SELECT 4); -- move beyond the final row MOVE 3 FROM cur; -- Ensure we get two rows. FETCH BACKWARD ALL FROM cur; COMMIT; BEGIN; -- Test run-time pruning using stable functions CREATE FUNCTION list_part_fn (int) RETURNS int AS $$ BEGIN RETURN $1; END; $$ LANGUAGE plpgsql STABLE; -- Ensure pruning works using a stable function containing no Vars EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) SELECT * FROM list_part WHERE a = list_part_fn (1); -- Ensure pruning does not take place when the function has a Var parameter EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) SELECT * FROM list_part WHERE a = list_part_fn (a); -- Ensure pruning does not take place when the expression contains a Var. EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) SELECT * FROM list_part WHERE a = list_part_fn (1) + a; ROLLBACK; DROP TABLE list_part; -- Parallel append -- Suppress the number of loops each parallel node runs for. This is because -- more than one worker may run the same parallel node if timing conditions -- are just right, which destabilizes the test. CREATE FUNCTION explain_parallel_append (text) RETURNS SETOF text LANGUAGE plpgsql AS $$ DECLARE ln text; BEGIN FOR ln IN EXECUTE format('explain (analyze, costs off, summary off, timing off) %s', $1) LOOP IF ln LIKE '%Parallel%' THEN ln := regexp_replace(ln, 'loops=\d*', 'loops=N'); END IF; RETURN NEXT ln; END LOOP; END; $$; PREPARE ab_q4 (int, int) AS SELECT avg(a) FROM ab WHERE a BETWEEN $1 AND $2 AND b < 4; -- Encourage use of parallel plans SET parallel_setup_cost = 0; SET parallel_tuple_cost = 0; SET min_parallel_table_scan_size = 0; SET max_parallel_workers_per_gather = 2; -- Execute query 5 times to allow choose_custom_plan -- to start considering a generic plan. EXECUTE ab_q4 (1, 8); EXECUTE ab_q4 (1, 8); EXECUTE ab_q4 (1, 8); EXECUTE ab_q4 (1, 8); EXECUTE ab_q4 (1, 8); SELECT explain_parallel_append ('execute ab_q4 (2, 2)'); -- Test run-time pruning with IN lists. PREPARE ab_q5 (int, int, int) AS SELECT avg(a) FROM ab WHERE a IN ($1, $2, $3) AND b < 4; -- Execute query 5 times to allow choose_custom_plan -- to start considering a generic plan. EXECUTE ab_q5 (1, 2, 3); EXECUTE ab_q5 (1, 2, 3); EXECUTE ab_q5 (1, 2, 3); EXECUTE ab_q5 (1, 2, 3); EXECUTE ab_q5 (1, 2, 3); SELECT explain_parallel_append ('execute ab_q5 (1, 1, 1)'); SELECT explain_parallel_append ('execute ab_q5 (2, 3, 3)'); -- Try some params whose values do not belong to any partition. -- We'll still get a single subplan in this case, but it should not be scanned. SELECT explain_parallel_append ('execute ab_q5 (33, 44, 55)'); -- Test Parallel Append with PARAM_EXEC Params SELECT explain_parallel_append ('select count(*) from ab where (a = (select 1) or a = (select 3)) and b = 2'); -- Test pruning during parallel nested loop query CREATE TABLE lprt_a ( a int NOT NULL ); -- Insert some values we won't find in ab INSERT INTO lprt_a SELECT 0 FROM generate_series(1, 100); -- and insert some values that we should find. INSERT INTO lprt_a VALUES (1), (1); ANALYZE lprt_a; CREATE INDEX ab_a2_b1_a_idx ON ab_a2_b1 (a); CREATE INDEX ab_a2_b2_a_idx ON ab_a2_b2 (a); CREATE INDEX ab_a2_b3_a_idx ON ab_a2_b3 (a); CREATE INDEX ab_a1_b1_a_idx ON ab_a1_b1 (a); CREATE INDEX ab_a1_b2_a_idx ON ab_a1_b2 (a); CREATE INDEX ab_a1_b3_a_idx ON ab_a1_b3 (a); CREATE INDEX ab_a3_b1_a_idx ON ab_a3_b1 (a); CREATE INDEX ab_a3_b2_a_idx ON ab_a3_b2 (a); CREATE INDEX ab_a3_b3_a_idx ON ab_a3_b3 (a); SET enable_hashjoin = 0; SET enable_mergejoin = 0; SELECT explain_parallel_append ('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(0, 0, 1)'); -- Ensure the same partitions are pruned when we make the nested loop -- parameter an Expr rather than a plain Param. SELECT explain_parallel_append ('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a + 0 where a.a in(0, 0, 1)'); INSERT INTO lprt_a VALUES (3), (3); SELECT explain_parallel_append ('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 3)'); SELECT explain_parallel_append ('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 0)'); DELETE FROM lprt_a WHERE a = 1; SELECT explain_parallel_append ('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 0)'); RESET enable_hashjoin; RESET enable_mergejoin; RESET parallel_setup_cost; RESET parallel_tuple_cost; RESET min_parallel_table_scan_size; RESET max_parallel_workers_per_gather; -- Test run-time partition pruning with an initplan EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) SELECT * FROM ab WHERE a = ( SELECT max(a) FROM lprt_a) AND b = ( SELECT max(a) - 1 FROM lprt_a); -- Test run-time partition pruning with UNION ALL parents EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) SELECT * FROM ( SELECT * FROM ab WHERE a = 1 UNION ALL SELECT * FROM ab) ab WHERE b = ( SELECT 1); -- A case containing a UNION ALL with a non-partitioned child. EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) SELECT * FROM ( SELECT * FROM ab WHERE a = 1 UNION ALL ( VALUES (10, 5)) UNION ALL SELECT * FROM ab) ab WHERE b = ( SELECT 1); -- Another UNION ALL test, but containing a mix of exec init and exec run-time pruning. CREATE TABLE xy_1 ( x int, y int ); INSERT INTO xy_1 VALUES (100, - 10); SET enable_bitmapscan = 0; SET enable_indexscan = 0; SET plan_cache_mode = 'force_generic_plan'; PREPARE ab_q6 AS SELECT * FROM ( SELECT tableoid::regclass, a, b FROM ab UNION ALL SELECT tableoid::regclass, x, y FROM xy_1 UNION ALL SELECT tableoid::regclass, a, b FROM ab) ab WHERE a = $1 AND b = ( SELECT - 10); -- Ensure the xy_1 subplan is not pruned. EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) EXECUTE ab_q6 (1); -- Ensure we see just the xy_1 row. EXECUTE ab_q6 (100); RESET enable_bitmapscan; RESET enable_indexscan; RESET plan_cache_mode; DEALLOCATE ab_q1; DEALLOCATE ab_q2; DEALLOCATE ab_q3; DEALLOCATE ab_q4; DEALLOCATE ab_q5; DEALLOCATE ab_q6; -- UPDATE on a partition subtree has been seen to have problems. INSERT INTO ab VALUES (1, 2); EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) UPDATE ab_a1 SET b = 3 FROM ab WHERE ab.a = 1 AND ab.a = ab_a1.a; TABLE ab; -- Test UPDATE where source relation has run-time pruning enabled TRUNCATE ab; INSERT INTO ab VALUES (1, 1), (1, 2), (1, 3), (2, 1); EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) UPDATE ab_a1 SET b = 3 FROM ab_a2 WHERE ab_a2.b = ( SELECT 1); SELECT tableoid::regclass, * FROM ab; DROP TABLE ab, lprt_a; -- Join CREATE TABLE tbl1 ( col1 int ); INSERT INTO tbl1 VALUES (501), (505); -- Basic table CREATE TABLE tprt ( col1 int ) PARTITION BY RANGE (col1); CREATE TABLE tprt_1 PARTITION OF tprt FOR VALUES FROM (1) TO (501); CREATE TABLE tprt_2 PARTITION OF tprt FOR VALUES FROM (501) TO (1001); CREATE TABLE tprt_3 PARTITION OF tprt FOR VALUES FROM (1001) TO (2001); CREATE TABLE tprt_4 PARTITION OF tprt FOR VALUES FROM (2001) TO (3001); CREATE TABLE tprt_5 PARTITION OF tprt FOR VALUES FROM (3001) TO (4001); CREATE TABLE tprt_6 PARTITION OF tprt FOR VALUES FROM (4001) TO (5001); CREATE INDEX tprt1_idx ON tprt_1 (col1); CREATE INDEX tprt2_idx ON tprt_2 (col1); CREATE INDEX tprt3_idx ON tprt_3 (col1); CREATE INDEX tprt4_idx ON tprt_4 (col1); CREATE INDEX tprt5_idx ON tprt_5 (col1); CREATE INDEX tprt6_idx ON tprt_6 (col1); INSERT INTO tprt VALUES (10), (20), (501), (502), (505), (1001), (4500); SET enable_hashjoin = OFF; SET enable_mergejoin = OFF; EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) SELECT * FROM tbl1 JOIN tprt ON tbl1.col1 > tprt.col1; EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) SELECT * FROM tbl1 JOIN tprt ON tbl1.col1 = tprt.col1; SELECT tbl1.col1, tprt.col1 FROM tbl1 INNER JOIN tprt ON tbl1.col1 > tprt.col1 ORDER BY tbl1.col1, tprt.col1; SELECT tbl1.col1, tprt.col1 FROM tbl1 INNER JOIN tprt ON tbl1.col1 = tprt.col1 ORDER BY tbl1.col1, tprt.col1; -- Multiple partitions INSERT INTO tbl1 VALUES (1001), (1010), (1011); EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) SELECT * FROM tbl1 INNER JOIN tprt ON tbl1.col1 > tprt.col1; EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) SELECT * FROM tbl1 INNER JOIN tprt ON tbl1.col1 = tprt.col1; SELECT tbl1.col1, tprt.col1 FROM tbl1 INNER JOIN tprt ON tbl1.col1 > tprt.col1 ORDER BY tbl1.col1, tprt.col1; SELECT tbl1.col1, tprt.col1 FROM tbl1 INNER JOIN tprt ON tbl1.col1 = tprt.col1 ORDER BY tbl1.col1, tprt.col1; -- Last partition DELETE FROM tbl1; INSERT INTO tbl1 VALUES (4400); EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) SELECT * FROM tbl1 JOIN tprt ON tbl1.col1 < tprt.col1; SELECT tbl1.col1, tprt.col1 FROM tbl1 INNER JOIN tprt ON tbl1.col1 < tprt.col1 ORDER BY tbl1.col1, tprt.col1; -- No matching partition DELETE FROM tbl1; INSERT INTO tbl1 VALUES (10000); EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) SELECT * FROM tbl1 JOIN tprt ON tbl1.col1 = tprt.col1; SELECT tbl1.col1, tprt.col1 FROM tbl1 INNER JOIN tprt ON tbl1.col1 = tprt.col1 ORDER BY tbl1.col1, tprt.col1; DROP TABLE tbl1, tprt; -- Test with columns defined in varying orders between each level CREATE TABLE part_abc ( a int NOT NULL, b int NOT NULL, c int NOT NULL ) PARTITION BY LIST (a); CREATE TABLE part_bac ( b int NOT NULL, a int NOT NULL, c int NOT NULL ) PARTITION BY LIST (b); CREATE TABLE part_cab ( c int NOT NULL, a int NOT NULL, b int NOT NULL ) PARTITION BY LIST (c); CREATE TABLE part_abc_p1 ( a int NOT NULL, b int NOT NULL, c int NOT NULL ); ALTER TABLE part_abc ATTACH PARTITION part_bac FOR VALUES IN (1); ALTER TABLE part_bac ATTACH PARTITION part_cab FOR VALUES IN (2); ALTER TABLE part_cab ATTACH PARTITION part_abc_p1 FOR VALUES IN (3); PREPARE part_abc_q1 (int, int, int) AS SELECT * FROM part_abc WHERE a = $1 AND b = $2 AND c = $3; -- Execute query 5 times to allow choose_custom_plan -- to start considering a generic plan. EXECUTE part_abc_q1 (1, 2, 3); EXECUTE part_abc_q1 (1, 2, 3); EXECUTE part_abc_q1 (1, 2, 3); EXECUTE part_abc_q1 (1, 2, 3); EXECUTE part_abc_q1 (1, 2, 3); -- Single partition should be scanned. EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) EXECUTE part_abc_q1 (1, 2, 3); DEALLOCATE part_abc_q1; DROP TABLE part_abc; -- Ensure that an Append node properly handles a sub-partitioned table -- matching without any of its leaf partitions matching the clause. CREATE TABLE listp ( a int, b int ) PARTITION BY LIST (a); CREATE TABLE listp_1 PARTITION OF listp FOR VALUES IN (1) PARTITION BY LIST (b); CREATE TABLE listp_1_1 PARTITION OF listp_1 FOR VALUES IN (1); CREATE TABLE listp_2 PARTITION OF listp FOR VALUES IN (2) PARTITION BY LIST (b); CREATE TABLE listp_2_1 PARTITION OF listp_2 FOR VALUES IN (2); SELECT * FROM listp WHERE b = 1; -- Ensure that an Append node properly can handle selection of all first level -- partitions before finally detecting the correct set of 2nd level partitions -- which match the given parameter. PREPARE q1 (int, int) AS SELECT * FROM listp WHERE b IN ($1, $2); EXECUTE q1 (1, 2); EXECUTE q1 (1, 2); EXECUTE q1 (1, 2); EXECUTE q1 (1, 2); EXECUTE q1 (1, 2); EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) EXECUTE q1 (1, 1); EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) EXECUTE q1 (2, 2); -- Try with no matching partitions. One subplan should remain in this case, -- but it shouldn't be executed. EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) EXECUTE q1 (0, 0); DEALLOCATE q1; -- Test more complex cases where a not-equal condition further eliminates partitions. PREPARE q1 (int, int, int, int) AS SELECT * FROM listp WHERE b IN ($1, $2) AND $3 <> b AND $4 <> b; EXECUTE q1 (1, 2, 3, 4); EXECUTE q1 (1, 2, 3, 4); EXECUTE q1 (1, 2, 3, 4); EXECUTE q1 (1, 2, 3, 4); EXECUTE q1 (1, 2, 3, 4); -- Both partitions allowed by IN clause, but one disallowed by <> clause EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) EXECUTE q1 (1, 2, 2, 0); -- Both partitions allowed by IN clause, then both excluded again by <> clauses. -- One subplan will remain in this case, but it should not be executed. EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) EXECUTE q1 (1, 2, 2, 1); -- Ensure Params that evaluate to NULL properly prune away all partitions EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) SELECT * FROM listp WHERE a = ( SELECT NULL::int); DROP TABLE listp; -- Ensure runtime pruning works with initplans params with boolean types CREATE TABLE boolvalues ( value bool NOT NULL ); INSERT INTO boolvalues VALUES ('t'), ('f'); CREATE TABLE boolp ( a bool ) PARTITION BY LIST (a); CREATE TABLE boolp_t PARTITION OF boolp FOR VALUES IN ('t'); CREATE TABLE boolp_f PARTITION OF boolp FOR VALUES IN ('f'); EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) SELECT * FROM boolp WHERE a = ( SELECT value FROM boolvalues WHERE value); EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) SELECT * FROM boolp WHERE a = ( SELECT value FROM boolvalues WHERE NOT value); DROP TABLE boolp; -- -- Test run-time pruning of MergeAppend subnodes -- SET enable_seqscan = OFF; SET enable_sort = OFF; CREATE TABLE ma_test ( a int, b int ) PARTITION BY RANGE (a); CREATE TABLE ma_test_p1 PARTITION OF ma_test FOR VALUES FROM (0) TO (10); CREATE TABLE ma_test_p2 PARTITION OF ma_test FOR VALUES FROM (10) TO (20); CREATE TABLE ma_test_p3 PARTITION OF ma_test FOR VALUES FROM (20) TO (30); INSERT INTO ma_test SELECT x, x FROM generate_series(0, 29) t (x); CREATE INDEX ON ma_test (b); ANALYZE ma_test; PREPARE mt_q1 (int) AS SELECT a FROM ma_test WHERE a >= $1 AND a % 10 = 5 ORDER BY b; -- Execute query 5 times to allow choose_custom_plan -- to start considering a generic plan. EXECUTE mt_q1 (0); EXECUTE mt_q1 (0); EXECUTE mt_q1 (0); EXECUTE mt_q1 (0); EXECUTE mt_q1 (0); EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) EXECUTE mt_q1 (15); EXECUTE mt_q1 (15); EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) EXECUTE mt_q1 (25); EXECUTE mt_q1 (25); -- Ensure MergeAppend behaves correctly when no subplans match EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) EXECUTE mt_q1 (35); EXECUTE mt_q1 (35); DEALLOCATE mt_q1; -- ensure initplan params properly prune partitions EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) SELECT * FROM ma_test WHERE a >= ( SELECT min(b) FROM ma_test_p2) ORDER BY b; RESET enable_seqscan; RESET enable_sort; DROP TABLE ma_test; RESET enable_indexonlyscan; -- -- check that pruning works properly when the partition key is of a -- pseudotype -- -- array type list partition key CREATE TABLE pp_arrpart ( a int[] ) PARTITION BY LIST (a); CREATE TABLE pp_arrpart1 PARTITION OF pp_arrpart FOR VALUES IN ('{1}'); CREATE TABLE pp_arrpart2 PARTITION OF pp_arrpart FOR VALUES IN ('{2, 3}', '{4, 5}'); EXPLAIN ( COSTS OFF ) SELECT * FROM pp_arrpart WHERE a = '{1}'; EXPLAIN ( COSTS OFF ) SELECT * FROM pp_arrpart WHERE a = '{1, 2}'; EXPLAIN ( COSTS OFF ) SELECT * FROM pp_arrpart WHERE a IN ('{4, 5}', '{1}'); EXPLAIN ( COSTS OFF ) UPDATE pp_arrpart SET a = a WHERE a = '{1}'; EXPLAIN ( COSTS OFF ) DELETE FROM pp_arrpart WHERE a = '{1}'; DROP TABLE pp_arrpart; -- array type hash partition key CREATE TABLE pph_arrpart ( a int[] ) PARTITION BY HASH (a); CREATE TABLE pph_arrpart1 PARTITION OF pph_arrpart FOR VALUES WITH (MODULUS 2, REMAINDER 0); CREATE TABLE pph_arrpart2 PARTITION OF pph_arrpart FOR VALUES WITH (MODULUS 2, REMAINDER 1); INSERT INTO pph_arrpart VALUES ('{1}'), ('{1, 2}'), ('{4, 5}'); SELECT tableoid::regclass, * FROM pph_arrpart ORDER BY 1; EXPLAIN ( COSTS OFF ) SELECT * FROM pph_arrpart WHERE a = '{1}'; EXPLAIN ( COSTS OFF ) SELECT * FROM pph_arrpart WHERE a = '{1, 2}'; EXPLAIN ( COSTS OFF ) SELECT * FROM pph_arrpart WHERE a IN ('{4, 5}', '{1}'); DROP TABLE pph_arrpart; -- enum type list partition key CREATE TYPE pp_colors AS enum ( 'green', 'blue', 'black' ); CREATE TABLE pp_enumpart ( a pp_colors ) PARTITION BY LIST (a); CREATE TABLE pp_enumpart_green PARTITION OF pp_enumpart FOR VALUES IN ('green'); CREATE TABLE pp_enumpart_blue PARTITION OF pp_enumpart FOR VALUES IN ('blue'); EXPLAIN ( COSTS OFF ) SELECT * FROM pp_enumpart WHERE a = 'blue'; EXPLAIN ( COSTS OFF ) SELECT * FROM pp_enumpart WHERE a = 'black'; DROP TABLE pp_enumpart; DROP TYPE pp_colors; -- record type as partition key CREATE TYPE pp_rectype AS ( a int, b int ); CREATE TABLE pp_recpart ( a pp_rectype ) PARTITION BY LIST (a); CREATE TABLE pp_recpart_11 PARTITION OF pp_recpart FOR VALUES IN ('(1,1)'); CREATE TABLE pp_recpart_23 PARTITION OF pp_recpart FOR VALUES IN ('(2,3)'); EXPLAIN ( COSTS OFF ) SELECT * FROM pp_recpart WHERE a = '(1,1)'::pp_rectype; EXPLAIN ( COSTS OFF ) SELECT * FROM pp_recpart WHERE a = '(1,2)'::pp_rectype; DROP TABLE pp_recpart; DROP TYPE pp_rectype; -- range type partition key CREATE TABLE pp_intrangepart ( a int4range ) PARTITION BY LIST (a); CREATE TABLE pp_intrangepart12 PARTITION OF pp_intrangepart FOR VALUES IN ('[1,2]'); CREATE TABLE pp_intrangepart2inf PARTITION OF pp_intrangepart FOR VALUES IN ('[2,)'); EXPLAIN ( COSTS OFF ) SELECT * FROM pp_intrangepart WHERE a = '[1,2]'::int4range; EXPLAIN ( COSTS OFF ) SELECT * FROM pp_intrangepart WHERE a = '(1,2)'::int4range; DROP TABLE pp_intrangepart; -- -- Ensure the enable_partition_prune GUC properly disables partition pruning. -- CREATE TABLE pp_lp ( a int, value int ) PARTITION BY LIST (a); CREATE TABLE pp_lp1 PARTITION OF pp_lp FOR VALUES IN (1); CREATE TABLE pp_lp2 PARTITION OF pp_lp FOR VALUES IN (2); EXPLAIN ( COSTS OFF ) SELECT * FROM pp_lp WHERE a = 1; EXPLAIN ( COSTS OFF ) UPDATE pp_lp SET value = 10 WHERE a = 1; EXPLAIN ( COSTS OFF ) DELETE FROM pp_lp WHERE a = 1; SET enable_partition_pruning = OFF; SET constraint_exclusion = 'partition'; -- this should not affect the result. EXPLAIN ( COSTS OFF ) SELECT * FROM pp_lp WHERE a = 1; EXPLAIN ( COSTS OFF ) UPDATE pp_lp SET value = 10 WHERE a = 1; EXPLAIN ( COSTS OFF ) DELETE FROM pp_lp WHERE a = 1; SET constraint_exclusion = 'off'; -- this should not affect the result. EXPLAIN ( COSTS OFF ) SELECT * FROM pp_lp WHERE a = 1; EXPLAIN ( COSTS OFF ) UPDATE pp_lp SET value = 10 WHERE a = 1; EXPLAIN ( COSTS OFF ) DELETE FROM pp_lp WHERE a = 1; DROP TABLE pp_lp; -- Ensure enable_partition_prune does not affect non-partitioned tables. CREATE TABLE inh_lp ( a int, value int ); CREATE TABLE inh_lp1 ( a int, value int, CHECK (a = 1) ) INHERITS ( inh_lp ); CREATE TABLE inh_lp2 ( a int, value int, CHECK (a = 2) ) INHERITS ( inh_lp ); SET constraint_exclusion = 'partition'; -- inh_lp2 should be removed in the following 3 cases. EXPLAIN ( COSTS OFF ) SELECT * FROM inh_lp WHERE a = 1; EXPLAIN ( COSTS OFF ) UPDATE inh_lp SET value = 10 WHERE a = 1; EXPLAIN ( COSTS OFF ) DELETE FROM inh_lp WHERE a = 1; -- Ensure we don't exclude normal relations when we only expect to exclude -- inheritance children EXPLAIN ( COSTS OFF ) UPDATE inh_lp1 SET value = 10 WHERE a = 2; DROP TABLE inh_lp CASCADE; RESET enable_partition_pruning; RESET constraint_exclusion; -- Check pruning for a partition tree containing only temporary relations CREATE temp TABLE pp_temp_parent ( a int ) PARTITION BY LIST (a); CREATE temp TABLE pp_temp_part_1 PARTITION OF pp_temp_parent FOR VALUES IN (1); CREATE temp TABLE pp_temp_part_def PARTITION OF pp_temp_parent DEFAULT; EXPLAIN ( COSTS OFF ) SELECT * FROM pp_temp_parent WHERE TRUE; EXPLAIN ( COSTS OFF ) SELECT * FROM pp_temp_parent WHERE a = 2; DROP TABLE pp_temp_parent; -- Stress run-time partition pruning a bit more, per bug reports CREATE temp TABLE p ( a int, b int, c int ) PARTITION BY LIST (a); CREATE temp TABLE p1 PARTITION OF p FOR VALUES IN (1); CREATE temp TABLE p2 PARTITION OF p FOR VALUES IN (2); CREATE temp TABLE q ( a int, b int, c int ) PARTITION BY LIST (a); CREATE temp TABLE q1 PARTITION OF q FOR VALUES IN (1) PARTITION BY LIST (b); CREATE temp TABLE q11 PARTITION OF q1 FOR VALUES IN (1) PARTITION BY LIST (c); CREATE temp TABLE q111 PARTITION OF q11 FOR VALUES IN (1); CREATE temp TABLE q2 PARTITION OF q FOR VALUES IN (2) PARTITION BY LIST (b); CREATE temp TABLE q21 PARTITION OF q2 FOR VALUES IN (1); CREATE temp TABLE q22 PARTITION OF q2 FOR VALUES IN (2); INSERT INTO q22 VALUES (2, 2, 3); EXPLAIN ( COSTS OFF ) SELECT * FROM ( SELECT * FROM p UNION ALL SELECT * FROM q1 UNION ALL SELECT 1, 1, 1) s (a, b, c) WHERE s.a = 1 AND s.b = 1 AND s.c = ( SELECT 1); SELECT * FROM ( SELECT * FROM p UNION ALL SELECT * FROM q1 UNION ALL SELECT 1, 1, 1) s (a, b, c) WHERE s.a = 1 AND s.b = 1 AND s.c = ( SELECT 1); PREPARE q (int, int) AS SELECT * FROM ( SELECT * FROM p UNION ALL SELECT * FROM q1 UNION ALL SELECT 1, 1, 1) s (a, b, c) WHERE s.a = $1 AND s.b = $2 AND s.c = ( SELECT 1); SET plan_cache_mode TO force_generic_plan; EXPLAIN ( COSTS OFF ) EXECUTE q (1, 1); EXECUTE q (1, 1); RESET plan_cache_mode; DROP TABLE p, q; -- Ensure run-time pruning works correctly when we match a partitioned table -- on the first level but find no matching partitions on the second level. CREATE TABLE listp ( a int, b int ) PARTITION BY LIST (a); CREATE TABLE listp1 PARTITION OF listp FOR VALUES IN (1); CREATE TABLE listp2 PARTITION OF listp FOR VALUES IN (2) PARTITION BY LIST (b); CREATE TABLE listp2_10 PARTITION OF listp2 FOR VALUES IN (10); EXPLAIN ( ANALYZE, COSTS OFF, summary OFF, timing OFF ) SELECT * FROM listp WHERE a = ( SELECT 2) AND b <> 10; -- -- check that a partition directly accessed in a query is excluded with -- constraint_exclusion = on -- -- turn off partition pruning, so that it doesn't interfere SET enable_partition_pruning TO OFF; -- setting constraint_exclusion to 'partition' disables exclusion SET constraint_exclusion TO 'partition'; EXPLAIN ( COSTS OFF ) SELECT * FROM listp1 WHERE a = 2; EXPLAIN ( COSTS OFF ) UPDATE listp1 SET a = 1 WHERE a = 2; -- constraint exclusion enabled SET constraint_exclusion TO 'on'; EXPLAIN ( COSTS OFF ) SELECT * FROM listp1 WHERE a = 2; EXPLAIN ( COSTS OFF ) UPDATE listp1 SET a = 1 WHERE a = 2; RESET constraint_exclusion; RESET enable_partition_pruning; DROP TABLE listp; pgFormatter-4.2/t/pg-test-files/expected/password.sql000066400000000000000000000075151361326045100227740ustar00rootroot00000000000000-- -- Tests for password verifiers -- -- Tests for GUC password_encryption SET password_encryption = 'novalue'; -- error SET password_encryption = TRUE; -- ok SET password_encryption = 'md5'; -- ok SET password_encryption = 'scram-sha-256'; -- ok -- consistency of password entries SET password_encryption = 'md5'; CREATE ROLE regress_passwd1 PASSWORD 'role_pwd1'; SET password_encryption = 'on'; CREATE ROLE regress_passwd2 PASSWORD 'role_pwd2'; SET password_encryption = 'scram-sha-256'; CREATE ROLE regress_passwd3 PASSWORD 'role_pwd3'; CREATE ROLE regress_passwd4 PASSWORD NULL; -- check list of created entries -- -- The scram verifier will look something like: -- SCRAM-SHA-256$4096:E4HxLGtnRzsYwg==$6YtlR4t69SguDiwFvbVgVZtuz6gpJQQqUMZ7IQJK5yI=:ps75jrHeYU4lXCcXI4O8oIdJ3eO8o2jirjruw9phBTo= -- -- Since the salt is random, the exact value stored will be different on every test -- run. Use a regular expression to mask the changing parts. SELECT rolname, regexp_replace(rolpassword, '(SCRAM-SHA-256)\$(\d+):([a-zA-Z0-9+/=]+)\$([a-zA-Z0-9+=/]+):([a-zA-Z0-9+/=]+)', '\1$\2:$:') AS rolpassword_masked FROM pg_authid WHERE rolname LIKE 'regress_passwd%' ORDER BY rolname, rolpassword; -- Rename a role ALTER ROLE regress_passwd2 RENAME TO regress_passwd2_new; -- md5 entry should have been removed SELECT rolname, rolpassword FROM pg_authid WHERE rolname LIKE 'regress_passwd2_new' ORDER BY rolname, rolpassword; ALTER ROLE regress_passwd2_new RENAME TO regress_passwd2; -- Change passwords with ALTER USER. With plaintext or already-encrypted -- passwords. SET password_encryption = 'md5'; -- encrypt with MD5 ALTER ROLE regress_passwd2 PASSWORD 'foo'; -- already encrypted, use as they are ALTER ROLE regress_passwd1 PASSWORD 'md5cd3578025fe2c3d7ed1b9a9b26238b70'; ALTER ROLE regress_passwd3 PASSWORD 'SCRAM-SHA-256$4096:VLK4RMaQLCvNtQ==$6YtlR4t69SguDiwFvbVgVZtuz6gpJQQqUMZ7IQJK5yI=:ps75jrHeYU4lXCcXI4O8oIdJ3eO8o2jirjruw9phBTo='; SET password_encryption = 'scram-sha-256'; -- create SCRAM verifier ALTER ROLE regress_passwd4 PASSWORD 'foo'; -- already encrypted with MD5, use as it is CREATE ROLE regress_passwd5 PASSWORD 'md5e73a4b11df52a6068f8b39f90be36023'; -- This looks like a valid SCRAM-SHA-256 verifier, but it is not -- so it should be hashed with SCRAM-SHA-256. CREATE ROLE regress_passwd6 PASSWORD 'SCRAM-SHA-256$1234'; -- These may look like valid MD5 verifiers, but they are not, so they -- should be hashed with SCRAM-SHA-256. -- trailing garbage at the end CREATE ROLE regress_passwd7 PASSWORD 'md5012345678901234567890123456789zz'; -- invalid length CREATE ROLE regress_passwd8 PASSWORD 'md501234567890123456789012345678901zz'; SELECT rolname, regexp_replace(rolpassword, '(SCRAM-SHA-256)\$(\d+):([a-zA-Z0-9+/=]+)\$([a-zA-Z0-9+=/]+):([a-zA-Z0-9+/=]+)', '\1$\2:$:') AS rolpassword_masked FROM pg_authid WHERE rolname LIKE 'regress_passwd%' ORDER BY rolname, rolpassword; -- An empty password is not allowed, in any form CREATE ROLE regress_passwd_empty PASSWORD ''; ALTER ROLE regress_passwd_empty PASSWORD 'md585939a5ce845f1a1b620742e3c659e0a'; ALTER ROLE regress_passwd_empty PASSWORD 'SCRAM-SHA-256$4096:hpFyHTUsSWcR7O9P$LgZFIt6Oqdo27ZFKbZ2nV+vtnYM995pDh9ca6WSi120=:qVV5NeluNfUPkwm7Vqat25RjSPLkGeoZBQs6wVv+um4='; SELECT rolpassword FROM pg_authid WHERE rolname = 'regress_passwd_empty'; DROP ROLE regress_passwd1; DROP ROLE regress_passwd2; DROP ROLE regress_passwd3; DROP ROLE regress_passwd4; DROP ROLE regress_passwd5; DROP ROLE regress_passwd6; DROP ROLE regress_passwd7; DROP ROLE regress_passwd8; DROP ROLE regress_passwd_empty; -- all entries should have been removed SELECT rolname, rolpassword FROM pg_authid WHERE rolname LIKE 'regress_passwd%' ORDER BY rolname, rolpassword; pgFormatter-4.2/t/pg-test-files/expected/path.sql000066400000000000000000000022351361326045100220600ustar00rootroot00000000000000-- -- PATH -- --DROP TABLE PATH_TBL; CREATE TABLE PATH_TBL ( f1 path ); INSERT INTO PATH_TBL VALUES ('[(1,2),(3,4)]'); INSERT INTO PATH_TBL VALUES (' (( 1 , 2 ) , ( 3 , 4 ) ) '); INSERT INTO PATH_TBL VALUES ('[ (0,0),(3,0),(4,5),(1,6) ]'); INSERT INTO PATH_TBL VALUES ('((1,2) ,(3,4 ))'); INSERT INTO PATH_TBL VALUES ('1,2 ,3,4 '); INSERT INTO PATH_TBL VALUES (' [1,2,3, 4] '); INSERT INTO PATH_TBL VALUES ('((10,20))'); -- Only one point INSERT INTO PATH_TBL VALUES ('[ 11,12,13,14 ]'); INSERT INTO PATH_TBL VALUES ('( 11,12,13,14) '); -- bad values for parser testing INSERT INTO PATH_TBL VALUES ('[]'); INSERT INTO PATH_TBL VALUES ('[(,2),(3,4)]'); INSERT INTO PATH_TBL VALUES ('[(1,2),(3,4)'); INSERT INTO PATH_TBL VALUES ('(1,2,3,4'); INSERT INTO PATH_TBL VALUES ('(1,2),(3,4)]'); SELECT '' AS count, f1 AS open_path FROM PATH_TBL WHERE isopen(f1); SELECT '' AS count, f1 AS closed_path FROM PATH_TBL WHERE isclosed(f1); SELECT '' AS count, pclose(f1) AS closed_path FROM PATH_TBL; SELECT '' AS count, popen(f1) AS open_path FROM PATH_TBL; pgFormatter-4.2/t/pg-test-files/expected/pg_lsn.sql000066400000000000000000000023401361326045100224030ustar00rootroot00000000000000-- -- PG_LSN -- CREATE TABLE PG_LSN_TBL ( f1 pg_lsn ); -- Largest and smallest input INSERT INTO PG_LSN_TBL VALUES ('0/0'); INSERT INTO PG_LSN_TBL VALUES ('FFFFFFFF/FFFFFFFF'); -- Incorrect input INSERT INTO PG_LSN_TBL VALUES ('G/0'); INSERT INTO PG_LSN_TBL VALUES ('-1/0'); INSERT INTO PG_LSN_TBL VALUES (' 0/12345678'); INSERT INTO PG_LSN_TBL VALUES ('ABCD/'); INSERT INTO PG_LSN_TBL VALUES ('/ABCD'); DROP TABLE PG_LSN_TBL; -- Operators SELECT '0/16AE7F8' = '0/16AE7F8'::pg_lsn; SELECT '0/16AE7F8'::pg_lsn != '0/16AE7F7'; SELECT '0/16AE7F7' < '0/16AE7F8'::pg_lsn; SELECT '0/16AE7F8' > pg_lsn '0/16AE7F7'; SELECT '0/16AE7F7'::pg_lsn - '0/16AE7F8'::pg_lsn; SELECT '0/16AE7F8'::pg_lsn - '0/16AE7F7'::pg_lsn; -- Check btree and hash opclasses EXPLAIN ( COSTS OFF ) SELECT DISTINCT (i || '/' || j)::pg_lsn f FROM generate_series(1, 10) i, generate_series(1, 10) j, generate_series(1, 5) k WHERE i <= 10 AND j > 0 AND j <= 10 ORDER BY f; SELECT DISTINCT (i || '/' || j)::pg_lsn f FROM generate_series(1, 10) i, generate_series(1, 10) j, generate_series(1, 5) k WHERE i <= 10 AND j > 0 AND j <= 10 ORDER BY f; pgFormatter-4.2/t/pg-test-files/expected/plancache.sql000066400000000000000000000132111361326045100230360ustar00rootroot00000000000000-- -- Tests to exercise the plan caching/invalidation mechanism -- CREATE TEMP TABLE pcachetest AS SELECT * FROM int8_tbl; -- create and use a cached plan PREPARE prepstmt AS SELECT * FROM pcachetest; EXECUTE prepstmt; -- and one with parameters PREPARE prepstmt2 (bigint) AS SELECT * FROM pcachetest WHERE q1 = $1; EXECUTE prepstmt2 (123); -- invalidate the plans and see what happens DROP TABLE pcachetest; EXECUTE prepstmt; EXECUTE prepstmt2 (123); -- recreate the temp table (this demonstrates that the raw plan is -- purely textual and doesn't depend on OIDs, for instance) CREATE TEMP TABLE pcachetest AS SELECT * FROM int8_tbl ORDER BY 2; EXECUTE prepstmt; EXECUTE prepstmt2 (123); -- prepared statements should prevent change in output tupdesc, -- since clients probably aren't expecting that to change on the fly ALTER TABLE pcachetest ADD COLUMN q3 bigint; EXECUTE prepstmt; EXECUTE prepstmt2 (123); -- but we're nice guys and will let you undo your mistake ALTER TABLE pcachetest DROP COLUMN q3; EXECUTE prepstmt; EXECUTE prepstmt2 (123); -- Try it with a view, which isn't directly used in the resulting plan -- but should trigger invalidation anyway CREATE TEMP VIEW pcacheview AS SELECT * FROM pcachetest; PREPARE vprep AS SELECT * FROM pcacheview; EXECUTE vprep; CREATE OR REPLACE TEMP VIEW pcacheview AS SELECT q1, q2 / 2 AS q2 FROM pcachetest; EXECUTE vprep; -- Check basic SPI plan invalidation CREATE FUNCTION cache_test (int) RETURNS int AS $$ DECLARE total int; BEGIN CREATE temp TABLE t1 ( f1 int ); INSERT INTO t1 VALUES ($1); INSERT INTO t1 VALUES (11); INSERT INTO t1 VALUES (12); INSERT INTO t1 VALUES (13); SELECT sum(f1) INTO total FROM t1; DROP TABLE t1; RETURN total; END $$ LANGUAGE plpgsql; SELECT cache_test (1); SELECT cache_test (2); SELECT cache_test (3); -- Check invalidation of plpgsql "simple expression" CREATE temp VIEW v1 AS SELECT 2 + 2 AS f1; CREATE FUNCTION cache_test_2 () RETURNS int AS $$ BEGIN RETURN f1 FROM v1; END $$ LANGUAGE plpgsql; SELECT cache_test_2 (); CREATE OR REPLACE temp VIEW v1 AS SELECT 2 + 2 + 4 AS f1; SELECT cache_test_2 (); CREATE OR REPLACE temp VIEW v1 AS SELECT 2 + 2 + 4 + ( SELECT max(unique1) FROM tenk1) AS f1; SELECT cache_test_2 (); --- Check that change of search_path is honored when re-using cached plan CREATE SCHEMA s1 CREATE TABLE abc ( f1 int ); CREATE SCHEMA s2 CREATE TABLE abc ( f1 int ); INSERT INTO s1.abc VALUES (123); INSERT INTO s2.abc VALUES (456); SET search_path = s1; PREPARE p1 AS SELECT f1 FROM abc; EXECUTE p1; SET search_path = s2; SELECT f1 FROM abc; EXECUTE p1; ALTER TABLE s1.abc ADD COLUMN f2 float8; -- force replan EXECUTE p1; DROP SCHEMA s1 CASCADE; DROP SCHEMA s2 CASCADE; RESET search_path; -- Check that invalidation deals with regclass constants CREATE temp SEQUENCE seq; PREPARE p2 AS SELECT nextval('seq'); EXECUTE p2; DROP SEQUENCE seq; CREATE temp SEQUENCE seq; EXECUTE p2; -- Check DDL via SPI, immediately followed by SPI plan re-use -- (bug in original coding) CREATE FUNCTION cachebug () RETURNS void AS $$ DECLARE r int; BEGIN DROP TABLE IF EXISTS temptable CASCADE; CREATE temp TABLE temptable AS SELECT * FROM generate_series(1, 3) AS f1; CREATE temp VIEW vv AS SELECT * FROM temptable; FOR r IN SELECT * FROM vv LOOP RAISE notice '%', r; END LOOP; END $$ LANGUAGE plpgsql; SELECT cachebug (); SELECT cachebug (); -- Check that addition or removal of any partition is correctly dealt with by -- default partition table when it is being used in prepared statement. CREATE TABLE pc_list_parted ( a int ) PARTITION BY LIST (a); CREATE TABLE pc_list_part_null PARTITION OF pc_list_parted FOR VALUES IN (NULL); CREATE TABLE pc_list_part_1 PARTITION OF pc_list_parted FOR VALUES IN (1); CREATE TABLE pc_list_part_def PARTITION OF pc_list_parted DEFAULT; PREPARE pstmt_def_insert (int) AS INSERT INTO pc_list_part_def VALUES ($1); -- should fail EXECUTE pstmt_def_insert (NULL); EXECUTE pstmt_def_insert (1); CREATE TABLE pc_list_part_2 PARTITION OF pc_list_parted FOR VALUES IN (2); EXECUTE pstmt_def_insert (2); ALTER TABLE pc_list_parted DETACH PARTITION pc_list_part_null; -- should be ok EXECUTE pstmt_def_insert (NULL); DROP TABLE pc_list_part_1; -- should be ok EXECUTE pstmt_def_insert (1); DROP TABLE pc_list_parted, pc_list_part_null; DEALLOCATE pstmt_def_insert; -- Test plan_cache_mode CREATE TABLE test_mode ( a int ); INSERT INTO test_mode SELECT 1 FROM generate_series(1, 1000) UNION ALL SELECT 2; CREATE INDEX ON test_mode (a); ANALYZE test_mode; PREPARE test_mode_pp (int) AS SELECT count(*) FROM test_mode WHERE a = $1; -- up to 5 executions, custom plan is used EXPLAIN ( COSTS OFF ) EXECUTE test_mode_pp (2); -- force generic plan SET plan_cache_mode TO force_generic_plan; EXPLAIN ( COSTS OFF ) EXECUTE test_mode_pp (2); -- get to generic plan by 5 executions SET plan_cache_mode TO auto; EXECUTE test_mode_pp (1); -- 1x EXECUTE test_mode_pp (1); -- 2x EXECUTE test_mode_pp (1); -- 3x EXECUTE test_mode_pp (1); -- 4x EXECUTE test_mode_pp (1); -- 5x -- we should now get a really bad plan EXPLAIN ( COSTS OFF ) EXECUTE test_mode_pp (2); -- but we can force a custom plan SET plan_cache_mode TO force_custom_plan; EXPLAIN ( COSTS OFF ) EXECUTE test_mode_pp (2); DROP TABLE test_mode; pgFormatter-4.2/t/pg-test-files/expected/plpgsql.sql000066400000000000000000004113101361326045100226040ustar00rootroot00000000000000-- -- PLPGSQL -- -- Scenario: -- -- A building with a modern TP cable installation where any -- of the wall connectors can be used to plug in phones, -- ethernet interfaces or local office hubs. The backside -- of the wall connectors is wired to one of several patch- -- fields in the building. -- -- In the patchfields, there are hubs and all the slots -- representing the wall connectors. In addition there are -- slots that can represent a phone line from the central -- phone system. -- -- Triggers ensure consistency of the patching information. -- -- Functions are used to build up powerful views that let -- you look behind the wall when looking at a patchfield -- or into a room. -- CREATE TABLE Room ( roomno char(8), comment text ); CREATE UNIQUE INDEX Room_rno ON Room USING btree (roomno bpchar_ops); CREATE TABLE WSlot ( slotname char(20), roomno char(8), slotlink char(20), backlink char(20) ); CREATE UNIQUE INDEX WSlot_name ON WSlot USING btree (slotname bpchar_ops); CREATE TABLE PField ( name text, comment text ); CREATE UNIQUE INDEX PField_name ON PField USING btree (name text_ops); CREATE TABLE PSlot ( slotname char(20), pfname text, slotlink char(20), backlink char(20) ); CREATE UNIQUE INDEX PSlot_name ON PSlot USING btree (slotname bpchar_ops); CREATE TABLE PLine ( slotname char(20), phonenumber char(20), comment text, backlink char(20) ); CREATE UNIQUE INDEX PLine_name ON PLine USING btree (slotname bpchar_ops); CREATE TABLE Hub ( name char(14), comment text, nslots integer ); CREATE UNIQUE INDEX Hub_name ON Hub USING btree (name bpchar_ops); CREATE TABLE HSlot ( slotname char(20), hubname char(14), slotno integer, slotlink char(20) ); CREATE UNIQUE INDEX HSlot_name ON HSlot USING btree (slotname bpchar_ops); CREATE INDEX HSlot_hubname ON HSlot USING btree (hubname bpchar_ops); CREATE TABLE SYSTEM ( name text, comment text ); CREATE UNIQUE INDEX System_name ON SYSTEM USING btree (name text_ops); CREATE TABLE IFace ( slotname char(20), sysname text, ifname text, slotlink char(20) ); CREATE UNIQUE INDEX IFace_name ON IFace USING btree (slotname bpchar_ops); CREATE TABLE PHone ( slotname char(20), comment text, slotlink char(20) ); CREATE UNIQUE INDEX PHone_name ON PHone USING btree (slotname bpchar_ops); -- ************************************************************ -- * -- * Trigger procedures and functions for the patchfield -- * test of PL/pgSQL -- * -- ************************************************************ -- ************************************************************ -- * AFTER UPDATE on Room -- * - If room no changes let wall slots follow -- ************************************************************ CREATE FUNCTION tg_room_au () RETURNS TRIGGER AS $$ BEGIN IF new.roomno != old.roomno THEN UPDATE WSlot SET roomno = new.roomno WHERE roomno = old.roomno; END IF; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_room_au AFTER UPDATE ON Room FOR EACH ROW EXECUTE PROCEDURE tg_room_au (); -- ************************************************************ -- * AFTER DELETE on Room -- * - delete wall slots in this room -- ************************************************************ CREATE FUNCTION tg_room_ad () RETURNS TRIGGER AS $$ BEGIN DELETE FROM WSlot WHERE roomno = old.roomno; RETURN old; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_room_ad AFTER DELETE ON Room FOR EACH ROW EXECUTE PROCEDURE tg_room_ad (); -- ************************************************************ -- * BEFORE INSERT or UPDATE on WSlot -- * - Check that room exists -- ************************************************************ CREATE FUNCTION tg_wslot_biu () RETURNS TRIGGER AS $$ BEGIN IF count(*) = 0 FROM Room WHERE roomno = new.roomno THEN RAISE exception 'Room % does not exist', new.roomno; END IF; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_wslot_biu BEFORE INSERT OR UPDATE ON WSlot FOR EACH ROW EXECUTE PROCEDURE tg_wslot_biu (); -- ************************************************************ -- * AFTER UPDATE on PField -- * - Let PSlots of this field follow -- ************************************************************ CREATE FUNCTION tg_pfield_au () RETURNS TRIGGER AS $$ BEGIN IF new.name != old.name THEN UPDATE PSlot SET pfname = new.name WHERE pfname = old.name; END IF; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_pfield_au AFTER UPDATE ON PField FOR EACH ROW EXECUTE PROCEDURE tg_pfield_au (); -- ************************************************************ -- * AFTER DELETE on PField -- * - Remove all slots of this patchfield -- ************************************************************ CREATE FUNCTION tg_pfield_ad () RETURNS TRIGGER AS $$ BEGIN DELETE FROM PSlot WHERE pfname = old.name; RETURN old; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_pfield_ad AFTER DELETE ON PField FOR EACH ROW EXECUTE PROCEDURE tg_pfield_ad (); -- ************************************************************ -- * BEFORE INSERT or UPDATE on PSlot -- * - Ensure that our patchfield does exist -- ************************************************************ CREATE FUNCTION tg_pslot_biu () RETURNS TRIGGER AS $proc$ DECLARE pfrec record; ps alias FOR new; BEGIN SELECT INTO pfrec * FROM PField WHERE name = ps.pfname; IF NOT found THEN RAISE exception $$ Patchfield "%" does NOT exist$$, ps.pfname; END IF; RETURN ps; END; $proc$ LANGUAGE plpgsql; CREATE TRIGGER tg_pslot_biu BEFORE INSERT OR UPDATE ON PSlot FOR EACH ROW EXECUTE PROCEDURE tg_pslot_biu (); -- ************************************************************ -- * AFTER UPDATE on System -- * - If system name changes let interfaces follow -- ************************************************************ CREATE FUNCTION tg_system_au () RETURNS TRIGGER AS $$ BEGIN IF new.name != old.name THEN UPDATE IFace SET sysname = new.name WHERE sysname = old.name; END IF; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_system_au AFTER UPDATE ON SYSTEM FOR EACH ROW EXECUTE PROCEDURE tg_system_au (); -- ************************************************************ -- * BEFORE INSERT or UPDATE on IFace -- * - set the slotname to IF.sysname.ifname -- ************************************************************ CREATE FUNCTION tg_iface_biu () RETURNS TRIGGER AS $$ DECLARE sname text; sysrec record; BEGIN SELECT INTO sysrec * FROM SYSTEM WHERE name = new.sysname; IF NOT found THEN RAISE exception $q$system "%" does not exist$q$, new.sysname; END IF; sname := 'IF.' || new.sysname; sname := sname || '.'; sname := sname || new.ifname; IF length(sname) > 20 THEN RAISE exception 'IFace slotname "%" too long (20 char max)', sname; END IF; new.slotname := sname; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_iface_biu BEFORE INSERT OR UPDATE ON IFace FOR EACH ROW EXECUTE PROCEDURE tg_iface_biu (); -- ************************************************************ -- * AFTER INSERT or UPDATE or DELETE on Hub -- * - insert/delete/rename slots as required -- ************************************************************ CREATE FUNCTION tg_hub_a () RETURNS TRIGGER AS $$ DECLARE hname text; dummy integer; BEGIN IF tg_op = 'INSERT' THEN dummy := tg_hub_adjustslots (new.name, 0, new.nslots); RETURN new; END IF; IF tg_op = 'UPDATE' THEN IF new.name != old.name THEN UPDATE HSlot SET hubname = new.name WHERE hubname = old.name; END IF; dummy := tg_hub_adjustslots (new.name, old.nslots, new.nslots); RETURN new; END IF; IF tg_op = 'DELETE' THEN dummy := tg_hub_adjustslots (old.name, old.nslots, 0); RETURN old; END IF; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_hub_a AFTER INSERT OR UPDATE OR DELETE ON Hub FOR EACH ROW EXECUTE PROCEDURE tg_hub_a (); -- ************************************************************ -- * Support function to add/remove slots of Hub -- ************************************************************ CREATE FUNCTION tg_hub_adjustslots (hname bpchar, oldnslots integer, newnslots integer) RETURNS integer AS $$ BEGIN IF newnslots = oldnslots THEN RETURN 0; END IF; IF newnslots < oldnslots THEN DELETE FROM HSlot WHERE hubname = hname AND slotno > newnslots; RETURN 0; END IF; FOR i IN oldnslots + 1..newnslots LOOP INSERT INTO HSlot (slotname, hubname, slotno, slotlink) VALUES ('HS.dummy', hname, i, ''); END LOOP; RETURN 0; END $$ LANGUAGE plpgsql; -- Test comments COMMENT ON FUNCTION tg_hub_adjustslots_wrong (bpchar, integer, integer) IS 'function with args'; COMMENT ON FUNCTION tg_hub_adjustslots (bpchar, integer, integer) IS 'function with args'; COMMENT ON FUNCTION tg_hub_adjustslots (bpchar, integer, integer) IS NULL; -- ************************************************************ -- * BEFORE INSERT or UPDATE on HSlot -- * - prevent from manual manipulation -- * - set the slotname to HS.hubname.slotno -- ************************************************************ CREATE FUNCTION tg_hslot_biu () RETURNS TRIGGER AS $$ DECLARE sname text; xname HSlot.slotname % TYPE; hubrec record; BEGIN SELECT INTO hubrec * FROM Hub WHERE name = new.hubname; IF NOT found THEN RAISE exception 'no manual manipulation of HSlot'; END IF; IF new.slotno < 1 OR new.slotno > hubrec.nslots THEN RAISE exception 'no manual manipulation of HSlot'; END IF; IF tg_op = 'UPDATE' AND new.hubname != old.hubname THEN IF count(*) > 0 FROM Hub WHERE name = old.hubname THEN RAISE exception 'no manual manipulation of HSlot'; END IF; END IF; sname := 'HS.' || trim(new.hubname); sname := sname || '.'; sname := sname || new.slotno::text; IF length(sname) > 20 THEN RAISE exception 'HSlot slotname "%" too long (20 char max)', sname; END IF; new.slotname := sname; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_hslot_biu BEFORE INSERT OR UPDATE ON HSlot FOR EACH ROW EXECUTE PROCEDURE tg_hslot_biu (); -- ************************************************************ -- * BEFORE DELETE on HSlot -- * - prevent from manual manipulation -- ************************************************************ CREATE FUNCTION tg_hslot_bd () RETURNS TRIGGER AS $$ DECLARE hubrec record; BEGIN SELECT INTO hubrec * FROM Hub WHERE name = old.hubname; IF NOT found THEN RETURN old; END IF; IF old.slotno > hubrec.nslots THEN RETURN old; END IF; RAISE exception 'no manual manipulation of HSlot'; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_hslot_bd BEFORE DELETE ON HSlot FOR EACH ROW EXECUTE PROCEDURE tg_hslot_bd (); -- ************************************************************ -- * BEFORE INSERT on all slots -- * - Check name prefix -- ************************************************************ CREATE FUNCTION tg_chkslotname () RETURNS TRIGGER AS $$ BEGIN IF substr(new.slotname, 1, 2) != tg_argv[0] THEN RAISE exception 'slotname must begin with %', tg_argv[0]; END IF; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_chkslotname BEFORE INSERT ON PSlot FOR EACH ROW EXECUTE PROCEDURE tg_chkslotname ('PS'); CREATE TRIGGER tg_chkslotname BEFORE INSERT ON WSlot FOR EACH ROW EXECUTE PROCEDURE tg_chkslotname ('WS'); CREATE TRIGGER tg_chkslotname BEFORE INSERT ON PLine FOR EACH ROW EXECUTE PROCEDURE tg_chkslotname ('PL'); CREATE TRIGGER tg_chkslotname BEFORE INSERT ON IFace FOR EACH ROW EXECUTE PROCEDURE tg_chkslotname ('IF'); CREATE TRIGGER tg_chkslotname BEFORE INSERT ON PHone FOR EACH ROW EXECUTE PROCEDURE tg_chkslotname ('PH'); -- ************************************************************ -- * BEFORE INSERT or UPDATE on all slots with slotlink -- * - Set slotlink to empty string if NULL value given -- ************************************************************ CREATE FUNCTION tg_chkslotlink () RETURNS TRIGGER AS $$ BEGIN IF new.slotlink ISNULL THEN new.slotlink := ''; END IF; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_chkslotlink BEFORE INSERT OR UPDATE ON PSlot FOR EACH ROW EXECUTE PROCEDURE tg_chkslotlink (); CREATE TRIGGER tg_chkslotlink BEFORE INSERT OR UPDATE ON WSlot FOR EACH ROW EXECUTE PROCEDURE tg_chkslotlink (); CREATE TRIGGER tg_chkslotlink BEFORE INSERT OR UPDATE ON IFace FOR EACH ROW EXECUTE PROCEDURE tg_chkslotlink (); CREATE TRIGGER tg_chkslotlink BEFORE INSERT OR UPDATE ON HSlot FOR EACH ROW EXECUTE PROCEDURE tg_chkslotlink (); CREATE TRIGGER tg_chkslotlink BEFORE INSERT OR UPDATE ON PHone FOR EACH ROW EXECUTE PROCEDURE tg_chkslotlink (); -- ************************************************************ -- * BEFORE INSERT or UPDATE on all slots with backlink -- * - Set backlink to empty string if NULL value given -- ************************************************************ CREATE FUNCTION tg_chkbacklink () RETURNS TRIGGER AS $$ BEGIN IF new.backlink ISNULL THEN new.backlink := ''; END IF; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_chkbacklink BEFORE INSERT OR UPDATE ON PSlot FOR EACH ROW EXECUTE PROCEDURE tg_chkbacklink (); CREATE TRIGGER tg_chkbacklink BEFORE INSERT OR UPDATE ON WSlot FOR EACH ROW EXECUTE PROCEDURE tg_chkbacklink (); CREATE TRIGGER tg_chkbacklink BEFORE INSERT OR UPDATE ON PLine FOR EACH ROW EXECUTE PROCEDURE tg_chkbacklink (); -- ************************************************************ -- * BEFORE UPDATE on PSlot -- * - do delete/insert instead of update if name changes -- ************************************************************ CREATE FUNCTION tg_pslot_bu () RETURNS TRIGGER AS $$ BEGIN IF new.slotname != old.slotname THEN DELETE FROM PSlot WHERE slotname = old.slotname; INSERT INTO PSlot (slotname, pfname, slotlink, backlink) VALUES (new.slotname, new.pfname, new.slotlink, new.backlink); RETURN NULL; END IF; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_pslot_bu BEFORE UPDATE ON PSlot FOR EACH ROW EXECUTE PROCEDURE tg_pslot_bu (); -- ************************************************************ -- * BEFORE UPDATE on WSlot -- * - do delete/insert instead of update if name changes -- ************************************************************ CREATE FUNCTION tg_wslot_bu () RETURNS TRIGGER AS $$ BEGIN IF new.slotname != old.slotname THEN DELETE FROM WSlot WHERE slotname = old.slotname; INSERT INTO WSlot (slotname, roomno, slotlink, backlink) VALUES (new.slotname, new.roomno, new.slotlink, new.backlink); RETURN NULL; END IF; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_wslot_bu BEFORE UPDATE ON WSlot FOR EACH ROW EXECUTE PROCEDURE tg_Wslot_bu (); -- ************************************************************ -- * BEFORE UPDATE on PLine -- * - do delete/insert instead of update if name changes -- ************************************************************ CREATE FUNCTION tg_pline_bu () RETURNS TRIGGER AS $$ BEGIN IF new.slotname != old.slotname THEN DELETE FROM PLine WHERE slotname = old.slotname; INSERT INTO PLine (slotname, phonenumber, comment, backlink) VALUES (new.slotname, new.phonenumber, new.comment, new.backlink); RETURN NULL; END IF; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_pline_bu BEFORE UPDATE ON PLine FOR EACH ROW EXECUTE PROCEDURE tg_pline_bu (); -- ************************************************************ -- * BEFORE UPDATE on IFace -- * - do delete/insert instead of update if name changes -- ************************************************************ CREATE FUNCTION tg_iface_bu () RETURNS TRIGGER AS $$ BEGIN IF new.slotname != old.slotname THEN DELETE FROM IFace WHERE slotname = old.slotname; INSERT INTO IFace (slotname, sysname, ifname, slotlink) VALUES (new.slotname, new.sysname, new.ifname, new.slotlink); RETURN NULL; END IF; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_iface_bu BEFORE UPDATE ON IFace FOR EACH ROW EXECUTE PROCEDURE tg_iface_bu (); -- ************************************************************ -- * BEFORE UPDATE on HSlot -- * - do delete/insert instead of update if name changes -- ************************************************************ CREATE FUNCTION tg_hslot_bu () RETURNS TRIGGER AS $$ BEGIN IF new.slotname != old.slotname OR new.hubname != old.hubname THEN DELETE FROM HSlot WHERE slotname = old.slotname; INSERT INTO HSlot (slotname, hubname, slotno, slotlink) VALUES (new.slotname, new.hubname, new.slotno, new.slotlink); RETURN NULL; END IF; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_hslot_bu BEFORE UPDATE ON HSlot FOR EACH ROW EXECUTE PROCEDURE tg_hslot_bu (); -- ************************************************************ -- * BEFORE UPDATE on PHone -- * - do delete/insert instead of update if name changes -- ************************************************************ CREATE FUNCTION tg_phone_bu () RETURNS TRIGGER AS $$ BEGIN IF new.slotname != old.slotname THEN DELETE FROM PHone WHERE slotname = old.slotname; INSERT INTO PHone (slotname, comment, slotlink) VALUES (new.slotname, new.comment, new.slotlink); RETURN NULL; END IF; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_phone_bu BEFORE UPDATE ON PHone FOR EACH ROW EXECUTE PROCEDURE tg_phone_bu (); -- ************************************************************ -- * AFTER INSERT or UPDATE or DELETE on slot with backlink -- * - Ensure that the opponent correctly points back to us -- ************************************************************ CREATE FUNCTION tg_backlink_a () RETURNS TRIGGER AS $$ DECLARE dummy integer; BEGIN IF tg_op = 'INSERT' THEN IF new.backlink != '' THEN dummy := tg_backlink_set (new.backlink, new.slotname); END IF; RETURN new; END IF; IF tg_op = 'UPDATE' THEN IF new.backlink != old.backlink THEN IF old.backlink != '' THEN dummy := tg_backlink_unset (old.backlink, old.slotname); END IF; IF new.backlink != '' THEN dummy := tg_backlink_set (new.backlink, new.slotname); END IF; ELSE IF new.slotname != old.slotname AND new.backlink != '' THEN dummy := tg_slotlink_set (new.backlink, new.slotname); END IF; END IF; RETURN new; END IF; IF tg_op = 'DELETE' THEN IF old.backlink != '' THEN dummy := tg_backlink_unset (old.backlink, old.slotname); END IF; RETURN old; END IF; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_backlink_a AFTER INSERT OR UPDATE OR DELETE ON PSlot FOR EACH ROW EXECUTE PROCEDURE tg_backlink_a ('PS'); CREATE TRIGGER tg_backlink_a AFTER INSERT OR UPDATE OR DELETE ON WSlot FOR EACH ROW EXECUTE PROCEDURE tg_backlink_a ('WS'); CREATE TRIGGER tg_backlink_a AFTER INSERT OR UPDATE OR DELETE ON PLine FOR EACH ROW EXECUTE PROCEDURE tg_backlink_a ('PL'); -- ************************************************************ -- * Support function to set the opponents backlink field -- * if it does not already point to the requested slot -- ************************************************************ CREATE FUNCTION tg_backlink_set (myname bpchar, blname bpchar) RETURNS integer AS $$ DECLARE mytype char(2); link char(4); rec record; BEGIN mytype := substr(myname, 1, 2); link := mytype || substr(blname, 1, 2); IF link = 'PLPL' THEN RAISE exception 'backlink between two phone lines does not make sense'; END IF; IF link IN ('PLWS', 'WSPL') THEN RAISE exception 'direct link of phone line to wall slot not permitted'; END IF; IF mytype = 'PS' THEN SELECT INTO rec * FROM PSlot WHERE slotname = myname; IF NOT found THEN RAISE exception '% does not exist', myname; END IF; IF rec.backlink != blname THEN UPDATE PSlot SET backlink = blname WHERE slotname = myname; END IF; RETURN 0; END IF; IF mytype = 'WS' THEN SELECT INTO rec * FROM WSlot WHERE slotname = myname; IF NOT found THEN RAISE exception '% does not exist', myname; END IF; IF rec.backlink != blname THEN UPDATE WSlot SET backlink = blname WHERE slotname = myname; END IF; RETURN 0; END IF; IF mytype = 'PL' THEN SELECT INTO rec * FROM PLine WHERE slotname = myname; IF NOT found THEN RAISE exception '% does not exist', myname; END IF; IF rec.backlink != blname THEN UPDATE PLine SET backlink = blname WHERE slotname = myname; END IF; RETURN 0; END IF; RAISE exception 'illegal backlink beginning with %', mytype; END; $$ LANGUAGE plpgsql; -- ************************************************************ -- * Support function to clear out the backlink field if -- * it still points to specific slot -- ************************************************************ CREATE FUNCTION tg_backlink_unset (bpchar, bpchar) RETURNS integer AS $$ DECLARE myname alias FOR $1; blname alias FOR $2; mytype char(2); rec record; BEGIN mytype := substr(myname, 1, 2); IF mytype = 'PS' THEN SELECT INTO rec * FROM PSlot WHERE slotname = myname; IF NOT found THEN RETURN 0; END IF; IF rec.backlink = blname THEN UPDATE PSlot SET backlink = '' WHERE slotname = myname; END IF; RETURN 0; END IF; IF mytype = 'WS' THEN SELECT INTO rec * FROM WSlot WHERE slotname = myname; IF NOT found THEN RETURN 0; END IF; IF rec.backlink = blname THEN UPDATE WSlot SET backlink = '' WHERE slotname = myname; END IF; RETURN 0; END IF; IF mytype = 'PL' THEN SELECT INTO rec * FROM PLine WHERE slotname = myname; IF NOT found THEN RETURN 0; END IF; IF rec.backlink = blname THEN UPDATE PLine SET backlink = '' WHERE slotname = myname; END IF; RETURN 0; END IF; END $$ LANGUAGE plpgsql; -- ************************************************************ -- * AFTER INSERT or UPDATE or DELETE on slot with slotlink -- * - Ensure that the opponent correctly points back to us -- ************************************************************ CREATE FUNCTION tg_slotlink_a () RETURNS TRIGGER AS $$ DECLARE dummy integer; BEGIN IF tg_op = 'INSERT' THEN IF new.slotlink != '' THEN dummy := tg_slotlink_set (new.slotlink, new.slotname); END IF; RETURN new; END IF; IF tg_op = 'UPDATE' THEN IF new.slotlink != old.slotlink THEN IF old.slotlink != '' THEN dummy := tg_slotlink_unset (old.slotlink, old.slotname); END IF; IF new.slotlink != '' THEN dummy := tg_slotlink_set (new.slotlink, new.slotname); END IF; ELSE IF new.slotname != old.slotname AND new.slotlink != '' THEN dummy := tg_slotlink_set (new.slotlink, new.slotname); END IF; END IF; RETURN new; END IF; IF tg_op = 'DELETE' THEN IF old.slotlink != '' THEN dummy := tg_slotlink_unset (old.slotlink, old.slotname); END IF; RETURN old; END IF; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tg_slotlink_a AFTER INSERT OR UPDATE OR DELETE ON PSlot FOR EACH ROW EXECUTE PROCEDURE tg_slotlink_a ('PS'); CREATE TRIGGER tg_slotlink_a AFTER INSERT OR UPDATE OR DELETE ON WSlot FOR EACH ROW EXECUTE PROCEDURE tg_slotlink_a ('WS'); CREATE TRIGGER tg_slotlink_a AFTER INSERT OR UPDATE OR DELETE ON IFace FOR EACH ROW EXECUTE PROCEDURE tg_slotlink_a ('IF'); CREATE TRIGGER tg_slotlink_a AFTER INSERT OR UPDATE OR DELETE ON HSlot FOR EACH ROW EXECUTE PROCEDURE tg_slotlink_a ('HS'); CREATE TRIGGER tg_slotlink_a AFTER INSERT OR UPDATE OR DELETE ON PHone FOR EACH ROW EXECUTE PROCEDURE tg_slotlink_a ('PH'); -- ************************************************************ -- * Support function to set the opponents slotlink field -- * if it does not already point to the requested slot -- ************************************************************ CREATE FUNCTION tg_slotlink_set (bpchar, bpchar) RETURNS integer AS $$ DECLARE myname alias FOR $1; blname alias FOR $2; mytype char(2); link char(4); rec record; BEGIN mytype := substr(myname, 1, 2); link := mytype || substr(blname, 1, 2); IF link = 'PHPH' THEN RAISE exception 'slotlink between two phones does not make sense'; END IF; IF link IN ('PHHS', 'HSPH') THEN RAISE exception 'link of phone to hub does not make sense'; END IF; IF link IN ('PHIF', 'IFPH') THEN RAISE exception 'link of phone to hub does not make sense'; END IF; IF link IN ('PSWS', 'WSPS') THEN RAISE exception 'slotlink from patchslot to wallslot not permitted'; END IF; IF mytype = 'PS' THEN SELECT INTO rec * FROM PSlot WHERE slotname = myname; IF NOT found THEN RAISE exception '% does not exist', myname; END IF; IF rec.slotlink != blname THEN UPDATE PSlot SET slotlink = blname WHERE slotname = myname; END IF; RETURN 0; END IF; IF mytype = 'WS' THEN SELECT INTO rec * FROM WSlot WHERE slotname = myname; IF NOT found THEN RAISE exception '% does not exist', myname; END IF; IF rec.slotlink != blname THEN UPDATE WSlot SET slotlink = blname WHERE slotname = myname; END IF; RETURN 0; END IF; IF mytype = 'IF' THEN SELECT INTO rec * FROM IFace WHERE slotname = myname; IF NOT found THEN RAISE exception '% does not exist', myname; END IF; IF rec.slotlink != blname THEN UPDATE IFace SET slotlink = blname WHERE slotname = myname; END IF; RETURN 0; END IF; IF mytype = 'HS' THEN SELECT INTO rec * FROM HSlot WHERE slotname = myname; IF NOT found THEN RAISE exception '% does not exist', myname; END IF; IF rec.slotlink != blname THEN UPDATE HSlot SET slotlink = blname WHERE slotname = myname; END IF; RETURN 0; END IF; IF mytype = 'PH' THEN SELECT INTO rec * FROM PHone WHERE slotname = myname; IF NOT found THEN RAISE exception '% does not exist', myname; END IF; IF rec.slotlink != blname THEN UPDATE PHone SET slotlink = blname WHERE slotname = myname; END IF; RETURN 0; END IF; RAISE exception 'illegal slotlink beginning with %', mytype; END; $$ LANGUAGE plpgsql; -- ************************************************************ -- * Support function to clear out the slotlink field if -- * it still points to specific slot -- ************************************************************ CREATE FUNCTION tg_slotlink_unset (bpchar, bpchar) RETURNS integer AS $$ DECLARE myname alias FOR $1; blname alias FOR $2; mytype char(2); rec record; BEGIN mytype := substr(myname, 1, 2); IF mytype = 'PS' THEN SELECT INTO rec * FROM PSlot WHERE slotname = myname; IF NOT found THEN RETURN 0; END IF; IF rec.slotlink = blname THEN UPDATE PSlot SET slotlink = '' WHERE slotname = myname; END IF; RETURN 0; END IF; IF mytype = 'WS' THEN SELECT INTO rec * FROM WSlot WHERE slotname = myname; IF NOT found THEN RETURN 0; END IF; IF rec.slotlink = blname THEN UPDATE WSlot SET slotlink = '' WHERE slotname = myname; END IF; RETURN 0; END IF; IF mytype = 'IF' THEN SELECT INTO rec * FROM IFace WHERE slotname = myname; IF NOT found THEN RETURN 0; END IF; IF rec.slotlink = blname THEN UPDATE IFace SET slotlink = '' WHERE slotname = myname; END IF; RETURN 0; END IF; IF mytype = 'HS' THEN SELECT INTO rec * FROM HSlot WHERE slotname = myname; IF NOT found THEN RETURN 0; END IF; IF rec.slotlink = blname THEN UPDATE HSlot SET slotlink = '' WHERE slotname = myname; END IF; RETURN 0; END IF; IF mytype = 'PH' THEN SELECT INTO rec * FROM PHone WHERE slotname = myname; IF NOT found THEN RETURN 0; END IF; IF rec.slotlink = blname THEN UPDATE PHone SET slotlink = '' WHERE slotname = myname; END IF; RETURN 0; END IF; END; $$ LANGUAGE plpgsql; -- ************************************************************ -- * Describe the backside of a patchfield slot -- ************************************************************ CREATE FUNCTION pslot_backlink_view (bpchar) RETURNS text AS $$ << OUTER >> DECLARE rec record; bltype char(2); retval text; BEGIN SELECT INTO rec * FROM PSlot WHERE slotname = $1; IF NOT found THEN RETURN ''; END IF; IF rec.backlink = '' THEN RETURN '-'; END IF; bltype := substr(rec.backlink, 1, 2); IF bltype = 'PL' THEN DECLARE rec record; BEGIN SELECT INTO rec * FROM PLine WHERE slotname = "outer".rec.backlink; retval := 'Phone line ' || trim(rec.phonenumber); IF rec.comment != '' THEN retval := retval || ' ('; retval := retval || rec.comment; retval := retval || ')'; END IF; RETURN retval; END; END IF; IF bltype = 'WS' THEN SELECT INTO rec * FROM WSlot WHERE slotname = rec.backlink; retval := trim(rec.slotname) || ' in room '; retval := retval || trim(rec.roomno); retval := retval || ' -> '; RETURN retval || wslot_slotlink_view (rec.slotname); END IF; RETURN rec.backlink; END; $$ LANGUAGE plpgsql; -- ************************************************************ -- * Describe the front of a patchfield slot -- ************************************************************ CREATE FUNCTION pslot_slotlink_view (bpchar) RETURNS text AS $$ DECLARE psrec record; sltype char(2); retval text; BEGIN SELECT INTO psrec * FROM PSlot WHERE slotname = $1; IF NOT found THEN RETURN ''; END IF; IF psrec.slotlink = '' THEN RETURN '-'; END IF; sltype := substr(psrec.slotlink, 1, 2); IF sltype = 'PS' THEN retval := trim(psrec.slotlink) || ' -> '; RETURN retval || pslot_backlink_view (psrec.slotlink); END IF; IF sltype = 'HS' THEN retval := comment FROM Hub H, HSlot HS WHERE HS.slotname = psrec.slotlink AND H.name = HS.hubname; retval := retval || ' slot '; retval := retval || slotno::text FROM HSlot WHERE slotname = psrec.slotlink; RETURN retval; END IF; RETURN psrec.slotlink; END; $$ LANGUAGE plpgsql; -- ************************************************************ -- * Describe the front of a wall connector slot -- ************************************************************ CREATE FUNCTION wslot_slotlink_view (bpchar) RETURNS text AS $$ DECLARE rec record; sltype char(2); retval text; BEGIN SELECT INTO rec * FROM WSlot WHERE slotname = $1; IF NOT found THEN RETURN ''; END IF; IF rec.slotlink = '' THEN RETURN '-'; END IF; sltype := substr(rec.slotlink, 1, 2); IF sltype = 'PH' THEN SELECT INTO rec * FROM PHone WHERE slotname = rec.slotlink; retval := 'Phone ' || trim(rec.slotname); IF rec.comment != '' THEN retval := retval || ' ('; retval := retval || rec.comment; retval := retval || ')'; END IF; RETURN retval; END IF; IF sltype = 'IF' THEN DECLARE syrow SYSTEM % RowType; ifrow IFace % ROWTYPE; BEGIN SELECT INTO ifrow * FROM IFace WHERE slotname = rec.slotlink; SELECT INTO syrow * FROM SYSTEM WHERE name = ifrow.sysname; retval := syrow.name || ' IF '; retval := retval || ifrow.ifname; IF syrow.comment != '' THEN retval := retval || ' ('; retval := retval || syrow.comment; retval := retval || ')'; END IF; RETURN retval; END; END IF; RETURN rec.slotlink; END; $$ LANGUAGE plpgsql; -- ************************************************************ -- * View of a patchfield describing backside and patches -- ************************************************************ CREATE VIEW Pfield_v1 AS SELECT PF.pfname, PF.slotname, pslot_backlink_view (PF.slotname) AS backside, pslot_slotlink_view (PF.slotname) AS patch FROM PSlot PF; -- -- First we build the house - so we create the rooms -- INSERT INTO Room VALUES ('001', 'Entrance'); INSERT INTO Room VALUES ('002', 'Office'); INSERT INTO Room VALUES ('003', 'Office'); INSERT INTO Room VALUES ('004', 'Technical'); INSERT INTO Room VALUES ('101', 'Office'); INSERT INTO Room VALUES ('102', 'Conference'); INSERT INTO Room VALUES ('103', 'Restroom'); INSERT INTO Room VALUES ('104', 'Technical'); INSERT INTO Room VALUES ('105', 'Office'); INSERT INTO Room VALUES ('106', 'Office'); -- -- Second we install the wall connectors -- INSERT INTO WSlot VALUES ('WS.001.1a', '001', ', '); INSERT INTO WSlot VALUES ('WS.001.1b', '001', ', '); INSERT INTO WSlot VALUES ('WS.001.2a', '001', ', '); INSERT INTO WSlot VALUES ('WS.001.2b', '001', ', '); INSERT INTO WSlot VALUES ('WS.001.3a', '001', ', '); INSERT INTO WSlot VALUES ('WS.001.3b', '001', ', '); INSERT INTO WSlot VALUES ('WS.002.1a', '002', ', '); INSERT INTO WSlot VALUES ('WS.002.1b', '002', ', '); INSERT INTO WSlot VALUES ('WS.002.2a', '002', ', '); INSERT INTO WSlot VALUES ('WS.002.2b', '002', ', '); INSERT INTO WSlot VALUES ('WS.002.3a', '002', ', '); INSERT INTO WSlot VALUES ('WS.002.3b', '002', ', '); INSERT INTO WSlot VALUES ('WS.003.1a', '003', ', '); INSERT INTO WSlot VALUES ('WS.003.1b', '003', ', '); INSERT INTO WSlot VALUES ('WS.003.2a', '003', ', '); INSERT INTO WSlot VALUES ('WS.003.2b', '003', ', '); INSERT INTO WSlot VALUES ('WS.003.3a', '003', ', '); INSERT INTO WSlot VALUES ('WS.003.3b', '003', ', '); INSERT INTO WSlot VALUES ('WS.101.1a', '101', ', '); INSERT INTO WSlot VALUES ('WS.101.1b', '101', ', '); INSERT INTO WSlot VALUES ('WS.101.2a', '101', ', '); INSERT INTO WSlot VALUES ('WS.101.2b', '101', ', '); INSERT INTO WSlot VALUES ('WS.101.3a', '101', ', '); INSERT INTO WSlot VALUES ('WS.101.3b', '101', ', '); INSERT INTO WSlot VALUES ('WS.102.1a', '102', ', '); INSERT INTO WSlot VALUES ('WS.102.1b', '102', ', '); INSERT INTO WSlot VALUES ('WS.102.2a', '102', ', '); INSERT INTO WSlot VALUES ('WS.102.2b', '102', ', '); INSERT INTO WSlot VALUES ('WS.102.3a', '102', ', '); INSERT INTO WSlot VALUES ('WS.102.3b', '102', ', '); INSERT INTO WSlot VALUES ('WS.105.1a', '105', ', '); INSERT INTO WSlot VALUES ('WS.105.1b', '105', ', '); INSERT INTO WSlot VALUES ('WS.105.2a', '105', ', '); INSERT INTO WSlot VALUES ('WS.105.2b', '105', ', '); INSERT INTO WSlot VALUES ('WS.105.3a', '105', ', '); INSERT INTO WSlot VALUES ('WS.105.3b', '105', ', '); INSERT INTO WSlot VALUES ('WS.106.1a', '106', ', '); INSERT INTO WSlot VALUES ('WS.106.1b', '106', ', '); INSERT INTO WSlot VALUES ('WS.106.2a', '106', ', '); INSERT INTO WSlot VALUES ('WS.106.2b', '106', ', '); INSERT INTO WSlot VALUES ('WS.106.3a', '106', ', '); INSERT INTO WSlot VALUES ('WS.106.3b', '106', ', '); -- -- Now create the patch fields and their slots -- INSERT INTO PField VALUES ('PF0_1', 'Wallslots basement'); -- -- The cables for these will be made later, so they are unconnected for now -- INSERT INTO PSlot VALUES ('PS.base.a1', 'PF0_1', ', '); INSERT INTO PSlot VALUES ('PS.base.a2', 'PF0_1', ', '); INSERT INTO PSlot VALUES ('PS.base.a3', 'PF0_1', ', '); INSERT INTO PSlot VALUES ('PS.base.a4', 'PF0_1', ', '); INSERT INTO PSlot VALUES ('PS.base.a5', 'PF0_1', ', '); INSERT INTO PSlot VALUES ('PS.base.a6', 'PF0_1', ', '); -- -- These are already wired to the wall connectors -- INSERT INTO PSlot VALUES ('PS.base.b1', 'PF0_1', '', 'WS.002.1a'); INSERT INTO PSlot VALUES ('PS.base.b2', 'PF0_1', '', 'WS.002.1b'); INSERT INTO PSlot VALUES ('PS.base.b3', 'PF0_1', '', 'WS.002.2a'); INSERT INTO PSlot VALUES ('PS.base.b4', 'PF0_1', '', 'WS.002.2b'); INSERT INTO PSlot VALUES ('PS.base.b5', 'PF0_1', '', 'WS.002.3a'); INSERT INTO PSlot VALUES ('PS.base.b6', 'PF0_1', '', 'WS.002.3b'); INSERT INTO PSlot VALUES ('PS.base.c1', 'PF0_1', '', 'WS.003.1a'); INSERT INTO PSlot VALUES ('PS.base.c2', 'PF0_1', '', 'WS.003.1b'); INSERT INTO PSlot VALUES ('PS.base.c3', 'PF0_1', '', 'WS.003.2a'); INSERT INTO PSlot VALUES ('PS.base.c4', 'PF0_1', '', 'WS.003.2b'); INSERT INTO PSlot VALUES ('PS.base.c5', 'PF0_1', '', 'WS.003.3a'); INSERT INTO PSlot VALUES ('PS.base.c6', 'PF0_1', '', 'WS.003.3b'); -- -- This patchfield will be renamed later into PF0_2 - so its -- slots references in pfname should follow -- INSERT INTO PField VALUES ('PF0_X', 'Phonelines basement'); INSERT INTO PSlot VALUES ('PS.base.ta1', 'PF0_X', ', '); INSERT INTO PSlot VALUES ('PS.base.ta2', 'PF0_X', ', '); INSERT INTO PSlot VALUES ('PS.base.ta3', 'PF0_X', ', '); INSERT INTO PSlot VALUES ('PS.base.ta4', 'PF0_X', ', '); INSERT INTO PSlot VALUES ('PS.base.ta5', 'PF0_X', ', '); INSERT INTO PSlot VALUES ('PS.base.ta6', 'PF0_X', ', '); INSERT INTO PSlot VALUES ('PS.base.tb1', 'PF0_X', ', '); INSERT INTO PSlot VALUES ('PS.base.tb2', 'PF0_X', ', '); INSERT INTO PSlot VALUES ('PS.base.tb3', 'PF0_X', ', '); INSERT INTO PSlot VALUES ('PS.base.tb4', 'PF0_X', ', '); INSERT INTO PSlot VALUES ('PS.base.tb5', 'PF0_X', ', '); INSERT INTO PSlot VALUES ('PS.base.tb6', 'PF0_X', ', '); INSERT INTO PField VALUES ('PF1_1', 'Wallslots first floor'); INSERT INTO PSlot VALUES ('PS.first.a1', 'PF1_1', '', 'WS.101.1a'); INSERT INTO PSlot VALUES ('PS.first.a2', 'PF1_1', '', 'WS.101.1b'); INSERT INTO PSlot VALUES ('PS.first.a3', 'PF1_1', '', 'WS.101.2a'); INSERT INTO PSlot VALUES ('PS.first.a4', 'PF1_1', '', 'WS.101.2b'); INSERT INTO PSlot VALUES ('PS.first.a5', 'PF1_1', '', 'WS.101.3a'); INSERT INTO PSlot VALUES ('PS.first.a6', 'PF1_1', '', 'WS.101.3b'); INSERT INTO PSlot VALUES ('PS.first.b1', 'PF1_1', '', 'WS.102.1a'); INSERT INTO PSlot VALUES ('PS.first.b2', 'PF1_1', '', 'WS.102.1b'); INSERT INTO PSlot VALUES ('PS.first.b3', 'PF1_1', '', 'WS.102.2a'); INSERT INTO PSlot VALUES ('PS.first.b4', 'PF1_1', '', 'WS.102.2b'); INSERT INTO PSlot VALUES ('PS.first.b5', 'PF1_1', '', 'WS.102.3a'); INSERT INTO PSlot VALUES ('PS.first.b6', 'PF1_1', '', 'WS.102.3b'); INSERT INTO PSlot VALUES ('PS.first.c1', 'PF1_1', '', 'WS.105.1a'); INSERT INTO PSlot VALUES ('PS.first.c2', 'PF1_1', '', 'WS.105.1b'); INSERT INTO PSlot VALUES ('PS.first.c3', 'PF1_1', '', 'WS.105.2a'); INSERT INTO PSlot VALUES ('PS.first.c4', 'PF1_1', '', 'WS.105.2b'); INSERT INTO PSlot VALUES ('PS.first.c5', 'PF1_1', '', 'WS.105.3a'); INSERT INTO PSlot VALUES ('PS.first.c6', 'PF1_1', '', 'WS.105.3b'); INSERT INTO PSlot VALUES ('PS.first.d1', 'PF1_1', '', 'WS.106.1a'); INSERT INTO PSlot VALUES ('PS.first.d2', 'PF1_1', '', 'WS.106.1b'); INSERT INTO PSlot VALUES ('PS.first.d3', 'PF1_1', '', 'WS.106.2a'); INSERT INTO PSlot VALUES ('PS.first.d4', 'PF1_1', '', 'WS.106.2b'); INSERT INTO PSlot VALUES ('PS.first.d5', 'PF1_1', '', 'WS.106.3a'); INSERT INTO PSlot VALUES ('PS.first.d6', 'PF1_1', '', 'WS.106.3b'); -- -- Now we wire the wall connectors 1a-2a in room 001 to the -- patchfield. In the second update we make an error, and -- correct it after -- UPDATE PSlot SET backlink = 'WS.001.1a' WHERE slotname = 'PS.base.a1'; UPDATE PSlot SET backlink = 'WS.001.1b' WHERE slotname = 'PS.base.a3'; SELECT * FROM WSlot WHERE roomno = '001' ORDER BY slotname; SELECT * FROM PSlot WHERE slotname ~ 'PS.base.a' ORDER BY slotname; UPDATE PSlot SET backlink = 'WS.001.2a' WHERE slotname = 'PS.base.a3'; SELECT * FROM WSlot WHERE roomno = '001' ORDER BY slotname; SELECT * FROM PSlot WHERE slotname ~ 'PS.base.a' ORDER BY slotname; UPDATE PSlot SET backlink = 'WS.001.1b' WHERE slotname = 'PS.base.a2'; SELECT * FROM WSlot WHERE roomno = '001' ORDER BY slotname; SELECT * FROM PSlot WHERE slotname ~ 'PS.base.a' ORDER BY slotname; -- -- Same procedure for 2b-3b but this time updating the WSlot instead -- of the PSlot. Due to the triggers the result is the same: -- WSlot and corresponding PSlot point to each other. -- UPDATE WSlot SET backlink = 'PS.base.a4' WHERE slotname = 'WS.001.2b'; UPDATE WSlot SET backlink = 'PS.base.a6' WHERE slotname = 'WS.001.3a'; SELECT * FROM WSlot WHERE roomno = '001' ORDER BY slotname; SELECT * FROM PSlot WHERE slotname ~ 'PS.base.a' ORDER BY slotname; UPDATE WSlot SET backlink = 'PS.base.a6' WHERE slotname = 'WS.001.3b'; SELECT * FROM WSlot WHERE roomno = '001' ORDER BY slotname; SELECT * FROM PSlot WHERE slotname ~ 'PS.base.a' ORDER BY slotname; UPDATE WSlot SET backlink = 'PS.base.a5' WHERE slotname = 'WS.001.3a'; SELECT * FROM WSlot WHERE roomno = '001' ORDER BY slotname; SELECT * FROM PSlot WHERE slotname ~ 'PS.base.a' ORDER BY slotname; INSERT INTO PField VALUES ('PF1_2', 'Phonelines first floor'); INSERT INTO PSlot VALUES ('PS.first.ta1', 'PF1_2', ', '); INSERT INTO PSlot VALUES ('PS.first.ta2', 'PF1_2', ', '); INSERT INTO PSlot VALUES ('PS.first.ta3', 'PF1_2', ', '); INSERT INTO PSlot VALUES ('PS.first.ta4', 'PF1_2', ', '); INSERT INTO PSlot VALUES ('PS.first.ta5', 'PF1_2', ', '); INSERT INTO PSlot VALUES ('PS.first.ta6', 'PF1_2', ', '); INSERT INTO PSlot VALUES ('PS.first.tb1', 'PF1_2', ', '); INSERT INTO PSlot VALUES ('PS.first.tb2', 'PF1_2', ', '); INSERT INTO PSlot VALUES ('PS.first.tb3', 'PF1_2', ', '); INSERT INTO PSlot VALUES ('PS.first.tb4', 'PF1_2', ', '); INSERT INTO PSlot VALUES ('PS.first.tb5', 'PF1_2', ', '); INSERT INTO PSlot VALUES ('PS.first.tb6', 'PF1_2', ', '); -- -- Fix the wrong name for patchfield PF0_2 -- UPDATE PField SET name = 'PF0_2' WHERE name = 'PF0_X'; SELECT * FROM PSlot ORDER BY slotname; SELECT * FROM WSlot ORDER BY slotname; -- -- Install the central phone system and create the phone numbers. -- They are wired on insert to the patchfields. Again the -- triggers automatically tell the PSlots to update their -- backlink field. -- INSERT INTO PLine VALUES ('PL.001', '-0', 'Central call', 'PS.base.ta1'); INSERT INTO PLine VALUES ('PL.002', '-101', '', 'PS.base.ta2'); INSERT INTO PLine VALUES ('PL.003', '-102', '', 'PS.base.ta3'); INSERT INTO PLine VALUES ('PL.004', '-103', '', 'PS.base.ta5'); INSERT INTO PLine VALUES ('PL.005', '-104', '', 'PS.base.ta6'); INSERT INTO PLine VALUES ('PL.006', '-106', '', 'PS.base.tb2'); INSERT INTO PLine VALUES ('PL.007', '-108', '', 'PS.base.tb3'); INSERT INTO PLine VALUES ('PL.008', '-109', '', 'PS.base.tb4'); INSERT INTO PLine VALUES ('PL.009', '-121', '', 'PS.base.tb5'); INSERT INTO PLine VALUES ('PL.010', '-122', '', 'PS.base.tb6'); INSERT INTO PLine VALUES ('PL.015', '-134', '', 'PS.first.ta1'); INSERT INTO PLine VALUES ('PL.016', '-137', '', 'PS.first.ta3'); INSERT INTO PLine VALUES ('PL.017', '-139', '', 'PS.first.ta4'); INSERT INTO PLine VALUES ('PL.018', '-362', '', 'PS.first.tb1'); INSERT INTO PLine VALUES ('PL.019', '-363', '', 'PS.first.tb2'); INSERT INTO PLine VALUES ('PL.020', '-364', '', 'PS.first.tb3'); INSERT INTO PLine VALUES ('PL.021', '-365', '', 'PS.first.tb5'); INSERT INTO PLine VALUES ('PL.022', '-367', '', 'PS.first.tb6'); INSERT INTO PLine VALUES ('PL.028', '-501', 'Fax entrance', 'PS.base.ta2'); INSERT INTO PLine VALUES ('PL.029', '-502', 'Fax first floor', 'PS.first.ta1'); -- -- Buy some phones, plug them into the wall and patch the -- phone lines to the corresponding patchfield slots. -- INSERT INTO PHone VALUES ('PH.hc001', 'Hicom standard', 'WS.001.1a'); UPDATE PSlot SET slotlink = 'PS.base.ta1' WHERE slotname = 'PS.base.a1'; INSERT INTO PHone VALUES ('PH.hc002', 'Hicom standard', 'WS.002.1a'); UPDATE PSlot SET slotlink = 'PS.base.ta5' WHERE slotname = 'PS.base.b1'; INSERT INTO PHone VALUES ('PH.hc003', 'Hicom standard', 'WS.002.2a'); UPDATE PSlot SET slotlink = 'PS.base.tb2' WHERE slotname = 'PS.base.b3'; INSERT INTO PHone VALUES ('PH.fax001', 'Canon fax', 'WS.001.2a'); UPDATE PSlot SET slotlink = 'PS.base.ta2' WHERE slotname = 'PS.base.a3'; -- -- Install a hub at one of the patchfields, plug a computers -- ethernet interface into the wall and patch it to the hub. -- INSERT INTO Hub VALUES ('base.hub1', 'Patchfield PF0_1 hub', 16); INSERT INTO SYSTEM VALUES ('orion', 'PC'); INSERT INTO IFace VALUES ('IF', 'orion', 'eth0', 'WS.002.1b'); UPDATE PSlot SET slotlink = 'HS.base.hub1.1' WHERE slotname = 'PS.base.b2'; -- -- Now we take a look at the patchfield -- SELECT * FROM PField_v1 WHERE pfname = 'PF0_1' ORDER BY slotname; SELECT * FROM PField_v1 WHERE pfname = 'PF0_2' ORDER BY slotname; -- -- Finally we want errors -- INSERT INTO PField VALUES ('PF1_1', 'should fail due to unique index'); UPDATE PSlot SET backlink = 'WS.not.there' WHERE slotname = 'PS.base.a1'; UPDATE PSlot SET backlink = 'XX.illegal' WHERE slotname = 'PS.base.a1'; UPDATE PSlot SET slotlink = 'PS.not.there' WHERE slotname = 'PS.base.a1'; UPDATE PSlot SET slotlink = 'XX.illegal' WHERE slotname = 'PS.base.a1'; INSERT INTO HSlot VALUES ('HS', 'base.hub1', 1, ''); INSERT INTO HSlot VALUES ('HS', 'base.hub1', 20, ''); DELETE FROM HSlot; INSERT INTO IFace VALUES ('IF', 'notthere', 'eth0', ''); INSERT INTO IFace VALUES ('IF', 'orion', 'ethernet_interface_name_too_long', ''); -- -- The following tests are unrelated to the scenario outlined above; -- they merely exercise specific parts of PL/pgSQL -- -- -- Test recursion, per bug report 7-Sep-01 -- CREATE FUNCTION recursion_test (int, int) RETURNS text AS $$ DECLARE rslt text; BEGIN IF $1 <= 0 THEN rslt = CAST($2 AS TEXT); ELSE rslt = CAST($1 AS TEXT) || ',' || recursion_test ($1 - 1, $2); END IF; RETURN rslt; END; $$ LANGUAGE plpgsql; SELECT recursion_test (4, 3); -- -- Test the FOUND magic variable -- CREATE TABLE found_test_tbl ( a int ); CREATE FUNCTION test_found () RETURNS boolean AS $$ DECLARE BEGIN INSERT INTO found_test_tbl VALUES (1); IF FOUND THEN INSERT INTO found_test_tbl VALUES (2); END IF; UPDATE found_test_tbl SET a = 100 WHERE a = 1; IF FOUND THEN INSERT INTO found_test_tbl VALUES (3); END IF; DELETE FROM found_test_tbl WHERE a = 9999; -- matches no rows IF NOT FOUND THEN INSERT INTO found_test_tbl VALUES (4); END IF; FOR i IN 1..10 LOOP -- no need to do anything END LOOP; IF FOUND THEN INSERT INTO found_test_tbl VALUES (5); END IF; -- never executes the loop FOR i IN 2..1 LOOP -- no need to do anything END LOOP; IF NOT FOUND THEN INSERT INTO found_test_tbl VALUES (6); END IF; RETURN TRUE; END; $$ LANGUAGE plpgsql; SELECT test_found (); SELECT * FROM found_test_tbl; -- -- Test set-returning functions for PL/pgSQL -- CREATE FUNCTION test_table_func_rec () RETURNS SETOF found_test_tbl AS $$ DECLARE rec RECORD; BEGIN FOR rec IN SELECT * FROM found_test_tbl LOOP RETURN NEXT rec; END LOOP; RETURN; END; $$ LANGUAGE plpgsql; SELECT * FROM test_table_func_rec (); CREATE FUNCTION test_table_func_row () RETURNS SETOF found_test_tbl AS $$ DECLARE ROW found_test_tbl % ROWTYPE; BEGIN FOR ROW IN SELECT * FROM found_test_tbl LOOP RETURN NEXT ROW; END LOOP; RETURN; END; $$ LANGUAGE plpgsql; SELECT * FROM test_table_func_row (); CREATE FUNCTION test_ret_set_scalar (int, int) RETURNS SETOF int AS $$ DECLARE i int; BEGIN FOR i IN $1..$2 LOOP RETURN NEXT i + 1; END LOOP; RETURN; END; $$ LANGUAGE plpgsql; SELECT * FROM test_ret_set_scalar (1, 10); CREATE FUNCTION test_ret_set_rec_dyn (int) RETURNS SETOF record AS $$ DECLARE retval RECORD; BEGIN IF $1 > 10 THEN SELECT INTO retval 5, 10, 15; RETURN NEXT retval; RETURN NEXT retval; ELSE SELECT INTO retval 50, 5::numeric, 'xxx'::text; RETURN NEXT retval; RETURN NEXT retval; END IF; RETURN; END; $$ LANGUAGE plpgsql; SELECT * FROM test_ret_set_rec_dyn (1500) AS (a int, b int, c int); SELECT * FROM test_ret_set_rec_dyn (5) AS (a int, b numeric, c text); CREATE FUNCTION test_ret_rec_dyn (int) RETURNS record AS $$ DECLARE retval RECORD; BEGIN IF $1 > 10 THEN SELECT INTO retval 5, 10, 15; RETURN retval; ELSE SELECT INTO retval 50, 5::numeric, 'xxx'::text; RETURN retval; END IF; END; $$ LANGUAGE plpgsql; SELECT * FROM test_ret_rec_dyn (1500) AS (a int, b int, c int); SELECT * FROM test_ret_rec_dyn (5) AS (a int, b numeric, c text); -- -- Test handling of OUT parameters, including polymorphic cases. -- Note that RETURN is optional with OUT params; we try both ways. -- -- wrong way to do it: CREATE FUNCTION f1 (IN i int, out j int) RETURNS int AS $$ BEGIN RETURN i + 1; END $$ LANGUAGE plpgsql; CREATE FUNCTION f1 (IN i int, out j int ) AS $$ BEGIN j := i + 1; RETURN; END $$ LANGUAGE plpgsql; SELECT f1 (42); SELECT * FROM f1 (42); CREATE OR REPLACE FUNCTION f1 (INOUT i int ) AS $$ BEGIN i := i + 1; END $$ LANGUAGE plpgsql; SELECT f1 (42); SELECT * FROM f1 (42); DROP FUNCTION f1 (int); CREATE FUNCTION f1 (IN i int, out j int) RETURNS SETOF int AS $$ BEGIN j := i + 1; RETURN NEXT; j := i + 2; RETURN NEXT; RETURN; END $$ LANGUAGE plpgsql; SELECT * FROM f1 (42); DROP FUNCTION f1 (int); CREATE FUNCTION f1 (IN i int, out j int, out k text ) AS $$ BEGIN j := i; j := j + 1; k := 'foo'; END $$ LANGUAGE plpgsql; SELECT f1 (42); SELECT * FROM f1 (42); DROP FUNCTION f1 (int); CREATE FUNCTION f1 (IN i int, out j int, out k text) RETURNS SETOF record AS $$ BEGIN j := i + 1; k := 'foo'; RETURN NEXT; j := j + 1; k := 'foot'; RETURN NEXT; END $$ LANGUAGE plpgsql; SELECT * FROM f1 (42); DROP FUNCTION f1 (int); CREATE FUNCTION duplic (IN i anyelement, out j anyelement, out k anyarray ) AS $$ BEGIN j := i; k := ARRAY[j, j]; RETURN; END $$ LANGUAGE plpgsql; SELECT * FROM duplic (42); SELECT * FROM duplic ('foo'::text); DROP FUNCTION duplic (anyelement); -- -- test PERFORM -- CREATE TABLE perform_test ( a int, b int ); CREATE FUNCTION perform_simple_func (int) RETURNS boolean AS $$ BEGIN IF $1 < 20 THEN INSERT INTO perform_test VALUES ($1, $1 + 10); RETURN TRUE; ELSE RETURN FALSE; END IF; END; $$ LANGUAGE plpgsql; CREATE FUNCTION perform_test_func () RETURNS void AS $$ BEGIN IF FOUND THEN INSERT INTO perform_test VALUES (100, 100); END IF; PERFORM perform_simple_func (5); IF FOUND THEN INSERT INTO perform_test VALUES (100, 100); END IF; PERFORM perform_simple_func (50); IF FOUND THEN INSERT INTO perform_test VALUES (100, 100); END IF; RETURN; END; $$ LANGUAGE plpgsql; SELECT perform_test_func (); SELECT * FROM perform_test; DROP TABLE perform_test; -- -- Test proper snapshot handling in simple expressions -- CREATE temp TABLE users ( LOGIN text, id serial ); CREATE FUNCTION sp_id_user (a_login text) RETURNS int AS $$ DECLARE x int; BEGIN SELECT INTO x id FROM users WHERE LOGIN = a_login; IF found THEN RETURN x; END IF; RETURN 0; END $$ LANGUAGE plpgsql STABLE; INSERT INTO users VALUES ('user1'); SELECT sp_id_user ('user1'); SELECT sp_id_user ('userx'); CREATE FUNCTION sp_add_user (a_login text) RETURNS int AS $$ DECLARE my_id_user int; BEGIN my_id_user = sp_id_user (a_login); IF my_id_user > 0 THEN RETURN - 1; -- error code for existing user END IF; INSERT INTO users (LOGIN) VALUES (a_login); my_id_user = sp_id_user (a_login); IF my_id_user = 0 THEN RETURN - 2; -- error code for insertion failure END IF; RETURN my_id_user; END $$ LANGUAGE plpgsql; SELECT sp_add_user ('user1'); SELECT sp_add_user ('user2'); SELECT sp_add_user ('user2'); SELECT sp_add_user ('user3'); SELECT sp_add_user ('user3'); DROP FUNCTION sp_add_user (text); DROP FUNCTION sp_id_user (text); -- -- tests for refcursors -- CREATE TABLE rc_test ( a int, b int ); CREATE FUNCTION return_unnamed_refcursor () RETURNS refcursor AS $$ DECLARE rc refcursor; BEGIN OPEN rc FOR SELECT a FROM rc_test; RETURN rc; END $$ LANGUAGE plpgsql; CREATE FUNCTION use_refcursor (rc refcursor) RETURNS int AS $$ DECLARE rc refcursor; x record; BEGIN rc := return_unnamed_refcursor (); FETCH NEXT FROM rc INTO x; RETURN x.a; END $$ LANGUAGE plpgsql; SELECT use_refcursor (return_unnamed_refcursor ()); CREATE FUNCTION return_refcursor (rc refcursor) RETURNS refcursor AS $$ BEGIN OPEN rc FOR SELECT a FROM rc_test; RETURN rc; END $$ LANGUAGE plpgsql; CREATE FUNCTION refcursor_test1 (refcursor) RETURNS refcursor AS $$ BEGIN PERFORM return_refcursor ($1); RETURN $1; END $$ LANGUAGE plpgsql; BEGIN; SELECT refcursor_test1 ('test1'); FETCH NEXT IN test1; SELECT refcursor_test1 ('test2'); FETCH ALL FROM test2; COMMIT; -- should fail FETCH NEXT FROM test1; CREATE FUNCTION refcursor_test2 (int, int) RETURNS boolean AS $$ DECLARE c1 CURSOR (param1 int, param2 int) FOR SELECT * FROM rc_test WHERE a > param1 AND b > param2; nonsense record; BEGIN OPEN c1 ($1, $2); FETCH c1 INTO nonsense; CLOSE c1; IF found THEN RETURN TRUE; ELSE RETURN FALSE; END IF; END $$ LANGUAGE plpgsql; SELECT refcursor_test2 (20000, 20000) AS "Should be false", refcursor_test2 (20, 20) AS "Should be true"; -- -- tests for cursors with named parameter arguments -- CREATE FUNCTION namedparmcursor_test1 (int, int) RETURNS boolean AS $$ DECLARE c1 CURSOR (param1 int, param12 int) FOR SELECT * FROM rc_test WHERE a > param1 AND b > param12; nonsense record; BEGIN OPEN c1 (param12 := $2, param1 := $1); FETCH c1 INTO nonsense; CLOSE c1; IF found THEN RETURN TRUE; ELSE RETURN FALSE; END IF; END $$ LANGUAGE plpgsql; SELECT namedparmcursor_test1 (20000, 20000) AS "Should be false", namedparmcursor_test1 (20, 20) AS "Should be true"; -- mixing named and positional argument notations CREATE FUNCTION namedparmcursor_test2 (int, int) RETURNS boolean AS $$ DECLARE c1 CURSOR (param1 int, param2 int) FOR SELECT * FROM rc_test WHERE a > param1 AND b > param2; nonsense record; BEGIN OPEN c1 (param1 := $1, $2); FETCH c1 INTO nonsense; CLOSE c1; IF found THEN RETURN TRUE; ELSE RETURN FALSE; END IF; END $$ LANGUAGE plpgsql; SELECT namedparmcursor_test2 (20, 20); -- mixing named and positional: param2 is given twice, once in named notation -- and second time in positional notation. Should throw an error at parse time CREATE FUNCTION namedparmcursor_test3 () RETURNS void AS $$ DECLARE c1 CURSOR (param1 int, param2 int) FOR SELECT * FROM rc_test WHERE a > param1 AND b > param2; BEGIN OPEN c1 (param2 := 20, 21); END $$ LANGUAGE plpgsql; -- mixing named and positional: same as previous test, but param1 is duplicated CREATE FUNCTION namedparmcursor_test4 () RETURNS void AS $$ DECLARE c1 CURSOR (param1 int, param2 int) FOR SELECT * FROM rc_test WHERE a > param1 AND b > param2; BEGIN OPEN c1 (20, param1 := 21); END $$ LANGUAGE plpgsql; -- duplicate named parameter, should throw an error at parse time CREATE FUNCTION namedparmcursor_test5 () RETURNS void AS $$ DECLARE c1 CURSOR (p1 int, p2 int) FOR SELECT * FROM tenk1 WHERE thousand = p1 AND tenthous = p2; BEGIN OPEN c1 (p2 := 77, p2 := 42); END $$ LANGUAGE plpgsql; -- not enough parameters, should throw an error at parse time CREATE FUNCTION namedparmcursor_test6 () RETURNS void AS $$ DECLARE c1 CURSOR (p1 int, p2 int) FOR SELECT * FROM tenk1 WHERE thousand = p1 AND tenthous = p2; BEGIN OPEN c1 (p2 := 77); END $$ LANGUAGE plpgsql; -- division by zero runtime error, the context given in the error message -- should be sensible CREATE FUNCTION namedparmcursor_test7 () RETURNS void AS $$ DECLARE c1 CURSOR (p1 int, p2 int) FOR SELECT * FROM tenk1 WHERE thousand = p1 AND tenthous = p2; BEGIN OPEN c1 (p2 := 77, p1 := 42 / 0); END $$ LANGUAGE plpgsql; SELECT namedparmcursor_test7 (); -- check that line comments work correctly within the argument list (there -- is some special handling of this case in the code: the newline after the -- comment must be preserved when the argument-evaluating query is -- constructed, otherwise the comment effectively comments out the next -- argument, too) CREATE FUNCTION namedparmcursor_test8 () RETURNS int4 AS $$ DECLARE c1 CURSOR (p1 int, p2 int) FOR SELECT count(*) FROM tenk1 WHERE thousand = p1 AND tenthous = p2; n int4; BEGIN OPEN c1 (77 -- test , 42); FETCH c1 INTO n; RETURN n; END $$ LANGUAGE plpgsql; SELECT namedparmcursor_test8 (); -- cursor parameter name can match plpgsql variable or unreserved keyword CREATE FUNCTION namedparmcursor_test9 (p1 int) RETURNS int4 AS $$ DECLARE c1 CURSOR (p1 int, p2 int, debug int) FOR SELECT count(*) FROM tenk1 WHERE thousand = p1 AND tenthous = p2 AND four = debug; p2 int4 := 1006; n int4; BEGIN OPEN c1 (p1 := p1, p2 := p2, debug := 2); FETCH c1 INTO n; RETURN n; END $$ LANGUAGE plpgsql; SELECT namedparmcursor_test9 (6); -- -- tests for "raise" processing -- CREATE FUNCTION raise_test1 (int) RETURNS int AS $$ BEGIN RAISE notice 'This message has too many parameters!', $1; RETURN $1; END; $$ LANGUAGE plpgsql; CREATE FUNCTION raise_test2 (int) RETURNS int AS $$ BEGIN RAISE notice 'This message has too few parameters: %, %, %', $1, $1; RETURN $1; END; $$ LANGUAGE plpgsql; CREATE FUNCTION raise_test3 (int) RETURNS int AS $$ BEGIN RAISE notice 'This message has no parameters (despite having %% signs in it)!'; RETURN $1; END; $$ LANGUAGE plpgsql; SELECT raise_test3 (1); -- Test re-RAISE inside a nested exception block. This case is allowed -- by Oracle's PL/SQL but was handled differently by PG before 9.1. CREATE FUNCTION reraise_test () RETURNS void AS $$ BEGIN BEGIN RAISE syntax_error; EXCEPTION WHEN syntax_error THEN BEGIN RAISE notice 'exception % thrown in inner block, reraising', sqlerrm; RAISE; EXCEPTION WHEN OTHERS THEN RAISE notice 'RIGHT - exception % caught in inner block', sqlerrm; END; END; EXCEPTION WHEN OTHERS THEN RAISE notice 'WRONG - exception % caught in outer block', sqlerrm; END; $$ LANGUAGE plpgsql; SELECT reraise_test (); -- -- reject function definitions that contain malformed SQL queries at -- compile-time, where possible -- CREATE FUNCTION bad_sql1 () RETURNS int AS $$ DECLARE a int; BEGIN a := 5; Johnny Yuma; a := 10; RETURN a; END $$ LANGUAGE plpgsql; CREATE FUNCTION bad_sql2 () RETURNS int AS $$ DECLARE r record; BEGIN FOR r IN SELECT I fought the law, the law won LOOP RAISE notice 'in loop'; END LOOP; RETURN 5; END; $$ LANGUAGE plpgsql; -- a RETURN expression is mandatory, except for void-returning -- functions, where it is not allowed CREATE FUNCTION missing_return_expr () RETURNS int AS $$ BEGIN RETURN; END; $$ LANGUAGE plpgsql; CREATE FUNCTION void_return_expr () RETURNS void AS $$ BEGIN RETURN 5; END; $$ LANGUAGE plpgsql; -- VOID functions are allowed to omit RETURN CREATE FUNCTION void_return_expr () RETURNS void AS $$ BEGIN PERFORM 2 + 2; END; $$ LANGUAGE plpgsql; SELECT void_return_expr (); -- but ordinary functions are not CREATE FUNCTION missing_return_expr () RETURNS int AS $$ BEGIN PERFORM 2 + 2; END; $$ LANGUAGE plpgsql; SELECT missing_return_expr (); DROP FUNCTION void_return_expr (); DROP FUNCTION missing_return_expr (); -- -- EXECUTE ... INTO test -- CREATE TABLE eifoo ( i integer, y integer ); CREATE TYPE eitype AS ( i integer, y integer ); CREATE OR REPLACE FUNCTION execute_into_test (varchar) RETURNS record AS $$ DECLARE _r record; _rt eifoo % rowtype; _v eitype; i int; j int; k int; BEGIN EXECUTE 'insert into ' || $1 || ' values(10,15)'; EXECUTE 'select (row).* from (select row(10,1)::eifoo) s' INTO _r; RAISE notice '% %', _r.i, _r.y; EXECUTE 'select * from ' || $1 || ' limit 1' INTO _rt; RAISE notice '% %', _rt.i, _rt.y; EXECUTE 'select *, 20 from ' || $1 || ' limit 1' INTO i, j, k; RAISE notice '% % %', i, j, k; EXECUTE 'select 1,2' INTO _v; RETURN _v; END; $$ LANGUAGE plpgsql; SELECT execute_into_test ('eifoo'); DROP TABLE eifoo CASCADE; DROP TYPE eitype CASCADE; -- -- SQLSTATE and SQLERRM test -- CREATE FUNCTION excpt_test1 () RETURNS void AS $$ BEGIN RAISE notice '% %', sqlstate, sqlerrm; END; $$ LANGUAGE plpgsql; -- should fail: SQLSTATE and SQLERRM are only in defined EXCEPTION -- blocks SELECT excpt_test1 (); CREATE FUNCTION excpt_test2 () RETURNS void AS $$ BEGIN BEGIN BEGIN RAISE notice '% %', sqlstate, sqlerrm; END; END; END; $$ LANGUAGE plpgsql; -- should fail SELECT excpt_test2 (); CREATE FUNCTION excpt_test3 () RETURNS void AS $$ BEGIN BEGIN RAISE exception 'user exception'; exception WHEN OTHERS THEN RAISE notice 'caught exception % %', sqlstate, sqlerrm; BEGIN RAISE notice '% %', sqlstate, sqlerrm; PERFORM 10 / 0; exception WHEN substring_error THEN -- this exception handler shouldn't be invoked RAISE notice 'unexpected exception: % %', sqlstate, sqlerrm; WHEN division_by_zero THEN RAISE notice 'caught exception % %', sqlstate, sqlerrm; END; RAISE notice '% %', sqlstate, sqlerrm; END; END; $$ LANGUAGE plpgsql; SELECT excpt_test3 (); CREATE FUNCTION excpt_test4 () RETURNS text AS $$ BEGIN BEGIN PERFORM 1 / 0; exception WHEN OTHERS THEN RETURN sqlerrm; END; END; $$ LANGUAGE plpgsql; SELECT excpt_test4 (); DROP FUNCTION excpt_test1 (); DROP FUNCTION excpt_test2 (); DROP FUNCTION excpt_test3 (); DROP FUNCTION excpt_test4 (); -- parameters of raise stmt can be expressions CREATE FUNCTION raise_exprs () RETURNS void AS $$ DECLARE a integer[] = '{10,20,30}'; c varchar = 'xyz'; i integer; BEGIN i := 2; RAISE notice '%; %; %; %; %; %', a, a[i], c, ( SELECT c || 'abc'), ROW (10, 'aaa', NULL, 30), NULL; END; $$ LANGUAGE plpgsql; SELECT raise_exprs (); DROP FUNCTION raise_exprs (); -- regression test: verify that multiple uses of same plpgsql datum within -- a SQL command all get mapped to the same $n parameter. The return value -- of the SELECT is not important, we only care that it doesn't fail with -- a complaint about an ungrouped column reference. CREATE FUNCTION multi_datum_use (p1 int) RETURNS bool AS $$ DECLARE x int; y int; BEGIN SELECT INTO x, y unique1 / p1, unique1 / $1 FROM tenk1 GROUP BY unique1 / p1; RETURN x = y; END $$ LANGUAGE plpgsql; SELECT multi_datum_use (42); -- -- Test STRICT limiter in both planned and EXECUTE invocations. -- Note that a data-modifying query is quasi strict (disallow multi rows) -- by default in the planned case, but not in EXECUTE. -- CREATE temp TABLE foo ( f1 int, f2 int ); INSERT INTO foo VALUES (1, 2), (3, 4); CREATE OR REPLACE FUNCTION stricttest () RETURNS void AS $$ DECLARE x record; BEGIN -- should work INSERT INTO foo VALUES (5, 6) RETURNING * INTO x; RAISE notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; END $$ LANGUAGE plpgsql; SELECT stricttest (); CREATE OR REPLACE FUNCTION stricttest () RETURNS void AS $$ DECLARE x record; BEGIN -- should fail due to implicit strict INSERT INTO foo VALUES (7, 8), (9, 10) RETURNING * INTO x; RAISE notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; END $$ LANGUAGE plpgsql; SELECT stricttest (); CREATE OR REPLACE FUNCTION stricttest () RETURNS void AS $$ DECLARE x record; BEGIN -- should work EXECUTE 'insert into foo values(5,6) returning *' INTO x; RAISE notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; END $$ LANGUAGE plpgsql; SELECT stricttest (); CREATE OR REPLACE FUNCTION stricttest () RETURNS void AS $$ DECLARE x record; BEGIN -- this should work since EXECUTE isn't as picky EXECUTE 'insert into foo values(7,8),(9,10) returning *' INTO x; RAISE notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; END $$ LANGUAGE plpgsql; SELECT stricttest (); SELECT * FROM foo; CREATE OR REPLACE FUNCTION stricttest () RETURNS void AS $$ DECLARE x record; BEGIN -- should work SELECT * FROM foo WHERE f1 = 3 INTO STRICT x; RAISE notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; END $$ LANGUAGE plpgsql; SELECT stricttest (); CREATE OR REPLACE FUNCTION stricttest () RETURNS void AS $$ DECLARE x record; BEGIN -- should fail, no rows SELECT * FROM foo WHERE f1 = 0 INTO STRICT x; RAISE notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; END $$ LANGUAGE plpgsql; SELECT stricttest (); CREATE OR REPLACE FUNCTION stricttest () RETURNS void AS $$ DECLARE x record; BEGIN -- should fail, too many rows SELECT * FROM foo WHERE f1 > 3 INTO STRICT x; RAISE notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; END $$ LANGUAGE plpgsql; SELECT stricttest (); CREATE OR REPLACE FUNCTION stricttest () RETURNS void AS $$ DECLARE x record; BEGIN -- should work EXECUTE 'select * from foo where f1 = 3' INTO STRICT x; RAISE notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; END $$ LANGUAGE plpgsql; SELECT stricttest (); CREATE OR REPLACE FUNCTION stricttest () RETURNS void AS $$ DECLARE x record; BEGIN -- should fail, no rows EXECUTE 'select * from foo where f1 = 0' INTO STRICT x; RAISE notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; END $$ LANGUAGE plpgsql; SELECT stricttest (); CREATE OR REPLACE FUNCTION stricttest () RETURNS void AS $$ DECLARE x record; BEGIN -- should fail, too many rows EXECUTE 'select * from foo where f1 > 3' INTO STRICT x; RAISE notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; END $$ LANGUAGE plpgsql; SELECT stricttest (); DROP FUNCTION stricttest (); -- test printing parameters after failure due to STRICT SET plpgsql.print_strict_params TO TRUE; CREATE OR REPLACE FUNCTION stricttest () RETURNS void AS $$ DECLARE x record; p1 int := 2; p3 text := 'foo'; BEGIN -- no rows SELECT * FROM foo WHERE f1 = p1 AND f1::text = p3 INTO STRICT x; RAISE notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; END $$ LANGUAGE plpgsql; SELECT stricttest (); CREATE OR REPLACE FUNCTION stricttest () RETURNS void AS $$ DECLARE x record; p1 int := 2; p3 text := 'foo'; BEGIN -- too many rows SELECT * FROM foo WHERE f1 > p1 OR f1::text = p3 INTO STRICT x; RAISE notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; END $$ LANGUAGE plpgsql; SELECT stricttest (); CREATE OR REPLACE FUNCTION stricttest () RETURNS void AS $$ DECLARE x record; BEGIN -- too many rows, no params SELECT * FROM foo WHERE f1 > 3 INTO STRICT x; RAISE notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; END $$ LANGUAGE plpgsql; SELECT stricttest (); CREATE OR REPLACE FUNCTION stricttest () RETURNS void AS $$ DECLARE x record; BEGIN -- no rows EXECUTE 'select * from foo where f1 = $1 or f1::text = $2' USING 0, 'foo' INTO STRICT x; RAISE notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; END $$ LANGUAGE plpgsql; SELECT stricttest (); CREATE OR REPLACE FUNCTION stricttest () RETURNS void AS $$ DECLARE x record; BEGIN -- too many rows EXECUTE 'select * from foo where f1 > $1' USING 1 INTO STRICT x; RAISE notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; END $$ LANGUAGE plpgsql; SELECT stricttest (); CREATE OR REPLACE FUNCTION stricttest () RETURNS void AS $$ DECLARE x record; BEGIN -- too many rows, no parameters EXECUTE 'select * from foo where f1 > 3' INTO STRICT x; RAISE notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; END $$ LANGUAGE plpgsql; SELECT stricttest (); CREATE OR REPLACE FUNCTION stricttest () RETURNS void AS $$ -- override the global # print_strict_params OFF DECLARE x record; p1 int := 2; p3 text := 'foo'; BEGIN -- too many rows SELECT * FROM foo WHERE f1 > p1 OR f1::text = p3 INTO STRICT x; RAISE notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; END $$ LANGUAGE plpgsql; SELECT stricttest (); RESET plpgsql.print_strict_params; CREATE OR REPLACE FUNCTION stricttest () RETURNS void AS $$ -- override the global # print_strict_params ON DECLARE x record; p1 int := 2; p3 text := 'foo'; BEGIN -- too many rows SELECT * FROM foo WHERE f1 > p1 OR f1::text = p3 INTO STRICT x; RAISE notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; END $$ LANGUAGE plpgsql; SELECT stricttest (); -- test warnings and errors SET plpgsql.extra_warnings TO 'all'; SET plpgsql.extra_warnings TO 'none'; SET plpgsql.extra_errors TO 'all'; SET plpgsql.extra_errors TO 'none'; -- test warnings when shadowing a variable SET plpgsql.extra_warnings TO 'shadowed_variables'; -- simple shadowing of input and output parameters CREATE OR REPLACE FUNCTION shadowtest (in1 int) RETURNS TABLE ( out1 int ) AS $$ DECLARE in1 int; out1 int; BEGIN END $$ LANGUAGE plpgsql; SELECT shadowtest (1); SET plpgsql.extra_warnings TO 'shadowed_variables'; SELECT shadowtest (1); CREATE OR REPLACE FUNCTION shadowtest (in1 int) RETURNS TABLE ( out1 int ) AS $$ DECLARE in1 int; out1 int; BEGIN END $$ LANGUAGE plpgsql; SELECT shadowtest (1); DROP FUNCTION shadowtest (int); -- shadowing in a second DECLARE block CREATE OR REPLACE FUNCTION shadowtest () RETURNS void AS $$ DECLARE f1 int; BEGIN DECLARE f1 int; BEGIN END; END $$ LANGUAGE plpgsql; DROP FUNCTION shadowtest (); -- several levels of shadowing CREATE OR REPLACE FUNCTION shadowtest (in1 int) RETURNS void AS $$ DECLARE in1 int; BEGIN DECLARE in1 int; BEGIN END; END $$ LANGUAGE plpgsql; DROP FUNCTION shadowtest (int); -- shadowing in cursor definitions CREATE OR REPLACE FUNCTION shadowtest () RETURNS void AS $$ DECLARE f1 int; c1 CURSOR (f1 int) FOR SELECT 1; BEGIN END $$ LANGUAGE plpgsql; DROP FUNCTION shadowtest (); -- test errors when shadowing a variable SET plpgsql.extra_errors TO 'shadowed_variables'; CREATE OR REPLACE FUNCTION shadowtest (f1 int) RETURNS boolean AS $$ DECLARE f1 int; BEGIN RETURN 1; END $$ LANGUAGE plpgsql; SELECT shadowtest (1); RESET plpgsql.extra_errors; RESET plpgsql.extra_warnings; CREATE OR REPLACE FUNCTION shadowtest (f1 int) RETURNS boolean AS $$ DECLARE f1 int; BEGIN RETURN 1; END $$ LANGUAGE plpgsql; SELECT shadowtest (1); -- runtime extra checks SET plpgsql.extra_warnings TO 'too_many_rows'; DO $$ DECLARE x int; BEGIN SELECT v FROM generate_series(1, 2) g (v) INTO x; END; $$; SET plpgsql.extra_errors TO 'too_many_rows'; DO $$ DECLARE x int; BEGIN SELECT v FROM generate_series(1, 2) g (v) INTO x; END; $$; RESET plpgsql.extra_errors; RESET plpgsql.extra_warnings; SET plpgsql.extra_warnings TO 'strict_multi_assignment'; DO $$ DECLARE x int; y int; BEGIN SELECT 1 INTO x, y; SELECT 1, 2 INTO x, y; SELECT 1, 2, 3 INTO x, y; END $$; SET plpgsql.extra_errors TO 'strict_multi_assignment'; DO $$ DECLARE x int; y int; BEGIN SELECT 1 INTO x, y; SELECT 1, 2 INTO x, y; SELECT 1, 2, 3 INTO x, y; END $$; CREATE TABLE test_01 ( a int, b int, c int ); ALTER TABLE test_01 DROP COLUMN a; -- the check is active only when source table is not empty INSERT INTO test_01 VALUES (10, 20); DO $$ DECLARE x int; y int; BEGIN SELECT * FROM test_01 INTO x, y; -- should be ok RAISE notice 'ok'; SELECT * FROM test_01 INTO x; -- should to fail END; $$; DO $$ DECLARE t test_01; BEGIN SELECT 1, 2 INTO t; -- should be ok RAISE notice 'ok'; SELECT 1, 2, 3 INTO t; -- should fail; END; $$; DO $$ DECLARE t test_01; BEGIN SELECT 1 INTO t; -- should fail; END; $$; DROP TABLE test_01; RESET plpgsql.extra_errors; RESET plpgsql.extra_warnings; -- test scrollable cursor support CREATE FUNCTION sc_test () RETURNS SETOF integer AS $$ DECLARE c SCROLL CURSOR FOR SELECT f1 FROM int4_tbl; x integer; BEGIN OPEN c; FETCH LAST FROM c INTO x; while found LOOP RETURN NEXT x; FETCH prior FROM c INTO x; END LOOP; CLOSE c; END; $$ LANGUAGE plpgsql; SELECT * FROM sc_test (); CREATE OR REPLACE FUNCTION sc_test () RETURNS SETOF integer AS $$ DECLARE c NO SCROLL CURSOR FOR SELECT f1 FROM int4_tbl; x integer; BEGIN OPEN c; FETCH LAST FROM c INTO x; while found LOOP RETURN NEXT x; FETCH prior FROM c INTO x; END LOOP; CLOSE c; END; $$ LANGUAGE plpgsql; SELECT * FROM sc_test (); -- fails because of NO SCROLL specification CREATE OR REPLACE FUNCTION sc_test () RETURNS SETOF integer AS $$ DECLARE c refcursor; x integer; BEGIN OPEN c SCROLL FOR SELECT f1 FROM int4_tbl; FETCH LAST FROM c INTO x; while found LOOP RETURN NEXT x; FETCH prior FROM c INTO x; END LOOP; CLOSE c; END; $$ LANGUAGE plpgsql; SELECT * FROM sc_test (); CREATE OR REPLACE FUNCTION sc_test () RETURNS SETOF integer AS $$ DECLARE c refcursor; x integer; BEGIN OPEN c SCROLL FOR EXECUTE 'select f1 from int4_tbl'; FETCH LAST FROM c INTO x; while found LOOP RETURN NEXT x; FETCH relative - 2 FROM c INTO x; END LOOP; CLOSE c; END; $$ LANGUAGE plpgsql; SELECT * FROM sc_test (); CREATE OR REPLACE FUNCTION sc_test () RETURNS SETOF integer AS $$ DECLARE c refcursor; x integer; BEGIN OPEN c SCROLL FOR EXECUTE 'select f1 from int4_tbl'; FETCH LAST FROM c INTO x; while found LOOP RETURN NEXT x; MOVE BACKWARD 2 FROM c; FETCH relative - 1 FROM c INTO x; END LOOP; CLOSE c; END; $$ LANGUAGE plpgsql; SELECT * FROM sc_test (); CREATE OR REPLACE FUNCTION sc_test () RETURNS SETOF integer AS $$ DECLARE c CURSOR FOR SELECT * FROM generate_series(1, 10); x integer; BEGIN OPEN c; LOOP MOVE relative 2 IN c; IF NOT found THEN exit; END IF; FETCH NEXT FROM c INTO x; IF found THEN RETURN NEXT x; END IF; END LOOP; CLOSE c; END; $$ LANGUAGE plpgsql; SELECT * FROM sc_test (); CREATE OR REPLACE FUNCTION sc_test () RETURNS SETOF integer AS $$ DECLARE c CURSOR FOR SELECT * FROM generate_series(1, 10); x integer; BEGIN OPEN c; MOVE FORWARD ALL IN c; FETCH BACKWARD FROM c INTO x; IF found THEN RETURN NEXT x; END IF; CLOSE c; END; $$ LANGUAGE plpgsql; SELECT * FROM sc_test (); DROP FUNCTION sc_test (); -- test qualified variable names CREATE FUNCTION pl_qual_names (param1 int) RETURNS void AS $$ << outerblock >> DECLARE param1 int := 1; BEGIN << innerblock >> DECLARE param1 int := 2; BEGIN RAISE notice 'param1 = %', param1; RAISE notice 'pl_qual_names.param1 = %', pl_qual_names.param1; RAISE notice 'outerblock.param1 = %', outerblock.param1; RAISE notice 'innerblock.param1 = %', innerblock.param1; END; END; $$ LANGUAGE plpgsql; SELECT pl_qual_names (42); DROP FUNCTION pl_qual_names (int); -- tests for RETURN QUERY CREATE FUNCTION ret_query1 (out int, out int) RETURNS SETOF record AS $$ BEGIN $1 := - 1; $2 := - 2; RETURN NEXT; RETURN query SELECT x + 1, x * 10 FROM generate_series(0, 10) s (x); RETURN NEXT; END; $$ LANGUAGE plpgsql; SELECT * FROM ret_query1 (); CREATE TYPE record_type AS ( x text, y int, z boolean ); CREATE OR REPLACE FUNCTION ret_query2 (lim int) RETURNS SETOF record_type AS $$ BEGIN RETURN query SELECT md5(s.x::text), s.x, s.x > 0 FROM generate_series(- 8, lim) s (x) WHERE s.x % 2 = 0; END; $$ LANGUAGE plpgsql; SELECT * FROM ret_query2 (8); -- test EXECUTE USING CREATE FUNCTION exc_using (int, text) RETURNS int AS $$ DECLARE i int; BEGIN FOR i IN EXECUTE 'select * from generate_series(1,$1)' USING $1 + 1 LOOP RAISE notice '%', i; END LOOP; EXECUTE 'select $2 + $2*3 + length($1)' INTO i USING $2, $1; RETURN i; END $$ LANGUAGE plpgsql; SELECT exc_using (5, 'foobar'); DROP FUNCTION exc_using (int, text); CREATE OR REPLACE FUNCTION exc_using (int) RETURNS void AS $$ DECLARE c refcursor; i int; BEGIN OPEN c FOR EXECUTE 'select * from generate_series(1,$1)' USING $1 + 1; LOOP FETCH c INTO i; exit WHEN NOT found; RAISE notice '%', i; END LOOP; CLOSE c; RETURN; END; $$ LANGUAGE plpgsql; SELECT exc_using (5); DROP FUNCTION exc_using (int); -- test FOR-over-cursor CREATE OR REPLACE FUNCTION forc01 () RETURNS void AS $$ DECLARE c CURSOR (r1 integer, r2 integer) FOR SELECT * FROM generate_series(r1, r2) i; c2 CURSOR FOR SELECT * FROM generate_series(41, 43) i; BEGIN FOR r IN c (5, 7) LOOP RAISE notice '% from %', r.i, c; END LOOP; -- again, to test if cursor was closed properly FOR r IN c (9, 10) LOOP RAISE notice '% from %', r.i, c; END LOOP; -- and test a parameterless cursor FOR r IN c2 LOOP RAISE notice '% from %', r.i, c2; END LOOP; -- and try it with a hand-assigned name RAISE notice 'after loop, c2 = %', c2; c2 := 'special_name'; FOR r IN c2 LOOP RAISE notice '% from %', r.i, c2; END LOOP; RAISE notice 'after loop, c2 = %', c2; -- and try it with a generated name -- (which we can't show in the output because it's variable) c2 := NULL; FOR r IN c2 LOOP RAISE notice '%', r.i; END LOOP; RAISE notice 'after loop, c2 = %', c2; RETURN; END; $$ LANGUAGE plpgsql; SELECT forc01 (); -- try updating the cursor's current row CREATE temp TABLE forc_test AS SELECT n AS i, n AS j FROM generate_series(1, 10) n; CREATE OR REPLACE FUNCTION forc01 () RETURNS void AS $$ DECLARE c CURSOR FOR SELECT * FROM forc_test; BEGIN FOR r IN c LOOP RAISE notice '%, %', r.i, r.j; UPDATE forc_test SET i = i * 100, j = r.j * 2 WHERE CURRENT OF c; END LOOP; END; $$ LANGUAGE plpgsql; SELECT forc01 (); SELECT * FROM forc_test; -- same, with a cursor whose portal name doesn't match variable name CREATE OR REPLACE FUNCTION forc01 () RETURNS void AS $$ DECLARE c refcursor := 'fooled_ya'; r record; BEGIN OPEN c FOR SELECT * FROM forc_test; LOOP FETCH c INTO r; exit WHEN NOT found; RAISE notice '%, %', r.i, r.j; UPDATE forc_test SET i = i * 100, j = r.j * 2 WHERE CURRENT OF c; END LOOP; END; $$ LANGUAGE plpgsql; SELECT forc01 (); SELECT * FROM forc_test; DROP FUNCTION forc01 (); -- fail because cursor has no query bound to it CREATE OR REPLACE FUNCTION forc_bad () RETURNS void AS $$ DECLARE c refcursor; BEGIN FOR r IN c LOOP RAISE notice '%', r.i; END LOOP; END; $$ LANGUAGE plpgsql; -- test RETURN QUERY EXECUTE CREATE OR REPLACE FUNCTION return_dquery () RETURNS SETOF int AS $$ BEGIN RETURN query EXECUTE 'select * from (values(10),(20)) f'; RETURN query EXECUTE 'select * from (values($1),($2)) f' USING 40, 50; END; $$ LANGUAGE plpgsql; SELECT * FROM return_dquery (); DROP FUNCTION return_dquery (); -- test RETURN QUERY with dropped columns CREATE TABLE tabwithcols ( a int, b int, c int, d int ); INSERT INTO tabwithcols VALUES (10, 20, 30, 40), (50, 60, 70, 80); CREATE OR REPLACE FUNCTION returnqueryf () RETURNS SETOF tabwithcols AS $$ BEGIN RETURN query SELECT * FROM tabwithcols; RETURN query EXECUTE 'select * from tabwithcols'; END; $$ LANGUAGE plpgsql; SELECT * FROM returnqueryf (); ALTER TABLE tabwithcols DROP COLUMN b; SELECT * FROM returnqueryf (); ALTER TABLE tabwithcols DROP COLUMN d; SELECT * FROM returnqueryf (); ALTER TABLE tabwithcols ADD COLUMN d int; SELECT * FROM returnqueryf (); DROP FUNCTION returnqueryf (); DROP TABLE tabwithcols; -- -- Tests for composite-type results -- CREATE TYPE compostype AS ( x int, y varchar ); -- test: use of variable of composite type in return statement CREATE OR REPLACE FUNCTION compos () RETURNS compostype AS $$ DECLARE v compostype; BEGIN v := (1, 'hello'); RETURN v; END; $$ LANGUAGE plpgsql; SELECT compos (); -- test: use of variable of record type in return statement CREATE OR REPLACE FUNCTION compos () RETURNS compostype AS $$ DECLARE v record; BEGIN v := (1, 'hello'::varchar); RETURN v; END; $$ LANGUAGE plpgsql; SELECT compos (); -- test: use of row expr in return statement CREATE OR REPLACE FUNCTION compos () RETURNS compostype AS $$ BEGIN RETURN (1, 'hello'::varchar); END; $$ LANGUAGE plpgsql; SELECT compos (); -- this does not work currently (no implicit casting) CREATE OR REPLACE FUNCTION compos () RETURNS compostype AS $$ BEGIN RETURN (1, 'hello'); END; $$ LANGUAGE plpgsql; SELECT compos (); -- ... but this does CREATE OR REPLACE FUNCTION compos () RETURNS compostype AS $$ BEGIN RETURN (1, 'hello')::compostype; END; $$ LANGUAGE plpgsql; SELECT compos (); DROP FUNCTION compos (); -- test: return a row expr as record. CREATE OR REPLACE FUNCTION composrec () RETURNS record AS $$ DECLARE v record; BEGIN v := (1, 'hello'); RETURN v; END; $$ LANGUAGE plpgsql; SELECT composrec (); -- test: return row expr in return statement. CREATE OR REPLACE FUNCTION composrec () RETURNS record AS $$ BEGIN RETURN (1, 'hello'); END; $$ LANGUAGE plpgsql; SELECT composrec (); DROP FUNCTION composrec (); -- test: row expr in RETURN NEXT statement. CREATE OR REPLACE FUNCTION compos () RETURNS SETOF compostype AS $$ BEGIN FOR i IN 1..3 LOOP RETURN NEXT (1, 'hello'::varchar); END LOOP; RETURN NEXT NULL::compostype; RETURN NEXT (2, 'goodbye')::compostype; END; $$ LANGUAGE plpgsql; SELECT * FROM compos (); DROP FUNCTION compos (); -- test: use invalid expr in return statement. CREATE OR REPLACE FUNCTION compos () RETURNS compostype AS $$ BEGIN RETURN 1 + 1; END; $$ LANGUAGE plpgsql; SELECT compos (); -- RETURN variable is a different code path ... CREATE OR REPLACE FUNCTION compos () RETURNS compostype AS $$ DECLARE x int := 42; BEGIN RETURN x; END; $$ LANGUAGE plpgsql; SELECT * FROM compos (); DROP FUNCTION compos (); -- test: invalid use of composite variable in scalar-returning function CREATE OR REPLACE FUNCTION compos () RETURNS int AS $$ DECLARE v compostype; BEGIN v := (1, 'hello'); RETURN v; END; $$ LANGUAGE plpgsql; SELECT compos (); -- test: invalid use of composite expression in scalar-returning function CREATE OR REPLACE FUNCTION compos () RETURNS int AS $$ BEGIN RETURN (1, 'hello')::compostype; END; $$ LANGUAGE plpgsql; SELECT compos (); DROP FUNCTION compos (); DROP TYPE compostype; -- -- Tests for 8.4's new RAISE features -- CREATE OR REPLACE FUNCTION raise_test () RETURNS void AS $$ BEGIN RAISE notice '% % %', 1, 2, 3 USING errcode = '55001', detail = 'some detail info', hint = 'some hint'; RAISE '% % %', 1, 2, 3 USING errcode = 'division_by_zero', detail = 'some detail info'; END; $$ LANGUAGE plpgsql; SELECT raise_test (); -- Since we can't actually see the thrown SQLSTATE in default psql output, -- test it like this; this also tests re-RAISE CREATE OR REPLACE FUNCTION raise_test () RETURNS void AS $$ BEGIN RAISE 'check me' USING errcode = 'division_by_zero', detail = 'some detail info'; exception WHEN OTHERS THEN RAISE notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm; RAISE; END; $$ LANGUAGE plpgsql; SELECT raise_test (); CREATE OR REPLACE FUNCTION raise_test () RETURNS void AS $$ BEGIN RAISE 'check me' USING errcode = '1234F', detail = 'some detail info'; exception WHEN OTHERS THEN RAISE notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm; RAISE; END; $$ LANGUAGE plpgsql; SELECT raise_test (); -- SQLSTATE specification in WHEN CREATE OR REPLACE FUNCTION raise_test () RETURNS void AS $$ BEGIN RAISE 'check me' USING errcode = '1234F', detail = 'some detail info'; exception WHEN sqlstate '1234F' THEN RAISE notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm; RAISE; END; $$ LANGUAGE plpgsql; SELECT raise_test (); CREATE OR REPLACE FUNCTION raise_test () RETURNS void AS $$ BEGIN RAISE division_by_zero USING detail = 'some detail info'; exception WHEN OTHERS THEN RAISE notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm; RAISE; END; $$ LANGUAGE plpgsql; SELECT raise_test (); CREATE OR REPLACE FUNCTION raise_test () RETURNS void AS $$ BEGIN RAISE division_by_zero; END; $$ LANGUAGE plpgsql; SELECT raise_test (); CREATE OR REPLACE FUNCTION raise_test () RETURNS void AS $$ BEGIN RAISE sqlstate '1234F'; END; $$ LANGUAGE plpgsql; SELECT raise_test (); CREATE OR REPLACE FUNCTION raise_test () RETURNS void AS $$ BEGIN RAISE division_by_zero USING message = 'custom' || ' message'; END; $$ LANGUAGE plpgsql; SELECT raise_test (); CREATE OR REPLACE FUNCTION raise_test () RETURNS void AS $$ BEGIN RAISE USING message = 'custom' || ' message', errcode = '22012'; END; $$ LANGUAGE plpgsql; SELECT raise_test (); -- conflict on message CREATE OR REPLACE FUNCTION raise_test () RETURNS void AS $$ BEGIN RAISE notice 'some message' USING message = 'custom' || ' message', errcode = '22012'; END; $$ LANGUAGE plpgsql; SELECT raise_test (); -- conflict on errcode CREATE OR REPLACE FUNCTION raise_test () RETURNS void AS $$ BEGIN RAISE division_by_zero USING message = 'custom' || ' message', errcode = '22012'; END; $$ LANGUAGE plpgsql; SELECT raise_test (); -- nothing to re-RAISE CREATE OR REPLACE FUNCTION raise_test () RETURNS void AS $$ BEGIN RAISE; END; $$ LANGUAGE plpgsql; SELECT raise_test (); -- test access to exception data CREATE FUNCTION zero_divide () RETURNS int AS $$ DECLARE v int := 0; BEGIN RETURN 10 / v; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION raise_test () RETURNS void AS $$ BEGIN RAISE exception 'custom exception' USING detail = 'some detail of custom exception', hint = 'some hint related to custom exception'; END; $$ LANGUAGE plpgsql; CREATE FUNCTION stacked_diagnostics_test () RETURNS void AS $$ DECLARE _sqlstate text; _message text; _context text; BEGIN PERFORM zero_divide (); exception WHEN OTHERS THEN get stacked diagnostics _sqlstate = returned_sqlstate, _message = message_text, _context = pg_exception_context; RAISE notice 'sqlstate: %, message: %, context: [%]', _sqlstate, _message, replace(_context, E'\n', ' <- '); END; $$ LANGUAGE plpgsql; SELECT stacked_diagnostics_test (); CREATE OR REPLACE FUNCTION stacked_diagnostics_test () RETURNS void AS $$ DECLARE _detail text; _hint text; _message text; BEGIN PERFORM raise_test (); exception WHEN OTHERS THEN get stacked diagnostics _message = message_text, _detail = pg_exception_detail, _hint = pg_exception_hint; RAISE notice 'message: %, detail: %, hint: %', _message, _detail, _hint; END; $$ LANGUAGE plpgsql; SELECT stacked_diagnostics_test (); -- fail, cannot use stacked diagnostics statement outside handler CREATE OR REPLACE FUNCTION stacked_diagnostics_test () RETURNS void AS $$ DECLARE _detail text; _hint text; _message text; BEGIN get stacked diagnostics _message = message_text, _detail = pg_exception_detail, _hint = pg_exception_hint; RAISE notice 'message: %, detail: %, hint: %', _message, _detail, _hint; END; $$ LANGUAGE plpgsql; SELECT stacked_diagnostics_test (); DROP FUNCTION zero_divide (); DROP FUNCTION stacked_diagnostics_test (); -- check cases where implicit SQLSTATE variable could be confused with -- SQLSTATE as a keyword, cf bug #5524 CREATE OR REPLACE FUNCTION raise_test () RETURNS void AS $$ BEGIN PERFORM 1 / 0; exception WHEN sqlstate '22012' THEN RAISE notice USING message = sqlstate; RAISE sqlstate '22012' USING message = 'substitute message'; END; $$ LANGUAGE plpgsql; SELECT raise_test (); DROP FUNCTION raise_test (); -- test passing column_name, constraint_name, datatype_name, table_name -- and schema_name error fields CREATE OR REPLACE FUNCTION stacked_diagnostics_test () RETURNS void AS $$ DECLARE _column_name text; _constraint_name text; _datatype_name text; _table_name text; _schema_name text; BEGIN RAISE exception USING COLUMN = '>>some column name<<', CONSTRAINT = '>>some constraint name<<', datatype = '>>some datatype name<<', TABLE = '>>some table name<<', SCHEMA = '>>some schema name<<'; exception WHEN OTHERS THEN get stacked diagnostics _column_name = column_name, _constraint_name = constraint_name, _datatype_name = pg_datatype_name, _table_name = table_name, _schema_name = schema_name; RAISE notice 'column %, constraint %, type %, table %, schema %', _column_name, _constraint_name, _datatype_name, _table_name, _schema_name; END; $$ LANGUAGE plpgsql; SELECT stacked_diagnostics_test (); DROP FUNCTION stacked_diagnostics_test (); -- test variadic functions CREATE OR REPLACE FUNCTION vari (VARIADIC int[]) RETURNS void AS $$ BEGIN FOR i IN array_lower($1, 1)..array_upper($1, 1) LOOP RAISE notice '%', $1[i]; END LOOP; END; $$ LANGUAGE plpgsql; SELECT vari (1, 2, 3, 4, 5); SELECT vari (3, 4, 5); SELECT vari (VARIADIC ARRAY[5, 6, 7]); DROP FUNCTION vari (int[]); -- coercion test CREATE OR REPLACE FUNCTION pleast (VARIADIC numeric[]) RETURNS numeric AS $$ DECLARE aux numeric = $1[array_lower($1, 1)]; BEGIN FOR i IN array_lower($1, 1) + 1..array_upper($1, 1) LOOP IF $1[i] < aux THEN aux := $1[i]; END IF; END LOOP; RETURN aux; END; $$ LANGUAGE plpgsql IMMUTABLE STRICT; SELECT pleast (10, 1, 2, 3, - 16); SELECT pleast (10.2, 2.2, - 1.1); SELECT pleast (10.2, 10, - 20); SELECT pleast (10, 20, - 1.0); -- in case of conflict, non-variadic version is preferred CREATE OR REPLACE FUNCTION pleast (numeric) RETURNS numeric AS $$ BEGIN RAISE notice 'non-variadic function called'; RETURN $1; END; $$ LANGUAGE plpgsql IMMUTABLE STRICT; SELECT pleast (10); DROP FUNCTION pleast (numeric[]); DROP FUNCTION pleast (numeric); -- test table functions CREATE FUNCTION tftest (int) RETURNS TABLE ( a int, b int ) AS $$ BEGIN RETURN query SELECT $1, $1 + i FROM generate_series(1, 5) g (i); END; $$ LANGUAGE plpgsql IMMUTABLE STRICT; SELECT * FROM tftest (10); CREATE OR REPLACE FUNCTION tftest (a1 int) RETURNS TABLE ( a int, b int ) AS $$ BEGIN a := a1; b := a1 + 1; RETURN NEXT; a := a1 * 10; b := a1 * 10 + 1; RETURN NEXT; END; $$ LANGUAGE plpgsql IMMUTABLE STRICT; SELECT * FROM tftest (10); DROP FUNCTION tftest (int); CREATE OR REPLACE FUNCTION rttest () RETURNS SETOF int AS $$ DECLARE rc int; rca int[]; BEGIN RETURN query VALUES (10), (20); get diagnostics rc = row_count; RAISE notice '% %', found, rc; RETURN query SELECT * FROM ( VALUES (10), (20)) f (a) WHERE FALSE; get diagnostics rc = row_count; RAISE notice '% %', found, rc; RETURN query EXECUTE 'values(10),(20)'; -- just for fun, let's use array elements as targets get diagnostics rca[1] = row_count; RAISE notice '% %', found, rca[1]; RETURN query EXECUTE 'select * from (values(10),(20)) f(a) where false'; get diagnostics rca[2] = row_count; RAISE notice '% %', found, rca[2]; END; $$ LANGUAGE plpgsql; SELECT * FROM rttest (); DROP FUNCTION rttest (); -- Test for proper cleanup at subtransaction exit. This example -- exposed a bug in PG 8.2. CREATE FUNCTION leaker_1 (fail bool) RETURNS INTEGER AS $$ DECLARE v_var integer; BEGIN BEGIN v_var := (leaker_2 (fail)).error_code; EXCEPTION WHEN OTHERS THEN RETURN 0; END; RETURN 1; END; $$ LANGUAGE plpgsql; CREATE FUNCTION leaker_2 (fail bool, OUT error_code integer, OUT new_id integer) RETURNS RECORD AS $$ BEGIN IF fail THEN RAISE EXCEPTION 'fail ...'; END IF; error_code := 1; new_id := 1; RETURN; END; $$ LANGUAGE plpgsql; SELECT * FROM leaker_1 (FALSE); SELECT * FROM leaker_1 (TRUE); DROP FUNCTION leaker_1 (bool); DROP FUNCTION leaker_2 (bool); -- Test for appropriate cleanup of non-simple expression evaluations -- (bug in all versions prior to August 2010) CREATE FUNCTION nonsimple_expr_test () RETURNS text[] AS $$ DECLARE arr text[]; lr text; i integer; BEGIN arr := ARRAY[ARRAY['foo', 'bar'], ARRAY['baz', 'quux']]; lr := 'fool'; i := 1; -- use sub-SELECTs to make expressions non-simple arr[( SELECT i)][( SELECT i + 1)] := ( SELECT lr); RETURN arr; END; $$ LANGUAGE plpgsql; SELECT nonsimple_expr_test (); DROP FUNCTION nonsimple_expr_test (); CREATE FUNCTION nonsimple_expr_test () RETURNS integer AS $$ DECLARE i integer NOT NULL := 0; BEGIN BEGIN i := ( SELECT NULL::integer); -- should throw error exception WHEN OTHERS THEN i := ( SELECT 1::integer); END; RETURN i; END; $$ LANGUAGE plpgsql; SELECT nonsimple_expr_test (); DROP FUNCTION nonsimple_expr_test (); -- -- Test cases involving recursion and error recovery in simple expressions -- (bugs in all versions before October 2010). The problems are most -- easily exposed by mutual recursion between plpgsql and sql functions. -- CREATE FUNCTION recurse (float8) RETURNS float8 AS $$ BEGIN IF ($1 > 0) THEN RETURN sql_recurse ($1 - 1); ELSE RETURN $1; END IF; END; $$ LANGUAGE plpgsql; -- "limit" is to prevent this from being inlined CREATE FUNCTION sql_recurse (float8) RETURNS float8 AS $$ SELECT recurse ($1) LIMIT 1; $$ LANGUAGE sql; SELECT recurse (10); CREATE FUNCTION error1 (text) RETURNS text LANGUAGE sql AS $$ SELECT relname::text FROM pg_class c WHERE c.oid = $1::regclass $$; CREATE FUNCTION error2 (p_name_table text) RETURNS text LANGUAGE plpgsql AS $$ BEGIN RETURN error1 (p_name_table); END $$; BEGIN; CREATE TABLE public.stuffs ( stuff text ); SAVEPOINT a; SELECT error2 ('nonexistent.stuffs'); ROLLBACK TO a; SELECT error2 ('public.stuffs'); ROLLBACK; DROP FUNCTION error2 (p_name_table text); DROP FUNCTION error1 (text); -- Test for proper handling of cast-expression caching CREATE FUNCTION sql_to_date (integer) RETURNS date AS $$ SELECT $1::text::date $$ LANGUAGE sql IMMUTABLE STRICT; CREATE cast( integer AS date ) WITH FUNCTION sql_to_date (integer ) AS assignment ; CREATE FUNCTION cast_invoker (integer) RETURNS date AS $$ BEGIN RETURN $1; END $$ LANGUAGE plpgsql; SELECT cast_invoker (20150717); SELECT cast_invoker (20150718); -- second call crashed in pre-release 9.5 BEGIN; SELECT cast_invoker (20150717); SELECT cast_invoker (20150718); SAVEPOINT s1; SELECT cast_invoker (20150718); SELECT cast_invoker (- 1); -- fails ROLLBACK TO SAVEPOINT s1; SELECT cast_invoker (20150719); SELECT cast_invoker (20150720); COMMIT; DROP FUNCTION cast_invoker (integer); DROP FUNCTION sql_to_date (integer) CASCADE; -- Test handling of cast cache inside DO blocks -- (to check the original crash case, this must be a cast not previously -- used in this session) BEGIN; DO $$ DECLARE x text[]; BEGIN x := '{1.23, 4.56}'::numeric[]; END $$; DO $$ DECLARE x text[]; BEGIN x := '{1.23, 4.56}'::numeric[]; END $$; END; -- Test for consistent reporting of error context CREATE FUNCTION fail ( ) RETURNS int LANGUAGE plpgsql AS $$ BEGIN RETURN 1 / 0; END $$; SELECT fail (); SELECT fail (); DROP FUNCTION fail (); -- Test handling of string literals. SET standard_conforming_strings = OFF; CREATE OR REPLACE FUNCTION strtest ( ) RETURNS text AS $$ BEGIN RAISE notice 'foo\\bar\041baz'; RETURN 'foo\\bar\041baz'; END $$ LANGUAGE plpgsql; SELECT strtest (); CREATE OR REPLACE FUNCTION strtest ( ) RETURNS text AS $$ BEGIN RAISE notice E'foo\\bar\041baz'; RETURN E'foo\\bar\041baz'; END $$ LANGUAGE plpgsql; SELECT strtest (); SET standard_conforming_strings = ON; CREATE OR REPLACE FUNCTION strtest ( ) RETURNS text AS $$ BEGIN RAISE notice 'foo\\bar\041baz\'; return ' foo bar '041baz\'; END $$ LANGUAGE plpgsql; SELECT strtest (); CREATE OR REPLACE FUNCTION strtest ( ) RETURNS text AS $$ BEGIN RAISE notice E'foo\\bar\041baz'; RETURN E'foo\\bar\041baz'; END $$ LANGUAGE plpgsql; SELECT strtest (); DROP FUNCTION strtest (); -- Test anonymous code blocks. DO $$ DECLARE r record; BEGIN FOR r IN SELECT rtrim(roomno) AS roomno, comment FROM Room ORDER BY roomno LOOP RAISE NOTICE '%, %', r.roomno, r.comment; END LOOP; END $$; -- these are to check syntax error reporting DO LANGUAGE plpgsql $$ BEGIN RETURN 1; END $$; DO $$ DECLARE r record; BEGIN FOR r IN SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomno LOOP RAISE NOTICE '%, %', r.roomno, r.comment; END LOOP; END $$; -- Check handling of errors thrown from/into anonymous code blocks. DO $outer$ BEGIN FOR i IN 1..10 LOOP BEGIN EXECUTE $ex$ do $$ declare x int = 0; begin x := 1 / x; end; $$; $ex$; exception WHEN division_by_zero THEN RAISE notice 'caught division by zero'; END; END LOOP; END; $outer$; -- Check variable scoping -- a var is not available in its own or prior -- default expressions. CREATE FUNCTION scope_test ( ) RETURNS int AS $$ DECLARE x int := 42; BEGIN DECLARE y int := x + 1; x int := x + 2; BEGIN RETURN x * 100 + y; END; END; $$ LANGUAGE plpgsql; SELECT scope_test (); DROP FUNCTION scope_test (); -- Check handling of conflicts between plpgsql vars and table columns. SET plpgsql.variable_conflict = error; CREATE FUNCTION conflict_test ( ) RETURNS SETOF int8_tbl AS $$ DECLARE r record; q1 bigint := 42; BEGIN FOR r IN SELECT q1, q2 FROM int8_tbl LOOP RETURN NEXT r; END LOOP; END; $$ LANGUAGE plpgsql; SELECT * FROM conflict_test (); CREATE OR REPLACE FUNCTION conflict_test ( ) RETURNS SETOF int8_tbl AS $$ # variable_conflict use_variable DECLARE r record; q1 bigint := 42; BEGIN FOR r IN SELECT q1, q2 FROM int8_tbl LOOP RETURN NEXT r; END LOOP; END; $$ LANGUAGE plpgsql; SELECT * FROM conflict_test (); CREATE OR REPLACE FUNCTION conflict_test ( ) RETURNS SETOF int8_tbl AS $$ # variable_conflict use_column DECLARE r record; q1 bigint := 42; BEGIN FOR r IN SELECT q1, q2 FROM int8_tbl LOOP RETURN NEXT r; END LOOP; END; $$ LANGUAGE plpgsql; SELECT * FROM conflict_test (); DROP FUNCTION conflict_test (); -- Check that an unreserved keyword can be used as a variable name CREATE FUNCTION unreserved_test ( ) RETURNS int AS $$ DECLARE FORWARD int := 21; BEGIN FORWARD := FORWARD * 2; RETURN FORWARD; END $$ LANGUAGE plpgsql; SELECT unreserved_test (); CREATE OR REPLACE FUNCTION unreserved_test ( ) RETURNS int AS $$ DECLARE RETURN int := 42; BEGIN RETURN := RETURN + 1; RETURN RETURN; END $$ LANGUAGE plpgsql; SELECT unreserved_test (); CREATE OR REPLACE FUNCTION unreserved_test ( ) RETURNS int AS $$ DECLARE comment int := 21; BEGIN comment := comment * 2; COMMENT ON FUNCTION unreserved_test () IS 'this is a test'; RETURN comment; END $$ LANGUAGE plpgsql; SELECT unreserved_test (); SELECT obj_description('unreserved_test()'::regprocedure, 'pg_proc'); DROP FUNCTION unreserved_test (); -- -- Test FOREACH over arrays -- CREATE FUNCTION foreach_test (anyarray ) RETURNS void AS $$ DECLARE x int; BEGIN foreach x IN ARRAY $1 LOOP RAISE notice '%', x; END LOOP; END; $$ LANGUAGE plpgsql; SELECT foreach_test (ARRAY[1, 2, 3, 4]); SELECT foreach_test (ARRAY[[1, 2],[3, 4]]); CREATE OR REPLACE FUNCTION foreach_test (anyarray ) RETURNS void AS $$ DECLARE x int; BEGIN foreach x slice 1 IN ARRAY $1 LOOP RAISE notice '%', x; END LOOP; END; $$ LANGUAGE plpgsql; -- should fail SELECT foreach_test (ARRAY[1, 2, 3, 4]); SELECT foreach_test (ARRAY[[1, 2],[3, 4]]); CREATE OR REPLACE FUNCTION foreach_test (anyarray ) RETURNS void AS $$ DECLARE x int[]; BEGIN foreach x slice 1 IN ARRAY $1 LOOP RAISE notice '%', x; END LOOP; END; $$ LANGUAGE plpgsql; SELECT foreach_test (ARRAY[1, 2, 3, 4]); SELECT foreach_test (ARRAY[[1, 2],[3, 4]]); -- higher level of slicing CREATE OR REPLACE FUNCTION foreach_test (anyarray ) RETURNS void AS $$ DECLARE x int[]; BEGIN foreach x slice 2 IN ARRAY $1 LOOP RAISE notice '%', x; END LOOP; END; $$ LANGUAGE plpgsql; -- should fail SELECT foreach_test (ARRAY[1, 2, 3, 4]); -- ok SELECT foreach_test (ARRAY[[1, 2],[3, 4]]); SELECT foreach_test (ARRAY[[[1, 2]],[[3, 4]]]); CREATE TYPE xy_tuple AS ( x int, y int ); -- iteration over array of records CREATE OR REPLACE FUNCTION foreach_test (anyarray ) RETURNS void AS $$ DECLARE r record; BEGIN foreach r IN ARRAY $1 LOOP RAISE notice '%', r; END LOOP; END; $$ LANGUAGE plpgsql; SELECT foreach_test (ARRAY[(10, 20), (40, 69), (35, 78)]::xy_tuple[]); SELECT foreach_test (ARRAY[[(10, 20), (40, 69)],[(35, 78), (88, 76)]]::xy_tuple[]); CREATE OR REPLACE FUNCTION foreach_test (anyarray ) RETURNS void AS $$ DECLARE x int; y int; BEGIN foreach x, y IN ARRAY $1 LOOP RAISE notice 'x = %, y = %', x, y; END LOOP; END; $$ LANGUAGE plpgsql; SELECT foreach_test (ARRAY[(10, 20), (40, 69), (35, 78)]::xy_tuple[]); SELECT foreach_test (ARRAY[[(10, 20), (40, 69)],[(35, 78), (88, 76)]]::xy_tuple[]); -- slicing over array of composite types CREATE OR REPLACE FUNCTION foreach_test (anyarray ) RETURNS void AS $$ DECLARE x xy_tuple[]; BEGIN foreach x slice 1 IN ARRAY $1 LOOP RAISE notice '%', x; END LOOP; END; $$ LANGUAGE plpgsql; SELECT foreach_test (ARRAY[(10, 20), (40, 69), (35, 78)]::xy_tuple[]); SELECT foreach_test (ARRAY[[(10, 20), (40, 69)],[(35, 78), (88, 76)]]::xy_tuple[]); DROP FUNCTION foreach_test (anyarray); DROP TYPE xy_tuple; -- -- Assorted tests for array subscript assignment -- CREATE temp TABLE rtype ( id int, ar text[] ); CREATE FUNCTION arrayassign1 ( ) RETURNS text[] LANGUAGE plpgsql AS $$ DECLARE r record; BEGIN r := ROW (12, '{foo,bar,baz}')::rtype; r.ar[2] := 'replace'; RETURN r.ar; END $$; SELECT arrayassign1 (); SELECT arrayassign1 (); -- try again to exercise internal caching CREATE DOMAIN orderedarray AS int[2] CONSTRAINT sorted CHECK (value[1] < value[2]); SELECT '{1,2}'::orderedarray; SELECT '{2,1}'::orderedarray; -- fail CREATE FUNCTION testoa (x1 int, x2 int, x3 int ) RETURNS orderedarray LANGUAGE plpgsql AS $$ DECLARE res orderedarray; BEGIN res := ARRAY[x1, x2]; res[2] := x3; RETURN res; END $$; SELECT testoa (1, 2, 3); SELECT testoa (1, 2, 3); -- try again to exercise internal caching SELECT testoa (2, 1, 3); -- fail at initial assign SELECT testoa (1, 2, 1); -- fail at update DROP FUNCTION arrayassign1 (); DROP FUNCTION testoa (x1 int, x2 int, x3 int); -- -- Test handling of expanded arrays -- CREATE FUNCTION returns_rw_array (int ) RETURNS int[] LANGUAGE plpgsql AS $$ DECLARE r int[]; BEGIN r := ARRAY[$1, $1]; RETURN r; END; $$ STABLE; CREATE FUNCTION consumes_rw_array (int[] ) RETURNS int LANGUAGE plpgsql AS $$ BEGIN RETURN $1[1]; END; $$ STABLE; SELECT consumes_rw_array (returns_rw_array (42)); -- bug #14174 EXPLAIN ( VERBOSE, COSTS OFF ) SELECT i, a FROM ( SELECT returns_rw_array (1) AS a offset 0) ss, LATERAL consumes_rw_array (a) i; SELECT i, a FROM ( SELECT returns_rw_array (1) AS a offset 0) ss, LATERAL consumes_rw_array (a) i; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT consumes_rw_array (a), a FROM returns_rw_array (1) a; SELECT consumes_rw_array (a), a FROM returns_rw_array (1) a; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT consumes_rw_array (a), a FROM ( VALUES (returns_rw_array (1)), (returns_rw_array (2))) v (a); SELECT consumes_rw_array (a), a FROM ( VALUES (returns_rw_array (1)), (returns_rw_array (2))) v (a); DO $$ DECLARE a int[] := ARRAY[1, 2]; BEGIN a := a || 3; RAISE notice 'a = %', a; END $$; -- -- Test access to call stack -- CREATE FUNCTION inner_func (int ) RETURNS int AS $$ DECLARE _context text; BEGIN get diagnostics _context = pg_context; RAISE notice '***%***', _context; -- lets do it again, just for fun.. get diagnostics _context = pg_context; RAISE notice '***%***', _context; RAISE notice 'lets make sure we didnt break anything'; RETURN 2 * $1; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION outer_func (int ) RETURNS int AS $$ DECLARE myresult int; BEGIN RAISE notice 'calling down into inner_func()'; myresult := inner_func ($1); RAISE notice 'inner_func() done'; RETURN myresult; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION outer_outer_func (int ) RETURNS int AS $$ DECLARE myresult int; BEGIN RAISE notice 'calling down into outer_func()'; myresult := outer_func ($1); RAISE notice 'outer_func() done'; RETURN myresult; END; $$ LANGUAGE plpgsql; SELECT outer_outer_func (10); -- repeated call should to work SELECT outer_outer_func (20); DROP FUNCTION outer_outer_func (int); DROP FUNCTION outer_func (int); DROP FUNCTION inner_func (int); -- access to call stack from exception CREATE FUNCTION inner_func (int ) RETURNS int AS $$ DECLARE _context text; sx int := 5; BEGIN BEGIN PERFORM sx / 0; exception WHEN division_by_zero THEN get diagnostics _context = pg_context; RAISE notice '***%***', _context; END; -- lets do it again, just for fun.. get diagnostics _context = pg_context; RAISE notice '***%***', _context; RAISE notice 'lets make sure we didnt break anything'; RETURN 2 * $1; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION outer_func (int) RETURNS int AS $$ DECLARE myresult int; BEGIN RAISE notice 'calling down into inner_func()'; myresult := inner_func ($1); RAISE notice 'inner_func() done'; RETURN myresult; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION outer_outer_func (int ) RETURNS int AS $$ DECLARE myresult int; BEGIN RAISE notice 'calling down into outer_func()'; myresult := outer_func ($1); RAISE notice 'outer_func() done'; RETURN myresult; END; $$ LANGUAGE plpgsql; SELECT outer_outer_func (10); -- repeated call should to work SELECT outer_outer_func (20); DROP FUNCTION outer_outer_func (int); DROP FUNCTION outer_func (int); DROP FUNCTION inner_func (int); -- -- Test ASSERT -- DO $$ BEGIN assert 1 = 1; -- should succeed END; $$; DO $$ BEGIN assert 1 = 0; -- should fail END; $$; DO $$ BEGIN assert NULL; -- should fail END; $$; -- check controlling GUC SET plpgsql.check_asserts = OFF; DO $$ BEGIN assert 1 = 0; -- won't be tested END; $$; RESET plpgsql.check_asserts; -- test custom message DO $$ DECLARE var text := 'some value'; BEGIN assert 1 = 0, format('assertion failed, var = "%s"', var); END; $$; -- ensure assertions are not trapped by 'others' DO $$ BEGIN assert 1 = 0, 'unhandled assertion'; exception WHEN OTHERS THEN NULL; -- do nothing END; $$; -- Test use of plpgsql in a domain check constraint (cf. bug #14414) CREATE FUNCTION plpgsql_domain_check (val int ) RETURNS boolean AS $$ BEGIN RETURN val > 0; END $$ LANGUAGE plpgsql IMMUTABLE; CREATE DOMAIN plpgsql_domain AS integer CHECK (plpgsql_domain_check (value)); DO $$ DECLARE v_test plpgsql_domain; BEGIN v_test := 1; END; $$; DO $$ DECLARE v_test plpgsql_domain := 1; BEGIN v_test := 0; -- fail END; $$; -- Test handling of expanded array passed to a domain constraint (bug #14472) CREATE FUNCTION plpgsql_arr_domain_check (val int[] ) RETURNS boolean AS $$ BEGIN RETURN val[1] > 0; END $$ LANGUAGE plpgsql IMMUTABLE; CREATE DOMAIN plpgsql_arr_domain AS int[] CHECK (plpgsql_arr_domain_check (value)); DO $$ DECLARE v_test plpgsql_arr_domain; BEGIN v_test := ARRAY[1]; v_test := v_test || 2; END; $$; DO $$ DECLARE v_test plpgsql_arr_domain := ARRAY[1]; BEGIN v_test := 0 || v_test; -- fail END; $$; -- -- test usage of transition tables in AFTER triggers -- CREATE TABLE transition_table_base ( id int PRIMARY KEY, val text ); CREATE FUNCTION transition_table_base_ins_func ( ) RETURNS TRIGGER LANGUAGE plpgsql AS $$ DECLARE t text; l text; BEGIN t = ''; FOR l IN EXECUTE $q$ EXPLAIN (TIMING off, COSTS off, VERBOSE on) SELECT * FROM newtable $q$ LOOP t = t || l || E'\n'; END LOOP; RAISE INFO '%', t; RETURN new; END; $$; CREATE TRIGGER transition_table_base_ins_trig AFTER INSERT ON transition_table_base REFERENCING OLD TABLE AS oldtable NEW TABLE AS newtable FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_base_ins_func ( ); CREATE TRIGGER transition_table_base_ins_trig AFTER INSERT ON transition_table_base REFERENCING NEW TABLE AS newtable FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_base_ins_func ( ); INSERT INTO transition_table_base VALUES (1, 'One'), (2, 'Two'); INSERT INTO transition_table_base VALUES (3, 'Three'), (4, 'Four'); CREATE OR REPLACE FUNCTION transition_table_base_upd_func ( ) RETURNS TRIGGER LANGUAGE plpgsql AS $$ DECLARE t text; l text; BEGIN t = ''; FOR l IN EXECUTE $q$ EXPLAIN (TIMING off, COSTS off, VERBOSE on) SELECT * FROM oldtable ot FULL JOIN newtable nt USING (id) $q$ LOOP t = t || l || E'\n'; END LOOP; RAISE INFO '%', t; RETURN new; END; $$; CREATE TRIGGER transition_table_base_upd_trig AFTER UPDATE ON transition_table_base REFERENCING OLD TABLE AS oldtable NEW TABLE AS newtable FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_base_upd_func ( ); UPDATE transition_table_base SET val = '*' || val || '*' WHERE id BETWEEN 2 AND 3; CREATE TABLE transition_table_level1 ( level1_no serial NOT NULL, level1_node_name varchar(255 ), PRIMARY KEY (level1_no ) ) WITHOUT OIDS; CREATE TABLE transition_table_level2 ( level2_no serial NOT NULL, parent_no int NOT NULL, level1_node_name varchar(255 ), PRIMARY KEY (level2_no ) ) WITHOUT OIDS; CREATE TABLE transition_table_status ( level int NOT NULL, node_no int NOT NULL, status int, PRIMARY KEY (level, node_no ) ) WITHOUT OIDS; CREATE FUNCTION transition_table_level1_ri_parent_del_func ( ) RETURNS TRIGGER LANGUAGE plpgsql AS $$ DECLARE n bigint; BEGIN PERFORM FROM p JOIN transition_table_level2 c ON c.parent_no = p.level1_no; IF FOUND THEN RAISE EXCEPTION 'RI error'; END IF; RETURN NULL; END; $$; CREATE TRIGGER transition_table_level1_ri_parent_del_trigger AFTER DELETE ON transition_table_level1 REFERENCING OLD TABLE AS p FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_level1_ri_parent_del_func ( ); CREATE FUNCTION transition_table_level1_ri_parent_upd_func ( ) RETURNS TRIGGER LANGUAGE plpgsql AS $$ DECLARE x int; BEGIN WITH p AS ( SELECT level1_no, sum(delta) cnt FROM ( SELECT level1_no, 1 AS delta FROM i UNION ALL SELECT level1_no, - 1 AS delta FROM d) w GROUP BY level1_no HAVING sum(delta) < 0 ) SELECT level1_no FROM p JOIN transition_table_level2 c ON c.parent_no = p.level1_no INTO x; IF FOUND THEN RAISE EXCEPTION 'RI error'; END IF; RETURN NULL; END; $$; CREATE TRIGGER transition_table_level1_ri_parent_upd_trigger AFTER UPDATE ON transition_table_level1 REFERENCING OLD TABLE AS d NEW TABLE AS i FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_level1_ri_parent_upd_func ( ); CREATE FUNCTION transition_table_level2_ri_child_insupd_func ( ) RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN PERFORM FROM i LEFT JOIN transition_table_level1 p ON p.level1_no IS NOT NULL AND p.level1_no = i.parent_no WHERE p.level1_no IS NULL; IF FOUND THEN RAISE EXCEPTION 'RI error'; END IF; RETURN NULL; END; $$; CREATE TRIGGER transition_table_level2_ri_child_ins_trigger AFTER INSERT ON transition_table_level2 REFERENCING NEW TABLE AS i FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_level2_ri_child_insupd_func ( ); CREATE TRIGGER transition_table_level2_ri_child_upd_trigger AFTER UPDATE ON transition_table_level2 REFERENCING NEW TABLE AS i FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_level2_ri_child_insupd_func ( ); -- create initial test data INSERT INTO transition_table_level1 (level1_no) SELECT generate_series(1, 200); ANALYZE transition_table_level1; INSERT INTO transition_table_level2 (level2_no, parent_no) SELECT level2_no, level2_no / 50 + 1 AS parent_no FROM generate_series(1, 9999) level2_no; ANALYZE transition_table_level2; INSERT INTO transition_table_status (level, node_no, status) SELECT 1, level1_no, 0 FROM transition_table_level1; INSERT INTO transition_table_status (level, node_no, status) SELECT 2, level2_no, 0 FROM transition_table_level2; ANALYZE transition_table_status; INSERT INTO transition_table_level1 (level1_no) SELECT generate_series(201, 1000); ANALYZE transition_table_level1; -- behave reasonably if someone tries to modify a transition table CREATE FUNCTION transition_table_level2_bad_usage_func ( ) RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN INSERT INTO dx VALUES (1000000, 1000000, 'x'); RETURN NULL; END; $$; CREATE TRIGGER transition_table_level2_bad_usage_trigger AFTER DELETE ON transition_table_level2 REFERENCING OLD TABLE AS dx FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_level2_bad_usage_func ( ); DELETE FROM transition_table_level2 WHERE level2_no BETWEEN 301 AND 305; DROP TRIGGER transition_table_level2_bad_usage_trigger ON transition_table_level2; -- attempt modifications which would break RI (should all fail) DELETE FROM transition_table_level1 WHERE level1_no = 25; UPDATE transition_table_level1 SET level1_no = - 1 WHERE level1_no = 30; INSERT INTO transition_table_level2 (level2_no, parent_no) VALUES (10000, 10000); UPDATE transition_table_level2 SET parent_no = 2000 WHERE level2_no = 40; -- attempt modifications which would not break RI (should all succeed) DELETE FROM transition_table_level1 WHERE level1_no BETWEEN 201 AND 1000; DELETE FROM transition_table_level1 WHERE level1_no BETWEEN 100000000 AND 100000010; SELECT count(*) FROM transition_table_level1; DELETE FROM transition_table_level2 WHERE level2_no BETWEEN 211 AND 220; SELECT count(*) FROM transition_table_level2; CREATE TABLE alter_table_under_transition_tables ( id int PRIMARY KEY, name text ); CREATE FUNCTION alter_table_under_transition_tables_upd_func ( ) RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RAISE WARNING 'old table = %, new table = %', ( SELECT string_agg(id || '=' || name, ',') FROM d), ( SELECT string_agg(id || '=' || name, ',') FROM i); RAISE NOTICE 'one = %', ( SELECT 1 FROM alter_table_under_transition_tables LIMIT 1); RETURN NULL; END; $$; -- should fail, TRUNCATE is not compatible with transition tables CREATE TRIGGER alter_table_under_transition_tables_upd_trigger AFTER TRUNCATE OR UPDATE ON alter_table_under_transition_tables REFERENCING OLD TABLE AS d NEW TABLE AS i FOR EACH STATEMENT EXECUTE PROCEDURE alter_table_under_transition_tables_upd_func ( ); -- should work CREATE TRIGGER alter_table_under_transition_tables_upd_trigger AFTER UPDATE ON alter_table_under_transition_tables REFERENCING OLD TABLE AS d NEW TABLE AS i FOR EACH STATEMENT EXECUTE PROCEDURE alter_table_under_transition_tables_upd_func ( ); INSERT INTO alter_table_under_transition_tables VALUES (1, '1'), (2, '2'), (3, '3'); UPDATE alter_table_under_transition_tables SET name = name || name; -- now change 'name' to an integer to see what happens... ALTER TABLE alter_table_under_transition_tables ALTER COLUMN name TYPE int USING name::integer; UPDATE alter_table_under_transition_tables SET name = (name::text || name::text)::integer; -- now drop column 'name' ALTER TABLE alter_table_under_transition_tables DROP COLUMN name; UPDATE alter_table_under_transition_tables SET id = id; -- -- Test multiple reference to a transition table -- CREATE TABLE multi_test ( i int ); INSERT INTO multi_test VALUES (1); CREATE OR REPLACE FUNCTION multi_test_trig ( ) RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RAISE NOTICE 'count = %', ( SELECT COUNT(*) FROM new_test); RAISE NOTICE 'count union = %', ( SELECT COUNT(*) FROM ( SELECT * FROM new_test UNION ALL SELECT * FROM new_test) ss); RETURN NULL; END $$; CREATE TRIGGER my_trigger AFTER UPDATE ON multi_test REFERENCING NEW TABLE AS new_test OLD TABLE AS old_test FOR EACH STATEMENT EXECUTE PROCEDURE multi_test_trig ( ); UPDATE multi_test SET i = i; DROP TABLE multi_test; DROP FUNCTION multi_test_trig (); -- -- Check type parsing and record fetching from partitioned tables -- CREATE TABLE partitioned_table ( a int, b text ) PARTITION BY LIST (a ); CREATE TABLE pt_part1 PARTITION OF partitioned_table FOR VALUES IN (1 ); CREATE TABLE pt_part2 PARTITION OF partitioned_table FOR VALUES IN (2 ); INSERT INTO partitioned_table VALUES (1, 'Row 1'); INSERT INTO partitioned_table VALUES (2, 'Row 2'); CREATE OR REPLACE FUNCTION get_from_partitioned_table (partitioned_table.a % TYPE ) RETURNS partitioned_table AS $$ DECLARE a_val partitioned_table.a % TYPE; result partitioned_table % ROWTYPE; BEGIN a_val := $1; SELECT * INTO result FROM partitioned_table WHERE a = a_val; RETURN result; END; $$ LANGUAGE plpgsql; SELECT * FROM get_from_partitioned_table (1) AS t; CREATE OR REPLACE FUNCTION list_partitioned_table ( ) RETURNS SETOF partitioned_table.a % TYPE AS $$ DECLARE ROW partitioned_table % ROWTYPE; a_val partitioned_table.a % TYPE; BEGIN FOR ROW IN SELECT * FROM partitioned_table ORDER BY a LOOP a_val := row.a; RETURN NEXT a_val; END LOOP; RETURN; END; $$ LANGUAGE plpgsql; SELECT * FROM list_partitioned_table () AS t; -- -- Check argument name is used instead of $n in error message -- CREATE FUNCTION fx (x WSlot ) RETURNS void AS $$ BEGIN GET DIAGNOSTICS x = ROW_COUNT; RETURN; END; $$ LANGUAGE plpgsql; pgFormatter-4.2/t/pg-test-files/expected/point.sql000066400000000000000000000103311361326045100222510ustar00rootroot00000000000000-- -- POINT -- -- avoid bit-exact output here because operations may not be bit-exact. SET extra_float_digits = 0; CREATE TABLE POINT_TBL ( f1 point ); INSERT INTO POINT_TBL (f1) VALUES ('(0.0,0.0)'); INSERT INTO POINT_TBL (f1) VALUES ('(-10.0,0.0)'); INSERT INTO POINT_TBL (f1) VALUES ('(-3.0,4.0)'); INSERT INTO POINT_TBL (f1) VALUES ('(5.1, 34.5)'); INSERT INTO POINT_TBL (f1) VALUES ('(-5.0,-12.0)'); INSERT INTO POINT_TBL (f1) VALUES ('(1e-300,-1e-300)'); -- To underflow INSERT INTO POINT_TBL (f1) VALUES ('(1e+300,Inf)'); -- To overflow INSERT INTO POINT_TBL (f1) VALUES (' ( Nan , NaN ) '); -- bad format points INSERT INTO POINT_TBL (f1) VALUES ('asdfasdf'); INSERT INTO POINT_TBL (f1) VALUES ('10.0,10.0'); INSERT INTO POINT_TBL (f1) VALUES ('(10.0 10.0)'); INSERT INTO POINT_TBL (f1) VALUES ('(10.0, 10.0) x'); INSERT INTO POINT_TBL (f1) VALUES ('(10.0,10.0'); INSERT INTO POINT_TBL (f1) VALUES ('(10.0, 1e+500)'); -- Out of range SELECT '' AS six, * FROM POINT_TBL; -- left of SELECT '' AS three, p.* FROM POINT_TBL p WHERE p.f1 << '(0.0, 0.0)'; -- right of SELECT '' AS three, p.* FROM POINT_TBL p WHERE '(0.0,0.0)' >> p.f1; -- above SELECT '' AS one, p.* FROM POINT_TBL p WHERE '(0.0,0.0)' >^ p.f1; -- below SELECT '' AS one, p.* FROM POINT_TBL p WHERE p.f1 <^ '(0.0, 0.0)'; -- equal SELECT '' AS one, p.* FROM POINT_TBL p WHERE p.f1 ~= '(5.1, 34.5)'; -- point in box SELECT '' AS three, p.* FROM POINT_TBL p WHERE p.f1 <@ box '(0,0,100,100)'; SELECT '' AS three, p.* FROM POINT_TBL p WHERE box '(0,0,100,100)' @> p.f1; SELECT '' AS three, p.* FROM POINT_TBL p WHERE NOT p.f1 <@ box '(0,0,100,100)'; SELECT '' AS two, p.* FROM POINT_TBL p WHERE p.f1 <@ path '[(0,0),(-10,0),(-10,10)]'; SELECT '' AS three, p.* FROM POINT_TBL p WHERE NOT box '(0,0,100,100)' @> p.f1; SELECT '' AS six, p.f1, p.f1 <-> point '(0,0)' AS dist FROM POINT_TBL p ORDER BY dist; SELECT '' AS thirtysix, p1.f1 AS point1, p2.f1 AS point2, p1.f1 <-> p2.f1 AS dist FROM POINT_TBL p1, POINT_TBL p2 ORDER BY dist, p1.f1[0], p2.f1[0]; SELECT '' AS thirty, p1.f1 AS point1, p2.f1 AS point2 FROM POINT_TBL p1, POINT_TBL p2 WHERE (p1.f1 <-> p2.f1) > 3; -- put distance result into output to allow sorting with GEQ optimizer - tgl 97/05/10 SELECT '' AS fifteen, p1.f1 AS point1, p2.f1 AS point2, (p1.f1 <-> p2.f1) AS distance FROM POINT_TBL p1, POINT_TBL p2 WHERE (p1.f1 <-> p2.f1) > 3 AND p1.f1 << p2.f1 ORDER BY distance, p1.f1[0], p2.f1[0]; -- put distance result into output to allow sorting with GEQ optimizer - tgl 97/05/10 SELECT '' AS three, p1.f1 AS point1, p2.f1 AS point2, (p1.f1 <-> p2.f1) AS distance FROM POINT_TBL p1, POINT_TBL p2 WHERE (p1.f1 <-> p2.f1) > 3 AND p1.f1 << p2.f1 AND p1.f1 >^ p2.f1 ORDER BY distance; -- Test that GiST indexes provide same behavior as sequential scan CREATE TEMP TABLE point_gist_tbl ( f1 point ); INSERT INTO point_gist_tbl SELECT '(0,0)' FROM generate_series(0, 1000); CREATE INDEX point_gist_tbl_index ON point_gist_tbl USING gist (f1); INSERT INTO point_gist_tbl VALUES ('(0.0000009,0.0000009)'); SET enable_seqscan TO TRUE; SET enable_indexscan TO FALSE; SET enable_bitmapscan TO FALSE; SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000009,0.0000009)'::point; SELECT COUNT(*) FROM point_gist_tbl WHERE f1 <@ '(0.0000009,0.0000009),(0.0000009,0.0000009)'::box; SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000018,0.0000018)'::point; SET enable_seqscan TO FALSE; SET enable_indexscan TO TRUE; SET enable_bitmapscan TO TRUE; SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000009,0.0000009)'::point; SELECT COUNT(*) FROM point_gist_tbl WHERE f1 <@ '(0.0000009,0.0000009),(0.0000009,0.0000009)'::box; SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000018,0.0000018)'::point; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; pgFormatter-4.2/t/pg-test-files/expected/polygon.sql000066400000000000000000000133721361326045100226170ustar00rootroot00000000000000-- -- POLYGON -- -- polygon logic -- CREATE TABLE POLYGON_TBL ( f1 polygon ); INSERT INTO POLYGON_TBL (f1) VALUES ('(2.0,0.0),(2.0,4.0),(0.0,0.0)'); INSERT INTO POLYGON_TBL (f1) VALUES ('(3.0,1.0),(3.0,3.0),(1.0,0.0)'); INSERT INTO POLYGON_TBL (f1) VALUES ('(1,2),(3,4),(5,6),(7,8)'); INSERT INTO POLYGON_TBL (f1) VALUES ('(7,8),(5,6),(3,4),(1,2)'); -- Reverse INSERT INTO POLYGON_TBL (f1) VALUES ('(1,2),(7,8),(5,6),(3,-4)'); -- degenerate polygons INSERT INTO POLYGON_TBL (f1) VALUES ('(0.0,0.0)'); INSERT INTO POLYGON_TBL (f1) VALUES ('(0.0,1.0),(0.0,1.0)'); -- bad polygon input strings INSERT INTO POLYGON_TBL (f1) VALUES ('0.0'); INSERT INTO POLYGON_TBL (f1) VALUES ('(0.0 0.0'); INSERT INTO POLYGON_TBL (f1) VALUES ('(0,1,2)'); INSERT INTO POLYGON_TBL (f1) VALUES ('(0,1,2,3'); INSERT INTO POLYGON_TBL (f1) VALUES ('asdf'); SELECT '' AS four, * FROM POLYGON_TBL; -- -- Test the SP-GiST index -- CREATE TABLE quad_poly_tbl ( id int, p polygon ); INSERT INTO quad_poly_tbl SELECT (x - 1) * 100 + y, polygon(circle(point(x * 10, y * 10), 1 + (x + y) % 10)) FROM generate_series(1, 100) x, generate_series(1, 100) y; INSERT INTO quad_poly_tbl SELECT i, polygon '((200, 300),(210, 310),(230, 290))' FROM generate_series(10001, 11000) AS i; INSERT INTO quad_poly_tbl VALUES (11001, NULL), (11002, NULL), (11003, NULL); CREATE INDEX quad_poly_tbl_idx ON quad_poly_tbl USING spgist (p); -- get reference results for ORDER BY distance from seq scan SET enable_seqscan = ON; SET enable_indexscan = OFF; SET enable_bitmapscan = OFF; CREATE TEMP TABLE quad_poly_tbl_ord_seq2 AS SELECT rank() OVER (ORDER BY p <-> point '123,456') n, p <-> point '123,456' dist, id FROM quad_poly_tbl WHERE p <@ polygon '((300,300),(400,600),(600,500),(700,200))'; -- check results results from index scan SET enable_seqscan = OFF; SET enable_indexscan = OFF; SET enable_bitmapscan = ON; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_poly_tbl WHERE p << polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p << polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_poly_tbl WHERE p &< polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p &< polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_poly_tbl WHERE p && polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p && polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_poly_tbl WHERE p &> polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p &> polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_poly_tbl WHERE p >> polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p >> polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_poly_tbl WHERE p <<| polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p <<| polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_poly_tbl WHERE p &<| polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p &<| polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_poly_tbl WHERE p |&> polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p |&> polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_poly_tbl WHERE p |>> polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p |>> polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_poly_tbl WHERE p <@ polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p <@ polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_poly_tbl WHERE p @> polygon '((340,550),(343,552),(341,553))'; SELECT count(*) FROM quad_poly_tbl WHERE p @> polygon '((340,550),(343,552),(341,553))'; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM quad_poly_tbl WHERE p ~= polygon '((200, 300),(210, 310),(230, 290))'; SELECT count(*) FROM quad_poly_tbl WHERE p ~= polygon '((200, 300),(210, 310),(230, 290))'; -- test ORDER BY distance SET enable_indexscan = ON; SET enable_bitmapscan = OFF; EXPLAIN ( COSTS OFF ) SELECT rank() OVER (ORDER BY p <-> point '123,456') n, p <-> point '123,456' dist, id FROM quad_poly_tbl WHERE p <@ polygon '((300,300),(400,600),(600,500),(700,200))'; CREATE TEMP TABLE quad_poly_tbl_ord_idx2 AS SELECT rank() OVER (ORDER BY p <-> point '123,456') n, p <-> point '123,456' dist, id FROM quad_poly_tbl WHERE p <@ polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT * FROM quad_poly_tbl_ord_seq2 seq FULL JOIN quad_poly_tbl_ord_idx2 idx ON seq.n = idx.n AND seq.id = idx.id AND (seq.dist = idx.dist OR seq.dist IS NULL AND idx.dist IS NULL) WHERE seq.id IS NULL OR idx.id IS NULL; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; pgFormatter-4.2/t/pg-test-files/expected/polymorphism.sql000066400000000000000000000771061361326045100236770ustar00rootroot00000000000000-- Currently this tests polymorphic aggregates and indirectly does some -- testing of polymorphic SQL functions. It ought to be extended. -- Tests for other features related to function-calling have snuck in, too. -- Legend: ----------- -- A = type is ANY -- P = type is polymorphic -- N = type is non-polymorphic -- B = aggregate base type -- S = aggregate state type -- R = aggregate return type -- 1 = arg1 of a function -- 2 = arg2 of a function -- ag = aggregate -- tf = trans (state) function -- ff = final function -- rt = return type of a function -- -> = implies -- => = allowed -- !> = not allowed -- E = exists -- NE = not-exists -- -- Possible states: -- ---------------- -- B = (A || P || N) -- when (B = A) -> (tf2 = NE) -- S = (P || N) -- ff = (E || NE) -- tf1 = (P || N) -- tf2 = (NE || P || N) -- R = (P || N) -- create functions for use as tf and ff with the needed combinations of -- argument polymorphism, but within the constraints of valid aggregate -- functions, i.e. tf arg1 and tf return type must match -- polymorphic single arg transfn CREATE FUNCTION stfp (anyarray) RETURNS anyarray AS 'select $1' LANGUAGE SQL; -- non-polymorphic single arg transfn CREATE FUNCTION stfnp (int[]) RETURNS int[] AS 'select $1' LANGUAGE SQL; -- dual polymorphic transfn CREATE FUNCTION tfp (anyarray, anyelement) RETURNS anyarray AS 'select $1 || $2' LANGUAGE SQL; -- dual non-polymorphic transfn CREATE FUNCTION tfnp (int[], int) RETURNS int[] AS 'select $1 || $2' LANGUAGE SQL; -- arg1 only polymorphic transfn CREATE FUNCTION tf1p (anyarray, int) RETURNS anyarray AS 'select $1' LANGUAGE SQL; -- arg2 only polymorphic transfn CREATE FUNCTION tf2p (int[], anyelement) RETURNS int[] AS 'select $1' LANGUAGE SQL; -- multi-arg polymorphic CREATE FUNCTION sum3 (anyelement, anyelement, anyelement) RETURNS anyelement AS 'select $1+$2+$3' LANGUAGE sql STRICT; -- finalfn polymorphic CREATE FUNCTION ffp (anyarray) RETURNS anyarray AS 'select $1' LANGUAGE SQL; -- finalfn non-polymorphic CREATE FUNCTION ffnp (int[]) RETURNS int[] AS 'select $1' LANGUAGE SQL; -- Try to cover all the possible states: -- -- Note: in Cases 1 & 2, we are trying to return P. Therefore, if the transfn -- is stfnp, tfnp, or tf2p, we must use ffp as finalfn, because stfnp, tfnp, -- and tf2p do not return P. Conversely, in Cases 3 & 4, we are trying to -- return N. Therefore, if the transfn is stfp, tfp, or tf1p, we must use ffnp -- as finalfn, because stfp, tfp, and tf1p do not return N. -- -- Case1 (R = P) && (B = A) -- ------------------------ -- S tf1 -- ------- -- N N -- should CREATE CREATE AGGREGATE myaggp01a (*) ( SFUNC = stfnp, STYPE = int4[], FINALFUNC = ffp, INITCOND = '{}' ); -- P N -- should ERROR: stfnp(anyarray) not matched by stfnp(int[]) CREATE AGGREGATE myaggp02a (*) ( SFUNC = stfnp, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}' ); -- N P -- should CREATE CREATE AGGREGATE myaggp03a (*) ( SFUNC = stfp, STYPE = int4[], FINALFUNC = ffp, INITCOND = '{}' ); CREATE AGGREGATE myaggp03b (*) ( SFUNC = stfp, STYPE = int4[], INITCOND = '{}' ); -- P P -- should ERROR: we have no way to resolve S CREATE AGGREGATE myaggp04a (*) ( SFUNC = stfp, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}' ); CREATE AGGREGATE myaggp04b (*) ( SFUNC = stfp, STYPE = anyarray, INITCOND = '{}' ); -- Case2 (R = P) && ((B = P) || (B = N)) -- ------------------------------------- -- S tf1 B tf2 -- ----------------------- -- N N N N -- should CREATE CREATE AGGREGATE myaggp05a ( BASETYPE = int, SFUNC = tfnp, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}' ); -- N N N P -- should CREATE CREATE AGGREGATE myaggp06a ( BASETYPE = int, SFUNC = tf2p, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}' ); -- N N P N -- should ERROR: tfnp(int[], anyelement) not matched by tfnp(int[], int) CREATE AGGREGATE myaggp07a ( BASETYPE = anyelement, SFUNC = tfnp, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}' ); -- N N P P -- should CREATE CREATE AGGREGATE myaggp08a ( BASETYPE = anyelement, SFUNC = tf2p, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}' ); -- N P N N -- should CREATE CREATE AGGREGATE myaggp09a ( BASETYPE = int, SFUNC = tf1p, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}' ); CREATE AGGREGATE myaggp09b ( BASETYPE = int, SFUNC = tf1p, STYPE = int[], INITCOND = '{}' ); -- N P N P -- should CREATE CREATE AGGREGATE myaggp10a ( BASETYPE = int, SFUNC = tfp, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}' ); CREATE AGGREGATE myaggp10b ( BASETYPE = int, SFUNC = tfp, STYPE = int[], INITCOND = '{}' ); -- N P P N -- should ERROR: tf1p(int[],anyelement) not matched by tf1p(anyarray,int) CREATE AGGREGATE myaggp11a ( BASETYPE = anyelement, SFUNC = tf1p, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}' ); CREATE AGGREGATE myaggp11b ( BASETYPE = anyelement, SFUNC = tf1p, STYPE = int[], INITCOND = '{}' ); -- N P P P -- should ERROR: tfp(int[],anyelement) not matched by tfp(anyarray,anyelement) CREATE AGGREGATE myaggp12a ( BASETYPE = anyelement, SFUNC = tfp, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}' ); CREATE AGGREGATE myaggp12b ( BASETYPE = anyelement, SFUNC = tfp, STYPE = int[], INITCOND = '{}' ); -- P N N N -- should ERROR: tfnp(anyarray, int) not matched by tfnp(int[],int) CREATE AGGREGATE myaggp13a ( BASETYPE = int, SFUNC = tfnp, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}' ); -- P N N P -- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement) CREATE AGGREGATE myaggp14a ( BASETYPE = int, SFUNC = tf2p, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}' ); -- P N P N -- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int) CREATE AGGREGATE myaggp15a ( BASETYPE = anyelement, SFUNC = tfnp, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}' ); -- P N P P -- should ERROR: tf2p(anyarray, anyelement) not matched by tf2p(int[],anyelement) CREATE AGGREGATE myaggp16a ( BASETYPE = anyelement, SFUNC = tf2p, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}' ); -- P P N N -- should ERROR: we have no way to resolve S CREATE AGGREGATE myaggp17a ( BASETYPE = int, SFUNC = tf1p, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}' ); CREATE AGGREGATE myaggp17b ( BASETYPE = int, SFUNC = tf1p, STYPE = anyarray, INITCOND = '{}' ); -- P P N P -- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement) CREATE AGGREGATE myaggp18a ( BASETYPE = int, SFUNC = tfp, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}' ); CREATE AGGREGATE myaggp18b ( BASETYPE = int, SFUNC = tfp, STYPE = anyarray, INITCOND = '{}' ); -- P P P N -- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int) CREATE AGGREGATE myaggp19a ( BASETYPE = anyelement, SFUNC = tf1p, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}' ); CREATE AGGREGATE myaggp19b ( BASETYPE = anyelement, SFUNC = tf1p, STYPE = anyarray, INITCOND = '{}' ); -- P P P P -- should CREATE CREATE AGGREGATE myaggp20a ( BASETYPE = anyelement, SFUNC = tfp, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}' ); CREATE AGGREGATE myaggp20b ( BASETYPE = anyelement, SFUNC = tfp, STYPE = anyarray, INITCOND = '{}' ); -- Case3 (R = N) && (B = A) -- ------------------------ -- S tf1 -- ------- -- N N -- should CREATE CREATE AGGREGATE myaggn01a (*) ( SFUNC = stfnp, STYPE = int4[], FINALFUNC = ffnp, INITCOND = '{}' ); CREATE AGGREGATE myaggn01b (*) ( SFUNC = stfnp, STYPE = int4[], INITCOND = '{}' ); -- P N -- should ERROR: stfnp(anyarray) not matched by stfnp(int[]) CREATE AGGREGATE myaggn02a (*) ( SFUNC = stfnp, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}' ); CREATE AGGREGATE myaggn02b (*) ( SFUNC = stfnp, STYPE = anyarray, INITCOND = '{}' ); -- N P -- should CREATE CREATE AGGREGATE myaggn03a (*) ( SFUNC = stfp, STYPE = int4[], FINALFUNC = ffnp, INITCOND = '{}' ); -- P P -- should ERROR: ffnp(anyarray) not matched by ffnp(int[]) CREATE AGGREGATE myaggn04a (*) ( SFUNC = stfp, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}' ); -- Case4 (R = N) && ((B = P) || (B = N)) -- ------------------------------------- -- S tf1 B tf2 -- ----------------------- -- N N N N -- should CREATE CREATE AGGREGATE myaggn05a ( BASETYPE = int, SFUNC = tfnp, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}' ); CREATE AGGREGATE myaggn05b ( BASETYPE = int, SFUNC = tfnp, STYPE = int[], INITCOND = '{}' ); -- N N N P -- should CREATE CREATE AGGREGATE myaggn06a ( BASETYPE = int, SFUNC = tf2p, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}' ); CREATE AGGREGATE myaggn06b ( BASETYPE = int, SFUNC = tf2p, STYPE = int[], INITCOND = '{}' ); -- N N P N -- should ERROR: tfnp(int[], anyelement) not matched by tfnp(int[], int) CREATE AGGREGATE myaggn07a ( BASETYPE = anyelement, SFUNC = tfnp, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}' ); CREATE AGGREGATE myaggn07b ( BASETYPE = anyelement, SFUNC = tfnp, STYPE = int[], INITCOND = '{}' ); -- N N P P -- should CREATE CREATE AGGREGATE myaggn08a ( BASETYPE = anyelement, SFUNC = tf2p, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}' ); CREATE AGGREGATE myaggn08b ( BASETYPE = anyelement, SFUNC = tf2p, STYPE = int[], INITCOND = '{}' ); -- N P N N -- should CREATE CREATE AGGREGATE myaggn09a ( BASETYPE = int, SFUNC = tf1p, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}' ); -- N P N P -- should CREATE CREATE AGGREGATE myaggn10a ( BASETYPE = int, SFUNC = tfp, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}' ); -- N P P N -- should ERROR: tf1p(int[],anyelement) not matched by tf1p(anyarray,int) CREATE AGGREGATE myaggn11a ( BASETYPE = anyelement, SFUNC = tf1p, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}' ); -- N P P P -- should ERROR: tfp(int[],anyelement) not matched by tfp(anyarray,anyelement) CREATE AGGREGATE myaggn12a ( BASETYPE = anyelement, SFUNC = tfp, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}' ); -- P N N N -- should ERROR: tfnp(anyarray, int) not matched by tfnp(int[],int) CREATE AGGREGATE myaggn13a ( BASETYPE = int, SFUNC = tfnp, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}' ); CREATE AGGREGATE myaggn13b ( BASETYPE = int, SFUNC = tfnp, STYPE = anyarray, INITCOND = '{}' ); -- P N N P -- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement) CREATE AGGREGATE myaggn14a ( BASETYPE = int, SFUNC = tf2p, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}' ); CREATE AGGREGATE myaggn14b ( BASETYPE = int, SFUNC = tf2p, STYPE = anyarray, INITCOND = '{}' ); -- P N P N -- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int) CREATE AGGREGATE myaggn15a ( BASETYPE = anyelement, SFUNC = tfnp, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}' ); CREATE AGGREGATE myaggn15b ( BASETYPE = anyelement, SFUNC = tfnp, STYPE = anyarray, INITCOND = '{}' ); -- P N P P -- should ERROR: tf2p(anyarray, anyelement) not matched by tf2p(int[],anyelement) CREATE AGGREGATE myaggn16a ( BASETYPE = anyelement, SFUNC = tf2p, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}' ); CREATE AGGREGATE myaggn16b ( BASETYPE = anyelement, SFUNC = tf2p, STYPE = anyarray, INITCOND = '{}' ); -- P P N N -- should ERROR: ffnp(anyarray) not matched by ffnp(int[]) CREATE AGGREGATE myaggn17a ( BASETYPE = int, SFUNC = tf1p, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}' ); -- P P N P -- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement) CREATE AGGREGATE myaggn18a ( BASETYPE = int, SFUNC = tfp, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}' ); -- P P P N -- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int) CREATE AGGREGATE myaggn19a ( BASETYPE = anyelement, SFUNC = tf1p, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}' ); -- P P P P -- should ERROR: ffnp(anyarray) not matched by ffnp(int[]) CREATE AGGREGATE myaggn20a ( BASETYPE = anyelement, SFUNC = tfp, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}' ); -- multi-arg polymorphic CREATE AGGREGATE mysum2 (anyelement, anyelement) ( SFUNC = sum3, STYPE = anyelement, INITCOND = '0' ); -- create test data for polymorphic aggregates CREATE temp TABLE t ( f1 int, f2 int[], f3 text ); INSERT INTO t VALUES (1, ARRAY[1], 'a'); INSERT INTO t VALUES (1, ARRAY[11], 'b'); INSERT INTO t VALUES (1, ARRAY[111], 'c'); INSERT INTO t VALUES (2, ARRAY[2], 'a'); INSERT INTO t VALUES (2, ARRAY[22], 'b'); INSERT INTO t VALUES (2, ARRAY[222], 'c'); INSERT INTO t VALUES (3, ARRAY[3], 'a'); INSERT INTO t VALUES (3, ARRAY[3], 'b'); -- test the successfully created polymorphic aggregates SELECT f3, myaggp01a (*) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggp03a (*) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggp03b (*) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggp05a (f1) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggp06a (f1) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggp08a (f1) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggp09a (f1) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggp09b (f1) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggp10a (f1) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggp10b (f1) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggp20a (f1) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggp20b (f1) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggn01a (*) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggn01b (*) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggn03a (*) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggn05a (f1) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggn05b (f1) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggn06a (f1) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggn06b (f1) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggn08a (f1) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggn08b (f1) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggn09a (f1) FROM t GROUP BY f3 ORDER BY f3; SELECT f3, myaggn10a (f1) FROM t GROUP BY f3 ORDER BY f3; SELECT mysum2 (f1, f1 + 1) FROM t; -- test inlining of polymorphic SQL functions CREATE FUNCTION bleat (int) RETURNS int AS $$ BEGIN RAISE notice 'bleat %', $1; RETURN $1; END $$ LANGUAGE plpgsql; CREATE FUNCTION sql_if (bool, anyelement, anyelement) RETURNS anyelement AS $$ SELECT CASE WHEN $1 THEN $2 ELSE $3 END $$ LANGUAGE sql; -- Note this would fail with integer overflow, never mind wrong bleat() output, -- if the CASE expression were not successfully inlined SELECT f1, sql_if (f1 > 0, bleat (f1), bleat (f1 + 1)) FROM int4_tbl; SELECT q2, sql_if (q2 > 0, q2, q2 + 1) FROM int8_tbl; -- another sort of polymorphic aggregate CREATE AGGREGATE array_cat_accum (anyarray) ( SFUNC = array_cat, STYPE = anyarray, INITCOND = '{}' ); SELECT array_cat_accum (i) FROM ( VALUES (ARRAY[1, 2]), (ARRAY[3, 4])) AS t (i); SELECT array_cat_accum (i) FROM ( VALUES (ARRAY[ROW (1, 2), ROW (3, 4)]), (ARRAY[ROW (5, 6), ROW (7, 8)])) AS t (i); -- another kind of polymorphic aggregate CREATE FUNCTION add_group (grp anyarray, ad anyelement, size integer) RETURNS anyarray AS $$ BEGIN IF grp IS NULL THEN RETURN ARRAY[ad]; END IF; IF array_upper(grp, 1) < size THEN RETURN grp || ad; END IF; RETURN grp; END; $$ LANGUAGE plpgsql IMMUTABLE; CREATE AGGREGATE build_group (anyelement, integer) ( SFUNC = add_group, STYPE = anyarray ); SELECT build_group (q1, 3) FROM int8_tbl; -- this should fail because stype isn't compatible with arg CREATE AGGREGATE build_group (int8, integer) ( SFUNC = add_group, STYPE = int2[] ); -- but we can make a non-poly agg from a poly sfunc if types are OK CREATE AGGREGATE build_group (int8, integer) ( SFUNC = add_group, STYPE = int8[] ); -- check proper resolution of data types for polymorphic transfn/finalfn CREATE FUNCTION first_el (anyarray) RETURNS anyelement AS 'select $1[1]' LANGUAGE sql STRICT IMMUTABLE; CREATE AGGREGATE first_el_agg_f8 (float8) ( SFUNC = array_append, STYPE = float8[], FINALFUNC = first_el ); CREATE AGGREGATE first_el_agg_any (anyelement) ( SFUNC = array_append, STYPE = anyarray, FINALFUNC = first_el ); SELECT first_el_agg_f8 (x::float8) FROM generate_series(1, 10) x; SELECT first_el_agg_any (x) FROM generate_series(1, 10) x; SELECT first_el_agg_f8 (x::float8) OVER (ORDER BY x) FROM generate_series(1, 10) x; SELECT first_el_agg_any (x) OVER (ORDER BY x) FROM generate_series(1, 10) x; -- check that we can apply functions taking ANYARRAY to pg_stats SELECT DISTINCT array_ndims(histogram_bounds) FROM pg_stats WHERE histogram_bounds IS NOT NULL; -- such functions must protect themselves if varying element type isn't OK -- (WHERE clause here is to avoid possibly getting a collation error instead) SELECT max(histogram_bounds) FROM pg_stats WHERE tablename = 'pg_am'; -- test variadic polymorphic functions CREATE FUNCTION myleast (VARIADIC anyarray) RETURNS anyelement AS $$ SELECT min($1[i]) FROM generate_subscripts($1, 1) g (i) $$ LANGUAGE sql IMMUTABLE STRICT; SELECT myleast (10, 1, 20, 33); SELECT myleast (1.1, 0.22, 0.55); SELECT myleast ('z'::text); SELECT myleast (); -- fail -- test with variadic call parameter SELECT myleast (VARIADIC ARRAY[1, 2, 3, 4, - 1]); SELECT myleast (VARIADIC ARRAY[1.1, - 5.5]); --test with empty variadic call parameter SELECT myleast (VARIADIC ARRAY[]::int[]); -- an example with some ordinary arguments too CREATE FUNCTION concat(text, VARIADIC anyarray) RETURNS text AS $$ SELECT array_to_string($2, $1); $$ LANGUAGE sql IMMUTABLE STRICT; SELECT concat('%', 1, 2, 3, 4, 5); SELECT concat('|', 'a'::text, 'b', 'c'); SELECT concat('|', VARIADIC ARRAY[1, 2, 33]); SELECT concat('|', VARIADIC ARRAY[]::int[]); DROP FUNCTION concat(text, anyarray); -- mix variadic with anyelement CREATE FUNCTION formarray (anyelement, VARIADIC anyarray) RETURNS anyarray AS $$ SELECT array_prepend($1, $2); $$ LANGUAGE sql IMMUTABLE STRICT; SELECT formarray (1, 2, 3, 4, 5); SELECT formarray (1.1, VARIADIC ARRAY[1.2, 55.5]); SELECT formarray (1.1, ARRAY[1.2, 55.5]); -- fail without variadic SELECT formarray (1, 'x'::text); -- fail, type mismatch SELECT formarray (1, VARIADIC ARRAY['x'::text]); -- fail, type mismatch DROP FUNCTION formarray (anyelement, VARIADIC anyarray); -- test pg_typeof() function SELECT pg_typeof(NULL); -- unknown SELECT pg_typeof(0); -- integer SELECT pg_typeof(0.0); -- numeric SELECT pg_typeof(1 + 1 = 2); -- boolean SELECT pg_typeof('x'); -- unknown SELECT pg_typeof('' || ''); -- text SELECT pg_typeof(pg_typeof(0)); -- regtype SELECT pg_typeof(ARRAY[1.2, 55.5]); -- numeric[] SELECT pg_typeof(myleast (10, 1, 20, 33)); -- polymorphic input -- test functions with default parameters -- test basic functionality CREATE FUNCTION dfunc (a int = 1, int = 2) RETURNS int AS $$ SELECT $1 + $2; $$ LANGUAGE sql; SELECT dfunc (); SELECT dfunc (10); SELECT dfunc (10, 20); SELECT dfunc (10, 20, 30); -- fail DROP FUNCTION dfunc (); -- fail DROP FUNCTION dfunc (int); -- fail DROP FUNCTION dfunc (int, int); -- ok -- fail: defaults must be at end of argument list CREATE FUNCTION dfunc (a int = 1, b int) RETURNS int AS $$ SELECT $1 + $2; $$ LANGUAGE sql; -- however, this should work: CREATE FUNCTION dfunc (a int = 1, out sum int, b int = 2 ) AS $$ SELECT $1 + $2; $$ LANGUAGE sql; SELECT dfunc (); -- verify it lists properly \df dfunc DROP FUNCTION dfunc (int, int); -- check implicit coercion CREATE FUNCTION dfunc (a int DEFAULT 1.0, int DEFAULT '-1') RETURNS int AS $$ SELECT $1 + $2; $$ LANGUAGE sql; SELECT dfunc (); CREATE FUNCTION dfunc (a text DEFAULT 'Hello', b text DEFAULT 'World') RETURNS text AS $$ SELECT $1 || ', ' || $2; $$ LANGUAGE sql; SELECT dfunc (); -- fail: which dfunc should be called? int or text SELECT dfunc ('Hi'); -- ok SELECT dfunc ('Hi', 'City'); -- ok SELECT dfunc (0); -- ok SELECT dfunc (10, 20); -- ok DROP FUNCTION dfunc (int, int); DROP FUNCTION dfunc (text, text); CREATE FUNCTION dfunc (int = 1, int = 2) RETURNS int AS $$ SELECT 2; $$ LANGUAGE sql; CREATE FUNCTION dfunc (int = 1, int = 2, int = 3, int = 4) RETURNS int AS $$ SELECT 4; $$ LANGUAGE sql; -- Now, dfunc(nargs = 2) and dfunc(nargs = 4) are ambiguous when called -- with 0 to 2 arguments. SELECT dfunc (); -- fail SELECT dfunc (1); -- fail SELECT dfunc (1, 2); -- fail SELECT dfunc (1, 2, 3); -- ok SELECT dfunc (1, 2, 3, 4); -- ok DROP FUNCTION dfunc (int, int); DROP FUNCTION dfunc (int, int, int, int); -- default values are not allowed for output parameters CREATE FUNCTION dfunc (out int = 20) RETURNS int AS $$ SELECT 1; $$ LANGUAGE sql; -- polymorphic parameter test CREATE FUNCTION dfunc (anyelement = 'World' ::text) RETURNS text AS $$ SELECT 'Hello, ' || $1::text; $$ LANGUAGE sql; SELECT dfunc (); SELECT dfunc (0); SELECT dfunc (to_date('20081215', 'YYYYMMDD')); SELECT dfunc ('City'::text); DROP FUNCTION dfunc (anyelement); -- check defaults for variadics CREATE FUNCTION dfunc (a VARIADIC int[]) RETURNS int AS $$ SELECT array_upper($1, 1) $$ LANGUAGE sql; SELECT dfunc (); -- fail SELECT dfunc (10); SELECT dfunc (10, 20); CREATE OR REPLACE FUNCTION dfunc (a VARIADIC int[] DEFAULT ARRAY[] ::int[]) RETURNS int AS $$ SELECT array_upper($1, 1) $$ LANGUAGE sql; SELECT dfunc (); -- now ok SELECT dfunc (10); SELECT dfunc (10, 20); -- can't remove the default once it exists CREATE OR REPLACE FUNCTION dfunc (a VARIADIC int[]) RETURNS int AS $$ SELECT array_upper($1, 1) $$ LANGUAGE sql; \df dfunc DROP FUNCTION dfunc (a VARIADIC int[]); -- Ambiguity should be reported only if there's not a better match available CREATE FUNCTION dfunc (int = 1, int = 2, int = 3) RETURNS int AS $$ SELECT 3; $$ LANGUAGE sql; CREATE FUNCTION dfunc (int = 1, int = 2) RETURNS int AS $$ SELECT 2; $$ LANGUAGE sql; CREATE FUNCTION dfunc (text) RETURNS text AS $$ SELECT $1; $$ LANGUAGE sql; -- dfunc(narg=2) and dfunc(narg=3) are ambiguous SELECT dfunc (1); -- fail -- but this works since the ambiguous functions aren't preferred anyway SELECT dfunc ('Hi'); DROP FUNCTION dfunc (int, int, int); DROP FUNCTION dfunc (int, int); DROP FUNCTION dfunc (text); -- -- Tests for named- and mixed-notation function calling -- CREATE FUNCTION dfunc (a int, b int, c int = 0, d int = 0) RETURNS TABLE ( a int, b int, c int, d int ) AS $$ SELECT $1, $2, $3, $4; $$ LANGUAGE sql; SELECT (dfunc (10, 20, 30)).*; SELECT (dfunc (a := 10, b := 20, c := 30)).*; SELECT * FROM dfunc (a := 10, b := 20); SELECT * FROM dfunc (b := 10, a := 20); SELECT * FROM dfunc (0); -- fail SELECT * FROM dfunc (1, 2); SELECT * FROM dfunc (1, 2, c := 3); SELECT * FROM dfunc (1, 2, d := 3); SELECT * FROM dfunc (x := 20, b := 10, x := 30); -- fail, duplicate name SELECT * FROM dfunc (10, b := 20, 30); -- fail, named args must be last SELECT * FROM dfunc (x := 10, b := 20, c := 30); -- fail, unknown param SELECT * FROM dfunc (10, 10, a := 20); -- fail, a overlaps positional parameter SELECT * FROM dfunc (1, c := 2, d := 3); -- fail, no value for b DROP FUNCTION dfunc (int, int, int, int); -- test with different parameter types CREATE FUNCTION dfunc (a varchar, b numeric, c date = CURRENT_DATE) RETURNS TABLE ( a varchar, b numeric, c date ) AS $$ SELECT $1, $2, $3; $$ LANGUAGE sql; SELECT (dfunc ('Hello World', 20, '2009-07-25'::date)).*; SELECT * FROM dfunc ('Hello World', 20, '2009-07-25'::date); SELECT * FROM dfunc (c := '2009-07-25'::date, a := 'Hello World', b := 20); SELECT * FROM dfunc ('Hello World', b := 20, c := '2009-07-25'::date); SELECT * FROM dfunc ('Hello World', c := '2009-07-25'::date, b := 20); SELECT * FROM dfunc ('Hello World', c := 20, b := '2009-07-25'::date); -- fail DROP FUNCTION dfunc (varchar, numeric, date); -- test out parameters with named params CREATE FUNCTION dfunc (a varchar = 'def a', out _a varchar, c numeric = NULL, out _c numeric) RETURNS record AS $$ SELECT $1, $2; $$ LANGUAGE sql; SELECT (dfunc ()).*; SELECT * FROM dfunc (); SELECT * FROM dfunc ('Hello', 100); SELECT * FROM dfunc (a := 'Hello', c := 100); SELECT * FROM dfunc (c := 100, a := 'Hello'); SELECT * FROM dfunc ('Hello'); SELECT * FROM dfunc ('Hello', c := 100); SELECT * FROM dfunc (c := 100); -- fail, can no longer change an input parameter's name CREATE OR REPLACE FUNCTION dfunc (a varchar = 'def a', out _a varchar, x numeric = NULL, out _c numeric) RETURNS record AS $$ SELECT $1, $2; $$ LANGUAGE sql; CREATE OR REPLACE FUNCTION dfunc (a varchar = 'def a', out _a varchar, numeric = NULL, out _c numeric) RETURNS record AS $$ SELECT $1, $2; $$ LANGUAGE sql; DROP FUNCTION dfunc (varchar, numeric); --fail, named parameters are not unique CREATE FUNCTION testpolym (a int, a int) RETURNS int AS $$ SELECT 1; $$ LANGUAGE sql; CREATE FUNCTION testpolym (int, out a int, out a int) RETURNS int AS $$ SELECT 1; $$ LANGUAGE sql; CREATE FUNCTION testpolym (out a int, INOUT a int) RETURNS int AS $$ SELECT 1; $$ LANGUAGE sql; CREATE FUNCTION testpolym (a int, INOUT a int) RETURNS int AS $$ SELECT 1; $$ LANGUAGE sql; -- valid CREATE FUNCTION testpolym (a int, out a int) RETURNS int AS $$ SELECT $1; $$ LANGUAGE sql; SELECT testpolym (37); DROP FUNCTION testpolym (int); CREATE FUNCTION testpolym (a int) RETURNS TABLE ( a int ) AS $$ SELECT $1; $$ LANGUAGE sql; SELECT * FROM testpolym (37); DROP FUNCTION testpolym (int); -- test polymorphic params and defaults CREATE FUNCTION dfunc (a anyelement, b anyelement = NULL, flag bool = TRUE) RETURNS anyelement AS $$ SELECT CASE WHEN $3 THEN $1 ELSE $2 END; $$ LANGUAGE sql; SELECT dfunc (1, 2); SELECT dfunc ('a'::text, 'b'); -- positional notation with default SELECT dfunc (a := 1, b := 2); SELECT dfunc (a := 'a'::text, b := 'b'); SELECT dfunc (a := 'a'::text, b := 'b', flag := FALSE); -- named notation SELECT dfunc (b := 'b'::text, a := 'a'); -- named notation with default SELECT dfunc (a := 'a'::text, flag := TRUE); -- named notation with default SELECT dfunc (a := 'a'::text, flag := FALSE); -- named notation with default SELECT dfunc (b := 'b'::text, a := 'a', flag := TRUE); -- named notation SELECT dfunc ('a'::text, 'b', FALSE); -- full positional notation SELECT dfunc ('a'::text, 'b', flag := FALSE); -- mixed notation SELECT dfunc ('a'::text, 'b', TRUE); -- full positional notation SELECT dfunc ('a'::text, 'b', flag := TRUE); -- mixed notation -- ansi/sql syntax SELECT dfunc (a => 1, b => 2); SELECT dfunc (a => 'a'::text, b => 'b'); SELECT dfunc (a => 'a'::text, b => 'b', flag => FALSE); -- named notation SELECT dfunc (b => 'b'::text, a => 'a'); -- named notation with default SELECT dfunc (a => 'a'::text, flag => TRUE); -- named notation with default SELECT dfunc (a => 'a'::text, flag => FALSE); -- named notation with default SELECT dfunc (b => 'b'::text, a => 'a', flag => TRUE); -- named notation SELECT dfunc ('a'::text, 'b', FALSE); -- full positional notation SELECT dfunc ('a'::text, 'b', flag => FALSE); -- mixed notation SELECT dfunc ('a'::text, 'b', TRUE); -- full positional notation SELECT dfunc ('a'::text, 'b', flag => TRUE); -- mixed notation -- this tests lexer edge cases around => SELECT dfunc (a => - 1); SELECT dfunc (a => + 1); SELECT dfunc (a => /**/ 1); SELECT dfunc (a => --comment to be removed by psql 1); -- need DO to protect the -- from psql DO $$ DECLARE r integer; BEGIN SELECT dfunc (a => -- comment 1) INTO r; RAISE info 'r = %', r; END; $$; -- check reverse-listing of named-arg calls CREATE VIEW dfview AS SELECT q1, q2, dfunc (q1, q2, flag := q1 > q2) AS c3, dfunc (q1, flag := q1 < q2, b := q2) AS c4 FROM int8_tbl; SELECT * FROM dfview; \d+ dfview DROP VIEW dfview; DROP FUNCTION dfunc (anyelement, anyelement, bool); pgFormatter-4.2/t/pg-test-files/expected/portals.sql000066400000000000000000000354741361326045100226230ustar00rootroot00000000000000-- -- Cursor regression tests -- BEGIN; DECLARE foo1 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo2 SCROLL CURSOR FOR SELECT * FROM tenk2; DECLARE foo3 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo4 SCROLL CURSOR FOR SELECT * FROM tenk2; DECLARE foo5 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo6 SCROLL CURSOR FOR SELECT * FROM tenk2; DECLARE foo7 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo8 SCROLL CURSOR FOR SELECT * FROM tenk2; DECLARE foo9 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo10 SCROLL CURSOR FOR SELECT * FROM tenk2; DECLARE foo11 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo12 SCROLL CURSOR FOR SELECT * FROM tenk2; DECLARE foo13 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo14 SCROLL CURSOR FOR SELECT * FROM tenk2; DECLARE foo15 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo16 SCROLL CURSOR FOR SELECT * FROM tenk2; DECLARE foo17 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo18 SCROLL CURSOR FOR SELECT * FROM tenk2; DECLARE foo19 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo20 SCROLL CURSOR FOR SELECT * FROM tenk2; DECLARE foo21 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; DECLARE foo22 SCROLL CURSOR FOR SELECT * FROM tenk2; DECLARE foo23 SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; FETCH 1 IN foo1; FETCH 2 IN foo2; FETCH 3 IN foo3; FETCH 4 IN foo4; FETCH 5 IN foo5; FETCH 6 IN foo6; FETCH 7 IN foo7; FETCH 8 IN foo8; FETCH 9 IN foo9; FETCH 10 IN foo10; FETCH 11 IN foo11; FETCH 12 IN foo12; FETCH 13 IN foo13; FETCH 14 IN foo14; FETCH 15 IN foo15; FETCH 16 IN foo16; FETCH 17 IN foo17; FETCH 18 IN foo18; FETCH 19 IN foo19; FETCH 20 IN foo20; FETCH 21 IN foo21; FETCH 22 IN foo22; FETCH 23 IN foo23; FETCH BACKWARD 1 IN foo23; FETCH BACKWARD 2 IN foo22; FETCH BACKWARD 3 IN foo21; FETCH BACKWARD 4 IN foo20; FETCH BACKWARD 5 IN foo19; FETCH BACKWARD 6 IN foo18; FETCH BACKWARD 7 IN foo17; FETCH BACKWARD 8 IN foo16; FETCH BACKWARD 9 IN foo15; FETCH BACKWARD 10 IN foo14; FETCH BACKWARD 11 IN foo13; FETCH BACKWARD 12 IN foo12; FETCH BACKWARD 13 IN foo11; FETCH BACKWARD 14 IN foo10; FETCH BACKWARD 15 IN foo9; FETCH BACKWARD 16 IN foo8; FETCH BACKWARD 17 IN foo7; FETCH BACKWARD 18 IN foo6; FETCH BACKWARD 19 IN foo5; FETCH BACKWARD 20 IN foo4; FETCH BACKWARD 21 IN foo3; FETCH BACKWARD 22 IN foo2; FETCH BACKWARD 23 IN foo1; CLOSE foo1; CLOSE foo2; CLOSE foo3; CLOSE foo4; CLOSE foo5; CLOSE foo6; CLOSE foo7; CLOSE foo8; CLOSE foo9; CLOSE foo10; CLOSE foo11; CLOSE foo12; -- leave some cursors open, to test that auto-close works. -- record this in the system view as well (don't query the time field there -- however) SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors ORDER BY 1; END; SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; -- -- NO SCROLL disallows backward fetching -- BEGIN; DECLARE foo24 NO SCROLL CURSOR FOR SELECT * FROM tenk1 ORDER BY unique2; FETCH 1 FROM foo24; FETCH BACKWARD 1 FROM foo24; -- should fail END; -- -- Cursors outside transaction blocks -- SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; BEGIN; DECLARE foo25 SCROLL CURSOR WITH HOLD FOR SELECT * FROM tenk2; FETCH FROM foo25; FETCH FROM foo25; COMMIT; FETCH FROM foo25; FETCH BACKWARD FROM foo25; FETCH ABSOLUTE - 1 FROM foo25; SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; CLOSE foo25; -- -- ROLLBACK should close holdable cursors -- BEGIN; DECLARE foo26 CURSOR WITH HOLD FOR SELECT * FROM tenk1 ORDER BY unique2; ROLLBACK; -- should fail FETCH FROM foo26; -- -- Parameterized DECLARE needs to insert param values into the cursor portal -- BEGIN; CREATE FUNCTION declares_cursor (text) RETURNS void AS 'DECLARE c CURSOR FOR SELECT stringu1 FROM tenk1 WHERE stringu1 LIKE $1;' LANGUAGE SQL; SELECT declares_cursor ('AB%'); FETCH ALL FROM c; ROLLBACK; -- -- Test behavior of both volatile and stable functions inside a cursor; -- in particular we want to see what happens during commit of a holdable -- cursor -- CREATE temp TABLE tt1 ( f1 int ); CREATE FUNCTION count_tt1_v () RETURNS int8 AS 'select count(*) from tt1' LANGUAGE sql VOLATILE; CREATE FUNCTION count_tt1_s () RETURNS int8 AS 'select count(*) from tt1' LANGUAGE sql STABLE; BEGIN; INSERT INTO tt1 VALUES (1); DECLARE c1 CURSOR FOR SELECT count_tt1_v (), count_tt1_s (); INSERT INTO tt1 VALUES (2); FETCH ALL FROM c1; ROLLBACK; BEGIN; INSERT INTO tt1 VALUES (1); DECLARE c2 CURSOR WITH hold FOR SELECT count_tt1_v ( ), count_tt1_s (); INSERT INTO tt1 VALUES (2); COMMIT; DELETE FROM tt1; FETCH ALL FROM c2; DROP FUNCTION count_tt1_v (); DROP FUNCTION count_tt1_s (); -- Create a cursor with the BINARY option and check the pg_cursors view BEGIN; SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; DECLARE bc BINARY CURSOR FOR SELECT * FROM tenk1; SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors ORDER BY 1; ROLLBACK; -- We should not see the portal that is created internally to -- implement EXECUTE in pg_cursors PREPARE cprep AS SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors; EXECUTE cprep; -- test CLOSE ALL; SELECT name FROM pg_cursors ORDER BY 1; CLOSE ALL; SELECT name FROM pg_cursors ORDER BY 1; BEGIN; DECLARE foo1 CURSOR WITH HOLD FOR SELECT 1; DECLARE foo2 CURSOR WITHOUT HOLD FOR SELECT 1; SELECT name FROM pg_cursors ORDER BY 1; CLOSE ALL; SELECT name FROM pg_cursors ORDER BY 1; COMMIT; -- -- Tests for updatable cursors -- CREATE TEMP TABLE uctest ( f1 int, f2 text ); INSERT INTO uctest VALUES (1, 'one'), (2, 'two'), (3, 'three'); SELECT * FROM uctest; -- Check DELETE WHERE CURRENT BEGIN; DECLARE c1 CURSOR FOR SELECT * FROM uctest; FETCH 2 FROM c1; DELETE FROM uctest WHERE CURRENT OF c1; -- should show deletion SELECT * FROM uctest; -- cursor did not move FETCH ALL FROM c1; -- cursor is insensitive MOVE BACKWARD ALL IN c1; FETCH ALL FROM c1; COMMIT; -- should still see deletion SELECT * FROM uctest; -- Check UPDATE WHERE CURRENT; this time use FOR UPDATE BEGIN; DECLARE c1 CURSOR FOR SELECT * FROM uctest FOR UPDATE; FETCH c1; UPDATE uctest SET f1 = 8 WHERE CURRENT OF c1; SELECT * FROM uctest; COMMIT; SELECT * FROM uctest; -- Check repeated-update and update-then-delete cases BEGIN; DECLARE c1 CURSOR FOR SELECT * FROM uctest; FETCH c1; UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; SELECT * FROM uctest; UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; SELECT * FROM uctest; -- insensitive cursor should not show effects of updates or deletes FETCH RELATIVE 0 FROM c1; DELETE FROM uctest WHERE CURRENT OF c1; SELECT * FROM uctest; DELETE FROM uctest WHERE CURRENT OF c1; -- no-op SELECT * FROM uctest; UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- no-op SELECT * FROM uctest; FETCH RELATIVE 0 FROM c1; ROLLBACK; SELECT * FROM uctest; BEGIN; DECLARE c1 CURSOR FOR SELECT * FROM uctest FOR UPDATE; FETCH c1; UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; SELECT * FROM uctest; UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; SELECT * FROM uctest; DELETE FROM uctest WHERE CURRENT OF c1; SELECT * FROM uctest; DELETE FROM uctest WHERE CURRENT OF c1; -- no-op SELECT * FROM uctest; UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- no-op SELECT * FROM uctest; --- sensitive cursors can't currently scroll back, so this is an error: FETCH RELATIVE 0 FROM c1; ROLLBACK; SELECT * FROM uctest; -- Check inheritance cases CREATE TEMP TABLE ucchild () INHERITS ( uctest ); INSERT INTO ucchild VALUES (100, 'hundred'); SELECT * FROM uctest; BEGIN; DECLARE c1 CURSOR FOR SELECT * FROM uctest FOR UPDATE; FETCH 1 FROM c1; UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; FETCH 1 FROM c1; UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; FETCH 1 FROM c1; UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; FETCH 1 FROM c1; COMMIT; SELECT * FROM uctest; -- Can update from a self-join, but only if FOR UPDATE says which to use BEGIN; DECLARE c1 CURSOR FOR SELECT * FROM uctest a, uctest b WHERE a.f1 = b.f1 + 5; FETCH 1 FROM c1; UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- fail ROLLBACK; BEGIN; DECLARE c1 CURSOR FOR SELECT * FROM uctest a, uctest b WHERE a.f1 = b.f1 + 5 FOR UPDATE; FETCH 1 FROM c1; UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- fail ROLLBACK; BEGIN; DECLARE c1 CURSOR FOR SELECT * FROM uctest a, uctest b WHERE a.f1 = b.f1 + 5 FOR SHARE OF a; FETCH 1 FROM c1; UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; SELECT * FROM uctest; ROLLBACK; -- Check various error cases DELETE FROM uctest WHERE CURRENT OF c1; -- fail, no such cursor DECLARE cx CURSOR WITH HOLD FOR SELECT * FROM uctest; DELETE FROM uctest WHERE CURRENT OF cx; -- fail, can't use held cursor BEGIN; DECLARE c CURSOR FOR SELECT * FROM tenk2; DELETE FROM uctest WHERE CURRENT OF c; -- fail, cursor on wrong table ROLLBACK; BEGIN; DECLARE c CURSOR FOR SELECT * FROM tenk2 FOR SHARE; DELETE FROM uctest WHERE CURRENT OF c; -- fail, cursor on wrong table ROLLBACK; BEGIN; DECLARE c CURSOR FOR SELECT * FROM tenk1 JOIN tenk2 USING (unique1); DELETE FROM tenk1 WHERE CURRENT OF c; -- fail, cursor is on a join ROLLBACK; BEGIN; DECLARE c CURSOR FOR SELECT f1, count(*) FROM uctest GROUP BY f1; DELETE FROM uctest WHERE CURRENT OF c; -- fail, cursor is on aggregation ROLLBACK; BEGIN; DECLARE c1 CURSOR FOR SELECT * FROM uctest; DELETE FROM uctest WHERE CURRENT OF c1; -- fail, no current row ROLLBACK; BEGIN; DECLARE c1 CURSOR FOR SELECT MIN(f1) FROM uctest FOR UPDATE; ROLLBACK; -- WHERE CURRENT OF may someday work with views, but today is not that day. -- For now, just make sure it errors out cleanly. CREATE TEMP VIEW ucview AS SELECT * FROM uctest; CREATE RULE ucrule AS ON DELETE TO ucview DO INSTEAD DELETE FROM uctest WHERE f1 = OLD.f1; BEGIN; DECLARE c1 CURSOR FOR SELECT * FROM ucview; FETCH FROM c1; DELETE FROM ucview WHERE CURRENT OF c1; -- fail, views not supported ROLLBACK; -- Check WHERE CURRENT OF with an index-only scan BEGIN; EXPLAIN ( COSTS OFF ) DECLARE c1 CURSOR FOR SELECT stringu1 FROM onek WHERE stringu1 = 'DZAAAA'; DECLARE c1 CURSOR FOR SELECT stringu1 FROM onek WHERE stringu1 = 'DZAAAA'; FETCH FROM c1; DELETE FROM onek WHERE CURRENT OF c1; SELECT stringu1 FROM onek WHERE stringu1 = 'DZAAAA'; ROLLBACK; -- Check behavior with rewinding to a previous child scan node, -- as per bug #15395 BEGIN; CREATE TABLE current_check ( currentid int, payload text ); CREATE TABLE current_check_1 () INHERITS ( current_check ); CREATE TABLE current_check_2 () INHERITS ( current_check ); INSERT INTO current_check_1 SELECT i, 'p' || i FROM generate_series(1, 9) i; INSERT INTO current_check_2 SELECT i, 'P' || i FROM generate_series(10, 19) i; DECLARE c1 SCROLL CURSOR FOR SELECT * FROM current_check; -- This tests the fetch-backwards code path FETCH ABSOLUTE 12 FROM c1; FETCH ABSOLUTE 8 FROM c1; DELETE FROM current_check WHERE CURRENT OF c1 RETURNING *; -- This tests the ExecutorRewind code path FETCH ABSOLUTE 13 FROM c1; FETCH ABSOLUTE 1 FROM c1; DELETE FROM current_check WHERE CURRENT OF c1 RETURNING *; SELECT * FROM current_check; ROLLBACK; -- Make sure snapshot management works okay, per bug report in -- 235395b90909301035v7228ce63q392931f15aa74b31@mail.gmail.com BEGIN; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; CREATE TABLE CURSOR ( a int ); INSERT INTO CURSOR VALUES (1); DECLARE c1 NO SCROLL CURSOR FOR SELECT * FROM CURSOR FOR UPDATE; UPDATE CURSOR SET a = 2; FETCH ALL FROM c1; COMMIT; DROP TABLE CURSOR; -- Check rewinding a cursor containing a stable function in LIMIT, -- per bug report in 8336843.9833.1399385291498.JavaMail.root@quick BEGIN; CREATE FUNCTION nochange (int) RETURNS int AS 'select $1 limit 1' LANGUAGE sql STABLE; DECLARE c CURSOR FOR SELECT * FROM int8_tbl LIMIT nochange (3); FETCH ALL FROM c; MOVE BACKWARD ALL IN c; FETCH ALL FROM c; ROLLBACK; -- Check handling of non-backwards-scan-capable plans with scroll cursors BEGIN; EXPLAIN ( COSTS OFF ) DECLARE c1 CURSOR FOR SELECT ( SELECT 42) AS x; EXPLAIN ( COSTS OFF ) DECLARE c1 SCROLL CURSOR FOR SELECT ( SELECT 42) AS x; DECLARE c1 SCROLL CURSOR FOR SELECT ( SELECT 42) AS x; FETCH ALL IN c1; FETCH BACKWARD ALL IN c1; ROLLBACK; BEGIN; EXPLAIN ( COSTS OFF ) DECLARE c2 CURSOR FOR SELECT generate_series(1, 3) AS g; EXPLAIN ( COSTS OFF ) DECLARE c2 SCROLL CURSOR FOR SELECT generate_series(1, 3) AS g; DECLARE c2 SCROLL CURSOR FOR SELECT generate_series(1, 3) AS g; FETCH ALL IN c2; FETCH BACKWARD ALL IN c2; ROLLBACK; pgFormatter-4.2/t/pg-test-files/expected/portals_p2.sql000066400000000000000000000033441361326045100232130ustar00rootroot00000000000000-- -- PORTALS_P2 -- BEGIN; DECLARE foo13 CURSOR FOR SELECT * FROM onek WHERE unique1 = 50; DECLARE foo14 CURSOR FOR SELECT * FROM onek WHERE unique1 = 51; DECLARE foo15 CURSOR FOR SELECT * FROM onek WHERE unique1 = 52; DECLARE foo16 CURSOR FOR SELECT * FROM onek WHERE unique1 = 53; DECLARE foo17 CURSOR FOR SELECT * FROM onek WHERE unique1 = 54; DECLARE foo18 CURSOR FOR SELECT * FROM onek WHERE unique1 = 55; DECLARE foo19 CURSOR FOR SELECT * FROM onek WHERE unique1 = 56; DECLARE foo20 CURSOR FOR SELECT * FROM onek WHERE unique1 = 57; DECLARE foo21 CURSOR FOR SELECT * FROM onek WHERE unique1 = 58; DECLARE foo22 CURSOR FOR SELECT * FROM onek WHERE unique1 = 59; DECLARE foo23 CURSOR FOR SELECT * FROM onek WHERE unique1 = 60; DECLARE foo24 CURSOR FOR SELECT * FROM onek2 WHERE unique1 = 50; DECLARE foo25 CURSOR FOR SELECT * FROM onek2 WHERE unique1 = 60; FETCH ALL IN foo13; FETCH ALL IN foo14; FETCH ALL IN foo15; FETCH ALL IN foo16; FETCH ALL IN foo17; FETCH ALL IN foo18; FETCH ALL IN foo19; FETCH ALL IN foo20; FETCH ALL IN foo21; FETCH ALL IN foo22; FETCH ALL IN foo23; FETCH ALL IN foo24; FETCH ALL IN foo25; CLOSE foo13; CLOSE foo14; CLOSE foo15; CLOSE foo16; CLOSE foo17; CLOSE foo18; CLOSE foo19; CLOSE foo20; CLOSE foo21; CLOSE foo22; CLOSE foo23; CLOSE foo24; CLOSE foo25; END; pgFormatter-4.2/t/pg-test-files/expected/prepare.sql000066400000000000000000000050371361326045100225650ustar00rootroot00000000000000-- Regression tests for prepareable statements. We query the content -- of the pg_prepared_statements view as prepared statements are -- created and removed. SELECT name, statement, parameter_types FROM pg_prepared_statements; PREPARE q1 AS SELECT 1 AS a; EXECUTE q1; SELECT name, statement, parameter_types FROM pg_prepared_statements; -- should fail PREPARE q1 AS SELECT 2; -- should succeed DEALLOCATE q1; PREPARE q1 AS SELECT 2; EXECUTE q1; PREPARE q2 AS SELECT 2 AS b; SELECT name, statement, parameter_types FROM pg_prepared_statements; -- sql92 syntax DEALLOCATE PREPARE q1; SELECT name, statement, parameter_types FROM pg_prepared_statements; DEALLOCATE PREPARE q2; -- the view should return the empty set again SELECT name, statement, parameter_types FROM pg_prepared_statements; -- parameterized queries PREPARE q2 (text) AS SELECT datname, datistemplate, datallowconn FROM pg_database WHERE datname = $1; EXECUTE q2 ('postgres'); PREPARE q3 (text, int, float, boolean, smallint) AS SELECT * FROM tenk1 WHERE string4 = $1 AND (four = $2 OR ten = $3::bigint OR TRUE = $4 OR odd = $5::int) ORDER BY unique1; EXECUTE q3 ('AAAAxx', 5::smallint, 10.5::float, FALSE, 4::bigint); -- too few params EXECUTE q3 ('bool'); -- too many params EXECUTE q3 ('bytea', 5::smallint, 10.5::float, FALSE, 4::bigint, TRUE); -- wrong param types EXECUTE q3 (5::smallint, 10.5::float, FALSE, 4::bigint, 'bytea'); -- invalid type PREPARE q4 (nonexistenttype) AS SELECT $1; -- create table as execute PREPARE q5 (int, text) AS SELECT * FROM tenk1 WHERE unique1 = $1 OR stringu1 = $2 ORDER BY unique1; CREATE TEMPORARY TABLE q5_prep_results AS EXECUTE q5 ( 200, 'DTAAAA' ); SELECT * FROM q5_prep_results; CREATE TEMPORARY TABLE q5_prep_nodata AS EXECUTE q5 ( 200, 'DTAAAA' ) WITH NO DATA; SELECT * FROM q5_prep_nodata; -- unknown or unspecified parameter types: should succeed PREPARE q6 AS SELECT * FROM tenk1 WHERE unique1 = $1 AND stringu1 = $2; PREPARE q7 (unknown) AS SELECT * FROM road WHERE thepath = $1; SELECT name, statement, parameter_types FROM pg_prepared_statements ORDER BY name; -- test DEALLOCATE ALL; DEALLOCATE ALL; SELECT name, statement, parameter_types FROM pg_prepared_statements ORDER BY name; pgFormatter-4.2/t/pg-test-files/expected/prepared_xacts.sql000066400000000000000000000100161361326045100241240ustar00rootroot00000000000000-- -- PREPARED TRANSACTIONS (two-phase commit) -- -- We can't readily test persistence of prepared xacts within the -- regression script framework, unfortunately. Note that a crash -- isn't really needed ... stopping and starting the postmaster would -- be enough, but we can't even do that here. -- create a simple table that we'll use in the tests CREATE TABLE pxtest1 ( foobar varchar(10) ); INSERT INTO pxtest1 VALUES ('aaa'); -- Test PREPARE TRANSACTION BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; UPDATE pxtest1 SET foobar = 'bbb' WHERE foobar = 'aaa'; SELECT * FROM pxtest1; PREPARE TRANSACTION 'foo1'; SELECT * FROM pxtest1; -- Test pg_prepared_xacts system view SELECT gid FROM pg_prepared_xacts; -- Test ROLLBACK PREPARED ROLLBACK PREPARED 'foo1'; SELECT * FROM pxtest1; SELECT gid FROM pg_prepared_xacts; -- Test COMMIT PREPARED BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; INSERT INTO pxtest1 VALUES ('ddd'); SELECT * FROM pxtest1; PREPARE TRANSACTION 'foo2'; SELECT * FROM pxtest1; COMMIT PREPARED 'foo2'; SELECT * FROM pxtest1; -- Test duplicate gids BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; UPDATE pxtest1 SET foobar = 'eee' WHERE foobar = 'ddd'; SELECT * FROM pxtest1; PREPARE TRANSACTION 'foo3'; SELECT gid FROM pg_prepared_xacts; BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; INSERT INTO pxtest1 VALUES ('fff'); -- This should fail, because the gid foo3 is already in use PREPARE TRANSACTION 'foo3'; SELECT * FROM pxtest1; ROLLBACK PREPARED 'foo3'; SELECT * FROM pxtest1; -- Test serialization failure (SSI) BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; UPDATE pxtest1 SET foobar = 'eee' WHERE foobar = 'ddd'; SELECT * FROM pxtest1; PREPARE TRANSACTION 'foo4'; SELECT gid FROM pg_prepared_xacts; BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; SELECT * FROM pxtest1; -- This should fail, because the two transactions have a write-skew anomaly INSERT INTO pxtest1 VALUES ('fff'); PREPARE TRANSACTION 'foo5'; SELECT gid FROM pg_prepared_xacts; ROLLBACK PREPARED 'foo4'; SELECT gid FROM pg_prepared_xacts; -- Clean up DROP TABLE pxtest1; -- Test subtransactions BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; CREATE TABLE pxtest2 ( a int ); INSERT INTO pxtest2 VALUES (1); SAVEPOINT a; INSERT INTO pxtest2 VALUES (2); ROLLBACK TO a; SAVEPOINT b; INSERT INTO pxtest2 VALUES (3); PREPARE TRANSACTION 'regress-one'; CREATE TABLE pxtest3 ( fff int ); -- Test shared invalidation BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; DROP TABLE pxtest3; CREATE TABLE pxtest4 ( a int ); INSERT INTO pxtest4 VALUES (1); INSERT INTO pxtest4 VALUES (2); DECLARE foo CURSOR FOR SELECT * FROM pxtest4; -- Fetch 1 tuple, keeping the cursor open FETCH 1 FROM foo; PREPARE TRANSACTION 'regress-two'; -- No such cursor FETCH 1 FROM foo; -- Table doesn't exist, the creation hasn't been committed yet SELECT * FROM pxtest2; -- There should be two prepared transactions SELECT gid FROM pg_prepared_xacts; -- pxtest3 should be locked because of the pending DROP BEGIN; LOCK TABLE pxtest3 IN access share mode NOWAIT; ROLLBACK; -- Disconnect, we will continue testing in a different backend \c - -- There should still be two prepared transactions SELECT gid FROM pg_prepared_xacts; -- pxtest3 should still be locked because of the pending DROP BEGIN; LOCK TABLE pxtest3 IN access share mode NOWAIT; ROLLBACK; -- Commit table creation COMMIT PREPARED 'regress-one'; \d pxtest2 SELECT * FROM pxtest2; -- There should be one prepared transaction SELECT gid FROM pg_prepared_xacts; -- Commit table drop COMMIT PREPARED 'regress-two'; SELECT * FROM pxtest3; -- There should be no prepared transactions SELECT gid FROM pg_prepared_xacts; -- Clean up DROP TABLE pxtest2; DROP TABLE pxtest3; -- will still be there if prepared xacts are disabled DROP TABLE pxtest4; pgFormatter-4.2/t/pg-test-files/expected/privileges.sql000066400000000000000000001430171361326045100233010ustar00rootroot00000000000000-- -- Test access privileges -- -- Clean up in case a prior regression run failed -- Suppress NOTICE messages when users/groups don't exist SET client_min_messages TO 'warning'; DROP ROLE IF EXISTS regress_priv_group1; DROP ROLE IF EXISTS regress_priv_group2; DROP ROLE IF EXISTS regress_priv_user1; DROP ROLE IF EXISTS regress_priv_user2; DROP ROLE IF EXISTS regress_priv_user3; DROP ROLE IF EXISTS regress_priv_user4; DROP ROLE IF EXISTS regress_priv_user5; DROP ROLE IF EXISTS regress_priv_user6; SELECT lo_unlink(oid) FROM pg_largeobject_metadata WHERE oid >= 1000 AND oid < 3000 ORDER BY oid; RESET client_min_messages; -- test proper begins here CREATE USER regress_priv_user1; CREATE USER regress_priv_user2; CREATE USER regress_priv_user3; CREATE USER regress_priv_user4; CREATE USER regress_priv_user5; CREATE USER regress_priv_user5; -- duplicate CREATE GROUP regress_priv_group1; CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2; ALTER GROUP regress_priv_group1 ADD USER regress_priv_user4; ALTER GROUP regress_priv_group2 ADD USER regress_priv_user2; -- duplicate ALTER GROUP regress_priv_group2 DROP USER regress_priv_user2; GRANT regress_priv_group2 TO regress_priv_user4 WITH ADMIN OPTION; -- test owner privileges SET SESSION AUTHORIZATION regress_priv_user1; SELECT SESSION_USER, CURRENT_USER; CREATE TABLE atest1 ( a int, b text ); SELECT * FROM atest1; INSERT INTO atest1 VALUES (1, 'one'); DELETE FROM atest1; UPDATE atest1 SET a = 1 WHERE b = 'blech'; TRUNCATE atest1; BEGIN; LOCK atest1 IN ACCESS EXCLUSIVE MODE; COMMIT; REVOKE ALL ON atest1 FROM PUBLIC; SELECT * FROM atest1; GRANT ALL ON atest1 TO regress_priv_user2; GRANT SELECT ON atest1 TO regress_priv_user3, regress_priv_user4; SELECT * FROM atest1; CREATE TABLE atest2 ( col1 varchar(10), col2 boolean ); GRANT SELECT ON atest2 TO regress_priv_user2; GRANT UPDATE ON atest2 TO regress_priv_user3; GRANT INSERT ON atest2 TO regress_priv_user4; GRANT TRUNCATE ON atest2 TO regress_priv_user5; SET SESSION AUTHORIZATION regress_priv_user2; SELECT SESSION_USER, CURRENT_USER; -- try various combinations of queries on atest1 and atest2 SELECT * FROM atest1; -- ok SELECT * FROM atest2; -- ok INSERT INTO atest1 VALUES (2, 'two'); -- ok INSERT INTO atest2 VALUES ('foo', TRUE); -- fail INSERT INTO atest1 SELECT 1, b FROM atest1; -- ok UPDATE atest1 SET a = 1 WHERE a = 2; -- ok UPDATE atest2 SET col2 = NOT col2; -- fail SELECT * FROM atest1 FOR UPDATE; -- ok SELECT * FROM atest2 FOR UPDATE; -- fail DELETE FROM atest2; -- fail TRUNCATE atest2; -- fail BEGIN; LOCK atest2 IN ACCESS EXCLUSIVE MODE; -- fail COMMIT; GRANT ALL ON atest1 TO PUBLIC; -- fail -- checks in subquery, both ok SELECT * FROM atest1 WHERE (b IN ( SELECT col1 FROM atest2)); SELECT * FROM atest2 WHERE (col1 IN ( SELECT b FROM atest1)); SET SESSION AUTHORIZATION regress_priv_user3; SELECT SESSION_USER, CURRENT_USER; SELECT * FROM atest1; -- ok SELECT * FROM atest2; -- fail INSERT INTO atest1 VALUES (2, 'two'); -- fail INSERT INTO atest2 VALUES ('foo', TRUE); -- fail INSERT INTO atest1 SELECT 1, b FROM atest1; -- fail UPDATE atest1 SET a = 1 WHERE a = 2; -- fail UPDATE atest2 SET col2 = NULL; -- ok UPDATE atest2 SET col2 = NOT col2; -- fails; requires SELECT on atest2 UPDATE atest2 SET col2 = TRUE FROM atest1 WHERE atest1.a = 5; -- ok SELECT * FROM atest1 FOR UPDATE; -- fail SELECT * FROM atest2 FOR UPDATE; -- fail DELETE FROM atest2; -- fail TRUNCATE atest2; -- fail BEGIN; LOCK atest2 IN ACCESS EXCLUSIVE MODE; -- ok COMMIT; -- checks in subquery, both fail SELECT * FROM atest1 WHERE (b IN ( SELECT col1 FROM atest2)); SELECT * FROM atest2 WHERE (col1 IN ( SELECT b FROM atest1)); SET SESSION AUTHORIZATION regress_priv_user4; SELECT * FROM atest1; -- ok -- test leaky-function protections in selfuncs -- regress_priv_user1 will own a table and provide a view for it. SET SESSION AUTHORIZATION regress_priv_user1; CREATE TABLE atest12 AS SELECT x AS a, 10001 - x AS b FROM generate_series(1, 10000) x; CREATE INDEX ON atest12 (a); CREATE INDEX ON atest12 (abs(a)); VACUUM ANALYZE atest12; CREATE FUNCTION leak (integer, integer) RETURNS boolean AS $$ BEGIN RETURN $1 < $2; END $$ LANGUAGE plpgsql IMMUTABLE; CREATE OPERATOR <<< ( PROCEDURE = leak, LEFTARG = integer, RIGHTARG = integer, RESTRICT = scalarltsel ); -- view with leaky operator CREATE VIEW atest12v AS SELECT * FROM atest12 WHERE b << < 5; GRANT SELECT ON atest12v TO PUBLIC; -- This plan should use nestloop, knowing that few rows will be selected. EXPLAIN ( COSTS OFF ) SELECT * FROM atest12v x, atest12v y WHERE x.a = y.b; -- And this one. EXPLAIN ( COSTS OFF ) SELECT * FROM atest12 x, atest12 y WHERE x.a = y.b AND abs(y.a) << < 5; -- Check if regress_priv_user2 can break security. SET SESSION AUTHORIZATION regress_priv_user2; CREATE FUNCTION leak2 (integer, integer) RETURNS boolean AS $$ BEGIN RAISE notice 'leak % %', $1, $2; RETURN $1 > $2; END $$ LANGUAGE plpgsql IMMUTABLE; CREATE OPERATOR >>> ( PROCEDURE = leak2, LEFTARG = integer, RIGHTARG = integer, RESTRICT = scalargtsel ); -- This should not show any "leak" notices before failing. EXPLAIN ( COSTS OFF ) SELECT * FROM atest12 WHERE a >> > 0; -- This plan should use hashjoin, as it will expect many rows to be selected. EXPLAIN ( COSTS OFF ) SELECT * FROM atest12v x, atest12v y WHERE x.a = y.b; -- Now regress_priv_user1 grants sufficient access to regress_priv_user2. SET SESSION AUTHORIZATION regress_priv_user1; GRANT SELECT (a, b) ON atest12 TO PUBLIC; SET SESSION AUTHORIZATION regress_priv_user2; -- Now regress_priv_user2 will also get a good row estimate. EXPLAIN ( COSTS OFF ) SELECT * FROM atest12v x, atest12v y WHERE x.a = y.b; -- But not for this, due to lack of table-wide permissions needed -- to make use of the expression index's statistics. EXPLAIN ( COSTS OFF ) SELECT * FROM atest12 x, atest12 y WHERE x.a = y.b AND abs(y.a) << < 5; -- clean up (regress_priv_user1's objects are all dropped later) DROP FUNCTION leak2 (integer, integer) CASCADE; -- groups SET SESSION AUTHORIZATION regress_priv_user3; CREATE TABLE atest3 ( one int, two int, three int ); GRANT DELETE ON atest3 TO GROUP regress_priv_group2; SET SESSION AUTHORIZATION regress_priv_user1; SELECT * FROM atest3; -- fail DELETE FROM atest3; -- ok -- views SET SESSION AUTHORIZATION regress_priv_user3; CREATE VIEW atestv1 AS SELECT * FROM atest1; -- ok /* The next *should* fail, but it's not implemented that way yet. */ CREATE VIEW atestv2 AS SELECT * FROM atest2; CREATE VIEW atestv3 AS SELECT * FROM atest3; -- ok /* Empty view is a corner case that failed in 9.2. */ CREATE VIEW atestv0 AS SELECT 0 AS x WHERE FALSE; -- ok SELECT * FROM atestv1; -- ok SELECT * FROM atestv2; -- fail GRANT SELECT ON atestv1, atestv3 TO regress_priv_user4; GRANT SELECT ON atestv2 TO regress_priv_user2; SET SESSION AUTHORIZATION regress_priv_user4; SELECT * FROM atestv1; -- ok SELECT * FROM atestv2; -- fail SELECT * FROM atestv3; -- ok SELECT * FROM atestv0; -- fail -- Appendrels excluded by constraints failed to check permissions in 8.4-9.2. SELECT * FROM (( SELECT a.q1 AS x FROM int8_tbl a offset 0) UNION ALL ( SELECT b.q2 AS x FROM int8_tbl b offset 0)) ss WHERE FALSE; SET constraint_exclusion = ON; SELECT * FROM (( SELECT a.q1 AS x, random() FROM int8_tbl a WHERE q1 > 0) UNION ALL ( SELECT b.q2 AS x, random() FROM int8_tbl b WHERE q2 > 0)) ss WHERE x < 0; RESET constraint_exclusion; CREATE VIEW atestv4 AS SELECT * FROM atestv3; -- nested view SELECT * FROM atestv4; -- ok GRANT SELECT ON atestv4 TO regress_priv_user2; SET SESSION AUTHORIZATION regress_priv_user2; -- Two complex cases: SELECT * FROM atestv3; -- fail SELECT * FROM atestv4; -- ok (even though regress_priv_user2 cannot access underlying atestv3) SELECT * FROM atest2; -- ok SELECT * FROM atestv2; -- fail (even though regress_priv_user2 can access underlying atest2) -- Test column level permissions SET SESSION AUTHORIZATION regress_priv_user1; CREATE TABLE atest5 ( one int, two int UNIQUE, three int, four int UNIQUE ); CREATE TABLE atest6 ( one int, two int, blue int ); GRANT SELECT (one), INSERT (two), UPDATE (three) ON atest5 TO regress_priv_user4; GRANT ALL (one) ON atest5 TO regress_priv_user3; INSERT INTO atest5 VALUES (1, 2, 3); SET SESSION AUTHORIZATION regress_priv_user4; SELECT * FROM atest5; -- fail SELECT one FROM atest5; -- ok SELECT two FROM atest5; -- fail SELECT atest5 FROM atest5; -- fail SELECT 1 FROM atest5; -- ok SELECT 1 FROM atest5 a JOIN atest5 b USING (one); -- ok SELECT 1 FROM atest5 a JOIN atest5 b USING (two); -- fail SELECT 1 FROM atest5 a NATURAL JOIN atest5 b; -- fail SELECT (j.*) IS NULL FROM (atest5 a JOIN atest5 b USING (one)) j; -- fail SELECT 1 FROM atest5 WHERE two = 2; -- fail SELECT * FROM atest1, atest5; -- fail SELECT atest1.* FROM atest1, atest5; -- ok SELECT atest1.*, atest5.one FROM atest1, atest5; -- ok SELECT atest1.*, atest5.one FROM atest1 JOIN atest5 ON (atest1.a = atest5.two); -- fail SELECT atest1.*, atest5.one FROM atest1 JOIN atest5 ON (atest1.a = atest5.one); -- ok SELECT one, two FROM atest5; -- fail SET SESSION AUTHORIZATION regress_priv_user1; GRANT SELECT (one, two) ON atest6 TO regress_priv_user4; SET SESSION AUTHORIZATION regress_priv_user4; SELECT one, two FROM atest5 NATURAL JOIN atest6; -- fail still SET SESSION AUTHORIZATION regress_priv_user1; GRANT SELECT (two) ON atest5 TO regress_priv_user4; SET SESSION AUTHORIZATION regress_priv_user4; SELECT one, two FROM atest5 NATURAL JOIN atest6; -- ok now -- test column-level privileges for INSERT and UPDATE INSERT INTO atest5 (two) VALUES (3); -- ok INSERT INTO atest5 (three) VALUES (4); -- fail INSERT INTO atest5 VALUES (5, 5, 5); -- fail UPDATE atest5 SET three = 10; -- ok UPDATE atest5 SET one = 8; -- fail UPDATE atest5 SET three = 5, one = 2; -- fail -- Check that column level privs are enforced in RETURNING -- Ok. INSERT INTO atest5 (two) VALUES (6) ON CONFLICT (two) DO UPDATE SET three = 10; -- Error. No SELECT on column three. INSERT INTO atest5 (two) VALUES (6) ON CONFLICT (two) DO UPDATE SET three = 10 RETURNING atest5.three; -- Ok. May SELECT on column "one": INSERT INTO atest5 (two) VALUES (6) ON CONFLICT (two) DO UPDATE SET three = 10 RETURNING atest5.one; -- Check that column level privileges are enforced for EXCLUDED -- Ok. we may select one INSERT INTO atest5 (two) VALUES (6) ON CONFLICT (two) DO UPDATE SET three = EXCLUDED.one; -- Error. No select rights on three INSERT INTO atest5 (two) VALUES (6) ON CONFLICT (two) DO UPDATE SET three = EXCLUDED.three; INSERT INTO atest5 (two) VALUES (6) ON CONFLICT (two) DO UPDATE SET one = 8; -- fails (due to UPDATE) INSERT INTO atest5 (three) VALUES (4) ON CONFLICT (two) DO UPDATE SET three = 10; -- fails (due to INSERT) -- Check that the columns in the inference require select privileges INSERT INTO atest5 (four) VALUES (4); -- fail SET SESSION AUTHORIZATION regress_priv_user1; GRANT INSERT (four) ON atest5 TO regress_priv_user4; SET SESSION AUTHORIZATION regress_priv_user4; INSERT INTO atest5 (four) VALUES (4) ON CONFLICT (four) DO UPDATE SET three = 3; -- fails (due to SELECT) INSERT INTO atest5 (four) VALUES (4) ON CONFLICT ON CONSTRAINT atest5_four_key DO UPDATE SET three = 3; -- fails (due to SELECT) INSERT INTO atest5 (four) VALUES (4); -- ok SET SESSION AUTHORIZATION regress_priv_user1; GRANT SELECT (four) ON atest5 TO regress_priv_user4; SET SESSION AUTHORIZATION regress_priv_user4; INSERT INTO atest5 (four) VALUES (4) ON CONFLICT (four) DO UPDATE SET three = 3; -- ok INSERT INTO atest5 (four) VALUES (4) ON CONFLICT ON CONSTRAINT atest5_four_key DO UPDATE SET three = 3; -- ok SET SESSION AUTHORIZATION regress_priv_user1; REVOKE ALL (one) ON atest5 FROM regress_priv_user4; GRANT SELECT (one, two, blue) ON atest6 TO regress_priv_user4; SET SESSION AUTHORIZATION regress_priv_user4; SELECT one FROM atest5; -- fail UPDATE atest5 SET one = 1; -- fail SELECT atest6 FROM atest6; -- ok -- check error reporting with column privs SET SESSION AUTHORIZATION regress_priv_user1; CREATE TABLE t1 ( c1 int, c2 int, c3 int CHECK (c3 < 5), PRIMARY KEY (c1, c2) ); GRANT SELECT (c1) ON t1 TO regress_priv_user2; GRANT INSERT (c1, c2, c3) ON t1 TO regress_priv_user2; GRANT UPDATE (c1, c2, c3) ON t1 TO regress_priv_user2; -- seed data INSERT INTO t1 VALUES (1, 1, 1); INSERT INTO t1 VALUES (1, 2, 1); INSERT INTO t1 VALUES (2, 1, 2); INSERT INTO t1 VALUES (2, 2, 2); INSERT INTO t1 VALUES (3, 1, 3); SET SESSION AUTHORIZATION regress_priv_user2; INSERT INTO t1 (c1, c2) VALUES (1, 1); -- fail, but row not shown UPDATE t1 SET c2 = 1; -- fail, but row not shown INSERT INTO t1 (c1, c2) VALUES (NULL, NULL); -- fail, but see columns being inserted INSERT INTO t1 (c3) VALUES (NULL); -- fail, but see columns being inserted or have SELECT INSERT INTO t1 (c1) VALUES (5); -- fail, but see columns being inserted or have SELECT UPDATE t1 SET c3 = 10; -- fail, but see columns with SELECT rights, or being modified SET SESSION AUTHORIZATION regress_priv_user1; DROP TABLE t1; -- test column-level privileges when involved with DELETE SET SESSION AUTHORIZATION regress_priv_user1; ALTER TABLE atest6 ADD COLUMN three integer; GRANT DELETE ON atest5 TO regress_priv_user3; GRANT SELECT (two) ON atest5 TO regress_priv_user3; REVOKE ALL (one) ON atest5 FROM regress_priv_user3; GRANT SELECT (one) ON atest5 TO regress_priv_user4; SET SESSION AUTHORIZATION regress_priv_user4; SELECT atest6 FROM atest6; -- fail SELECT one FROM atest5 NATURAL JOIN atest6; -- fail SET SESSION AUTHORIZATION regress_priv_user1; ALTER TABLE atest6 DROP COLUMN three; SET SESSION AUTHORIZATION regress_priv_user4; SELECT atest6 FROM atest6; -- ok SELECT one FROM atest5 NATURAL JOIN atest6; -- ok SET SESSION AUTHORIZATION regress_priv_user1; ALTER TABLE atest6 DROP COLUMN two; REVOKE SELECT (one, blue) ON atest6 FROM regress_priv_user4; SET SESSION AUTHORIZATION regress_priv_user4; SELECT * FROM atest6; -- fail SELECT 1 FROM atest6; -- fail SET SESSION AUTHORIZATION regress_priv_user3; DELETE FROM atest5 WHERE one = 1; -- fail DELETE FROM atest5 WHERE two = 2; -- ok -- check inheritance cases SET SESSION AUTHORIZATION regress_priv_user1; CREATE TABLE atestp1 ( f1 int, f2 int ); CREATE TABLE atestp2 ( fx int, fy int ); CREATE TABLE atestc ( fz int ) INHERITS ( atestp1, atestp2 ); GRANT SELECT (fx, fy, tableoid) ON atestp2 TO regress_priv_user2; GRANT SELECT (fx) ON atestc TO regress_priv_user2; SET SESSION AUTHORIZATION regress_priv_user2; SELECT fx FROM atestp2; -- ok SELECT fy FROM atestp2; -- ok SELECT atestp2 FROM atestp2; -- ok SELECT tableoid FROM atestp2; -- ok SELECT fy FROM atestc; -- fail SET SESSION AUTHORIZATION regress_priv_user1; GRANT SELECT (fy, tableoid) ON atestc TO regress_priv_user2; SET SESSION AUTHORIZATION regress_priv_user2; SELECT fx FROM atestp2; -- still ok SELECT fy FROM atestp2; -- ok SELECT atestp2 FROM atestp2; -- ok SELECT tableoid FROM atestp2; -- ok -- privileges on functions, languages -- switch to superuser \c - REVOKE ALL PRIVILEGES ON LANGUAGE sql FROM PUBLIC; GRANT USAGE ON LANGUAGE sql TO regress_priv_user1; -- ok GRANT USAGE ON LANGUAGE c TO PUBLIC; -- fail SET SESSION AUTHORIZATION regress_priv_user1; GRANT USAGE ON LANGUAGE sql TO regress_priv_user2; -- fail CREATE FUNCTION priv_testfunc1 (int) RETURNS int AS 'select 2 * $1;' LANGUAGE sql; CREATE FUNCTION priv_testfunc2 (int) RETURNS int AS 'select 3 * $1;' LANGUAGE sql; CREATE AGGREGATE priv_testagg1 (int) ( SFUNC = int4pl, STYPE = int4 ); CREATE PROCEDURE priv_testproc1 (int ) AS 'select $1;' LANGUAGE sql; REVOKE ALL ON FUNCTION priv_testfunc1 (int), priv_testfunc2 (int), priv_testagg1 (int) FROM PUBLIC; GRANT EXECUTE ON FUNCTION priv_testfunc1 (int), priv_testfunc2 (int), priv_testagg1 (int) TO regress_priv_user2; REVOKE ALL ON FUNCTION priv_testproc1 (int) FROM PUBLIC; -- fail, not a function REVOKE ALL ON PROCEDURE priv_testproc1 (int) FROM PUBLIC; GRANT EXECUTE ON PROCEDURE priv_testproc1 (int) TO regress_priv_user2; GRANT USAGE ON FUNCTION priv_testfunc1 (int) TO regress_priv_user3; -- semantic error GRANT USAGE ON FUNCTION priv_testagg1 (int) TO regress_priv_user3; -- semantic error GRANT USAGE ON PROCEDURE priv_testproc1 (int) TO regress_priv_user3; -- semantic error GRANT ALL PRIVILEGES ON FUNCTION priv_testfunc1 (int) TO regress_priv_user4; GRANT ALL PRIVILEGES ON FUNCTION priv_testfunc_nosuch (int) TO regress_priv_user4; GRANT ALL PRIVILEGES ON FUNCTION priv_testagg1 (int) TO regress_priv_user4; GRANT ALL PRIVILEGES ON PROCEDURE priv_testproc1 (int) TO regress_priv_user4; CREATE FUNCTION priv_testfunc4 (boolean) RETURNS text AS 'select col1 from atest2 where col2 = $1;' LANGUAGE sql SECURITY DEFINER; GRANT EXECUTE ON FUNCTION priv_testfunc4 (boolean) TO regress_priv_user3; SET SESSION AUTHORIZATION regress_priv_user2; SELECT priv_testfunc1 (5), priv_testfunc2 (5); -- ok CREATE FUNCTION priv_testfunc3 (int) RETURNS int AS 'select 2 * $1;' LANGUAGE sql; -- fail SELECT priv_testagg1 (x) FROM ( VALUES (1), (2), (3)) _ (x); -- ok CALL priv_testproc1 (6); -- ok SET SESSION AUTHORIZATION regress_priv_user3; SELECT priv_testfunc1 (5); -- fail SELECT priv_testagg1 (x) FROM ( VALUES (1), (2), (3)) _ (x); -- fail CALL priv_testproc1 (6); -- fail SELECT col1 FROM atest2 WHERE col2 = TRUE; -- fail SELECT priv_testfunc4 (TRUE); -- ok SET SESSION AUTHORIZATION regress_priv_user4; SELECT priv_testfunc1 (5); -- ok SELECT priv_testagg1 (x) FROM ( VALUES (1), (2), (3)) _ (x); -- ok CALL priv_testproc1 (6); -- ok DROP FUNCTION priv_testfunc1 (int); -- fail DROP AGGREGATE priv_testagg1 (int); -- fail DROP PROCEDURE priv_testproc1 (int); -- fail \c - DROP FUNCTION priv_testfunc1 (int); -- ok -- restore to sanity GRANT ALL PRIVILEGES ON LANGUAGE sql TO PUBLIC; -- verify privilege checks on array-element coercions BEGIN; SELECT '{1}'::int4[]::int8[]; REVOKE ALL ON FUNCTION int8(integer) FROM PUBLIC; SELECT '{1}'::int4[]::int8[]; --superuser, succeed SET SESSION AUTHORIZATION regress_priv_user4; SELECT '{1}'::int4[]::int8[]; --other user, fail ROLLBACK; -- privileges on types -- switch to superuser \c - CREATE TYPE priv_testtype1 AS ( a int, b text ); REVOKE USAGE ON TYPE priv_testtype1 FROM PUBLIC; GRANT USAGE ON TYPE priv_testtype1 TO regress_priv_user2; GRANT USAGE ON TYPE _priv_testtype1 TO regress_priv_user2; -- fail GRANT USAGE ON DOMAIN priv_testtype1 TO regress_priv_user2; -- fail CREATE DOMAIN priv_testdomain1 AS int; REVOKE USAGE ON DOMAIN priv_testdomain1 FROM PUBLIC; GRANT USAGE ON DOMAIN priv_testdomain1 TO regress_priv_user2; GRANT USAGE ON TYPE priv_testdomain1 TO regress_priv_user2; -- ok SET SESSION AUTHORIZATION regress_priv_user1; -- commands that should fail CREATE AGGREGATE priv_testagg1a (priv_testdomain1) ( SFUNC = int4_sum, STYPE = bigint ); CREATE DOMAIN priv_testdomain2a AS priv_testdomain1; CREATE DOMAIN priv_testdomain3a AS int; CREATE FUNCTION castfunc (int) RETURNS priv_testdomain3a AS $$ SELECT $1::priv_testdomain3a $$ LANGUAGE SQL; CREATE CAST( priv_testdomain1 AS priv_testdomain3a ) WITH FUNCTION castfunc (int); DROP FUNCTION castfunc (int) CASCADE; DROP DOMAIN priv_testdomain3a; CREATE FUNCTION priv_testfunc5a (a priv_testdomain1) RETURNS int LANGUAGE SQL AS $$ SELECT $1 $$; CREATE FUNCTION priv_testfunc6a (b int) RETURNS priv_testdomain1 LANGUAGE SQL AS $$ SELECT $1::priv_testdomain1 $$; CREATE OPERATOR "!+!" ( PROCEDURE = int4pl, LEFTARG = priv_testdomain1, RIGHTARG = priv_testdomain1 ); CREATE TABLE test5a ( a int, b priv_testdomain1 ); CREATE TABLE test6a OF priv_testtype1; CREATE TABLE test10a ( a int[], b priv_testtype1[] ); CREATE TABLE test9a ( a int, b int ); ALTER TABLE test9a ADD COLUMN c priv_testdomain1; ALTER TABLE test9a ALTER COLUMN b TYPE priv_testdomain1; CREATE TYPE test7a AS ( a int, b priv_testdomain1 ); CREATE TYPE test8a AS ( a int, b int ); ALTER TYPE test8a ADD ATTRIBUTE c priv_testdomain1; ALTER TYPE test8a ALTER ATTRIBUTE b TYPE priv_testdomain1; CREATE TABLE test11a AS ( SELECT 1::priv_testdomain1 AS a ); REVOKE ALL ON TYPE priv_testtype1 FROM PUBLIC; SET SESSION AUTHORIZATION regress_priv_user2; -- commands that should succeed CREATE AGGREGATE priv_testagg1b (priv_testdomain1) ( SFUNC = int4_sum, STYPE = bigint ); CREATE DOMAIN priv_testdomain2b AS priv_testdomain1; CREATE DOMAIN priv_testdomain3b AS int; CREATE FUNCTION castfunc (int) RETURNS priv_testdomain3b AS $$ SELECT $1::priv_testdomain3b $$ LANGUAGE SQL; CREATE CAST( priv_testdomain1 AS priv_testdomain3b ) WITH FUNCTION castfunc (int); CREATE FUNCTION priv_testfunc5b (a priv_testdomain1) RETURNS int LANGUAGE SQL AS $$ SELECT $1 $$; CREATE FUNCTION priv_testfunc6b (b int) RETURNS priv_testdomain1 LANGUAGE SQL AS $$ SELECT $1::priv_testdomain1 $$; CREATE OPERATOR !+! ( PROCEDURE = priv_testfunc5b, RIGHTARG = priv_testdomain1 ); CREATE TABLE test5b ( a int, b priv_testdomain1 ); CREATE TABLE test6b OF priv_testtype1; CREATE TABLE test10b ( a int[], b priv_testtype1[] ); CREATE TABLE test9b ( a int, b int ); ALTER TABLE test9b ADD COLUMN c priv_testdomain1; ALTER TABLE test9b ALTER COLUMN b TYPE priv_testdomain1; CREATE TYPE test7b AS ( a int, b priv_testdomain1 ); CREATE TYPE test8b AS ( a int, b int ); ALTER TYPE test8b ADD ATTRIBUTE c priv_testdomain1; ALTER TYPE test8b ALTER ATTRIBUTE b TYPE priv_testdomain1; CREATE TABLE test11b AS ( SELECT 1::priv_testdomain1 AS a ); REVOKE ALL ON TYPE priv_testtype1 FROM PUBLIC; \c - DROP AGGREGATE priv_testagg1b (priv_testdomain1); DROP DOMAIN priv_testdomain2b; DROP OPERATOR !+! ( NONE, priv_testdomain1); DROP FUNCTION priv_testfunc5b (a priv_testdomain1); DROP FUNCTION priv_testfunc6b (b int); DROP TABLE test5b; DROP TABLE test6b; DROP TABLE test9b; DROP TABLE test10b; DROP TYPE test7b; DROP TYPE test8b; DROP CAST(priv_testdomain1 AS priv_testdomain3b); DROP FUNCTION castfunc (int) CASCADE; DROP DOMAIN priv_testdomain3b; DROP TABLE test11b; DROP TYPE priv_testtype1; -- ok DROP DOMAIN priv_testdomain1; -- ok -- truncate SET SESSION AUTHORIZATION regress_priv_user5; TRUNCATE atest2; -- ok TRUNCATE atest3; -- fail -- has_table_privilege function -- bad-input checks SELECT has_table_privilege(NULL, 'pg_authid', 'select'); SELECT has_table_privilege('pg_shad', 'select'); SELECT has_table_privilege('nosuchuser', 'pg_authid', 'select'); SELECT has_table_privilege('pg_authid', 'sel'); SELECT has_table_privilege(- 999999, 'pg_authid', 'update'); SELECT has_table_privilege(1, 'select'); -- superuser \c - SELECT has_table_privilege(CURRENT_USER, 'pg_authid', 'select'); SELECT has_table_privilege(CURRENT_USER, 'pg_authid', 'insert'); SELECT has_table_privilege(t2.oid, 'pg_authid', 'update') FROM ( SELECT oid FROM pg_roles WHERE rolname = CURRENT_USER) AS t2; SELECT has_table_privilege(t2.oid, 'pg_authid', 'delete') FROM ( SELECT oid FROM pg_roles WHERE rolname = CURRENT_USER) AS t2; -- 'rule' privilege no longer exists, but for backwards compatibility -- has_table_privilege still recognizes the keyword and says FALSE SELECT has_table_privilege(CURRENT_USER, t1.oid, 'rule') FROM ( SELECT oid FROM pg_class WHERE relname = 'pg_authid') AS t1; SELECT has_table_privilege(CURRENT_USER, t1.oid, 'references') FROM ( SELECT oid FROM pg_class WHERE relname = 'pg_authid') AS t1; SELECT has_table_privilege(t2.oid, t1.oid, 'select') FROM ( SELECT oid FROM pg_class WHERE relname = 'pg_authid') AS t1, ( SELECT oid FROM pg_roles WHERE rolname = CURRENT_USER) AS t2; SELECT has_table_privilege(t2.oid, t1.oid, 'insert') FROM ( SELECT oid FROM pg_class WHERE relname = 'pg_authid') AS t1, ( SELECT oid FROM pg_roles WHERE rolname = CURRENT_USER) AS t2; SELECT has_table_privilege('pg_authid', 'update'); SELECT has_table_privilege('pg_authid', 'delete'); SELECT has_table_privilege('pg_authid', 'truncate'); SELECT has_table_privilege(t1.oid, 'select') FROM ( SELECT oid FROM pg_class WHERE relname = 'pg_authid') AS t1; SELECT has_table_privilege(t1.oid, 'trigger') FROM ( SELECT oid FROM pg_class WHERE relname = 'pg_authid') AS t1; -- non-superuser SET SESSION AUTHORIZATION regress_priv_user3; SELECT has_table_privilege(CURRENT_USER, 'pg_class', 'select'); SELECT has_table_privilege(CURRENT_USER, 'pg_class', 'insert'); SELECT has_table_privilege(t2.oid, 'pg_class', 'update') FROM ( SELECT oid FROM pg_roles WHERE rolname = CURRENT_USER) AS t2; SELECT has_table_privilege(t2.oid, 'pg_class', 'delete') FROM ( SELECT oid FROM pg_roles WHERE rolname = CURRENT_USER) AS t2; SELECT has_table_privilege(CURRENT_USER, t1.oid, 'references') FROM ( SELECT oid FROM pg_class WHERE relname = 'pg_class') AS t1; SELECT has_table_privilege(t2.oid, t1.oid, 'select') FROM ( SELECT oid FROM pg_class WHERE relname = 'pg_class') AS t1, ( SELECT oid FROM pg_roles WHERE rolname = CURRENT_USER) AS t2; SELECT has_table_privilege(t2.oid, t1.oid, 'insert') FROM ( SELECT oid FROM pg_class WHERE relname = 'pg_class') AS t1, ( SELECT oid FROM pg_roles WHERE rolname = CURRENT_USER) AS t2; SELECT has_table_privilege('pg_class', 'update'); SELECT has_table_privilege('pg_class', 'delete'); SELECT has_table_privilege('pg_class', 'truncate'); SELECT has_table_privilege(t1.oid, 'select') FROM ( SELECT oid FROM pg_class WHERE relname = 'pg_class') AS t1; SELECT has_table_privilege(t1.oid, 'trigger') FROM ( SELECT oid FROM pg_class WHERE relname = 'pg_class') AS t1; SELECT has_table_privilege(CURRENT_USER, 'atest1', 'select'); SELECT has_table_privilege(CURRENT_USER, 'atest1', 'insert'); SELECT has_table_privilege(t2.oid, 'atest1', 'update') FROM ( SELECT oid FROM pg_roles WHERE rolname = CURRENT_USER) AS t2; SELECT has_table_privilege(t2.oid, 'atest1', 'delete') FROM ( SELECT oid FROM pg_roles WHERE rolname = CURRENT_USER) AS t2; SELECT has_table_privilege(CURRENT_USER, t1.oid, 'references') FROM ( SELECT oid FROM pg_class WHERE relname = 'atest1') AS t1; SELECT has_table_privilege(t2.oid, t1.oid, 'select') FROM ( SELECT oid FROM pg_class WHERE relname = 'atest1') AS t1, ( SELECT oid FROM pg_roles WHERE rolname = CURRENT_USER) AS t2; SELECT has_table_privilege(t2.oid, t1.oid, 'insert') FROM ( SELECT oid FROM pg_class WHERE relname = 'atest1') AS t1, ( SELECT oid FROM pg_roles WHERE rolname = CURRENT_USER) AS t2; SELECT has_table_privilege('atest1', 'update'); SELECT has_table_privilege('atest1', 'delete'); SELECT has_table_privilege('atest1', 'truncate'); SELECT has_table_privilege(t1.oid, 'select') FROM ( SELECT oid FROM pg_class WHERE relname = 'atest1') AS t1; SELECT has_table_privilege(t1.oid, 'trigger') FROM ( SELECT oid FROM pg_class WHERE relname = 'atest1') AS t1; -- has_column_privilege function -- bad-input checks (as non-super-user) SELECT has_column_privilege('pg_authid', NULL, 'select'); SELECT has_column_privilege('pg_authid', 'nosuchcol', 'select'); SELECT has_column_privilege(9999, 'nosuchcol', 'select'); SELECT has_column_privilege(9999, 99::int2, 'select'); SELECT has_column_privilege('pg_authid', 99::int2, 'select'); SELECT has_column_privilege(9999, 99::int2, 'select'); CREATE temp TABLE mytable ( f1 int, f2 int, f3 int ); ALTER TABLE mytable DROP COLUMN f2; SELECT has_column_privilege('mytable', 'f2', 'select'); SELECT has_column_privilege('mytable', '........pg.dropped.2........', 'select'); SELECT has_column_privilege('mytable', 2::int2, 'select'); REVOKE SELECT ON TABLE mytable FROM regress_priv_user3; SELECT has_column_privilege('mytable', 2::int2, 'select'); DROP TABLE mytable; -- Grant options SET SESSION AUTHORIZATION regress_priv_user1; CREATE TABLE atest4 ( a int ); GRANT SELECT ON atest4 TO regress_priv_user2 WITH GRANT OPTION; GRANT UPDATE ON atest4 TO regress_priv_user2; GRANT SELECT ON atest4 TO GROUP regress_priv_group1 WITH GRANT OPTION; SET SESSION AUTHORIZATION regress_priv_user2; GRANT SELECT ON atest4 TO regress_priv_user3; GRANT UPDATE ON atest4 TO regress_priv_user3; -- fail SET SESSION AUTHORIZATION regress_priv_user1; REVOKE SELECT ON atest4 FROM regress_priv_user3; -- does nothing SELECT has_table_privilege('regress_priv_user3', 'atest4', 'SELECT'); -- true REVOKE SELECT ON atest4 FROM regress_priv_user2; -- fail REVOKE GRANT OPTION FOR SELECT ON atest4 FROM regress_priv_user2 CASCADE; -- ok SELECT has_table_privilege('regress_priv_user2', 'atest4', 'SELECT'); -- true SELECT has_table_privilege('regress_priv_user3', 'atest4', 'SELECT'); -- false SELECT has_table_privilege('regress_priv_user1', 'atest4', 'SELECT WITH GRANT OPTION'); -- true -- Admin options SET SESSION AUTHORIZATION regress_priv_user4; CREATE FUNCTION dogrant_ok () RETURNS void LANGUAGE sql SECURITY DEFINER AS 'GRANT regress_priv_group2 TO regress_priv_user5' ; GRANT regress_priv_group2 TO regress_priv_user5; -- ok: had ADMIN OPTION SET ROLE regress_priv_group2; GRANT regress_priv_group2 TO regress_priv_user5; -- fails: SET ROLE suspended privilege SET SESSION AUTHORIZATION regress_priv_user1; GRANT regress_priv_group2 TO regress_priv_user5; -- fails: no ADMIN OPTION SELECT dogrant_ok (); -- ok: SECURITY DEFINER conveys ADMIN SET ROLE regress_priv_group2; GRANT regress_priv_group2 TO regress_priv_user5; -- fails: SET ROLE did not help SET SESSION AUTHORIZATION regress_priv_group2; GRANT regress_priv_group2 TO regress_priv_user5; -- ok: a role can self-admin CREATE FUNCTION dogrant_fails () RETURNS void LANGUAGE sql SECURITY DEFINER AS 'GRANT regress_priv_group2 TO regress_priv_user5' ; SELECT dogrant_fails (); -- fails: no self-admin in SECURITY DEFINER DROP FUNCTION dogrant_fails (); SET SESSION AUTHORIZATION regress_priv_user4; DROP FUNCTION dogrant_ok (); REVOKE regress_priv_group2 FROM regress_priv_user5; -- has_sequence_privilege tests \c - CREATE SEQUENCE x_seq; GRANT USAGE ON x_seq TO regress_priv_user2; SELECT has_sequence_privilege('regress_priv_user1', 'atest1', 'SELECT'); SELECT has_sequence_privilege('regress_priv_user1', 'x_seq', 'INSERT'); SELECT has_sequence_privilege('regress_priv_user1', 'x_seq', 'SELECT'); SET SESSION AUTHORIZATION regress_priv_user2; SELECT has_sequence_privilege('x_seq', 'USAGE'); -- largeobject privilege tests \c - SET SESSION AUTHORIZATION regress_priv_user1; SELECT lo_create(1001); SELECT lo_create(1002); SELECT lo_create(1003); SELECT lo_create(1004); SELECT lo_create(1005); GRANT ALL ON LARGE OBJECT 1001 TO PUBLIC; GRANT SELECT ON LARGE OBJECT 1003 TO regress_priv_user2; GRANT SELECT, UPDATE ON LARGE OBJECT 1004 TO regress_priv_user2; GRANT ALL ON LARGE OBJECT 1005 TO regress_priv_user2; GRANT SELECT ON LARGE OBJECT 1005 TO regress_priv_user2 WITH GRANT OPTION; GRANT SELECT, INSERT ON LARGE OBJECT 1001 TO PUBLIC; -- to be failed GRANT SELECT, UPDATE ON LARGE OBJECT 1001 TO nosuchuser; -- to be failed GRANT SELECT, UPDATE ON LARGE OBJECT 999 TO PUBLIC; -- to be failed \c - SET SESSION AUTHORIZATION regress_priv_user2; SELECT lo_create(2001); SELECT lo_create(2002); SELECT loread(lo_open(1001, x '20000'::int), 32); -- allowed, for now SELECT lowrite(lo_open(1001, x '40000'::int), 'abcd'); -- fail, wrong mode SELECT loread(lo_open(1001, x '40000'::int), 32); SELECT loread(lo_open(1002, x '40000'::int), 32); -- to be denied SELECT loread(lo_open(1003, x '40000'::int), 32); SELECT loread(lo_open(1004, x '40000'::int), 32); SELECT lowrite(lo_open(1001, x '20000'::int), 'abcd'); SELECT lowrite(lo_open(1002, x '20000'::int), 'abcd'); -- to be denied SELECT lowrite(lo_open(1003, x '20000'::int), 'abcd'); -- to be denied SELECT lowrite(lo_open(1004, x '20000'::int), 'abcd'); GRANT SELECT ON LARGE OBJECT 1005 TO regress_priv_user3; GRANT UPDATE ON LARGE OBJECT 1006 TO regress_priv_user3; -- to be denied REVOKE ALL ON LARGE OBJECT 2001, 2002 FROM PUBLIC; GRANT ALL ON LARGE OBJECT 2001 TO regress_priv_user3; SELECT lo_unlink(1001); -- to be denied SELECT lo_unlink(2002); \c - -- confirm ACL setting SELECT oid, pg_get_userbyid(lomowner) ownername, lomacl FROM pg_largeobject_metadata WHERE oid >= 1000 AND oid < 3000 ORDER BY oid; SET SESSION AUTHORIZATION regress_priv_user3; SELECT loread(lo_open(1001, x '40000'::int), 32); SELECT loread(lo_open(1003, x '40000'::int), 32); -- to be denied SELECT loread(lo_open(1005, x '40000'::int), 32); SELECT lo_truncate(lo_open(1005, x '20000'::int), 10); -- to be denied SELECT lo_truncate(lo_open(2001, x '20000'::int), 10); -- compatibility mode in largeobject permission \c - SET lo_compat_privileges = FALSE; -- default setting SET SESSION AUTHORIZATION regress_priv_user4; SELECT loread(lo_open(1002, x '40000'::int), 32); -- to be denied SELECT lowrite(lo_open(1002, x '20000'::int), 'abcd'); -- to be denied SELECT lo_truncate(lo_open(1002, x '20000'::int), 10); -- to be denied SELECT lo_put (1002, 1, 'abcd'); -- to be denied SELECT lo_unlink(1002); -- to be denied SELECT lo_export(1001, '/dev/null'); -- to be denied SELECT lo_import('/dev/null'); -- to be denied SELECT lo_import('/dev/null', 2003); -- to be denied \c - SET lo_compat_privileges = TRUE; -- compatibility mode SET SESSION AUTHORIZATION regress_priv_user4; SELECT loread(lo_open(1002, x '40000'::int), 32); SELECT lowrite(lo_open(1002, x '20000'::int), 'abcd'); SELECT lo_truncate(lo_open(1002, x '20000'::int), 10); SELECT lo_unlink(1002); SELECT lo_export(1001, '/dev/null'); -- to be denied -- don't allow unpriv users to access pg_largeobject contents \c - SELECT * FROM pg_largeobject LIMIT 0; SET SESSION AUTHORIZATION regress_priv_user1; SELECT * FROM pg_largeobject LIMIT 0; -- to be denied -- test default ACLs \c - CREATE SCHEMA testns; GRANT ALL ON SCHEMA testns TO regress_priv_user1; CREATE TABLE testns.acltest1 ( x int ); SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'SELECT'); -- no SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'INSERT'); -- no ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT SELECT ON TABLES TO public; SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'SELECT'); -- no SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'INSERT'); -- no DROP TABLE testns.acltest1; CREATE TABLE testns.acltest1 ( x int ); SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'SELECT'); -- yes SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'INSERT'); -- no ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT INSERT ON TABLES TO regress_priv_user1; DROP TABLE testns.acltest1; CREATE TABLE testns.acltest1 ( x int ); SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'SELECT'); -- yes SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'INSERT'); -- yes ALTER DEFAULT PRIVILEGES IN SCHEMA testns REVOKE INSERT ON TABLES FROM regress_priv_user1; DROP TABLE testns.acltest1; CREATE TABLE testns.acltest1 ( x int ); SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'SELECT'); -- yes SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'INSERT'); -- no ALTER DEFAULT PRIVILEGES FOR ROLE regress_priv_user1 REVOKE EXECUTE ON FUNCTIONS FROM public; ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT USAGE ON SCHEMAS TO regress_priv_user2; -- error -- -- Testing blanket default grants is very hazardous since it might change -- the privileges attached to objects created by concurrent regression tests. -- To avoid that, be sure to revoke the privileges again before committing. -- BEGIN; ALTER DEFAULT PRIVILEGES GRANT USAGE ON SCHEMAS TO regress_priv_user2; CREATE SCHEMA testns2; SELECT has_schema_privilege('regress_priv_user2', 'testns2', 'USAGE'); -- yes SELECT has_schema_privilege('regress_priv_user2', 'testns2', 'CREATE'); -- no ALTER DEFAULT PRIVILEGES REVOKE USAGE ON SCHEMAS FROM regress_priv_user2; CREATE SCHEMA testns3; SELECT has_schema_privilege('regress_priv_user2', 'testns3', 'USAGE'); -- no SELECT has_schema_privilege('regress_priv_user2', 'testns3', 'CREATE'); -- no ALTER DEFAULT PRIVILEGES GRANT ALL ON SCHEMAS TO regress_priv_user2; CREATE SCHEMA testns4; SELECT has_schema_privilege('regress_priv_user2', 'testns4', 'USAGE'); -- yes SELECT has_schema_privilege('regress_priv_user2', 'testns4', 'CREATE'); -- yes ALTER DEFAULT PRIVILEGES REVOKE ALL ON SCHEMAS FROM regress_priv_user2; COMMIT; CREATE SCHEMA testns5; SELECT has_schema_privilege('regress_priv_user2', 'testns5', 'USAGE'); -- no SELECT has_schema_privilege('regress_priv_user2', 'testns5', 'CREATE'); -- no SET ROLE regress_priv_user1; CREATE FUNCTION testns.foo () RETURNS int AS 'select 1' LANGUAGE sql; CREATE AGGREGATE testns.agg1 (int) ( SFUNC = int4pl, STYPE = int4 ); CREATE PROCEDURE testns.bar () AS 'select 1' LANGUAGE sql; SELECT has_function_privilege('regress_priv_user2', 'testns.foo()', 'EXECUTE'); -- no SELECT has_function_privilege('regress_priv_user2', 'testns.agg1(int)', 'EXECUTE'); -- no SELECT has_function_privilege('regress_priv_user2', 'testns.bar()', 'EXECUTE'); -- no ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT EXECUTE ON ROUTINES TO public; DROP FUNCTION testns.foo (); CREATE FUNCTION testns.foo () RETURNS int AS 'select 1' LANGUAGE sql; DROP AGGREGATE testns.agg1 (int); CREATE AGGREGATE testns.agg1 (int) ( SFUNC = int4pl, STYPE = int4 ); DROP PROCEDURE testns.bar (); CREATE PROCEDURE testns.bar () AS 'select 1' LANGUAGE sql; SELECT has_function_privilege('regress_priv_user2', 'testns.foo()', 'EXECUTE'); -- yes SELECT has_function_privilege('regress_priv_user2', 'testns.agg1(int)', 'EXECUTE'); -- yes SELECT has_function_privilege('regress_priv_user2', 'testns.bar()', 'EXECUTE'); -- yes (counts as function here) DROP FUNCTION testns.foo (); DROP AGGREGATE testns.agg1 (int); DROP PROCEDURE testns.bar (); ALTER DEFAULT PRIVILEGES FOR ROLE regress_priv_user1 REVOKE USAGE ON TYPES FROM public; CREATE DOMAIN testns.priv_testdomain1 AS int; SELECT has_type_privilege('regress_priv_user2', 'testns.priv_testdomain1', 'USAGE'); -- no ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT USAGE ON TYPES TO public; DROP DOMAIN testns.priv_testdomain1; CREATE DOMAIN testns.priv_testdomain1 AS int; SELECT has_type_privilege('regress_priv_user2', 'testns.priv_testdomain1', 'USAGE'); -- yes DROP DOMAIN testns.priv_testdomain1; RESET ROLE; SELECT count(*) FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid WHERE nspname = 'testns'; DROP SCHEMA testns CASCADE; DROP SCHEMA testns2 CASCADE; DROP SCHEMA testns3 CASCADE; DROP SCHEMA testns4 CASCADE; DROP SCHEMA testns5 CASCADE; SELECT d.* -- check that entries went away FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid WHERE nspname IS NULL AND defaclnamespace != 0; -- Grant on all objects of given type in a schema \c - CREATE SCHEMA testns; CREATE TABLE testns.t1 ( f1 int ); CREATE TABLE testns.t2 ( f1 int ); SELECT has_table_privilege('regress_priv_user1', 'testns.t1', 'SELECT'); -- false GRANT ALL ON ALL TABLES IN SCHEMA testns TO regress_priv_user1; SELECT has_table_privilege('regress_priv_user1', 'testns.t1', 'SELECT'); -- true SELECT has_table_privilege('regress_priv_user1', 'testns.t2', 'SELECT'); -- true REVOKE ALL ON ALL TABLES IN SCHEMA testns FROM regress_priv_user1; SELECT has_table_privilege('regress_priv_user1', 'testns.t1', 'SELECT'); -- false SELECT has_table_privilege('regress_priv_user1', 'testns.t2', 'SELECT'); -- false CREATE FUNCTION testns.priv_testfunc (int) RETURNS int AS 'select 3 * $1;' LANGUAGE sql; CREATE AGGREGATE testns.priv_testagg (int) ( SFUNC = int4pl, STYPE = int4 ); CREATE PROCEDURE testns.priv_testproc (int ) AS 'select 3' LANGUAGE sql; SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testfunc(int)', 'EXECUTE'); -- true by default SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testagg(int)', 'EXECUTE'); -- true by default SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testproc(int)', 'EXECUTE'); -- true by default REVOKE ALL ON ALL FUNCTIONS IN SCHEMA testns FROM PUBLIC; SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testfunc(int)', 'EXECUTE'); -- false SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testagg(int)', 'EXECUTE'); -- false SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testproc(int)', 'EXECUTE'); -- still true, not a function REVOKE ALL ON ALL PROCEDURES IN SCHEMA testns FROM PUBLIC; SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testproc(int)', 'EXECUTE'); -- now false GRANT ALL ON ALL ROUTINES IN SCHEMA testns TO PUBLIC; SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testfunc(int)', 'EXECUTE'); -- true SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testagg(int)', 'EXECUTE'); -- true SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testproc(int)', 'EXECUTE'); -- true DROP SCHEMA testns CASCADE; -- Change owner of the schema & and rename of new schema owner \c - CREATE ROLE regress_schemauser1 superuser LOGIN; CREATE ROLE regress_schemauser2 superuser LOGIN; SET SESSION ROLE regress_schemauser1; CREATE SCHEMA testns; SELECT nspname, rolname FROM pg_namespace, pg_roles WHERE pg_namespace.nspname = 'testns' AND pg_namespace.nspowner = pg_roles.oid; ALTER SCHEMA testns OWNER TO regress_schemauser2; ALTER ROLE regress_schemauser2 RENAME TO regress_schemauser_renamed; SELECT nspname, rolname FROM pg_namespace, pg_roles WHERE pg_namespace.nspname = 'testns' AND pg_namespace.nspowner = pg_roles.oid; SET session ROLE regress_schemauser_renamed; DROP SCHEMA testns CASCADE; -- clean up \c - DROP ROLE regress_schemauser1; DROP ROLE regress_schemauser_renamed; -- test that dependent privileges are revoked (or not) properly \c - SET session ROLE regress_priv_user1; CREATE TABLE dep_priv_test ( a int ); GRANT SELECT ON dep_priv_test TO regress_priv_user2 WITH GRANT option; GRANT SELECT ON dep_priv_test TO regress_priv_user3 WITH GRANT option; SET session ROLE regress_priv_user2; GRANT SELECT ON dep_priv_test TO regress_priv_user4 WITH GRANT option; SET session ROLE regress_priv_user3; GRANT SELECT ON dep_priv_test TO regress_priv_user4 WITH GRANT option; SET session ROLE regress_priv_user4; GRANT SELECT ON dep_priv_test TO regress_priv_user5; \dp dep_priv_test SET session ROLE regress_priv_user2; REVOKE SELECT ON dep_priv_test FROM regress_priv_user4 CASCADE; \dp dep_priv_test SET session ROLE regress_priv_user3; REVOKE SELECT ON dep_priv_test FROM regress_priv_user4 CASCADE; \dp dep_priv_test SET session ROLE regress_priv_user1; DROP TABLE dep_priv_test; -- clean up \c DROP SEQUENCE x_seq; DROP AGGREGATE priv_testagg1 (int); DROP FUNCTION priv_testfunc2 (int); DROP FUNCTION priv_testfunc4 (boolean); DROP PROCEDURE priv_testproc1 (int); DROP VIEW atestv0; DROP VIEW atestv1; DROP VIEW atestv2; -- this should cascade to drop atestv4 DROP VIEW atestv3 CASCADE; -- this should complain "does not exist" DROP VIEW atestv4; DROP TABLE atest1; DROP TABLE atest2; DROP TABLE atest3; DROP TABLE atest4; DROP TABLE atest5; DROP TABLE atest6; DROP TABLE atestc; DROP TABLE atestp1; DROP TABLE atestp2; SELECT lo_unlink(oid) FROM pg_largeobject_metadata WHERE oid >= 1000 AND oid < 3000 ORDER BY oid; DROP GROUP regress_priv_group1; DROP GROUP regress_priv_group2; -- these are needed to clean up permissions REVOKE USAGE ON LANGUAGE sql FROM regress_priv_user1; DROP OWNED BY regress_priv_user1; DROP USER regress_priv_user1; DROP USER regress_priv_user2; DROP USER regress_priv_user3; DROP USER regress_priv_user4; DROP USER regress_priv_user5; DROP USER regress_priv_user6; -- permissions with LOCK TABLE CREATE USER regress_locktable_user; CREATE TABLE lock_table ( a int ); -- LOCK TABLE and SELECT permission GRANT SELECT ON lock_table TO regress_locktable_user; SET SESSION AUTHORIZATION regress_locktable_user; BEGIN; LOCK TABLE lock_table IN ROW EXCLUSIVE MODE; -- should fail ROLLBACK; BEGIN; LOCK TABLE lock_table IN ACCESS SHARE MODE; -- should pass COMMIT; BEGIN; LOCK TABLE lock_table IN ACCESS EXCLUSIVE MODE; -- should fail ROLLBACK; \c REVOKE SELECT ON lock_table FROM regress_locktable_user; -- LOCK TABLE and INSERT permission GRANT INSERT ON lock_table TO regress_locktable_user; SET SESSION AUTHORIZATION regress_locktable_user; BEGIN; LOCK TABLE lock_table IN ROW EXCLUSIVE MODE; -- should pass COMMIT; BEGIN; LOCK TABLE lock_table IN ACCESS SHARE MODE; -- should fail ROLLBACK; BEGIN; LOCK TABLE lock_table IN ACCESS EXCLUSIVE MODE; -- should fail ROLLBACK; \c REVOKE INSERT ON lock_table FROM regress_locktable_user; -- LOCK TABLE and UPDATE permission GRANT UPDATE ON lock_table TO regress_locktable_user; SET SESSION AUTHORIZATION regress_locktable_user; BEGIN; LOCK TABLE lock_table IN ROW EXCLUSIVE MODE; -- should pass COMMIT; BEGIN; LOCK TABLE lock_table IN ACCESS SHARE MODE; -- should fail ROLLBACK; BEGIN; LOCK TABLE lock_table IN ACCESS EXCLUSIVE MODE; -- should pass COMMIT; \c REVOKE UPDATE ON lock_table FROM regress_locktable_user; -- LOCK TABLE and DELETE permission GRANT DELETE ON lock_table TO regress_locktable_user; SET SESSION AUTHORIZATION regress_locktable_user; BEGIN; LOCK TABLE lock_table IN ROW EXCLUSIVE MODE; -- should pass COMMIT; BEGIN; LOCK TABLE lock_table IN ACCESS SHARE MODE; -- should fail ROLLBACK; BEGIN; LOCK TABLE lock_table IN ACCESS EXCLUSIVE MODE; -- should pass COMMIT; \c REVOKE DELETE ON lock_table FROM regress_locktable_user; -- LOCK TABLE and TRUNCATE permission GRANT TRUNCATE ON lock_table TO regress_locktable_user; SET SESSION AUTHORIZATION regress_locktable_user; BEGIN; LOCK TABLE lock_table IN ROW EXCLUSIVE MODE; -- should pass COMMIT; BEGIN; LOCK TABLE lock_table IN ACCESS SHARE MODE; -- should fail ROLLBACK; BEGIN; LOCK TABLE lock_table IN ACCESS EXCLUSIVE MODE; -- should pass COMMIT; \c REVOKE TRUNCATE ON lock_table FROM regress_locktable_user; -- clean up DROP TABLE lock_table; DROP USER regress_locktable_user; pgFormatter-4.2/t/pg-test-files/expected/psql.sql000066400000000000000000000540311361326045100221040ustar00rootroot00000000000000-- -- Tests for psql features that aren't closely connected to any -- specific server features -- -- \set -- fail: invalid name \set invalid/name foo -- fail: invalid value for special variable \set AUTOCOMMIT foo \set FETCH_COUNT foo -- check handling of built-in boolean variable \echo :ON_ERROR_ROLLBACK \set ON_ERROR_ROLLBACK \echo :ON_ERROR_ROLLBACK \set ON_ERROR_ROLLBACK foo \echo :ON_ERROR_ROLLBACK \set ON_ERROR_ROLLBACK on \echo :ON_ERROR_ROLLBACK \unset ON_ERROR_ROLLBACK \echo :ON_ERROR_ROLLBACK -- \g and \gx SELECT 1 AS one, 2 AS two \g \gx SELECT 3 AS three, 4 AS four \gx \g -- \gx should work in FETCH_COUNT mode too \set FETCH_COUNT 1 SELECT 1 AS one, 2 AS two \g \gx SELECT 3 AS three, 4 AS four \gx \g \unset FETCH_COUNT -- \gset SELECT 10 AS test01, 20 AS test02, 'Hello' AS test03 \gset pref01_ \echo :pref01_test01 :pref01_test02 :pref01_test03 -- should fail: bad variable name SELECT 10 AS "bad name" \gset -- multiple backslash commands in one line SELECT 1 AS x, 2 AS y \gset pref01_ \echo :pref01_x SELECT 3 AS x, 4 AS y \gset pref01_ \echo :pref01_x \echo :pref01_y SELECT 5 AS x, 6 AS y \gset pref01_ \g \echo :pref01_x :pref01_y SELECT 7 AS x, 8 AS y \g \gset pref01_ \echo :pref01_x :pref01_y -- NULL should unset the variable \set var2 xyz SELECT 1 AS var1, NULL AS var2, 3 AS var3 \gset \echo :var1 :var2 :var3 -- \gset requires just one tuple SELECT 10 AS test01, 20 AS test02 FROM generate_series(1, 3) \gset SELECT 10 AS test01, 20 AS test02 FROM generate_series(1, 0) \gset -- \gset should work in FETCH_COUNT mode too \set FETCH_COUNT 1 SELECT 1 AS x, 2 AS y \gset pref01_ \echo :pref01_x SELECT 3 AS x, 4 AS y \gset pref01_ \echo :pref01_x \echo :pref01_y SELECT 10 AS test01, 20 AS test02 FROM generate_series(1, 3) \gset SELECT 10 AS test01, 20 AS test02 FROM generate_series(1, 0) \gset \unset FETCH_COUNT -- \gdesc SELECT NULL AS zero, 1 AS one, 2.0 AS two, 'three' AS three, $1 AS four, sin($2) AS five, 'foo'::varchar(4) AS six, CURRENT_DATE AS now gdesc -- should work with tuple-returning utilities, such as EXECUTE PREPARE test AS SELECT 1 AS FIRST, 2 AS second; EXECUTE test gdesc EXPLAIN EXECUTE test gdesc -- should fail cleanly - syntax error SELECT 1 + gdesc -- check behavior with empty results SELECT gdesc CREATE TABLE bububu ( a int) gdesc -- subject command should not have executed TABLE bububu; -- fail -- query buffer should remain unchanged SELECT 1 AS x, 'Hello', 2 AS y, TRUE AS "dirty\name" gdesc \g -- all on one line SELECT 3 AS x, 'Hello', 4 AS y, TRUE AS "dirty\name" gdesc \g -- \gexec CREATE TEMPORARY TABLE gexec_test ( a int, b text, c date, d float ); SELECT format('create index on gexec_test(%I)', attname) FROM pg_attribute WHERE attrelid = 'gexec_test'::regclass AND attnum > 0 ORDER BY attnum \gexec -- \gexec should work in FETCH_COUNT mode too -- (though the fetch limit applies to the executed queries not the meta query) \set FETCH_COUNT 1 SELECT 'select 1 as ones', 'select x.y, x.y*2 as double from generate_series(1,4) as x(y)' UNION ALL SELECT 'drop table gexec_test', NULL UNION ALL SELECT 'drop table gexec_test', 'select ''2000-01-01''::date as party_over' \gexec \unset FETCH_COUNT -- show all pset options \pset -- test multi-line headers, wrapping, and newline indicators -- in aligned, unaligned, and wrapped formats PREPARE q AS SELECT array_to_string(array_agg(repeat('x', 2 * n)), E'\n') AS "ab c", array_to_string(array_agg(repeat('y', 20 - 2 * n)), E'\n') AS "a bc" FROM generate_series(1, 10) AS n (n) GROUP BY n > 1 ORDER BY n > 1; \pset linestyle ascii \pset expanded off \pset columns 40 \pset border 0 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset border 1 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset border 2 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset expanded on \pset columns 20 \pset border 0 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset border 1 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset border 2 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset linestyle old-ascii \pset expanded off \pset columns 40 \pset border 0 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset border 1 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset border 2 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset expanded on \pset columns 20 \pset border 0 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset border 1 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset border 2 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; DEALLOCATE q; -- test single-line header and data PREPARE q AS SELECT repeat('x', 2 * n) AS "0123456789abcdef", repeat('y', 20 - 2 * n) AS "0123456789" FROM generate_series(1, 10) AS n; \pset linestyle ascii \pset expanded off \pset columns 40 \pset border 0 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset border 1 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset border 2 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset expanded on \pset columns 30 \pset border 0 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset border 1 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset border 2 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset expanded on \pset columns 20 \pset border 0 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset border 1 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset border 2 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset linestyle old-ascii \pset expanded off \pset columns 40 \pset border 0 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset border 1 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset border 2 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset expanded on \pset border 0 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset border 1 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; \pset border 2 \pset format unaligned EXECUTE q; \pset format aligned EXECUTE q; \pset format wrapped EXECUTE q; DEALLOCATE q; \pset linestyle ascii \pset border 1 -- support table for output-format tests (useful to create a footer) CREATE TABLE psql_serial_tab ( id serial ); -- test header/footer/tuples_only behavior in aligned/unaligned/wrapped cases \pset format aligned \pset expanded off \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset expanded on \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false -- empty table is a special case for this format SELECT 1 WHERE FALSE; \pset format unaligned \pset expanded off \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset expanded on \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset format wrapped \pset expanded off \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset expanded on \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false -- check conditional tableam display -- Create a heap2 table am handler with heapam handler CREATE ACCESS METHOD heap_psql TYPE TABLE HANDLER heap_tableam_handler; CREATE TABLE tbl_heap_psql ( f1 int, f2 char(100)) USING heap_psql; CREATE TABLE tbl_heap ( f1 int, f2 char(100)) USING heap; \d+ tbl_heap_psql \d+ tbl_heap \set HIDE_TABLEAM off \d+ tbl_heap_psql \d+ tbl_heap \set HIDE_TABLEAM on DROP TABLE tbl_heap, tbl_heap_psql; DROP ACCESS METHOD heap_psql; -- test numericlocale (as best we can without control of psql's locale) \pset format aligned \pset expanded off \pset numericlocale true SELECT n, - n AS m, n * 111 AS x, '1e90'::float8 AS f FROM generate_series(0, 3) n; \pset numericlocale false -- test asciidoc output format \pset format asciidoc \pset border 1 \pset expanded off \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset expanded on \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false PREPARE q AS SELECT 'some|text' AS "a|title", ' ' AS "empty ", n AS int FROM generate_series(1, 2) AS n; \pset expanded off \pset border 0 EXECUTE q; \pset border 1 EXECUTE q; \pset border 2 EXECUTE q; \pset expanded on \pset border 0 EXECUTE q; \pset border 1 EXECUTE q; \pset border 2 EXECUTE q; DEALLOCATE q; -- test csv output format \pset format csv \pset border 1 \pset expanded off \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset expanded on \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false PREPARE q AS SELECT 'some"text' AS "a""title", E' \n' AS "junk", ' ' AS "empty", n AS int FROM generate_series(1, 2) AS n; \pset expanded off EXECUTE q; \pset expanded on EXECUTE q; DEALLOCATE q; -- special cases \pset expanded off SELECT 'comma,comma' AS comma, 'semi;semi' AS semi; \pset csv_fieldsep ';' SELECT 'comma,comma' AS comma, 'semi;semi' AS semi; SELECT '\.' AS data; \pset csv_fieldsep '.' SELECT '\' as d1, '' as d2; -- illegal csv separators \pset csv_fieldsep '' \pset csv_fieldsep ' 0 ' \pset csv_fieldsep ' n ' \pset csv_fieldsep ' r ' \pset csv_fieldsep ' "' \pset csv_fieldsep ',,' \pset csv_fieldsep ',' -- test html output format \pset format html \pset border 1 \pset expanded off \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset expanded on \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false prepare q as select 'some" text ' as "a&title", E' < foo > n < bar > ' as "junk", ' ' as "empty", n as int from generate_series(1,2) as n; \pset expanded off \pset border 0 execute q; \pset border 1 execute q; \pset tableattr foobar execute q; \pset tableattr \pset expanded on \pset border 0 execute q; \pset border 1 execute q; \pset tableattr foobar execute q; \pset tableattr deallocate q; -- test latex output format \pset format latex \pset border 1 \pset expanded off \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset expanded on \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false prepare q as select ' SOME more_text ' as "a$title", E' #< foo > % & ^ ~ | n bar ' as "junk", ' ' as "empty", n as int from generate_series(1,2) as n; \pset expanded off \pset border 0 execute q; \pset border 1 execute q; \pset border 2 execute q; \pset border 3 execute q; \pset expanded on \pset border 0 execute q; \pset border 1 execute q; \pset border 2 execute q; \pset border 3 execute q; deallocate q; -- test latex-longtable output format \pset format latex-longtable \pset border 1 \pset expanded off \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset expanded on \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false prepare q as select ' SOME more_text ' as "a$title", E' #< foo > % & ^ ~ | n bar ' as "junk", ' ' as "empty", n as int from generate_series(1,2) as n; \pset expanded off \pset border 0 execute q; \pset border 1 execute q; \pset border 2 execute q; \pset border 3 execute q; \pset tableattr lr execute q; \pset tableattr \pset expanded on \pset border 0 execute q; \pset border 1 execute q; \pset border 2 execute q; \pset border 3 execute q; \pset tableattr lr execute q; \pset tableattr deallocate q; -- test troff-ms output format \pset format troff-ms \pset border 1 \pset expanded off \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset expanded on \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false prepare q as select ' SOME text ' as "a\title", E' < foo > n < bar > ' as "junk", ' ' as "empty", n as int from generate_series(1,2) as n; \pset expanded off \pset border 0 execute q; \pset border 1 execute q; \pset border 2 execute q; \pset expanded on \pset border 0 execute q; \pset border 1 execute q; \pset border 2 execute q; deallocate q; -- check ambiguous format requests \pset format a \pset format l -- clean up after output format tests drop table psql_serial_tab; \pset format aligned \pset expanded off \pset border 1 -- tests for \if ... \endif \if true select ' okay '; select ' still okay '; \else not okay; still not okay \endif -- at this point query buffer should still have last valid line \g -- \if should work okay on part of a query select \if true 42 \else (bogus \endif forty_two; select \if false \\ (bogus \else \\ 42 \endif \\ forty_two; -- test a large nested if using a variety of true-equivalents \if true \if 1 \if yes \if on \echo ' ALL TRUE ' \else \echo ' should NOT print # 1 - 1 ' \endif \else \echo ' should NOT print # 1 - 2 ' \endif \else \echo ' should NOT print # 1 - 3 ' \endif \else \echo ' should NOT print # 1 - 4 ' \endif -- test a variety of false-equivalents in an if/elif/else structure \if false \echo ' should NOT print # 2 - 1 ' \elif 0 \echo ' should NOT print # 2 - 2 ' \elif no \echo ' should NOT print # 2 - 3 ' \elif off \echo ' should NOT print # 2 - 4 ' \else \echo ' ALL FALSE ' \endif -- test simple true-then-else \if true \echo ' FIRST thing TRUE ' \else \echo ' should NOT print # 3 - 1 ' \endif -- test simple false-true-else \if false \echo ' should NOT print # 4 - 1 ' \elif true \echo ' second thing TRUE ' \else \echo ' should NOT print # 5 - 1 ' \endif -- invalid boolean expressions are false \if invalid boolean expression \echo ' will NOT print # 6 - 1 ' \else \echo ' will print anyway # 6 - 2 ' \endif -- test un-matched endif \endif -- test un-matched else \else -- test un-matched elif \elif -- test double-else error \if true \else \else \endif -- test elif out-of-order \if false \else \elif \endif -- test if-endif matching in a false branch \if false \if false \echo ' should NOT print # 7 - 1 ' \else \echo ' should NOT print # 7 - 2 ' \endif \echo ' should NOT print # 7 - 3 ' \else \echo ' should print # 7 - 4 ' \endif -- show that vars and backticks are not expanded when ignoring extra args \set foo bar \echo :foo :' foo ' :"foo" \pset fieldsep | `nosuchcommand` :foo :' foo ' :"foo" -- show that vars and backticks are not expanded and commands are ignored -- when in a false if-branch \set try_to_quit ' q ' \if false :try_to_quit \echo `nosuchcommand` :foo :' foo ' :"foo" \pset fieldsep | `nosuchcommand` :foo :' foo ' :"foo" \a \C arg1 \c arg1 arg2 arg3 arg4 \cd arg1 \conninfo \copy arg1 arg2 arg3 arg4 arg5 arg6 \copyright \dt arg1 \e arg1 arg2 \ef whole_line \ev whole_line \echo arg1 arg2 arg3 arg4 arg5 \echo arg1 \encoding arg1 \errverbose \g arg1 \gx arg1 \gexec \h \html \i arg1 \ir arg1 \l arg1 \lo arg1 arg2 \o arg1 \p \password arg1 \prompt arg1 arg2 \pset arg1 arg2 \q \reset \s arg1 \set arg1 arg2 arg3 arg4 arg5 arg6 arg7 \setenv arg1 arg2 \sf whole_line \sv whole_line \t arg1 \T arg1 \timing arg1 \unset arg1 \w arg1 \watch arg1 \x arg1 -- \else here is eaten as part of OT_FILEPIPE argument \w |/no/such/file \else -- \endif here is eaten as part of whole-line argument \! whole_line \endif \else \echo ' should print # 8 - 1 ' \endif -- :{?...} defined variable test \set i 1 \if :{?i} \echo ' # 9 - 1 ok, variable i IS defined ' \else \echo ' should NOT print # 9 - 2 ' \endif \if :{?no_such_variable} \echo ' should NOT print # 10 - 1 ' \else \echo ' # 10 - 2 ok, variable no_such_variable IS NOT defined ' \endif SELECT :{?i} AS i_is_defined; SELECT NOT :{?no_such_var} AS no_such_var_is_not_defined; -- SHOW_CONTEXT \set SHOW_CONTEXT never do $$ begin raise notice ' foo '; raise exception ' bar '; end $$; \set SHOW_CONTEXT errors do $$ begin raise notice ' foo '; raise exception ' bar '; end $$; \set SHOW_CONTEXT always do $$ begin raise notice ' foo '; raise exception ' bar '; end $$; -- test printing and clearing the query buffer SELECT 1; \p SELECT 2 \r \p SELECT 3 \p UNION SELECT 4 \p UNION SELECT 5 ORDER BY 1; \r \p -- tests for special result variables -- working query, 2 rows selected SELECT 1 AS stuff UNION SELECT 2; \echo ' error: ' :ERROR \echo ' error code: ' :SQLSTATE \echo ' number OF rows: ' :ROW_COUNT -- syntax error SELECT 1 UNION; \echo ' error: ' :ERROR \echo ' error code: ' :SQLSTATE \echo ' number OF rows: ' :ROW_COUNT \echo ' LAST error message: ' :LAST_ERROR_MESSAGE \echo ' LAST error code: ' :LAST_ERROR_SQLSTATE -- empty query ; \echo ' error: ' :ERROR \echo ' error code: ' :SQLSTATE \echo ' number OF rows: ' :ROW_COUNT -- must have kept previous values \echo ' LAST error message: ' :LAST_ERROR_MESSAGE \echo ' LAST error code: ' :LAST_ERROR_SQLSTATE -- other query error DROP TABLE this_table_does_not_exist; \echo ' error: ' :ERROR \echo ' error code: ' :SQLSTATE \echo ' number OF rows: ' :ROW_COUNT \echo ' LAST error message: ' :LAST_ERROR_MESSAGE \echo ' LAST error code: ' :LAST_ERROR_SQLSTATE -- nondefault verbosity error settings (except verbose, which is too unstable) \set VERBOSITY terse SELECT 1 UNION; \echo ' error: ' :ERROR \echo ' error code: ' :SQLSTATE \echo ' LAST error message: ' :LAST_ERROR_MESSAGE \set VERBOSITY sqlstate SELECT 1/0; \echo ' error: ' :ERROR \echo ' error code: ' :SQLSTATE \echo ' LAST error message: ' :LAST_ERROR_MESSAGE \set VERBOSITY default -- working \gdesc SELECT 3 AS three, 4 AS four \gdesc \echo ' error: ' :ERROR \echo ' error code: ' :SQLSTATE \echo ' number OF rows: ' :ROW_COUNT -- \gdesc with an error SELECT 4 AS \gdesc \echo ' error: ' :ERROR \echo ' error code: ' :SQLSTATE \echo ' number OF rows: ' :ROW_COUNT \echo ' LAST error message: ' :LAST_ERROR_MESSAGE \echo ' LAST error code: ' :LAST_ERROR_SQLSTATE -- check row count for a cursor-fetched query \set FETCH_COUNT 10 select unique2 from tenk1 order by unique2 limit 19; \echo ' error: ' :ERROR \echo ' error code: ' :SQLSTATE \echo ' number OF rows: ' :ROW_COUNT -- cursor-fetched query with an error after the first group select 1/(15-unique2) from tenk1 order by unique2 limit 19; \echo ' error: ' :ERROR \echo ' error code: ' :SQLSTATE \echo ' number OF rows: ' :ROW_COUNT \echo ' LAST error message: ' :LAST_ERROR_MESSAGE \echo ' LAST error code: :LAST_ERROR_SQLSTATE \unset FETCH_COUNT CREATE SCHEMA testpart; CREATE ROLE testrole_partitioning; ALTER SCHEMA testpart OWNER TO testrole_partitioning; SET ROLE TO testrole_partitioning; -- run test inside own schema and hide other partitions SET search_path TO testpart; CREATE TABLE testtable_apple ( logdate date ); CREATE TABLE testtable_orange ( logdate date ); CREATE INDEX testtable_apple_index ON testtable_apple (logdate); CREATE INDEX testtable_orange_index ON testtable_orange (logdate); CREATE TABLE testpart_apple ( logdate date ) PARTITION BY RANGE (logdate); CREATE TABLE testpart_orange ( logdate date ) PARTITION BY RANGE (logdate); CREATE INDEX testpart_apple_index ON testpart_apple (logdate); CREATE INDEX testpart_orange_index ON testpart_orange (logdate); -- only partition related object should be displayed dP test * apple * dPt test * apple * dPi test * apple * DROP TABLE testtable_apple; DROP TABLE testtable_orange; DROP TABLE testpart_apple; DROP TABLE testpart_orange; CREATE TABLE parent_tab ( id int ) PARTITION BY RANGE (id); CREATE INDEX parent_index ON parent_tab (id); CREATE TABLE child_0_10 PARTITION OF parent_tab FOR VALUES FROM (0) TO (10); CREATE TABLE child_10_20 PARTITION OF parent_tab FOR VALUES FROM (10) TO (20); CREATE TABLE child_20_30 PARTITION OF parent_tab FOR VALUES FROM (20) TO (30); INSERT INTO parent_tab VALUES (generate_series(0, 29)); CREATE TABLE child_30_40 PARTITION OF parent_tab FOR VALUES FROM (30) TO (40) PARTITION BY RANGE (id); CREATE TABLE child_30_35 PARTITION OF child_30_40 FOR VALUES FROM (30) TO (35); CREATE TABLE child_35_40 PARTITION OF child_30_40 FOR VALUES FROM (35) TO (40); INSERT INTO parent_tab VALUES (generate_series(30, 39)); dPt dPi dP testpart.* dP dPtn dPin dPn dPn testpart.* DROP TABLE parent_tab CASCADE; DROP SCHEMA testpart; SET search_path TO DEFAULT; SET ROLE TO DEFAULT; DROP ROLE testrole_partitioning; pgFormatter-4.2/t/pg-test-files/expected/psql_crosstab.sql000066400000000000000000000105031361326045100240000ustar00rootroot00000000000000-- -- \crosstabview -- CREATE TABLE ctv_data ( v, h, c, i, d ) AS VALUES ( 'v1', 'h2', 'foo', 3, '2015-04-01' ::date), ( 'v2', 'h1', 'bar', 3, '2015-01-02'), ( 'v1', 'h0', 'baz', NULL, '2015-07-12'), ( 'v0', 'h4', 'qux', 4, '2015-07-15'), ( 'v0', 'h4', 'dbl', - 3, '2014-12-15'), ( 'v0', NULL, 'qux', 5, '2014-07-15'), ( 'v1', 'h2', 'quux', 7, '2015-04-04' ); -- make plans more stable ANALYZE ctv_data; -- running \crosstabview after query uses query in buffer SELECT v, EXTRACT(year FROM d), count(*) FROM ctv_data GROUP BY 1, 2 ORDER BY 1, 2; -- basic usage with 3 columns \crosstabview -- ordered months in horizontal header, quoted column name SELECT v, to_char(d, 'Mon') AS "month name", EXTRACT(month FROM d) AS num, count(*) FROM ctv_data GROUP BY 1, 2, 3 ORDER BY 1 \crosstabview v "month name" 4 num -- ordered months in vertical header, ordered years in horizontal header SELECT EXTRACT(year FROM d) AS year, to_char(d, 'Mon') AS "" "month"" name", EXTRACT(month FROM d) AS month, format('sum=%s avg=%s', sum(i), avg(i)::numeric(2, 1)) FROM ctv_data GROUP BY EXTRACT(year FROM d), to_char(d, 'Mon'), EXTRACT(month FROM d) ORDER BY month \crosstabview """month"" name" year format year -- combine contents vertically into the same cell (V/H duplicates) SELECT v, h, string_agg(c, E'\n') FROM ctv_data GROUP BY v, h ORDER BY 1, 2, 3 \crosstabview 1 2 3 -- horizontal ASC order from window function SELECT v, h, string_agg(c, E'\n') AS c, row_number() OVER (ORDER BY h) AS r FROM ctv_data GROUP BY v, h ORDER BY 1, 3, 2 \crosstabview v h c r -- horizontal DESC order from window function SELECT v, h, string_agg(c, E'\n') AS c, row_number() OVER (ORDER BY h DESC) AS r FROM ctv_data GROUP BY v, h ORDER BY 1, 3, 2 \crosstabview v h c r -- horizontal ASC order from window function, NULLs pushed rightmost SELECT v, h, string_agg(c, E'\n') AS c, row_number() OVER (ORDER BY h NULLS LAST) AS r FROM ctv_data GROUP BY v, h ORDER BY 1, 3, 2 \crosstabview v h c r -- only null, no column name, 2 columns: error SELECT NULL, NULL \crosstabview -- only null, no column name, 3 columns: works SELECT NULL, NULL, NULL \crosstabview -- null display \pset null '#null#' SELECT v, h, string_agg(i::text, E'\n') AS i FROM ctv_data GROUP BY v, h ORDER BY h, v \crosstabview v h i \pset null '' -- refer to columns by position SELECT v, h, string_agg(i::text, E'\n'), string_agg(c, E'\n') FROM ctv_data GROUP BY v, h ORDER BY h, v \crosstabview 2 1 4 -- refer to columns by positions and names mixed SELECT v, h, string_agg(i::text, E'\n') AS i, string_agg(c, E'\n') AS c FROM ctv_data GROUP BY v, h ORDER BY h, v \crosstabview 1 "h" 4 -- refer to columns by quoted names, check downcasing of unquoted name SELECT 1 AS "22", 2 AS b, 3 AS "Foo" \crosstabview "22" B "Foo" -- error: bad column name SELECT v, h, c, i FROM ctv_data \crosstabview v h j -- error: need to quote name SELECT 1 AS "22", 2 AS b, 3 AS "Foo" \crosstabview 1 2 Foo -- error: need to not quote name SELECT 1 AS "22", 2 AS b, 3 AS "Foo" \crosstabview 1 "B" "Foo" -- error: bad column number SELECT v, h, i, c FROM ctv_data \crosstabview 2 1 5 -- error: same H and V columns SELECT v, h, i, c FROM ctv_data \crosstabview 2 h 4 -- error: too many columns SELECT a, a, 1 FROM generate_series(1, 3000) AS a \crosstabview -- error: only one column SELECT 1 \crosstabview DROP TABLE ctv_data; -- check error reporting (bug #14476) CREATE TABLE ctv_data ( x int, y int, v text ); INSERT INTO ctv_data SELECT 1, x, '*' || x FROM generate_series(1, 10) x; SELECT * FROM ctv_data \crosstabview INSERT INTO ctv_data VALUES (1, 10, '*'); -- duplicate data to cause error SELECT * FROM ctv_data \crosstabview DROP TABLE ctv_data; pgFormatter-4.2/t/pg-test-files/expected/publication.sql000066400000000000000000000111361361326045100234350ustar00rootroot00000000000000-- -- PUBLICATION -- CREATE ROLE regress_publication_user LOGIN SUPERUSER; CREATE ROLE regress_publication_user2; CREATE ROLE regress_publication_user_dummy LOGIN NOSUPERUSER; SET SESSION AUTHORIZATION 'regress_publication_user'; CREATE PUBLICATION testpub_default; COMMENT ON PUBLICATION testpub_default IS 'test publication'; SELECT obj_description(p.oid, 'pg_publication') FROM pg_publication p; CREATE PUBLICATION testpib_ins_trunct WITH (publish = INSERT); ALTER PUBLICATION testpub_default SET (publish = UPDATE); -- error cases CREATE PUBLICATION testpub_xxx WITH (foo); CREATE PUBLICATION testpub_xxx WITH (publish = 'cluster, vacuum'); \dRp ALTER PUBLICATION testpub_default SET (publish = 'insert, update, delete'); \dRp --- adding tables CREATE SCHEMA pub_test; CREATE TABLE testpub_tbl1 ( id serial PRIMARY KEY, data text ); CREATE TABLE pub_test.testpub_nopk ( foo int, bar int ); CREATE VIEW testpub_view AS SELECT 1; CREATE TABLE testpub_parted ( a int ) PARTITION BY LIST (a); CREATE PUBLICATION testpub_foralltables FOR ALL TABLES WITH (publish = 'insert'); ALTER PUBLICATION testpub_foralltables SET (publish = 'insert, update'); CREATE TABLE testpub_tbl2 ( id serial PRIMARY KEY, data text ); -- fail - can't add to for all tables publication ALTER PUBLICATION testpub_foralltables ADD TABLE testpub_tbl2; -- fail - can't drop from all tables publication ALTER PUBLICATION testpub_foralltables DROP TABLE testpub_tbl2; -- fail - can't add to for all tables publication ALTER PUBLICATION testpub_foralltables SET TABLE pub_test.testpub_nopk; SELECT pubname, puballtables FROM pg_publication WHERE pubname = 'testpub_foralltables'; \d+ testpub_tbl2 \dRp+ testpub_foralltables DROP TABLE testpub_tbl2; DROP PUBLICATION testpub_foralltables; CREATE TABLE testpub_tbl3 ( a int ); CREATE TABLE testpub_tbl3a ( b text ) INHERITS ( testpub_tbl3 ); CREATE PUBLICATION testpub3 FOR TABLE testpub_tbl3; CREATE PUBLICATION testpub4 FOR TABLE ONLY testpub_tbl3; \dRp+ testpub3 \dRp+ testpub4 DROP TABLE testpub_tbl3, testpub_tbl3a; DROP PUBLICATION testpub3, testpub4; -- fail - view CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_view; CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_tbl1, pub_test.testpub_nopk; -- fail - already added ALTER PUBLICATION testpub_fortbl ADD TABLE testpub_tbl1; -- fail - already added CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_tbl1; \dRp+ testpub_fortbl -- fail - view ALTER PUBLICATION testpub_default ADD TABLE testpub_view; -- fail - partitioned table ALTER PUBLICATION testpub_fortbl ADD TABLE testpub_parted; ALTER PUBLICATION testpub_default ADD TABLE testpub_tbl1; ALTER PUBLICATION testpub_default SET TABLE testpub_tbl1; ALTER PUBLICATION testpub_default ADD TABLE pub_test.testpub_nopk; ALTER PUBLICATION testpib_ins_trunct ADD TABLE pub_test.testpub_nopk, testpub_tbl1; \d+ pub_test.testpub_nopk \d+ testpub_tbl1 \dRp+ testpub_default ALTER PUBLICATION testpub_default DROP TABLE testpub_tbl1, pub_test.testpub_nopk; -- fail - nonexistent ALTER PUBLICATION testpub_default DROP TABLE pub_test.testpub_nopk; \d+ testpub_tbl1 -- permissions SET ROLE regress_publication_user2; CREATE PUBLICATION testpub2; -- fail SET ROLE regress_publication_user; GRANT CREATE ON DATABASE regression TO regress_publication_user2; SET ROLE regress_publication_user2; CREATE PUBLICATION testpub2; -- ok ALTER PUBLICATION testpub2 ADD TABLE testpub_tbl1; -- fail SET ROLE regress_publication_user; GRANT regress_publication_user TO regress_publication_user2; SET ROLE regress_publication_user2; ALTER PUBLICATION testpub2 ADD TABLE testpub_tbl1; -- ok DROP PUBLICATION testpub2; SET ROLE regress_publication_user; REVOKE CREATE ON DATABASE regression FROM regress_publication_user2; DROP TABLE testpub_parted; DROP VIEW testpub_view; DROP TABLE testpub_tbl1; \dRp+ testpub_default -- fail - must be owner of publication SET ROLE regress_publication_user_dummy; ALTER PUBLICATION testpub_default RENAME TO testpub_dummy; RESET ROLE; ALTER PUBLICATION testpub_default RENAME TO testpub_foo; \dRp testpub_foo -- rename back to keep the rest simple ALTER PUBLICATION testpub_foo RENAME TO testpub_default; ALTER PUBLICATION testpub_default OWNER TO regress_publication_user2; \dRp testpub_default DROP PUBLICATION testpub_default; DROP PUBLICATION testpib_ins_trunct; DROP PUBLICATION testpub_fortbl; DROP SCHEMA pub_test CASCADE; RESET SESSION AUTHORIZATION; DROP ROLE regress_publication_user, regress_publication_user2; DROP ROLE regress_publication_user_dummy; pgFormatter-4.2/t/pg-test-files/expected/random.sql000066400000000000000000000024611361326045100224050ustar00rootroot00000000000000-- -- RANDOM -- Test the random function -- -- count the number of tuples originally, should be 1000 SELECT count(*) FROM onek; -- pick three random rows, they shouldn't match ( SELECT unique1 AS random FROM onek ORDER BY random() LIMIT 1) INTERSECT ( SELECT unique1 AS random FROM onek ORDER BY random() LIMIT 1) INTERSECT ( SELECT unique1 AS random FROM onek ORDER BY random() LIMIT 1); -- count roughly 1/10 of the tuples SELECT count(*) AS random INTO RANDOM_TBL FROM onek WHERE random() < 1.0 / 10; -- select again, the count should be different INSERT INTO RANDOM_TBL (random) SELECT count(*) FROM onek WHERE random() < 1.0 / 10; -- select again, the count should be different INSERT INTO RANDOM_TBL (random) SELECT count(*) FROM onek WHERE random() < 1.0 / 10; -- select again, the count should be different INSERT INTO RANDOM_TBL (random) SELECT count(*) FROM onek WHERE random() < 1.0 / 10; -- now test that they are different counts SELECT random, count(random) FROM RANDOM_TBL GROUP BY random HAVING count(random) > 3; SELECT AVG(random) FROM RANDOM_TBL HAVING AVG(random) NOT BETWEEN 80 AND 120; pgFormatter-4.2/t/pg-test-files/expected/rangefuncs.sql000066400000000000000000001005731361326045100232630ustar00rootroot00000000000000CREATE TABLE rngfunc2 ( rngfuncid int, f2 int ); INSERT INTO rngfunc2 VALUES (1, 11); INSERT INTO rngfunc2 VALUES (2, 22); INSERT INTO rngfunc2 VALUES (1, 111); CREATE FUNCTION rngfunct (int) RETURNS SETOF rngfunc2 AS 'SELECT * FROM rngfunc2 WHERE rngfuncid = $1 ORDER BY f2;' LANGUAGE SQL; -- function with ORDINALITY SELECT * FROM rngfunct (1 ) WITH ORDINALITY AS z (a, b, ord); SELECT * FROM rngfunct (1 ) WITH ORDINALITY AS z (a, b, ord) WHERE b > 100; -- ordinal 2, not 1 -- ordinality vs. column names and types SELECT a, b, ord FROM rngfunct (1 ) WITH ORDINALITY AS z (a, b, ord); SELECT a, ord FROM unnest(ARRAY['a', 'b']) WITH ORDINALITY AS z (a, ord); SELECT * FROM unnest(ARRAY['a', 'b']) WITH ORDINALITY AS z (a, ord); SELECT a, ord FROM unnest(ARRAY[1.0::float8]) WITH ORDINALITY AS z (a, ord); SELECT * FROM unnest(ARRAY[1.0::float8]) WITH ORDINALITY AS z (a, ord); SELECT row_to_json(s.*) FROM generate_series(11, 14 ) WITH ORDINALITY s; -- ordinality vs. views CREATE TEMPORARY VIEW vw_ord AS SELECT * FROM ( VALUES (1)) v (n) JOIN rngfunct (1 ) WITH ORDINALITY AS z (a, b, ord) ON (n = ord); SELECT * FROM vw_ord; SELECT definition FROM pg_views WHERE viewname = 'vw_ord'; DROP VIEW vw_ord; -- multiple functions SELECT * FROM ROWS FROM (rngfunct (1), rngfunct (2)) WITH ORDINALITY AS z (a, b, c, d, ord); CREATE TEMPORARY VIEW vw_ord AS SELECT * FROM ( VALUES (1)) v (n) JOIN ROWS FROM (rngfunct (1), rngfunct (2)) WITH ORDINALITY AS z (a, b, c, d, ord) ON (n = ord); SELECT * FROM vw_ord; SELECT definition FROM pg_views WHERE viewname = 'vw_ord'; DROP VIEW vw_ord; -- expansions of unnest() SELECT * FROM unnest(ARRAY[10, 20], ARRAY['foo', 'bar'], ARRAY[1.0]); SELECT * FROM unnest(ARRAY[10, 20], ARRAY['foo', 'bar'], ARRAY[1.0]) WITH ORDINALITY AS z (a, b, c, ord); SELECT * FROM ROWS FROM (unnest(ARRAY[10, 20], ARRAY['foo', 'bar'], ARRAY[1.0])) WITH ORDINALITY AS z (a, b, c, ord); SELECT * FROM ROWS FROM (unnest(ARRAY[10, 20], ARRAY['foo', 'bar']), generate_series(101, 102)) WITH ORDINALITY AS z (a, b, c, ord); CREATE TEMPORARY VIEW vw_ord AS SELECT * FROM unnest(ARRAY[10, 20], ARRAY['foo', 'bar'], ARRAY[1.0]) AS z (a, b, c); SELECT * FROM vw_ord; SELECT definition FROM pg_views WHERE viewname = 'vw_ord'; DROP VIEW vw_ord; CREATE TEMPORARY VIEW vw_ord AS SELECT * FROM ROWS FROM (unnest(ARRAY[10, 20], ARRAY['foo', 'bar'], ARRAY[1.0])) AS z (a, b, c); SELECT * FROM vw_ord; SELECT definition FROM pg_views WHERE viewname = 'vw_ord'; DROP VIEW vw_ord; CREATE TEMPORARY VIEW vw_ord AS SELECT * FROM ROWS FROM (unnest(ARRAY[10, 20], ARRAY['foo', 'bar']), generate_series(1, 2)) AS z (a, b, c); SELECT * FROM vw_ord; SELECT definition FROM pg_views WHERE viewname = 'vw_ord'; DROP VIEW vw_ord; -- ordinality and multiple functions vs. rewind and reverse scan BEGIN; DECLARE rf_cur SCROLL CURSOR FOR SELECT * FROM ROWS FROM (generate_series(1, 5), generate_series(1, 2)) WITH ORDINALITY AS g (i, j, o); FETCH ALL FROM rf_cur; FETCH BACKWARD ALL FROM rf_cur; FETCH ALL FROM rf_cur; FETCH NEXT FROM rf_cur; FETCH NEXT FROM rf_cur; FETCH prior FROM rf_cur; FETCH absolute 1 FROM rf_cur; FETCH NEXT FROM rf_cur; FETCH NEXT FROM rf_cur; FETCH NEXT FROM rf_cur; FETCH prior FROM rf_cur; FETCH prior FROM rf_cur; FETCH prior FROM rf_cur; COMMIT; -- function with implicit LATERAL SELECT * FROM rngfunc2, rngfunct (rngfunc2.rngfuncid) z WHERE rngfunc2.f2 = z.f2; -- function with implicit LATERAL and explicit ORDINALITY SELECT * FROM rngfunc2, rngfunct (rngfunc2.rngfuncid ) WITH ORDINALITY AS z (rngfuncid, f2, ord) WHERE rngfunc2.f2 = z.f2; -- function in subselect SELECT * FROM rngfunc2 WHERE f2 IN ( SELECT f2 FROM rngfunct (rngfunc2.rngfuncid) z WHERE z.rngfuncid = rngfunc2.rngfuncid) ORDER BY 1, 2; -- function in subselect SELECT * FROM rngfunc2 WHERE f2 IN ( SELECT f2 FROM rngfunct (1) z WHERE z.rngfuncid = rngfunc2.rngfuncid) ORDER BY 1, 2; -- function in subselect SELECT * FROM rngfunc2 WHERE f2 IN ( SELECT f2 FROM rngfunct (rngfunc2.rngfuncid) z WHERE z.rngfuncid = 1) ORDER BY 1, 2; -- nested functions SELECT rngfunct.rngfuncid, rngfunct.f2 FROM rngfunct (sin(pi() / 2)::int) ORDER BY 1, 2; CREATE TABLE rngfunc ( rngfuncid int, rngfuncsubid int, rngfuncname text, PRIMARY KEY (rngfuncid, rngfuncsubid) ); INSERT INTO rngfunc VALUES (1, 1, 'Joe'); INSERT INTO rngfunc VALUES (1, 2, 'Ed'); INSERT INTO rngfunc VALUES (2, 1, 'Mary'); -- sql, proretset = f, prorettype = b CREATE FUNCTION getrngfunc1 (int) RETURNS int AS 'SELECT $1;' LANGUAGE SQL; SELECT * FROM getrngfunc1 (1) AS t1; SELECT * FROM getrngfunc1 (1 ) WITH ORDINALITY AS t1 (v, o); CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc1 (1); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc1 (1 ) WITH ORDINALITY AS t1 (v, o); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; -- sql, proretset = t, prorettype = b CREATE FUNCTION getrngfunc2 (int) RETURNS SETOF int AS 'SELECT rngfuncid FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; SELECT * FROM getrngfunc2 (1) AS t1; SELECT * FROM getrngfunc2 (1 ) WITH ORDINALITY AS t1 (v, o); CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc2 (1); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc2 (1 ) WITH ORDINALITY AS t1 (v, o); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; -- sql, proretset = t, prorettype = b CREATE FUNCTION getrngfunc3 (int) RETURNS SETOF text AS 'SELECT rngfuncname FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; SELECT * FROM getrngfunc3 (1) AS t1; SELECT * FROM getrngfunc3 (1 ) WITH ORDINALITY AS t1 (v, o); CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc3 (1); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc3 (1 ) WITH ORDINALITY AS t1 (v, o); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; -- sql, proretset = f, prorettype = c CREATE FUNCTION getrngfunc4 (int) RETURNS rngfunc AS 'SELECT * FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; SELECT * FROM getrngfunc4 (1) AS t1; SELECT * FROM getrngfunc4 (1 ) WITH ORDINALITY AS t1 (a, b, c, o); CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc4 (1); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc4 (1 ) WITH ORDINALITY AS t1 (a, b, c, o); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; -- sql, proretset = t, prorettype = c CREATE FUNCTION getrngfunc5 (int) RETURNS SETOF rngfunc AS 'SELECT * FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; SELECT * FROM getrngfunc5 (1) AS t1; SELECT * FROM getrngfunc5 (1 ) WITH ORDINALITY AS t1 (a, b, c, o); CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc5 (1); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc5 (1 ) WITH ORDINALITY AS t1 (a, b, c, o); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; -- sql, proretset = f, prorettype = record CREATE FUNCTION getrngfunc6 (int) RETURNS RECORD AS 'SELECT * FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; SELECT * FROM getrngfunc6 (1) AS t1 (rngfuncid int, rngfuncsubid int, rngfuncname text); SELECT * FROM ROWS FROM (getrngfunc6 (1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text)) WITH ORDINALITY; CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc6 (1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; CREATE VIEW vw_getrngfunc AS SELECT * FROM ROWS FROM (getrngfunc6 (1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text)) WITH ORDINALITY; SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; -- sql, proretset = t, prorettype = record CREATE FUNCTION getrngfunc7 (int) RETURNS SETOF record AS 'SELECT * FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; SELECT * FROM getrngfunc7 (1) AS t1 (rngfuncid int, rngfuncsubid int, rngfuncname text); SELECT * FROM ROWS FROM (getrngfunc7 (1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text)) WITH ORDINALITY; CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc7 (1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; CREATE VIEW vw_getrngfunc AS SELECT * FROM ROWS FROM (getrngfunc7 (1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text)) WITH ORDINALITY; SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; -- plpgsql, proretset = f, prorettype = b CREATE FUNCTION getrngfunc8 (int) RETURNS int AS 'DECLARE rngfuncint int; BEGIN SELECT rngfuncid into rngfuncint FROM rngfunc WHERE rngfuncid = $1; RETURN rngfuncint; END;' LANGUAGE plpgsql; SELECT * FROM getrngfunc8 (1) AS t1; SELECT * FROM getrngfunc8 (1 ) WITH ORDINALITY AS t1 (v, o); CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc8 (1); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc8 (1 ) WITH ORDINALITY AS t1 (v, o); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; -- plpgsql, proretset = f, prorettype = c CREATE FUNCTION getrngfunc9 (int) RETURNS rngfunc AS 'DECLARE rngfunctup rngfunc%ROWTYPE; BEGIN SELECT * into rngfunctup FROM rngfunc WHERE rngfuncid = $1; RETURN rngfunctup; END;' LANGUAGE plpgsql; SELECT * FROM getrngfunc9 (1) AS t1; SELECT * FROM getrngfunc9 (1 ) WITH ORDINALITY AS t1 (a, b, c, o); CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc9 (1); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc9 (1 ) WITH ORDINALITY AS t1 (a, b, c, o); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; -- mix 'n match kinds, to exercise expandRTE and related logic SELECT * FROM ROWS FROM (getrngfunc1 (1), getrngfunc2 (1), getrngfunc3 (1), getrngfunc4 (1), getrngfunc5 (1), getrngfunc6 (1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text), getrngfunc7 (1) AS ( rngfuncid int, rngfuncsubid int, rngfuncname text), getrngfunc8 (1), getrngfunc9 (1)) WITH ORDINALITY AS t1 (a, b, c, d, e, f, g, h, i, j, k, l, m, o, p, q, r, s, t, u); SELECT * FROM ROWS FROM (getrngfunc9 (1), getrngfunc8 (1), getrngfunc7 (1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text), getrngfunc6 (1) AS ( rngfuncid int, rngfuncsubid int, rngfuncname text), getrngfunc5 (1), getrngfunc4 (1), getrngfunc3 (1), getrngfunc2 (1), getrngfunc1 (1)) WITH ORDINALITY AS t1 (a, b, c, d, e, f, g, h, i, j, k, l, m, o, p, q, r, s, t, u); CREATE TEMPORARY VIEW vw_rngfunc AS SELECT * FROM ROWS FROM (getrngfunc9 (1), getrngfunc7 (1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text), getrngfunc1 (1)) WITH ORDINALITY AS t1 (a, b, c, d, e, f, g, n); SELECT * FROM vw_rngfunc; SELECT pg_get_viewdef('vw_rngfunc'); DROP VIEW vw_rngfunc; DROP FUNCTION getrngfunc1 (int); DROP FUNCTION getrngfunc2 (int); DROP FUNCTION getrngfunc3 (int); DROP FUNCTION getrngfunc4 (int); DROP FUNCTION getrngfunc5 (int); DROP FUNCTION getrngfunc6 (int); DROP FUNCTION getrngfunc7 (int); DROP FUNCTION getrngfunc8 (int); DROP FUNCTION getrngfunc9 (int); DROP FUNCTION rngfunct (int); DROP TABLE rngfunc2; DROP TABLE rngfunc; -- Rescan tests -- CREATE TEMPORARY SEQUENCE rngfunc_rescan_seq1; CREATE TEMPORARY SEQUENCE rngfunc_rescan_seq2; CREATE TYPE rngfunc_rescan_t AS ( i integer, s bigint ); CREATE FUNCTION rngfunc_sql (int, int) RETURNS SETOF rngfunc_rescan_t AS 'SELECT i, nextval(''rngfunc_rescan_seq1'') FROM generate_series($1,$2) i;' LANGUAGE SQL; -- plpgsql functions use materialize mode CREATE FUNCTION rngfunc_mat (int, int) RETURNS SETOF rngfunc_rescan_t AS 'begin for i in $1..$2 loop return next (i, nextval(''rngfunc_rescan_seq2'')); end loop; end;' LANGUAGE plpgsql; --invokes ExecReScanFunctionScan - all these cases should materialize the function only once -- LEFT JOIN on a condition that the planner can't prove to be true is used to ensure the function -- is on the inner path of a nestloop join SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (1), (2), (3)) v (r) LEFT JOIN rngfunc_sql (11, 13) ON (r + i) < 100; SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (1), (2), (3)) v (r) LEFT JOIN rngfunc_sql (11, 13 ) WITH ORDINALITY AS f (i, s, o) ON (r + i) < 100; SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (1), (2), (3)) v (r) LEFT JOIN rngfunc_mat (11, 13) ON (r + i) < 100; SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (1), (2), (3)) v (r) LEFT JOIN rngfunc_mat (11, 13 ) WITH ORDINALITY AS f (i, s, o) ON (r + i) < 100; SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (1), (2), (3)) v (r) LEFT JOIN ROWS FROM (rngfunc_sql (11, 13), rngfunc_mat (11, 13)) WITH ORDINALITY AS f (i1, s1, i2, s2, o) ON (r + i1 + i2) < 100; SELECT * FROM ( VALUES (1), (2), (3)) v (r) LEFT JOIN generate_series(11, 13) f (i) ON (r + i) < 100; SELECT * FROM ( VALUES (1), (2), (3)) v (r) LEFT JOIN generate_series(11, 13 ) WITH ORDINALITY AS f (i, o) ON (r + i) < 100; SELECT * FROM ( VALUES (1), (2), (3)) v (r) LEFT JOIN unnest(ARRAY[10, 20, 30]) f (i) ON (r + i) < 100; SELECT * FROM ( VALUES (1), (2), (3)) v (r) LEFT JOIN unnest(ARRAY[10, 20, 30]) WITH ORDINALITY AS f (i, o) ON (r + i) < 100; --invokes ExecReScanFunctionScan with chgParam != NULL (using implied LATERAL) SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (1), (2), (3)) v (r), rngfunc_sql (10 + r, 13); SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (1), (2), (3)) v (r), rngfunc_sql (10 + r, 13 ) WITH ORDINALITY AS f (i, s, o); SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (1), (2), (3)) v (r), rngfunc_sql (11, 10 + r); SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (1), (2), (3)) v (r), rngfunc_sql (11, 10 + r ) WITH ORDINALITY AS f (i, s, o); SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (11, 12), (13, 15), (16, 20)) v (r1, r2), rngfunc_sql (r1, r2); SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (11, 12), (13, 15), (16, 20)) v (r1, r2), rngfunc_sql (r1, r2 ) WITH ORDINALITY AS f (i, s, o); SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (1), (2), (3)) v (r), rngfunc_mat (10 + r, 13); SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (1), (2), (3)) v (r), rngfunc_mat (10 + r, 13 ) WITH ORDINALITY AS f (i, s, o); SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (1), (2), (3)) v (r), rngfunc_mat (11, 10 + r); SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (1), (2), (3)) v (r), rngfunc_mat (11, 10 + r ) WITH ORDINALITY AS f (i, s, o); SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (11, 12), (13, 15), (16, 20)) v (r1, r2), rngfunc_mat (r1, r2); SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (11, 12), (13, 15), (16, 20)) v (r1, r2), rngfunc_mat (r1, r2 ) WITH ORDINALITY AS f (i, s, o); -- selective rescan of multiple functions: SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (1), (2), (3)) v (r), ROWS FROM (rngfunc_sql (11, 11), rngfunc_mat (10 + r, 13)); SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (1), (2), (3)) v (r), ROWS FROM (rngfunc_sql (10 + r, 13), rngfunc_mat (11, 11)); SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM ( VALUES (1), (2), (3)) v (r), ROWS FROM (rngfunc_sql (10 + r, 13), rngfunc_mat (10 + r, 13)); SELECT setval('rngfunc_rescan_seq1', 1, FALSE), setval('rngfunc_rescan_seq2', 1, FALSE); SELECT * FROM generate_series(1, 2) r1, generate_series(r1, 3) r2, ROWS FROM (rngfunc_sql (10 + r1, 13), rngfunc_mat (10 + r2, 13)); SELECT * FROM ( VALUES (1), (2), (3)) v (r), generate_series(10 + r, 20 - r) f (i); SELECT * FROM ( VALUES (1), (2), (3)) v (r), generate_series(10 + r, 20 - r ) WITH ORDINALITY AS f (i, o); SELECT * FROM ( VALUES (1), (2), (3)) v (r), unnest(ARRAY[r * 10, r * 20, r * 30]) f (i); SELECT * FROM ( VALUES (1), (2), (3)) v (r), unnest(ARRAY[r * 10, r * 20, r * 30]) WITH ORDINALITY AS f (i, o); -- deep nesting SELECT * FROM ( VALUES (1), (2), (3)) v1 (r1), LATERAL ( SELECT r1, * FROM ( VALUES (10), (20), (30)) v2 (r2) LEFT JOIN generate_series(21, 23) f (i) ON ((r2 + i) < 100) OFFSET 0) s1; SELECT * FROM ( VALUES (1), (2), (3)) v1 (r1), LATERAL ( SELECT r1, * FROM ( VALUES (10), (20), (30)) v2 (r2) LEFT JOIN generate_series(20 + r1, 23) f (i) ON ((r2 + i) < 100) OFFSET 0) s1; SELECT * FROM ( VALUES (1), (2), (3)) v1 (r1), LATERAL ( SELECT r1, * FROM ( VALUES (10), (20), (30)) v2 (r2) LEFT JOIN generate_series(r2, r2 + 3) f (i) ON ((r2 + i) < 100) OFFSET 0) s1; SELECT * FROM ( VALUES (1), (2), (3)) v1 (r1), LATERAL ( SELECT r1, * FROM ( VALUES (10), (20), (30)) v2 (r2) LEFT JOIN generate_series(r1, 2 + r2 / 5) f (i) ON ((r2 + i) < 100) OFFSET 0) s1; -- check handling of FULL JOIN with multiple lateral references (bug #15741) SELECT * FROM ( VALUES (1), (2)) v1 (r1) LEFT JOIN LATERAL ( SELECT * FROM generate_series(1, v1.r1) AS gs1 LEFT JOIN LATERAL ( SELECT * FROM generate_series(1, gs1) AS gs2 LEFT JOIN generate_series(1, gs2) AS gs3 ON TRUE) AS ss1 ON TRUE FULL JOIN generate_series(1, v1.r1) AS gs4 ON FALSE) AS ss0 ON TRUE; DROP FUNCTION rngfunc_sql (int, int); DROP FUNCTION rngfunc_mat (int, int); DROP SEQUENCE rngfunc_rescan_seq1; DROP SEQUENCE rngfunc_rescan_seq2; -- -- Test cases involving OUT parameters -- CREATE FUNCTION rngfunc (IN f1 int, out f2 int ) AS 'select $1+1' LANGUAGE sql; SELECT rngfunc (42); SELECT * FROM rngfunc (42); SELECT * FROM rngfunc (42) AS p (x); -- explicit spec of return type is OK CREATE OR REPLACE FUNCTION rngfunc (IN f1 int, out f2 int) RETURNS int AS 'select $1+1' LANGUAGE sql; -- error, wrong result type CREATE OR REPLACE FUNCTION rngfunc (IN f1 int, out f2 int) RETURNS float AS 'select $1+1' LANGUAGE sql; -- with multiple OUT params you must get a RECORD result CREATE OR REPLACE FUNCTION rngfunc (IN f1 int, out f2 int, out f3 text) RETURNS int AS 'select $1+1' LANGUAGE sql; CREATE OR REPLACE FUNCTION rngfunc (IN f1 int, out f2 int, out f3 text) RETURNS record AS 'select $1+1' LANGUAGE sql; CREATE OR REPLACE FUNCTION rngfuncr (IN f1 int, out f2 int, out text ) AS $$ SELECT $1 - 1, $1::text || 'z' $$ LANGUAGE sql; SELECT f1, rngfuncr (f1) FROM int4_tbl; SELECT * FROM rngfuncr (42); SELECT * FROM rngfuncr (42) AS p (a, b); CREATE OR REPLACE FUNCTION rngfuncb (IN f1 int, INOUT f2 int, out text ) AS $$ SELECT $2 - 1, $1::text || 'z' $$ LANGUAGE sql; SELECT f1, rngfuncb (f1, f1 / 2) FROM int4_tbl; SELECT * FROM rngfuncb (42, 99); SELECT * FROM rngfuncb (42, 99) AS p (a, b); -- Can reference function with or without OUT params for DROP, etc DROP FUNCTION rngfunc (int); DROP FUNCTION rngfuncr (IN f2 int, out f1 int, out text); DROP FUNCTION rngfuncb (IN f1 int, INOUT f2 int); -- -- For my next trick, polymorphic OUT parameters -- CREATE FUNCTION dup (f1 anyelement, f2 out anyelement, f3 out anyarray ) AS 'select $1, array[$1,$1]' LANGUAGE sql; SELECT dup (22); SELECT dup ('xyz'); -- fails SELECT dup ('xyz'::text); SELECT * FROM dup ('xyz'::text); -- fails, as we are attempting to rename first argument CREATE OR REPLACE FUNCTION dup (INOUT f2 anyelement, out f3 anyarray ) AS 'select $1, array[$1,$1]' LANGUAGE sql; DROP FUNCTION dup (anyelement); -- equivalent behavior, though different name exposed for input arg CREATE OR REPLACE FUNCTION dup (INOUT f2 anyelement, out f3 anyarray ) AS 'select $1, array[$1,$1]' LANGUAGE sql; SELECT dup (22); DROP FUNCTION dup (anyelement); -- fails, no way to deduce outputs CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray ) AS 'select $1, array[$1,$1]' LANGUAGE sql; -- -- table functions -- CREATE OR REPLACE FUNCTION rngfunc () RETURNS TABLE ( a int ) AS $$ SELECT a FROM generate_series(1, 5) a (a) $$ LANGUAGE sql; SELECT * FROM rngfunc (); DROP FUNCTION rngfunc (); CREATE OR REPLACE FUNCTION rngfunc (int) RETURNS TABLE ( a int, b int ) AS $$ SELECT a, b FROM generate_series(1, $1) a (a), generate_series(1, $1) b (b) $$ LANGUAGE sql; SELECT * FROM rngfunc (3); DROP FUNCTION rngfunc (int); -- case that causes change of typmod knowledge during inlining CREATE OR REPLACE FUNCTION rngfunc () RETURNS TABLE ( a varchar(5) ) AS $$ SELECT 'hello'::varchar(5) $$ LANGUAGE sql STABLE; SELECT * FROM rngfunc () GROUP BY 1; DROP FUNCTION rngfunc (); -- -- some tests on SQL functions with RETURNING -- CREATE temp TABLE tt ( f1 serial, data text ); CREATE FUNCTION insert_tt (text) RETURNS int AS $$ INSERT INTO tt (data) VALUES ($1) RETURNING f1 $$ LANGUAGE sql; SELECT insert_tt ('foo'); SELECT insert_tt ('bar'); SELECT * FROM tt; -- insert will execute to completion even if function needs just 1 row CREATE OR REPLACE FUNCTION insert_tt (text) RETURNS int AS $$ INSERT INTO tt (data) VALUES ($1), ($1 || $1) RETURNING f1 $$ LANGUAGE sql; SELECT insert_tt ('fool'); SELECT * FROM tt; -- setof does what's expected CREATE OR REPLACE FUNCTION insert_tt2 (text, text) RETURNS SETOF int AS $$ INSERT INTO tt (data) VALUES ($1), ($2) RETURNING f1 $$ LANGUAGE sql; SELECT insert_tt2 ('foolish', 'barrish'); SELECT * FROM insert_tt2 ('baz', 'quux'); SELECT * FROM tt; -- limit doesn't prevent execution to completion SELECT insert_tt2 ('foolish', 'barrish') LIMIT 1; SELECT * FROM tt; -- triggers will fire, too CREATE FUNCTION noticetrigger () RETURNS TRIGGER AS $$ BEGIN RAISE notice 'noticetrigger % %', new.f1, new.data; RETURN NULL; END $$ LANGUAGE plpgsql; CREATE TRIGGER tnoticetrigger AFTER INSERT ON tt FOR EACH ROW EXECUTE PROCEDURE noticetrigger (); SELECT insert_tt2 ('foolme', 'barme') LIMIT 1; SELECT * FROM tt; -- and rules work CREATE temp TABLE tt_log ( f1 int, data text ); CREATE RULE insert_tt_rule AS ON INSERT TO tt DO also INSERT INTO tt_log VALUES (new.*); SELECT insert_tt2 ('foollog', 'barlog') LIMIT 1; SELECT * FROM tt; -- note that nextval() gets executed a second time in the rule expansion, -- which is expected. SELECT * FROM tt_log; -- test case for a whole-row-variable bug CREATE FUNCTION rngfunc1 (n integer, out a text, out b text) RETURNS SETOF record LANGUAGE sql AS $$ SELECT 'foo ' || i, 'bar ' || i FROM generate_series(1, $1) i $$; SET work_mem = '64kB'; SELECT t.a, t, t.a FROM rngfunc1 (10000) t LIMIT 1; RESET work_mem; SELECT t.a, t, t.a FROM rngfunc1 (10000) t LIMIT 1; DROP FUNCTION rngfunc1 (n integer); -- test use of SQL functions returning record -- this is supported in some cases where the query doesn't specify -- the actual record type ... CREATE FUNCTION array_to_set (anyarray) RETURNS SETOF record AS $$ SELECT i AS "index", $1[i] AS "value" FROM generate_subscripts($1, 1) i $$ LANGUAGE sql STRICT IMMUTABLE; SELECT array_to_set (ARRAY['one', 'two']); SELECT * FROM array_to_set (ARRAY['one', 'two']) AS t (f1 int, f2 text); SELECT * FROM array_to_set (ARRAY['one', 'two']); -- fail CREATE temp TABLE rngfunc ( f1 int8, f2 int8 ); CREATE FUNCTION testrngfunc () RETURNS record AS $$ INSERT INTO rngfunc VALUES (1, 2) RETURNING *; $$ LANGUAGE sql; SELECT testrngfunc (); SELECT * FROM testrngfunc () AS t (f1 int8, f2 int8); SELECT * FROM testrngfunc (); -- fail DROP FUNCTION testrngfunc (); CREATE FUNCTION testrngfunc () RETURNS SETOF record AS $$ INSERT INTO rngfunc VALUES (1, 2), (3, 4) RETURNING *; $$ LANGUAGE sql; SELECT testrngfunc (); SELECT * FROM testrngfunc () AS t (f1 int8, f2 int8); SELECT * FROM testrngfunc (); -- fail DROP FUNCTION testrngfunc (); -- -- Check some cases involving added/dropped columns in a rowtype result -- CREATE temp TABLE users ( userid text, seq int, email text, todrop bool, moredrop int, enabled bool ); INSERT INTO users VALUES ('id', 1, 'email', TRUE, 11, TRUE); INSERT INTO users VALUES ('id2', 2, 'email2', TRUE, 12, TRUE); ALTER TABLE users DROP COLUMN todrop; CREATE OR REPLACE FUNCTION get_first_user () RETURNS users AS $$ SELECT * FROM users ORDER BY userid LIMIT 1; $$ LANGUAGE sql STABLE; SELECT get_first_user (); SELECT * FROM get_first_user (); CREATE OR REPLACE FUNCTION get_users () RETURNS SETOF users AS $$ SELECT * FROM users ORDER BY userid; $$ LANGUAGE sql STABLE; SELECT get_users (); SELECT * FROM get_users (); SELECT * FROM get_users ( ) WITH ORDINALITY; -- make sure ordinality copes -- multiple functions vs. dropped columns SELECT * FROM ROWS FROM (generate_series(10, 11), get_users ()) WITH ORDINALITY; SELECT * FROM ROWS FROM (get_users (), generate_series(10, 11)) WITH ORDINALITY; -- check that we can cope with post-parsing changes in rowtypes CREATE temp VIEW usersview AS SELECT * FROM ROWS FROM (get_users (), generate_series(10, 11)) WITH ORDINALITY; SELECT * FROM usersview; ALTER TABLE users ADD COLUMN junk text; SELECT * FROM usersview; BEGIN; ALTER TABLE users DROP COLUMN moredrop; SELECT * FROM usersview; -- expect clean failure ROLLBACK; ALTER TABLE users ALTER COLUMN seq TYPE numeric; SELECT * FROM usersview; -- expect clean failure DROP VIEW usersview; DROP FUNCTION get_first_user (); DROP FUNCTION get_users (); DROP TABLE users; -- this won't get inlined because of type coercion, but it shouldn't fail CREATE OR REPLACE FUNCTION rngfuncbar () RETURNS SETOF text AS $$ SELECT 'foo'::varchar UNION ALL SELECT 'bar'::varchar; $$ LANGUAGE sql STABLE; SELECT rngfuncbar (); SELECT * FROM rngfuncbar (); DROP FUNCTION rngfuncbar (); -- check handling of a SQL function with multiple OUT params (bug #5777) CREATE OR REPLACE FUNCTION rngfuncbar (out integer, out numeric ) AS $$ SELECT (1, 2.1) $$ LANGUAGE sql; SELECT * FROM rngfuncbar (); CREATE OR REPLACE FUNCTION rngfuncbar (out integer, out numeric ) AS $$ SELECT (1, 2) $$ LANGUAGE sql; SELECT * FROM rngfuncbar (); -- fail CREATE OR REPLACE FUNCTION rngfuncbar (out integer, out numeric ) AS $$ SELECT (1, 2.1, 3) $$ LANGUAGE sql; SELECT * FROM rngfuncbar (); -- fail DROP FUNCTION rngfuncbar (); -- check whole-row-Var handling in nested lateral functions (bug #11703) CREATE FUNCTION extractq2 (t int8_tbl) RETURNS int8 AS $$ SELECT t.q2 $$ LANGUAGE sql IMMUTABLE; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT x FROM int8_tbl, extractq2 (int8_tbl) f (x); SELECT x FROM int8_tbl, extractq2 (int8_tbl) f (x); CREATE FUNCTION extractq2_2 (t int8_tbl) RETURNS TABLE ( ret1 int8 ) AS $$ SELECT extractq2 (t) offset 0 $$ LANGUAGE sql IMMUTABLE; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT x FROM int8_tbl, extractq2_2 (int8_tbl) f (x); SELECT x FROM int8_tbl, extractq2_2 (int8_tbl) f (x); -- without the "offset 0", this function gets optimized quite differently CREATE FUNCTION extractq2_2_opt (t int8_tbl) RETURNS TABLE ( ret1 int8 ) AS $$ SELECT extractq2 (t) $$ LANGUAGE sql IMMUTABLE; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT x FROM int8_tbl, extractq2_2_opt (int8_tbl) f (x); SELECT x FROM int8_tbl, extractq2_2_opt (int8_tbl) f (x); -- check handling of nulls in SRF results (bug #7808) CREATE TYPE rngfunc2 AS ( a integer, b text ); SELECT *, row_to_json(u) FROM unnest(ARRAY[(1, 'foo')::rngfunc2, NULL::rngfunc2]) u; SELECT *, row_to_json(u) FROM unnest(ARRAY[NULL::rngfunc2, NULL::rngfunc2]) u; SELECT *, row_to_json(u) FROM unnest(ARRAY[NULL::rngfunc2, (1, 'foo')::rngfunc2, NULL::rngfunc2]) u; SELECT *, row_to_json(u) FROM unnest(ARRAY[]::rngfunc2[]) u; DROP TYPE rngfunc2; pgFormatter-4.2/t/pg-test-files/expected/rangetypes.sql000066400000000000000000000531511361326045100233100ustar00rootroot00000000000000-- Tests for range data types. CREATE TYPE textrange AS RANGE ( subtype = text, COLLATION = "C" ); -- -- test input parser -- -- negative tests; should fail SELECT ''::textrange; SELECT '-[a,z)'::textrange; SELECT '[a,z) - '::textrange; SELECT '(",a)'::textrange; SELECT '(,,a)'::textrange; SELECT '(),a)'::textrange; SELECT '(a,))'::textrange; SELECT '(],a)'::textrange; SELECT '(a,])'::textrange; SELECT '[z,a]'::textrange; -- should succeed SELECT ' empty '::textrange; SELECT ' ( empty, empty ) '::textrange; SELECT ' ( " a " " a ", " z " " z " ) '::textrange; SELECT '(,z)'::textrange; SELECT '(a,)'::textrange; SELECT '[,z]'::textrange; SELECT '[a,]'::textrange; SELECT '(,)'::textrange; SELECT '[ , ]'::textrange; SELECT '["",""]'::textrange; SELECT '[",",","]'::textrange; SELECT '["\\","\\"]'::textrange; SELECT '(\\,a)'::textrange; SELECT '((,z)'::textrange; SELECT '([,z)'::textrange; SELECT '(!,()'::textrange; SELECT '(!,[)'::textrange; SELECT '[a,a]'::textrange; -- these are allowed but normalize to empty: SELECT '[a,a)'::textrange; SELECT '(a,a]'::textrange; SELECT '(a,a)'::textrange; -- -- create some test data and test the operators -- CREATE TABLE numrange_test ( nr NUMRANGE ); CREATE INDEX numrange_test_btree ON numrange_test (nr); INSERT INTO numrange_test VALUES ('[,)'); INSERT INTO numrange_test VALUES ('[3,]'); INSERT INTO numrange_test VALUES ('[, 5)'); INSERT INTO numrange_test VALUES (numrange(1.1, 2.2)); INSERT INTO numrange_test VALUES ('empty'); INSERT INTO numrange_test VALUES (numrange(1.7, 1.7, '[]')); SELECT nr, isempty(nr), lower(nr), upper(nr) FROM numrange_test; SELECT nr, lower_inc(nr), lower_inf(nr), upper_inc(nr), upper_inf(nr) FROM numrange_test; SELECT * FROM numrange_test WHERE range_contains(nr, numrange(1.9, 1.91)); SELECT * FROM numrange_test WHERE nr @> numrange(1.0, 10000.1); SELECT * FROM numrange_test WHERE range_contained_by(numrange(- 1e7, - 10000.1), nr); SELECT * FROM numrange_test WHERE 1.9 <@ nr; SELECT * FROM numrange_test WHERE nr = 'empty'; SELECT * FROM numrange_test WHERE nr = '(1.1, 2.2)'; SELECT * FROM numrange_test WHERE nr = '[1.1, 2.2)'; SELECT * FROM numrange_test WHERE nr < 'empty'; SELECT * FROM numrange_test WHERE nr < numrange(- 1000.0, - 1000.0, '[]'); SELECT * FROM numrange_test WHERE nr < numrange(0.0, 1.0, '[]'); SELECT * FROM numrange_test WHERE nr < numrange(1000.0, 1001.0, '[]'); SELECT * FROM numrange_test WHERE nr <= 'empty'; SELECT * FROM numrange_test WHERE nr >= 'empty'; SELECT * FROM numrange_test WHERE nr > 'empty'; SELECT * FROM numrange_test WHERE nr > numrange(- 1001.0, - 1000.0, '[]'); SELECT * FROM numrange_test WHERE nr > numrange(0.0, 1.0, '[]'); SELECT * FROM numrange_test WHERE nr > numrange(1000.0, 1000.0, '[]'); SELECT numrange(2.0, 1.0); SELECT numrange(2.0, 3.0) -|- numrange(3.0, 4.0); SELECT range_adjacent(numrange(2.0, 3.0), numrange(3.1, 4.0)); SELECT range_adjacent(numrange(2.0, 3.0), numrange(3.1, NULL)); SELECT numrange(2.0, 3.0, '[]') -|- numrange(3.0, 4.0, '()'); SELECT numrange(1.0, 2.0) -|- numrange(2.0, 3.0, '[]'); SELECT range_adjacent(numrange(2.0, 3.0, '(]'), numrange(1.0, 2.0, '(]')); SELECT numrange(1.1, 3.3) <@ numrange(0.1, 10.1); SELECT numrange(0.1, 10.1) <@ numrange(1.1, 3.3); SELECT numrange(1.1, 2.2) - numrange(2.0, 3.0); SELECT numrange(1.1, 2.2) - numrange(2.2, 3.0); SELECT numrange(1.1, 2.2, '[]') - numrange(2.0, 3.0); SELECT range_minus(numrange(10.1, 12.2, '[]'), numrange(110.0, 120.2, '(]')); SELECT range_minus(numrange(10.1, 12.2, '[]'), numrange(0.0, 120.2, '(]')); SELECT numrange(4.5, 5.5, '[]') && numrange(5.5, 6.5); SELECT numrange(1.0, 2.0) << numrange(3.0, 4.0); SELECT numrange(1.0, 3.0, '[]') << numrange(3.0, 4.0, '[]'); SELECT numrange(1.0, 3.0, '()') << numrange(3.0, 4.0, '()'); SELECT numrange(1.0, 2.0) >> numrange(3.0, 4.0); SELECT numrange(3.0, 70.0) &< numrange(6.6, 100.0); SELECT numrange(1.1, 2.2) < numrange(1.0, 200.2); SELECT numrange(1.1, 2.2) < numrange(1.1, 1.2); SELECT numrange(1.0, 2.0) + numrange(2.0, 3.0); SELECT numrange(1.0, 2.0) + numrange(1.5, 3.0); SELECT numrange(1.0, 2.0) + numrange(2.5, 3.0); -- should fail SELECT range_merge (numrange(1.0, 2.0), numrange(2.0, 3.0)); SELECT range_merge (numrange(1.0, 2.0), numrange(1.5, 3.0)); SELECT range_merge (numrange(1.0, 2.0), numrange(2.5, 3.0)); -- shouldn't fail SELECT numrange(1.0, 2.0) * numrange(2.0, 3.0); SELECT numrange(1.0, 2.0) * numrange(1.5, 3.0); SELECT numrange(1.0, 2.0) * numrange(2.5, 3.0); CREATE TABLE numrange_test2 ( nr numrange ); CREATE INDEX numrange_test2_hash_idx ON numrange_test2 (nr); INSERT INTO numrange_test2 VALUES ('[, 5)'); INSERT INTO numrange_test2 VALUES (numrange(1.1, 2.2)); INSERT INTO numrange_test2 VALUES (numrange(1.1, 2.2)); INSERT INTO numrange_test2 VALUES (numrange(1.1, 2.2, '()')); INSERT INTO numrange_test2 VALUES ('empty'); SELECT * FROM numrange_test2 WHERE nr = 'empty'::numrange; SELECT * FROM numrange_test2 WHERE nr = numrange(1.1, 2.2); SELECT * FROM numrange_test2 WHERE nr = numrange(1.1, 2.3); SET enable_nestloop = t; SET enable_hashjoin = f; SET enable_mergejoin = f; SELECT * FROM numrange_test NATURAL JOIN numrange_test2 ORDER BY nr; SET enable_nestloop = f; SET enable_hashjoin = t; SET enable_mergejoin = f; SELECT * FROM numrange_test NATURAL JOIN numrange_test2 ORDER BY nr; SET enable_nestloop = f; SET enable_hashjoin = f; SET enable_mergejoin = t; SELECT * FROM numrange_test NATURAL JOIN numrange_test2 ORDER BY nr; SET enable_nestloop TO DEFAULT; SET enable_hashjoin TO DEFAULT; SET enable_mergejoin TO DEFAULT; DROP TABLE numrange_test; DROP TABLE numrange_test2; -- test canonical form for int4range SELECT int4range(1, 10, '[]'); SELECT int4range(1, 10, '[)'); SELECT int4range(1, 10, '(]'); SELECT int4range(1, 10, '()'); SELECT int4range(1, 2, '()'); -- test canonical form for daterange SELECT daterange('2000-01-10'::date, '2000-01-20'::date, '[]'); SELECT daterange('2000-01-10'::date, '2000-01-20'::date, '[)'); SELECT daterange('2000-01-10'::date, '2000-01-20'::date, '(]'); SELECT daterange('2000-01-10'::date, '2000-01-20'::date, '()'); SELECT daterange('2000-01-10'::date, '2000-01-11'::date, '()'); SELECT daterange('2000-01-10'::date, '2000-01-11'::date, '(]'); -- test GiST index that's been built incrementally CREATE TABLE test_range_gist ( ir int4range ); CREATE INDEX test_range_gist_idx ON test_range_gist USING gist (ir); INSERT INTO test_range_gist SELECT int4range(g, g + 10) FROM generate_series(1, 2000) g; INSERT INTO test_range_gist SELECT 'empty'::int4range FROM generate_series(1, 500) g; INSERT INTO test_range_gist SELECT int4range(g, g + 10000) FROM generate_series(1, 1000) g; INSERT INTO test_range_gist SELECT 'empty'::int4range FROM generate_series(1, 500) g; INSERT INTO test_range_gist SELECT int4range(NULL, g * 10, '(]') FROM generate_series(1, 100) g; INSERT INTO test_range_gist SELECT int4range(g * 10, NULL, '(]') FROM generate_series(1, 100) g; INSERT INTO test_range_gist SELECT int4range(g, g + 10) FROM generate_series(1, 2000) g; -- first, verify non-indexed results SET enable_seqscan = t; SET enable_indexscan = f; SET enable_bitmapscan = f; SELECT count(*) FROM test_range_gist WHERE ir @> 'empty'::int4range; SELECT count(*) FROM test_range_gist WHERE ir = int4range(10, 20); SELECT count(*) FROM test_range_gist WHERE ir @> 10; SELECT count(*) FROM test_range_gist WHERE ir @> int4range(10, 20); SELECT count(*) FROM test_range_gist WHERE ir && int4range(10, 20); SELECT count(*) FROM test_range_gist WHERE ir <@ int4range(10, 50); SELECT count(*) FROM test_range_gist WHERE ir << int4range(100, 500); SELECT count(*) FROM test_range_gist WHERE ir >> int4range(100, 500); SELECT count(*) FROM test_range_gist WHERE ir &< int4range(100, 500); SELECT count(*) FROM test_range_gist WHERE ir &> int4range(100, 500); SELECT count(*) FROM test_range_gist WHERE ir -|- int4range(100, 500); -- now check same queries using index SET enable_seqscan = f; SET enable_indexscan = t; SET enable_bitmapscan = f; SELECT count(*) FROM test_range_gist WHERE ir @> 'empty'::int4range; SELECT count(*) FROM test_range_gist WHERE ir = int4range(10, 20); SELECT count(*) FROM test_range_gist WHERE ir @> 10; SELECT count(*) FROM test_range_gist WHERE ir @> int4range(10, 20); SELECT count(*) FROM test_range_gist WHERE ir && int4range(10, 20); SELECT count(*) FROM test_range_gist WHERE ir <@ int4range(10, 50); SELECT count(*) FROM test_range_gist WHERE ir << int4range(100, 500); SELECT count(*) FROM test_range_gist WHERE ir >> int4range(100, 500); SELECT count(*) FROM test_range_gist WHERE ir &< int4range(100, 500); SELECT count(*) FROM test_range_gist WHERE ir &> int4range(100, 500); SELECT count(*) FROM test_range_gist WHERE ir -|- int4range(100, 500); -- now check same queries using a bulk-loaded index DROP INDEX test_range_gist_idx; CREATE INDEX test_range_gist_idx ON test_range_gist USING gist (ir); SELECT count(*) FROM test_range_gist WHERE ir @> 'empty'::int4range; SELECT count(*) FROM test_range_gist WHERE ir = int4range(10, 20); SELECT count(*) FROM test_range_gist WHERE ir @> 10; SELECT count(*) FROM test_range_gist WHERE ir @> int4range(10, 20); SELECT count(*) FROM test_range_gist WHERE ir && int4range(10, 20); SELECT count(*) FROM test_range_gist WHERE ir <@ int4range(10, 50); SELECT count(*) FROM test_range_gist WHERE ir << int4range(100, 500); SELECT count(*) FROM test_range_gist WHERE ir >> int4range(100, 500); SELECT count(*) FROM test_range_gist WHERE ir &< int4range(100, 500); SELECT count(*) FROM test_range_gist WHERE ir &> int4range(100, 500); SELECT count(*) FROM test_range_gist WHERE ir -|- int4range(100, 500); -- test SP-GiST index that's been built incrementally CREATE TABLE test_range_spgist ( ir int4range ); CREATE INDEX test_range_spgist_idx ON test_range_spgist USING spgist (ir); INSERT INTO test_range_spgist SELECT int4range(g, g + 10) FROM generate_series(1, 2000) g; INSERT INTO test_range_spgist SELECT 'empty'::int4range FROM generate_series(1, 500) g; INSERT INTO test_range_spgist SELECT int4range(g, g + 10000) FROM generate_series(1, 1000) g; INSERT INTO test_range_spgist SELECT 'empty'::int4range FROM generate_series(1, 500) g; INSERT INTO test_range_spgist SELECT int4range(NULL, g * 10, '(]') FROM generate_series(1, 100) g; INSERT INTO test_range_spgist SELECT int4range(g * 10, NULL, '(]') FROM generate_series(1, 100) g; INSERT INTO test_range_spgist SELECT int4range(g, g + 10) FROM generate_series(1, 2000) g; -- first, verify non-indexed results SET enable_seqscan = t; SET enable_indexscan = f; SET enable_bitmapscan = f; SELECT count(*) FROM test_range_spgist WHERE ir @> 'empty'::int4range; SELECT count(*) FROM test_range_spgist WHERE ir = int4range(10, 20); SELECT count(*) FROM test_range_spgist WHERE ir @> 10; SELECT count(*) FROM test_range_spgist WHERE ir @> int4range(10, 20); SELECT count(*) FROM test_range_spgist WHERE ir && int4range(10, 20); SELECT count(*) FROM test_range_spgist WHERE ir <@ int4range(10, 50); SELECT count(*) FROM test_range_spgist WHERE ir << int4range(100, 500); SELECT count(*) FROM test_range_spgist WHERE ir >> int4range(100, 500); SELECT count(*) FROM test_range_spgist WHERE ir &< int4range(100, 500); SELECT count(*) FROM test_range_spgist WHERE ir &> int4range(100, 500); SELECT count(*) FROM test_range_spgist WHERE ir -|- int4range(100, 500); -- now check same queries using index SET enable_seqscan = f; SET enable_indexscan = t; SET enable_bitmapscan = f; SELECT count(*) FROM test_range_spgist WHERE ir @> 'empty'::int4range; SELECT count(*) FROM test_range_spgist WHERE ir = int4range(10, 20); SELECT count(*) FROM test_range_spgist WHERE ir @> 10; SELECT count(*) FROM test_range_spgist WHERE ir @> int4range(10, 20); SELECT count(*) FROM test_range_spgist WHERE ir && int4range(10, 20); SELECT count(*) FROM test_range_spgist WHERE ir <@ int4range(10, 50); SELECT count(*) FROM test_range_spgist WHERE ir << int4range(100, 500); SELECT count(*) FROM test_range_spgist WHERE ir >> int4range(100, 500); SELECT count(*) FROM test_range_spgist WHERE ir &< int4range(100, 500); SELECT count(*) FROM test_range_spgist WHERE ir &> int4range(100, 500); SELECT count(*) FROM test_range_spgist WHERE ir -|- int4range(100, 500); -- now check same queries using a bulk-loaded index DROP INDEX test_range_spgist_idx; CREATE INDEX test_range_spgist_idx ON test_range_spgist USING spgist (ir); SELECT count(*) FROM test_range_spgist WHERE ir @> 'empty'::int4range; SELECT count(*) FROM test_range_spgist WHERE ir = int4range(10, 20); SELECT count(*) FROM test_range_spgist WHERE ir @> 10; SELECT count(*) FROM test_range_spgist WHERE ir @> int4range(10, 20); SELECT count(*) FROM test_range_spgist WHERE ir && int4range(10, 20); SELECT count(*) FROM test_range_spgist WHERE ir <@ int4range(10, 50); SELECT count(*) FROM test_range_spgist WHERE ir << int4range(100, 500); SELECT count(*) FROM test_range_spgist WHERE ir >> int4range(100, 500); SELECT count(*) FROM test_range_spgist WHERE ir &< int4range(100, 500); SELECT count(*) FROM test_range_spgist WHERE ir &> int4range(100, 500); SELECT count(*) FROM test_range_spgist WHERE ir -|- int4range(100, 500); -- test index-only scans EXPLAIN ( COSTS OFF ) SELECT ir FROM test_range_spgist WHERE ir -|- int4range(10, 20) ORDER BY ir; SELECT ir FROM test_range_spgist WHERE ir -|- int4range(10, 20) ORDER BY ir; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; -- test elem <@ range operator CREATE TABLE test_range_elem ( i int4 ); CREATE INDEX test_range_elem_idx ON test_range_elem (i); INSERT INTO test_range_elem SELECT i FROM generate_series(1, 100) i; SELECT count(*) FROM test_range_elem WHERE i <@ int4range(10, 50); DROP TABLE test_range_elem; -- -- Btree_gist is not included by default, so to test exclusion -- constraints with range types, use singleton int ranges for the "=" -- portion of the constraint. -- CREATE TABLE test_range_excl ( room int4range, speaker int4range, during tsrange, EXCLUDE USING gist (room WITH =, during WITH &&), EXCLUDE USING gist (speaker WITH =, during WITH &&) ); INSERT INTO test_range_excl VALUES (int4range(123, 123, '[]'), int4range(1, 1, '[]'), '[2010-01-02 10:00, 2010-01-02 11:00)'); INSERT INTO test_range_excl VALUES (int4range(123, 123, '[]'), int4range(2, 2, '[]'), '[2010-01-02 11:00, 2010-01-02 12:00)'); INSERT INTO test_range_excl VALUES (int4range(123, 123, '[]'), int4range(3, 3, '[]'), '[2010-01-02 10:10, 2010-01-02 11:00)'); INSERT INTO test_range_excl VALUES (int4range(124, 124, '[]'), int4range(3, 3, '[]'), '[2010-01-02 10:10, 2010-01-02 11:10)'); INSERT INTO test_range_excl VALUES (int4range(125, 125, '[]'), int4range(1, 1, '[]'), '[2010-01-02 10:10, 2010-01-02 11:00)'); -- test bigint ranges SELECT int8range(10000000000::int8, 20000000000::int8, '(]'); -- test tstz ranges SET timezone TO '-08'; SELECT '[2010-01-01 01:00:00 -05, 2010-01-01 02:00:00 -08)'::tstzrange; -- should fail SELECT '[2010-01-01 01:00:00 -08, 2010-01-01 02:00:00 -05)'::tstzrange; SET timezone TO DEFAULT; -- -- Test user-defined range of floats -- --should fail CREATE TYPE float8range AS RANGE ( subtype = float8, subtype_diff = float4mi ); --should succeed CREATE TYPE float8range AS RANGE ( subtype = float8, subtype_diff = float8mi ); SELECT '[123.001, 5.e9)'::float8range @> 888.882::float8; CREATE TABLE float8range_test ( f8r float8range, i int ); INSERT INTO float8range_test VALUES (float8range (- 100.00007, '1.111113e9'), 42); SELECT * FROM float8range_test; DROP TABLE float8range_test; -- -- Test range types over domains -- CREATE DOMAIN mydomain AS int4; CREATE TYPE mydomainrange AS RANGE ( subtype = mydomain ); SELECT '[4,50)'::mydomainrange @> 7::mydomain; DROP DOMAIN mydomain; -- fail DROP DOMAIN mydomain CASCADE; -- -- Test domains over range types -- CREATE DOMAIN restrictedrange AS int4range CHECK (upper(value) < 10); SELECT '[4,5)'::restrictedrange @> 7; SELECT '[4,50)'::restrictedrange @> 7; -- should fail DROP DOMAIN restrictedrange; -- -- Test multiple range types over the same subtype -- CREATE TYPE textrange1 AS RANGE ( subtype = text, COLLATION = "C" ); CREATE TYPE textrange2 AS RANGE ( subtype = text, COLLATION = "C" ); SELECT textrange1 ('a', 'Z') @> 'b'::text; SELECT textrange2 ('a', 'z') @> 'b'::text; DROP TYPE textrange1; DROP TYPE textrange2; -- -- Test polymorphic type system -- CREATE FUNCTION anyarray_anyrange_func (a anyarray, r anyrange) RETURNS anyelement AS 'select $1[1] + lower($2);' LANGUAGE sql; SELECT anyarray_anyrange_func (ARRAY[1, 2], int4range(10, 20)); -- should fail SELECT anyarray_anyrange_func (ARRAY[1, 2], numrange(10, 20)); DROP FUNCTION anyarray_anyrange_func (anyarray, anyrange); -- should fail CREATE FUNCTION bogus_func (anyelement) RETURNS anyrange AS 'select int4range(1,10)' LANGUAGE sql; -- should fail CREATE FUNCTION bogus_func (int) RETURNS anyrange AS 'select int4range(1,10)' LANGUAGE sql; CREATE FUNCTION range_add_bounds (anyrange) RETURNS anyelement AS 'select lower($1) + upper($1)' LANGUAGE sql; SELECT range_add_bounds (int4range(1, 17)); SELECT range_add_bounds (numrange(1.0001, 123.123)); CREATE FUNCTION rangetypes_sql (q anyrange, b anyarray, out c anyelement ) AS $$ SELECT upper($1) + $2[1] $$ LANGUAGE sql; SELECT rangetypes_sql (int4range(1, 10), ARRAY[2, 20]); SELECT rangetypes_sql (numrange(1, 10), ARRAY[2, 20]); -- match failure -- -- Arrays of ranges -- SELECT ARRAY[numrange(1.1, 1.2), numrange(12.3, 155.5)]; CREATE TABLE i8r_array ( f1 int, f2 int8range[] ); INSERT INTO i8r_array VALUES (42, ARRAY[int8range(1, 10), int8range(2, 20)]); SELECT * FROM i8r_array; DROP TABLE i8r_array; -- -- Ranges of arrays -- CREATE TYPE arrayrange AS RANGE ( subtype = int4[] ); SELECT arrayrange (ARRAY[1, 2], ARRAY[2, 1]); SELECT arrayrange (ARRAY[2, 1], ARRAY[1, 2]); -- fail SELECT ARRAY[1, 1] <@ arrayrange (ARRAY[1, 2], ARRAY[2, 1]); SELECT ARRAY[1, 3] <@ arrayrange (ARRAY[1, 2], ARRAY[2, 1]); -- -- Ranges of composites -- CREATE TYPE two_ints AS ( a int, b int ); CREATE TYPE two_ints_range AS RANGE ( subtype = two_ints ); -- with force_parallel_mode on, this exercises tqueue.c's range remapping SELECT *, row_to_json(upper(t)) AS u FROM ( VALUES (two_ints_range (ROW (1, 2), ROW (3, 4))), (two_ints_range (ROW (5, 6), ROW (7, 8)))) v (t); DROP TYPE two_ints CASCADE; -- -- Check behavior when subtype lacks a hash function -- CREATE TYPE cashrange AS RANGE ( subtype = money ); SET enable_sort = OFF; -- try to make it pick a hash setop implementation SELECT '(2,5)'::cashrange EXCEPT SELECT '(5,6)'::cashrange; RESET enable_sort; -- -- OUT/INOUT/TABLE functions -- CREATE FUNCTION outparam_succeed (i anyrange, out r anyrange, out t text ) AS $$ SELECT $1, 'foo'::text $$ LANGUAGE sql; SELECT * FROM outparam_succeed (int4range(1, 2)); CREATE FUNCTION inoutparam_succeed (out i anyelement, INOUT r anyrange ) AS $$ SELECT upper($1), $1 $$ LANGUAGE sql; SELECT * FROM inoutparam_succeed (int4range(1, 2)); CREATE FUNCTION table_succeed (i anyelement, r anyrange) RETURNS TABLE ( i anyelement, r anyrange ) AS $$ SELECT $1, $2 $$ LANGUAGE sql; SELECT * FROM table_succeed (123, int4range(1, 11)); -- should fail CREATE FUNCTION outparam_fail (i anyelement, out r anyrange, out t text ) AS $$ SELECT '[1,10]', 'foo' $$ LANGUAGE sql; --should fail CREATE FUNCTION inoutparam_fail (INOUT i anyelement, out r anyrange ) AS $$ SELECT $1, '[1,10]' $$ LANGUAGE sql; --should fail CREATE FUNCTION table_fail (i anyelement) RETURNS TABLE ( i anyelement, r anyrange ) AS $$ SELECT $1, '[1,10]' $$ LANGUAGE sql; pgFormatter-4.2/t/pg-test-files/expected/regex.linux.utf8.sql000066400000000000000000000035001361326045100242550ustar00rootroot00000000000000/* * This test is for Linux/glibc systems and others that implement proper * locale classification of Unicode characters with high code values. * It must be run in a database with UTF8 encoding and a Unicode-aware locale. */ SET client_encoding TO UTF8; -- -- Test the "high colormap" logic with single characters and ranges that -- exceed the MAX_SIMPLE_CHR cutoff, here assumed to be less than U+2000. -- -- trivial cases: SELECT 'aⓐ' ~ U & 'a\24D0' AS t; SELECT 'aⓐ' ~ U & 'a\24D1' AS f; SELECT 'aⓕ' ~ 'a[ⓐ-ⓩ]' AS t; SELECT 'aⒻ' ~ 'a[ⓐ-ⓩ]' AS f; -- cases requiring splitting of ranges: SELECT 'aⓕⓕ' ~ 'aⓕ[ⓐ-ⓩ]' AS t; SELECT 'aⓕⓐ' ~ 'aⓕ[ⓐ-ⓩ]' AS t; SELECT 'aⓐⓕ' ~ 'aⓕ[ⓐ-ⓩ]' AS f; SELECT 'aⓕⓕ' ~ 'a[ⓐ-ⓩ]ⓕ' AS t; SELECT 'aⓕⓐ' ~ 'a[ⓐ-ⓩ]ⓕ' AS f; SELECT 'aⓐⓕ' ~ 'a[ⓐ-ⓩ]ⓕ' AS t; SELECT 'aⒶⓜ' ~ 'a[Ⓐ-ⓜ][ⓜ-ⓩ]' AS t; SELECT 'aⓜⓜ' ~ 'a[Ⓐ-ⓜ][ⓜ-ⓩ]' AS t; SELECT 'aⓜⓩ' ~ 'a[Ⓐ-ⓜ][ⓜ-ⓩ]' AS t; SELECT 'aⓩⓩ' ~ 'a[Ⓐ-ⓜ][ⓜ-ⓩ]' AS f; SELECT 'aⓜ⓪' ~ 'a[Ⓐ-ⓜ][ⓜ-ⓩ]' AS f; SELECT 'a0' ~ 'a[a-ⓩ]' AS f; SELECT 'aq' ~ 'a[a-ⓩ]' AS t; SELECT 'aⓜ' ~ 'a[a-ⓩ]' AS t; SELECT 'a⓪' ~ 'a[a-ⓩ]' AS f; -- Locale-dependent character classes SELECT 'aⒶⓜ⓪' ~ '[[:alpha:]][[:alpha:]][[:alpha:]][[:graph:]]' AS t; SELECT 'aⒶⓜ⓪' ~ '[[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]]' AS f; -- Locale-dependent character classes with high ranges SELECT 'aⒶⓜ⓪' ~ '[a-z][[:alpha:]][ⓐ-ⓩ][[:graph:]]' AS t; SELECT 'aⓜⒶ⓪' ~ '[a-z][[:alpha:]][ⓐ-ⓩ][[:graph:]]' AS f; SELECT 'aⓜⒶ⓪' ~ '[a-z][ⓐ-ⓩ][[:alpha:]][[:graph:]]' AS t; SELECT 'aⒶⓜ⓪' ~ '[a-z][ⓐ-ⓩ][[:alpha:]][[:graph:]]' AS f; pgFormatter-4.2/t/pg-test-files/expected/regex.sql000066400000000000000000000125621361326045100222420ustar00rootroot00000000000000-- -- Regular expression tests -- -- Don't want to have to double backslashes in regexes SET standard_conforming_strings = ON; -- Test simple quantified backrefs SELECT 'bbbbb' ~ '^([bc])\1*$' AS t; SELECT 'ccc' ~ '^([bc])\1*$' AS t; SELECT 'xxx' ~ '^([bc])\1*$' AS f; SELECT 'bbc' ~ '^([bc])\1*$' AS f; SELECT 'b' ~ '^([bc])\1*$' AS t; -- Test quantified backref within a larger expression SELECT 'abc abc abc' ~ '^(\w+)( \1)+$' AS t; SELECT 'abc abd abc' ~ '^(\w+)( \1)+$' AS f; SELECT 'abc abc abd' ~ '^(\w+)( \1)+$' AS f; SELECT 'abc abc abc' ~ '^(.+)( \1)+$' AS t; SELECT 'abc abd abc' ~ '^(.+)( \1)+$' AS f; SELECT 'abc abc abd' ~ '^(.+)( \1)+$' AS f; -- Test some cases that crashed in 9.2beta1 due to pmatch[] array overrun SELECT substring('asd TO foo' FROM ' TO (([a-z0-9._]+|"([^"]+|"")+")+)'); SELECT substring('a' FROM '((a))+'); SELECT substring('a' FROM '((a)+)'); -- Test regexp_match() SELECT regexp_match ('abc', ''); SELECT regexp_match ('abc', 'bc'); SELECT regexp_match ('abc', 'd') IS NULL; SELECT regexp_match ('abc', '(B)(c)', 'i'); SELECT regexp_match ('abc', 'Bd', 'ig'); -- error -- Test lookahead constraints SELECT regexp_matches('ab', 'a(?=b)b*'); SELECT regexp_matches('a', 'a(?=b)b*'); SELECT regexp_matches('abc', 'a(?=b)b*(?=c)c*'); SELECT regexp_matches('ab', 'a(?=b)b*(?=c)c*'); SELECT regexp_matches('ab', 'a(?!b)b*'); SELECT regexp_matches('a', 'a(?!b)b*'); SELECT regexp_matches('b', '(?=b)b'); SELECT regexp_matches('a', '(?=b)b'); -- Test lookbehind constraints SELECT regexp_matches('abb', '(?<=a)b*'); SELECT regexp_matches('a', 'a(?<=a)b*'); SELECT regexp_matches('abc', 'a(?<=a)b*(?<=b)c*'); SELECT regexp_matches('ab', 'a(?<=a)b*(?<=b)c*'); SELECT regexp_matches('ab', 'a*(? 0; SELECT reloptions FROM pg_class WHERE oid = ( SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass); ALTER TABLE reloptions_test RESET (vacuum_truncate); SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; INSERT INTO reloptions_test VALUES (1, NULL), (NULL, NULL); VACUUM reloptions_test; SELECT pg_relation_size('reloptions_test') = 0; -- Test toast.* options DROP TABLE reloptions_test; CREATE TABLE reloptions_test ( s varchar ) WITH ( toast.autovacuum_vacuum_cost_delay = 23 ); SELECT reltoastrelid AS toast_oid FROM pg_class WHERE oid = 'reloptions_test'::regclass \gset SELECT reloptions FROM pg_class WHERE oid = :toast_oid; ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24); SELECT reloptions FROM pg_class WHERE oid = :toast_oid; ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay); SELECT reloptions FROM pg_class WHERE oid = :toast_oid; -- Fail on non-existent options in toast namespace CREATE TABLE reloptions_test2 ( i int ) WITH ( toast.not_existing_option = 42 ); -- Mix TOAST & heap DROP TABLE reloptions_test; CREATE TABLE reloptions_test ( s varchar ) WITH ( toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40 ); SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; SELECT reloptions FROM pg_class WHERE oid = ( SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass); -- -- CREATE INDEX, ALTER INDEX for btrees -- CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor = 30); SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass; -- Fail when option and namespace do not exist CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option = 2); CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_ns.fillfactor = 2); -- Check allowed ranges CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor = 1); CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor = 130); -- Check ALTER ALTER INDEX reloptions_test_idx SET (fillfactor = 40); SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass; -- Check ALTER on empty reloption list CREATE INDEX reloptions_test_idx3 ON reloptions_test (s); ALTER INDEX reloptions_test_idx3 SET (fillfactor = 40); SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass; pgFormatter-4.2/t/pg-test-files/expected/replica_identity.sql000066400000000000000000000075461361326045100244660ustar00rootroot00000000000000CREATE TABLE test_replica_identity ( id serial PRIMARY KEY, keya text NOT NULL, keyb text NOT NULL, nonkey text, CONSTRAINT test_replica_identity_unique_defer UNIQUE (keya, keyb) DEFERRABLE, CONSTRAINT test_replica_identity_unique_nondefer UNIQUE (keya, keyb) ); CREATE TABLE test_replica_identity_othertable ( id serial PRIMARY KEY ); CREATE INDEX test_replica_identity_keyab ON test_replica_identity (keya, keyb); CREATE UNIQUE INDEX test_replica_identity_keyab_key ON test_replica_identity (keya, keyb); CREATE UNIQUE INDEX test_replica_identity_nonkey ON test_replica_identity (keya, nonkey); CREATE INDEX test_replica_identity_hash ON test_replica_identity USING HASH (nonkey); CREATE UNIQUE INDEX test_replica_identity_expr ON test_replica_identity (keya, keyb, (3)); CREATE UNIQUE INDEX test_replica_identity_partial ON test_replica_identity (keya, keyb) WHERE keyb != '3'; -- default is 'd'/DEFAULT for user created tables SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; -- but 'none' for system tables SELECT relreplident FROM pg_class WHERE oid = 'pg_class'::regclass; SELECT relreplident FROM pg_class WHERE oid = 'pg_constraint'::regclass; ---- -- Make sure we detect ineligible indexes ---- -- fail, not unique ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_keyab; -- fail, not a candidate key, nullable column ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_nonkey; -- fail, hash indexes cannot do uniqueness ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_hash; -- fail, expression index ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_expr; -- fail, partial index ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_partial; -- fail, not our index ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_othertable_pkey; -- fail, deferrable ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_unique_defer; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; ---- -- Make sure index cases succeed ---- -- succeed, primary key ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_pkey; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; \d test_replica_identity -- succeed, nondeferrable unique constraint over nonnullable cols ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_unique_nondefer; -- succeed unique index over nonnullable cols ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_keyab_key; ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_keyab_key; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; \d test_replica_identity SELECT count(*) FROM pg_index WHERE indrelid = 'test_replica_identity'::regclass AND indisreplident; ---- -- Make sure non index cases work ---- ALTER TABLE test_replica_identity REPLICA IDENTITY DEFAULT; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; SELECT count(*) FROM pg_index WHERE indrelid = 'test_replica_identity'::regclass AND indisreplident; ALTER TABLE test_replica_identity REPLICA IDENTITY FULL; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; \d+ test_replica_identity ALTER TABLE test_replica_identity REPLICA IDENTITY NOTHING; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; DROP TABLE test_replica_identity; DROP TABLE test_replica_identity_othertable; pgFormatter-4.2/t/pg-test-files/expected/returning.sql000066400000000000000000000115641361326045100231460ustar00rootroot00000000000000-- -- Test INSERT/UPDATE/DELETE RETURNING -- -- Simple cases CREATE TEMP TABLE foo ( f1 serial, f2 text, f3 int DEFAULT 42 ); INSERT INTO foo (f2, f3) VALUES ('test', DEFAULT), ('More', 11), (upper('more'), 7 + 9) RETURNING *, f1 + f3 AS sum; SELECT * FROM foo; UPDATE foo SET f2 = lower(f2), f3 = DEFAULT RETURNING foo.*, f1 + f3 AS sum13; SELECT * FROM foo; DELETE FROM foo WHERE f1 > 2 RETURNING f3, f2, f1, least (f1, f3); SELECT * FROM foo; -- Subplans and initplans in the RETURNING list INSERT INTO foo SELECT f1 + 10, f2, f3 + 99 FROM foo RETURNING *, f1 + 112 IN ( SELECT q1 FROM int8_tbl) AS subplan, EXISTS ( SELECT * FROM int4_tbl) AS initplan; UPDATE foo SET f3 = f3 * 2 WHERE f1 > 10 RETURNING *, f1 + 112 IN ( SELECT q1 FROM int8_tbl) AS subplan, EXISTS ( SELECT * FROM int4_tbl) AS initplan; DELETE FROM foo WHERE f1 > 10 RETURNING *, f1 + 112 IN ( SELECT q1 FROM int8_tbl) AS subplan, EXISTS ( SELECT * FROM int4_tbl) AS initplan; -- Joins UPDATE foo SET f3 = f3 * 2 FROM int4_tbl i WHERE foo.f1 + 123455 = i.f1 RETURNING foo.*, i.f1 AS "i.f1"; SELECT * FROM foo; DELETE FROM foo USING int4_tbl i WHERE foo.f1 + 123455 = i.f1 RETURNING foo.*, i.f1 AS "i.f1"; SELECT * FROM foo; -- Check inheritance cases CREATE TEMP TABLE foochild ( fc int ) INHERITS ( foo ); INSERT INTO foochild VALUES (123, 'child', 999, - 123); ALTER TABLE foo ADD COLUMN f4 int8 DEFAULT 99; SELECT * FROM foo; SELECT * FROM foochild; UPDATE foo SET f4 = f4 + f3 WHERE f4 = 99 RETURNING *; SELECT * FROM foo; SELECT * FROM foochild; UPDATE foo SET f3 = f3 * 2 FROM int8_tbl i WHERE foo.f1 = i.q2 RETURNING *; SELECT * FROM foo; SELECT * FROM foochild; DELETE FROM foo USING int8_tbl i WHERE foo.f1 = i.q2 RETURNING *; SELECT * FROM foo; SELECT * FROM foochild; DROP TABLE foochild; -- Rules and views CREATE TEMP VIEW voo AS SELECT f1, f2 FROM foo; CREATE RULE voo_i AS ON INSERT TO voo DO INSTEAD INSERT INTO foo VALUES (new.*, 57); INSERT INTO voo VALUES (11, 'zit'); -- fails: INSERT INTO voo VALUES (12, 'zoo') RETURNING *, f1 * 2; -- fails, incompatible list: CREATE OR REPLACE RULE voo_i AS ON INSERT TO voo DO INSTEAD INSERT INTO foo VALUES ( new.*, 57) RETURNING *; CREATE OR REPLACE RULE voo_i AS ON INSERT TO voo DO INSTEAD INSERT INTO foo VALUES ( new.*, 57) RETURNING f1, f2; -- should still work INSERT INTO voo VALUES (13, 'zit2'); -- works now INSERT INTO voo VALUES (14, 'zoo2') RETURNING *; SELECT * FROM foo; SELECT * FROM voo; CREATE OR REPLACE RULE voo_u AS ON UPDATE TO voo DO INSTEAD UPDATE foo SET f1 = new.f1, f2 = new.f2 WHERE f1 = old.f1 RETURNING f1, f2; UPDATE voo SET f1 = f1 + 1 WHERE f2 = 'zoo2'; UPDATE voo SET f1 = f1 + 1 WHERE f2 = 'zoo2' RETURNING *, f1 * 2; SELECT * FROM foo; SELECT * FROM voo; CREATE OR REPLACE RULE voo_d AS ON DELETE TO voo DO INSTEAD DELETE FROM foo WHERE f1 = old.f1 RETURNING f1, f2; DELETE FROM foo WHERE f1 = 13; DELETE FROM foo WHERE f2 = 'zit' RETURNING *; SELECT * FROM foo; SELECT * FROM voo; -- Try a join case CREATE TEMP TABLE joinme ( f2j text, other int ); INSERT INTO joinme VALUES ('more', 12345); INSERT INTO joinme VALUES ('zoo2', 54321); INSERT INTO joinme VALUES ('other', 0); CREATE TEMP VIEW joinview AS SELECT foo.*, other FROM foo JOIN joinme ON (f2 = f2j); SELECT * FROM joinview; CREATE RULE joinview_u AS ON UPDATE TO joinview DO INSTEAD UPDATE foo SET f1 = new.f1, f3 = new.f3 FROM joinme WHERE f2 = f2j AND f2 = old.f2 RETURNING foo.*, other; UPDATE joinview SET f1 = f1 + 1 WHERE f3 = 57 RETURNING *, other + 1; SELECT * FROM joinview; SELECT * FROM foo; SELECT * FROM voo; -- Check aliased target relation INSERT INTO foo AS bar DEFAULT VALUES RETURNING *; -- ok INSERT INTO foo AS bar DEFAULT VALUES RETURNING foo.*; -- fails, wrong name INSERT INTO foo AS bar DEFAULT VALUES RETURNING bar.*; -- ok INSERT INTO foo AS bar DEFAULT VALUES RETURNING bar.f3; -- ok pgFormatter-4.2/t/pg-test-files/expected/roleattributes.sql000066400000000000000000000252161361326045100242000ustar00rootroot00000000000000-- default for superuser is false CREATE ROLE regress_test_def_superuser; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_def_superuser'; CREATE ROLE regress_test_superuser WITH SUPERUSER; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_superuser'; ALTER ROLE regress_test_superuser WITH NOSUPERUSER; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_superuser'; ALTER ROLE regress_test_superuser WITH SUPERUSER; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_superuser'; -- default for inherit is true CREATE ROLE regress_test_def_inherit; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_def_inherit'; CREATE ROLE regress_test_inherit WITH NOINHERIT; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_inherit'; ALTER ROLE regress_test_inherit WITH INHERIT; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_inherit'; ALTER ROLE regress_test_inherit WITH NOINHERIT; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_inherit'; -- default for create role is false CREATE ROLE regress_test_def_createrole; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_def_createrole'; CREATE ROLE regress_test_createrole WITH CREATEROLE; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_createrole'; ALTER ROLE regress_test_createrole WITH NOCREATEROLE; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_createrole'; ALTER ROLE regress_test_createrole WITH CREATEROLE; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_createrole'; -- default for create database is false CREATE ROLE regress_test_def_createdb; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_def_createdb'; CREATE ROLE regress_test_createdb WITH CREATEDB; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_createdb'; ALTER ROLE regress_test_createdb WITH NOCREATEDB; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_createdb'; ALTER ROLE regress_test_createdb WITH CREATEDB; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_createdb'; -- default for can login is false for role CREATE ROLE regress_test_def_role_canlogin; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_def_role_canlogin'; CREATE ROLE regress_test_role_canlogin WITH LOGIN; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_role_canlogin'; ALTER ROLE regress_test_role_canlogin WITH NOLOGIN; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_role_canlogin'; ALTER ROLE regress_test_role_canlogin WITH LOGIN; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_role_canlogin'; -- default for can login is true for user CREATE USER regress_test_def_user_canlogin; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_def_user_canlogin'; CREATE USER regress_test_user_canlogin WITH NOLOGIN; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_user_canlogin'; ALTER USER regress_test_user_canlogin WITH LOGIN; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_user_canlogin'; ALTER USER regress_test_user_canlogin WITH NOLOGIN; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_user_canlogin'; -- default for replication is false CREATE ROLE regress_test_def_replication; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_def_replication'; CREATE ROLE regress_test_replication WITH REPLICATION; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_replication'; ALTER ROLE regress_test_replication WITH NOREPLICATION; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_replication'; ALTER ROLE regress_test_replication WITH REPLICATION; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_replication'; -- default for bypassrls is false CREATE ROLE regress_test_def_bypassrls; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_def_bypassrls'; CREATE ROLE regress_test_bypassrls WITH BYPASSRLS; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_bypassrls'; ALTER ROLE regress_test_bypassrls WITH NOBYPASSRLS; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_bypassrls'; ALTER ROLE regress_test_bypassrls WITH BYPASSRLS; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_bypassrls'; -- clean up roles DROP ROLE regress_test_def_superuser; DROP ROLE regress_test_superuser; DROP ROLE regress_test_def_inherit; DROP ROLE regress_test_inherit; DROP ROLE regress_test_def_createrole; DROP ROLE regress_test_createrole; DROP ROLE regress_test_def_createdb; DROP ROLE regress_test_createdb; DROP ROLE regress_test_def_role_canlogin; DROP ROLE regress_test_role_canlogin; DROP USER regress_test_def_user_canlogin; DROP USER regress_test_user_canlogin; DROP ROLE regress_test_def_replication; DROP ROLE regress_test_replication; DROP ROLE regress_test_def_bypassrls; DROP ROLE regress_test_bypassrls; pgFormatter-4.2/t/pg-test-files/expected/rolenames.sql000066400000000000000000000476061361326045100231240ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION chkrolattr () RETURNS TABLE ( "role" name, rolekeyword text, canlogin bool, replication bool ) AS $$ SELECT r.rolname, v.keyword, r.rolcanlogin, r.rolreplication FROM pg_roles r JOIN ( VALUES (CURRENT_USER, 'current_user'), (SESSION_USER, 'session_user'), ('current_user', '-'), ('session_user', '-'), ('Public', '-'), ('None', '-')) AS v (uname, keyword) ON (r.rolname = v.uname) ORDER BY 1; $$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION chksetconfig () RETURNS TABLE ( db name, "role" name, rolkeyword text, setconfig text[] ) AS $$ SELECT COALESCE(d.datname, 'ALL'), COALESCE(r.rolname, 'ALL'), COALESCE(v.keyword, '-'), s.setconfig FROM pg_db_role_setting s LEFT JOIN pg_roles r ON (r.oid = s.setrole) LEFT JOIN pg_database d ON (d.oid = s.setdatabase) LEFT JOIN ( VALUES (CURRENT_USER, 'current_user'), (SESSION_USER, 'session_user')) AS v (uname, keyword) ON (r.rolname = v.uname) WHERE (r.rolname) IN ('Public', 'current_user', 'regress_testrol1', 'regress_testrol2') ORDER BY 1, 2; $$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION chkumapping () RETURNS TABLE ( umname name, umserver name, umoptions text[] ) AS $$ SELECT r.rolname, s.srvname, m.umoptions FROM pg_user_mapping m LEFT JOIN pg_roles r ON (r.oid = m.umuser) JOIN pg_foreign_server s ON (s.oid = m.umserver) ORDER BY 2; $$ LANGUAGE SQL; CREATE ROLE "Public"; CREATE ROLE "None"; CREATE ROLE "current_user"; CREATE ROLE "session_user"; CREATE ROLE "user"; CREATE ROLE CURRENT_USER; -- error CREATE ROLE CURRENT_ROLE; -- error CREATE ROLE SESSION_USER; -- error CREATE ROLE USER; -- error CREATE ROLE ALL; -- error CREATE ROLE public; -- error CREATE ROLE "public"; -- error CREATE ROLE none; -- error CREATE ROLE "none"; -- error CREATE ROLE pg_abc; -- error CREATE ROLE "pg_abc"; -- error CREATE ROLE pg_abcdef; -- error CREATE ROLE "pg_abcdef"; -- error CREATE ROLE regress_testrol0 SUPERUSER LOGIN; CREATE ROLE regress_testrolx SUPERUSER LOGIN; CREATE ROLE regress_testrol2 SUPERUSER; CREATE ROLE regress_testrol1 SUPERUSER LOGIN IN ROLE regress_testrol2; \c - SET SESSION AUTHORIZATION regress_testrol1; SET ROLE regress_testrol2; -- ALTER ROLE BEGIN; SELECT * FROM chkrolattr (); ALTER ROLE CURRENT_USER WITH REPLICATION; SELECT * FROM chkrolattr (); ALTER ROLE "current_user" WITH REPLICATION; SELECT * FROM chkrolattr (); ALTER ROLE SESSION_USER WITH REPLICATION; SELECT * FROM chkrolattr (); ALTER ROLE "session_user" WITH REPLICATION; SELECT * FROM chkrolattr (); ALTER USER "Public" WITH REPLICATION; ALTER USER "None" WITH REPLICATION; SELECT * FROM chkrolattr (); ALTER USER regress_testrol1 WITH NOREPLICATION; ALTER USER regress_testrol2 WITH NOREPLICATION; SELECT * FROM chkrolattr (); ROLLBACK; ALTER ROLE USER WITH LOGIN; -- error ALTER ROLE CURRENT_ROLE WITH LOGIN; --error ALTER ROLE ALL WITH REPLICATION; -- error ALTER ROLE SESSION_ROLE WITH NOREPLICATION; -- error ALTER ROLE PUBLIC WITH NOREPLICATION; -- error ALTER ROLE "public" WITH NOREPLICATION; -- error ALTER ROLE NONE WITH NOREPLICATION; -- error ALTER ROLE "none" WITH NOREPLICATION; -- error ALTER ROLE nonexistent WITH NOREPLICATION; -- error -- ALTER USER BEGIN; SELECT * FROM chkrolattr (); ALTER USER CURRENT_USER WITH REPLICATION; SELECT * FROM chkrolattr (); ALTER USER "current_user" WITH REPLICATION; SELECT * FROM chkrolattr (); ALTER USER SESSION_USER WITH REPLICATION; SELECT * FROM chkrolattr (); ALTER USER "session_user" WITH REPLICATION; SELECT * FROM chkrolattr (); ALTER USER "Public" WITH REPLICATION; ALTER USER "None" WITH REPLICATION; SELECT * FROM chkrolattr (); ALTER USER regress_testrol1 WITH NOREPLICATION; ALTER USER regress_testrol2 WITH NOREPLICATION; SELECT * FROM chkrolattr (); ROLLBACK; ALTER USER USER WITH LOGIN; -- error ALTER USER CURRENT_ROLE WITH LOGIN; -- error ALTER USER ALL WITH REPLICATION; -- error ALTER USER SESSION_ROLE WITH NOREPLICATION; -- error ALTER USER PUBLIC WITH NOREPLICATION; -- error ALTER USER "public" WITH NOREPLICATION; -- error ALTER USER NONE WITH NOREPLICATION; -- error ALTER USER "none" WITH NOREPLICATION; -- error ALTER USER nonexistent WITH NOREPLICATION; -- error -- ALTER ROLE SET/RESET SELECT * FROM chksetconfig (); ALTER ROLE CURRENT_USER SET application_name TO 'FOO'; ALTER ROLE SESSION_USER SET application_name TO 'BAR'; ALTER ROLE "current_user" SET application_name TO 'FOOFOO'; ALTER ROLE "Public" SET application_name TO 'BARBAR'; ALTER ROLE ALL SET application_name TO 'SLAP'; SELECT * FROM chksetconfig (); ALTER ROLE regress_testrol1 SET application_name TO 'SLAM'; SELECT * FROM chksetconfig (); ALTER ROLE CURRENT_USER RESET application_name; ALTER ROLE SESSION_USER RESET application_name; ALTER ROLE "current_user" RESET application_name; ALTER ROLE "Public" RESET application_name; ALTER ROLE ALL RESET application_name; SELECT * FROM chksetconfig (); ALTER ROLE CURRENT_ROLE SET application_name TO 'BAZ'; -- error ALTER ROLE USER SET application_name TO 'BOOM'; -- error ALTER ROLE PUBLIC SET application_name TO 'BOMB'; -- error ALTER ROLE nonexistent SET application_name TO 'BOMB'; -- error -- ALTER USER SET/RESET SELECT * FROM chksetconfig (); ALTER USER CURRENT_USER SET application_name TO 'FOO'; ALTER USER SESSION_USER SET application_name TO 'BAR'; ALTER USER "current_user" SET application_name TO 'FOOFOO'; ALTER USER "Public" SET application_name TO 'BARBAR'; ALTER USER ALL SET application_name TO 'SLAP'; SELECT * FROM chksetconfig (); ALTER USER regress_testrol1 SET application_name TO 'SLAM'; SELECT * FROM chksetconfig (); ALTER USER CURRENT_USER RESET application_name; ALTER USER SESSION_USER RESET application_name; ALTER USER "current_user" RESET application_name; ALTER USER "Public" RESET application_name; ALTER USER ALL RESET application_name; SELECT * FROM chksetconfig (); ALTER USER CURRENT_USER SET application_name TO 'BAZ'; -- error ALTER USER USER SET application_name TO 'BOOM'; -- error ALTER USER PUBLIC SET application_name TO 'BOMB'; -- error ALTER USER NONE SET application_name TO 'BOMB'; -- error ALTER USER nonexistent SET application_name TO 'BOMB'; -- error -- CREATE SCHEMA CREATE SCHEMA newschema1 AUTHORIZATION CURRENT_USER; CREATE SCHEMA newschema2 AUTHORIZATION "current_user"; CREATE SCHEMA newschema3 AUTHORIZATION SESSION_USER; CREATE SCHEMA newschema4 AUTHORIZATION regress_testrolx; CREATE SCHEMA newschema5 AUTHORIZATION "Public"; CREATE SCHEMA newschema6 AUTHORIZATION USER; -- error CREATE SCHEMA newschema6 AUTHORIZATION CURRENT_ROLE; -- error CREATE SCHEMA newschema6 AUTHORIZATION PUBLIC; -- error CREATE SCHEMA newschema6 AUTHORIZATION "public"; -- error CREATE SCHEMA newschema6 AUTHORIZATION NONE; -- error CREATE SCHEMA newschema6 AUTHORIZATION nonexistent; -- error SELECT n.nspname, r.rolname FROM pg_namespace n JOIN pg_roles r ON (r.oid = n.nspowner) WHERE n.nspname LIKE 'newschema_' ORDER BY 1; CREATE SCHEMA IF NOT EXISTS newschema1 AUTHORIZATION CURRENT_USER; CREATE SCHEMA IF NOT EXISTS newschema2 AUTHORIZATION "current_user"; CREATE SCHEMA IF NOT EXISTS newschema3 AUTHORIZATION SESSION_USER; CREATE SCHEMA IF NOT EXISTS newschema4 AUTHORIZATION regress_testrolx; CREATE SCHEMA IF NOT EXISTS newschema5 AUTHORIZATION "Public"; CREATE SCHEMA IF NOT EXISTS newschema6 AUTHORIZATION USER; -- error CREATE SCHEMA IF NOT EXISTS newschema6 AUTHORIZATION CURRENT_ROLE; -- error CREATE SCHEMA IF NOT EXISTS newschema6 AUTHORIZATION PUBLIC; -- error CREATE SCHEMA IF NOT EXISTS newschema6 AUTHORIZATION "public"; -- error CREATE SCHEMA IF NOT EXISTS newschema6 AUTHORIZATION NONE; -- error CREATE SCHEMA IF NOT EXISTS newschema6 AUTHORIZATION nonexistent; -- error SELECT n.nspname, r.rolname FROM pg_namespace n JOIN pg_roles r ON (r.oid = n.nspowner) WHERE n.nspname LIKE 'newschema_' ORDER BY 1; -- ALTER TABLE OWNER TO \c - SET SESSION AUTHORIZATION regress_testrol0; CREATE TABLE testtab1 ( a int ); CREATE TABLE testtab2 ( a int ); CREATE TABLE testtab3 ( a int ); CREATE TABLE testtab4 ( a int ); CREATE TABLE testtab5 ( a int ); CREATE TABLE testtab6 ( a int ); \c - SET SESSION AUTHORIZATION regress_testrol1; SET ROLE regress_testrol2; ALTER TABLE testtab1 OWNER TO CURRENT_USER; ALTER TABLE testtab2 OWNER TO "current_user"; ALTER TABLE testtab3 OWNER TO SESSION_USER; ALTER TABLE testtab4 OWNER TO regress_testrolx; ALTER TABLE testtab5 OWNER TO "Public"; ALTER TABLE testtab6 OWNER TO CURRENT_ROLE; -- error ALTER TABLE testtab6 OWNER TO USER; --error ALTER TABLE testtab6 OWNER TO PUBLIC; -- error ALTER TABLE testtab6 OWNER TO "public"; -- error ALTER TABLE testtab6 OWNER TO nonexistent; -- error SELECT c.relname, r.rolname FROM pg_class c JOIN pg_roles r ON (r.oid = c.relowner) WHERE relname LIKE 'testtab_' ORDER BY 1; -- ALTER TABLE, VIEW, MATERIALIZED VIEW, FOREIGN TABLE, SEQUENCE are -- changed their owner in the same way. -- ALTER AGGREGATE \c - SET SESSION AUTHORIZATION regress_testrol0; CREATE AGGREGATE testagg1 (int2) ( SFUNC = int2_sum, STYPE = int8 ); CREATE AGGREGATE testagg2 (int2) ( SFUNC = int2_sum, STYPE = int8 ); CREATE AGGREGATE testagg3 (int2) ( SFUNC = int2_sum, STYPE = int8 ); CREATE AGGREGATE testagg4 (int2) ( SFUNC = int2_sum, STYPE = int8 ); CREATE AGGREGATE testagg5 (int2) ( SFUNC = int2_sum, STYPE = int8 ); CREATE AGGREGATE testagg5 (int2) ( SFUNC = int2_sum, STYPE = int8 ); CREATE AGGREGATE testagg6 (int2) ( SFUNC = int2_sum, STYPE = int8 ); CREATE AGGREGATE testagg7 (int2) ( SFUNC = int2_sum, STYPE = int8 ); CREATE AGGREGATE testagg8 (int2) ( SFUNC = int2_sum, STYPE = int8 ); CREATE AGGREGATE testagg9 (int2) ( SFUNC = int2_sum, STYPE = int8 ); \c - SET SESSION AUTHORIZATION regress_testrol1; SET ROLE regress_testrol2; ALTER AGGREGATE testagg1 (int2) OWNER TO CURRENT_USER; ALTER AGGREGATE testagg2 (int2) OWNER TO "current_user"; ALTER AGGREGATE testagg3 (int2) OWNER TO SESSION_USER; ALTER AGGREGATE testagg4 (int2) OWNER TO regress_testrolx; ALTER AGGREGATE testagg5 (int2) OWNER TO "Public"; ALTER AGGREGATE testagg5 (int2) OWNER TO CURRENT_ROLE; -- error ALTER AGGREGATE testagg5 (int2) OWNER TO USER; -- error ALTER AGGREGATE testagg5 (int2) OWNER TO PUBLIC; -- error ALTER AGGREGATE testagg5 (int2) OWNER TO "public"; -- error ALTER AGGREGATE testagg5 (int2) OWNER TO nonexistent; -- error SELECT p.proname, r.rolname FROM pg_proc p JOIN pg_roles r ON (r.oid = p.proowner) WHERE proname LIKE 'testagg_' ORDER BY 1; -- CREATE USER MAPPING CREATE FOREIGN DATA WRAPPER test_wrapper; CREATE SERVER sv1 FOREIGN DATA WRAPPER test_wrapper; CREATE SERVER sv2 FOREIGN DATA WRAPPER test_wrapper; CREATE SERVER sv3 FOREIGN DATA WRAPPER test_wrapper; CREATE SERVER sv4 FOREIGN DATA WRAPPER test_wrapper; CREATE SERVER sv5 FOREIGN DATA WRAPPER test_wrapper; CREATE SERVER sv6 FOREIGN DATA WRAPPER test_wrapper; CREATE SERVER sv7 FOREIGN DATA WRAPPER test_wrapper; CREATE SERVER sv8 FOREIGN DATA WRAPPER test_wrapper; CREATE SERVER sv9 FOREIGN DATA WRAPPER test_wrapper; CREATE USER MAPPING FOR CURRENT_USER SERVER sv1 OPTIONS ( USER 'CURRENT_USER' ); CREATE USER MAPPING FOR "current_user" SERVER sv2 OPTIONS ( USER '"current_user"' ); CREATE USER MAPPING FOR USER SERVER sv3 OPTIONS ( USER 'USER' ); CREATE USER MAPPING FOR "user" SERVER sv4 OPTIONS ( USER '"USER"' ); CREATE USER MAPPING FOR SESSION_USER SERVER sv5 OPTIONS ( USER 'SESSION_USER' ); CREATE USER MAPPING FOR PUBLIC SERVER sv6 OPTIONS ( USER 'PUBLIC' ); CREATE USER MAPPING FOR "Public" SERVER sv7 OPTIONS ( USER '"Public"' ); CREATE USER MAPPING FOR regress_testrolx SERVER sv8 OPTIONS ( USER 'regress_testrolx' ); CREATE USER MAPPING FOR CURRENT_ROLE SERVER sv9 OPTIONS ( USER 'CURRENT_ROLE' ); -- error CREATE USER MAPPING FOR nonexistent SERVER sv9 OPTIONS ( USER 'nonexistent' ); -- error; SELECT * FROM chkumapping (); -- ALTER USER MAPPING ALTER USER MAPPING FOR CURRENT_USER SERVER sv1 OPTIONS ( SET USER 'CURRENT_USER_alt'); ALTER USER MAPPING FOR "current_user" SERVER sv2 OPTIONS ( SET USER '"current_user"_alt'); ALTER USER MAPPING FOR USER SERVER sv3 OPTIONS ( SET USER 'USER_alt'); ALTER USER MAPPING FOR "user" SERVER sv4 OPTIONS ( SET USER '"user"_alt'); ALTER USER MAPPING FOR SESSION_USER SERVER sv5 OPTIONS ( SET USER 'SESSION_USER_alt'); ALTER USER MAPPING FOR PUBLIC SERVER sv6 OPTIONS ( SET USER 'public_alt'); ALTER USER MAPPING FOR "Public" SERVER sv7 OPTIONS ( SET USER '"Public"_alt'); ALTER USER MAPPING FOR regress_testrolx SERVER sv8 OPTIONS ( SET USER 'regress_testrolx_alt'); ALTER USER MAPPING FOR CURRENT_ROLE SERVER sv9 OPTIONS ( SET USER 'CURRENT_ROLE_alt'); ALTER USER MAPPING FOR nonexistent SERVER sv9 OPTIONS ( SET USER 'nonexistent_alt'); -- error SELECT * FROM chkumapping (); -- DROP USER MAPPING DROP USER MAPPING FOR CURRENT_USER SERVER sv1; DROP USER MAPPING FOR "current_user" SERVER sv2; DROP USER MAPPING FOR USER SERVER sv3; DROP USER MAPPING FOR "user" SERVER sv4; DROP USER MAPPING FOR SESSION_USER SERVER sv5; DROP USER MAPPING FOR PUBLIC SERVER sv6; DROP USER MAPPING FOR "Public" SERVER sv7; DROP USER MAPPING FOR regress_testrolx SERVER sv8; DROP USER MAPPING FOR CURRENT_ROLE SERVER sv9; -- error DROP USER MAPPING FOR nonexistent SERVER sv; -- error SELECT * FROM chkumapping (); CREATE USER MAPPING FOR CURRENT_USER SERVER sv1 OPTIONS ( USER 'CURRENT_USER' ); CREATE USER MAPPING FOR "current_user" SERVER sv2 OPTIONS ( USER '"current_user"' ); CREATE USER MAPPING FOR USER SERVER sv3 OPTIONS ( USER 'USER' ); CREATE USER MAPPING FOR "user" SERVER sv4 OPTIONS ( USER '"USER"' ); CREATE USER MAPPING FOR SESSION_USER SERVER sv5 OPTIONS ( USER 'SESSION_USER' ); CREATE USER MAPPING FOR PUBLIC SERVER sv6 OPTIONS ( USER 'PUBLIC' ); CREATE USER MAPPING FOR "Public" SERVER sv7 OPTIONS ( USER '"Public"' ); CREATE USER MAPPING FOR regress_testrolx SERVER sv8 OPTIONS ( USER 'regress_testrolx' ); SELECT * FROM chkumapping (); -- DROP USER MAPPING IF EXISTS DROP USER MAPPING IF EXISTS FOR CURRENT_USER SERVER sv1; SELECT * FROM chkumapping (); DROP USER MAPPING IF EXISTS FOR "current_user" SERVER sv2; SELECT * FROM chkumapping (); DROP USER MAPPING IF EXISTS FOR USER SERVER sv3; SELECT * FROM chkumapping (); DROP USER MAPPING IF EXISTS FOR "user" SERVER sv4; SELECT * FROM chkumapping (); DROP USER MAPPING IF EXISTS FOR SESSION_USER SERVER sv5; SELECT * FROM chkumapping (); DROP USER MAPPING IF EXISTS FOR PUBLIC SERVER sv6; SELECT * FROM chkumapping (); DROP USER MAPPING IF EXISTS FOR "Public" SERVER sv7; SELECT * FROM chkumapping (); DROP USER MAPPING IF EXISTS FOR regress_testrolx SERVER sv8; SELECT * FROM chkumapping (); DROP USER MAPPING IF EXISTS FOR CURRENT_ROLE SERVER sv9; --error DROP USER MAPPING IF EXISTS FOR nonexistent SERVER sv9; -- error -- GRANT/REVOKE GRANT regress_testrol0 TO pg_signal_backend; -- success SET ROLE pg_signal_backend; --success RESET ROLE; CREATE SCHEMA test_roles_schema AUTHORIZATION pg_signal_backend; --success SET ROLE regress_testrol2; UPDATE pg_proc SET proacl = NULL WHERE proname LIKE 'testagg_'; SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_'; REVOKE ALL PRIVILEGES ON FUNCTION testagg1 (int2) FROM PUBLIC; REVOKE ALL PRIVILEGES ON FUNCTION testagg2 (int2) FROM PUBLIC; REVOKE ALL PRIVILEGES ON FUNCTION testagg3 (int2) FROM PUBLIC; REVOKE ALL PRIVILEGES ON FUNCTION testagg4 (int2) FROM PUBLIC; REVOKE ALL PRIVILEGES ON FUNCTION testagg5 (int2) FROM PUBLIC; REVOKE ALL PRIVILEGES ON FUNCTION testagg6 (int2) FROM PUBLIC; REVOKE ALL PRIVILEGES ON FUNCTION testagg7 (int2) FROM PUBLIC; REVOKE ALL PRIVILEGES ON FUNCTION testagg8 (int2) FROM PUBLIC; GRANT ALL PRIVILEGES ON FUNCTION testagg1 (int2) TO PUBLIC; GRANT ALL PRIVILEGES ON FUNCTION testagg2 (int2) TO CURRENT_USER; GRANT ALL PRIVILEGES ON FUNCTION testagg3 (int2) TO "current_user"; GRANT ALL PRIVILEGES ON FUNCTION testagg4 (int2) TO SESSION_USER; GRANT ALL PRIVILEGES ON FUNCTION testagg5 (int2) TO "Public"; GRANT ALL PRIVILEGES ON FUNCTION testagg6 (int2) TO regress_testrolx; GRANT ALL PRIVILEGES ON FUNCTION testagg7 (int2) TO "public"; GRANT ALL PRIVILEGES ON FUNCTION testagg8 (int2) TO CURRENT_USER, public, regress_testrolx; SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_'; GRANT ALL PRIVILEGES ON FUNCTION testagg9 (int2) TO CURRENT_ROLE; --error GRANT ALL PRIVILEGES ON FUNCTION testagg9 (int2) TO USER; --error GRANT ALL PRIVILEGES ON FUNCTION testagg9 (int2) TO NONE; --error GRANT ALL PRIVILEGES ON FUNCTION testagg9 (int2) TO "none"; --error SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_'; REVOKE ALL PRIVILEGES ON FUNCTION testagg1 (int2) FROM PUBLIC; REVOKE ALL PRIVILEGES ON FUNCTION testagg2 (int2) FROM CURRENT_USER; REVOKE ALL PRIVILEGES ON FUNCTION testagg3 (int2) FROM "current_user"; REVOKE ALL PRIVILEGES ON FUNCTION testagg4 (int2) FROM SESSION_USER; REVOKE ALL PRIVILEGES ON FUNCTION testagg5 (int2) FROM "Public"; REVOKE ALL PRIVILEGES ON FUNCTION testagg6 (int2) FROM regress_testrolx; REVOKE ALL PRIVILEGES ON FUNCTION testagg7 (int2) FROM "public"; REVOKE ALL PRIVILEGES ON FUNCTION testagg8 (int2) FROM CURRENT_USER, public, regress_testrolx; SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_'; REVOKE ALL PRIVILEGES ON FUNCTION testagg9 (int2) FROM CURRENT_ROLE; --error REVOKE ALL PRIVILEGES ON FUNCTION testagg9 (int2) FROM USER; --error REVOKE ALL PRIVILEGES ON FUNCTION testagg9 (int2) FROM NONE; --error REVOKE ALL PRIVILEGES ON FUNCTION testagg9 (int2) FROM "none"; --error SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_'; -- DEFAULT MONITORING ROLES CREATE ROLE regress_role_haspriv; CREATE ROLE regress_role_nopriv; -- pg_read_all_stats GRANT pg_read_all_stats TO regress_role_haspriv; SET SESSION AUTHORIZATION regress_role_haspriv; -- returns true with role member of pg_read_all_stats SELECT COUNT(*) = 0 AS haspriv FROM pg_stat_activity WHERE query = ''; SET SESSION AUTHORIZATION regress_role_nopriv; -- returns false with role not member of pg_read_all_stats SELECT COUNT(*) = 0 AS haspriv FROM pg_stat_activity WHERE query = ''; RESET SESSION AUTHORIZATION; REVOKE pg_read_all_stats FROM regress_role_haspriv; -- pg_read_all_settings GRANT pg_read_all_settings TO regress_role_haspriv; BEGIN; -- A GUC using GUC_SUPERUSER_ONLY is useful for negative tests. SET LOCAL session_preload_libraries TO 'path-to-preload-libraries'; SET SESSION AUTHORIZATION regress_role_haspriv; -- passes with role member of pg_read_all_settings SHOW session_preload_libraries; SET SESSION AUTHORIZATION regress_role_nopriv; -- fails with role not member of pg_read_all_settings SHOW session_preload_libraries; RESET SESSION AUTHORIZATION; ROLLBACK; REVOKE pg_read_all_settings FROM regress_role_haspriv; -- clean up \c DROP SCHEMA test_roles_schema; DROP OWNED BY regress_testrol0, "Public", "current_user", regress_testrol1, regress_testrol2, regress_testrolx CASCADE; DROP ROLE regress_testrol0, regress_testrol1, regress_testrol2, regress_testrolx; DROP ROLE "Public", "None", "current_user", "session_user", "user"; DROP ROLE regress_role_haspriv, regress_role_nopriv; pgFormatter-4.2/t/pg-test-files/expected/rowsecurity.sql000066400000000000000000002063741361326045100235350ustar00rootroot00000000000000-- -- Test of Row-level security feature -- -- Clean up in case a prior regression run failed -- Suppress NOTICE messages when users/groups don't exist SET client_min_messages TO 'warning'; DROP USER IF EXISTS regress_rls_alice; DROP USER IF EXISTS regress_rls_bob; DROP USER IF EXISTS regress_rls_carol; DROP USER IF EXISTS regress_rls_dave; DROP USER IF EXISTS regress_rls_exempt_user; DROP ROLE IF EXISTS regress_rls_group1; DROP ROLE IF EXISTS regress_rls_group2; DROP SCHEMA IF EXISTS regress_rls_schema CASCADE; RESET client_min_messages; -- initial setup CREATE USER regress_rls_alice NOLOGIN; CREATE USER regress_rls_bob NOLOGIN; CREATE USER regress_rls_carol NOLOGIN; CREATE USER regress_rls_dave NOLOGIN; CREATE USER regress_rls_exempt_user BYPASSRLS NOLOGIN; CREATE ROLE regress_rls_group1 NOLOGIN; CREATE ROLE regress_rls_group2 NOLOGIN; GRANT regress_rls_group1 TO regress_rls_bob; GRANT regress_rls_group2 TO regress_rls_carol; CREATE SCHEMA regress_rls_schema; GRANT ALL ON SCHEMA regress_rls_schema TO public; SET search_path = regress_rls_schema; -- setup of malicious function CREATE OR REPLACE FUNCTION f_leak (text) RETURNS bool COST 0.0000001 LANGUAGE plpgsql AS 'BEGIN RAISE NOTICE ''f_leak => %'', $1; RETURN true; END' ; GRANT EXECUTE ON FUNCTION f_leak (text) TO public; -- BASIC Row-Level Security Scenario SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE uaccount ( pguser name PRIMARY KEY, seclv int ); GRANT SELECT ON uaccount TO public; INSERT INTO uaccount VALUES ('regress_rls_alice', 99), ('regress_rls_bob', 1), ('regress_rls_carol', 2), ('regress_rls_dave', 3); CREATE TABLE category ( cid int PRIMARY KEY, cname text ); GRANT ALL ON category TO public; INSERT INTO category VALUES (11, 'novel'), (22, 'science fiction'), (33, 'technology'), (44, 'manga'); CREATE TABLE document ( did int PRIMARY KEY, cid int REFERENCES category (cid), dlevel int NOT NULL, dauthor name, dtitle text ); GRANT ALL ON document TO public; INSERT INTO document VALUES (1, 11, 1, 'regress_rls_bob', 'my first novel'), (2, 11, 2, 'regress_rls_bob', 'my second novel'), (3, 22, 2, 'regress_rls_bob', 'my science fiction'), (4, 44, 1, 'regress_rls_bob', 'my first manga'), (5, 44, 2, 'regress_rls_bob', 'my second manga'), (6, 22, 1, 'regress_rls_carol', 'great science fiction'), (7, 33, 2, 'regress_rls_carol', 'great technology book'), (8, 44, 1, 'regress_rls_carol', 'great manga'), (9, 22, 1, 'regress_rls_dave', 'awesome science fiction'), (10, 33, 2, 'regress_rls_dave', 'awesome technology book'); ALTER TABLE document ENABLE ROW LEVEL SECURITY; -- user's security level must be higher than or equal to document's CREATE POLICY p1 ON document AS PERMISSIVE USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = CURRENT_USER)); -- try to create a policy of bogus type CREATE POLICY p1 ON document AS UGLY USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = CURRENT_USER)); -- but Dave isn't allowed to anything at cid 50 or above -- this is to make sure that we sort the policies by name first -- when applying WITH CHECK, a later INSERT by Dave should fail due -- to p1r first CREATE POLICY p2r ON document AS RESTRICTIVE TO regress_rls_dave USING (cid <> 44 AND cid < 50); -- and Dave isn't allowed to see manga documents CREATE POLICY p1r ON document AS RESTRICTIVE TO regress_rls_dave USING (cid <> 44); \dp \d document SELECT * FROM pg_policies WHERE schemaname = 'regress_rls_schema' AND tablename = 'document' ORDER BY policyname; -- viewpoint from regress_rls_bob SET SESSION AUTHORIZATION regress_rls_bob; SET row_security TO ON; SELECT * FROM document WHERE f_leak (dtitle) ORDER BY did; SELECT * FROM document NATURAL JOIN category WHERE f_leak (dtitle) ORDER BY did; -- try a sampled version SELECT * FROM document TABLESAMPLE BERNOULLI (50) REPEATABLE (0) WHERE f_leak (dtitle) ORDER BY did; -- viewpoint from regress_rls_carol SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM document WHERE f_leak (dtitle) ORDER BY did; SELECT * FROM document NATURAL JOIN category WHERE f_leak (dtitle) ORDER BY did; -- try a sampled version SELECT * FROM document TABLESAMPLE BERNOULLI (50) REPEATABLE (0) WHERE f_leak (dtitle) ORDER BY did; EXPLAIN ( COSTS OFF ) SELECT * FROM document WHERE f_leak (dtitle); EXPLAIN ( COSTS OFF ) SELECT * FROM document NATURAL JOIN category WHERE f_leak (dtitle); -- viewpoint from regress_rls_dave SET SESSION AUTHORIZATION regress_rls_dave; SELECT * FROM document WHERE f_leak (dtitle) ORDER BY did; SELECT * FROM document NATURAL JOIN category WHERE f_leak (dtitle) ORDER BY did; EXPLAIN ( COSTS OFF ) SELECT * FROM document WHERE f_leak (dtitle); EXPLAIN ( COSTS OFF ) SELECT * FROM document NATURAL JOIN category WHERE f_leak (dtitle); -- 44 would technically fail for both p2r and p1r, but we should get an error -- back from p1r for this because it sorts first INSERT INTO document VALUES (100, 44, 1, 'regress_rls_dave', 'testing sorting of policies'); -- fail -- Just to see a p2r error INSERT INTO document VALUES (100, 55, 1, 'regress_rls_dave', 'testing sorting of policies'); -- fail -- only owner can change policies ALTER POLICY p1 ON document USING (TRUE); --fail DROP POLICY p1 ON document; --fail SET SESSION AUTHORIZATION regress_rls_alice; ALTER POLICY p1 ON document USING (dauthor = CURRENT_USER); -- viewpoint from regress_rls_bob again SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM document WHERE f_leak (dtitle) ORDER BY did; SELECT * FROM document NATURAL JOIN category WHERE f_leak (dtitle) ORDER BY did; -- viewpoint from rls_regres_carol again SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM document WHERE f_leak (dtitle) ORDER BY did; SELECT * FROM document NATURAL JOIN category WHERE f_leak (dtitle) ORDER BY did; EXPLAIN ( COSTS OFF ) SELECT * FROM document WHERE f_leak (dtitle); EXPLAIN ( COSTS OFF ) SELECT * FROM document NATURAL JOIN category WHERE f_leak (dtitle); -- interaction of FK/PK constraints SET SESSION AUTHORIZATION regress_rls_alice; CREATE POLICY p2 ON category USING ( CASE WHEN CURRENT_USER = 'regress_rls_bob' THEN cid IN (11, 33) WHEN CURRENT_USER = 'regress_rls_carol' THEN cid IN (22, 44) ELSE FALSE END); ALTER TABLE category ENABLE ROW LEVEL SECURITY; -- cannot delete PK referenced by invisible FK SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM document d FULL OUTER JOIN category c ON d.cid = c.cid ORDER BY d.did, c.cid; DELETE FROM category WHERE cid = 33; -- fails with FK violation -- can insert FK referencing invisible PK SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM document d FULL OUTER JOIN category c ON d.cid = c.cid ORDER BY d.did, c.cid; INSERT INTO document VALUES (11, 33, 1, CURRENT_USER, 'hoge'); -- UNIQUE or PRIMARY KEY constraint violation DOES reveal presence of row SET SESSION AUTHORIZATION regress_rls_bob; INSERT INTO document VALUES (8, 44, 1, 'regress_rls_bob', 'my third manga'); -- Must fail with unique violation, revealing presence of did we can't see SELECT * FROM document WHERE did = 8; -- and confirm we can't see it -- RLS policies are checked before constraints INSERT INTO document VALUES (8, 44, 1, 'regress_rls_carol', 'my third manga'); -- Should fail with RLS check violation, not duplicate key violation UPDATE document SET did = 8, dauthor = 'regress_rls_carol' WHERE did = 5; -- Should fail with RLS check violation, not duplicate key violation -- database superuser does bypass RLS policy when enabled RESET SESSION AUTHORIZATION; SET row_security TO ON; SELECT * FROM document; SELECT * FROM category; -- database superuser does bypass RLS policy when disabled RESET SESSION AUTHORIZATION; SET row_security TO OFF; SELECT * FROM document; SELECT * FROM category; -- database non-superuser with bypass privilege can bypass RLS policy when disabled SET SESSION AUTHORIZATION regress_rls_exempt_user; SET row_security TO OFF; SELECT * FROM document; SELECT * FROM category; -- RLS policy does not apply to table owner when RLS enabled. SET SESSION AUTHORIZATION regress_rls_alice; SET row_security TO ON; SELECT * FROM document; SELECT * FROM category; -- RLS policy does not apply to table owner when RLS disabled. SET SESSION AUTHORIZATION regress_rls_alice; SET row_security TO OFF; SELECT * FROM document; SELECT * FROM category; -- -- Table inheritance and RLS policy -- SET SESSION AUTHORIZATION regress_rls_alice; SET row_security TO ON; CREATE TABLE t1 ( id int NOT NULL PRIMARY KEY, a int, junk1 text, b text ); ALTER TABLE t1 DROP COLUMN junk1; -- just a disturbing factor GRANT ALL ON t1 TO public; CREATE TABLE t2 ( c float ) INHERITS ( t1 ); GRANT ALL ON t2 TO public; CREATE TABLE t3 ( id int NOT NULL PRIMARY KEY, c text, b text, a int ); ALTER TABLE t3 INHERIT t1; GRANT ALL ON t3 TO public; CREATE POLICY p1 ON t1 FOR ALL TO PUBLIC USING (a % 2 = 0); -- be even number CREATE POLICY p2 ON t2 FOR ALL TO PUBLIC USING (a % 2 = 1); -- be odd number ALTER TABLE t1 ENABLE ROW LEVEL SECURITY; ALTER TABLE t2 ENABLE ROW LEVEL SECURITY; SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM t1; EXPLAIN ( COSTS OFF ) SELECT * FROM t1; SELECT * FROM t1 WHERE f_leak (b); EXPLAIN ( COSTS OFF ) SELECT * FROM t1 WHERE f_leak (b); -- reference to system column SELECT tableoid::regclass, * FROM t1; EXPLAIN ( COSTS OFF ) SELECT *, t1 FROM t1; -- reference to whole-row reference SELECT *, t1 FROM t1; EXPLAIN ( COSTS OFF ) SELECT *, t1 FROM t1; -- for share/update lock SELECT * FROM t1 FOR SHARE; EXPLAIN ( COSTS OFF ) SELECT * FROM t1 FOR SHARE; SELECT * FROM t1 WHERE f_leak (b) FOR SHARE; EXPLAIN ( COSTS OFF ) SELECT * FROM t1 WHERE f_leak (b) FOR SHARE; -- union all query SELECT a, b, tableoid::regclass FROM t2 UNION ALL SELECT a, b, tableoid::regclass FROM t3; EXPLAIN ( COSTS OFF ) SELECT a, b, tableoid::regclass FROM t2 UNION ALL SELECT a, b, tableoid::regclass FROM t3; -- superuser is allowed to bypass RLS checks RESET SESSION AUTHORIZATION; SET row_security TO OFF; SELECT * FROM t1 WHERE f_leak (b); EXPLAIN ( COSTS OFF ) SELECT * FROM t1 WHERE f_leak (b); -- non-superuser with bypass privilege can bypass RLS policy when disabled SET SESSION AUTHORIZATION regress_rls_exempt_user; SET row_security TO OFF; SELECT * FROM t1 WHERE f_leak (b); EXPLAIN ( COSTS OFF ) SELECT * FROM t1 WHERE f_leak (b); -- -- Partitioned Tables -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE part_document ( did int, cid int, dlevel int NOT NULL, dauthor name, dtitle text ) PARTITION BY RANGE (cid); GRANT ALL ON part_document TO public; -- Create partitions for document categories CREATE TABLE part_document_fiction PARTITION OF part_document FOR VALUES FROM (11) TO (12); CREATE TABLE part_document_satire PARTITION OF part_document FOR VALUES FROM (55) TO (56); CREATE TABLE part_document_nonfiction PARTITION OF part_document FOR VALUES FROM (99) TO (100); GRANT ALL ON part_document_fiction TO public; GRANT ALL ON part_document_satire TO public; GRANT ALL ON part_document_nonfiction TO public; INSERT INTO part_document VALUES (1, 11, 1, 'regress_rls_bob', 'my first novel'), (2, 11, 2, 'regress_rls_bob', 'my second novel'), (3, 99, 2, 'regress_rls_bob', 'my science textbook'), (4, 55, 1, 'regress_rls_bob', 'my first satire'), (5, 99, 2, 'regress_rls_bob', 'my history book'), (6, 11, 1, 'regress_rls_carol', 'great science fiction'), (7, 99, 2, 'regress_rls_carol', 'great technology book'), (8, 55, 2, 'regress_rls_carol', 'great satire'), (9, 11, 1, 'regress_rls_dave', 'awesome science fiction'), (10, 99, 2, 'regress_rls_dave', 'awesome technology book'); ALTER TABLE part_document ENABLE ROW LEVEL SECURITY; -- Create policy on parent -- user's security level must be higher than or equal to document's CREATE POLICY pp1 ON part_document AS PERMISSIVE USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = CURRENT_USER)); -- Dave is only allowed to see cid < 55 CREATE POLICY pp1r ON part_document AS RESTRICTIVE TO regress_rls_dave USING (cid < 55); \d+ part_document SELECT * FROM pg_policies WHERE schemaname = 'regress_rls_schema' AND tablename LIKE '%part_document%' ORDER BY policyname; -- viewpoint from regress_rls_bob SET SESSION AUTHORIZATION regress_rls_bob; SET row_security TO ON; SELECT * FROM part_document WHERE f_leak (dtitle) ORDER BY did; EXPLAIN ( COSTS OFF ) SELECT * FROM part_document WHERE f_leak (dtitle); -- viewpoint from regress_rls_carol SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM part_document WHERE f_leak (dtitle) ORDER BY did; EXPLAIN ( COSTS OFF ) SELECT * FROM part_document WHERE f_leak (dtitle); -- viewpoint from regress_rls_dave SET SESSION AUTHORIZATION regress_rls_dave; SELECT * FROM part_document WHERE f_leak (dtitle) ORDER BY did; EXPLAIN ( COSTS OFF ) SELECT * FROM part_document WHERE f_leak (dtitle); -- pp1 ERROR INSERT INTO part_document VALUES (100, 11, 5, 'regress_rls_dave', 'testing pp1'); -- fail -- pp1r ERROR INSERT INTO part_document VALUES (100, 99, 1, 'regress_rls_dave', 'testing pp1r'); -- fail -- Show that RLS policy does not apply for direct inserts to children -- This should fail with RLS POLICY pp1r violation. INSERT INTO part_document VALUES (100, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- fail -- But this should succeed. INSERT INTO part_document_satire VALUES (100, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- success -- We still cannot see the row using the parent SELECT * FROM part_document WHERE f_leak (dtitle) ORDER BY did; -- But we can if we look directly SELECT * FROM part_document_satire WHERE f_leak (dtitle) ORDER BY did; -- Turn on RLS and create policy on child to show RLS is checked before constraints SET SESSION AUTHORIZATION regress_rls_alice; ALTER TABLE part_document_satire ENABLE ROW LEVEL SECURITY; CREATE POLICY pp3 ON part_document_satire AS RESTRICTIVE USING (cid < 55); -- This should fail with RLS violation now. SET SESSION AUTHORIZATION regress_rls_dave; INSERT INTO part_document_satire VALUES (101, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- fail -- And now we cannot see directly into the partition either, due to RLS SELECT * FROM part_document_satire WHERE f_leak (dtitle) ORDER BY did; -- The parent looks same as before -- viewpoint from regress_rls_dave SELECT * FROM part_document WHERE f_leak (dtitle) ORDER BY did; EXPLAIN ( COSTS OFF ) SELECT * FROM part_document WHERE f_leak (dtitle); -- viewpoint from regress_rls_carol SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM part_document WHERE f_leak (dtitle) ORDER BY did; EXPLAIN ( COSTS OFF ) SELECT * FROM part_document WHERE f_leak (dtitle); -- only owner can change policies ALTER POLICY pp1 ON part_document USING (TRUE); --fail DROP POLICY pp1 ON part_document; --fail SET SESSION AUTHORIZATION regress_rls_alice; ALTER POLICY pp1 ON part_document USING (dauthor = CURRENT_USER); -- viewpoint from regress_rls_bob again SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM part_document WHERE f_leak (dtitle) ORDER BY did; -- viewpoint from rls_regres_carol again SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM part_document WHERE f_leak (dtitle) ORDER BY did; EXPLAIN ( COSTS OFF ) SELECT * FROM part_document WHERE f_leak (dtitle); -- database superuser does bypass RLS policy when enabled RESET SESSION AUTHORIZATION; SET row_security TO ON; SELECT * FROM part_document ORDER BY did; SELECT * FROM part_document_satire ORDER BY did; -- database non-superuser with bypass privilege can bypass RLS policy when disabled SET SESSION AUTHORIZATION regress_rls_exempt_user; SET row_security TO OFF; SELECT * FROM part_document ORDER BY did; SELECT * FROM part_document_satire ORDER BY did; -- RLS policy does not apply to table owner when RLS enabled. SET SESSION AUTHORIZATION regress_rls_alice; SET row_security TO ON; SELECT * FROM part_document ORDER BY did; SELECT * FROM part_document_satire ORDER BY did; -- When RLS disabled, other users get ERROR. SET SESSION AUTHORIZATION regress_rls_dave; SET row_security TO OFF; SELECT * FROM part_document ORDER BY did; SELECT * FROM part_document_satire ORDER BY did; -- Check behavior with a policy that uses a SubPlan not an InitPlan. SET SESSION AUTHORIZATION regress_rls_alice; SET row_security TO ON; CREATE POLICY pp3 ON part_document AS RESTRICTIVE USING ((SELECT dlevel <= seclv FROM uaccount WHERE pguser = CURRENT_USER)); SET SESSION AUTHORIZATION regress_rls_carol; INSERT INTO part_document VALUES (100, 11, 5, 'regress_rls_carol', 'testing pp3'); -- fail ----- Dependencies ----- SET SESSION AUTHORIZATION regress_rls_alice; SET row_security TO ON; CREATE TABLE dependee ( x integer, y integer ); CREATE TABLE dependent ( x integer, y integer ); CREATE POLICY d1 ON dependent FOR ALL TO PUBLIC USING (x = (SELECT d.x FROM dependee d WHERE d.y = y)); DROP TABLE dependee; -- Should fail without CASCADE due to dependency on row security qual? DROP TABLE dependee CASCADE; EXPLAIN ( COSTS OFF ) SELECT * FROM dependent; -- After drop, should be unqualified ----- RECURSION ---- -- -- Simple recursion -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE rec1 ( x integer, y integer ); CREATE POLICY r1 ON rec1 USING (x = (SELECT r.x FROM rec1 r WHERE y = r.y)); ALTER TABLE rec1 ENABLE ROW LEVEL SECURITY; SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM rec1; -- fail, direct recursion -- -- Mutual recursion -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE rec2 ( a integer, b integer ); ALTER POLICY r1 ON rec1 USING (x = (SELECT a FROM rec2 WHERE b = y)); CREATE POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1 WHERE y = b)); ALTER TABLE rec2 ENABLE ROW LEVEL SECURITY; SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM rec1; -- fail, mutual recursion -- -- Mutual recursion via views -- SET SESSION AUTHORIZATION regress_rls_bob; CREATE VIEW rec1v AS SELECT * FROM rec1; CREATE VIEW rec2v AS SELECT * FROM rec2; SET SESSION AUTHORIZATION regress_rls_alice; ALTER POLICY r1 ON rec1 USING (x = (SELECT a FROM rec2v WHERE b = y)); ALTER POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1v WHERE y = b)); SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM rec1; -- fail, mutual recursion via views -- -- Mutual recursion via .s.b views -- SET SESSION AUTHORIZATION regress_rls_bob; DROP VIEW rec1v, rec2v CASCADE; CREATE VIEW rec1v WITH ( security_barrier ) AS SELECT * FROM rec1; CREATE VIEW rec2v WITH ( security_barrier ) AS SELECT * FROM rec2; SET SESSION AUTHORIZATION regress_rls_alice; CREATE POLICY r1 ON rec1 USING (x = (SELECT a FROM rec2v WHERE b = y)); CREATE POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1v WHERE y = b)); SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM rec1; -- fail, mutual recursion via s.b. views -- -- recursive RLS and VIEWs in policy -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE s1 ( a int, b text ); INSERT INTO s1 ( SELECT x, md5(x::text) FROM generate_series(- 10, 10) x); CREATE TABLE s2 ( x int, y text ); INSERT INTO s2 ( SELECT x, md5(x::text) FROM generate_series(- 6, 6) x); GRANT SELECT ON s1, s2 TO regress_rls_bob; CREATE POLICY p1 ON s1 USING (a IN (SELECT x FROM s2 WHERE y LIKE '%2f%')); CREATE POLICY p2 ON s2 USING (x IN (SELECT a FROM s1 WHERE b LIKE '%22%')); CREATE POLICY p3 ON s1 FOR INSERT WITH CHECK (a = (SELECT a FROM s1)); ALTER TABLE s1 ENABLE ROW LEVEL SECURITY; ALTER TABLE s2 ENABLE ROW LEVEL SECURITY; SET SESSION AUTHORIZATION regress_rls_bob; CREATE VIEW v2 AS SELECT * FROM s2 WHERE y LIKE '%af%'; SELECT * FROM s1 WHERE f_leak (b); -- fail (infinite recursion) INSERT INTO s1 VALUES (1, 'foo'); -- fail (infinite recursion) SET SESSION AUTHORIZATION regress_rls_alice; DROP POLICY p3 ON s1; ALTER POLICY p2 ON s2 USING (x % 2 = 0); SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM s1 WHERE f_leak (b); -- OK EXPLAIN ( COSTS OFF ) SELECT * FROM ONLY s1 WHERE f_leak (b); SET SESSION AUTHORIZATION regress_rls_alice; ALTER POLICY p1 ON s1 USING (a IN (SELECT x FROM v2)); -- using VIEW in RLS policy SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM s1 WHERE f_leak (b); -- OK EXPLAIN ( COSTS OFF ) SELECT * FROM s1 WHERE f_leak (b); SELECT ( SELECT x FROM s1 LIMIT 1) xx, * FROM s2 WHERE y LIKE '%28%'; EXPLAIN ( COSTS OFF ) SELECT ( SELECT x FROM s1 LIMIT 1) xx, * FROM s2 WHERE y LIKE '%28%'; SET SESSION AUTHORIZATION regress_rls_alice; ALTER POLICY p2 ON s2 USING (x IN (SELECT a FROM s1 WHERE b LIKE '%d2%')); SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM s1 WHERE f_leak (b); -- fail (infinite recursion via view) -- prepared statement with regress_rls_alice privilege PREPARE p1 (int) AS SELECT * FROM t1 WHERE a <= $1; EXECUTE p1 (2); EXPLAIN ( COSTS OFF ) EXECUTE p1 (2); -- superuser is allowed to bypass RLS checks RESET SESSION AUTHORIZATION; SET row_security TO OFF; SELECT * FROM t1 WHERE f_leak (b); EXPLAIN ( COSTS OFF ) SELECT * FROM t1 WHERE f_leak (b); -- plan cache should be invalidated EXECUTE p1 (2); EXPLAIN ( COSTS OFF ) EXECUTE p1 (2); PREPARE p2 (int) AS SELECT * FROM t1 WHERE a = $1; EXECUTE p2 (2); EXPLAIN ( COSTS OFF ) EXECUTE p2 (2); -- also, case when privilege switch from superuser SET SESSION AUTHORIZATION regress_rls_bob; SET row_security TO ON; EXECUTE p2 (2); EXPLAIN ( COSTS OFF ) EXECUTE p2 (2); -- -- UPDATE / DELETE and Row-level security -- SET SESSION AUTHORIZATION regress_rls_bob; EXPLAIN ( COSTS OFF ) UPDATE t1 SET b = b || b WHERE f_leak (b); UPDATE t1 SET b = b || b WHERE f_leak (b); EXPLAIN ( COSTS OFF ) UPDATE ONLY t1 SET b = b || '_updt' WHERE f_leak (b); UPDATE ONLY t1 SET b = b || '_updt' WHERE f_leak (b); -- returning clause with system column UPDATE ONLY t1 SET b = b WHERE f_leak (b) RETURNING tableoid::regclass, *, t1; UPDATE t1 SET b = b WHERE f_leak (b) RETURNING *; UPDATE t1 SET b = b WHERE f_leak (b) RETURNING tableoid::regclass, *, t1; -- updates with from clause EXPLAIN ( COSTS OFF ) UPDATE t2 SET b = t2.b FROM t3 WHERE t2.a = 3 AND t3.a = 2 AND f_leak (t2.b) AND f_leak (t3.b); UPDATE t2 SET b = t2.b FROM t3 WHERE t2.a = 3 AND t3.a = 2 AND f_leak (t2.b) AND f_leak (t3.b); EXPLAIN ( COSTS OFF ) UPDATE t1 SET b = t1.b FROM t2 WHERE t1.a = 3 AND t2.a = 3 AND f_leak (t1.b) AND f_leak (t2.b); UPDATE t1 SET b = t1.b FROM t2 WHERE t1.a = 3 AND t2.a = 3 AND f_leak (t1.b) AND f_leak (t2.b); EXPLAIN ( COSTS OFF ) UPDATE t2 SET b = t2.b FROM t1 WHERE t1.a = 3 AND t2.a = 3 AND f_leak (t1.b) AND f_leak (t2.b); UPDATE t2 SET b = t2.b FROM t1 WHERE t1.a = 3 AND t2.a = 3 AND f_leak (t1.b) AND f_leak (t2.b); -- updates with from clause self join EXPLAIN ( COSTS OFF ) UPDATE t2 t2_1 SET b = t2_2.b FROM t2 t2_2 WHERE t2_1.a = 3 AND t2_2.a = t2_1.a AND t2_2.b = t2_1.b AND f_leak (t2_1.b) AND f_leak (t2_2.b) RETURNING *, t2_1, t2_2; UPDATE t2 t2_1 SET b = t2_2.b FROM t2 t2_2 WHERE t2_1.a = 3 AND t2_2.a = t2_1.a AND t2_2.b = t2_1.b AND f_leak (t2_1.b) AND f_leak (t2_2.b) RETURNING *, t2_1, t2_2; EXPLAIN ( COSTS OFF ) UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2 WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b AND f_leak (t1_1.b) AND f_leak (t1_2.b) RETURNING *, t1_1, t1_2; UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2 WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b AND f_leak (t1_1.b) AND f_leak (t1_2.b) RETURNING *, t1_1, t1_2; RESET SESSION AUTHORIZATION; SET row_security TO OFF; SELECT * FROM t1 ORDER BY a, b; SET SESSION AUTHORIZATION regress_rls_bob; SET row_security TO ON; EXPLAIN ( COSTS OFF ) DELETE FROM ONLY t1 WHERE f_leak (b); EXPLAIN ( COSTS OFF ) DELETE FROM t1 WHERE f_leak (b); DELETE FROM ONLY t1 WHERE f_leak (b) RETURNING tableoid::regclass, *, t1; DELETE FROM t1 WHERE f_leak (b) RETURNING tableoid::regclass, *, t1; -- -- S.b. view on top of Row-level security -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE b1 ( a int, b text ); INSERT INTO b1 ( SELECT x, md5(x::text) FROM generate_series(- 10, 10) x); CREATE POLICY p1 ON b1 USING (a % 2 = 0); ALTER TABLE b1 ENABLE ROW LEVEL SECURITY; GRANT ALL ON b1 TO regress_rls_bob; SET SESSION AUTHORIZATION regress_rls_bob; CREATE VIEW bv1 WITH ( security_barrier ) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION; GRANT ALL ON bv1 TO regress_rls_carol; SET SESSION AUTHORIZATION regress_rls_carol; EXPLAIN ( COSTS OFF ) SELECT * FROM bv1 WHERE f_leak (b); SELECT * FROM bv1 WHERE f_leak (b); INSERT INTO bv1 VALUES (- 1, 'xxx'); -- should fail view WCO INSERT INTO bv1 VALUES (11, 'xxx'); -- should fail RLS check INSERT INTO bv1 VALUES (12, 'xxx'); -- ok EXPLAIN ( COSTS OFF ) UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak (b); UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak (b); EXPLAIN ( COSTS OFF ) DELETE FROM bv1 WHERE a = 6 AND f_leak (b); DELETE FROM bv1 WHERE a = 6 AND f_leak (b); SET SESSION AUTHORIZATION regress_rls_alice; SELECT * FROM b1; -- -- INSERT ... ON CONFLICT DO UPDATE and Row-level security -- SET SESSION AUTHORIZATION regress_rls_alice; DROP POLICY p1 ON document; DROP POLICY p1r ON document; CREATE POLICY p1 ON document FOR SELECT USING (TRUE); CREATE POLICY p2 ON document FOR INSERT WITH CHECK (dauthor = CURRENT_USER); CREATE POLICY p3 ON document FOR UPDATE USING (cid = (SELECT cid FROM category WHERE cname = 'novel')) WITH CHECK (dauthor = CURRENT_USER); SET SESSION AUTHORIZATION regress_rls_bob; -- Exists... SELECT * FROM document WHERE did = 2; -- ...so violates actual WITH CHECK OPTION within UPDATE (not INSERT, since -- alternative UPDATE path happens to be taken): INSERT INTO document VALUES (2, ( SELECT cid FROM category WHERE cname = 'novel'), 1, 'regress_rls_carol', 'my first novel') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, dauthor = EXCLUDED.dauthor; -- Violates USING qual for UPDATE policy p3. -- -- UPDATE path is taken, but UPDATE fails purely because *existing* row to be -- updated is not a "novel"/cid 11 (row is not leaked, even though we have -- SELECT privileges sufficient to see the row in this instance): INSERT INTO document VALUES (33, 22, 1, 'regress_rls_bob', 'okay science fiction'); -- preparation for next statement INSERT INTO document VALUES (33, ( SELECT cid FROM category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'Some novel, replaces sci-fi') -- takes UPDATE path ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle; -- Fine (we UPDATE, since INSERT WCOs and UPDATE security barrier quals + WCOs -- not violated): INSERT INTO document VALUES (2, ( SELECT cid FROM category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'my first novel') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *; -- Fine (we INSERT, so "cid = 33" ("technology") isn't evaluated): INSERT INTO document VALUES (78, ( SELECT cid FROM category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'some technology novel') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *; -- Fine (same query, but we UPDATE, so "cid = 33", ("technology") is not the -- case in respect of *existing* tuple): INSERT INTO document VALUES (78, ( SELECT cid FROM category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'some technology novel') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *; -- Same query a third time, but now fails due to existing tuple finally not -- passing quals: INSERT INTO document VALUES (78, ( SELECT cid FROM category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'some technology novel') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *; -- Don't fail just because INSERT doesn't satisfy WITH CHECK option that -- originated as a barrier/USING() qual from the UPDATE. Note that the UPDATE -- path *isn't* taken, and so UPDATE-related policy does not apply: INSERT INTO document VALUES (79, ( SELECT cid FROM category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'technology book, can only insert') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *; -- But this time, the same statement fails, because the UPDATE path is taken, -- and updating the row just inserted falls afoul of security barrier qual -- (enforced as WCO) -- what we might have updated target tuple to is -- irrelevant, in fact. INSERT INTO document VALUES (79, ( SELECT cid FROM category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'technology book, can only insert') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *; -- Test default USING qual enforced as WCO SET SESSION AUTHORIZATION regress_rls_alice; DROP POLICY p1 ON document; DROP POLICY p2 ON document; DROP POLICY p3 ON document; CREATE POLICY p3_with_default ON document FOR UPDATE USING (cid = (SELECT cid FROM category WHERE cname = 'novel')); SET SESSION AUTHORIZATION regress_rls_bob; -- Just because WCO-style enforcement of USING quals occurs with -- existing/target tuple does not mean that the implementation can be allowed -- to fail to also enforce this qual against the final tuple appended to -- relation (since in the absence of an explicit WCO, this is also interpreted -- as an UPDATE/ALL WCO in general). -- -- UPDATE path is taken here (fails due to existing tuple). Note that this is -- not reported as a "USING expression", because it's an RLS UPDATE check that originated as -- a USING qual for the purposes of RLS in general, as opposed to an explicit -- USING qual that is ordinarily a security barrier. We leave it up to the -- UPDATE to make this fail: INSERT INTO document VALUES (79, ( SELECT cid FROM category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'technology book, can only insert') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *; -- UPDATE path is taken here. Existing tuple passes, since its cid -- corresponds to "novel", but default USING qual is enforced against -- post-UPDATE tuple too (as always when updating with a policy that lacks an -- explicit WCO), and so this fails: INSERT INTO document VALUES (2, ( SELECT cid FROM category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'my first novel') ON CONFLICT (did) DO UPDATE SET cid = EXCLUDED.cid, dtitle = EXCLUDED.dtitle RETURNING *; SET SESSION AUTHORIZATION regress_rls_alice; DROP POLICY p3_with_default ON document; -- -- Test ALL policies with ON CONFLICT DO UPDATE (much the same as existing UPDATE -- tests) -- CREATE POLICY p3_with_all ON document FOR ALL USING (cid = (SELECT cid FROM category WHERE cname = 'novel')) WITH CHECK (dauthor = CURRENT_USER); SET SESSION AUTHORIZATION regress_rls_bob; -- Fails, since ALL WCO is enforced in insert path: INSERT INTO document VALUES (80, ( SELECT cid FROM category WHERE cname = 'novel'), 1, 'regress_rls_carol', 'my first novel') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33; -- Fails, since ALL policy USING qual is enforced (existing, target tuple is in -- violation, since it has the "manga" cid): INSERT INTO document VALUES (4, ( SELECT cid FROM category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'my first novel') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle; -- Fails, since ALL WCO are enforced: INSERT INTO document VALUES (1, ( SELECT cid FROM category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'my first novel') ON CONFLICT (did) DO UPDATE SET dauthor = 'regress_rls_carol'; -- -- ROLE/GROUP -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE z1 ( a int, b text ); CREATE TABLE z2 ( a int, b text ); GRANT SELECT ON z1, z2 TO regress_rls_group1, regress_rls_group2, regress_rls_bob, regress_rls_carol; INSERT INTO z1 VALUES (1, 'aba'), (2, 'bbb'), (3, 'ccc'), (4, 'dad'); CREATE POLICY p1 ON z1 TO regress_rls_group1 USING (a % 2 = 0); CREATE POLICY p2 ON z1 TO regress_rls_group2 USING (a % 2 = 1); ALTER TABLE z1 ENABLE ROW LEVEL SECURITY; SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM z1 WHERE f_leak (b); EXPLAIN ( COSTS OFF ) SELECT * FROM z1 WHERE f_leak (b); PREPARE plancache_test AS SELECT * FROM z1 WHERE f_leak (b); EXPLAIN ( COSTS OFF ) EXECUTE plancache_test; PREPARE plancache_test2 AS WITH q AS MATERIALIZED ( SELECT * FROM z1 WHERE f_leak ( b )) SELECT * FROM q, z2; EXPLAIN ( COSTS OFF ) EXECUTE plancache_test2; PREPARE plancache_test3 AS WITH q AS MATERIALIZED ( SELECT * FROM z2 ) SELECT * FROM q, z1 WHERE f_leak (z1.b); EXPLAIN ( COSTS OFF ) EXECUTE plancache_test3; SET ROLE regress_rls_group1; SELECT * FROM z1 WHERE f_leak (b); EXPLAIN ( COSTS OFF ) SELECT * FROM z1 WHERE f_leak (b); EXPLAIN ( COSTS OFF ) EXECUTE plancache_test; EXPLAIN ( COSTS OFF ) EXECUTE plancache_test2; EXPLAIN ( COSTS OFF ) EXECUTE plancache_test3; SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM z1 WHERE f_leak (b); EXPLAIN ( COSTS OFF ) SELECT * FROM z1 WHERE f_leak (b); EXPLAIN ( COSTS OFF ) EXECUTE plancache_test; EXPLAIN ( COSTS OFF ) EXECUTE plancache_test2; EXPLAIN ( COSTS OFF ) EXECUTE plancache_test3; SET ROLE regress_rls_group2; SELECT * FROM z1 WHERE f_leak (b); EXPLAIN ( COSTS OFF ) SELECT * FROM z1 WHERE f_leak (b); EXPLAIN ( COSTS OFF ) EXECUTE plancache_test; EXPLAIN ( COSTS OFF ) EXECUTE plancache_test2; EXPLAIN ( COSTS OFF ) EXECUTE plancache_test3; -- -- Views should follow policy for view owner. -- -- View and Table owner are the same. SET SESSION AUTHORIZATION regress_rls_alice; CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak (b); GRANT SELECT ON rls_view TO regress_rls_bob; -- Query as role that is not owner of view or table. Should return all records. SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM rls_view; EXPLAIN ( COSTS OFF ) SELECT * FROM rls_view; -- Query as view/table owner. Should return all records. SET SESSION AUTHORIZATION regress_rls_alice; SELECT * FROM rls_view; EXPLAIN ( COSTS OFF ) SELECT * FROM rls_view; DROP VIEW rls_view; -- View and Table owners are different. SET SESSION AUTHORIZATION regress_rls_bob; CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak (b); GRANT SELECT ON rls_view TO regress_rls_alice; -- Query as role that is not owner of view but is owner of table. -- Should return records based on view owner policies. SET SESSION AUTHORIZATION regress_rls_alice; SELECT * FROM rls_view; EXPLAIN ( COSTS OFF ) SELECT * FROM rls_view; -- Query as role that is not owner of table but is owner of view. -- Should return records based on view owner policies. SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM rls_view; EXPLAIN ( COSTS OFF ) SELECT * FROM rls_view; -- Query as role that is not the owner of the table or view without permissions. SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM rls_view; --fail - permission denied. EXPLAIN ( COSTS OFF ) SELECT * FROM rls_view; --fail - permission denied. -- Query as role that is not the owner of the table or view with permissions. SET SESSION AUTHORIZATION regress_rls_bob; GRANT SELECT ON rls_view TO regress_rls_carol; SELECT * FROM rls_view; EXPLAIN ( COSTS OFF ) SELECT * FROM rls_view; SET SESSION AUTHORIZATION regress_rls_bob; DROP VIEW rls_view; -- -- Command specific -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE x1 ( a int, b text, c text ); GRANT ALL ON x1 TO PUBLIC; INSERT INTO x1 VALUES (1, 'abc', 'regress_rls_bob'), (2, 'bcd', 'regress_rls_bob'), (3, 'cde', 'regress_rls_carol'), (4, 'def', 'regress_rls_carol'), (5, 'efg', 'regress_rls_bob'), (6, 'fgh', 'regress_rls_bob'), (7, 'fgh', 'regress_rls_carol'), (8, 'fgh', 'regress_rls_carol'); CREATE POLICY p0 ON x1 FOR ALL USING (c = CURRENT_USER); CREATE POLICY p1 ON x1 FOR SELECT USING (a % 2 = 0); CREATE POLICY p2 ON x1 FOR INSERT WITH CHECK (a % 2 = 1); CREATE POLICY p3 ON x1 FOR UPDATE USING (a % 2 = 0); CREATE POLICY p4 ON x1 FOR DELETE USING (a < 8); ALTER TABLE x1 ENABLE ROW LEVEL SECURITY; SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM x1 WHERE f_leak (b) ORDER BY a ASC; UPDATE x1 SET b = b || '_updt' WHERE f_leak (b) RETURNING *; SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM x1 WHERE f_leak (b) ORDER BY a ASC; UPDATE x1 SET b = b || '_updt' WHERE f_leak (b) RETURNING *; DELETE FROM x1 WHERE f_leak (b) RETURNING *; -- -- Duplicate Policy Names -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE y1 ( a int, b text ); CREATE TABLE y2 ( a int, b text ); GRANT ALL ON y1, y2 TO regress_rls_bob; CREATE POLICY p1 ON y1 FOR ALL USING (a % 2 = 0); CREATE POLICY p2 ON y1 FOR SELECT USING (a > 2); CREATE POLICY p1 ON y1 FOR SELECT USING (a % 2 = 1); --fail CREATE POLICY p1 ON y2 FOR ALL USING (a % 2 = 0); --OK ALTER TABLE y1 ENABLE ROW LEVEL SECURITY; ALTER TABLE y2 ENABLE ROW LEVEL SECURITY; -- -- Expression structure with SBV -- -- Create view as table owner. RLS should NOT be applied. SET SESSION AUTHORIZATION regress_rls_alice; CREATE VIEW rls_sbv WITH ( security_barrier ) AS SELECT * FROM y1 WHERE f_leak ( b); EXPLAIN ( COSTS OFF ) SELECT * FROM rls_sbv WHERE (a = 1); DROP VIEW rls_sbv; -- Create view as role that does not own table. RLS should be applied. SET SESSION AUTHORIZATION regress_rls_bob; CREATE VIEW rls_sbv WITH ( security_barrier ) AS SELECT * FROM y1 WHERE f_leak ( b); EXPLAIN ( COSTS OFF ) SELECT * FROM rls_sbv WHERE (a = 1); DROP VIEW rls_sbv; -- -- Expression structure -- SET SESSION AUTHORIZATION regress_rls_alice; INSERT INTO y2 ( SELECT x, md5(x::text) FROM generate_series(0, 20) x); CREATE POLICY p2 ON y2 USING (a % 3 = 0); CREATE POLICY p3 ON y2 USING (a % 4 = 0); SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM y2 WHERE f_leak (b); EXPLAIN ( COSTS OFF ) SELECT * FROM y2 WHERE f_leak (b); -- -- Qual push-down of leaky functions, when not referring to table -- SELECT * FROM y2 WHERE f_leak ('abc'); EXPLAIN ( COSTS OFF ) SELECT * FROM y2 WHERE f_leak ('abc'); CREATE TABLE test_qual_pushdown ( abc text ); INSERT INTO test_qual_pushdown VALUES ('abc'), ('def'); SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak (abc); EXPLAIN ( COSTS OFF ) SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak (abc); SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak (b); EXPLAIN ( COSTS OFF ) SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak (b); DROP TABLE test_qual_pushdown; -- -- Plancache invalidate on user change. -- RESET SESSION AUTHORIZATION; DROP TABLE t1 CASCADE; CREATE TABLE t1 ( a integer ); GRANT SELECT ON t1 TO regress_rls_bob, regress_rls_carol; CREATE POLICY p1 ON t1 TO regress_rls_bob USING ((a % 2) = 0); CREATE POLICY p2 ON t1 TO regress_rls_carol USING ((a % 4) = 0); ALTER TABLE t1 ENABLE ROW LEVEL SECURITY; -- Prepare as regress_rls_bob SET ROLE regress_rls_bob; PREPARE role_inval AS SELECT * FROM t1; -- Check plan EXPLAIN ( COSTS OFF ) EXECUTE role_inval; -- Change to regress_rls_carol SET ROLE regress_rls_carol; -- Check plan- should be different EXPLAIN ( COSTS OFF ) EXECUTE role_inval; -- Change back to regress_rls_bob SET ROLE regress_rls_bob; -- Check plan- should be back to original EXPLAIN ( COSTS OFF ) EXECUTE role_inval; -- -- CTE and RLS -- RESET SESSION AUTHORIZATION; DROP TABLE t1 CASCADE; CREATE TABLE t1 ( a integer, b text ); CREATE POLICY p1 ON t1 USING (a % 2 = 0); ALTER TABLE t1 ENABLE ROW LEVEL SECURITY; GRANT ALL ON t1 TO regress_rls_bob; INSERT INTO t1 ( SELECT x, md5(x::text) FROM generate_series(0, 20) x); SET SESSION AUTHORIZATION regress_rls_bob; WITH cte1 AS MATERIALIZED ( SELECT * FROM t1 WHERE f_leak ( b )) SELECT * FROM cte1; EXPLAIN ( COSTS OFF ) WITH cte1 AS MATERIALIZED ( SELECT * FROM t1 WHERE f_leak (b)) SELECT * FROM cte1; WITH cte1 AS ( UPDATE t1 SET a = a + 1 RETURNING * ) SELECT * FROM cte1; --fail WITH cte1 AS ( UPDATE t1 SET a = a RETURNING * ) SELECT * FROM cte1; --ok WITH cte1 AS ( INSERT INTO t1 VALUES (21, 'Fail') RETURNING *) SELECT * FROM cte1; --fail WITH cte1 AS ( INSERT INTO t1 VALUES (20, 'Success') RETURNING *) SELECT * FROM cte1; --ok -- -- Rename Policy -- RESET SESSION AUTHORIZATION; ALTER POLICY p1 ON t1 RENAME TO p1; --fail SELECT polname, relname FROM pg_policy pol JOIN pg_class pc ON (pc.oid = pol.polrelid) WHERE relname = 't1'; ALTER POLICY p1 ON t1 RENAME TO p2; --ok SELECT polname, relname FROM pg_policy pol JOIN pg_class pc ON (pc.oid = pol.polrelid) WHERE relname = 't1'; -- -- Check INSERT SELECT -- SET SESSION AUTHORIZATION regress_rls_bob; CREATE TABLE t2 ( a integer, b text ); INSERT INTO t2 ( SELECT * FROM t1); EXPLAIN ( COSTS OFF ) INSERT INTO t2 ( SELECT * FROM t1); SELECT * FROM t2; EXPLAIN ( COSTS OFF ) SELECT * FROM t2; CREATE TABLE t3 AS SELECT * FROM t1; SELECT * FROM t3; SELECT * INTO t4 FROM t1; SELECT * FROM t4; -- -- RLS with JOIN -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE blog ( id integer, author text, post text ); CREATE TABLE comment ( blog_id integer, message text ); GRANT ALL ON blog, comment TO regress_rls_bob; CREATE POLICY blog_1 ON blog USING (id % 2 = 0); ALTER TABLE blog ENABLE ROW LEVEL SECURITY; INSERT INTO blog VALUES (1, 'alice', 'blog #1'), (2, 'bob', 'blog #1'), (3, 'alice', 'blog #2'), (4, 'alice', 'blog #3'), (5, 'john', 'blog #1'); INSERT INTO comment VALUES (1, 'cool blog'), (1, 'fun blog'), (3, 'crazy blog'), (5, 'what?'), (4, 'insane!'), (2, 'who did it?'); SET SESSION AUTHORIZATION regress_rls_bob; -- Check RLS JOIN with Non-RLS. SELECT id, author, message FROM blog JOIN COMMENT ON id = blog_id; -- Check Non-RLS JOIN with RLS. SELECT id, author, message FROM comment JOIN blog ON id = blog_id; SET SESSION AUTHORIZATION regress_rls_alice; CREATE POLICY comment_1 ON comment USING (blog_id < 4); ALTER TABLE comment ENABLE ROW LEVEL SECURITY; SET SESSION AUTHORIZATION regress_rls_bob; -- Check RLS JOIN RLS SELECT id, author, message FROM blog JOIN COMMENT ON id = blog_id; SELECT id, author, message FROM comment JOIN blog ON id = blog_id; SET SESSION AUTHORIZATION regress_rls_alice; DROP TABLE blog, comment; -- -- Default Deny Policy -- RESET SESSION AUTHORIZATION; DROP POLICY p2 ON t1; ALTER TABLE t1 OWNER TO regress_rls_alice; -- Check that default deny does not apply to superuser. RESET SESSION AUTHORIZATION; SELECT * FROM t1; EXPLAIN ( COSTS OFF ) SELECT * FROM t1; -- Check that default deny does not apply to table owner. SET SESSION AUTHORIZATION regress_rls_alice; SELECT * FROM t1; EXPLAIN ( COSTS OFF ) SELECT * FROM t1; -- Check that default deny applies to non-owner/non-superuser when RLS on. SET SESSION AUTHORIZATION regress_rls_bob; SET row_security TO ON; SELECT * FROM t1; EXPLAIN ( COSTS OFF ) SELECT * FROM t1; SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM t1; EXPLAIN ( COSTS OFF ) SELECT * FROM t1; -- -- COPY TO/FROM -- RESET SESSION AUTHORIZATION; DROP TABLE copy_t CASCADE; CREATE TABLE copy_t ( a integer, b text ); CREATE POLICY p1 ON copy_t USING (a % 2 = 0); ALTER TABLE copy_t ENABLE ROW LEVEL SECURITY; GRANT ALL ON copy_t TO regress_rls_bob, regress_rls_exempt_user; INSERT INTO copy_t ( SELECT x, md5(x::text) FROM generate_series(0, 10) x); -- Check COPY TO as Superuser/owner. RESET SESSION AUTHORIZATION; SET row_security TO OFF; COPY ( SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; SET row_security TO ON; COPY ( SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; -- Check COPY TO as user with permissions. SET SESSION AUTHORIZATION regress_rls_bob; SET row_security TO OFF; COPY ( SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS SET row_security TO ON; COPY ( SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok -- Check COPY TO as user with permissions and BYPASSRLS SET SESSION AUTHORIZATION regress_rls_exempt_user; SET row_security TO OFF; COPY ( SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok SET row_security TO ON; COPY ( SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok -- Check COPY TO as user without permissions. SET row_security TO OFF; SET SESSION AUTHORIZATION regress_rls_carol; SET row_security TO OFF; COPY ( SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS SET row_security TO ON; COPY ( SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - permission denied -- Check COPY relation TO; keep it just one row to avoid reordering issues RESET SESSION AUTHORIZATION; SET row_security TO ON; CREATE TABLE copy_rel_to ( a integer, b text ); CREATE POLICY p1 ON copy_rel_to USING (a % 2 = 0); ALTER TABLE copy_rel_to ENABLE ROW LEVEL SECURITY; GRANT ALL ON copy_rel_to TO regress_rls_bob, regress_rls_exempt_user; INSERT INTO copy_rel_to VALUES (1, md5('1')); -- Check COPY TO as Superuser/owner. RESET SESSION AUTHORIZATION; SET row_security TO OFF; COPY copy_rel_to TO STDOUT WITH DELIMITER ','; SET row_security TO ON; COPY copy_rel_to TO STDOUT WITH DELIMITER ','; -- Check COPY TO as user with permissions. SET SESSION AUTHORIZATION regress_rls_bob; SET row_security TO OFF; COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS SET row_security TO ON; COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok -- Check COPY TO as user with permissions and BYPASSRLS SET SESSION AUTHORIZATION regress_rls_exempt_user; SET row_security TO OFF; COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok SET row_security TO ON; COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok -- Check COPY TO as user without permissions. SET row_security TO OFF; SET SESSION AUTHORIZATION regress_rls_carol; SET row_security TO OFF; COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied SET row_security TO ON; COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied -- Check COPY FROM as Superuser/owner. RESET SESSION AUTHORIZATION; SET row_security TO OFF; SET row_security TO ON; -- Check COPY FROM as user with permissions. SET SESSION AUTHORIZATION regress_rls_bob; SET row_security TO OFF; COPY copy_t FROM STDIN; --fail - would be affected by RLS. SET row_security TO ON; COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS. -- Check COPY FROM as user with permissions and BYPASSRLS SET SESSION AUTHORIZATION regress_rls_exempt_user; SET row_security TO ON; -- Check COPY FROM as user without permissions. SET SESSION AUTHORIZATION regress_rls_carol; SET row_security TO OFF; COPY copy_t FROM STDIN; --fail - permission denied. SET row_security TO ON; COPY copy_t FROM STDIN; --fail - permission denied. RESET SESSION AUTHORIZATION; DROP TABLE copy_t; DROP TABLE copy_rel_to CASCADE; -- Check WHERE CURRENT OF SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE current_check ( currentid int, payload text, rlsuser text ); GRANT ALL ON current_check TO PUBLIC; INSERT INTO current_check VALUES (1, 'abc', 'regress_rls_bob'), (2, 'bcd', 'regress_rls_bob'), (3, 'cde', 'regress_rls_bob'), (4, 'def', 'regress_rls_bob'); CREATE POLICY p1 ON current_check FOR SELECT USING (currentid % 2 = 0); CREATE POLICY p2 ON current_check FOR DELETE USING (currentid = 4 AND rlsuser = CURRENT_USER); CREATE POLICY p3 ON current_check FOR UPDATE USING (currentid = 4) WITH CHECK (rlsuser = CURRENT_USER); ALTER TABLE current_check ENABLE ROW LEVEL SECURITY; SET SESSION AUTHORIZATION regress_rls_bob; -- Can SELECT even rows SELECT * FROM current_check; -- Cannot UPDATE row 2 UPDATE current_check SET payload = payload || '_new' WHERE currentid = 2 RETURNING *; BEGIN; DECLARE current_check_cursor SCROLL CURSOR FOR SELECT * FROM current_check; -- Returns rows that can be seen according to SELECT policy, like plain SELECT -- above (even rows) FETCH ABSOLUTE 1 FROM current_check_cursor; -- Still cannot UPDATE row 2 through cursor UPDATE current_check SET payload = payload || '_new' WHERE CURRENT OF current_check_cursor RETURNING *; -- Can update row 4 through cursor, which is the next visible row FETCH RELATIVE 1 FROM current_check_cursor; UPDATE current_check SET payload = payload || '_new' WHERE CURRENT OF current_check_cursor RETURNING *; SELECT * FROM current_check; -- Plan should be a subquery TID scan EXPLAIN ( COSTS OFF ) UPDATE current_check SET payload = payload WHERE CURRENT OF current_check_cursor; -- Similarly can only delete row 4 FETCH ABSOLUTE 1 FROM current_check_cursor; DELETE FROM current_check WHERE CURRENT OF current_check_cursor RETURNING *; FETCH RELATIVE 1 FROM current_check_cursor; DELETE FROM current_check WHERE CURRENT OF current_check_cursor RETURNING *; SELECT * FROM current_check; COMMIT; -- -- check pg_stats view filtering -- SET row_security TO ON; SET SESSION AUTHORIZATION regress_rls_alice; ANALYZE current_check; -- Stats visible SELECT row_security_active ('current_check'); SELECT attname, most_common_vals FROM pg_stats WHERE tablename = 'current_check' ORDER BY 1; SET SESSION AUTHORIZATION regress_rls_bob; -- Stats not visible SELECT row_security_active ('current_check'); SELECT attname, most_common_vals FROM pg_stats WHERE tablename = 'current_check' ORDER BY 1; -- -- Collation support -- BEGIN; CREATE TABLE coll_t ( c ) AS VALUES ( 'bar' ::text ); CREATE POLICY coll_p ON coll_t USING (c < ('foo'::text COLLATE "C")); ALTER TABLE coll_t ENABLE ROW LEVEL SECURITY; GRANT SELECT ON coll_t TO regress_rls_alice; SELECT (string_to_array(polqual, ':'))[7] AS inputcollid FROM pg_policy WHERE polrelid = 'coll_t'::regclass; SET SESSION AUTHORIZATION regress_rls_alice; SELECT * FROM coll_t; ROLLBACK; -- -- Shared Object Dependencies -- RESET SESSION AUTHORIZATION; BEGIN; CREATE ROLE regress_rls_eve; CREATE ROLE regress_rls_frank; CREATE TABLE tbl1 ( c ) AS VALUES ( 'bar' ::text ); GRANT SELECT ON TABLE tbl1 TO regress_rls_eve; CREATE POLICY P ON tbl1 TO regress_rls_eve, regress_rls_frank USING (TRUE); SELECT refclassid::regclass, deptype FROM pg_depend WHERE classid = 'pg_policy'::regclass AND refobjid = 'tbl1'::regclass; SELECT refclassid::regclass, deptype FROM pg_shdepend WHERE classid = 'pg_policy'::regclass AND refobjid IN ('regress_rls_eve'::regrole, 'regress_rls_frank'::regrole); SAVEPOINT q; DROP ROLE regress_rls_eve; --fails due to dependency on POLICY p ROLLBACK TO q; ALTER POLICY p ON tbl1 TO regress_rls_frank USING (TRUE); SAVEPOINT q; DROP ROLE regress_rls_eve; --fails due to dependency on GRANT SELECT ROLLBACK TO q; REVOKE ALL ON TABLE tbl1 FROM regress_rls_eve; SAVEPOINT q; DROP ROLE regress_rls_eve; --succeeds ROLLBACK TO q; SAVEPOINT q; DROP ROLE regress_rls_frank; --fails due to dependency on POLICY p ROLLBACK TO q; DROP POLICY p ON tbl1; SAVEPOINT q; DROP ROLE regress_rls_frank; -- succeeds ROLLBACK TO q; ROLLBACK; -- cleanup -- -- Converting table to view -- BEGIN; CREATE TABLE t ( c int ); CREATE POLICY p ON t USING (c % 2 = 1); ALTER TABLE t ENABLE ROW LEVEL SECURITY; SAVEPOINT q; CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD SELECT * FROM generate_series(1, 5) t0 (c); -- fails due to row level security enabled ROLLBACK TO q; ALTER TABLE t DISABLE ROW LEVEL SECURITY; SAVEPOINT q; CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD SELECT * FROM generate_series(1, 5) t0 (c); -- fails due to policy p on t ROLLBACK TO q; DROP POLICY p ON t; CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD SELECT * FROM generate_series(1, 5) t0 (c); -- succeeds ROLLBACK; -- -- Policy expression handling -- BEGIN; CREATE TABLE t ( c ) AS VALUES ( 'bar' ::text ); CREATE POLICY p ON t USING (max(c)); -- fails: aggregate functions are not allowed in policy expressions ROLLBACK; -- -- Non-target relations are only subject to SELECT policies -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE r1 ( a int ); CREATE TABLE r2 ( a int ); INSERT INTO r1 VALUES (10), (20); INSERT INTO r2 VALUES (10), (20); GRANT ALL ON r1, r2 TO regress_rls_bob; CREATE POLICY p1 ON r1 USING (TRUE); ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; CREATE POLICY p1 ON r2 FOR SELECT USING (TRUE); CREATE POLICY p2 ON r2 FOR INSERT WITH CHECK (FALSE); CREATE POLICY p3 ON r2 FOR UPDATE USING (FALSE); CREATE POLICY p4 ON r2 FOR DELETE USING (FALSE); ALTER TABLE r2 ENABLE ROW LEVEL SECURITY; SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM r1; SELECT * FROM r2; -- r2 is read-only INSERT INTO r2 VALUES (2); -- Not allowed UPDATE r2 SET a = 2 RETURNING *; -- Updates nothing DELETE FROM r2 RETURNING *; -- Deletes nothing -- r2 can be used as a non-target relation in DML INSERT INTO r1 SELECT a + 1 FROM r2 RETURNING *; -- OK UPDATE r1 SET a = r2.a + 2 FROM r2 WHERE r1.a = r2.a RETURNING *; -- OK DELETE FROM r1 USING r2 WHERE r1.a = r2.a + 2 RETURNING *; -- OK SELECT * FROM r1; SELECT * FROM r2; SET SESSION AUTHORIZATION regress_rls_alice; DROP TABLE r1; DROP TABLE r2; -- -- FORCE ROW LEVEL SECURITY applies RLS to owners too -- SET SESSION AUTHORIZATION regress_rls_alice; SET row_security = ON; CREATE TABLE r1 ( a int ); INSERT INTO r1 VALUES (10), (20); CREATE POLICY p1 ON r1 USING (FALSE); ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; ALTER TABLE r1 FORCE ROW LEVEL SECURITY; -- No error, but no rows TABLE r1; -- RLS error INSERT INTO r1 VALUES (1); -- No error (unable to see any rows to update) UPDATE r1 SET a = 1; TABLE r1; -- No error (unable to see any rows to delete) DELETE FROM r1; TABLE r1; SET row_security = OFF; -- these all fail, would be affected by RLS TABLE r1; UPDATE r1 SET a = 1; DELETE FROM r1; DROP TABLE r1; -- -- FORCE ROW LEVEL SECURITY does not break RI -- SET SESSION AUTHORIZATION regress_rls_alice; SET row_security = ON; CREATE TABLE r1 ( a int PRIMARY KEY ); CREATE TABLE r2 ( a int REFERENCES r1 ); INSERT INTO r1 VALUES (10), (20); INSERT INTO r2 VALUES (10), (20); -- Create policies on r2 which prevent the -- owner from seeing any rows, but RI should -- still see them. CREATE POLICY p1 ON r2 USING (FALSE); ALTER TABLE r2 ENABLE ROW LEVEL SECURITY; ALTER TABLE r2 FORCE ROW LEVEL SECURITY; -- Errors due to rows in r2 DELETE FROM r1; -- Reset r2 to no-RLS DROP POLICY p1 ON r2; ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY; ALTER TABLE r2 DISABLE ROW LEVEL SECURITY; -- clean out r2 for INSERT test below DELETE FROM r2; -- Change r1 to not allow rows to be seen CREATE POLICY p1 ON r1 USING (FALSE); ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; ALTER TABLE r1 FORCE ROW LEVEL SECURITY; -- No rows seen TABLE r1; -- No error, RI still sees that row exists in r1 INSERT INTO r2 VALUES (10); DROP TABLE r2; DROP TABLE r1; -- Ensure cascaded DELETE works CREATE TABLE r1 ( a int PRIMARY KEY ); CREATE TABLE r2 ( a int REFERENCES r1 ON DELETE CASCADE ); INSERT INTO r1 VALUES (10), (20); INSERT INTO r2 VALUES (10), (20); -- Create policies on r2 which prevent the -- owner from seeing any rows, but RI should -- still see them. CREATE POLICY p1 ON r2 USING (FALSE); ALTER TABLE r2 ENABLE ROW LEVEL SECURITY; ALTER TABLE r2 FORCE ROW LEVEL SECURITY; -- Deletes all records from both DELETE FROM r1; -- Remove FORCE from r2 ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY; -- As owner, we now bypass RLS -- verify no rows in r2 now TABLE r2; DROP TABLE r2; DROP TABLE r1; -- Ensure cascaded UPDATE works CREATE TABLE r1 ( a int PRIMARY KEY ); CREATE TABLE r2 ( a int REFERENCES r1 ON UPDATE CASCADE ); INSERT INTO r1 VALUES (10), (20); INSERT INTO r2 VALUES (10), (20); -- Create policies on r2 which prevent the -- owner from seeing any rows, but RI should -- still see them. CREATE POLICY p1 ON r2 USING (FALSE); ALTER TABLE r2 ENABLE ROW LEVEL SECURITY; ALTER TABLE r2 FORCE ROW LEVEL SECURITY; -- Updates records in both UPDATE r1 SET a = a + 5; -- Remove FORCE from r2 ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY; -- As owner, we now bypass RLS -- verify records in r2 updated TABLE r2; DROP TABLE r2; DROP TABLE r1; -- -- Test INSERT+RETURNING applies SELECT policies as -- WithCheckOptions (meaning an error is thrown) -- SET SESSION AUTHORIZATION regress_rls_alice; SET row_security = ON; CREATE TABLE r1 ( a int ); CREATE POLICY p1 ON r1 FOR SELECT USING (FALSE); CREATE POLICY p2 ON r1 FOR INSERT WITH CHECK (TRUE); ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; ALTER TABLE r1 FORCE ROW LEVEL SECURITY; -- Works fine INSERT INTO r1 VALUES (10), (20); -- No error, but no rows TABLE r1; SET row_security = OFF; -- fail, would be affected by RLS TABLE r1; SET row_security = ON; -- Error INSERT INTO r1 VALUES (10), (20) RETURNING *; DROP TABLE r1; -- -- Test UPDATE+RETURNING applies SELECT policies as -- WithCheckOptions (meaning an error is thrown) -- SET SESSION AUTHORIZATION regress_rls_alice; SET row_security = ON; CREATE TABLE r1 ( a int PRIMARY KEY ); CREATE POLICY p1 ON r1 FOR SELECT USING (a < 20); CREATE POLICY p2 ON r1 FOR UPDATE USING (a < 20) WITH CHECK (TRUE); CREATE POLICY p3 ON r1 FOR INSERT WITH CHECK (TRUE); INSERT INTO r1 VALUES (10); ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; ALTER TABLE r1 FORCE ROW LEVEL SECURITY; -- Works fine UPDATE r1 SET a = 30; -- Show updated rows ALTER TABLE r1 NO FORCE ROW LEVEL SECURITY; TABLE r1; -- reset value in r1 for test with RETURNING UPDATE r1 SET a = 10; -- Verify row reset TABLE r1; ALTER TABLE r1 FORCE ROW LEVEL SECURITY; -- Error UPDATE r1 SET a = 30 RETURNING *; -- UPDATE path of INSERT ... ON CONFLICT DO UPDATE should also error out INSERT INTO r1 VALUES (10) ON CONFLICT (a) DO UPDATE SET a = 30 RETURNING *; -- Should still error out without RETURNING (use of arbiter always requires -- SELECT permissions) INSERT INTO r1 VALUES (10) ON CONFLICT (a) DO UPDATE SET a = 30; INSERT INTO r1 VALUES (10) ON CONFLICT ON CONSTRAINT r1_pkey DO UPDATE SET a = 30; DROP TABLE r1; -- Check dependency handling RESET SESSION AUTHORIZATION; CREATE TABLE dep1 ( c1 int ); CREATE TABLE dep2 ( c1 int ); CREATE POLICY dep_p1 ON dep1 TO regress_rls_bob USING (c1 > (SELECT max(dep2.c1) FROM dep2)); ALTER POLICY dep_p1 ON dep1 TO regress_rls_bob, regress_rls_carol; -- Should return one SELECT count(*) = 1 FROM pg_depend WHERE objid = ( SELECT oid FROM pg_policy WHERE polname = 'dep_p1') AND refobjid = ( SELECT oid FROM pg_class WHERE relname = 'dep2'); ALTER POLICY dep_p1 ON dep1 USING (TRUE); -- Should return one SELECT count(*) = 1 FROM pg_shdepend WHERE objid = ( SELECT oid FROM pg_policy WHERE polname = 'dep_p1') AND refobjid = ( SELECT oid FROM pg_authid WHERE rolname = 'regress_rls_bob'); -- Should return one SELECT count(*) = 1 FROM pg_shdepend WHERE objid = ( SELECT oid FROM pg_policy WHERE polname = 'dep_p1') AND refobjid = ( SELECT oid FROM pg_authid WHERE rolname = 'regress_rls_carol'); -- Should return zero SELECT count(*) = 0 FROM pg_depend WHERE objid = ( SELECT oid FROM pg_policy WHERE polname = 'dep_p1') AND refobjid = ( SELECT oid FROM pg_class WHERE relname = 'dep2'); -- DROP OWNED BY testing RESET SESSION AUTHORIZATION; CREATE ROLE regress_rls_dob_role1; CREATE ROLE regress_rls_dob_role2; CREATE TABLE dob_t1 ( c1 int ); CREATE TABLE dob_t2 ( c1 int ) PARTITION BY RANGE (c1); CREATE POLICY p1 ON dob_t1 TO regress_rls_dob_role1 USING (TRUE); DROP OWNED BY regress_rls_dob_role1; DROP POLICY p1 ON dob_t1; -- should fail, already gone CREATE POLICY p1 ON dob_t1 TO regress_rls_dob_role1, regress_rls_dob_role2 USING (TRUE); DROP OWNED BY regress_rls_dob_role1; DROP POLICY p1 ON dob_t1; -- should succeed CREATE POLICY p1 ON dob_t2 TO regress_rls_dob_role1, regress_rls_dob_role2 USING (TRUE); DROP OWNED BY regress_rls_dob_role1; DROP POLICY p1 ON dob_t2; -- should succeed DROP USER regress_rls_dob_role1; DROP USER regress_rls_dob_role2; -- Bug #15708: view + table with RLS should check policies as view owner CREATE TABLE ref_tbl ( a int ); INSERT INTO ref_tbl VALUES (1); CREATE TABLE rls_tbl ( a int ); INSERT INTO rls_tbl VALUES (10); ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY; CREATE POLICY p1 ON rls_tbl USING (EXISTS (SELECT 1 FROM ref_tbl)); GRANT SELECT ON ref_tbl TO regress_rls_bob; GRANT SELECT ON rls_tbl TO regress_rls_bob; CREATE VIEW rls_view AS SELECT * FROM rls_tbl; ALTER VIEW rls_view OWNER TO regress_rls_bob; GRANT SELECT ON rls_view TO regress_rls_alice; SET SESSION AUTHORIZATION regress_rls_alice; SELECT * FROM ref_tbl; -- Permission denied SELECT * FROM rls_tbl; -- Permission denied SELECT * FROM rls_view; -- OK RESET SESSION AUTHORIZATION; DROP VIEW rls_view; DROP TABLE rls_tbl; DROP TABLE ref_tbl; -- -- Clean up objects -- RESET SESSION AUTHORIZATION; DROP SCHEMA regress_rls_schema CASCADE; DROP USER regress_rls_alice; DROP USER regress_rls_bob; DROP USER regress_rls_carol; DROP USER regress_rls_dave; DROP USER regress_rls_exempt_user; DROP ROLE regress_rls_group1; DROP ROLE regress_rls_group2; -- Arrange to have a few policies left over, for testing -- pg_dump/pg_restore CREATE SCHEMA regress_rls_schema; CREATE TABLE rls_tbl ( c1 int ); ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY; CREATE POLICY p1 ON rls_tbl USING (c1 > 5); CREATE POLICY p2 ON rls_tbl FOR SELECT USING (c1 <= 3); CREATE POLICY p3 ON rls_tbl FOR UPDATE USING (c1 <= 3) WITH CHECK (c1 > 5); CREATE POLICY p4 ON rls_tbl FOR DELETE USING (c1 <= 3); CREATE TABLE rls_tbl_force ( c1 int ); ALTER TABLE rls_tbl_force ENABLE ROW LEVEL SECURITY; ALTER TABLE rls_tbl_force FORCE ROW LEVEL SECURITY; CREATE POLICY p1 ON rls_tbl_force USING (c1 = 5) WITH CHECK (c1 < 5); CREATE POLICY p2 ON rls_tbl_force FOR SELECT USING (c1 = 8); CREATE POLICY p3 ON rls_tbl_force FOR UPDATE USING (c1 = 8) WITH CHECK (c1 >= 5); CREATE POLICY p4 ON rls_tbl_force FOR DELETE USING (c1 = 8); pgFormatter-4.2/t/pg-test-files/expected/rowtypes.sql000066400000000000000000000476031361326045100230300ustar00rootroot00000000000000-- -- ROWTYPES -- -- Make both a standalone composite type and a table rowtype CREATE TYPE complex AS ( r float8, i float8 ); CREATE temp TABLE fullname ( first text, last text ); -- Nested composite CREATE TYPE quad AS ( c1 complex, c2 complex ); -- Some simple tests of I/O conversions and row construction SELECT (1.1, 2.2)::complex, ROW ((3.3, 4.4), (5.5, NULL))::quad; SELECT ROW ('Joe', 'Blow')::fullname, '(Joe,Blow)'::fullname; SELECT '(Joe,von Blow)'::fullname, '(Joe,d''Blow)'::fullname; SELECT '(Joe,"von""Blow")'::fullname, E'(Joe,d\\\\Blow)'::fullname; SELECT '(Joe,"Blow,Jr")'::fullname; SELECT '(Joe,)'::fullname; -- ok, null 2nd column SELECT '(Joe)'::fullname; -- bad SELECT '(Joe,,)'::fullname; -- bad SELECT '[]'::fullname; -- bad SELECT ' (Joe,Blow) '::fullname; -- ok, extra whitespace SELECT '(Joe,Blow) /'::fullname; -- bad CREATE temp TABLE quadtable ( f1 int, q quad ); INSERT INTO quadtable VALUES (1, ((3.3, 4.4), (5.5, 6.6))); INSERT INTO quadtable VALUES (2, ((NULL, 4.4), (5.5, 6.6))); SELECT * FROM quadtable; SELECT f1, q.c1 FROM quadtable; -- fails, q is a table reference SELECT f1, (q).c1, (qq.q).c1.i FROM quadtable qq; CREATE temp TABLE people ( fn fullname, bd date ); INSERT INTO people VALUES ('(Joe,Blow)', '1984-01-10'); SELECT * FROM people; -- at the moment this will not work due to ALTER TABLE inadequacy: ALTER TABLE fullname ADD COLUMN suffix text DEFAULT ''; -- but this should work: ALTER TABLE fullname ADD COLUMN suffix text DEFAULT NULL; SELECT * FROM people; -- test insertion/updating of subfields UPDATE people SET fn.suffix = 'Jr'; SELECT * FROM people; INSERT INTO quadtable (f1, q.c1.r, q.c2.i) VALUES (44, 55, 66); SELECT * FROM quadtable; -- The object here is to ensure that toasted references inside -- composite values don't cause problems. The large f1 value will -- be toasted inside pp, it must still work after being copied to people. CREATE temp TABLE pp ( f1 text ); INSERT INTO pp VALUES (repeat('abcdefghijkl', 100000)); INSERT INTO people SELECT ('Jim', f1, NULL)::fullname, CURRENT_DATE FROM pp; SELECT (fn).FIRST, substr((fn).LAST, 1, 20), length((fn).LAST) FROM people; -- Test row comparison semantics. Prior to PG 8.2 we did this in a totally -- non-spec-compliant way. SELECT ROW (1, 2) < ROW (1, 3) AS true; SELECT ROW (1, 2) < ROW (1, 1) AS false; SELECT ROW (1, 2) < ROW (1, NULL) AS NULL; SELECT ROW (1, 2, 3) < ROW (1, 3, NULL) AS true; -- the NULL is not examined SELECT ROW (11, 'ABC') < ROW (11, 'DEF') AS true; SELECT ROW (11, 'ABC') > ROW (11, 'DEF') AS false; SELECT ROW (12, 'ABC') > ROW (11, 'DEF') AS true; -- = and <> have different NULL-behavior than < etc SELECT ROW (1, 2, 3) < ROW (1, NULL, 4) AS NULL; SELECT ROW (1, 2, 3) = ROW (1, NULL, 4) AS false; SELECT ROW (1, 2, 3) <> ROW (1, NULL, 4) AS true; -- We allow operators beyond the six standard ones, if they have btree -- operator classes. SELECT ROW ('ABC', 'DEF') ~<=~ ROW ('DEF', 'ABC') AS true; SELECT ROW ('ABC', 'DEF') ~>=~ ROW ('DEF', 'ABC') AS false; SELECT ROW ('ABC', 'DEF') ~~ ROW ('DEF', 'ABC') AS fail; -- Comparisons of ROW() expressions can cope with some type mismatches SELECT ROW (1, 2) = ROW (1, 2::int8); SELECT ROW (1, 2) IN (ROW (3, 4), ROW (1, 2)); SELECT ROW (1, 2) IN (ROW (3, 4), ROW (1, 2::int8)); -- Check row comparison with a subselect SELECT unique1, unique2 FROM tenk1 WHERE (unique1, unique2) < ANY ( SELECT ten, ten FROM tenk1 WHERE hundred < 3) AND unique1 <= 20 ORDER BY 1; -- Also check row comparison with an indexable condition EXPLAIN ( COSTS OFF ) SELECT thousand, tenthous FROM tenk1 WHERE (thousand, tenthous) >= (997, 5000) ORDER BY thousand, tenthous; SELECT thousand, tenthous FROM tenk1 WHERE (thousand, tenthous) >= (997, 5000) ORDER BY thousand, tenthous; EXPLAIN ( COSTS OFF ) SELECT thousand, tenthous, four FROM tenk1 WHERE (thousand, tenthous, four) > (998, 5000, 3) ORDER BY thousand, tenthous; SELECT thousand, tenthous, four FROM tenk1 WHERE (thousand, tenthous, four) > (998, 5000, 3) ORDER BY thousand, tenthous; EXPLAIN ( COSTS OFF ) SELECT thousand, tenthous FROM tenk1 WHERE (998, 5000) < (thousand, tenthous) ORDER BY thousand, tenthous; SELECT thousand, tenthous FROM tenk1 WHERE (998, 5000) < (thousand, tenthous) ORDER BY thousand, tenthous; EXPLAIN ( COSTS OFF ) SELECT thousand, hundred FROM tenk1 WHERE (998, 5000) < (thousand, hundred) ORDER BY thousand, hundred; SELECT thousand, hundred FROM tenk1 WHERE (998, 5000) < (thousand, hundred) ORDER BY thousand, hundred; -- Test case for bug #14010: indexed row comparisons fail with nulls CREATE temp TABLE test_table ( a text, b text ); INSERT INTO test_table VALUES ('a', 'b'); INSERT INTO test_table SELECT 'a', NULL FROM generate_series(1, 1000); INSERT INTO test_table VALUES ('b', 'a'); CREATE INDEX ON test_table (a, b); SET enable_sort = OFF; EXPLAIN ( COSTS OFF ) SELECT a, b FROM test_table WHERE (a, b) > ('a', 'a') ORDER BY a, b; SELECT a, b FROM test_table WHERE (a, b) > ('a', 'a') ORDER BY a, b; RESET enable_sort; -- Check row comparisons with IN SELECT * FROM int8_tbl i8 WHERE i8 IN (ROW (123, 456)); -- fail, type mismatch EXPLAIN ( COSTS OFF ) SELECT * FROM int8_tbl i8 WHERE i8 IN (ROW (123, 456)::int8_tbl, '(4567890123456789,123)'); SELECT * FROM int8_tbl i8 WHERE i8 IN (ROW (123, 456)::int8_tbl, '(4567890123456789,123)'); -- Check some corner cases involving empty rowtypes SELECT ROW (); SELECT ROW () IS NULL; SELECT ROW () = ROW (); -- Check ability to create arrays of anonymous rowtypes SELECT ARRAY[ROW (1, 2), ROW (3, 4), ROW (5, 6)]; -- Check ability to compare an anonymous row to elements of an array SELECT ROW (1, 1.1) = ANY (ARRAY[ROW (7, 7.7), ROW (1, 1.1), ROW (0, 0.0)]); SELECT ROW (1, 1.1) = ANY (ARRAY[ROW (7, 7.7), ROW (1, 1.0), ROW (0, 0.0)]); -- Check behavior with a non-comparable rowtype CREATE TYPE cantcompare AS ( p point, r float8 ); CREATE temp TABLE cc ( f1 cantcompare ); INSERT INTO cc VALUES ('("(1,2)",3)'); INSERT INTO cc VALUES ('("(4,5)",6)'); SELECT * FROM cc ORDER BY f1; -- fail, but should complain about cantcompare -- -- Tests for record_{eq,cmp} -- CREATE TYPE testtype1 AS ( a int, b int ); -- all true SELECT ROW (1, 2)::testtype1 < ROW (1, 3)::testtype1; SELECT ROW (1, 2)::testtype1 <= ROW (1, 3)::testtype1; SELECT ROW (1, 2)::testtype1 = ROW (1, 2)::testtype1; SELECT ROW (1, 2)::testtype1 <> ROW (1, 3)::testtype1; SELECT ROW (1, 3)::testtype1 >= ROW (1, 2)::testtype1; SELECT ROW (1, 3)::testtype1 > ROW (1, 2)::testtype1; -- all false SELECT ROW (1, - 2)::testtype1 < ROW (1, - 3)::testtype1; SELECT ROW (1, - 2)::testtype1 <= ROW (1, - 3)::testtype1; SELECT ROW (1, - 2)::testtype1 = ROW (1, - 3)::testtype1; SELECT ROW (1, - 2)::testtype1 <> ROW (1, - 2)::testtype1; SELECT ROW (1, - 3)::testtype1 >= ROW (1, - 2)::testtype1; SELECT ROW (1, - 3)::testtype1 > ROW (1, - 2)::testtype1; -- true, but see *< below SELECT ROW (1, - 2)::testtype1 < ROW (1, 3)::testtype1; -- mismatches CREATE TYPE testtype3 AS ( a int, b text ); SELECT ROW (1, 2)::testtype1 < ROW (1, 'abc')::testtype3; SELECT ROW (1, 2)::testtype1 <> ROW (1, 'abc')::testtype3; CREATE TYPE testtype5 AS ( a int ); SELECT ROW (1, 2)::testtype1 < ROW (1)::testtype5; SELECT ROW (1, 2)::testtype1 <> ROW (1)::testtype5; -- non-comparable types CREATE TYPE testtype6 AS ( a int, b point ); SELECT ROW (1, '(1,2)')::testtype6 < ROW (1, '(1,3)')::testtype6; SELECT ROW (1, '(1,2)')::testtype6 <> ROW (1, '(1,3)')::testtype6; DROP TYPE testtype1, testtype3, testtype5, testtype6; -- -- Tests for record_image_{eq,cmp} -- CREATE TYPE testtype1 AS ( a int, b int ); -- all true SELECT ROW (1, 2)::testtype1 *< ROW (1, 3)::testtype1; SELECT ROW (1, 2)::testtype1 *<= ROW (1, 3)::testtype1; SELECT ROW (1, 2)::testtype1 *= ROW (1, 2)::testtype1; SELECT ROW (1, 2)::testtype1 *<> ROW (1, 3)::testtype1; SELECT ROW (1, 3)::testtype1 *>= ROW (1, 2)::testtype1; SELECT ROW (1, 3)::testtype1 *> ROW (1, 2)::testtype1; -- all false SELECT ROW (1, - 2)::testtype1 *< ROW (1, - 3)::testtype1; SELECT ROW (1, - 2)::testtype1 *<= ROW (1, - 3)::testtype1; SELECT ROW (1, - 2)::testtype1 *= ROW (1, - 3)::testtype1; SELECT ROW (1, - 2)::testtype1 *<> ROW (1, - 2)::testtype1; SELECT ROW (1, - 3)::testtype1 *>= ROW (1, - 2)::testtype1; SELECT ROW (1, - 3)::testtype1 *> ROW (1, - 2)::testtype1; -- This returns the "wrong" order because record_image_cmp works on -- unsigned datums without knowing about the actual data type. SELECT ROW (1, - 2)::testtype1 *< ROW (1, 3)::testtype1; -- other types CREATE TYPE testtype2 AS ( a smallint, b bool ); -- byval different sizes SELECT ROW (1, TRUE)::testtype2 *< ROW (2, TRUE)::testtype2; SELECT ROW (- 2, TRUE)::testtype2 *< ROW (- 1, TRUE)::testtype2; SELECT ROW (0, FALSE)::testtype2 *< ROW (0, TRUE)::testtype2; SELECT ROW (0, FALSE)::testtype2 *<> ROW (0, TRUE)::testtype2; CREATE TYPE testtype3 AS ( a int, b text ); -- variable length SELECT ROW (1, 'abc')::testtype3 *< ROW (1, 'abd')::testtype3; SELECT ROW (1, 'abc')::testtype3 *< ROW (1, 'abcd')::testtype3; SELECT ROW (1, 'abc')::testtype3 *> ROW (1, 'abd')::testtype3; SELECT ROW (1, 'abc')::testtype3 *<> ROW (1, 'abd')::testtype3; CREATE TYPE testtype4 AS ( a int, b point ); -- by ref, fixed length SELECT ROW (1, '(1,2)')::testtype4 *< ROW (1, '(1,3)')::testtype4; SELECT ROW (1, '(1,2)')::testtype4 *<> ROW (1, '(1,3)')::testtype4; -- mismatches SELECT ROW (1, 2)::testtype1 *< ROW (1, 'abc')::testtype3; SELECT ROW (1, 2)::testtype1 *<> ROW (1, 'abc')::testtype3; CREATE TYPE testtype5 AS ( a int ); SELECT ROW (1, 2)::testtype1 *< ROW (1)::testtype5; SELECT ROW (1, 2)::testtype1 *<> ROW (1)::testtype5; -- non-comparable types CREATE TYPE testtype6 AS ( a int, b point ); SELECT ROW (1, '(1,2)')::testtype6 *< ROW (1, '(1,3)')::testtype6; SELECT ROW (1, '(1,2)')::testtype6 *>= ROW (1, '(1,3)')::testtype6; SELECT ROW (1, '(1,2)')::testtype6 *<> ROW (1, '(1,3)')::testtype6; -- anonymous rowtypes in coldeflists SELECT q.a, q.b = ROW (2), q.c = ARRAY[ROW (3)], q.d = ROW (ROW (4)) FROM unnest(ARRAY[ROW (1, ROW (2), ARRAY[ROW (3)], ROW (ROW (4))), ROW (2, ROW (3), ARRAY[ROW (4)], ROW (ROW (5)))]) AS q (a int, b record, c record[], d record); DROP TYPE testtype1, testtype2, testtype3, testtype4, testtype5, testtype6; -- -- Test case derived from bug #5716: check multiple uses of a rowtype result -- BEGIN; CREATE TABLE price ( id serial PRIMARY KEY, active boolean NOT NULL, price numeric ); CREATE TYPE price_input AS ( id integer, price numeric ); CREATE TYPE price_key AS ( id integer ); CREATE FUNCTION price_key_from_table (price) RETURNS price_key AS $$ SELECT $1.id $$ LANGUAGE SQL; CREATE FUNCTION price_key_from_input (price_input) RETURNS price_key AS $$ SELECT $1.id $$ LANGUAGE SQL; INSERT INTO price VALUES (1, FALSE, 42), (10, FALSE, 100), (11, TRUE, 17.99); UPDATE price SET active = TRUE, price = input_prices.price FROM unnest(ARRAY[(10, 123.00), (11, 99.99)]::price_input[]) input_prices WHERE price_key_from_table (price.*) = price_key_from_input (input_prices.*); SELECT * FROM price; ROLLBACK; -- -- Test case derived from bug #9085: check * qualification of composite -- parameters for SQL functions -- CREATE temp TABLE compos ( f1 int, f2 text ); CREATE FUNCTION fcompos1 (v compos) RETURNS void AS $$ INSERT INTO compos VALUES (v); -- fail $$ LANGUAGE sql; CREATE FUNCTION fcompos1 (v compos) RETURNS void AS $$ INSERT INTO compos VALUES (v.*); $$ LANGUAGE sql; CREATE FUNCTION fcompos2 (v compos) RETURNS void AS $$ SELECT fcompos1 (v); $$ LANGUAGE sql; CREATE FUNCTION fcompos3 (v compos) RETURNS void AS $$ SELECT fcompos1 (fcompos3.v.*); $$ LANGUAGE sql; SELECT fcompos1 (ROW (1, 'one')); SELECT fcompos2 (ROW (2, 'two')); SELECT fcompos3 (ROW (3, 'three')); SELECT * FROM compos; -- -- We allow I/O conversion casts from composite types to strings to be -- invoked via cast syntax, but not functional syntax. This is because -- the latter is too prone to be invoked unintentionally. -- SELECT cast(fullname AS text) FROM fullname; SELECT fullname::text FROM fullname; SELECT text(fullname) FROM fullname; -- error SELECT fullname.text FROM fullname; -- error -- same, but RECORD instead of named composite type: SELECT cast(ROW ('Jim', 'Beam') AS text); SELECT (ROW ('Jim', 'Beam'))::text; SELECT text(ROW ('Jim', 'Beam')); -- error SELECT (ROW ('Jim', 'Beam')).text; -- error -- -- Check the equivalence of functional and column notation -- INSERT INTO fullname VALUES ('Joe', 'Blow'); SELECT f.last FROM fullname f; SELECT LAST (f) FROM fullname f; CREATE FUNCTION longname (fullname) RETURNS text LANGUAGE sql AS $$ SELECT $1.FIRST || ' ' || $1.last$$; SELECT f.longname FROM fullname f; SELECT longname (f) FROM fullname f; -- Starting in v11, the notational form does matter if there's ambiguity ALTER TABLE fullname ADD COLUMN longname text; SELECT f.longname FROM fullname f; SELECT longname (f) FROM fullname f; -- -- Test that composite values are seen to have the correct column names -- (bug #11210 and other reports) -- SELECT row_to_json(i) FROM int8_tbl i; SELECT row_to_json(i) FROM int8_tbl i (x, y); CREATE temp VIEW vv1 AS SELECT * FROM int8_tbl; SELECT row_to_json(i) FROM vv1 i; SELECT row_to_json(i) FROM vv1 i (x, y); SELECT row_to_json(ss) FROM ( SELECT q1, q2 FROM int8_tbl) AS ss; SELECT row_to_json(ss) FROM ( SELECT q1, q2 FROM int8_tbl offset 0) AS ss; SELECT row_to_json(ss) FROM ( SELECT q1 AS a, q2 AS b FROM int8_tbl) AS ss; SELECT row_to_json(ss) FROM ( SELECT q1 AS a, q2 AS b FROM int8_tbl offset 0) AS ss; SELECT row_to_json(ss) FROM ( SELECT q1 AS a, q2 AS b FROM int8_tbl) AS ss (x, y); SELECT row_to_json(ss) FROM ( SELECT q1 AS a, q2 AS b FROM int8_tbl offset 0) AS ss (x, y); EXPLAIN ( COSTS OFF ) SELECT row_to_json(q) FROM ( SELECT thousand, tenthous FROM tenk1 WHERE thousand = 42 AND tenthous < 2000 offset 0) q; SELECT row_to_json(q) FROM ( SELECT thousand, tenthous FROM tenk1 WHERE thousand = 42 AND tenthous < 2000 offset 0) q; SELECT row_to_json(q) FROM ( SELECT thousand AS x, tenthous AS y FROM tenk1 WHERE thousand = 42 AND tenthous < 2000 offset 0) q; SELECT row_to_json(q) FROM ( SELECT thousand AS x, tenthous AS y FROM tenk1 WHERE thousand = 42 AND tenthous < 2000 offset 0) q (a, b); CREATE temp TABLE tt1 AS SELECT * FROM int8_tbl LIMIT 2; CREATE temp TABLE tt2 () INHERITS ( tt1 ); INSERT INTO tt2 VALUES (0, 0); SELECT row_to_json(r) FROM ( SELECT q2, q1 FROM tt1 offset 0) r; -- check no-op rowtype conversions CREATE temp TABLE tt3 () INHERITS ( tt2 ); INSERT INTO tt3 VALUES (33, 44); SELECT row_to_json(tt3::tt2::tt1) FROM tt3; -- -- IS [NOT] NULL should not recurse into nested composites (bug #14235) -- EXPLAIN ( VERBOSE, COSTS OFF ) SELECT r, r IS NULL AS ISNULL, r IS NOT NULL AS isnotnull FROM ( VALUES (1, ROW (1, 2)), (1, ROW (NULL, NULL)), (1, NULL), (NULL, ROW (1, 2)), (NULL, ROW (NULL, NULL)), (NULL, NULL)) r (a, b); SELECT r, r IS NULL AS ISNULL, r IS NOT NULL AS isnotnull FROM ( VALUES (1, ROW (1, 2)), (1, ROW (NULL, NULL)), (1, NULL), (NULL, ROW (1, 2)), (NULL, ROW (NULL, NULL)), (NULL, NULL)) r (a, b); EXPLAIN ( VERBOSE, COSTS OFF ) WITH r (a, b) AS MATERIALIZED ( VALUES (1, ROW (1, 2)), (1, ROW (NULL, NULL)), (1, NULL), (NULL, ROW (1, 2)), (NULL, ROW (NULL, NULL)), (NULL, NULL)) SELECT r, r IS NULL AS ISNULL, r IS NOT NULL AS isnotnull FROM r; WITH r ( a, b ) AS MATERIALIZED ( VALUES ( 1, ROW ( 1, 2 ) ), ( 1, ROW ( NULL, NULL ) ), ( 1, NULL ), ( NULL, ROW ( 1, 2 ) ), ( NULL, ROW ( NULL, NULL ) ), ( NULL, NULL )) SELECT r, r IS NULL AS ISNULL, r IS NOT NULL AS isnotnull FROM r; -- -- Tests for component access / FieldSelect -- CREATE TABLE compositetable ( a text, b text ); INSERT INTO compositetable (a, b) VALUES ('fa', 'fb'); -- composite type columns can't directly be accessed (error) SELECT d.a FROM ( SELECT compositetable AS d FROM compositetable) s; -- but can be accessed with proper parens SELECT (d).a, (d).b FROM ( SELECT compositetable AS d FROM compositetable) s; -- system columns can't be accessed in composite types (error) SELECT (d).ctid FROM ( SELECT compositetable AS d FROM compositetable) s; -- accessing non-existing column in NULL datum errors out SELECT (NULL::compositetable).nonexistant; -- existing column in a NULL composite yield NULL SELECT (NULL::compositetable).a; -- oids can't be accessed in composite types (error) SELECT (NULL::compositetable).oid; DROP TABLE compositetable; pgFormatter-4.2/t/pg-test-files/expected/rules.sql000066400000000000000000001255621361326045100222670ustar00rootroot00000000000000-- -- RULES -- From Jan's original setup_ruletest.sql and run_ruletest.sql -- - thomas 1998-09-13 -- -- -- Tables and rules for the view test -- CREATE TABLE rtest_t1 ( a int4, b int4 ); CREATE TABLE rtest_t2 ( a int4, b int4 ); CREATE TABLE rtest_t3 ( a int4, b int4 ); CREATE VIEW rtest_v1 AS SELECT * FROM rtest_t1; CREATE RULE rtest_v1_ins AS ON INSERT TO rtest_v1 DO INSTEAD INSERT INTO rtest_t1 VALUES (new.a, new.b); CREATE RULE rtest_v1_upd AS ON UPDATE TO rtest_v1 DO INSTEAD UPDATE rtest_t1 SET a = new.a, b = new.b WHERE a = old.a; CREATE RULE rtest_v1_del AS ON DELETE TO rtest_v1 DO INSTEAD DELETE FROM rtest_t1 WHERE a = old.a; -- Test comments COMMENT ON RULE rtest_v1_bad ON rtest_v1 IS 'bad rule'; COMMENT ON RULE rtest_v1_del ON rtest_v1 IS 'delete rule'; COMMENT ON RULE rtest_v1_del ON rtest_v1 IS NULL; -- -- Tables and rules for the constraint update/delete test -- -- Note: -- Now that we have multiple action rule support, we check -- both possible syntaxes to define them (The last action -- can but must not have a semicolon at the end). -- CREATE TABLE rtest_system ( sysname text, sysdesc text ); CREATE TABLE rtest_interface ( sysname text, ifname text ); CREATE TABLE rtest_person ( pname text, pdesc text ); CREATE TABLE rtest_admin ( pname text, sysname text ); CREATE RULE rtest_sys_upd AS ON UPDATE TO rtest_system DO also ( UPDATE rtest_interface SET sysname = new.sysname WHERE sysname = old.sysname; UPDATE rtest_admin SET sysname = new.sysname WHERE sysname = old.sysname); CREATE RULE rtest_sys_del AS ON DELETE TO rtest_system DO also ( DELETE FROM rtest_interface WHERE sysname = old.sysname; DELETE FROM rtest_admin WHERE sysname = old.sysname; ); CREATE RULE rtest_pers_upd AS ON UPDATE TO rtest_person DO also UPDATE rtest_admin SET pname = new.pname WHERE pname = old.pname; CREATE RULE rtest_pers_del AS ON DELETE TO rtest_person DO also DELETE FROM rtest_admin WHERE pname = old.pname; -- -- Tables and rules for the logging test -- CREATE TABLE rtest_emp ( ename char(20), salary money ); CREATE TABLE rtest_emplog ( ename char(20), who name, action char(10), newsal money, oldsal money ); CREATE TABLE rtest_empmass ( ename char(20), salary money ); CREATE RULE rtest_emp_ins AS ON INSERT TO rtest_emp DO INSERT INTO rtest_emplog VALUES (new.ename, CURRENT_USER, 'hired', new.salary, '0.00'); CREATE RULE rtest_emp_upd AS ON UPDATE TO rtest_emp WHERE new.salary != old.salary DO INSERT INTO rtest_emplog VALUES (new.ename, CURRENT_USER, 'honored', new.salary, old.salary); CREATE RULE rtest_emp_del AS ON DELETE TO rtest_emp DO INSERT INTO rtest_emplog VALUES (old.ename, CURRENT_USER, 'fired', '0.00', old.salary); -- -- Tables and rules for the multiple cascaded qualified instead -- rule test -- CREATE TABLE rtest_t4 ( a int4, b text ); CREATE TABLE rtest_t5 ( a int4, b text ); CREATE TABLE rtest_t6 ( a int4, b text ); CREATE TABLE rtest_t7 ( a int4, b text ); CREATE TABLE rtest_t8 ( a int4, b text ); CREATE TABLE rtest_t9 ( a int4, b text ); CREATE RULE rtest_t4_ins1 AS ON INSERT TO rtest_t4 WHERE new.a >= 10 AND new.a < 20 DO INSTEAD INSERT INTO rtest_t5 VALUES (new.a, new.b); CREATE RULE rtest_t4_ins2 AS ON INSERT TO rtest_t4 WHERE new.a >= 20 AND new.a < 30 DO INSERT INTO rtest_t6 VALUES (new.a, new.b); CREATE RULE rtest_t5_ins AS ON INSERT TO rtest_t5 WHERE new.a > 15 DO INSERT INTO rtest_t7 VALUES (new.a, new.b); CREATE RULE rtest_t6_ins AS ON INSERT TO rtest_t6 WHERE new.a > 25 DO INSTEAD INSERT INTO rtest_t8 VALUES (new.a, new.b); -- -- Tables and rules for the rule fire order test -- -- As of PG 7.3, the rules should fire in order by name, regardless -- of INSTEAD attributes or creation order. -- CREATE TABLE rtest_order1 ( a int4 ); CREATE TABLE rtest_order2 ( a int4, b int4, c text ); CREATE SEQUENCE rtest_seq; CREATE RULE rtest_order_r3 AS ON INSERT TO rtest_order1 DO INSTEAD INSERT INTO rtest_order2 VALUES (new.a, nextval('rtest_seq'), 'rule 3 - this should run 3rd'); CREATE RULE rtest_order_r4 AS ON INSERT TO rtest_order1 WHERE a < 100 DO INSTEAD INSERT INTO rtest_order2 VALUES (new.a, nextval('rtest_seq'), 'rule 4 - this should run 4th'); CREATE RULE rtest_order_r2 AS ON INSERT TO rtest_order1 DO INSERT INTO rtest_order2 VALUES (new.a, nextval('rtest_seq'), 'rule 2 - this should run 2nd'); CREATE RULE rtest_order_r1 AS ON INSERT TO rtest_order1 DO INSTEAD INSERT INTO rtest_order2 VALUES (new.a, nextval('rtest_seq'), 'rule 1 - this should run 1st'); -- -- Tables and rules for the instead nothing test -- CREATE TABLE rtest_nothn1 ( a int4, b text ); CREATE TABLE rtest_nothn2 ( a int4, b text ); CREATE TABLE rtest_nothn3 ( a int4, b text ); CREATE TABLE rtest_nothn4 ( a int4, b text ); CREATE RULE rtest_nothn_r1 AS ON INSERT TO rtest_nothn1 WHERE new.a >= 10 AND new.a < 20 DO INSTEAD NOTHING; CREATE RULE rtest_nothn_r2 AS ON INSERT TO rtest_nothn1 WHERE new.a >= 30 AND new.a < 40 DO INSTEAD NOTHING; CREATE RULE rtest_nothn_r3 AS ON INSERT TO rtest_nothn2 WHERE new.a >= 100 DO INSTEAD INSERT INTO rtest_nothn3 VALUES (new.a, new.b); CREATE RULE rtest_nothn_r4 AS ON INSERT TO rtest_nothn2 DO INSTEAD NOTHING; -- -- Tests on a view that is select * of a table -- and has insert/update/delete instead rules to -- behave close like the real table. -- -- -- We need test date later -- INSERT INTO rtest_t2 VALUES (1, 21); INSERT INTO rtest_t2 VALUES (2, 22); INSERT INTO rtest_t2 VALUES (3, 23); INSERT INTO rtest_t3 VALUES (1, 31); INSERT INTO rtest_t3 VALUES (2, 32); INSERT INTO rtest_t3 VALUES (3, 33); INSERT INTO rtest_t3 VALUES (4, 34); INSERT INTO rtest_t3 VALUES (5, 35); -- insert values INSERT INTO rtest_v1 VALUES (1, 11); INSERT INTO rtest_v1 VALUES (2, 12); SELECT * FROM rtest_v1; -- delete with constant expression DELETE FROM rtest_v1 WHERE a = 1; SELECT * FROM rtest_v1; INSERT INTO rtest_v1 VALUES (1, 11); DELETE FROM rtest_v1 WHERE b = 12; SELECT * FROM rtest_v1; INSERT INTO rtest_v1 VALUES (2, 12); INSERT INTO rtest_v1 VALUES (2, 13); SELECT * FROM rtest_v1; * * Remember the DELETE RULE ON rtest_v1: It says * * DO INSTEAD DELETE FROM rtest_t1 WHERE a = old.a * * So this time BOTH ROWS WITH a = 2 must get deleted \p \r DELETE FROM rtest_v1 WHERE b = 12; SELECT * FROM rtest_v1; DELETE FROM rtest_v1; -- insert select INSERT INTO rtest_v1 SELECT * FROM rtest_t2; SELECT * FROM rtest_v1; DELETE FROM rtest_v1; -- same with swapped targetlist INSERT INTO rtest_v1 (b, a) SELECT b, a FROM rtest_t2; SELECT * FROM rtest_v1; -- now with only one target attribute INSERT INTO rtest_v1 (a) SELECT a FROM rtest_t3; SELECT * FROM rtest_v1; SELECT * FROM rtest_v1 WHERE b ISNULL; -- let attribute a differ (must be done on rtest_t1 - see above) UPDATE rtest_t1 SET a = a + 10 WHERE b ISNULL; DELETE FROM rtest_v1 WHERE b ISNULL; SELECT * FROM rtest_v1; -- now updates with constant expression UPDATE rtest_v1 SET b = 42 WHERE a = 2; SELECT * FROM rtest_v1; UPDATE rtest_v1 SET b = 99 WHERE b = 42; SELECT * FROM rtest_v1; UPDATE rtest_v1 SET b = 88 WHERE b < 50; SELECT * FROM rtest_v1; DELETE FROM rtest_v1; INSERT INTO rtest_v1 SELECT rtest_t2.a, rtest_t3.b FROM rtest_t2, rtest_t3 WHERE rtest_t2.a = rtest_t3.a; SELECT * FROM rtest_v1; -- updates in a mergejoin UPDATE rtest_v1 SET b = rtest_t2.b FROM rtest_t2 WHERE rtest_v1.a = rtest_t2.a; SELECT * FROM rtest_v1; INSERT INTO rtest_v1 SELECT * FROM rtest_t3; SELECT * FROM rtest_v1; UPDATE rtest_t1 SET a = a + 10 WHERE b > 30; SELECT * FROM rtest_v1; UPDATE rtest_v1 SET a = rtest_t3.a + 20 FROM rtest_t3 WHERE rtest_v1.b = rtest_t3.b; SELECT * FROM rtest_v1; -- -- Test for constraint updates/deletes -- INSERT INTO rtest_system VALUES ('orion', 'Linux Jan Wieck'); INSERT INTO rtest_system VALUES ('notjw', 'WinNT Jan Wieck (notebook)'); INSERT INTO rtest_system VALUES ('neptun', 'Fileserver'); INSERT INTO rtest_interface VALUES ('orion', 'eth0'); INSERT INTO rtest_interface VALUES ('orion', 'eth1'); INSERT INTO rtest_interface VALUES ('notjw', 'eth0'); INSERT INTO rtest_interface VALUES ('neptun', 'eth0'); INSERT INTO rtest_person VALUES ('jw', 'Jan Wieck'); INSERT INTO rtest_person VALUES ('bm', 'Bruce Momjian'); INSERT INTO rtest_admin VALUES ('jw', 'orion'); INSERT INTO rtest_admin VALUES ('jw', 'notjw'); INSERT INTO rtest_admin VALUES ('bm', 'neptun'); UPDATE rtest_system SET sysname = 'pluto' WHERE sysname = 'neptun'; SELECT * FROM rtest_interface; SELECT * FROM rtest_admin; UPDATE rtest_person SET pname = 'jwieck' WHERE pdesc = 'Jan Wieck'; -- Note: use ORDER BY here to ensure consistent output across all systems. -- The above UPDATE affects two rows with equal keys, so they could be -- updated in either order depending on the whim of the local qsort(). SELECT * FROM rtest_admin ORDER BY pname, sysname; DELETE FROM rtest_system WHERE sysname = 'orion'; SELECT * FROM rtest_interface; SELECT * FROM rtest_admin; -- -- Rule qualification test -- INSERT INTO rtest_emp VALUES ('wiecc', '5000.00'); INSERT INTO rtest_emp VALUES ('gates', '80000.00'); UPDATE rtest_emp SET ename = 'wiecx' WHERE ename = 'wiecc'; UPDATE rtest_emp SET ename = 'wieck', salary = '6000.00' WHERE ename = 'wiecx'; UPDATE rtest_emp SET salary = '7000.00' WHERE ename = 'wieck'; DELETE FROM rtest_emp WHERE ename = 'gates'; SELECT ename, who = CURRENT_USER AS "matches user", action, newsal, oldsal FROM rtest_emplog ORDER BY ename, action, newsal; INSERT INTO rtest_empmass VALUES ('meyer', '4000.00'); INSERT INTO rtest_empmass VALUES ('maier', '5000.00'); INSERT INTO rtest_empmass VALUES ('mayr', '6000.00'); INSERT INTO rtest_emp SELECT * FROM rtest_empmass; SELECT ename, who = CURRENT_USER AS "matches user", action, newsal, oldsal FROM rtest_emplog ORDER BY ename, action, newsal; UPDATE rtest_empmass SET salary = salary + '1000.00'; UPDATE rtest_emp SET salary = rtest_empmass.salary FROM rtest_empmass WHERE rtest_emp.ename = rtest_empmass.ename; SELECT ename, who = CURRENT_USER AS "matches user", action, newsal, oldsal FROM rtest_emplog ORDER BY ename, action, newsal; DELETE FROM rtest_emp USING rtest_empmass WHERE rtest_emp.ename = rtest_empmass.ename; SELECT ename, who = CURRENT_USER AS "matches user", action, newsal, oldsal FROM rtest_emplog ORDER BY ename, action, newsal; -- -- Multiple cascaded qualified instead rule test -- INSERT INTO rtest_t4 VALUES (1, 'Record should go to rtest_t4'); INSERT INTO rtest_t4 VALUES (2, 'Record should go to rtest_t4'); INSERT INTO rtest_t4 VALUES (10, 'Record should go to rtest_t5'); INSERT INTO rtest_t4 VALUES (15, 'Record should go to rtest_t5'); INSERT INTO rtest_t4 VALUES (19, 'Record should go to rtest_t5 and t7'); INSERT INTO rtest_t4 VALUES (20, 'Record should go to rtest_t4 and t6'); INSERT INTO rtest_t4 VALUES (26, 'Record should go to rtest_t4 and t8'); INSERT INTO rtest_t4 VALUES (28, 'Record should go to rtest_t4 and t8'); INSERT INTO rtest_t4 VALUES (30, 'Record should go to rtest_t4'); INSERT INTO rtest_t4 VALUES (40, 'Record should go to rtest_t4'); SELECT * FROM rtest_t4; SELECT * FROM rtest_t5; SELECT * FROM rtest_t6; SELECT * FROM rtest_t7; SELECT * FROM rtest_t8; DELETE FROM rtest_t4; DELETE FROM rtest_t5; DELETE FROM rtest_t6; DELETE FROM rtest_t7; DELETE FROM rtest_t8; INSERT INTO rtest_t9 VALUES (1, 'Record should go to rtest_t4'); INSERT INTO rtest_t9 VALUES (2, 'Record should go to rtest_t4'); INSERT INTO rtest_t9 VALUES (10, 'Record should go to rtest_t5'); INSERT INTO rtest_t9 VALUES (15, 'Record should go to rtest_t5'); INSERT INTO rtest_t9 VALUES (19, 'Record should go to rtest_t5 and t7'); INSERT INTO rtest_t9 VALUES (20, 'Record should go to rtest_t4 and t6'); INSERT INTO rtest_t9 VALUES (26, 'Record should go to rtest_t4 and t8'); INSERT INTO rtest_t9 VALUES (28, 'Record should go to rtest_t4 and t8'); INSERT INTO rtest_t9 VALUES (30, 'Record should go to rtest_t4'); INSERT INTO rtest_t9 VALUES (40, 'Record should go to rtest_t4'); INSERT INTO rtest_t4 SELECT * FROM rtest_t9 WHERE a < 20; SELECT * FROM rtest_t4; SELECT * FROM rtest_t5; SELECT * FROM rtest_t6; SELECT * FROM rtest_t7; SELECT * FROM rtest_t8; INSERT INTO rtest_t4 SELECT * FROM rtest_t9 WHERE b ~ 'and t8'; SELECT * FROM rtest_t4; SELECT * FROM rtest_t5; SELECT * FROM rtest_t6; SELECT * FROM rtest_t7; SELECT * FROM rtest_t8; INSERT INTO rtest_t4 SELECT a + 1, b FROM rtest_t9 WHERE a IN (20, 30, 40); SELECT * FROM rtest_t4; SELECT * FROM rtest_t5; SELECT * FROM rtest_t6; SELECT * FROM rtest_t7; SELECT * FROM rtest_t8; -- -- Check that the ordering of rules fired is correct -- INSERT INTO rtest_order1 VALUES (1); SELECT * FROM rtest_order2; -- -- Check if instead nothing w/without qualification works -- INSERT INTO rtest_nothn1 VALUES (1, 'want this'); INSERT INTO rtest_nothn1 VALUES (2, 'want this'); INSERT INTO rtest_nothn1 VALUES (10, 'don''t want this'); INSERT INTO rtest_nothn1 VALUES (19, 'don''t want this'); INSERT INTO rtest_nothn1 VALUES (20, 'want this'); INSERT INTO rtest_nothn1 VALUES (29, 'want this'); INSERT INTO rtest_nothn1 VALUES (30, 'don''t want this'); INSERT INTO rtest_nothn1 VALUES (39, 'don''t want this'); INSERT INTO rtest_nothn1 VALUES (40, 'want this'); INSERT INTO rtest_nothn1 VALUES (50, 'want this'); INSERT INTO rtest_nothn1 VALUES (60, 'want this'); SELECT * FROM rtest_nothn1; INSERT INTO rtest_nothn2 VALUES (10, 'too small'); INSERT INTO rtest_nothn2 VALUES (50, 'too small'); INSERT INTO rtest_nothn2 VALUES (100, 'OK'); INSERT INTO rtest_nothn2 VALUES (200, 'OK'); SELECT * FROM rtest_nothn2; SELECT * FROM rtest_nothn3; DELETE FROM rtest_nothn1; DELETE FROM rtest_nothn2; DELETE FROM rtest_nothn3; INSERT INTO rtest_nothn4 VALUES (1, 'want this'); INSERT INTO rtest_nothn4 VALUES (2, 'want this'); INSERT INTO rtest_nothn4 VALUES (10, 'don''t want this'); INSERT INTO rtest_nothn4 VALUES (19, 'don''t want this'); INSERT INTO rtest_nothn4 VALUES (20, 'want this'); INSERT INTO rtest_nothn4 VALUES (29, 'want this'); INSERT INTO rtest_nothn4 VALUES (30, 'don''t want this'); INSERT INTO rtest_nothn4 VALUES (39, 'don''t want this'); INSERT INTO rtest_nothn4 VALUES (40, 'want this'); INSERT INTO rtest_nothn4 VALUES (50, 'want this'); INSERT INTO rtest_nothn4 VALUES (60, 'want this'); INSERT INTO rtest_nothn1 SELECT * FROM rtest_nothn4; SELECT * FROM rtest_nothn1; DELETE FROM rtest_nothn4; INSERT INTO rtest_nothn4 VALUES (10, 'too small'); INSERT INTO rtest_nothn4 VALUES (50, 'too small'); INSERT INTO rtest_nothn4 VALUES (100, 'OK'); INSERT INTO rtest_nothn4 VALUES (200, 'OK'); INSERT INTO rtest_nothn2 SELECT * FROM rtest_nothn4; SELECT * FROM rtest_nothn2; SELECT * FROM rtest_nothn3; CREATE TABLE rtest_view1 ( a int4, b text, v bool ); CREATE TABLE rtest_view2 ( a int4 ); CREATE TABLE rtest_view3 ( a int4, b text ); CREATE TABLE rtest_view4 ( a int4, b text, c int4 ); CREATE VIEW rtest_vview1 AS SELECT a, b FROM rtest_view1 X WHERE 0 < ( SELECT count(*) FROM rtest_view2 Y WHERE Y.a = X.a); CREATE VIEW rtest_vview2 AS SELECT a, b FROM rtest_view1 WHERE v; CREATE VIEW rtest_vview3 AS SELECT a, b FROM rtest_vview2 X WHERE 0 < ( SELECT count(*) FROM rtest_view2 Y WHERE Y.a = X.a); CREATE VIEW rtest_vview4 AS SELECT X.a, X.b, count(Y.a) AS refcount FROM rtest_view1 X, rtest_view2 Y WHERE X.a = Y.a GROUP BY X.a, X.b; CREATE FUNCTION rtest_viewfunc1 (int4) RETURNS int4 AS 'select count(*)::int4 from rtest_view2 where a = $1' LANGUAGE sql; CREATE VIEW rtest_vview5 AS SELECT a, b, rtest_viewfunc1 (a) AS refcount FROM rtest_view1; INSERT INTO rtest_view1 VALUES (1, 'item 1', 't'); INSERT INTO rtest_view1 VALUES (2, 'item 2', 't'); INSERT INTO rtest_view1 VALUES (3, 'item 3', 't'); INSERT INTO rtest_view1 VALUES (4, 'item 4', 'f'); INSERT INTO rtest_view1 VALUES (5, 'item 5', 't'); INSERT INTO rtest_view1 VALUES (6, 'item 6', 'f'); INSERT INTO rtest_view1 VALUES (7, 'item 7', 't'); INSERT INTO rtest_view1 VALUES (8, 'item 8', 't'); INSERT INTO rtest_view2 VALUES (2); INSERT INTO rtest_view2 VALUES (2); INSERT INTO rtest_view2 VALUES (4); INSERT INTO rtest_view2 VALUES (5); INSERT INTO rtest_view2 VALUES (7); INSERT INTO rtest_view2 VALUES (7); INSERT INTO rtest_view2 VALUES (7); INSERT INTO rtest_view2 VALUES (7); SELECT * FROM rtest_vview1; SELECT * FROM rtest_vview2; SELECT * FROM rtest_vview3; SELECT * FROM rtest_vview4 ORDER BY a, b; SELECT * FROM rtest_vview5; INSERT INTO rtest_view3 SELECT * FROM rtest_vview1 WHERE a < 7; SELECT * FROM rtest_view3; DELETE FROM rtest_view3; INSERT INTO rtest_view3 SELECT * FROM rtest_vview2 WHERE a != 5 AND b !~ '2'; SELECT * FROM rtest_view3; DELETE FROM rtest_view3; INSERT INTO rtest_view3 SELECT * FROM rtest_vview3; SELECT * FROM rtest_view3; DELETE FROM rtest_view3; INSERT INTO rtest_view4 SELECT * FROM rtest_vview4 WHERE 3 > refcount; SELECT * FROM rtest_view4 ORDER BY a, b; DELETE FROM rtest_view4; INSERT INTO rtest_view4 SELECT * FROM rtest_vview5 WHERE a > 2 AND refcount = 0; SELECT * FROM rtest_view4; DELETE FROM rtest_view4; -- -- Test for computations in views -- CREATE TABLE rtest_comp ( part text, unit char(4), size float ); CREATE TABLE rtest_unitfact ( unit char(4), factor float ); CREATE VIEW rtest_vcomp AS SELECT X.part, (X.size * Y.factor) AS size_in_cm FROM rtest_comp X, rtest_unitfact Y WHERE X.unit = Y.unit; INSERT INTO rtest_unitfact VALUES ('m', 100.0); INSERT INTO rtest_unitfact VALUES ('cm', 1.0); INSERT INTO rtest_unitfact VALUES ('inch', 2.54); INSERT INTO rtest_comp VALUES ('p1', 'm', 5.0); INSERT INTO rtest_comp VALUES ('p2', 'm', 3.0); INSERT INTO rtest_comp VALUES ('p3', 'cm', 5.0); INSERT INTO rtest_comp VALUES ('p4', 'cm', 15.0); INSERT INTO rtest_comp VALUES ('p5', 'inch', 7.0); INSERT INTO rtest_comp VALUES ('p6', 'inch', 4.4); SELECT * FROM rtest_vcomp ORDER BY part; SELECT * FROM rtest_vcomp WHERE size_in_cm > 10.0 ORDER BY size_in_cm USING >; -- -- In addition run the (slightly modified) queries from the -- programmers manual section on the rule system. -- CREATE TABLE shoe_data ( shoename char(10), -- primary key sh_avail integer, -- available # of pairs slcolor char(10), -- preferred shoelace color slminlen float, -- minimum shoelace length slmaxlen float, -- maximum shoelace length slunit char(8) -- length unit ); CREATE TABLE shoelace_data ( sl_name char(10), -- primary key sl_avail integer, -- available # of pairs sl_color char(10), -- shoelace color sl_len float, -- shoelace length sl_unit char(8) -- length unit ); CREATE TABLE unit ( un_name char(8), -- the primary key un_fact float -- factor to transform to cm ); CREATE VIEW shoe AS SELECT sh.shoename, sh.sh_avail, sh.slcolor, sh.slminlen, sh.slminlen * un.un_fact AS slminlen_cm, sh.slmaxlen, sh.slmaxlen * un.un_fact AS slmaxlen_cm, sh.slunit FROM shoe_data sh, unit un WHERE sh.slunit = un.un_name; CREATE VIEW shoelace AS SELECT s.sl_name, s.sl_avail, s.sl_color, s.sl_len, s.sl_unit, s.sl_len * u.un_fact AS sl_len_cm FROM shoelace_data s, unit u WHERE s.sl_unit = u.un_name; CREATE VIEW shoe_ready AS SELECT rsh.shoename, rsh.sh_avail, rsl.sl_name, rsl.sl_avail, int4smaller(rsh.sh_avail, rsl.sl_avail) AS total_avail FROM shoe rsh, shoelace rsl WHERE rsl.sl_color = rsh.slcolor AND rsl.sl_len_cm >= rsh.slminlen_cm AND rsl.sl_len_cm <= rsh.slmaxlen_cm; INSERT INTO unit VALUES ('cm', 1.0); INSERT INTO unit VALUES ('m', 100.0); INSERT INTO unit VALUES ('inch', 2.54); INSERT INTO shoe_data VALUES ('sh1', 2, 'black', 70.0, 90.0, 'cm'); INSERT INTO shoe_data VALUES ('sh2', 0, 'black', 30.0, 40.0, 'inch'); INSERT INTO shoe_data VALUES ('sh3', 4, 'brown', 50.0, 65.0, 'cm'); INSERT INTO shoe_data VALUES ('sh4', 3, 'brown', 40.0, 50.0, 'inch'); INSERT INTO shoelace_data VALUES ('sl1', 5, 'black', 80.0, 'cm'); INSERT INTO shoelace_data VALUES ('sl2', 6, 'black', 100.0, 'cm'); INSERT INTO shoelace_data VALUES ('sl3', 0, 'black', 35.0, 'inch'); INSERT INTO shoelace_data VALUES ('sl4', 8, 'black', 40.0, 'inch'); INSERT INTO shoelace_data VALUES ('sl5', 4, 'brown', 1.0, 'm'); INSERT INTO shoelace_data VALUES ('sl6', 0, 'brown', 0.9, 'm'); INSERT INTO shoelace_data VALUES ('sl7', 7, 'brown', 60, 'cm'); INSERT INTO shoelace_data VALUES ('sl8', 1, 'brown', 40, 'inch'); -- SELECTs in doc SELECT * FROM shoelace ORDER BY sl_name; SELECT * FROM shoe_ready WHERE total_avail >= 2 ORDER BY 1; CREATE TABLE shoelace_log ( sl_name char(10), -- shoelace changed sl_avail integer, -- new available value log_who name, -- who did it log_when timestamp -- when ); -- Want "log_who" to be CURRENT_USER, -- but that is non-portable for the regression test -- - thomas 1999-02-21 CREATE RULE log_shoelace AS ON UPDATE TO shoelace_data WHERE NEW.sl_avail != OLD.sl_avail DO INSERT INTO shoelace_log VALUES (NEW.sl_name, NEW.sl_avail, 'Al Bundy', 'epoch'); UPDATE shoelace_data SET sl_avail = 6 WHERE sl_name = 'sl7'; SELECT * FROM shoelace_log; CREATE RULE shoelace_ins AS ON INSERT TO shoelace DO INSTEAD INSERT INTO shoelace_data VALUES (NEW.sl_name, NEW.sl_avail, NEW.sl_color, NEW.sl_len, NEW.sl_unit); CREATE RULE shoelace_upd AS ON UPDATE TO shoelace DO INSTEAD UPDATE shoelace_data SET sl_name = NEW.sl_name, sl_avail = NEW.sl_avail, sl_color = NEW.sl_color, sl_len = NEW.sl_len, sl_unit = NEW.sl_unit WHERE sl_name = OLD.sl_name; CREATE RULE shoelace_del AS ON DELETE TO shoelace DO INSTEAD DELETE FROM shoelace_data WHERE sl_name = OLD.sl_name; CREATE TABLE shoelace_arrive ( arr_name char(10), arr_quant integer ); CREATE TABLE shoelace_ok ( ok_name char(10), ok_quant integer ); CREATE RULE shoelace_ok_ins AS ON INSERT TO shoelace_ok DO INSTEAD UPDATE shoelace SET sl_avail = sl_avail + NEW.ok_quant WHERE sl_name = NEW.ok_name; INSERT INTO shoelace_arrive VALUES ('sl3', 10); INSERT INTO shoelace_arrive VALUES ('sl6', 20); INSERT INTO shoelace_arrive VALUES ('sl8', 20); SELECT * FROM shoelace ORDER BY sl_name; INSERT INTO shoelace_ok SELECT * FROM shoelace_arrive; SELECT * FROM shoelace ORDER BY sl_name; SELECT * FROM shoelace_log ORDER BY sl_name; CREATE VIEW shoelace_obsolete AS SELECT * FROM shoelace WHERE NOT EXISTS ( SELECT shoename FROM shoe WHERE slcolor = sl_color); CREATE VIEW shoelace_candelete AS SELECT * FROM shoelace_obsolete WHERE sl_avail = 0; INSERT INTO shoelace VALUES ('sl9', 0, 'pink', 35.0, 'inch', 0.0); INSERT INTO shoelace VALUES ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0); -- Unsupported (even though a similar updatable view construct is) INSERT INTO shoelace VALUES ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0) ON CONFLICT DO NOTHING; SELECT * FROM shoelace_obsolete ORDER BY sl_len_cm; SELECT * FROM shoelace_candelete; DELETE FROM shoelace WHERE EXISTS ( SELECT * FROM shoelace_candelete WHERE sl_name = shoelace.sl_name); SELECT * FROM shoelace ORDER BY sl_name; SELECT * FROM shoe ORDER BY shoename; SELECT count(*) FROM shoe; -- -- Simple test of qualified ON INSERT ... this did not work in 7.0 ... -- CREATE TABLE rules_foo ( f1 int ); CREATE TABLE rules_foo2 ( f1 int ); CREATE RULE rules_foorule AS ON INSERT TO rules_foo WHERE f1 < 100 DO INSTEAD NOTHING; INSERT INTO rules_foo VALUES (1); INSERT INTO rules_foo VALUES (1001); SELECT * FROM rules_foo; DROP RULE rules_foorule ON rules_foo; -- this should fail because f1 is not exposed for unqualified reference: CREATE RULE rules_foorule AS ON INSERT TO rules_foo WHERE f1 < 100 DO INSTEAD INSERT INTO rules_foo2 VALUES (f1); -- this is the correct way: CREATE RULE rules_foorule AS ON INSERT TO rules_foo WHERE f1 < 100 DO INSTEAD INSERT INTO rules_foo2 VALUES (new.f1); INSERT INTO rules_foo VALUES (2); INSERT INTO rules_foo VALUES (100); SELECT * FROM rules_foo; SELECT * FROM rules_foo2; DROP RULE rules_foorule ON rules_foo; DROP TABLE rules_foo; DROP TABLE rules_foo2; -- -- Test rules containing INSERT ... SELECT, which is a very ugly special -- case as of 7.1. Example is based on bug report from Joel Burton. -- CREATE TABLE pparent ( pid int, txt text ); INSERT INTO pparent VALUES (1, 'parent1'); INSERT INTO pparent VALUES (2, 'parent2'); CREATE TABLE cchild ( pid int, descrip text ); INSERT INTO cchild VALUES (1, 'descrip1'); CREATE VIEW vview AS SELECT pparent.pid, txt, descrip FROM pparent LEFT JOIN cchild USING (pid); CREATE RULE rrule AS ON UPDATE TO vview DO INSTEAD (INSERT INTO cchild (pid, descrip) SELECT old.pid, new.descrip WHERE old.descrip ISNULL; UPDATE cchild SET descrip = new.descrip WHERE cchild.pid = old.pid; ); SELECT * FROM vview; UPDATE vview SET descrip = 'test1' WHERE pid = 1; SELECT * FROM vview; UPDATE vview SET descrip = 'test2' WHERE pid = 2; SELECT * FROM vview; UPDATE vview SET descrip = 'test3' WHERE pid = 3; SELECT * FROM vview; SELECT * FROM cchild; DROP RULE rrule ON vview; DROP VIEW vview; DROP TABLE pparent; DROP TABLE cchild; -- -- Check that ruleutils are working -- -- temporarily disable fancy output, so view changes create less diff noise a \t SELECT viewname, definition FROM pg_views WHERE schemaname IN ('pg_catalog', 'public') ORDER BY viewname; SELECT tablename, rulename, definition FROM pg_rules WHERE schemaname IN ('pg_catalog', 'public') ORDER BY tablename, rulename; -- restore normal output mode a \t -- -- CREATE OR REPLACE RULE -- CREATE TABLE ruletest_tbl ( a int, b int ); CREATE TABLE ruletest_tbl2 ( a int, b int ); CREATE OR REPLACE RULE myrule AS ON INSERT TO ruletest_tbl DO INSTEAD INSERT INTO ruletest_tbl2 VALUES ( 10, 10 ); INSERT INTO ruletest_tbl VALUES (99, 99); CREATE OR REPLACE RULE myrule AS ON INSERT TO ruletest_tbl DO INSTEAD INSERT INTO ruletest_tbl2 VALUES ( 1000, 1000 ); INSERT INTO ruletest_tbl VALUES (99, 99); SELECT * FROM ruletest_tbl2; -- Check that rewrite rules splitting one INSERT into multiple -- conditional statements does not disable FK checking. CREATE TABLE rule_and_refint_t1 ( id1a integer, id1b integer, PRIMARY KEY (id1a, id1b) ); CREATE TABLE rule_and_refint_t2 ( id2a integer, id2c integer, PRIMARY KEY (id2a, id2c) ); CREATE TABLE rule_and_refint_t3 ( id3a integer, id3b integer, id3c integer, data text, PRIMARY KEY (id3a, id3b, id3c), FOREIGN KEY (id3a, id3b) REFERENCES rule_and_refint_t1 (id1a, id1b), FOREIGN KEY (id3a, id3c) REFERENCES rule_and_refint_t2 (id2a, id2c) ); INSERT INTO rule_and_refint_t1 VALUES (1, 11); INSERT INTO rule_and_refint_t1 VALUES (1, 12); INSERT INTO rule_and_refint_t1 VALUES (2, 21); INSERT INTO rule_and_refint_t1 VALUES (2, 22); INSERT INTO rule_and_refint_t2 VALUES (1, 11); INSERT INTO rule_and_refint_t2 VALUES (1, 12); INSERT INTO rule_and_refint_t2 VALUES (2, 21); INSERT INTO rule_and_refint_t2 VALUES (2, 22); INSERT INTO rule_and_refint_t3 VALUES (1, 11, 11, 'row1'); INSERT INTO rule_and_refint_t3 VALUES (1, 11, 12, 'row2'); INSERT INTO rule_and_refint_t3 VALUES (1, 12, 11, 'row3'); INSERT INTO rule_and_refint_t3 VALUES (1, 12, 12, 'row4'); INSERT INTO rule_and_refint_t3 VALUES (1, 11, 13, 'row5'); INSERT INTO rule_and_refint_t3 VALUES (1, 13, 11, 'row6'); -- Ordinary table INSERT INTO rule_and_refint_t3 VALUES (1, 13, 11, 'row6') ON CONFLICT DO NOTHING; -- rule not fired, so fk violation INSERT INTO rule_and_refint_t3 VALUES (1, 13, 11, 'row6') ON CONFLICT (id3a, id3b, id3c) DO UPDATE SET id3b = excluded.id3b; -- rule fired, so unsupported INSERT INTO shoelace VALUES ('sl9', 0, 'pink', 35.0, 'inch', 0.0) ON CONFLICT (sl_name) DO UPDATE SET sl_avail = excluded.sl_avail; CREATE RULE rule_and_refint_t3_ins AS ON INSERT TO rule_and_refint_t3 WHERE (EXISTS ( SELECT 1 FROM rule_and_refint_t3 WHERE (((rule_and_refint_t3.id3a = new.id3a) AND (rule_and_refint_t3.id3b = new.id3b)) AND (rule_and_refint_t3.id3c = new.id3c)))) DO INSTEAD UPDATE rule_and_refint_t3 SET data = new.data WHERE (((rule_and_refint_t3.id3a = new.id3a) AND (rule_and_refint_t3.id3b = new.id3b)) AND (rule_and_refint_t3.id3c = new.id3c)); INSERT INTO rule_and_refint_t3 VALUES (1, 11, 13, 'row7'); INSERT INTO rule_and_refint_t3 VALUES (1, 13, 11, 'row8'); -- -- disallow dropping a view's rule (bug #5072) -- CREATE VIEW rules_fooview AS SELECT 'rules_foo'::text; DROP RULE "_RETURN" ON rules_fooview; DROP VIEW rules_fooview; -- -- test conversion of table to view (needed to load some pg_dump files) -- CREATE TABLE rules_fooview ( x int, y text ); SELECT xmin, * FROM rules_fooview; CREATE RULE "_RETURN" AS ON SELECT TO rules_fooview DO INSTEAD SELECT 1 AS x, 'aaa'::text AS y; SELECT * FROM rules_fooview; SELECT xmin, * FROM rules_fooview; -- fail, views don't have such a column SELECT reltoastrelid, relkind, relfrozenxid FROM pg_class WHERE oid = 'rules_fooview'::regclass; DROP VIEW rules_fooview; -- trying to convert a partitioned table to view is not allowed CREATE TABLE rules_fooview ( x int, y text ) PARTITION BY LIST (x); CREATE RULE "_RETURN" AS ON SELECT TO rules_fooview DO INSTEAD SELECT 1 AS x, 'aaa'::text AS y; -- nor can one convert a partition to view CREATE TABLE rules_fooview_part PARTITION OF rules_fooview FOR VALUES IN (1); CREATE RULE "_RETURN" AS ON SELECT TO rules_fooview_part DO INSTEAD SELECT 1 AS x, 'aaa'::text AS y; -- -- check for planner problems with complex inherited UPDATES -- CREATE TABLE id ( id serial PRIMARY KEY, name text ); -- currently, must respecify PKEY for each inherited subtable CREATE TABLE test_1 ( id integer PRIMARY KEY ) INHERITS ( id ); CREATE TABLE test_2 ( id integer PRIMARY KEY ) INHERITS ( id ); CREATE TABLE test_3 ( id integer PRIMARY KEY ) INHERITS ( id ); INSERT INTO test_1 (name) VALUES ('Test 1'); INSERT INTO test_1 (name) VALUES ('Test 2'); INSERT INTO test_2 (name) VALUES ('Test 3'); INSERT INTO test_2 (name) VALUES ('Test 4'); INSERT INTO test_3 (name) VALUES ('Test 5'); INSERT INTO test_3 (name) VALUES ('Test 6'); CREATE VIEW id_ordered AS SELECT * FROM id ORDER BY id; CREATE RULE update_id_ordered AS ON UPDATE TO id_ordered DO INSTEAD UPDATE id SET name = new.name WHERE id = old.id; SELECT * FROM id_ordered; UPDATE id_ordered SET name = 'update 2' WHERE id = 2; UPDATE id_ordered SET name = 'update 4' WHERE id = 4; UPDATE id_ordered SET name = 'update 5' WHERE id = 5; SELECT * FROM id_ordered; DROP TABLE id CASCADE; -- -- check corner case where an entirely-dummy subplan is created by -- constraint exclusion -- CREATE temp TABLE t1 ( a integer PRIMARY KEY ); CREATE temp TABLE t1_1 ( CHECK (a >= 0 AND a < 10) ) INHERITS ( t1 ); CREATE temp TABLE t1_2 ( CHECK (a >= 10 AND a < 20) ) INHERITS ( t1 ); CREATE RULE t1_ins_1 AS ON INSERT TO t1 WHERE new.a >= 0 AND new.a < 10 DO INSTEAD INSERT INTO t1_1 VALUES (new.a); CREATE RULE t1_ins_2 AS ON INSERT TO t1 WHERE new.a >= 10 AND new.a < 20 DO INSTEAD INSERT INTO t1_2 VALUES (new.a); CREATE RULE t1_upd_1 AS ON UPDATE TO t1 WHERE old.a >= 0 AND old.a < 10 DO INSTEAD UPDATE t1_1 SET a = new.a WHERE a = old.a; CREATE RULE t1_upd_2 AS ON UPDATE TO t1 WHERE old.a >= 10 AND old.a < 20 DO INSTEAD UPDATE t1_2 SET a = new.a WHERE a = old.a; SET constraint_exclusion = ON; INSERT INTO t1 SELECT * FROM generate_series(5, 19, 1) g; UPDATE t1 SET a = 4 WHERE a = 5; SELECT * FROM ONLY t1; SELECT * FROM ONLY t1_1; SELECT * FROM ONLY t1_2; RESET constraint_exclusion; -- test various flavors of pg_get_viewdef() SELECT pg_get_viewdef('shoe'::regclass) AS unpretty; SELECT pg_get_viewdef('shoe'::regclass, TRUE) AS pretty; SELECT pg_get_viewdef('shoe'::regclass, 0) AS prettier; -- -- check multi-row VALUES in rules -- CREATE TABLE rules_src ( f1 int, f2 int ); CREATE TABLE rules_log ( f1 int, f2 int, tag text ); INSERT INTO rules_src VALUES (1, 2), (11, 12); CREATE RULE r1 AS ON UPDATE TO rules_src DO also INSERT INTO rules_log VALUES (old.*, 'old'), (new.*, 'new'); UPDATE rules_src SET f2 = f2 + 1; UPDATE rules_src SET f2 = f2 * 10; SELECT * FROM rules_src; SELECT * FROM rules_log; CREATE RULE r2 AS ON UPDATE TO rules_src DO also VALUES (old.*, 'old'), (new.*, 'new'); UPDATE rules_src SET f2 = f2 / 10; SELECT * FROM rules_src; SELECT * FROM rules_log; CREATE RULE r3 AS ON DELETE TO rules_src DO NOTIFY rules_src_deletion; \d+ rules_src -- -- Ensure an aliased target relation for insert is correctly deparsed. -- CREATE RULE r4 AS ON INSERT TO rules_src DO INSTEAD INSERT INTO rules_log AS trgt SELECT NEW.* RETURNING trgt.f1, trgt.f2; CREATE RULE r5 AS ON UPDATE TO rules_src DO INSTEAD UPDATE rules_log AS trgt SET tag = 'updated' WHERE trgt.f1 = new.f1; \d+ rules_src -- -- check alter rename rule -- CREATE TABLE rule_t1 ( a int ); CREATE VIEW rule_v1 AS SELECT * FROM rule_t1; CREATE RULE InsertRule AS ON INSERT TO rule_v1 DO INSTEAD INSERT INTO rule_t1 VALUES (new.a); ALTER RULE InsertRule ON rule_v1 RENAME TO NewInsertRule; INSERT INTO rule_v1 VALUES (1); SELECT * FROM rule_v1; \d+ rule_v1 -- -- error conditions for alter rename rule -- ALTER RULE InsertRule ON rule_v1 RENAME TO NewInsertRule; -- doesn't exist ALTER RULE NewInsertRule ON rule_v1 RENAME TO "_RETURN"; -- already exists ALTER RULE "_RETURN" ON rule_v1 RENAME TO abc; -- ON SELECT rule cannot be renamed DROP VIEW rule_v1; DROP TABLE rule_t1; -- -- check display of VALUES in view definitions -- CREATE VIEW rule_v1 AS VALUES (1, 2); \d+ rule_v1 DROP VIEW rule_v1; CREATE VIEW rule_v1 (x) AS VALUES (1, 2); \d+ rule_v1 DROP VIEW rule_v1; CREATE VIEW rule_v1 (x) AS SELECT * FROM ( VALUES (1, 2)) v; \d+ rule_v1 DROP VIEW rule_v1; CREATE VIEW rule_v1 (x) AS SELECT * FROM ( VALUES (1, 2)) v (q, w); \d+ rule_v1 DROP VIEW rule_v1; -- -- Check DO INSTEAD rules with ON CONFLICT -- CREATE TABLE hats ( hat_name char(10) PRIMARY KEY, hat_color char(10) -- hat color ); CREATE TABLE hat_data ( hat_name char(10), hat_color char(10) -- hat color ); CREATE UNIQUE INDEX hat_data_unique_idx ON hat_data (hat_name COLLATE "C" bpchar_pattern_ops); -- DO NOTHING with ON CONFLICT CREATE RULE hat_nosert AS ON INSERT TO hats DO INSTEAD INSERT INTO hat_data VALUES (NEW.hat_name, NEW.hat_color) ON CONFLICT (hat_name COLLATE "C" bpchar_pattern_ops) WHERE hat_color = 'green' DO NOTHING RETURNING *; SELECT definition FROM pg_rules WHERE tablename = 'hats' ORDER BY rulename; -- Works (projects row) INSERT INTO hats VALUES ('h7', 'black') RETURNING *; -- Works (does nothing) INSERT INTO hats VALUES ('h7', 'black') RETURNING *; SELECT tablename, rulename, definition FROM pg_rules WHERE tablename = 'hats'; DROP RULE hat_nosert ON hats; -- DO NOTHING without ON CONFLICT CREATE RULE hat_nosert_all AS ON INSERT TO hats DO INSTEAD INSERT INTO hat_data VALUES (NEW.hat_name, NEW.hat_color) ON CONFLICT DO NOTHING RETURNING *; SELECT definition FROM pg_rules WHERE tablename = 'hats' ORDER BY rulename; DROP RULE hat_nosert_all ON hats; -- Works (does nothing) INSERT INTO hats VALUES ('h7', 'black') RETURNING *; -- DO UPDATE with a WHERE clause CREATE RULE hat_upsert AS ON INSERT TO hats DO INSTEAD INSERT INTO hat_data VALUES (NEW.hat_name, NEW.hat_color) ON CONFLICT (hat_name) DO UPDATE SET hat_name = hat_data.hat_name, hat_color = excluded.hat_color WHERE excluded.hat_color <> 'forbidden' AND hat_data.* != excluded.* RETURNING *; SELECT definition FROM pg_rules WHERE tablename = 'hats' ORDER BY rulename; -- Works (does upsert) INSERT INTO hats VALUES ('h8', 'black') RETURNING *; SELECT * FROM hat_data WHERE hat_name = 'h8'; INSERT INTO hats VALUES ('h8', 'white') RETURNING *; SELECT * FROM hat_data WHERE hat_name = 'h8'; INSERT INTO hats VALUES ('h8', 'forbidden') RETURNING *; SELECT * FROM hat_data WHERE hat_name = 'h8'; SELECT tablename, rulename, definition FROM pg_rules WHERE tablename = 'hats'; -- ensure explain works for on insert conflict rules EXPLAIN ( COSTS OFF ) INSERT INTO hats VALUES ('h8', 'forbidden') RETURNING *; -- ensure upserting into a rule, with a CTE (different offsets!) works WITH data ( hat_name, hat_color ) AS MATERIALIZED ( VALUES ( 'h8', 'green' ), ( 'h9', 'blue' ), ( 'h7', 'forbidden' )) INSERT INTO hats SELECT * FROM data RETURNING *; EXPLAIN ( COSTS OFF ) WITH data (hat_name, hat_color) AS MATERIALIZED ( VALUES ('h8', 'green'), ('h9', 'blue'), ('h7', 'forbidden')) INSERT INTO hats SELECT * FROM data RETURNING *; SELECT * FROM hat_data WHERE hat_name IN ('h8', 'h9', 'h7') ORDER BY hat_name; DROP RULE hat_upsert ON hats; DROP TABLE hats; DROP TABLE hat_data; -- test for pg_get_functiondef properly regurgitating SET parameters -- Note that the function is kept around to stress pg_dump. CREATE FUNCTION func_with_set_params () RETURNS integer AS 'select 1;' LANGUAGE SQL SET search_path TO PG_CATALOG SET extra_float_digits TO 2 SET work_mem TO '4MB' SET datestyle TO iso, mdy SET local_preload_libraries TO "Mixed/Case", 'c:/''a"/path', '', '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' IMMUTABLE STRICT; SELECT pg_get_functiondef('func_with_set_params()'::regprocedure); -- tests for pg_get_*def with invalid objects SELECT pg_get_constraintdef(0); SELECT pg_get_functiondef(0); SELECT pg_get_indexdef(0); SELECT pg_get_ruledef(0); SELECT pg_get_statisticsobjdef (0); SELECT pg_get_triggerdef(0); SELECT pg_get_viewdef(0); SELECT pg_get_function_arguments(0); SELECT pg_get_function_identity_arguments(0); SELECT pg_get_function_result(0); SELECT pg_get_function_arg_default (0, 0); SELECT pg_get_function_arg_default ('pg_class'::regclass, 0); SELECT pg_get_partkeydef (0); -- test rename for a rule defined on a partitioned table CREATE TABLE rules_parted_table ( a int ) PARTITION BY LIST (a); CREATE TABLE rules_parted_table_1 PARTITION OF rules_parted_table FOR VALUES IN (1); CREATE RULE rules_parted_table_insert AS ON INSERT TO rules_parted_table DO INSTEAD INSERT INTO rules_parted_table_1 VALUES (NEW.*); ALTER RULE rules_parted_table_insert ON rules_parted_table RENAME TO rules_parted_table_insert_redirect; DROP TABLE rules_parted_table; -- -- Test enabling/disabling -- CREATE TABLE ruletest1 ( a int ); CREATE TABLE ruletest2 ( b int ); CREATE RULE rule1 AS ON INSERT TO ruletest1 DO INSTEAD INSERT INTO ruletest2 VALUES (NEW.*); INSERT INTO ruletest1 VALUES (1); ALTER TABLE ruletest1 DISABLE RULE rule1; INSERT INTO ruletest1 VALUES (2); ALTER TABLE ruletest1 ENABLE RULE rule1; SET session_replication_role = REPLICA; INSERT INTO ruletest1 VALUES (3); ALTER TABLE ruletest1 ENABLE REPLICA RULE rule1; INSERT INTO ruletest1 VALUES (4); RESET session_replication_role; INSERT INTO ruletest1 VALUES (5); SELECT * FROM ruletest1; SELECT * FROM ruletest2; DROP TABLE ruletest1; DROP TABLE ruletest2; pgFormatter-4.2/t/pg-test-files/expected/sanity_check.sql000066400000000000000000000030631361326045100235700ustar00rootroot00000000000000VACUUM; -- -- sanity check, if we don't have indices the test will take years to -- complete. But skip TOAST relations (since they will have varying -- names depending on the current OID counter) as well as temp tables -- of other backends (to avoid timing-dependent behavior). -- -- temporarily disable fancy output, so catalog changes create less diff noise a \t SELECT relname, relhasindex FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = relnamespace WHERE relkind IN ('r', 'p') AND (nspname ~ '^pg_temp_') IS NOT TRUE ORDER BY relname; -- restore normal output mode a \t -- -- another sanity check: every system catalog that has OIDs should have -- a unique index on OID. This ensures that the OIDs will be unique, -- even after the OID counter wraps around. -- We exclude non-system tables from the check by looking at nspname. -- SELECT relname, nspname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = relnamespace JOIN pg_attribute a ON (attrelid = c.oid AND attname = 'oid') WHERE relkind = 'r' AND c.oid < 16384 AND ((nspname ~ '^pg_') IS NOT FALSE) AND NOT EXISTS ( SELECT 1 FROM pg_index i WHERE indrelid = c.oid AND indkey[0] = a.attnum AND indnatts = 1 AND indisunique AND indimmediate); -- check that relations without storage don't have relfilenode SELECT relname, relkind FROM pg_class WHERE relkind IN ('v', 'c', 'f', 'p', 'I') AND relfilenode <> 0; pgFormatter-4.2/t/pg-test-files/expected/security_label.sql000066400000000000000000000031531361326045100241320ustar00rootroot00000000000000-- -- Test for facilities of security label -- -- initial setups SET client_min_messages TO 'warning'; DROP ROLE IF EXISTS regress_seclabel_user1; DROP ROLE IF EXISTS regress_seclabel_user2; RESET client_min_messages; CREATE USER regress_seclabel_user1 WITH CREATEROLE; CREATE USER regress_seclabel_user2; CREATE TABLE seclabel_tbl1 ( a int, b text ); CREATE TABLE seclabel_tbl2 ( x int, y text ); CREATE VIEW seclabel_view1 AS SELECT * FROM seclabel_tbl2; CREATE FUNCTION seclabel_four () RETURNS integer AS $$ SELECT 4$$ LANGUAGE sql; CREATE DOMAIN seclabel_domain AS text; ALTER TABLE seclabel_tbl1 OWNER TO regress_seclabel_user1; ALTER TABLE seclabel_tbl2 OWNER TO regress_seclabel_user2; -- -- Test of SECURITY LABEL statement without a plugin -- SECURITY LABEL ON TABLE seclabel_tbl1 IS 'classified'; -- fail SECURITY LABEL FOR 'dummy' ON TABLE seclabel_tbl1 IS 'classified'; -- fail SECURITY LABEL ON TABLE seclabel_tbl1 IS '...invalid label...'; -- fail SECURITY LABEL ON TABLE seclabel_tbl3 IS 'unclassified'; -- fail SECURITY LABEL ON ROLE regress_seclabel_user1 IS 'classified'; -- fail SECURITY LABEL FOR 'dummy' ON ROLE regress_seclabel_user1 IS 'classified'; -- fail SECURITY LABEL ON ROLE regress_seclabel_user1 IS '...invalid label...'; -- fail SECURITY LABEL ON ROLE regress_seclabel_user3 IS 'unclassified'; -- fail -- clean up objects DROP FUNCTION seclabel_four (); DROP DOMAIN seclabel_domain; DROP VIEW seclabel_view1; DROP TABLE seclabel_tbl1; DROP TABLE seclabel_tbl2; DROP USER regress_seclabel_user1; DROP USER regress_seclabel_user2; pgFormatter-4.2/t/pg-test-files/expected/select.sql000066400000000000000000000224331361326045100224050ustar00rootroot00000000000000-- -- SELECT -- -- btree index -- awk '{if($1<10){print;}else{next;}}' onek.data | sort +0n -1 -- SELECT * FROM onek WHERE onek.unique1 < 10 ORDER BY onek.unique1; -- -- awk '{if($1<20){print $1,$14;}else{next;}}' onek.data | sort +0nr -1 -- SELECT onek.unique1, onek.stringu1 FROM onek WHERE onek.unique1 < 20 ORDER BY unique1 USING >; -- -- awk '{if($1>980){print $1,$14;}else{next;}}' onek.data | sort +1d -2 -- SELECT onek.unique1, onek.stringu1 FROM onek WHERE onek.unique1 > 980 ORDER BY stringu1 USING <; -- -- awk '{if($1>980){print $1,$16;}else{next;}}' onek.data | -- sort +1d -2 +0nr -1 -- SELECT onek.unique1, onek.string4 FROM onek WHERE onek.unique1 > 980 ORDER BY string4 USING <, unique1 USING >; -- -- awk '{if($1>980){print $1,$16;}else{next;}}' onek.data | -- sort +1dr -2 +0n -1 -- SELECT onek.unique1, onek.string4 FROM onek WHERE onek.unique1 > 980 ORDER BY string4 USING >, unique1 USING <; -- -- awk '{if($1<20){print $1,$16;}else{next;}}' onek.data | -- sort +0nr -1 +1d -2 -- SELECT onek.unique1, onek.string4 FROM onek WHERE onek.unique1 < 20 ORDER BY unique1 USING >, string4 USING <; -- -- awk '{if($1<20){print $1,$16;}else{next;}}' onek.data | -- sort +0n -1 +1dr -2 -- SELECT onek.unique1, onek.string4 FROM onek WHERE onek.unique1 < 20 ORDER BY unique1 USING <, string4 USING >; -- -- test partial btree indexes -- -- As of 7.2, planner probably won't pick an indexscan without stats, -- so ANALYZE first. Also, we want to prevent it from picking a bitmapscan -- followed by sort, because that could hide index ordering problems. -- ANALYZE onek2; SET enable_seqscan TO OFF; SET enable_bitmapscan TO OFF; SET enable_sort TO OFF; -- -- awk '{if($1<10){print $0;}else{next;}}' onek.data | sort +0n -1 -- SELECT onek2.* FROM onek2 WHERE onek2.unique1 < 10; -- -- awk '{if($1<20){print $1,$14;}else{next;}}' onek.data | sort +0nr -1 -- SELECT onek2.unique1, onek2.stringu1 FROM onek2 WHERE onek2.unique1 < 20 ORDER BY unique1 USING >; -- -- awk '{if($1>980){print $1,$14;}else{next;}}' onek.data | sort +1d -2 -- SELECT onek2.unique1, onek2.stringu1 FROM onek2 WHERE onek2.unique1 > 980; RESET enable_seqscan; RESET enable_bitmapscan; RESET enable_sort; SELECT two, stringu1, ten, string4 INTO TABLE tmp FROM onek; -- -- awk '{print $1,$2;}' person.data | -- awk '{if(NF!=2){print $3,$2;}else{print;}}' - emp.data | -- awk '{if(NF!=2){print $3,$2;}else{print;}}' - student.data | -- awk 'BEGIN{FS=" ";}{if(NF!=2){print $4,$5;}else{print;}}' - stud_emp.data -- -- SELECT name, age FROM person*; ??? check if different SELECT p.name, p.age FROM person * p; -- -- awk '{print $1,$2;}' person.data | -- awk '{if(NF!=2){print $3,$2;}else{print;}}' - emp.data | -- awk '{if(NF!=2){print $3,$2;}else{print;}}' - student.data | -- awk 'BEGIN{FS=" ";}{if(NF!=1){print $4,$5;}else{print;}}' - stud_emp.data | -- sort +1nr -2 -- SELECT p.name, p.age FROM person * p ORDER BY age USING >, name; -- -- Test some cases involving whole-row Var referencing a subquery -- SELECT foo FROM ( SELECT 1 offset 0) AS foo; SELECT foo FROM ( SELECT NULL offset 0) AS foo; SELECT foo FROM ( SELECT 'xyzzy', 1, NULL offset 0) AS foo; -- -- Test VALUES lists -- SELECT * FROM onek, ( VALUES (147, 'RFAAAA'), (931, 'VJAAAA')) AS v (i, j) WHERE onek.unique1 = v.i AND onek.stringu1 = v.j; -- a more complex case -- looks like we're coding lisp :-) SELECT * FROM onek, ( VALUES (( SELECT i FROM ( VALUES (10000), (2), (389), (1000), (2000), (( SELECT 10029))) AS foo (i) ORDER BY i ASC LIMIT 1))) bar (i) WHERE onek.unique1 = bar.i; -- try VALUES in a subquery SELECT * FROM onek WHERE (unique1, ten) IN ( VALUES (1, 1), (20, 0), (99, 9), (17, 99)) ORDER BY unique1; -- VALUES is also legal as a standalone query or a set-operation member VALUES (1, 2), (3, 4 + 4), (7, 77.7); VALUES (1, 2), (3, 4 + 4), (7, 77.7) UNION ALL SELECT 2 + 2, 57 UNION ALL TABLE int8_tbl; -- -- Test ORDER BY options -- CREATE TEMP TABLE foo ( f1 int ); INSERT INTO foo VALUES (42), (3), (10), (7), (NULL), (NULL), (1); SELECT * FROM foo ORDER BY f1; SELECT * FROM foo ORDER BY f1 ASC; -- same thing SELECT * FROM foo ORDER BY f1 NULLS FIRST; SELECT * FROM foo ORDER BY f1 DESC; SELECT * FROM foo ORDER BY f1 DESC NULLS LAST; -- check if indexscans do the right things CREATE INDEX fooi ON foo (f1); SET enable_sort = FALSE; SELECT * FROM foo ORDER BY f1; SELECT * FROM foo ORDER BY f1 NULLS FIRST; SELECT * FROM foo ORDER BY f1 DESC; SELECT * FROM foo ORDER BY f1 DESC NULLS LAST; DROP INDEX fooi; CREATE INDEX fooi ON foo (f1 DESC); SELECT * FROM foo ORDER BY f1; SELECT * FROM foo ORDER BY f1 NULLS FIRST; SELECT * FROM foo ORDER BY f1 DESC; SELECT * FROM foo ORDER BY f1 DESC NULLS LAST; DROP INDEX fooi; CREATE INDEX fooi ON foo (f1 DESC NULLS LAST); SELECT * FROM foo ORDER BY f1; SELECT * FROM foo ORDER BY f1 NULLS FIRST; SELECT * FROM foo ORDER BY f1 DESC; SELECT * FROM foo ORDER BY f1 DESC NULLS LAST; -- -- Test planning of some cases with partial indexes -- -- partial index is usable EXPLAIN ( COSTS OFF ) SELECT * FROM onek2 WHERE unique2 = 11 AND stringu1 = 'ATAAAA'; SELECT * FROM onek2 WHERE unique2 = 11 AND stringu1 = 'ATAAAA'; -- actually run the query with an analyze to use the partial index EXPLAIN ( COSTS OFF, ANALYZE ON, timing OFF, summary OFF ) SELECT * FROM onek2 WHERE unique2 = 11 AND stringu1 = 'ATAAAA'; EXPLAIN ( COSTS OFF ) SELECT unique2 FROM onek2 WHERE unique2 = 11 AND stringu1 = 'ATAAAA'; SELECT unique2 FROM onek2 WHERE unique2 = 11 AND stringu1 = 'ATAAAA'; -- partial index predicate implies clause, so no need for retest EXPLAIN ( COSTS OFF ) SELECT * FROM onek2 WHERE unique2 = 11 AND stringu1 < 'B'; SELECT * FROM onek2 WHERE unique2 = 11 AND stringu1 < 'B'; EXPLAIN ( COSTS OFF ) SELECT unique2 FROM onek2 WHERE unique2 = 11 AND stringu1 < 'B'; SELECT unique2 FROM onek2 WHERE unique2 = 11 AND stringu1 < 'B'; -- but if it's an update target, must retest anyway EXPLAIN ( COSTS OFF ) SELECT unique2 FROM onek2 WHERE unique2 = 11 AND stringu1 < 'B' FOR UPDATE; SELECT unique2 FROM onek2 WHERE unique2 = 11 AND stringu1 < 'B' FOR UPDATE; -- partial index is not applicable EXPLAIN ( COSTS OFF ) SELECT unique2 FROM onek2 WHERE unique2 = 11 AND stringu1 < 'C'; SELECT unique2 FROM onek2 WHERE unique2 = 11 AND stringu1 < 'C'; -- partial index implies clause, but bitmap scan must recheck predicate anyway SET enable_indexscan TO OFF; EXPLAIN ( COSTS OFF ) SELECT unique2 FROM onek2 WHERE unique2 = 11 AND stringu1 < 'B'; SELECT unique2 FROM onek2 WHERE unique2 = 11 AND stringu1 < 'B'; RESET enable_indexscan; -- check multi-index cases too EXPLAIN ( COSTS OFF ) SELECT unique1, unique2 FROM onek2 WHERE (unique2 = 11 OR unique1 = 0) AND stringu1 < 'B'; SELECT unique1, unique2 FROM onek2 WHERE (unique2 = 11 OR unique1 = 0) AND stringu1 < 'B'; EXPLAIN ( COSTS OFF ) SELECT unique1, unique2 FROM onek2 WHERE (unique2 = 11 AND stringu1 < 'B') OR unique1 = 0; SELECT unique1, unique2 FROM onek2 WHERE (unique2 = 11 AND stringu1 < 'B') OR unique1 = 0; -- -- Test some corner cases that have been known to confuse the planner -- -- ORDER BY on a constant doesn't really need any sorting SELECT 1 AS x ORDER BY x; -- But ORDER BY on a set-valued expression does CREATE FUNCTION sillysrf (int) RETURNS SETOF int AS 'values (1),(10),(2),($1)' LANGUAGE sql IMMUTABLE; SELECT sillysrf (42); SELECT sillysrf (- 1) ORDER BY 1; DROP FUNCTION sillysrf (int); -- X = X isn't a no-op, it's effectively X IS NOT NULL assuming = is strict -- (see bug #5084) SELECT * FROM ( VALUES (2), (NULL), (1)) v (k) WHERE k = k ORDER BY k; SELECT * FROM ( VALUES (2), (NULL), (1)) v (k) WHERE k = k; -- Test partitioned tables with no partitions, which should be handled the -- same as the non-inheritance case when expanding its RTE. CREATE TABLE list_parted_tbl ( a int, b int ) PARTITION BY LIST (a); CREATE TABLE list_parted_tbl1 PARTITION OF list_parted_tbl FOR VALUES IN (1) PARTITION BY LIST (b); EXPLAIN ( COSTS OFF ) SELECT * FROM list_parted_tbl; DROP TABLE list_parted_tbl; pgFormatter-4.2/t/pg-test-files/expected/select_distinct.sql000066400000000000000000000043701361326045100243060ustar00rootroot00000000000000-- -- SELECT_DISTINCT -- -- -- awk '{print $3;}' onek.data | sort -n | uniq -- SELECT DISTINCT two FROM tmp ORDER BY 1; -- -- awk '{print $5;}' onek.data | sort -n | uniq -- SELECT DISTINCT ten FROM tmp ORDER BY 1; -- -- awk '{print $16;}' onek.data | sort -d | uniq -- SELECT DISTINCT string4 FROM tmp ORDER BY 1; -- -- awk '{print $3,$16,$5;}' onek.data | sort -d | uniq | -- sort +0n -1 +1d -2 +2n -3 -- SELECT DISTINCT two, string4, ten FROM tmp ORDER BY two USING <, string4 USING <, ten USING <; -- -- awk '{print $2;}' person.data | -- awk '{if(NF!=1){print $2;}else{print;}}' - emp.data | -- awk '{if(NF!=1){print $2;}else{print;}}' - student.data | -- awk 'BEGIN{FS=" ";}{if(NF!=1){print $5;}else{print;}}' - stud_emp.data | -- sort -n -r | uniq -- SELECT DISTINCT p.age FROM person * p ORDER BY age USING >; -- -- Check mentioning same column more than once -- EXPLAIN ( VERBOSE, COSTS OFF ) SELECT count(*) FROM ( SELECT DISTINCT two, four, two FROM tenk1) ss; SELECT count(*) FROM ( SELECT DISTINCT two, four, two FROM tenk1) ss; -- -- Also, some tests of IS DISTINCT FROM, which doesn't quite deserve its -- very own regression file. -- CREATE TEMP TABLE disttable ( f1 integer ); INSERT INTO DISTTABLE VALUES (1); INSERT INTO DISTTABLE VALUES (2); INSERT INTO DISTTABLE VALUES (3); INSERT INTO DISTTABLE VALUES (NULL); -- basic cases SELECT f1, f1 IS DISTINCT FROM 2 AS "not 2" FROM disttable; SELECT f1, f1 IS DISTINCT FROM NULL AS "not null" FROM disttable; SELECT f1, f1 IS DISTINCT FROM f1 AS "false" FROM disttable; SELECT f1, f1 IS DISTINCT FROM f1 + 1 AS "not null" FROM disttable; -- check that optimizer constant-folds it properly SELECT 1 IS DISTINCT FROM 2 AS "yes"; SELECT 2 IS DISTINCT FROM 2 AS "no"; SELECT 2 IS DISTINCT FROM NULL AS "yes"; SELECT NULL IS DISTINCT FROM NULL AS "no"; -- negated form SELECT 1 IS NOT DISTINCT FROM 2 AS "no"; SELECT 2 IS NOT DISTINCT FROM 2 AS "yes"; SELECT 2 IS NOT DISTINCT FROM NULL AS "no"; SELECT NULL IS NOT DISTINCT FROM NULL AS "yes"; pgFormatter-4.2/t/pg-test-files/expected/select_distinct_on.sql000066400000000000000000000012101361326045100247700ustar00rootroot00000000000000-- -- SELECT_DISTINCT_ON -- SELECT DISTINCT ON (string4) string4, two, ten FROM tmp ORDER BY string4 USING <, two USING >, ten USING <; -- this will fail due to conflict of ordering requirements SELECT DISTINCT ON (string4, ten) string4, two, ten FROM tmp ORDER BY string4 USING <, two USING <, ten USING <; SELECT DISTINCT ON (string4, ten) string4, ten, two FROM tmp ORDER BY string4 USING <, ten USING >, two USING <; -- bug #5049: early 8.4.x chokes on volatile DISTINCT ON clauses SELECT DISTINCT ON (1) floor(random()) AS r, f1 FROM int4_tbl ORDER BY 1, 2; pgFormatter-4.2/t/pg-test-files/expected/select_having.sql000066400000000000000000000037611361326045100237440ustar00rootroot00000000000000-- -- SELECT_HAVING -- -- load test data CREATE TABLE test_having ( a int, b int, c char(8), d char ); INSERT INTO test_having VALUES (0, 1, 'XXXX', 'A'); INSERT INTO test_having VALUES (1, 2, 'AAAA', 'b'); INSERT INTO test_having VALUES (2, 2, 'AAAA', 'c'); INSERT INTO test_having VALUES (3, 3, 'BBBB', 'D'); INSERT INTO test_having VALUES (4, 3, 'BBBB', 'e'); INSERT INTO test_having VALUES (5, 3, 'bbbb', 'F'); INSERT INTO test_having VALUES (6, 4, 'cccc', 'g'); INSERT INTO test_having VALUES (7, 4, 'cccc', 'h'); INSERT INTO test_having VALUES (8, 4, 'CCCC', 'I'); INSERT INTO test_having VALUES (9, 4, 'CCCC', 'j'); SELECT b, c FROM test_having GROUP BY b, c HAVING count(*) = 1 ORDER BY b, c; -- HAVING is effectively equivalent to WHERE in this case SELECT b, c FROM test_having GROUP BY b, c HAVING b = 3 ORDER BY b, c; SELECT lower(c), count(c) FROM test_having GROUP BY lower(c) HAVING count(*) > 2 OR min(a) = max(a) ORDER BY lower(c); SELECT c, max(a) FROM test_having GROUP BY c HAVING count(*) > 2 OR min(a) = max(a) ORDER BY c; -- test degenerate cases involving HAVING without GROUP BY -- Per SQL spec, these should generate 0 or 1 row, even without aggregates SELECT min(a), max(a) FROM test_having HAVING min(a) = max(a); SELECT min(a), max(a) FROM test_having HAVING min(a) < max(a); -- errors: ungrouped column references SELECT a FROM test_having HAVING min(a) < max(a); SELECT 1 AS one FROM test_having HAVING a > 1; -- the really degenerate case: need not scan table at all SELECT 1 AS one FROM test_having HAVING 1 > 2; SELECT 1 AS one FROM test_having HAVING 1 < 2; -- and just to prove that we aren't scanning the table: SELECT 1 AS one FROM test_having WHERE 1 / a = 1 HAVING 1 < 2; DROP TABLE test_having; pgFormatter-4.2/t/pg-test-files/expected/select_implicit.sql000066400000000000000000000137641361326045100243060ustar00rootroot00000000000000-- -- SELECT_IMPLICIT -- Test cases for queries with ordering terms missing from the target list. -- This used to be called "junkfilter.sql". -- The parser uses the term "resjunk" to handle these cases. -- - thomas 1998-07-09 -- -- load test data CREATE TABLE test_missing_target ( a int, b int, c char(8), d char ); INSERT INTO test_missing_target VALUES (0, 1, 'XXXX', 'A'); INSERT INTO test_missing_target VALUES (1, 2, 'ABAB', 'b'); INSERT INTO test_missing_target VALUES (2, 2, 'ABAB', 'c'); INSERT INTO test_missing_target VALUES (3, 3, 'BBBB', 'D'); INSERT INTO test_missing_target VALUES (4, 3, 'BBBB', 'e'); INSERT INTO test_missing_target VALUES (5, 3, 'bbbb', 'F'); INSERT INTO test_missing_target VALUES (6, 4, 'cccc', 'g'); INSERT INTO test_missing_target VALUES (7, 4, 'cccc', 'h'); INSERT INTO test_missing_target VALUES (8, 4, 'CCCC', 'I'); INSERT INTO test_missing_target VALUES (9, 4, 'CCCC', 'j'); -- w/ existing GROUP BY target SELECT c, count(*) FROM test_missing_target GROUP BY test_missing_target.c ORDER BY c; -- w/o existing GROUP BY target using a relation name in GROUP BY clause SELECT count(*) FROM test_missing_target GROUP BY test_missing_target.c ORDER BY c; -- w/o existing GROUP BY target and w/o existing a different ORDER BY target -- failure expected SELECT count(*) FROM test_missing_target GROUP BY a ORDER BY b; -- w/o existing GROUP BY target and w/o existing same ORDER BY target SELECT count(*) FROM test_missing_target GROUP BY b ORDER BY b; -- w/ existing GROUP BY target using a relation name in target SELECT test_missing_target.b, count(*) FROM test_missing_target GROUP BY b ORDER BY b; -- w/o existing GROUP BY target SELECT c FROM test_missing_target ORDER BY a; -- w/o existing ORDER BY target SELECT count(*) FROM test_missing_target GROUP BY b ORDER BY b DESC; -- group using reference number SELECT count(*) FROM test_missing_target ORDER BY 1 DESC; -- order using reference number SELECT c, count(*) FROM test_missing_target GROUP BY 1 ORDER BY 1; -- group using reference number out of range -- failure expected SELECT c, count(*) FROM test_missing_target GROUP BY 3; -- group w/o existing GROUP BY and ORDER BY target under ambiguous condition -- failure expected SELECT count(*) FROM test_missing_target x, test_missing_target y WHERE x.a = y.a GROUP BY b ORDER BY b; -- order w/ target under ambiguous condition -- failure NOT expected SELECT a, a FROM test_missing_target ORDER BY a; -- order expression w/ target under ambiguous condition -- failure NOT expected SELECT a / 2, a / 2 FROM test_missing_target ORDER BY a / 2; -- group expression w/ target under ambiguous condition -- failure NOT expected SELECT a / 2, a / 2 FROM test_missing_target GROUP BY a / 2 ORDER BY a / 2; -- group w/ existing GROUP BY target under ambiguous condition SELECT x.b, count(*) FROM test_missing_target x, test_missing_target y WHERE x.a = y.a GROUP BY x.b ORDER BY x.b; -- group w/o existing GROUP BY target under ambiguous condition SELECT count(*) FROM test_missing_target x, test_missing_target y WHERE x.a = y.a GROUP BY x.b ORDER BY x.b; -- group w/o existing GROUP BY target under ambiguous condition -- into a table SELECT count(*) INTO TABLE test_missing_target2 FROM test_missing_target x, test_missing_target y WHERE x.a = y.a GROUP BY x.b ORDER BY x.b; SELECT * FROM test_missing_target2; -- Functions and expressions -- w/ existing GROUP BY target SELECT a % 2, count(b) FROM test_missing_target GROUP BY test_missing_target.a % 2 ORDER BY test_missing_target.a % 2; -- w/o existing GROUP BY target using a relation name in GROUP BY clause SELECT count(c) FROM test_missing_target GROUP BY lower(test_missing_target.c) ORDER BY lower(test_missing_target.c); -- w/o existing GROUP BY target and w/o existing a different ORDER BY target -- failure expected SELECT count(a) FROM test_missing_target GROUP BY a ORDER BY b; -- w/o existing GROUP BY target and w/o existing same ORDER BY target SELECT count(b) FROM test_missing_target GROUP BY b / 2 ORDER BY b / 2; -- w/ existing GROUP BY target using a relation name in target SELECT lower(test_missing_target.c), count(c) FROM test_missing_target GROUP BY lower(c) ORDER BY lower(c); -- w/o existing GROUP BY target SELECT a FROM test_missing_target ORDER BY upper(d); -- w/o existing ORDER BY target SELECT count(b) FROM test_missing_target GROUP BY (b + 1) / 2 ORDER BY (b + 1) / 2 DESC; -- group w/o existing GROUP BY and ORDER BY target under ambiguous condition -- failure expected SELECT count(x.a) FROM test_missing_target x, test_missing_target y WHERE x.a = y.a GROUP BY b / 2 ORDER BY b / 2; -- group w/ existing GROUP BY target under ambiguous condition SELECT x.b / 2, count(x.b) FROM test_missing_target x, test_missing_target y WHERE x.a = y.a GROUP BY x.b / 2 ORDER BY x.b / 2; -- group w/o existing GROUP BY target under ambiguous condition -- failure expected due to ambiguous b in count(b) SELECT count(b) FROM test_missing_target x, test_missing_target y WHERE x.a = y.a GROUP BY x.b / 2; -- group w/o existing GROUP BY target under ambiguous condition -- into a table SELECT count(x.b) INTO TABLE test_missing_target3 FROM test_missing_target x, test_missing_target y WHERE x.a = y.a GROUP BY x.b / 2 ORDER BY x.b / 2; SELECT * FROM test_missing_target3; -- Cleanup DROP TABLE test_missing_target; DROP TABLE test_missing_target2; DROP TABLE test_missing_target3; pgFormatter-4.2/t/pg-test-files/expected/select_into.sql000066400000000000000000000073241361326045100234400ustar00rootroot00000000000000-- -- SELECT_INTO -- SELECT * INTO TABLE sitmp1 FROM onek WHERE onek.unique1 < 2; DROP TABLE sitmp1; SELECT * INTO TABLE sitmp1 FROM onek2 WHERE onek2.unique1 < 2; DROP TABLE sitmp1; -- -- SELECT INTO and INSERT permission, if owner is not allowed to insert. -- CREATE SCHEMA selinto_schema; CREATE USER regress_selinto_user; ALTER DEFAULT PRIVILEGES FOR ROLE regress_selinto_user REVOKE INSERT ON TABLES FROM regress_selinto_user; GRANT ALL ON SCHEMA selinto_schema TO public; SET SESSION AUTHORIZATION regress_selinto_user; SELECT * INTO TABLE selinto_schema.tmp1 FROM pg_class WHERE relname LIKE '%a%'; -- Error SELECT oid AS clsoid, relname, relnatts + 10 AS x INTO selinto_schema.tmp2 FROM pg_class WHERE relname LIKE '%b%'; -- Error CREATE TABLE selinto_schema.tmp3 ( a, b, c ) AS SELECT oid, relname, relacl FROM pg_class WHERE relname LIKE '%c%'; -- Error RESET SESSION AUTHORIZATION; ALTER DEFAULT PRIVILEGES FOR ROLE regress_selinto_user GRANT INSERT ON TABLES TO regress_selinto_user; SET SESSION AUTHORIZATION regress_selinto_user; SELECT * INTO TABLE selinto_schema.tmp1 FROM pg_class WHERE relname LIKE '%a%'; -- OK SELECT oid AS clsoid, relname, relnatts + 10 AS x INTO selinto_schema.tmp2 FROM pg_class WHERE relname LIKE '%b%'; -- OK CREATE TABLE selinto_schema.tmp3 ( a, b, c ) AS SELECT oid, relname, relacl FROM pg_class WHERE relname LIKE '%c%'; -- OK RESET SESSION AUTHORIZATION; DROP SCHEMA selinto_schema CASCADE; DROP USER regress_selinto_user; -- Tests for WITH NO DATA and column name consistency CREATE TABLE ctas_base ( i int, j int ); INSERT INTO ctas_base VALUES (1, 2); CREATE TABLE ctas_nodata ( ii, jj, kk ) AS SELECT i, j FROM ctas_base; -- Error CREATE TABLE ctas_nodata ( ii, jj, kk ) AS SELECT i, j FROM ctas_base WITH NO DATA; -- Error CREATE TABLE ctas_nodata ( ii, jj ) AS SELECT i, j FROM ctas_base; -- OK CREATE TABLE ctas_nodata_2 ( ii, jj ) AS SELECT i, j FROM ctas_base WITH NO DATA; -- OK CREATE TABLE ctas_nodata_3 ( ii ) AS SELECT i, j FROM ctas_base; -- OK CREATE TABLE ctas_nodata_4 ( ii ) AS SELECT i, j FROM ctas_base WITH NO DATA; -- OK SELECT * FROM ctas_nodata; SELECT * FROM ctas_nodata_2; SELECT * FROM ctas_nodata_3; SELECT * FROM ctas_nodata_4; DROP TABLE ctas_base; DROP TABLE ctas_nodata; DROP TABLE ctas_nodata_2; DROP TABLE ctas_nodata_3; DROP TABLE ctas_nodata_4; -- -- CREATE TABLE AS/SELECT INTO as last command in a SQL function -- have been known to cause problems -- CREATE FUNCTION make_table () RETURNS VOID AS $$ CREATE TABLE created_table AS SELECT * FROM int8_tbl; $$ LANGUAGE SQL; SELECT make_table (); SELECT * FROM created_table; -- Try EXPLAIN ANALYZE SELECT INTO and EXPLAIN ANALYZE CREATE TABLE AS -- WITH NO DATA, but hide the outputs since they won't be stable. DO $$ BEGIN EXECUTE 'EXPLAIN ANALYZE SELECT * INTO TABLE easi FROM int8_tbl'; EXECUTE 'EXPLAIN ANALYZE CREATE TABLE easi2 AS SELECT * FROM int8_tbl WITH NO DATA'; END $$; DROP TABLE created_table; DROP TABLE easi, easi2; -- -- Disallowed uses of SELECT ... INTO. All should fail -- DECLARE foo CURSOR FOR SELECT 1 INTO b; COPY ( SELECT 1 INTO frak UNION SELECT 2) TO 'blob'; SELECT * FROM ( SELECT 1 INTO f) bar; CREATE VIEW foo AS SELECT 1 INTO b; INSERT INTO b SELECT 1 INTO f; pgFormatter-4.2/t/pg-test-files/expected/select_parallel.sql000066400000000000000000000401561361326045100242630ustar00rootroot00000000000000-- -- PARALLEL -- CREATE FUNCTION sp_parallel_restricted (int) RETURNS int AS $$ BEGIN RETURN $1; END $$ LANGUAGE plpgsql parallel restricted; -- Serializable isolation would disable parallel query, so explicitly use an -- arbitrary other level. BEGIN ISOLATION level REPEATABLE read; -- encourage use of parallel plans SET parallel_setup_cost = 0; SET parallel_tuple_cost = 0; SET min_parallel_table_scan_size = 0; SET max_parallel_workers_per_gather = 4; -- Parallel Append with partial-subplans EXPLAIN ( COSTS OFF ) SELECT round(avg(aa)), sum(aa) FROM a_star; SELECT round(avg(aa)), sum(aa) FROM a_star a1; -- Parallel Append with both partial and non-partial subplans ALTER TABLE c_star SET (parallel_workers = 0); ALTER TABLE d_star SET (parallel_workers = 0); EXPLAIN ( COSTS OFF ) SELECT round(avg(aa)), sum(aa) FROM a_star; SELECT round(avg(aa)), sum(aa) FROM a_star a2; -- Parallel Append with only non-partial subplans ALTER TABLE a_star SET (parallel_workers = 0); ALTER TABLE b_star SET (parallel_workers = 0); ALTER TABLE e_star SET (parallel_workers = 0); ALTER TABLE f_star SET (parallel_workers = 0); EXPLAIN ( COSTS OFF ) SELECT round(avg(aa)), sum(aa) FROM a_star; SELECT round(avg(aa)), sum(aa) FROM a_star a3; -- Disable Parallel Append ALTER TABLE a_star RESET (parallel_workers); ALTER TABLE b_star RESET (parallel_workers); ALTER TABLE c_star RESET (parallel_workers); ALTER TABLE d_star RESET (parallel_workers); ALTER TABLE e_star RESET (parallel_workers); ALTER TABLE f_star RESET (parallel_workers); SET enable_parallel_append TO OFF; EXPLAIN ( COSTS OFF ) SELECT round(avg(aa)), sum(aa) FROM a_star; SELECT round(avg(aa)), sum(aa) FROM a_star a4; RESET enable_parallel_append; -- Parallel Append that runs serially CREATE FUNCTION sp_test_func () RETURNS SETOF text AS $$ SELECT 'foo'::varchar UNION ALL SELECT 'bar'::varchar $$ LANGUAGE sql STABLE; SELECT sp_test_func () ORDER BY 1; -- Parallel Append is not to be used when the subpath depends on the outer param CREATE TABLE part_pa_test ( a int, b int ) PARTITION BY RANGE (a); CREATE TABLE part_pa_test_p1 PARTITION OF part_pa_test FOR VALUES FROM (MINVALUE) TO (0); CREATE TABLE part_pa_test_p2 PARTITION OF part_pa_test FOR VALUES FROM (0) TO (MAXVALUE); EXPLAIN ( COSTS OFF ) SELECT ( SELECT max(( SELECT pa1.b FROM part_pa_test pa1 WHERE pa1.a = pa2.a))) FROM part_pa_test pa2; DROP TABLE part_pa_test; -- test with leader participation disabled SET parallel_leader_participation = OFF; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1 WHERE stringu1 = 'GRAAAA'; SELECT count(*) FROM tenk1 WHERE stringu1 = 'GRAAAA'; -- test with leader participation disabled, but no workers available (so -- the leader will have to run the plan despite the setting) SET max_parallel_workers = 0; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1 WHERE stringu1 = 'GRAAAA'; SELECT count(*) FROM tenk1 WHERE stringu1 = 'GRAAAA'; RESET max_parallel_workers; RESET parallel_leader_participation; -- test that parallel_restricted function doesn't run in worker ALTER TABLE tenk1 SET (parallel_workers = 4); EXPLAIN ( VERBOSE, COSTS OFF ) SELECT sp_parallel_restricted (unique1) FROM tenk1 WHERE stringu1 = 'GRAAAA' ORDER BY 1; -- test parallel plan when group by expression is in target list. EXPLAIN ( COSTS OFF ) SELECT length(stringu1) FROM tenk1 GROUP BY length(stringu1); SELECT length(stringu1) FROM tenk1 GROUP BY length(stringu1); EXPLAIN ( COSTS OFF ) SELECT stringu1, count(*) FROM tenk1 GROUP BY stringu1 ORDER BY stringu1; -- test that parallel plan for aggregates is not selected when -- target list contains parallel restricted clause. EXPLAIN ( COSTS OFF ) SELECT sum(sp_parallel_restricted (unique1)) FROM tenk1 GROUP BY (sp_parallel_restricted (unique1)); -- test prepared statement PREPARE tenk1_count (integer) AS SELECT count((unique1)) FROM tenk1 WHERE hundred > $1; EXPLAIN ( COSTS OFF ) EXECUTE tenk1_count (1); EXECUTE tenk1_count (1); DEALLOCATE tenk1_count; -- test parallel plans for queries containing un-correlated subplans. ALTER TABLE tenk2 SET (parallel_workers = 0); EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1 WHERE (two, four) NOT IN ( SELECT hundred, thousand FROM tenk2 WHERE thousand > 100); SELECT count(*) FROM tenk1 WHERE (two, four) NOT IN ( SELECT hundred, thousand FROM tenk2 WHERE thousand > 100); -- this is not parallel-safe due to use of random() within SubLink's testexpr: EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1 WHERE (unique1 + random())::integer NOT IN ( SELECT ten FROM tenk2); ALTER TABLE tenk2 RESET (parallel_workers); -- test parallel plan for a query containing initplan. SET enable_indexscan = OFF; SET enable_indexonlyscan = OFF; SET enable_bitmapscan = OFF; ALTER TABLE tenk2 SET (parallel_workers = 2); EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1 WHERE tenk1.unique1 = ( SELECT max(tenk2.unique1) FROM tenk2); SELECT count(*) FROM tenk1 WHERE tenk1.unique1 = ( SELECT max(tenk2.unique1) FROM tenk2); RESET enable_indexscan; RESET enable_indexonlyscan; RESET enable_bitmapscan; ALTER TABLE tenk2 RESET (parallel_workers); -- test parallel index scans. SET enable_seqscan TO OFF; SET enable_bitmapscan TO OFF; EXPLAIN ( COSTS OFF ) SELECT count((unique1)) FROM tenk1 WHERE hundred > 1; SELECT count((unique1)) FROM tenk1 WHERE hundred > 1; -- test parallel index-only scans. EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1 WHERE thousand > 95; SELECT count(*) FROM tenk1 WHERE thousand > 95; -- test rescan cases too SET enable_material = FALSE; EXPLAIN ( COSTS OFF ) SELECT * FROM ( SELECT count(unique1) FROM tenk1 WHERE hundred > 10) ss RIGHT JOIN ( VALUES (1), (2), (3)) v (x) ON TRUE; SELECT * FROM ( SELECT count(unique1) FROM tenk1 WHERE hundred > 10) ss RIGHT JOIN ( VALUES (1), (2), (3)) v (x) ON TRUE; EXPLAIN ( COSTS OFF ) SELECT * FROM ( SELECT count(*) FROM tenk1 WHERE thousand > 99) ss RIGHT JOIN ( VALUES (1), (2), (3)) v (x) ON TRUE; SELECT * FROM ( SELECT count(*) FROM tenk1 WHERE thousand > 99) ss RIGHT JOIN ( VALUES (1), (2), (3)) v (x) ON TRUE; RESET enable_material; RESET enable_seqscan; RESET enable_bitmapscan; -- test parallel bitmap heap scan. SET enable_seqscan TO OFF; SET enable_indexscan TO OFF; SET enable_hashjoin TO OFF; SET enable_mergejoin TO OFF; SET enable_material TO OFF; -- test prefetching, if the platform allows it DO $$ BEGIN SET effective_io_concurrency = 50; EXCEPTION WHEN invalid_parameter_value THEN END $$; SET work_mem = '64kB'; --set small work mem to force lossy pages EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1, tenk2 WHERE tenk1.hundred > 1 AND tenk2.thousand = 0; SELECT count(*) FROM tenk1, tenk2 WHERE tenk1.hundred > 1 AND tenk2.thousand = 0; CREATE TABLE bmscantest ( a int, t text ); INSERT INTO bmscantest SELECT r, 'fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo' FROM generate_series(1, 100000) r; CREATE INDEX i_bmtest ON bmscantest (a); SELECT count(*) FROM bmscantest WHERE a > 1; -- test accumulation of stats for parallel nodes RESET enable_seqscan; ALTER TABLE tenk2 SET (parallel_workers = 0); EXPLAIN ( ANALYZE, timing OFF, summary OFF, COSTS OFF ) SELECT count(*) FROM tenk1, tenk2 WHERE tenk1.hundred > 1 AND tenk2.thousand = 0; ALTER TABLE tenk2 RESET (parallel_workers); RESET work_mem; CREATE FUNCTION explain_parallel_sort_stats () RETURNS SETOF text LANGUAGE plpgsql AS $$ DECLARE ln text; BEGIN FOR ln IN EXPLAIN ( ANALYZE, timing OFF, summary OFF, COSTS OFF ) SELECT * FROM ( SELECT ten FROM tenk1 WHERE ten < 100 ORDER BY ten) ss RIGHT JOIN ( VALUES (1), (2), (3)) v (x) ON TRUE LOOP ln := regexp_replace(ln, 'Memory: \S*', 'Memory: xxx'); RETURN NEXT ln; END LOOP; END; $$; SELECT * FROM explain_parallel_sort_stats (); RESET enable_indexscan; RESET enable_hashjoin; RESET enable_mergejoin; RESET enable_material; RESET effective_io_concurrency; DROP TABLE bmscantest; DROP FUNCTION explain_parallel_sort_stats (); -- test parallel merge join path. SET enable_hashjoin TO OFF; SET enable_nestloop TO OFF; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1, tenk2 WHERE tenk1.unique1 = tenk2.unique1; SELECT count(*) FROM tenk1, tenk2 WHERE tenk1.unique1 = tenk2.unique1; RESET enable_hashjoin; RESET enable_nestloop; -- test gather merge SET enable_hashagg = FALSE; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1 GROUP BY twenty; SELECT count(*) FROM tenk1 GROUP BY twenty; --test expressions in targetlist are pushed down for gather merge CREATE FUNCTION sp_simple_func (var1 integer) RETURNS integer AS $$ BEGIN RETURN var1 + 10; END; $$ LANGUAGE plpgsql PARALLEL SAFE; EXPLAIN ( COSTS OFF, VERBOSE ) SELECT ten, sp_simple_func (ten) FROM tenk1 WHERE ten < 100 ORDER BY ten; DROP FUNCTION sp_simple_func (integer); -- test handling of SRFs in targetlist (bug in 10.0) EXPLAIN ( COSTS OFF ) SELECT count(*), generate_series(1, 2) FROM tenk1 GROUP BY twenty; SELECT count(*), generate_series(1, 2) FROM tenk1 GROUP BY twenty; -- test gather merge with parallel leader participation disabled SET parallel_leader_participation = OFF; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1 GROUP BY twenty; SELECT count(*) FROM tenk1 GROUP BY twenty; RESET parallel_leader_participation; --test rescan behavior of gather merge SET enable_material = FALSE; EXPLAIN ( COSTS OFF ) SELECT * FROM ( SELECT string4, count(unique2) FROM tenk1 GROUP BY string4 ORDER BY string4) ss RIGHT JOIN ( VALUES (1), (2), (3)) v (x) ON TRUE; SELECT * FROM ( SELECT string4, count(unique2) FROM tenk1 GROUP BY string4 ORDER BY string4) ss RIGHT JOIN ( VALUES (1), (2), (3)) v (x) ON TRUE; RESET enable_material; RESET enable_hashagg; -- check parallelized int8 aggregate (bug #14897) EXPLAIN ( COSTS OFF ) SELECT avg(unique1::int8) FROM tenk1; SELECT avg(unique1::int8) FROM tenk1; -- gather merge test with a LIMIT EXPLAIN ( COSTS OFF ) SELECT fivethous FROM tenk1 ORDER BY fivethous LIMIT 4; SELECT fivethous FROM tenk1 ORDER BY fivethous LIMIT 4; -- gather merge test with 0 worker SET max_parallel_workers = 0; EXPLAIN ( COSTS OFF ) SELECT string4 FROM tenk1 ORDER BY string4 LIMIT 5; SELECT string4 FROM tenk1 ORDER BY string4 LIMIT 5; -- gather merge test with 0 workers, with parallel leader -- participation disabled (the leader will have to run the plan -- despite the setting) SET parallel_leader_participation = OFF; EXPLAIN ( COSTS OFF ) SELECT string4 FROM tenk1 ORDER BY string4 LIMIT 5; SELECT string4 FROM tenk1 ORDER BY string4 LIMIT 5; RESET parallel_leader_participation; RESET max_parallel_workers; SAVEPOINT settings; SET LOCAL force_parallel_mode = 1; EXPLAIN ( COSTS OFF ) SELECT stringu1::int2 FROM tenk1 WHERE unique1 = 1; ROLLBACK TO SAVEPOINT settings; -- exercise record typmod remapping between backends CREATE FUNCTION make_record (n int) RETURNS RECORD LANGUAGE plpgsql PARALLEL SAFE AS $$ BEGIN RETURN CASE n WHEN 1 THEN ROW (1) WHEN 2 THEN ROW (1, 2) WHEN 3 THEN ROW (1, 2, 3) WHEN 4 THEN ROW (1, 2, 3, 4) ELSE ROW (1, 2, 3, 4, 5) END; END; $$; SAVEPOINT settings; SET LOCAL force_parallel_mode = 1; SELECT make_record (x) FROM ( SELECT generate_series(1, 5) x) ss ORDER BY x; ROLLBACK TO SAVEPOINT settings; DROP FUNCTION make_record (n int); -- test the sanity of parallel query after the active role is dropped. DROP ROLE IF EXISTS regress_parallel_worker; CREATE ROLE regress_parallel_worker; SET ROLE regress_parallel_worker; RESET session AUTHORIZATION; DROP ROLE regress_parallel_worker; SET force_parallel_mode = 1; SELECT count(*) FROM tenk1; RESET force_parallel_mode; RESET ROLE; -- Window function calculation can't be pushed to workers. EXPLAIN ( COSTS OFF, VERBOSE ) SELECT count(*) FROM tenk1 a WHERE (unique1, two) IN ( SELECT unique1, row_number() OVER () FROM tenk1 b); -- LIMIT/OFFSET within sub-selects can't be pushed to workers. EXPLAIN ( COSTS OFF ) SELECT * FROM tenk1 a WHERE two IN ( SELECT two FROM tenk1 b WHERE stringu1 LIKE '%AAAA' LIMIT 3); -- to increase the parallel query test coverage SAVEPOINT settings; SET LOCAL force_parallel_mode = 1; EXPLAIN ( ANALYZE, timing OFF, summary OFF, COSTS OFF ) SELECT * FROM tenk1; ROLLBACK TO SAVEPOINT settings; -- provoke error in worker SAVEPOINT settings; SET LOCAL force_parallel_mode = 1; SELECT stringu1::int2 FROM tenk1 WHERE unique1 = 1; ROLLBACK TO SAVEPOINT settings; -- test interaction with set-returning functions SAVEPOINT settings; -- multiple subqueries under a single Gather node -- must set parallel_setup_cost > 0 to discourage multiple Gather nodes SET LOCAL parallel_setup_cost = 10; EXPLAIN ( COSTS OFF ) SELECT unique1 FROM tenk1 WHERE fivethous = tenthous + 1 UNION ALL SELECT unique1 FROM tenk1 WHERE fivethous = tenthous + 1; ROLLBACK TO SAVEPOINT settings; -- can't use multiple subqueries under a single Gather node due to initPlans EXPLAIN ( COSTS OFF ) SELECT unique1 FROM tenk1 WHERE fivethous = ( SELECT unique1 FROM tenk1 WHERE fivethous = 1 LIMIT 1) UNION ALL SELECT unique1 FROM tenk1 WHERE fivethous = ( SELECT unique2 FROM tenk1 WHERE fivethous = 1 LIMIT 1) ORDER BY 1; -- test interaction with SRFs SELECT * FROM information_schema.foreign_data_wrapper_options ORDER BY 1, 2, 3; -- test passing expanded-value representations to workers CREATE FUNCTION make_some_array (int, int) RETURNS int[] AS $$ DECLARE x int[]; BEGIN x[1] := $1; x[2] := $2; RETURN x; END $$ LANGUAGE plpgsql parallel safe; CREATE TABLE fooarr ( f1 text, f2 int[], f3 text ); INSERT INTO fooarr VALUES ('1', ARRAY[1, 2], 'one'); PREPARE pstmt (text, int[]) AS SELECT * FROM fooarr WHERE f1 = $1 AND f2 = $2; EXPLAIN ( COSTS OFF ) EXECUTE pstmt ('1', make_some_array (1, 2)); EXECUTE pstmt ('1', make_some_array (1, 2)); DEALLOCATE pstmt; -- test interaction between subquery and partial_paths CREATE VIEW tenk1_vw_sec WITH ( security_barrier ) AS SELECT * FROM tenk1; EXPLAIN ( COSTS OFF ) SELECT 1 FROM tenk1_vw_sec WHERE ( SELECT sum(f1) FROM int4_tbl WHERE f1 < unique1) < 100; ROLLBACK; pgFormatter-4.2/t/pg-test-files/expected/select_views.sql000066400000000000000000000127331361326045100236240ustar00rootroot00000000000000-- -- SELECT_VIEWS -- test the views defined in CREATE_VIEWS -- SELECT * FROM street; SELECT name, # thepath FROM iexit ORDER BY name COLLATE "C", 2; SELECT * FROM toyemp WHERE name = 'sharon'; -- -- Test for Leaky view scenario -- CREATE ROLE regress_alice; CREATE FUNCTION f_leak (text) RETURNS bool LANGUAGE 'plpgsql' COST 0.0000001 AS 'BEGIN RAISE NOTICE ''f_leak => %'', $1; RETURN true; END' ; CREATE TABLE customer ( cid int PRIMARY KEY, name text NOT NULL, tel text, passwd text ); CREATE TABLE credit_card ( cid int REFERENCES customer (cid), cnum text, climit int ); CREATE TABLE credit_usage ( cid int REFERENCES customer (cid), ymd date, usage int ); INSERT INTO customer VALUES (101, 'regress_alice', '+81-12-3456-7890', 'passwd123'), (102, 'regress_bob', '+01-234-567-8901', 'beafsteak'), (103, 'regress_eve', '+49-8765-43210', 'hamburger'); INSERT INTO credit_card VALUES (101, '1111-2222-3333-4444', 4000), (102, '5555-6666-7777-8888', 3000), (103, '9801-2345-6789-0123', 2000); INSERT INTO credit_usage VALUES (101, '2011-09-15', 120), (101, '2011-10-05', 90), (101, '2011-10-18', 110), (101, '2011-10-21', 200), (101, '2011-11-10', 80), (102, '2011-09-22', 300), (102, '2011-10-12', 120), (102, '2011-10-28', 200), (103, '2011-10-15', 480); CREATE VIEW my_property_normal AS SELECT * FROM customer WHERE name = CURRENT_USER; CREATE VIEW my_property_secure WITH ( security_barrier ) AS SELECT * FROM customer WHERE name = CURRENT_USER; CREATE VIEW my_credit_card_normal AS SELECT * FROM customer l NATURAL JOIN credit_card r WHERE l.name = CURRENT_USER; CREATE VIEW my_credit_card_secure WITH ( security_barrier ) AS SELECT * FROM customer l NATURAL JOIN credit_card r WHERE l.name = CURRENT_USER; CREATE VIEW my_credit_card_usage_normal AS SELECT * FROM my_credit_card_secure l NATURAL JOIN credit_usage r; CREATE VIEW my_credit_card_usage_secure WITH ( security_barrier ) AS SELECT * FROM my_credit_card_secure l NATURAL JOIN credit_usage r; GRANT SELECT ON my_property_normal TO public; GRANT SELECT ON my_property_secure TO public; GRANT SELECT ON my_credit_card_normal TO public; GRANT SELECT ON my_credit_card_secure TO public; GRANT SELECT ON my_credit_card_usage_normal TO public; GRANT SELECT ON my_credit_card_usage_secure TO public; -- -- Run leaky view scenarios -- SET SESSION AUTHORIZATION regress_alice; -- -- scenario: if a qualifier with tiny-cost is given, it shall be launched -- prior to the security policy of the view. -- SELECT * FROM my_property_normal WHERE f_leak (passwd); EXPLAIN ( COSTS OFF ) SELECT * FROM my_property_normal WHERE f_leak (passwd); SELECT * FROM my_property_secure WHERE f_leak (passwd); EXPLAIN ( COSTS OFF ) SELECT * FROM my_property_secure WHERE f_leak (passwd); -- -- scenario: qualifiers can be pushed down if they contain leaky functions, -- provided they aren't passed data from inside the view. -- SELECT * FROM my_property_normal v WHERE f_leak ('passwd') AND f_leak (passwd); EXPLAIN ( COSTS OFF ) SELECT * FROM my_property_normal v WHERE f_leak ('passwd') AND f_leak (passwd); SELECT * FROM my_property_secure v WHERE f_leak ('passwd') AND f_leak (passwd); EXPLAIN ( COSTS OFF ) SELECT * FROM my_property_secure v WHERE f_leak ('passwd') AND f_leak (passwd); -- -- scenario: if a qualifier references only one-side of a particular join- -- tree, it shall be distributed to the most deep scan plan as -- possible as we can. -- SELECT * FROM my_credit_card_normal WHERE f_leak (cnum); EXPLAIN ( COSTS OFF ) SELECT * FROM my_credit_card_normal WHERE f_leak (cnum); SELECT * FROM my_credit_card_secure WHERE f_leak (cnum); EXPLAIN ( COSTS OFF ) SELECT * FROM my_credit_card_secure WHERE f_leak (cnum); -- -- scenario: an external qualifier can be pushed-down by in-front-of the -- views with "security_barrier" attribute, except for operators -- implemented with leakproof functions. -- SELECT * FROM my_credit_card_usage_normal WHERE f_leak (cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01'; EXPLAIN ( COSTS OFF ) SELECT * FROM my_credit_card_usage_normal WHERE f_leak (cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01'; SELECT * FROM my_credit_card_usage_secure WHERE f_leak (cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01'; EXPLAIN ( COSTS OFF ) SELECT * FROM my_credit_card_usage_secure WHERE f_leak (cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01'; -- -- Test for the case when security_barrier gets changed between rewriter -- and planner stage. -- PREPARE p1 AS SELECT * FROM my_property_normal WHERE f_leak (passwd); PREPARE p2 AS SELECT * FROM my_property_secure WHERE f_leak (passwd); EXECUTE p1; EXECUTE p2; RESET SESSION AUTHORIZATION; ALTER VIEW my_property_normal SET (security_barrier = TRUE); ALTER VIEW my_property_secure SET (security_barrier = FALSE); SET SESSION AUTHORIZATION regress_alice; EXECUTE p1; -- To be perform as a view with security-barrier EXECUTE p2; -- To be perform as a view without security-barrier -- Cleanup. RESET SESSION AUTHORIZATION; DROP ROLE regress_alice; pgFormatter-4.2/t/pg-test-files/expected/sequence.sql000066400000000000000000000310471361326045100227370ustar00rootroot00000000000000-- -- CREATE SEQUENCE -- -- various error cases CREATE UNLOGGED SEQUENCE sequence_testx; CREATE SEQUENCE sequence_testx INCREMENT BY 0; CREATE SEQUENCE sequence_testx INCREMENT BY - 1 MINVALUE 20; CREATE SEQUENCE sequence_testx INCREMENT BY 1 MAXVALUE - 20; CREATE SEQUENCE sequence_testx INCREMENT BY - 1 START 10; CREATE SEQUENCE sequence_testx INCREMENT BY 1 START - 10; CREATE SEQUENCE sequence_testx CACHE 0; -- OWNED BY errors CREATE SEQUENCE sequence_testx OWNED BY nobody; -- nonsense word CREATE SEQUENCE sequence_testx OWNED BY pg_class_oid_index.oid; -- not a table CREATE SEQUENCE sequence_testx OWNED BY pg_class.relname; -- not same schema CREATE TABLE sequence_test_table ( a int ); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column DROP TABLE sequence_test_table; -- sequence data types CREATE SEQUENCE sequence_test5 AS integer; CREATE SEQUENCE sequence_test6 AS smallint; CREATE SEQUENCE sequence_test7 AS bigint; CREATE SEQUENCE sequence_test8 AS integer MAXVALUE 100000; CREATE SEQUENCE sequence_test9 AS integer INCREMENT BY - 1; CREATE SEQUENCE sequence_test10 AS integer MINVALUE - 100000 START 1; CREATE SEQUENCE sequence_test11 AS smallint; CREATE SEQUENCE sequence_test12 AS smallint INCREMENT - 1; CREATE SEQUENCE sequence_test13 AS smallint MINVALUE - 32768; CREATE SEQUENCE sequence_test14 AS smallint MAXVALUE 32767 INCREMENT - 1; CREATE SEQUENCE sequence_testx AS text; CREATE SEQUENCE sequence_testx AS nosuchtype; CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; CREATE SEQUENCE sequence_testx AS smallint MINVALUE - 100000; ALTER SEQUENCE sequence_test5 AS smallint; -- success, max will be adjusted ALTER SEQUENCE sequence_test8 AS smallint; -- fail, max has to be adjusted ALTER SEQUENCE sequence_test8 AS smallint MAXVALUE 20000; -- ok now ALTER SEQUENCE sequence_test9 AS smallint; -- success, min will be adjusted ALTER SEQUENCE sequence_test10 AS smallint; -- fail, min has to be adjusted ALTER SEQUENCE sequence_test10 AS smallint MINVALUE - 20000; -- ok now ALTER SEQUENCE sequence_test11 AS int; -- max will be adjusted ALTER SEQUENCE sequence_test12 AS int; -- min will be adjusted ALTER SEQUENCE sequence_test13 AS int; -- min and max will be adjusted ALTER SEQUENCE sequence_test14 AS int; -- min and max will be adjusted --- --- test creation of SERIAL column --- CREATE TABLE serialTest1 ( f1 text, f2 serial ); INSERT INTO serialTest1 VALUES ('foo'); INSERT INTO serialTest1 VALUES ('bar'); INSERT INTO serialTest1 VALUES ('force', 100); INSERT INTO serialTest1 VALUES ('wrong', NULL); SELECT * FROM serialTest1; SELECT pg_get_serial_sequence('serialTest1', 'f2'); -- test smallserial / bigserial CREATE TABLE serialTest2 ( f1 text, f2 serial, f3 smallserial, f4 serial2, f5 bigserial, f6 serial8 ); INSERT INTO serialTest2 (f1) VALUES ('test_defaults'); INSERT INTO serialTest2 (f1, f2, f3, f4, f5, f6) VALUES ('test_max_vals', 2147483647, 32767, 32767, 9223372036854775807, 9223372036854775807), ('test_min_vals', - 2147483648, - 32768, - 32768, - 9223372036854775808, - 9223372036854775808); -- All these INSERTs should fail: INSERT INTO serialTest2 (f1, f3) VALUES ('bogus', - 32769); INSERT INTO serialTest2 (f1, f4) VALUES ('bogus', - 32769); INSERT INTO serialTest2 (f1, f3) VALUES ('bogus', 32768); INSERT INTO serialTest2 (f1, f4) VALUES ('bogus', 32768); INSERT INTO serialTest2 (f1, f5) VALUES ('bogus', - 9223372036854775809); INSERT INTO serialTest2 (f1, f6) VALUES ('bogus', - 9223372036854775809); INSERT INTO serialTest2 (f1, f5) VALUES ('bogus', 9223372036854775808); INSERT INTO serialTest2 (f1, f6) VALUES ('bogus', 9223372036854775808); SELECT * FROM serialTest2 ORDER BY f2 ASC; SELECT nextval('serialTest2_f2_seq'); SELECT nextval('serialTest2_f3_seq'); SELECT nextval('serialTest2_f4_seq'); SELECT nextval('serialTest2_f5_seq'); SELECT nextval('serialTest2_f6_seq'); -- basic sequence operations using both text and oid references CREATE SEQUENCE sequence_test; CREATE SEQUENCE IF NOT EXISTS sequence_test; SELECT nextval('sequence_test'::text); SELECT nextval('sequence_test'::regclass); SELECT currval('sequence_test'::text); SELECT currval('sequence_test'::regclass); SELECT setval('sequence_test'::text, 32); SELECT nextval('sequence_test'::regclass); SELECT setval('sequence_test'::text, 99, FALSE); SELECT nextval('sequence_test'::regclass); SELECT setval('sequence_test'::regclass, 32); SELECT nextval('sequence_test'::text); SELECT setval('sequence_test'::regclass, 99, FALSE); SELECT nextval('sequence_test'::text); DISCARD SEQUENCES; SELECT currval('sequence_test'::regclass); DROP SEQUENCE sequence_test; -- renaming sequences CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; SELECT nextval('foo_seq_new'); SELECT nextval('foo_seq_new'); -- log_cnt can be higher if there is a checkpoint just at the right -- time, so just test for the expected range SELECT last_value, log_cnt IN (31, 32) AS log_cnt_ok, is_called FROM foo_seq_new; DROP SEQUENCE foo_seq_new; -- renaming serial sequences ALTER TABLE serialtest1_f2_seq RENAME TO serialtest1_f2_foo; INSERT INTO serialTest1 VALUES ('more'); SELECT * FROM serialTest1; -- -- Check dependencies of serial and ordinary sequences -- CREATE TEMP SEQUENCE myseq2; CREATE TEMP SEQUENCE myseq3; CREATE TEMP TABLE t1 ( f1 serial, f2 int DEFAULT nextval('myseq2'), f3 int DEFAULT nextval('myseq3'::text) ); -- Both drops should fail, but with different error messages: DROP SEQUENCE t1_f1_seq; DROP SEQUENCE myseq2; -- This however will work: DROP SEQUENCE myseq3; DROP TABLE t1; -- Fails because no longer existent: DROP SEQUENCE t1_f1_seq; -- Now OK: DROP SEQUENCE myseq2; -- -- Alter sequence -- ALTER SEQUENCE IF EXISTS sequence_test2 RESTART WITH 24 INCREMENT BY 4 MAXVALUE 36 MINVALUE 5 CYCLE; ALTER SEQUENCE serialTest1 CYCLE; -- error, not a sequence CREATE SEQUENCE sequence_test2 START WITH 32; CREATE SEQUENCE sequence_test4 INCREMENT BY - 1; SELECT nextval('sequence_test2'); SELECT nextval('sequence_test4'); ALTER SEQUENCE sequence_test2 RESTART; SELECT nextval('sequence_test2'); ALTER SEQUENCE sequence_test2 RESTART WITH 0; -- error ALTER SEQUENCE sequence_test4 RESTART WITH 40; -- error -- test CYCLE and NO CYCLE ALTER SEQUENCE sequence_test2 RESTART WITH 24 INCREMENT BY 4 MAXVALUE 36 MINVALUE 5 CYCLE; SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); -- cycled ALTER SEQUENCE sequence_test2 RESTART WITH 24 NO CYCLE; SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); -- error ALTER SEQUENCE sequence_test2 RESTART WITH - 24 START WITH - 24 INCREMENT BY - 4 MINVALUE - 36 MAXVALUE - 5 CYCLE; SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); -- cycled ALTER SEQUENCE sequence_test2 RESTART WITH - 24 NO CYCLE; SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); -- error -- reset ALTER SEQUENCE IF EXISTS sequence_test2 RESTART WITH 32 START WITH 32 INCREMENT BY 4 MAXVALUE 36 MINVALUE 5 CYCLE; SELECT setval('sequence_test2', - 100); -- error SELECT setval('sequence_test2', 100); -- error SELECT setval('sequence_test2', 5); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema SELECT * FROM information_schema.sequences WHERE sequence_name ~ ANY (ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, CYCLE, cache_size, last_value FROM pg_sequences WHERE sequencename ~ ANY (ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); \d sequence_test4 \d serialtest2_f2_seq -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; COMMENT ON SEQUENCE sequence_test2 IS 'will work'; COMMENT ON SEQUENCE sequence_test2 IS NULL; -- Test lastval() CREATE SEQUENCE seq; SELECT nextval('seq'); SELECT lastval(); SELECT setval('seq', 99); SELECT lastval(); DISCARD SEQUENCES; SELECT lastval(); CREATE SEQUENCE seq2; SELECT nextval('seq2'); SELECT lastval(); DROP SEQUENCE seq2; -- should fail SELECT lastval(); CREATE USER regress_seq_user; -- Test sequences in read-only transactions CREATE TEMPORARY SEQUENCE sequence_test_temp1; START TRANSACTION READ ONLY; SELECT nextval('sequence_test_temp1'); -- ok SELECT nextval('sequence_test2'); -- error ROLLBACK; START TRANSACTION READ ONLY; SELECT setval('sequence_test_temp1', 1); -- ok SELECT setval('sequence_test2', 1); -- error ROLLBACK; -- privileges tests -- nextval BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; REVOKE ALL ON seq3 FROM regress_seq_user; GRANT SELECT ON seq3 TO regress_seq_user; SELECT nextval('seq3'); ROLLBACK; BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; REVOKE ALL ON seq3 FROM regress_seq_user; GRANT UPDATE ON seq3 TO regress_seq_user; SELECT nextval('seq3'); ROLLBACK; BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; REVOKE ALL ON seq3 FROM regress_seq_user; GRANT USAGE ON seq3 TO regress_seq_user; SELECT nextval('seq3'); ROLLBACK; -- currval BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; SELECT nextval('seq3'); REVOKE ALL ON seq3 FROM regress_seq_user; GRANT SELECT ON seq3 TO regress_seq_user; SELECT currval('seq3'); ROLLBACK; BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; SELECT nextval('seq3'); REVOKE ALL ON seq3 FROM regress_seq_user; GRANT UPDATE ON seq3 TO regress_seq_user; SELECT currval('seq3'); ROLLBACK; BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; SELECT nextval('seq3'); REVOKE ALL ON seq3 FROM regress_seq_user; GRANT USAGE ON seq3 TO regress_seq_user; SELECT currval('seq3'); ROLLBACK; -- lastval BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; SELECT nextval('seq3'); REVOKE ALL ON seq3 FROM regress_seq_user; GRANT SELECT ON seq3 TO regress_seq_user; SELECT lastval(); ROLLBACK; BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; SELECT nextval('seq3'); REVOKE ALL ON seq3 FROM regress_seq_user; GRANT UPDATE ON seq3 TO regress_seq_user; SELECT lastval(); ROLLBACK; BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; SELECT nextval('seq3'); REVOKE ALL ON seq3 FROM regress_seq_user; GRANT USAGE ON seq3 TO regress_seq_user; SELECT lastval(); ROLLBACK; -- setval BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; REVOKE ALL ON seq3 FROM regress_seq_user; SAVEPOINT save; SELECT setval('seq3', 5); ROLLBACK TO save; GRANT UPDATE ON seq3 TO regress_seq_user; SELECT setval('seq3', 5); SELECT nextval('seq3'); ROLLBACK; -- ALTER SEQUENCE BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; ALTER SEQUENCE sequence_test2 START WITH 1; ROLLBACK; -- Sequences should get wiped out as well: DROP TABLE serialTest1, serialTest2; -- Make sure sequences are gone: SELECT * FROM information_schema.sequences WHERE sequence_name IN ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') ORDER BY sequence_name ASC; DROP USER regress_seq_user; DROP SEQUENCE seq; -- cache tests CREATE SEQUENCE test_seq1 CACHE 10; SELECT nextval('test_seq1'); SELECT nextval('test_seq1'); SELECT nextval('test_seq1'); DROP SEQUENCE test_seq1; pgFormatter-4.2/t/pg-test-files/expected/spgist.sql000066400000000000000000000061171361326045100224400ustar00rootroot00000000000000-- -- Test SP-GiST indexes. -- -- There are other tests to test different SP-GiST opclasses. This is for -- testing SP-GiST code itself. CREATE TABLE spgist_point_tbl ( id int4, p point ); CREATE INDEX spgist_point_idx ON spgist_point_tbl USING spgist (p) WITH (fillfactor = 75); -- Test vacuum-root operation. It gets invoked when the root is also a leaf, -- i.e. the index is very small. INSERT INTO spgist_point_tbl (id, p) SELECT g, point(g * 10, g * 10) FROM generate_series(1, 10) g; DELETE FROM spgist_point_tbl WHERE id < 5; VACUUM spgist_point_tbl; -- Insert more data, to make the index a few levels deep. INSERT INTO spgist_point_tbl (id, p) SELECT g, point(g * 10, g * 10) FROM generate_series(1, 10000) g; INSERT INTO spgist_point_tbl (id, p) SELECT g + 100000, point(g * 10 + 1, g * 10 + 1) FROM generate_series(1, 10000) g; -- To test vacuum, delete some entries from all over the index. DELETE FROM spgist_point_tbl WHERE id % 2 = 1; -- And also delete some concentration of values. (SP-GiST doesn't currently -- attempt to delete pages even when they become empty, but if it did, this -- would exercise it) DELETE FROM spgist_point_tbl WHERE id < 10000; VACUUM spgist_point_tbl; -- Test rescan paths (cf. bug #15378) -- use box and && rather than point, so that rescan happens when the -- traverse stack is non-empty CREATE TABLE spgist_box_tbl ( id serial, b box ); INSERT INTO spgist_box_tbl (b) SELECT box(point(i, j), point(i + s, j + s)) FROM generate_series(1, 100, 5) i, generate_series(1, 100, 5) j, generate_series(1, 10) s; CREATE INDEX spgist_box_idx ON spgist_box_tbl USING spgist (b); SELECT count(*) FROM ( VALUES (point(5, 5)), (point(8, 8)), (point(12, 12))) v (p) WHERE EXISTS ( SELECT * FROM spgist_box_tbl b WHERE b.b && box(v.p, v.p)); -- The point opclass's choose method only uses the spgMatchNode action, -- so the other actions are not tested by the above. Create an index using -- text opclass, which uses the others actions. CREATE TABLE spgist_text_tbl ( id int4, t text ); CREATE INDEX spgist_text_idx ON spgist_text_tbl USING spgist (t); INSERT INTO spgist_text_tbl (id, t) SELECT g, 'f' || repeat('o', 100) || g FROM generate_series(1, 10000) g UNION ALL SELECT g, 'baaaaaaaaaaaaaar' || g FROM generate_series(1, 1000) g; -- Do a lot of insertions that have to split an existing node. Hopefully -- one of these will cause the page to run out of space, causing the inner -- tuple to be moved to another page. INSERT INTO spgist_text_tbl (id, t) SELECT - g, 'f' || repeat('o', 100 - g) || 'surprise' FROM generate_series(1, 100) g; -- Test out-of-range fillfactor values CREATE INDEX spgist_point_idx2 ON spgist_point_tbl USING spgist (p) WITH (fillfactor = 9); CREATE INDEX spgist_point_idx2 ON spgist_point_tbl USING spgist (p) WITH (fillfactor = 101); -- Modify fillfactor in existing index ALTER INDEX spgist_point_idx SET (fillfactor = 90); REINDEX INDEX spgist_point_idx; pgFormatter-4.2/t/pg-test-files/expected/stats.sql000066400000000000000000000156711361326045100222720ustar00rootroot00000000000000-- -- Test Statistics Collector -- -- Must be run after tenk2 has been created (by create_table), -- populated (by create_misc) and indexed (by create_index). -- -- conditio sine qua non SHOW track_counts; -- must be on -- ensure that both seqscan and indexscan plans are allowed SET enable_seqscan TO ON; SET enable_indexscan TO ON; -- for the moment, we don't want index-only scans here SET enable_indexonlyscan TO OFF; -- save counters CREATE TABLE prevstats AS SELECT t.seq_scan, t.seq_tup_read, t.idx_scan, t.idx_tup_fetch, (b.heap_blks_read + b.heap_blks_hit) AS heap_blks, (b.idx_blks_read + b.idx_blks_hit) AS idx_blks, pg_stat_get_snapshot_timestamp () AS snap_ts FROM pg_catalog.pg_stat_user_tables AS t, pg_catalog.pg_statio_user_tables AS b WHERE t.relname = 'tenk2' AND b.relname = 'tenk2'; -- function to wait for counters to advance CREATE FUNCTION wait_for_stats () RETURNS void AS $$ DECLARE start_time timestamptz := clock_timestamp(); updated1 bool; updated2 bool; updated3 bool; updated4 bool; BEGIN -- we don't want to wait forever; loop will exit after 30 seconds FOR i IN 1..300 LOOP -- With parallel query, the seqscan and indexscan on tenk2 might be done -- in parallel worker processes, which will send their stats counters -- asynchronously to what our own session does. So we must check for -- those counts to be registered separately from the update counts. -- check to see if seqscan has been sensed SELECT (st.seq_scan >= pr.seq_scan + 1) INTO updated1 FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr WHERE st.relname = 'tenk2' AND cl.relname = 'tenk2'; -- check to see if indexscan has been sensed SELECT (st.idx_scan >= pr.idx_scan + 1) INTO updated2 FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr WHERE st.relname = 'tenk2' AND cl.relname = 'tenk2'; -- check to see if all updates have been sensed SELECT (n_tup_ins > 0) INTO updated3 FROM pg_stat_user_tables WHERE relname = 'trunc_stats_test4'; -- We must also check explicitly that pg_stat_get_snapshot_timestamp has -- advanced, because that comes from the global stats file which might -- be older than the per-DB stats file we got the other values from. SELECT (pr.snap_ts < pg_stat_get_snapshot_timestamp ()) INTO updated4 FROM prevstats AS pr; exit WHEN updated1 AND updated2 AND updated3 AND updated4; -- wait a little PERFORM pg_sleep_for ('100 milliseconds'); -- reset stats snapshot so we can test again PERFORM pg_stat_clear_snapshot(); END LOOP; -- report time waited in postmaster log (where it won't change test output) RAISE log 'wait_for_stats delayed % seconds', extract(epoch FROM clock_timestamp() - start_time); END $$ LANGUAGE plpgsql; -- test effects of TRUNCATE on n_live_tup/n_dead_tup counters CREATE TABLE trunc_stats_test ( id serial ); CREATE TABLE trunc_stats_test1 ( id serial, stuff text ); CREATE TABLE trunc_stats_test2 ( id serial ); CREATE TABLE trunc_stats_test3 ( id serial, stuff text ); CREATE TABLE trunc_stats_test4 ( id serial ); -- check that n_live_tup is reset to 0 after truncate INSERT INTO trunc_stats_test DEFAULT VALUES; INSERT INTO trunc_stats_test DEFAULT VALUES; INSERT INTO trunc_stats_test DEFAULT VALUES; TRUNCATE trunc_stats_test; -- test involving a truncate in a transaction; 4 ins but only 1 live INSERT INTO trunc_stats_test1 DEFAULT VALUES; INSERT INTO trunc_stats_test1 DEFAULT VALUES; INSERT INTO trunc_stats_test1 DEFAULT VALUES; UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2); DELETE FROM trunc_stats_test1 WHERE id = 3; BEGIN; UPDATE trunc_stats_test1 SET id = id + 100; TRUNCATE trunc_stats_test1; INSERT INTO trunc_stats_test1 DEFAULT VALUES; COMMIT; -- use a savepoint: 1 insert, 1 live BEGIN; INSERT INTO trunc_stats_test2 DEFAULT VALUES; INSERT INTO trunc_stats_test2 DEFAULT VALUES; SAVEPOINT p1; INSERT INTO trunc_stats_test2 DEFAULT VALUES; TRUNCATE trunc_stats_test2; INSERT INTO trunc_stats_test2 DEFAULT VALUES; RELEASE SAVEPOINT p1; COMMIT; -- rollback a savepoint: this should count 4 inserts and have 2 -- live tuples after commit (and 2 dead ones due to aborted subxact) BEGIN; INSERT INTO trunc_stats_test3 DEFAULT VALUES; INSERT INTO trunc_stats_test3 DEFAULT VALUES; SAVEPOINT p1; INSERT INTO trunc_stats_test3 DEFAULT VALUES; INSERT INTO trunc_stats_test3 DEFAULT VALUES; TRUNCATE trunc_stats_test3; INSERT INTO trunc_stats_test3 DEFAULT VALUES; ROLLBACK TO SAVEPOINT p1; COMMIT; -- rollback a truncate: this should count 2 inserts and produce 2 dead tuples BEGIN; INSERT INTO trunc_stats_test4 DEFAULT VALUES; INSERT INTO trunc_stats_test4 DEFAULT VALUES; TRUNCATE trunc_stats_test4; INSERT INTO trunc_stats_test4 DEFAULT VALUES; ROLLBACK; -- do a seqscan SELECT count(*) FROM tenk2; -- do an indexscan -- make sure it is not a bitmap scan, which might skip fetching heap tuples SET enable_bitmapscan TO OFF; SELECT count(*) FROM tenk2 WHERE unique1 = 1; RESET enable_bitmapscan; -- We can't just call wait_for_stats() at this point, because we only -- transmit stats when the session goes idle, and we probably didn't -- transmit the last couple of counts yet thanks to the rate-limiting logic -- in pgstat_report_stat(). But instead of waiting for the rate limiter's -- timeout to elapse, let's just start a new session. The old one will -- then send its stats before dying. \c - -- wait for stats collector to update SELECT wait_for_stats (); -- check effects SELECT relname, n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup FROM pg_stat_user_tables WHERE relname LIKE 'trunc_stats_test%' ORDER BY relname; SELECT st.seq_scan >= pr.seq_scan + 1, st.seq_tup_read >= pr.seq_tup_read + cl.reltuples, st.idx_scan >= pr.idx_scan + 1, st.idx_tup_fetch >= pr.idx_tup_fetch + 1 FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr WHERE st.relname = 'tenk2' AND cl.relname = 'tenk2'; SELECT st.heap_blks_read + st.heap_blks_hit >= pr.heap_blks + cl.relpages, st.idx_blks_read + st.idx_blks_hit >= pr.idx_blks + 1 FROM pg_statio_user_tables AS st, pg_class AS cl, prevstats AS pr WHERE st.relname = 'tenk2' AND cl.relname = 'tenk2'; SELECT pr.snap_ts < pg_stat_get_snapshot_timestamp () AS snapshot_newer FROM prevstats AS pr; DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4; DROP TABLE prevstats; -- End of Stats Test pgFormatter-4.2/t/pg-test-files/expected/stats_ext.sql000066400000000000000000000404071361326045100231450ustar00rootroot00000000000000-- Generic extended statistics support -- We will be checking execution plans without/with statistics, so -- let's make sure we get simple non-parallel plans. Also set the -- work_mem low so that we can use small amounts of data. -- check the number of estimated/actual rows in the top node CREATE FUNCTION check_estimated_rows (text) RETURNS TABLE ( estimated int, actual int) LANGUAGE plpgsql AS $$ DECLARE ln text; tmp text[]; first_row bool := TRUE; BEGIN FOR ln IN EXECUTE format('explain analyze %s', $1) LOOP IF first_row THEN first_row := FALSE; tmp := regexp_match (ln, 'rows=(\d*) .* rows=(\d*)'); RETURN query SELECT tmp[1]::int, tmp[2]::int; END IF; END LOOP; END; $$; -- Verify failures CREATE STATISTICS tst; CREATE STATISTICS tst ON a, b; CREATE STATISTICS tst FROM sometab; CREATE STATISTICS tst ON a, b FROM nonexistant; CREATE STATISTICS tst ON a, b FROM pg_class; CREATE STATISTICS tst ON relname, relname, relnatts FROM pg_class; CREATE STATISTICS tst ON relnatts + relpages FROM pg_class; CREATE STATISTICS tst ON (relpages, reltuples) FROM pg_class; CREATE STATISTICS tst (unrecognized) ON relname, relnatts FROM pg_class; -- Ensure stats are dropped sanely, and test IF NOT EXISTS while at it CREATE TABLE ab1 ( a integer, b integer, c integer ); CREATE STATISTICS IF NOT EXISTS ab1_a_b_stats ON a, b FROM ab1; CREATE STATISTICS IF NOT EXISTS ab1_a_b_stats ON a, b FROM ab1; DROP STATISTICS ab1_a_b_stats; CREATE SCHEMA regress_schema_2; CREATE STATISTICS regress_schema_2.ab1_a_b_stats ON a, b FROM ab1; -- Let's also verify the pg_get_statisticsobjdef output looks sane. SELECT pg_get_statisticsobjdef (oid) FROM pg_statistic_ext WHERE stxname = 'ab1_a_b_stats'; DROP STATISTICS regress_schema_2.ab1_a_b_stats; -- Ensure statistics are dropped when columns are CREATE STATISTICS ab1_b_c_stats ON b, c FROM ab1; CREATE STATISTICS ab1_a_b_c_stats ON a, b, c FROM ab1; CREATE STATISTICS ab1_b_a_stats ON b, a FROM ab1; ALTER TABLE ab1 DROP COLUMN a; \d ab1 -- Ensure statistics are dropped when table is SELECT stxname FROM pg_statistic_ext WHERE stxname LIKE 'ab1%'; DROP TABLE ab1; SELECT stxname FROM pg_statistic_ext WHERE stxname LIKE 'ab1%'; -- Ensure things work sanely with SET STATISTICS 0 CREATE TABLE ab1 ( a integer, b integer ); ALTER TABLE ab1 ALTER a SET STATISTICS 0; INSERT INTO ab1 SELECT a, a % 23 FROM generate_series(1, 1000) a; CREATE STATISTICS ab1_a_b_stats ON a, b FROM ab1; ANALYZE ab1; ALTER TABLE ab1 ALTER a SET STATISTICS - 1; -- partial analyze doesn't build stats either ANALYZE ab1 (a); ANALYZE ab1; DROP TABLE ab1; -- Verify supported object types for extended statistics CREATE SCHEMA tststats; CREATE TABLE tststats.t ( a int, b int, c text ); CREATE INDEX ti ON tststats.t (a, b); CREATE SEQUENCE tststats.s; CREATE VIEW tststats.v AS SELECT * FROM tststats.t; CREATE MATERIALIZED VIEW tststats.mv AS SELECT * FROM tststats.t; CREATE TYPE tststats.ty AS ( a int, b int, c text ); CREATE FOREIGN DATA WRAPPER extstats_dummy_fdw; CREATE SERVER extstats_dummy_srv FOREIGN DATA WRAPPER extstats_dummy_fdw; CREATE FOREIGN TABLE tststats.f ( a int, b int, c text) SERVER extstats_dummy_srv; CREATE TABLE tststats.pt ( a int, b int, c text ) PARTITION BY RANGE (a, b); CREATE TABLE tststats.pt1 PARTITION OF tststats.pt FOR VALUES FROM (- 10, - 10) TO (10, 10); CREATE STATISTICS tststats.s1 ON a, b FROM tststats.t; CREATE STATISTICS tststats.s2 ON a, b FROM tststats.ti; CREATE STATISTICS tststats.s3 ON a, b FROM tststats.s; CREATE STATISTICS tststats.s4 ON a, b FROM tststats.v; CREATE STATISTICS tststats.s5 ON a, b FROM tststats.mv; CREATE STATISTICS tststats.s6 ON a, b FROM tststats.ty; CREATE STATISTICS tststats.s7 ON a, b FROM tststats.f; CREATE STATISTICS tststats.s8 ON a, b FROM tststats.pt; CREATE STATISTICS tststats.s9 ON a, b FROM tststats.pt1; DO $$ DECLARE relname text := reltoastrelid::regclass FROM pg_class WHERE oid = 'tststats.t'::regclass; BEGIN EXECUTE 'CREATE STATISTICS tststats.s10 ON a, b FROM ' || relname; EXCEPTION WHEN wrong_object_type THEN RAISE NOTICE 'stats on toast table not created'; END; $$; DROP SCHEMA tststats CASCADE; DROP FOREIGN DATA WRAPPER extstats_dummy_fdw CASCADE; -- n-distinct tests CREATE TABLE ndistinct ( filler1 text, filler2 numeric, a int, b int, filler3 date, c int, d int ); -- over-estimates when using only per-column statistics INSERT INTO ndistinct (a, b, c, filler1) SELECT i / 100, i / 100, i / 100, cash_words((i / 100)::money) FROM generate_series(1, 1000) s (i); ANALYZE ndistinct; -- Group Aggregate, due to over-estimate of the number of groups SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY b, c'); SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c'); SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c, d'); SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY b, c, d'); -- correct command CREATE STATISTICS s10 ON a, b, c FROM ndistinct; ANALYZE ndistinct; SELECT stxkind, stxndistinct FROM pg_statistic_ext WHERE stxrelid = 'ndistinct'::regclass; -- Hash Aggregate, thanks to estimates improved by the statistic SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY b, c'); SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c'); -- last two plans keep using Group Aggregate, because 'd' is not covered -- by the statistic and while it's NULL-only we assume 200 values for it SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c, d'); SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY b, c, d'); TRUNCATE TABLE ndistinct; -- under-estimates when using only per-column statistics INSERT INTO ndistinct (a, b, c, filler1) SELECT mod(i, 50), mod(i, 51), mod(i, 32), cash_words(mod(i, 33)::int::money) FROM generate_series(1, 5000) s (i); ANALYZE ndistinct; SELECT stxkind, stxndistinct FROM pg_statistic_ext WHERE stxrelid = 'ndistinct'::regclass; -- correct esimates SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c'); SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c, d'); SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY b, c, d'); SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY a, d'); DROP STATISTICS s10; SELECT stxkind, stxndistinct FROM pg_statistic_ext WHERE stxrelid = 'ndistinct'::regclass; -- dropping the statistics results in under-estimates SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c'); SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c, d'); SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY b, c, d'); SELECT * FROM check_estimated_rows ('SELECT COUNT(*) FROM ndistinct GROUP BY a, d'); -- functional dependencies tests CREATE TABLE functional_dependencies ( filler1 text, filler2 numeric, a int, b text, filler3 date, c int, d text ); CREATE INDEX fdeps_ab_idx ON functional_dependencies (a, b); CREATE INDEX fdeps_abc_idx ON functional_dependencies (a, b, c); -- random data (no functional dependencies) INSERT INTO functional_dependencies (a, b, c, filler1) SELECT mod(i, 23), mod(i, 29), mod(i, 31), i FROM generate_series(1, 5000) s (i); ANALYZE functional_dependencies; SELECT * FROM check_estimated_rows ('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'''); SELECT * FROM check_estimated_rows ('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'' AND c = 1'); -- create statistics CREATE STATISTICS func_deps_stat (dependencies) ON a, b, c FROM functional_dependencies; ANALYZE functional_dependencies; SELECT * FROM check_estimated_rows ('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'''); SELECT * FROM check_estimated_rows ('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'' AND c = 1'); -- a => b, a => c, b => c TRUNCATE functional_dependencies; DROP STATISTICS func_deps_stat; INSERT INTO functional_dependencies (a, b, c, filler1) SELECT mod(i, 100), mod(i, 50), mod(i, 25), i FROM generate_series(1, 5000) s (i); ANALYZE functional_dependencies; SELECT * FROM check_estimated_rows ('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'''); SELECT * FROM check_estimated_rows ('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'' AND c = 1'); -- create statistics CREATE STATISTICS func_deps_stat (dependencies) ON a, b, c FROM functional_dependencies; ANALYZE functional_dependencies; SELECT * FROM check_estimated_rows ('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'''); SELECT * FROM check_estimated_rows ('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'' AND c = 1'); -- check change of column type doesn't break it ALTER TABLE functional_dependencies ALTER COLUMN c TYPE numeric; SELECT * FROM check_estimated_rows ('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'' AND c = 1'); ANALYZE functional_dependencies; SELECT * FROM check_estimated_rows ('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'' AND c = 1'); -- MCV lists CREATE TABLE mcv_lists ( filler1 text, filler2 numeric, a int, b varchar, filler3 date, c int, d text ); -- random data (no MCV list) INSERT INTO mcv_lists (a, b, c, filler1) SELECT mod(i, 37), mod(i, 41), mod(i, 43), mod(i, 47) FROM generate_series(1, 5000) s (i); ANALYZE mcv_lists; SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'''); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'' AND c = 1'); -- create statistics CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, c FROM mcv_lists; ANALYZE mcv_lists; SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'''); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'' AND c = 1'); -- 100 distinct combinations, all in the MCV list TRUNCATE mcv_lists; DROP STATISTICS mcv_lists_stats; INSERT INTO mcv_lists (a, b, c, filler1) SELECT mod(i, 100), mod(i, 50), mod(i, 25), i FROM generate_series(1, 5000) s (i); ANALYZE mcv_lists; SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'''); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a < 1 AND b < ''1'''); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a <= 0 AND b <= ''0'''); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'' AND c = 1'); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a < 5 AND b < ''1'' AND c < 5'); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a <= 4 AND b <= ''0'' AND c <= 4'); -- create statistics CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, c FROM mcv_lists; ANALYZE mcv_lists; SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'''); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a < 1 AND b < ''1'''); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a <= 0 AND b <= ''0'''); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'' AND c = 1'); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a < 5 AND b < ''1'' AND c < 5'); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a <= 4 AND b <= ''0'' AND c <= 4'); -- check change of unrelated column type does not reset the MCV statistics ALTER TABLE mcv_lists ALTER COLUMN d TYPE VARCHAR(64); SELECT stxmcv IS NOT NULL FROM pg_statistic_ext WHERE stxname = 'mcv_lists_stats'; -- check change of column type resets the MCV statistics ALTER TABLE mcv_lists ALTER COLUMN c TYPE numeric; SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'''); ANALYZE mcv_lists; SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'''); -- 100 distinct combinations with NULL values, all in the MCV list TRUNCATE mcv_lists; DROP STATISTICS mcv_lists_stats; INSERT INTO mcv_lists (a, b, c, filler1) SELECT ( CASE WHEN mod(i, 100) = 1 THEN NULL ELSE mod(i, 100) END), ( CASE WHEN mod(i, 50) = 1 THEN NULL ELSE mod(i, 50) END), ( CASE WHEN mod(i, 25) = 1 THEN NULL ELSE mod(i, 25) END), i FROM generate_series(1, 5000) s (i); ANALYZE mcv_lists; SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a IS NULL AND b IS NULL'); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a IS NULL AND b IS NULL AND c IS NULL'); -- create statistics CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, c FROM mcv_lists; ANALYZE mcv_lists; SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a IS NULL AND b IS NULL'); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists WHERE a IS NULL AND b IS NULL AND c IS NULL'); -- test pg_mcv_list_items with a very simple (single item) MCV list TRUNCATE mcv_lists; INSERT INTO mcv_lists (a, b, c) SELECT 1, 2, 3 FROM generate_series(1, 1000) s (i); ANALYZE mcv_lists; SELECT m.* FROM pg_statistic_ext, pg_mcv_list_items (stxmcv) m WHERE stxname = 'mcv_lists_stats'; -- mcv with arrays CREATE TABLE mcv_lists_arrays ( a text[], b numeric[], c int[] ); INSERT INTO mcv_lists_arrays (a, b, c) SELECT ARRAY[md5((i / 100)::text), md5((i / 100 - 1)::text), md5((i / 100 + 1)::text)], ARRAY[(i / 100 - 1)::numeric / 1000, (i / 100)::numeric / 1000, (i / 100 + 1)::numeric / 1000], ARRAY[(i / 100 - 1), i / 100, (i / 100 + 1)] FROM generate_series(1, 5000) s (i); CREATE STATISTICS mcv_lists_arrays_stats (mcv) ON a, b, c FROM mcv_lists_arrays; ANALYZE mcv_lists_arrays; -- mcv with bool CREATE TABLE mcv_lists_bool ( a bool, b bool, c bool ); INSERT INTO mcv_lists_bool (a, b, c) SELECT (mod(i, 2) = 0), (mod(i, 4) = 0), (mod(i, 8) = 0) FROM generate_series(1, 10000) s (i); ANALYZE mcv_lists_bool; SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists_bool WHERE a AND b AND c'); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists_bool WHERE NOT a AND b AND c'); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists_bool WHERE NOT a AND NOT b AND c'); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists_bool WHERE NOT a AND b AND NOT c'); CREATE STATISTICS mcv_lists_bool_stats (mcv) ON a, b, c FROM mcv_lists_bool; ANALYZE mcv_lists_bool; SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists_bool WHERE a AND b AND c'); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists_bool WHERE NOT a AND b AND c'); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists_bool WHERE NOT a AND NOT b AND c'); SELECT * FROM check_estimated_rows ('SELECT * FROM mcv_lists_bool WHERE NOT a AND b AND NOT c'); pgFormatter-4.2/t/pg-test-files/expected/strings.sql000066400000000000000000000516201361326045100226170ustar00rootroot00000000000000-- -- STRINGS -- Test various data entry syntaxes. -- -- SQL string continuation syntax -- E021-03 character string literals SELECT 'first line' ' - next line' ' - third line' AS "Three lines to one"; -- illegal string continuation syntax SELECT 'first line' ' - next line' /* this comment is not allowed here */ ' - third line' AS "Illegal comment within continuation"; -- Unicode escapes SET standard_conforming_strings TO ON; SELECT U & 'd\0061t\+000061' AS U & "d\0061t\+000061"; SELECT U & 'd!0061t\+000061' UESCAPE '!' AS U & "d*0061t\+000061" UESCAPE '*'; SELECT U & ' \' UESCAPE ' ! ' AS "tricky"; SELECT ' tricky ' AS U&"\" UESCAPE ' ! '; SELECT U&' wrong: 061 '; SELECT U&' wrong: + 0061 '; SELECT U&' wrong: + 0061 ' UESCAPE ' + '; SET standard_conforming_strings TO off; SELECT U&' d 0061t + 000061 ' AS U&"d\0061t\+000061"; SELECT U&' d ! 0061t + 000061 ' UESCAPE ' ! ' AS U&"d*0061t\+000061" UESCAPE ' * '; SELECT U&' '\' UESCAPE '!' AS "tricky"; SELECT 'tricky' AS U & "\" UESCAPE '!'; SELECT U&'wrong: \061'; SELECT U&'wrong: \+0061'; SELECT U&'wrong: +0061' UESCAPE '+'; RESET standard_conforming_strings; -- bytea SET bytea_output TO hex; SELECT E'\\xDeAdBeEf'::bytea; SELECT E'\\x De Ad Be Ef '::bytea; SELECT E'\\xDeAdBeE'::bytea; SELECT E'\\xDeAdBeEx'::bytea; SELECT E'\\xDe00BeEf'::bytea; SELECT E'DeAdBeEf'::bytea; SELECT E'De\\000dBeEf'::bytea; SELECT E'De\123dBeEf'::bytea; SELECT E'De\\123dBeEf'::bytea; SELECT E'De\\678dBeEf'::bytea; SET bytea_output TO escape; SELECT E'\\xDeAdBeEf'::bytea; SELECT E'\\x De Ad Be Ef '::bytea; SELECT E'\\xDe00BeEf'::bytea; SELECT E'DeAdBeEf'::bytea; SELECT E'De\\000dBeEf'::bytea; SELECT E'De\\123dBeEf'::bytea; -- -- test conversions between various string types -- E021-10 implicit casting among the character data types -- SELECT CAST(f1 AS text) AS " text(char) " FROM CHAR_TBL; SELECT CAST(f1 AS text) AS " text(varchar) " FROM VARCHAR_TBL; SELECT CAST(name 'namefield' AS text) AS " text(name) "; -- since this is an explicit cast, it should truncate w/o error: SELECT CAST(f1 AS char(10)) AS " char(text) " FROM TEXT_TBL; -- note: implicit-cast case is tested in char.sql SELECT CAST(f1 AS char(20)) AS " char(text) " FROM TEXT_TBL; SELECT CAST(f1 AS char(10)) AS " char(varchar) " FROM VARCHAR_TBL; SELECT CAST(name 'namefield' AS char(10)) AS " char(name) "; SELECT CAST(f1 AS varchar) AS " varchar(text) " FROM TEXT_TBL; SELECT CAST(f1 AS varchar) AS " varchar(char) " FROM CHAR_TBL; SELECT CAST(name 'namefield' AS varchar) AS " varchar(name) "; -- -- test SQL string functions -- E### and T### are feature reference numbers from SQL99 -- -- E021-09 trim function SELECT TRIM(BOTH FROM ' bunch o blanks ') = 'bunch o blanks' AS " bunch o blanks "; SELECT TRIM(LEADING FROM ' bunch o blanks ') = 'bunch o blanks ' AS " bunch o blanks "; SELECT TRIM(TRAILING FROM ' bunch o blanks ') = ' bunch o blanks' AS " bunch o blanks "; SELECT TRIM(BOTH 'x' FROM 'xxxxxsome Xsxxxxx') = 'some Xs' AS " SOME Xs "; -- E021-06 substring expression SELECT SUBSTRING('1234567890' FROM 3) = '34567890' AS " 34567890 "; SELECT SUBSTRING('1234567890' FROM 4 FOR 3) = '456' AS " 456 "; -- T581 regular expression substring (with SQL99's bizarre regexp syntax) SELECT SUBSTRING('abcdefg' FROM 'a#" (b_d) # "%' FOR '#') AS " bcd "; -- No match should return NULL SELECT SUBSTRING('abcdefg' FROM '#" (b_d) # "%' FOR '#') IS NULL AS " TRUE "; -- Null inputs should return NULL SELECT SUBSTRING('abcdefg' FROM '(b|c)' FOR NULL) IS NULL AS " TRUE "; SELECT SUBSTRING(NULL FROM '(b|c)' FOR '#') IS NULL AS " TRUE "; SELECT SUBSTRING('abcdefg' FROM NULL FOR '#') IS NULL AS " TRUE "; -- PostgreSQL extension to allow omitting the escape character; -- here the regexp is taken as Posix syntax SELECT SUBSTRING('abcdefg' FROM 'c.e') AS " cde "; -- With a parenthesized subexpression, return only what matches the subexpr SELECT SUBSTRING('abcdefg' FROM 'b(.*)f') AS " cde "; -- PostgreSQL extension to allow using back reference in replace string; SELECT regexp_replace('1112223333', E'(\\d{3})(\\d{3})(\\d{4})', E'(\\1) \\2-\\3'); SELECT regexp_replace('AAA BBB CCC ', E'\\s+', ' ', 'g'); SELECT regexp_replace('AAA', '^|$', 'Z', 'g'); SELECT regexp_replace('AAA aaa', 'A+', 'Z', 'gi'); -- invalid regexp option SELECT regexp_replace('AAA aaa', 'A+', 'Z', 'z'); -- set so we can tell NULL from empty string \pset null '\\N' -- return all matches from regexp SELECT regexp_matches('foobarbequebaz', $re$(bar)(beque)$re$); -- test case insensitive SELECT regexp_matches('foObARbEqUEbAz', $re$(bar)(beque)$re$, 'i'); -- global option - more than one match SELECT regexp_matches('foobarbequebazilbarfbonk', $re$(b[^b]+)(b[^b]+)$re$, 'g'); -- empty capture group (matched empty string) SELECT regexp_matches('foobarbequebaz', $re$(bar)(.*)(beque)$re$); -- no match SELECT regexp_matches('foobarbequebaz', $re$(bar)(.+)(beque)$re$); -- optional capture group did not match, null entry in array SELECT regexp_matches('foobarbequebaz', $re$(bar)(.+)?(beque)$re$); -- no capture groups SELECT regexp_matches('foobarbequebaz', $re$barbeque$re$); -- start/end-of-line matches are of zero length SELECT regexp_matches('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^', 'mg'); SELECT regexp_matches('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '$', 'mg'); SELECT regexp_matches('1' || chr(10) || '2' || chr(10) || '3' || chr(10) || '4' || chr(10), '^.?', 'mg'); SELECT regexp_matches(chr(10) || '1' || chr(10) || '2' || chr(10) || '3' || chr(10) || '4' || chr(10), '.?$', 'mg'); SELECT regexp_matches(chr(10) || '1' || chr(10) || '2' || chr(10) || '3' || chr(10) || '4', '.?$', 'mg'); -- give me errors SELECT regexp_matches('foobarbequebaz', $re$(bar)(beque)$re$, 'gz'); SELECT regexp_matches('foobarbequebaz', $re$(barbeque$re$); SELECT regexp_matches('foobarbequebaz', $re$(bar)(beque){2,1}$re$); -- split string on regexp SELECT foo, length(foo) FROM regexp_split_to_table('the quick brown fox jumps over the lazy dog', $re$\s+$re$) AS foo; SELECT regexp_split_to_array('the quick brown fox jumps over the lazy dog', $re$\s+$re$); SELECT foo, length(foo) FROM regexp_split_to_table('the quick brown fox jumps over the lazy dog', $re$\s*$re$) AS foo; SELECT regexp_split_to_array('the quick brown fox jumps over the lazy dog', $re$\s*$re$); SELECT foo, length(foo) FROM regexp_split_to_table('the quick brown fox jumps over the lazy dog', '') AS foo; SELECT regexp_split_to_array('the quick brown fox jumps over the lazy dog', ''); -- case insensitive SELECT foo, length(foo) FROM regexp_split_to_table('thE QUick bROWn FOx jUMPs ovEr The lazy dOG', 'e', 'i') AS foo; SELECT regexp_split_to_array('thE QUick bROWn FOx jUMPs ovEr The lazy dOG', 'e', 'i'); -- no match of pattern SELECT foo, length(foo) FROM regexp_split_to_table('the quick brown fox jumps over the lazy dog', 'nomatch') AS foo; SELECT regexp_split_to_array('the quick brown fox jumps over the lazy dog', 'nomatch'); -- some corner cases SELECT regexp_split_to_array('123456','1'); SELECT regexp_split_to_array('123456','6'); SELECT regexp_split_to_array('123456','.'); SELECT regexp_split_to_array('123456',''); SELECT regexp_split_to_array('123456','(?:)'); SELECT regexp_split_to_array('1',''); -- errors SELECT foo, length(foo) FROM regexp_split_to_table('thE QUick bROWn FOx jUMPs ovEr The lazy dOG', 'e', 'zippy') AS foo; SELECT regexp_split_to_array('thE QUick bROWn FOx jUMPs ovEr The lazy dOG', 'e', 'iz'); -- global option meaningless for regexp_split SELECT foo, length(foo) FROM regexp_split_to_table('thE QUick bROWn FOx jUMPs ovEr The lazy dOG', 'e', 'g') AS foo; SELECT regexp_split_to_array('thE QUick bROWn FOx jUMPs ovEr The lazy dOG', 'e', 'g'); -- change NULL-display back \pset null '' -- E021-11 position expression SELECT POSITION('4' IN '1234567890') = '4' AS " 4 "; SELECT POSITION('5' IN '1234567890') = '5' AS " 5 "; -- T312 character overlay function SELECT OVERLAY('abcdef' PLACING '45' FROM 4) AS " abc45f "; SELECT OVERLAY('yabadoo' PLACING 'daba' FROM 5) AS " yabadaba "; SELECT OVERLAY('yabadoo' PLACING 'daba' FROM 5 FOR 0) AS " yabadabadoo "; SELECT OVERLAY('babosa' PLACING 'ubb' FROM 2 FOR 4) AS " bubba "; -- -- test LIKE -- Be sure to form every test as a LIKE/NOT LIKE pair. -- -- simplest examples -- E061-04 like predicate SELECT 'hawkeye' LIKE 'h%' AS " TRUE "; SELECT 'hawkeye' NOT LIKE 'h%' AS " FALSE "; SELECT 'hawkeye' LIKE 'H%' AS " FALSE "; SELECT 'hawkeye' NOT LIKE 'H%' AS " TRUE "; SELECT 'hawkeye' LIKE 'indio%' AS " FALSE "; SELECT 'hawkeye' NOT LIKE 'indio%' AS " TRUE "; SELECT 'hawkeye' LIKE 'h%eye' AS " TRUE "; SELECT 'hawkeye' NOT LIKE 'h%eye' AS " FALSE "; SELECT 'indio' LIKE '_ndio' AS " TRUE "; SELECT 'indio' NOT LIKE '_ndio' AS " FALSE "; SELECT 'indio' LIKE 'in__o' AS " TRUE "; SELECT 'indio' NOT LIKE 'in__o' AS " FALSE "; SELECT 'indio' LIKE 'in_o' AS " FALSE "; SELECT 'indio' NOT LIKE 'in_o' AS " TRUE "; -- unused escape character SELECT 'hawkeye' LIKE 'h%' ESCAPE '#' AS " TRUE "; SELECT 'hawkeye' NOT LIKE 'h%' ESCAPE '#' AS " FALSE "; SELECT 'indio' LIKE 'ind_o' ESCAPE '$' AS " TRUE "; SELECT 'indio' NOT LIKE 'ind_o' ESCAPE '$' AS " FALSE "; -- escape character -- E061-05 like predicate with escape clause SELECT 'h%' LIKE 'h#%' ESCAPE '#' AS " TRUE "; SELECT 'h%' NOT LIKE 'h#%' ESCAPE '#' AS " FALSE "; SELECT 'h%wkeye' LIKE 'h#%' ESCAPE '#' AS " FALSE "; SELECT 'h%wkeye' NOT LIKE 'h#%' ESCAPE '#' AS " TRUE "; SELECT 'h%wkeye' LIKE 'h#%%' ESCAPE '#' AS " TRUE "; SELECT 'h%wkeye' NOT LIKE 'h#%%' ESCAPE '#' AS " FALSE "; SELECT 'h%awkeye' LIKE 'h#%a%k%e' ESCAPE '#' AS " TRUE "; SELECT 'h%awkeye' NOT LIKE 'h#%a%k%e' ESCAPE '#' AS " FALSE "; SELECT 'indio' LIKE '_ndio' ESCAPE '$' AS " TRUE "; SELECT 'indio' NOT LIKE '_ndio' ESCAPE '$' AS " FALSE "; SELECT 'i_dio' LIKE 'i$_d_o' ESCAPE '$' AS " TRUE "; SELECT 'i_dio' NOT LIKE 'i$_d_o' ESCAPE '$' AS " FALSE "; SELECT 'i_dio' LIKE 'i$_nd_o' ESCAPE '$' AS " FALSE "; SELECT 'i_dio' NOT LIKE 'i$_nd_o' ESCAPE '$' AS " TRUE "; SELECT 'i_dio' LIKE 'i$_d%o' ESCAPE '$' AS " TRUE "; SELECT 'i_dio' NOT LIKE 'i$_d%o' ESCAPE '$' AS " FALSE "; -- escape character same as pattern character SELECT 'maca' LIKE 'm%aca' ESCAPE '%' AS " TRUE "; SELECT 'maca' NOT LIKE 'm%aca' ESCAPE '%' AS " FALSE "; SELECT 'ma%a' LIKE 'm%a%%a' ESCAPE '%' AS " TRUE "; SELECT 'ma%a' NOT LIKE 'm%a%%a' ESCAPE '%' AS " FALSE "; SELECT 'bear' LIKE 'b_ear' ESCAPE '_' AS " TRUE "; SELECT 'bear' NOT LIKE 'b_ear' ESCAPE '_' AS " FALSE "; SELECT 'be_r' LIKE 'b_e__r' ESCAPE '_' AS " TRUE "; SELECT 'be_r' NOT LIKE 'b_e__r' ESCAPE '_' AS " FALSE "; SELECT 'be_r' LIKE '__e__r' ESCAPE '_' AS " FALSE "; SELECT 'be_r' NOT LIKE '__e__r' ESCAPE '_' AS " TRUE "; -- -- test ILIKE (case-insensitive LIKE) -- Be sure to form every test as an ILIKE/NOT ILIKE pair. -- SELECT 'hawkeye' ILIKE 'h%' AS " TRUE "; SELECT 'hawkeye' NOT ILIKE 'h%' AS " FALSE "; SELECT 'hawkeye' ILIKE 'H%' AS " TRUE "; SELECT 'hawkeye' NOT ILIKE 'H%' AS " FALSE "; SELECT 'hawkeye' ILIKE 'H%Eye' AS " TRUE "; SELECT 'hawkeye' NOT ILIKE 'H%Eye' AS " FALSE "; SELECT 'Hawkeye' ILIKE 'h%' AS " TRUE "; SELECT 'Hawkeye' NOT ILIKE 'h%' AS " FALSE "; -- -- test %/_ combination cases, cf bugs #4821 and #5478 -- SELECT 'foo' LIKE '_%' as t, 'f' LIKE '_%' as t, '' LIKE '_%' as f; SELECT 'foo' LIKE '%_' as t, 'f' LIKE '%_' as t, '' LIKE '%_' as f; SELECT 'foo' LIKE '__%' as t, 'foo' LIKE '___%' as t, 'foo' LIKE '____%' as f; SELECT 'foo' LIKE '%__' as t, 'foo' LIKE '%___' as t, 'foo' LIKE '%____' as f; SELECT 'jack' LIKE '%____%' AS t; -- -- basic tests of LIKE with indexes -- CREATE TABLE texttest (a text PRIMARY KEY, b int); SELECT * FROM texttest WHERE a LIKE '%1%'; CREATE TABLE byteatest (a bytea PRIMARY KEY, b int); SELECT * FROM byteatest WHERE a LIKE '%1%'; DROP TABLE texttest, byteatest; -- -- test implicit type conversion -- -- E021-07 character concatenation SELECT 'unknown' || ' and unknown' AS " || unknown types "; SELECT text 'text' || ' and unknown' AS " || text TO unknown TYPE "; SELECT char(20) 'characters' || ' and text' AS " || char TO unknown TYPE "; SELECT text 'text' || char(20) ' and characters' AS " || text TO char "; SELECT text 'text' || varchar ' and varchar' AS " || text TO varchar "; -- -- test substr with toasted text values -- CREATE TABLE toasttest(f1 text); insert into toasttest values(repeat('1234567890',10000)); insert into toasttest values(repeat('1234567890',10000)); -- -- Ensure that some values are uncompressed, to test the faster substring -- operation used in that case -- alter table toasttest alter column f1 set storage external; insert into toasttest values(repeat('1234567890',10000)); insert into toasttest values(repeat('1234567890',10000)); -- If the starting position is zero or less, then return from the start of the string -- adjusting the length to be consistent with the " negative START " per SQL. SELECT substr(f1, -1, 5) from toasttest; -- If the length is less than zero, an ERROR is thrown. SELECT substr(f1, 5, -1) from toasttest; -- If no third argument (length) is provided, the length to the end of the -- string is assumed. SELECT substr(f1, 99995) from toasttest; -- If start plus length is > string length, the result is truncated to -- string length SELECT substr(f1, 99995, 10) from toasttest; TRUNCATE TABLE toasttest; INSERT INTO toasttest values (repeat('1234567890',300)); INSERT INTO toasttest values (repeat('1234567890',300)); INSERT INTO toasttest values (repeat('1234567890',300)); INSERT INTO toasttest values (repeat('1234567890',300)); -- expect >0 blocks SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty FROM pg_class where relname = 'toasttest'; TRUNCATE TABLE toasttest; ALTER TABLE toasttest set (toast_tuple_target = 4080); INSERT INTO toasttest values (repeat('1234567890',300)); INSERT INTO toasttest values (repeat('1234567890',300)); INSERT INTO toasttest values (repeat('1234567890',300)); INSERT INTO toasttest values (repeat('1234567890',300)); -- expect 0 blocks SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty FROM pg_class where relname = 'toasttest'; DROP TABLE toasttest; -- -- test substr with toasted bytea values -- CREATE TABLE toasttest(f1 bytea); insert into toasttest values(decode(repeat('1234567890',10000),'escape')); insert into toasttest values(decode(repeat('1234567890',10000),'escape')); -- -- Ensure that some values are uncompressed, to test the faster substring -- operation used in that case -- alter table toasttest alter column f1 set storage external; insert into toasttest values(decode(repeat('1234567890',10000),'escape')); insert into toasttest values(decode(repeat('1234567890',10000),'escape')); -- If the starting position is zero or less, then return from the start of the string -- adjusting the length to be consistent with the " negative START " per SQL. SELECT substr(f1, -1, 5) from toasttest; -- If the length is less than zero, an ERROR is thrown. SELECT substr(f1, 5, -1) from toasttest; -- If no third argument (length) is provided, the length to the end of the -- string is assumed. SELECT substr(f1, 99995) from toasttest; -- If start plus length is > string length, the result is truncated to -- string length SELECT substr(f1, 99995, 10) from toasttest; DROP TABLE toasttest; -- test internally compressing datums -- this tests compressing a datum to a very small size which exercises a -- corner case in packed-varlena handling: even though small, the compressed -- datum must be given a 4-byte header because there are no bits to indicate -- compression in a 1-byte header CREATE TABLE toasttest (c char(4096)); INSERT INTO toasttest VALUES('x'); SELECT length(c), c::text FROM toasttest; SELECT c FROM toasttest; DROP TABLE toasttest; -- -- test length -- SELECT length('abcdef') AS " length_6 "; -- -- test strpos -- SELECT strpos('abcdef', 'cd') AS " pos_3 "; SELECT strpos('abcdef', 'xy') AS " pos_0 "; -- -- test replace -- SELECT replace('abcdef', 'de', '45') AS " abc45f "; SELECT replace('yabadabadoo', 'ba', '123') AS " ya123da123doo "; SELECT replace('yabadoo', 'bad', '') AS " yaoo "; -- -- test split_part -- select split_part('joeuser@mydatabase','@',0) AS " an error "; select split_part('joeuser@mydatabase','@',1) AS " joeuser "; select split_part('joeuser@mydatabase','@',2) AS " mydatabase "; select split_part('joeuser@mydatabase','@',3) AS " empty string "; select split_part('@joeuser@mydatabase@','@',2) AS " joeuser "; -- -- test to_hex -- select to_hex(256*256*256 - 1) AS " ffffff "; select to_hex(256::bigint*256::bigint*256::bigint*256::bigint - 1) AS " ffffffff "; -- -- MD5 test suite - from IETF RFC 1321 -- (see: ftp://ftp.rfc-editor.org/in-notes/rfc1321.txt) -- select md5('') = 'd41d8cd98f00b204e9800998ecf8427e' AS " TRUE "; select md5('a') = '0cc175b9c0f1b6a831c399e269772661' AS " TRUE "; select md5('abc') = '900150983cd24fb0d6963f7d28e17f72' AS " TRUE "; select md5('message digest') = 'f96b697d7cb7938d525a2f31aaf161d0' AS " TRUE "; select md5('abcdefghijklmnopqrstuvwxyz') = 'c3fcd3d76192e4007dfb496cca67e13b' AS " TRUE "; select md5('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') = 'd174ab98d277d9f5a5611c2c9f419d9f' AS " TRUE "; select md5('12345678901234567890123456789012345678901234567890123456789012345678901234567890') = '57edf4a22be3c955ac49da2e2107b67a' AS " TRUE "; select md5(''::bytea) = 'd41d8cd98f00b204e9800998ecf8427e' AS " TRUE "; select md5('a'::bytea) = '0cc175b9c0f1b6a831c399e269772661' AS " TRUE "; select md5('abc'::bytea) = '900150983cd24fb0d6963f7d28e17f72' AS " TRUE "; select md5('message digest'::bytea) = 'f96b697d7cb7938d525a2f31aaf161d0' AS " TRUE "; select md5('abcdefghijklmnopqrstuvwxyz'::bytea) = 'c3fcd3d76192e4007dfb496cca67e13b' AS " TRUE "; select md5('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'::bytea) = 'd174ab98d277d9f5a5611c2c9f419d9f' AS " TRUE "; select md5('12345678901234567890123456789012345678901234567890123456789012345678901234567890'::bytea) = '57edf4a22be3c955ac49da2e2107b67a' AS " TRUE; -- -- SHA-2 -- SET bytea_output TO hex; SELECT sha224 (''); SELECT sha224 ('The quick brown fox jumps over the lazy dog.'); SELECT sha256 (''); SELECT sha256 ('The quick brown fox jumps over the lazy dog.'); SELECT sha384 (''); SELECT sha384 ('The quick brown fox jumps over the lazy dog.'); SELECT sha512 (''); SELECT sha512 ('The quick brown fox jumps over the lazy dog.'); -- -- test behavior of escape_string_warning and standard_conforming_strings options -- SET escape_string_warning = OFF; SET standard_conforming_strings = OFF; SHOW escape_string_warning; SHOW standard_conforming_strings; SET escape_string_warning = ON; SET standard_conforming_strings = ON; SHOW escape_string_warning; SHOW standard_conforming_strings; SELECT 'a\bcd' AS f1, 'a\b''cd' AS f2, 'a\b''''cd' AS f3, 'abcd\' as f4, ' 'ab\' 'cd' AS f5, '\\' as f6; set standard_conforming_strings = off; select ' a bcd ' as f1, ' a b\'cd ' as f2, ' a 'b\' '' cd ' as f3, ' abcd '\' AS f4, 'ab\\\'cd' AS f5, '\\\\' as f6; set escape_string_warning = off; set standard_conforming_strings = on; select ' a bcd ' as f1, ' a b '' cd ' as f2, ' a b '''' cd ' as f3, ' 'abcd\' AS f4, 'ab\'' cd ' as f5, ' '\' AS f6; SET standard_conforming_strings = OFF; SELECT 'a\\bcd' AS f1, 'a\\b\'cd' AS f2, 'a\\b\'''cd' AS f3, 'abcd\\' as f4, ' ab \'cd ' as f5, ' '\' AS f6; -- -- Additional string functions -- SET bytea_output TO escape; SELECT initcap('hi THOMAS'); SELECT lpad('hi', 5, 'xy'); SELECT lpad('hi', 5); SELECT lpad('hi', - 5, 'xy'); SELECT lpad('hello', 2); SELECT lpad('hi', 5, ''); SELECT rpad('hi', 5, 'xy'); SELECT rpad('hi', 5); SELECT rpad('hi', - 5, 'xy'); SELECT rpad('hello', 2); SELECT rpad('hi', 5, ''); SELECT ltrim('zzzytrim', 'xyz'); SELECT translate('', '14', 'ax'); SELECT translate('12345', '14', 'ax'); SELECT ascii('x'); SELECT ascii(''); SELECT chr(65); SELECT chr(0); SELECT repeat('Pg', 4); SELECT repeat('Pg', - 4); SELECT trim(E'\\000'::bytea FROM E'\\000Tom\\000'::bytea); SELECT btrim(E'\\000trim\\000'::bytea, E'\\000'::bytea); SELECT btrim(''::bytea, E'\\000'::bytea); SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea PLACING E'Th\\001omas'::bytea FROM 2), 'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea PLACING E'\\002\\003'::bytea FROM 8), 'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea PLACING E'\\002\\003'::bytea FROM 5 FOR 3), 'escape'); pgFormatter-4.2/t/pg-test-files/expected/subscription.sql000066400000000000000000000106611361326045100236520ustar00rootroot00000000000000-- -- SUBSCRIPTION -- CREATE ROLE regress_subscription_user LOGIN SUPERUSER; CREATE ROLE regress_subscription_user2; CREATE ROLE regress_subscription_user_dummy LOGIN NOSUPERUSER; SET SESSION AUTHORIZATION 'regress_subscription_user'; -- fail - no publications CREATE SUBSCRIPTION testsub CONNECTION 'foo'; -- fail - no connection CREATE SUBSCRIPTION testsub PUBLICATION foo; -- fail - cannot do CREATE SUBSCRIPTION CREATE SLOT inside transaction block BEGIN; CREATE SUBSCRIPTION testsub CONNECTION 'testconn' PUBLICATION testpub WITH ( create_slot ); COMMIT; -- fail - invalid connection string CREATE SUBSCRIPTION testsub CONNECTION 'testconn' PUBLICATION testpub; -- fail - duplicate publications CREATE SUBSCRIPTION testsub CONNECTION 'dbname=doesnotexist' PUBLICATION foo, testpub, foo WITH ( connect = FALSE ); -- ok CREATE SUBSCRIPTION testsub CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH ( connect = FALSE ); COMMENT ON SUBSCRIPTION testsub IS 'test subscription'; SELECT obj_description(s.oid, 'pg_subscription') FROM pg_subscription s; -- fail - name already exists CREATE SUBSCRIPTION testsub CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH ( connect = FALSE ); -- fail - must be superuser SET SESSION AUTHORIZATION 'regress_subscription_user2'; CREATE SUBSCRIPTION testsub2 CONNECTION 'dbname=doesnotexist' PUBLICATION foo WITH ( connect = FALSE ); SET SESSION AUTHORIZATION 'regress_subscription_user'; -- fail - invalid option combinations CREATE SUBSCRIPTION testsub2 CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH ( connect = FALSE, copy_data = TRUE ); CREATE SUBSCRIPTION testsub2 CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH ( connect = FALSE, enabled = TRUE ); CREATE SUBSCRIPTION testsub2 CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH ( connect = FALSE, create_slot = TRUE ); CREATE SUBSCRIPTION testsub2 CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH ( slot_name = NONE, enabled = TRUE ); CREATE SUBSCRIPTION testsub2 CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH ( slot_name = NONE, create_slot = TRUE ); CREATE SUBSCRIPTION testsub2 CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH ( slot_name = NONE ); CREATE SUBSCRIPTION testsub2 CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH ( slot_name = NONE, enabled = FALSE ); CREATE SUBSCRIPTION testsub2 CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH ( slot_name = NONE, create_slot = FALSE ); -- ok - with slot_name = NONE CREATE SUBSCRIPTION testsub3 CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH ( slot_name = NONE, connect = FALSE ); -- fail ALTER SUBSCRIPTION testsub3 ENABLE; ALTER SUBSCRIPTION testsub3 REFRESH PUBLICATION; DROP SUBSCRIPTION testsub3; -- fail - invalid connection string ALTER SUBSCRIPTION testsub CONNECTION 'foobar'; \dRs+ ALTER SUBSCRIPTION testsub SET PUBLICATION testpub2, testpub3 WITH ( REFRESH = FALSE); ALTER SUBSCRIPTION testsub CONNECTION 'dbname=doesnotexist2'; ALTER SUBSCRIPTION testsub SET (slot_name = 'newname'); -- fail ALTER SUBSCRIPTION doesnotexist CONNECTION 'dbname=doesnotexist2'; ALTER SUBSCRIPTION testsub SET (create_slot = FALSE); \dRs+ BEGIN; ALTER SUBSCRIPTION testsub ENABLE; \dRs ALTER SUBSCRIPTION testsub DISABLE; \dRs COMMIT; -- fail - must be owner of subscription SET ROLE regress_subscription_user_dummy; ALTER SUBSCRIPTION testsub RENAME TO testsub_dummy; RESET ROLE; ALTER SUBSCRIPTION testsub RENAME TO testsub_foo; ALTER SUBSCRIPTION testsub_foo SET (synchronous_commit = local); ALTER SUBSCRIPTION testsub_foo SET (synchronous_commit = foobar); \dRs+ -- rename back to keep the rest simple ALTER SUBSCRIPTION testsub_foo RENAME TO testsub; -- fail - new owner must be superuser ALTER SUBSCRIPTION testsub OWNER TO regress_subscription_user2; ALTER ROLE regress_subscription_user2 SUPERUSER; -- now it works ALTER SUBSCRIPTION testsub OWNER TO regress_subscription_user2; -- fail - cannot do DROP SUBSCRIPTION inside transaction block with slot name BEGIN; DROP SUBSCRIPTION testsub; COMMIT; ALTER SUBSCRIPTION testsub SET (slot_name = NONE); -- now it works BEGIN; DROP SUBSCRIPTION testsub; COMMIT; DROP SUBSCRIPTION IF EXISTS testsub; DROP SUBSCRIPTION testsub; -- fail RESET SESSION AUTHORIZATION; DROP ROLE regress_subscription_user; DROP ROLE regress_subscription_user2; DROP ROLE regress_subscription_user_dummy; pgFormatter-4.2/t/pg-test-files/expected/subselect.sql000066400000000000000000000734221361326045100231230ustar00rootroot00000000000000-- -- SUBSELECT -- SELECT 1 AS one WHERE 1 IN ( SELECT 1); SELECT 1 AS zero WHERE 1 NOT IN ( SELECT 1); SELECT 1 AS zero WHERE 1 IN ( SELECT 2); -- Check grammar's handling of extra parens in assorted contexts SELECT * FROM ( SELECT 1 AS x) ss; SELECT * FROM (( SELECT 1 AS x)) ss; ( SELECT 2) UNION SELECT 2; (( SELECT 2)) UNION SELECT 2; SELECT (( SELECT 2) UNION SELECT 2); SELECT ((( SELECT 2)) UNION SELECT 2); SELECT ( SELECT ARRAY[1, 2, 3])[1]; SELECT (( SELECT ARRAY[1, 2, 3]))[2]; SELECT ((( SELECT ARRAY[1, 2, 3])))[3]; -- Set up some simple test tables CREATE TABLE SUBSELECT_TBL ( f1 integer, f2 integer, f3 float ); INSERT INTO SUBSELECT_TBL VALUES (1, 2, 3); INSERT INTO SUBSELECT_TBL VALUES (2, 3, 4); INSERT INTO SUBSELECT_TBL VALUES (3, 4, 5); INSERT INTO SUBSELECT_TBL VALUES (1, 1, 1); INSERT INTO SUBSELECT_TBL VALUES (2, 2, 2); INSERT INTO SUBSELECT_TBL VALUES (3, 3, 3); INSERT INTO SUBSELECT_TBL VALUES (6, 7, 8); INSERT INTO SUBSELECT_TBL VALUES (8, 9, NULL); SELECT '' AS eight, * FROM SUBSELECT_TBL; -- Uncorrelated subselects SELECT '' AS two, f1 AS "Constant Select" FROM SUBSELECT_TBL WHERE f1 IN ( SELECT 1); SELECT '' AS six, f1 AS "Uncorrelated Field" FROM SUBSELECT_TBL WHERE f1 IN ( SELECT f2 FROM SUBSELECT_TBL); SELECT '' AS six, f1 AS "Uncorrelated Field" FROM SUBSELECT_TBL WHERE f1 IN ( SELECT f2 FROM SUBSELECT_TBL WHERE f2 IN ( SELECT f1 FROM SUBSELECT_TBL)); SELECT '' AS three, f1, f2 FROM SUBSELECT_TBL WHERE (f1, f2) NOT IN ( SELECT f2, CAST(f3 AS int4) FROM SUBSELECT_TBL WHERE f3 IS NOT NULL); -- Correlated subselects SELECT '' AS six, f1 AS "Correlated Field", f2 AS "Second Field" FROM SUBSELECT_TBL upper WHERE f1 IN ( SELECT f2 FROM SUBSELECT_TBL WHERE f1 = upper.f1); SELECT '' AS six, f1 AS "Correlated Field", f3 AS "Second Field" FROM SUBSELECT_TBL upper WHERE f1 IN ( SELECT f2 FROM SUBSELECT_TBL WHERE CAST(upper.f2 AS float) = f3); SELECT '' AS six, f1 AS "Correlated Field", f3 AS "Second Field" FROM SUBSELECT_TBL upper WHERE f3 IN ( SELECT upper.f1 + f2 FROM SUBSELECT_TBL WHERE f2 = CAST(f3 AS integer)); SELECT '' AS five, f1 AS "Correlated Field" FROM SUBSELECT_TBL WHERE (f1, f2) IN ( SELECT f2, CAST(f3 AS int4) FROM SUBSELECT_TBL WHERE f3 IS NOT NULL); -- -- Use some existing tables in the regression test -- SELECT '' AS eight, ss.f1 AS "Correlated Field", ss.f3 AS "Second Field" FROM SUBSELECT_TBL ss WHERE f1 NOT IN ( SELECT f1 + 1 FROM INT4_TBL WHERE f1 != ss.f1 AND f1 < 2147483647); SELECT q1, float8(count(*)) / ( SELECT count(*) FROM int8_tbl) FROM int8_tbl GROUP BY q1 ORDER BY q1; -- Unspecified-type literals in output columns should resolve as text SELECT *, pg_typeof(f1) FROM ( SELECT 'foo' AS f1 FROM generate_series(1, 3)) ss ORDER BY 1; -- ... unless there's context to suggest differently EXPLAIN ( VERBOSE, COSTS OFF ) SELECT '42' UNION ALL SELECT '43'; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT '42' UNION ALL SELECT 43; -- check materialization of an initplan reference (bug #14524) EXPLAIN ( VERBOSE, COSTS OFF ) SELECT 1 = ALL ( SELECT ( SELECT 1)); SELECT 1 = ALL ( SELECT ( SELECT 1)); -- -- Check EXISTS simplification with LIMIT -- EXPLAIN ( COSTS OFF ) SELECT * FROM int4_tbl o WHERE EXISTS ( SELECT 1 FROM int4_tbl i WHERE i.f1 = o.f1 LIMIT NULL); EXPLAIN ( COSTS OFF ) SELECT * FROM int4_tbl o WHERE NOT EXISTS ( SELECT 1 FROM int4_tbl i WHERE i.f1 = o.f1 LIMIT 1); EXPLAIN ( COSTS OFF ) SELECT * FROM int4_tbl o WHERE EXISTS ( SELECT 1 FROM int4_tbl i WHERE i.f1 = o.f1 LIMIT 0); -- -- Test cases to catch unpleasant interactions between IN-join processing -- and subquery pullup. -- SELECT count(*) FROM ( SELECT 1 FROM tenk1 a WHERE unique1 IN ( SELECT hundred FROM tenk1 b)) ss; SELECT count(DISTINCT ss.ten) FROM ( SELECT ten FROM tenk1 a WHERE unique1 IN ( SELECT hundred FROM tenk1 b)) ss; SELECT count(*) FROM ( SELECT 1 FROM tenk1 a WHERE unique1 IN ( SELECT DISTINCT hundred FROM tenk1 b)) ss; SELECT count(DISTINCT ss.ten) FROM ( SELECT ten FROM tenk1 a WHERE unique1 IN ( SELECT DISTINCT hundred FROM tenk1 b)) ss; -- -- Test cases to check for overenthusiastic optimization of -- "IN (SELECT DISTINCT ...)" and related cases. Per example from -- Luca Pireddu and Michael Fuhr. -- CREATE TEMP TABLE foo ( id integer ); CREATE TEMP TABLE bar ( id1 integer, id2 integer ); INSERT INTO foo VALUES (1); INSERT INTO bar VALUES (1, 1); INSERT INTO bar VALUES (2, 2); INSERT INTO bar VALUES (3, 1); -- These cases require an extra level of distinct-ing above subquery s SELECT * FROM foo WHERE id IN ( SELECT id2 FROM ( SELECT DISTINCT id1, id2 FROM bar) AS s); SELECT * FROM foo WHERE id IN ( SELECT id2 FROM ( SELECT id1, id2 FROM bar GROUP BY id1, id2) AS s); SELECT * FROM foo WHERE id IN ( SELECT id2 FROM ( SELECT id1, id2 FROM bar UNION SELECT id1, id2 FROM bar) AS s); -- These cases do not SELECT * FROM foo WHERE id IN ( SELECT id2 FROM ( SELECT DISTINCT ON (id2) id1, id2 FROM bar) AS s); SELECT * FROM foo WHERE id IN ( SELECT id2 FROM ( SELECT id2 FROM bar GROUP BY id2) AS s); SELECT * FROM foo WHERE id IN ( SELECT id2 FROM ( SELECT id2 FROM bar UNION SELECT id2 FROM bar) AS s); -- -- Test case to catch problems with multiply nested sub-SELECTs not getting -- recalculated properly. Per bug report from Didier Moens. -- CREATE TABLE orderstest ( approver_ref integer, po_ref integer, ordercanceled boolean ); INSERT INTO orderstest VALUES (1, 1, FALSE); INSERT INTO orderstest VALUES (66, 5, FALSE); INSERT INTO orderstest VALUES (66, 6, FALSE); INSERT INTO orderstest VALUES (66, 7, FALSE); INSERT INTO orderstest VALUES (66, 1, TRUE); INSERT INTO orderstest VALUES (66, 8, FALSE); INSERT INTO orderstest VALUES (66, 1, FALSE); INSERT INTO orderstest VALUES (77, 1, FALSE); INSERT INTO orderstest VALUES (1, 1, FALSE); INSERT INTO orderstest VALUES (66, 1, FALSE); INSERT INTO orderstest VALUES (1, 1, FALSE); CREATE VIEW orders_view AS SELECT *, ( SELECT CASE WHEN ord.approver_ref = 1 THEN '---' ELSE 'Approved' END) AS "Approved", ( SELECT CASE WHEN ord.ordercanceled THEN 'Canceled' ELSE ( SELECT CASE WHEN ord.po_ref = 1 THEN ( SELECT CASE WHEN ord.approver_ref = 1 THEN '---' ELSE 'Approved' END) ELSE 'PO' END) END) AS "Status", ( CASE WHEN ord.ordercanceled THEN 'Canceled' ELSE ( CASE WHEN ord.po_ref = 1 THEN ( CASE WHEN ord.approver_ref = 1 THEN '---' ELSE 'Approved' END) ELSE 'PO' END) END) AS "Status_OK" FROM orderstest ord; SELECT * FROM orders_view; DROP TABLE orderstest CASCADE; -- -- Test cases to catch situations where rule rewriter fails to propagate -- hasSubLinks flag correctly. Per example from Kyle Bateman. -- CREATE temp TABLE parts ( partnum text, COST float8 ); CREATE temp TABLE shipped ( ttype char(2), ordnum int4, partnum text, value float8 ); CREATE temp VIEW shipped_view AS SELECT * FROM shipped WHERE ttype = 'wt'; CREATE RULE shipped_view_insert AS ON INSERT TO shipped_view DO INSTEAD INSERT INTO shipped VALUES ('wt', new.ordnum, new.partnum, new.value); INSERT INTO parts (partnum, COST) VALUES (1, 1234.56); INSERT INTO shipped_view (ordnum, partnum, value) VALUES (0, 1, ( SELECT COST FROM parts WHERE partnum = '1')); SELECT * FROM shipped_view; CREATE RULE shipped_view_update AS ON UPDATE TO shipped_view DO INSTEAD UPDATE shipped SET partnum = new.partnum, value = new.value WHERE ttype = new.ttype AND ordnum = new.ordnum; UPDATE shipped_view SET value = 11 FROM int4_tbl a JOIN int4_tbl b ON (a.f1 = ( SELECT f1 FROM int4_tbl c WHERE c.f1 = b.f1)) WHERE ordnum = a.f1; SELECT * FROM shipped_view; SELECT f1, ss1 AS relabel FROM ( SELECT *, ( SELECT sum(f1) FROM int4_tbl b WHERE f1 >= a.f1) AS ss1 FROM int4_tbl a) ss; -- -- Test cases involving PARAM_EXEC parameters and min/max index optimizations. -- Per bug report from David Sanchez i Gregori. -- SELECT * FROM ( SELECT max(unique1) FROM tenk1 AS a WHERE EXISTS ( SELECT 1 FROM tenk1 AS b WHERE b.thousand = a.unique2)) ss; SELECT * FROM ( SELECT min(unique1) FROM tenk1 AS a WHERE NOT EXISTS ( SELECT 1 FROM tenk1 AS b WHERE b.unique2 = 10000)) ss; -- -- Test that an IN implemented using a UniquePath does unique-ification -- with the right semantics, as per bug #4113. (Unfortunately we have -- no simple way to ensure that this test case actually chooses that type -- of plan, but it does in releases 7.4-8.3. Note that an ordering difference -- here might mean that some other plan type is being used, rendering the test -- pointless.) -- CREATE temp TABLE numeric_table ( num_col numeric ); INSERT INTO numeric_table VALUES (1), (1.000000000000000000001), (2), (3); CREATE temp TABLE float_table ( float_col float8 ); INSERT INTO float_table VALUES (1), (2), (3); SELECT * FROM float_table WHERE float_col IN ( SELECT num_col FROM numeric_table); SELECT * FROM numeric_table WHERE num_col IN ( SELECT float_col FROM float_table); -- -- Test case for bug #4290: bogus calculation of subplan param sets -- CREATE temp TABLE ta ( id int PRIMARY KEY, val int ); INSERT INTO ta VALUES (1, 1); INSERT INTO ta VALUES (2, 2); CREATE temp TABLE tb ( id int PRIMARY KEY, aval int ); INSERT INTO tb VALUES (1, 1); INSERT INTO tb VALUES (2, 1); INSERT INTO tb VALUES (3, 2); INSERT INTO tb VALUES (4, 2); CREATE temp TABLE tc ( id int PRIMARY KEY, aid int ); INSERT INTO tc VALUES (1, 1); INSERT INTO tc VALUES (2, 2); SELECT ( SELECT min(tb.id) FROM tb WHERE tb.aval = ( SELECT ta.val FROM ta WHERE ta.id = tc.aid)) AS min_tb_id FROM tc; -- -- Test case for 8.3 "failed to locate grouping columns" bug -- CREATE temp TABLE t1 ( f1 numeric(14, 0), f2 varchar(30) ); SELECT * FROM ( SELECT DISTINCT f1, f2, ( SELECT f2 FROM t1 x WHERE x.f1 = up.f1) AS fs FROM t1 up) ss GROUP BY f1, f2, fs; -- -- Test case for bug #5514 (mishandling of whole-row Vars in subselects) -- CREATE temp TABLE table_a ( id integer ); INSERT INTO table_a VALUES (42); CREATE temp VIEW view_a AS SELECT * FROM table_a; SELECT view_a FROM view_a; SELECT ( SELECT view_a) FROM view_a; SELECT ( SELECT ( SELECT view_a)) FROM view_a; SELECT ( SELECT (a.*)::text) FROM view_a a; -- -- Check that whole-row Vars reading the result of a subselect don't include -- any junk columns therein -- SELECT q FROM ( SELECT max(f1) FROM int4_tbl GROUP BY f1 ORDER BY f1) q; WITH q AS ( SELECT max(f1) FROM int4_tbl GROUP BY f1 ORDER BY f1 ) SELECT q FROM q; -- -- Test case for sublinks pulled up into joinaliasvars lists in an -- inherited update/delete query -- BEGIN; -- this shouldn't delete anything, but be safe DELETE FROM road WHERE EXISTS ( SELECT 1 FROM int4_tbl CROSS JOIN ( SELECT f1, ARRAY ( SELECT q1 FROM int8_tbl) AS arr FROM text_tbl) ss WHERE road.name = ss.f1); ROLLBACK; -- -- Test case for sublinks pushed down into subselects via join alias expansion -- SELECT ( SELECT sq1) AS qq1 FROM ( SELECT EXISTS ( SELECT 1 FROM int4_tbl WHERE f1 = q2) AS sq1, 42 AS dummy FROM int8_tbl) sq0 JOIN int4_tbl i4 ON dummy = i4.f1; -- -- Test case for subselect within UPDATE of INSERT...ON CONFLICT DO UPDATE -- CREATE temp TABLE upsert ( key int4 PRIMARY KEY, val text ); INSERT INTO upsert VALUES (1, 'val') ON CONFLICT (KEY) DO UPDATE SET val = 'not seen'; INSERT INTO upsert VALUES (1, 'val') ON CONFLICT (KEY) DO UPDATE SET val = 'seen with subselect ' || ( SELECT f1 FROM int4_tbl WHERE f1 != 0 LIMIT 1)::text; SELECT * FROM upsert; WITH aa AS ( SELECT 'int4_tbl' u FROM int4_tbl LIMIT 1) INSERT INTO upsert VALUES (1, 'x'), (999, 'y') ON CONFLICT (KEY) DO UPDATE SET val = ( SELECT u FROM aa) RETURNING *; -- -- Test case for cross-type partial matching in hashed subplan (bug #7597) -- CREATE temp TABLE outer_7597 ( f1 int4, f2 int4 ); INSERT INTO outer_7597 VALUES (0, 0); INSERT INTO outer_7597 VALUES (1, 0); INSERT INTO outer_7597 VALUES (0, NULL); INSERT INTO outer_7597 VALUES (1, NULL); CREATE temp TABLE inner_7597 ( c1 int8, c2 int8 ); INSERT INTO inner_7597 VALUES (0, NULL); SELECT * FROM outer_7597 WHERE (f1, f2) NOT IN ( SELECT * FROM inner_7597); -- -- Similar test case using text that verifies that collation -- information is passed through by execTuplesEqual() in nodeSubplan.c -- (otherwise it would error in texteq()) -- CREATE temp TABLE outer_text ( f1 text, f2 text ); INSERT INTO outer_text VALUES ('a', 'a'); INSERT INTO outer_text VALUES ('b', 'a'); INSERT INTO outer_text VALUES ('a', NULL); INSERT INTO outer_text VALUES ('b', NULL); CREATE temp TABLE inner_text ( c1 text, c2 text ); INSERT INTO inner_text VALUES ('a', NULL); SELECT * FROM outer_text WHERE (f1, f2) NOT IN ( SELECT * FROM inner_text); -- -- Test case for premature memory release during hashing of subplan output -- SELECT '1'::text IN ( SELECT '1'::name UNION ALL SELECT '1'::name); -- -- Test case for planner bug with nested EXISTS handling -- SELECT a.thousand FROM tenk1 a, tenk1 b WHERE a.thousand = b.thousand AND EXISTS ( SELECT 1 FROM tenk1 c WHERE b.hundred = c.hundred AND NOT EXISTS ( SELECT 1 FROM tenk1 d WHERE a.thousand = d.thousand)); -- -- Check that nested sub-selects are not pulled up if they contain volatiles -- EXPLAIN ( VERBOSE, COSTS OFF ) SELECT x, x FROM ( SELECT ( SELECT now()) AS x FROM ( VALUES (1), (2)) v (y)) ss; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT x, x FROM ( SELECT ( SELECT random()) AS x FROM ( VALUES (1), (2)) v (y)) ss; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT x, x FROM ( SELECT ( SELECT now() WHERE y = y) AS x FROM ( VALUES (1), (2)) v (y)) ss; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT x, x FROM ( SELECT ( SELECT random() WHERE y = y) AS x FROM ( VALUES (1), (2)) v (y)) ss; -- -- Check we don't misoptimize a NOT IN where the subquery returns no rows. -- CREATE temp TABLE notinouter ( a int ); CREATE temp TABLE notininner ( b int NOT NULL ); INSERT INTO notinouter VALUES (NULL), (1); SELECT * FROM notinouter WHERE a NOT IN ( SELECT b FROM notininner); -- -- Check we behave sanely in corner case of empty SELECT list (bug #8648) -- CREATE temp TABLE nocolumns (); SELECT EXISTS ( SELECT * FROM nocolumns); -- -- Check behavior with a SubPlan in VALUES (bug #14924) -- SELECT val.x FROM generate_series(1, 10) AS s (i), LATERAL ( VALUES (( SELECT s.i + 1)), (s.i + 101)) AS val (x) WHERE s.i < 10 AND ( SELECT val.x) < 110; -- -- Check sane behavior with nested IN SubLinks -- EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM int4_tbl WHERE ( CASE WHEN f1 IN ( SELECT unique1 FROM tenk1 a) THEN f1 ELSE NULL END) IN ( SELECT ten FROM tenk1 b); SELECT * FROM int4_tbl WHERE ( CASE WHEN f1 IN ( SELECT unique1 FROM tenk1 a) THEN f1 ELSE NULL END) IN ( SELECT ten FROM tenk1 b); -- -- Check for incorrect optimization when IN subquery contains a SRF -- EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM int4_tbl o WHERE (f1, f1) IN ( SELECT f1, generate_series(1, 50) / 10 g FROM int4_tbl i GROUP BY f1); SELECT * FROM int4_tbl o WHERE (f1, f1) IN ( SELECT f1, generate_series(1, 50) / 10 g FROM int4_tbl i GROUP BY f1); -- -- check for over-optimization of whole-row Var referencing an Append plan -- SELECT ( SELECT q FROM ( SELECT 1, 2, 3 WHERE f1 > 0 UNION ALL SELECT 4, 5, 6.0 WHERE f1 <= 0) q) FROM int4_tbl; -- -- Check that volatile quals aren't pushed down past a DISTINCT: -- nextval() should not be called more than the nominal number of times -- CREATE temp SEQUENCE ts1; SELECT * FROM ( SELECT DISTINCT ten FROM tenk1) ss WHERE ten < 10 + nextval('ts1') ORDER BY 1; SELECT nextval('ts1'); -- -- Check that volatile quals aren't pushed down past a set-returning function; -- while a nonvolatile qual can be, if it doesn't reference the SRF. -- CREATE FUNCTION tattle (x int, y int) RETURNS bool VOLATILE LANGUAGE plpgsql AS $$ BEGIN RAISE notice 'x = %, y = %', x, y; RETURN x > y; END $$; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM ( SELECT 9 AS x, unnest(ARRAY[1, 2, 3, 11, 12, 13]) AS u) ss WHERE tattle (x, 8); SELECT * FROM ( SELECT 9 AS x, unnest(ARRAY[1, 2, 3, 11, 12, 13]) AS u) ss WHERE tattle (x, 8); -- if we pretend it's stable, we get different results: ALTER FUNCTION tattle (x int, y int) STABLE; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM ( SELECT 9 AS x, unnest(ARRAY[1, 2, 3, 11, 12, 13]) AS u) ss WHERE tattle (x, 8); SELECT * FROM ( SELECT 9 AS x, unnest(ARRAY[1, 2, 3, 11, 12, 13]) AS u) ss WHERE tattle (x, 8); -- although even a stable qual should not be pushed down if it references SRF EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM ( SELECT 9 AS x, unnest(ARRAY[1, 2, 3, 11, 12, 13]) AS u) ss WHERE tattle (x, u); SELECT * FROM ( SELECT 9 AS x, unnest(ARRAY[1, 2, 3, 11, 12, 13]) AS u) ss WHERE tattle (x, u); DROP FUNCTION tattle (x int, y int); -- -- Test that LIMIT can be pushed to SORT through a subquery that just projects -- columns. We check for that having happened by looking to see if EXPLAIN -- ANALYZE shows that a top-N sort was used. We must suppress or filter away -- all the non-invariant parts of the EXPLAIN ANALYZE output. -- CREATE TABLE sq_limit ( pk int PRIMARY KEY, c1 int, c2 int ); INSERT INTO sq_limit VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 1, 1), (6, 2, 2), (7, 3, 3), (8, 4, 4); CREATE FUNCTION explain_sq_limit () RETURNS SETOF text LANGUAGE plpgsql AS $$ DECLARE ln text; BEGIN FOR ln IN EXPLAIN ( ANALYZE, summary OFF, timing OFF, COSTS OFF ) SELECT * FROM ( SELECT pk, c2 FROM sq_limit ORDER BY c1, pk) AS x LIMIT 3 LOOP ln := regexp_replace(ln, 'Memory: \S*', 'Memory: xxx'); -- this case might occur if force_parallel_mode is on: ln := regexp_replace(ln, 'Worker 0: Sort Method', 'Sort Method'); RETURN NEXT ln; END LOOP; END; $$; SELECT * FROM explain_sq_limit (); SELECT * FROM ( SELECT pk, c2 FROM sq_limit ORDER BY c1, pk) AS x LIMIT 3; DROP FUNCTION explain_sq_limit (); DROP TABLE sq_limit; -- -- Ensure that backward scan direction isn't propagated into -- expression subqueries (bug #15336) -- BEGIN; DECLARE c1 SCROLL CURSOR FOR SELECT * FROM generate_series(1, 4) i WHERE i <> ALL ( VALUES (2), (3)); MOVE FORWARD ALL IN c1; FETCH BACKWARD ALL IN c1; COMMIT; -- -- Tests for CTE inlining behavior -- -- Basic subquery that can be inlined EXPLAIN ( VERBOSE, COSTS OFF ) WITH x AS ( SELECT * FROM ( SELECT f1 FROM subselect_tbl) ss ) SELECT * FROM x WHERE f1 = 1; -- Explicitly request materialization EXPLAIN ( VERBOSE, COSTS OFF ) WITH x AS MATERIALIZED ( SELECT * FROM ( SELECT f1 FROM subselect_tbl) ss ) SELECT * FROM x WHERE f1 = 1; -- Stable functions are safe to inline EXPLAIN ( VERBOSE, COSTS OFF ) WITH x AS ( SELECT * FROM ( SELECT f1, now() FROM subselect_tbl) ss ) SELECT * FROM x WHERE f1 = 1; -- Volatile functions prevent inlining EXPLAIN ( VERBOSE, COSTS OFF ) WITH x AS ( SELECT * FROM ( SELECT f1, random() FROM subselect_tbl) ss ) SELECT * FROM x WHERE f1 = 1; -- SELECT FOR UPDATE cannot be inlined EXPLAIN ( VERBOSE, COSTS OFF ) WITH x AS ( SELECT * FROM ( SELECT f1 FROM subselect_tbl FOR UPDATE) ss ) SELECT * FROM x WHERE f1 = 1; -- Multiply-referenced CTEs are inlined only when requested EXPLAIN ( VERBOSE, COSTS OFF ) WITH x AS ( SELECT * FROM ( SELECT f1, now() AS n FROM subselect_tbl) ss ) SELECT * FROM x, x x2 WHERE x.n = x2.n; EXPLAIN ( VERBOSE, COSTS OFF ) WITH x AS NOT MATERIALIZED ( SELECT * FROM ( SELECT f1, now() AS n FROM subselect_tbl) ss ) SELECT * FROM x, x x2 WHERE x.n = x2.n; -- Multiply-referenced CTEs can't be inlined if they contain outer self-refs EXPLAIN ( VERBOSE, COSTS OFF ) WITH RECURSIVE x (a) AS (( VALUES ('a'), ('b')) UNION ALL ( WITH z AS NOT MATERIALIZED ( SELECT * FROM x ) SELECT z.a || z1.a AS a FROM z CROSS JOIN z AS z1 WHERE length(z.a || z1.a) < 5)) SELECT * FROM x; WITH RECURSIVE x ( a ) AS (( VALUES ('a'), ('b')) UNION ALL ( WITH z AS NOT MATERIALIZED ( SELECT * FROM x ) SELECT z.a || z1.a AS a FROM z CROSS JOIN z AS z1 WHERE length( z.a || z1.a ) < 5 )) SELECT * FROM x; EXPLAIN ( VERBOSE, COSTS OFF ) WITH RECURSIVE x (a) AS (( VALUES ('a'), ('b')) UNION ALL ( WITH z AS NOT MATERIALIZED ( SELECT * FROM x ) SELECT z.a || z.a AS a FROM z WHERE length(z.a || z.a) < 5)) SELECT * FROM x; WITH RECURSIVE x ( a ) AS (( VALUES ('a'), ('b')) UNION ALL ( WITH z AS NOT MATERIALIZED ( SELECT * FROM x ) SELECT z.a || z.a AS a FROM z WHERE length( z.a || z.a ) < 5 )) SELECT * FROM x; -- Check handling of outer references EXPLAIN ( VERBOSE, COSTS OFF ) WITH x AS ( SELECT * FROM int4_tbl ) SELECT * FROM ( WITH y AS ( SELECT * FROM x ) SELECT * FROM y) ss; EXPLAIN ( VERBOSE, COSTS OFF ) WITH x AS MATERIALIZED ( SELECT * FROM int4_tbl ) SELECT * FROM ( WITH y AS ( SELECT * FROM x ) SELECT * FROM y) ss; -- Ensure that we inline the currect CTE when there are -- multiple CTEs with the same name EXPLAIN ( VERBOSE, COSTS OFF ) WITH x AS ( SELECT 1 AS y ) SELECT * FROM ( WITH x AS ( SELECT 2 AS y ) SELECT * FROM x) ss; -- Row marks are not pushed into CTEs EXPLAIN ( VERBOSE, COSTS OFF ) WITH x AS ( SELECT * FROM subselect_tbl ) SELECT * FROM x FOR UPDATE; pgFormatter-4.2/t/pg-test-files/expected/sysviews.sql000066400000000000000000000044411361326045100230210ustar00rootroot00000000000000-- -- Test assorted system views -- -- This test is mainly meant to provide some code coverage for the -- set-returning functions that underlie certain system views. -- The output of most of these functions is very environment-dependent, -- so our ability to test with fixed expected output is pretty limited; -- but even a trivial check of count(*) will exercise the normal code path -- through the SRF. SELECT count(*) >= 0 AS ok FROM pg_available_extension_versions; SELECT count(*) >= 0 AS ok FROM pg_available_extensions; -- At introduction, pg_config had 23 entries; it may grow SELECT count(*) > 20 AS ok FROM pg_config; -- We expect no cursors in this test; see also portals.sql SELECT count(*) = 0 AS ok FROM pg_cursors; SELECT count(*) >= 0 AS ok FROM pg_file_settings; -- There will surely be at least one rule SELECT count(*) > 0 AS ok FROM pg_hba_file_rules; -- There will surely be at least one active lock SELECT count(*) > 0 AS ok FROM pg_locks; -- We expect no prepared statements in this test; see also prepare.sql SELECT count(*) = 0 AS ok FROM pg_prepared_statements; -- See also prepared_xacts.sql SELECT count(*) >= 0 AS ok FROM pg_prepared_xacts; -- This is to record the prevailing planner enable_foo settings during -- a regression test run. SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%'; -- Test that the pg_timezone_names and pg_timezone_abbrevs views are -- more-or-less working. We can't test their contents in any great detail -- without the outputs changing anytime IANA updates the underlying data, -- but it seems reasonable to expect at least one entry per major meridian. -- (At the time of writing, the actual counts are around 38 because of -- zones using fractional GMT offsets, so this is a pretty loose test.) SELECT count(DISTINCT utc_offset) >= 24 AS ok FROM pg_timezone_names; SELECT count(DISTINCT utc_offset) >= 24 AS ok FROM pg_timezone_abbrevs; -- Let's check the non-default timezone abbreviation sets, too SET timezone_abbreviations = 'Australia'; SELECT count(DISTINCT utc_offset) >= 24 AS ok FROM pg_timezone_abbrevs; SET timezone_abbreviations = 'India'; SELECT count(DISTINCT utc_offset) >= 24 AS ok FROM pg_timezone_abbrevs; pgFormatter-4.2/t/pg-test-files/expected/tablesample.sql000066400000000000000000000115711361326045100234200ustar00rootroot00000000000000CREATE TABLE test_tablesample ( id int, name text ) WITH ( fillfactor = 10 ); -- use fillfactor so we don't have to load too much data to get multiple pages INSERT INTO test_tablesample SELECT i, repeat(i::text, 200) FROM generate_series(0, 9) s (i); SELECT t.id FROM test_tablesample AS t TABLESAMPLE SYSTEM (50) REPEATABLE (0); SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (100.0 / 11) REPEATABLE (0); SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0); SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (50) REPEATABLE (0); SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (5.5) REPEATABLE (0); -- 100% should give repeatable count results (ie, all rows) in any case SELECT count(*) FROM test_tablesample TABLESAMPLE SYSTEM (100); SELECT count(*) FROM test_tablesample TABLESAMPLE SYSTEM (100) REPEATABLE (1 + 2); SELECT count(*) FROM test_tablesample TABLESAMPLE SYSTEM (100) REPEATABLE (0.4); CREATE VIEW test_tablesample_v1 AS SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (10 * 2) REPEATABLE (2); CREATE VIEW test_tablesample_v2 AS SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (99); \d+ test_tablesample_v1 \d+ test_tablesample_v2 -- check a sampled query doesn't affect cursor in progress BEGIN; DECLARE tablesample_cur CURSOR FOR SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0); FETCH FIRST FROM tablesample_cur; FETCH NEXT FROM tablesample_cur; FETCH NEXT FROM tablesample_cur; SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0); FETCH NEXT FROM tablesample_cur; FETCH NEXT FROM tablesample_cur; FETCH NEXT FROM tablesample_cur; FETCH FIRST FROM tablesample_cur; FETCH NEXT FROM tablesample_cur; FETCH NEXT FROM tablesample_cur; FETCH NEXT FROM tablesample_cur; FETCH NEXT FROM tablesample_cur; FETCH NEXT FROM tablesample_cur; CLOSE tablesample_cur; END; EXPLAIN ( COSTS OFF ) SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (2); EXPLAIN ( COSTS OFF ) SELECT * FROM test_tablesample_v1; -- check inheritance behavior EXPLAIN ( COSTS OFF ) SELECT count(*) FROM person TABLESAMPLE BERNOULLI (100); SELECT count(*) FROM person TABLESAMPLE BERNOULLI (100); SELECT count(*) FROM person; -- check that collations get assigned within the tablesample arguments SELECT count(*) FROM test_tablesample TABLESAMPLE BERNOULLI (('1'::text < '0'::text)::int); -- check behavior during rescans, as well as correct handling of min/max pct SELECT * FROM ( VALUES (0), (100)) v (pct), LATERAL ( SELECT count(*) FROM tenk1 TABLESAMPLE BERNOULLI (pct)) ss; SELECT * FROM ( VALUES (0), (100)) v (pct), LATERAL ( SELECT count(*) FROM tenk1 TABLESAMPLE SYSTEM (pct)) ss; EXPLAIN ( COSTS OFF ) SELECT pct, count(unique1) FROM ( VALUES (0), (100)) v (pct), LATERAL ( SELECT * FROM tenk1 TABLESAMPLE BERNOULLI (pct)) ss GROUP BY pct; SELECT pct, count(unique1) FROM ( VALUES (0), (100)) v (pct), LATERAL ( SELECT * FROM tenk1 TABLESAMPLE BERNOULLI (pct)) ss GROUP BY pct; SELECT pct, count(unique1) FROM ( VALUES (0), (100)) v (pct), LATERAL ( SELECT * FROM tenk1 TABLESAMPLE SYSTEM (pct)) ss GROUP BY pct; -- errors SELECT id FROM test_tablesample TABLESAMPLE FOOBAR (1); SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (NULL); SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (NULL); SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (- 1); SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (200); SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (- 1); SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (200); SELECT id FROM test_tablesample_v1 TABLESAMPLE BERNOULLI (1); INSERT INTO test_tablesample_v1 VALUES (1); WITH query_select AS ( SELECT * FROM test_tablesample ) SELECT * FROM query_select TABLESAMPLE BERNOULLI (5.5) REPEATABLE (1); SELECT q.* FROM ( SELECT * FROM test_tablesample) AS q TABLESAMPLE BERNOULLI (5); -- check partitioned tables support tablesample CREATE TABLE parted_sample ( a int ) PARTITION BY LIST (a); CREATE TABLE parted_sample_1 PARTITION OF parted_sample FOR VALUES IN (1); CREATE TABLE parted_sample_2 PARTITION OF parted_sample FOR VALUES IN (2); EXPLAIN ( COSTS OFF ) SELECT * FROM parted_sample TABLESAMPLE BERNOULLI (100); DROP TABLE parted_sample, parted_sample_1, parted_sample_2; pgFormatter-4.2/t/pg-test-files/expected/temp.sql000066400000000000000000000201021361326045100220620ustar00rootroot00000000000000-- -- TEMP -- Test temp relations and indexes -- -- test temp table/index masking CREATE TABLE temptest ( col int ); CREATE INDEX i_temptest ON temptest (col); CREATE TEMP TABLE temptest ( tcol int ); CREATE INDEX i_temptest ON temptest (tcol); SELECT * FROM temptest; DROP INDEX i_temptest; DROP TABLE temptest; SELECT * FROM temptest; DROP INDEX i_temptest; DROP TABLE temptest; -- test temp table selects CREATE TABLE temptest ( col int ); INSERT INTO temptest VALUES (1); CREATE TEMP TABLE temptest ( tcol float ); INSERT INTO temptest VALUES (2.1); SELECT * FROM temptest; DROP TABLE temptest; SELECT * FROM temptest; DROP TABLE temptest; -- test temp table deletion CREATE TEMP TABLE temptest ( col int ); \c SELECT * FROM temptest; -- Test ON COMMIT DELETE ROWS CREATE TEMP TABLE temptest ( col int ) ON COMMIT DELETE ROWS; BEGIN; INSERT INTO temptest VALUES (1); INSERT INTO temptest VALUES (2); SELECT * FROM temptest; COMMIT; SELECT * FROM temptest; DROP TABLE temptest; BEGIN; CREATE TEMP TABLE temptest ( col ) ON COMMIT DELETE ROWS AS SELECT 1; SELECT * FROM temptest; COMMIT; SELECT * FROM temptest; DROP TABLE temptest; -- Test ON COMMIT DROP BEGIN; CREATE TEMP TABLE temptest ( col int ) ON COMMIT DROP; INSERT INTO temptest VALUES (1); INSERT INTO temptest VALUES (2); SELECT * FROM temptest; COMMIT; SELECT * FROM temptest; BEGIN; CREATE TEMP TABLE temptest ( col ) ON COMMIT DROP AS SELECT 1; SELECT * FROM temptest; COMMIT; SELECT * FROM temptest; -- ON COMMIT is only allowed for TEMP CREATE TABLE temptest ( col int ) ON COMMIT DELETE ROWS; CREATE TABLE temptest ( col ) ON COMMIT DELETE ROWS AS SELECT 1; -- Test foreign keys BEGIN; CREATE TEMP TABLE temptest1 ( col int PRIMARY KEY ); CREATE TEMP TABLE temptest2 ( col int REFERENCES temptest1 ) ON COMMIT DELETE ROWS; INSERT INTO temptest1 VALUES (1); INSERT INTO temptest2 VALUES (1); COMMIT; SELECT * FROM temptest1; SELECT * FROM temptest2; BEGIN; CREATE TEMP TABLE temptest3 ( col int PRIMARY KEY ) ON COMMIT DELETE ROWS; CREATE TEMP TABLE temptest4 ( col int REFERENCES temptest3 ); COMMIT; -- Test manipulation of temp schema's placement in search path CREATE TABLE public.whereami ( f1 text ); INSERT INTO public.whereami VALUES ('public'); CREATE temp TABLE whereami ( f1 text ); INSERT INTO whereami VALUES ('temp'); CREATE FUNCTION public.whoami () RETURNS text AS $$ SELECT 'public'::text$$ LANGUAGE sql; CREATE FUNCTION pg_temp.whoami () RETURNS text AS $$ SELECT 'temp'::text$$ LANGUAGE sql; -- default should have pg_temp implicitly first, but only for tables SELECT * FROM whereami; SELECT whoami (); -- can list temp first explicitly, but it still doesn't affect functions SET search_path = pg_temp, public; SELECT * FROM whereami; SELECT whoami (); -- or put it last for security SET search_path = public, pg_temp; SELECT * FROM whereami; SELECT whoami (); -- you can invoke a temp function explicitly, though SELECT pg_temp.whoami (); DROP TABLE public.whereami; -- For partitioned temp tables, ON COMMIT actions ignore storage-less -- partitioned tables. BEGIN; CREATE temp TABLE temp_parted_oncommit ( a int ) PARTITION BY LIST (a) ON COMMIT DELETE ROWS; CREATE temp TABLE temp_parted_oncommit_1 PARTITION OF temp_parted_oncommit FOR VALUES IN (1) ON COMMIT DELETE ROWS; INSERT INTO temp_parted_oncommit VALUES (1); COMMIT; -- partitions are emptied by the previous commit SELECT * FROM temp_parted_oncommit; DROP TABLE temp_parted_oncommit; -- Check dependencies between ON COMMIT actions with a partitioned -- table and its partitions. Using ON COMMIT DROP on a parent removes -- the whole set. BEGIN; CREATE temp TABLE temp_parted_oncommit_test ( a int ) PARTITION BY LIST (a) ON COMMIT DROP; CREATE temp TABLE temp_parted_oncommit_test1 PARTITION OF temp_parted_oncommit_test FOR VALUES IN (1) ON COMMIT DELETE ROWS; CREATE temp TABLE temp_parted_oncommit_test2 PARTITION OF temp_parted_oncommit_test FOR VALUES IN (2) ON COMMIT DROP; INSERT INTO temp_parted_oncommit_test VALUES (1), (2); COMMIT; -- no relations remain in this case. SELECT relname FROM pg_class WHERE relname LIKE 'temp_parted_oncommit_test%'; -- Using ON COMMIT DELETE on a partitioned table does not remove -- all rows if partitions preserve their data. BEGIN; CREATE temp TABLE temp_parted_oncommit_test ( a int ) PARTITION BY LIST (a) ON COMMIT DELETE ROWS; CREATE temp TABLE temp_parted_oncommit_test1 PARTITION OF temp_parted_oncommit_test FOR VALUES IN (1) ON COMMIT preserve ROWS; CREATE temp TABLE temp_parted_oncommit_test2 PARTITION OF temp_parted_oncommit_test FOR VALUES IN (2) ON COMMIT DROP; INSERT INTO temp_parted_oncommit_test VALUES (1), (2); COMMIT; -- Data from the remaining partition is still here as its rows are -- preserved. SELECT * FROM temp_parted_oncommit_test; -- two relations remain in this case. SELECT relname FROM pg_class WHERE relname LIKE 'temp_parted_oncommit_test%'; DROP TABLE temp_parted_oncommit_test; -- Check dependencies between ON COMMIT actions with inheritance trees. -- Using ON COMMIT DROP on a parent removes the whole set. BEGIN; CREATE temp TABLE temp_inh_oncommit_test ( a int ) ON COMMIT DROP; CREATE temp TABLE temp_inh_oncommit_test1 () INHERITS ( temp_inh_oncommit_test ) ON COMMIT DELETE ROWS; INSERT INTO temp_inh_oncommit_test1 VALUES (1); COMMIT; -- no relations remain in this case SELECT relname FROM pg_class WHERE relname LIKE 'temp_inh_oncommit_test%'; -- Data on the parent is removed, and the child goes away. BEGIN; CREATE temp TABLE temp_inh_oncommit_test ( a int ) ON COMMIT DELETE ROWS; CREATE temp TABLE temp_inh_oncommit_test1 () INHERITS ( temp_inh_oncommit_test ) ON COMMIT DROP; INSERT INTO temp_inh_oncommit_test1 VALUES (1); INSERT INTO temp_inh_oncommit_test VALUES (1); COMMIT; SELECT * FROM temp_inh_oncommit_test; -- one relation remains SELECT relname FROM pg_class WHERE relname LIKE 'temp_inh_oncommit_test%'; DROP TABLE temp_inh_oncommit_test; -- Tests with two-phase commit -- Transactions creating objects in a temporary namespace cannot be used -- with two-phase commit. -- These cases generate errors about temporary namespace. -- Function creation BEGIN; CREATE FUNCTION pg_temp.twophase_func () RETURNS void AS $$ SELECT '2pc_func'::text $$ LANGUAGE sql; PREPARE TRANSACTION 'twophase_func'; -- Function drop CREATE FUNCTION pg_temp.twophase_func () RETURNS void AS $$ SELECT '2pc_func'::text $$ LANGUAGE sql; BEGIN; DROP FUNCTION pg_temp.twophase_func (); PREPARE TRANSACTION 'twophase_func'; -- Operator creation BEGIN; CREATE OPERATOR pg_temp. @@ ( LEFTARG = int4, RIGHTARG = int4, PROCEDURE = int4mi ); PREPARE TRANSACTION 'twophase_operator'; -- These generate errors about temporary tables. BEGIN; CREATE TYPE pg_temp.twophase_type AS ( a int ); PREPARE TRANSACTION 'twophase_type'; BEGIN; CREATE VIEW pg_temp.twophase_view AS SELECT 1; PREPARE TRANSACTION 'twophase_view'; BEGIN; CREATE SEQUENCE pg_temp.twophase_seq; PREPARE TRANSACTION 'twophase_sequence'; -- Temporary tables cannot be used with two-phase commit. CREATE temp TABLE twophase_tab ( a int ); BEGIN; SELECT a FROM twophase_tab; PREPARE TRANSACTION 'twophase_tab'; BEGIN; INSERT INTO twophase_tab VALUES (1); PREPARE TRANSACTION 'twophase_tab'; BEGIN; LOCK twophase_tab IN access exclusive mode; PREPARE TRANSACTION 'twophase_tab'; BEGIN; DROP TABLE twophase_tab; PREPARE TRANSACTION 'twophase_tab'; -- Corner case: current_schema may create a temporary schema if namespace -- creation is pending, so check after that. First reset the connection -- to remove the temporary namespace. \c - SET search_path TO 'pg_temp'; BEGIN; SELECT current_schema() ~ 'pg_temp' AS is_temp_schema; PREPARE TRANSACTION 'twophase_search'; pgFormatter-4.2/t/pg-test-files/expected/text.sql000066400000000000000000000113251361326045100221100ustar00rootroot00000000000000-- -- TEXT -- SELECT text 'this is a text string' = text 'this is a text string' AS true; SELECT text 'this is a text string' = text 'this is a text strin' AS false; CREATE TABLE TEXT_TBL ( f1 text ); INSERT INTO TEXT_TBL VALUES ('doh!'); INSERT INTO TEXT_TBL VALUES ('hi de ho neighbor'); SELECT '' AS two, * FROM TEXT_TBL; -- As of 8.3 we have removed most implicit casts to text, so that for example -- this no longer works: SELECT length(42); -- But as a special exception for usability's sake, we still allow implicit -- casting to text in concatenations, so long as the other input is text or -- an unknown literal. So these work: SELECT 'four: '::text || 2 + 2; SELECT 'four: ' || 2 + 2; -- but not this: SELECT 3 || 4.0; /* * various string functions */ SELECT concat('one'); SELECT concat(1, 2, 3, 'hello', TRUE, FALSE, to_date('20100309', 'YYYYMMDD')); SELECT concat_ws('#', 'one'); SELECT concat_ws('#', 1, 2, 3, 'hello', TRUE, FALSE, to_date('20100309', 'YYYYMMDD')); SELECT concat_ws(',', 10, 20, NULL, 30); SELECT concat_ws('', 10, 20, NULL, 30); SELECT concat_ws(NULL, 10, 20, NULL, 30) IS NULL; SELECT reverse('abcde'); SELECT i, LEFT ('ahoj', i), RIGHT ('ahoj', i) FROM generate_series(- 5, 5) t (i) ORDER BY i; SELECT quote_literal(''); SELECT quote_literal('abc'''); SELECT quote_literal(e '\\'); -- check variadic labeled argument select concat(variadic array[1,2,3]); select concat_ws(', ', variadic array[1,2,3]); select concat_ws(', ', variadic NULL::int[]); select concat(variadic NULL::int[]) is NULL; select concat(variadic ' '::int[]) = ''; --should fail select concat_ws(', ', variadic 10); /* * format */ select format(NULL); select format(' Hello '); select format(' Hello % s ', ' World '); select format(' Hello % % '); select format(' Hello % % % % '); -- should fail select format(' Hello % s % s ', ' World '); select format(' Hello % s '); select format(' Hello % x ', 20); -- check literal and sql identifiers select format(' INSERT INTO % I VALUES (% L, % L) ', ' mytab ', 10, ' Hello '); select format(' % s % s % s ',' Hello ', NULL,' World '); select format(' INSERT INTO % I VALUES (% L, % L) ', ' mytab ', 10, NULL); select format(' INSERT INTO % I VALUES (% L, % L) ', ' mytab ', NULL, ' Hello '); -- should fail, sql identifier cannot be NULL select format(' INSERT INTO % I VALUES (% L, % L) ', NULL, 10, ' Hello '); -- check positional placeholders select format(' % 1$s % 3$s ', 1, 2, 3); select format(' % 1$s % 12$s ', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); -- should fail select format(' % 1$s % 4$s ', 1, 2, 3); select format(' % 1$s % 13$s ', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); select format(' % 0$s ', ' Hello '); select format(' % * 0$s ', ' Hello '); select format(' % 1$ ', 1); select format(' % 1$1 ', 1); -- check mix of positional and ordered placeholders select format(' Hello % s % 1$s % s ', ' World ', ' Hello again '); select format(' Hello % s % s, % 2$s % 2$s ', ' World ', ' Hello again '); -- check variadic labeled arguments select format(' % s, % s ', variadic array[' Hello ',' World ']); select format(' % s, % s ', variadic array[1, 2]); select format(' % s, % s ', variadic array[true, false]); select format(' % s, % s ', variadic array[true, false]::text[]); -- check variadic with positional placeholders select format(' % 2$s, % 1$s ', variadic array[' FIRST ', ' second ']); select format(' % 2$s, % 1$s ', variadic array[1, 2]); -- variadic argument can be array type NULL, but should not be referenced select format(' Hello ', variadic NULL::int[]); -- variadic argument allows simulating more than FUNC_MAX_ARGS parameters select format(string_agg(' % s ',', '), variadic array_agg(i)) from generate_series(1,200) g(i); -- check field widths and left, right alignment select format(' >> % 10s << ', ' Hello '); select format(' >> % 10s << ', NULL); select format(' >> % 10s << ', ''); select format(' >> % - 10s << ', ''); select format(' >> % - 10s << ', ' Hello '); select format(' >> % - 10s << ', NULL); select format(' >> % 1$10s << ', ' Hello '); select format(' >> % 1$ - 10I << ', ' Hello '); select format(' >> % 2$ * 1$L << ', 10, ' Hello '); select format(' >> % 2$ * 1$L << ', 10, NULL); select format(' >> % 2$ * 1$L << ', -10, NULL); select format(' >> % * s << ', 10, ' Hello '); select format(' >> % * 1$s << ', 10, ' Hello '); select format(' >> % - s << ', ' Hello '); select format(' >> % 10L << ', NULL); select format(' >> % 2$ * 1$L << ', NULL, ' Hello '); select format(' >> % 2$ * 1$L << ', 0, ' Hello); pgFormatter-4.2/t/pg-test-files/expected/tidscan.sql000066400000000000000000000077351361326045100225630ustar00rootroot00000000000000-- tests for tidscans CREATE TABLE tidscan ( id integer ); -- only insert a few rows, we don't want to spill onto a second table page INSERT INTO tidscan VALUES (1), (2), (3); -- show ctids SELECT ctid, * FROM tidscan; -- ctid equality - implemented as tidscan EXPLAIN ( COSTS OFF ) SELECT ctid, * FROM tidscan WHERE ctid = '(0,1)'; SELECT ctid, * FROM tidscan WHERE ctid = '(0,1)'; EXPLAIN ( COSTS OFF ) SELECT ctid, * FROM tidscan WHERE '(0,1)' = ctid; SELECT ctid, * FROM tidscan WHERE '(0,1)' = ctid; -- OR'd clauses EXPLAIN ( COSTS OFF ) SELECT ctid, * FROM tidscan WHERE ctid = '(0,2)' OR '(0,1)' = ctid; SELECT ctid, * FROM tidscan WHERE ctid = '(0,2)' OR '(0,1)' = ctid; -- ctid = ScalarArrayOp - implemented as tidscan EXPLAIN ( COSTS OFF ) SELECT ctid, * FROM tidscan WHERE ctid = ANY (ARRAY['(0,1)', '(0,2)']::tid[]); SELECT ctid, * FROM tidscan WHERE ctid = ANY (ARRAY['(0,1)', '(0,2)']::tid[]); -- ctid != ScalarArrayOp - can't be implemented as tidscan EXPLAIN ( COSTS OFF ) SELECT ctid, * FROM tidscan WHERE ctid != ANY (ARRAY['(0,1)', '(0,2)']::tid[]); SELECT ctid, * FROM tidscan WHERE ctid != ANY (ARRAY['(0,1)', '(0,2)']::tid[]); -- tid equality extracted from sub-AND clauses EXPLAIN ( COSTS OFF ) SELECT ctid, * FROM tidscan WHERE (id = 3 AND ctid IN ('(0,2)', '(0,3)')) OR (ctid = '(0,1)' AND id = 1); SELECT ctid, * FROM tidscan WHERE (id = 3 AND ctid IN ('(0,2)', '(0,3)')) OR (ctid = '(0,1)' AND id = 1); -- nestloop-with-inner-tidscan joins on tid SET enable_hashjoin TO OFF; -- otherwise hash join might win EXPLAIN ( COSTS OFF ) SELECT t1.ctid, t1.*, t2.ctid, t2.* FROM tidscan t1 JOIN tidscan t2 ON t1.ctid = t2.ctid WHERE t1.id = 1; SELECT t1.ctid, t1.*, t2.ctid, t2.* FROM tidscan t1 JOIN tidscan t2 ON t1.ctid = t2.ctid WHERE t1.id = 1; EXPLAIN ( COSTS OFF ) SELECT t1.ctid, t1.*, t2.ctid, t2.* FROM tidscan t1 LEFT JOIN tidscan t2 ON t1.ctid = t2.ctid WHERE t1.id = 1; SELECT t1.ctid, t1.*, t2.ctid, t2.* FROM tidscan t1 LEFT JOIN tidscan t2 ON t1.ctid = t2.ctid WHERE t1.id = 1; RESET enable_hashjoin; -- exercise backward scan and rewind BEGIN; DECLARE c CURSOR FOR SELECT ctid, * FROM tidscan WHERE ctid = ANY (ARRAY['(0,1)', '(0,2)']::tid[]); FETCH ALL FROM c; FETCH BACKWARD 1 FROM c; FETCH FIRST FROM c; ROLLBACK; -- tidscan via CURRENT OF BEGIN; DECLARE c CURSOR FOR SELECT ctid, * FROM tidscan; FETCH NEXT FROM c; -- skip one row FETCH NEXT FROM c; -- perform update EXPLAIN ( ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF ) UPDATE tidscan SET id = - id WHERE CURRENT OF c RETURNING *; FETCH NEXT FROM c; -- perform update EXPLAIN ( ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF ) UPDATE tidscan SET id = - id WHERE CURRENT OF c RETURNING *; SELECT * FROM tidscan; -- position cursor past any rows FETCH NEXT FROM c; -- should error out EXPLAIN ( ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF ) UPDATE tidscan SET id = - id WHERE CURRENT OF c RETURNING *; ROLLBACK; -- bulk joins on CTID -- (these plans don't use TID scans, but this still seems like an -- appropriate place for these tests) EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1 t1 JOIN tenk1 t2 ON t1.ctid = t2.ctid; SELECT count(*) FROM tenk1 t1 JOIN tenk1 t2 ON t1.ctid = t2.ctid; SET enable_hashjoin TO OFF; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM tenk1 t1 JOIN tenk1 t2 ON t1.ctid = t2.ctid; SELECT count(*) FROM tenk1 t1 JOIN tenk1 t2 ON t1.ctid = t2.ctid; RESET enable_hashjoin; DROP TABLE tidscan; pgFormatter-4.2/t/pg-test-files/expected/time.sql000066400000000000000000000026351361326045100220660ustar00rootroot00000000000000-- -- TIME -- CREATE TABLE TIME_TBL ( f1 time(2) ); INSERT INTO TIME_TBL VALUES ('00:00'); INSERT INTO TIME_TBL VALUES ('01:00'); -- as of 7.4, timezone spec should be accepted and ignored INSERT INTO TIME_TBL VALUES ('02:03 PST'); INSERT INTO TIME_TBL VALUES ('11:59 EDT'); INSERT INTO TIME_TBL VALUES ('12:00'); INSERT INTO TIME_TBL VALUES ('12:01'); INSERT INTO TIME_TBL VALUES ('23:59'); INSERT INTO TIME_TBL VALUES ('11:59:59.99 PM'); INSERT INTO TIME_TBL VALUES ('2003-03-07 15:36:39 America/New_York'); INSERT INTO TIME_TBL VALUES ('2003-07-07 15:36:39 America/New_York'); -- this should fail (the timezone offset is not known) INSERT INTO TIME_TBL VALUES ('15:36:39 America/New_York'); SELECT f1 AS "Time" FROM TIME_TBL; SELECT f1 AS "Three" FROM TIME_TBL WHERE f1 < '05:06:07'; SELECT f1 AS "Five" FROM TIME_TBL WHERE f1 > '05:06:07'; SELECT f1 AS "None" FROM TIME_TBL WHERE f1 < '00:00'; SELECT f1 AS "Eight" FROM TIME_TBL WHERE f1 >= '00:00'; -- -- TIME simple math -- -- We now make a distinction between time and intervals, -- and adding two times together makes no sense at all. -- Leave in one query to show that it is rejected, -- and do the rest of the testing in horology.sql -- where we do mixed-type arithmetic. - thomas 2000-12-02 SELECT f1 + time '00:01' AS "Illegal" FROM TIME_TBL; pgFormatter-4.2/t/pg-test-files/expected/timestamp.sql000066400000000000000000000251001361326045100231230ustar00rootroot00000000000000-- -- TIMESTAMP -- CREATE TABLE TIMESTAMP_TBL ( d1 timestamp(2) without time zone ); -- Test shorthand input values -- We can't just "select" the results since they aren't constants; test for -- equality instead. We can do that by running the test inside a transaction -- block, within which the value of 'now' shouldn't change. We also check -- that 'now' *does* change over a reasonable interval such as 100 msec. -- NOTE: it is possible for this part of the test to fail if the transaction -- block is entered exactly at local midnight; then 'now' and 'today' have -- the same values and the counts will come out different. INSERT INTO TIMESTAMP_TBL VALUES ('now'); SELECT pg_sleep(0.1); BEGIN; INSERT INTO TIMESTAMP_TBL VALUES ('now'); INSERT INTO TIMESTAMP_TBL VALUES ('today'); INSERT INTO TIMESTAMP_TBL VALUES ('yesterday'); INSERT INTO TIMESTAMP_TBL VALUES ('tomorrow'); -- time zone should be ignored by this data type INSERT INTO TIMESTAMP_TBL VALUES ('tomorrow EST'); INSERT INTO TIMESTAMP_TBL VALUES ('tomorrow zulu'); SELECT count(*) AS One FROM TIMESTAMP_TBL WHERE d1 = timestamp without time zone 'today'; SELECT count(*) AS Three FROM TIMESTAMP_TBL WHERE d1 = timestamp without time zone 'tomorrow'; SELECT count(*) AS One FROM TIMESTAMP_TBL WHERE d1 = timestamp without time zone 'yesterday'; SELECT count(*) AS One FROM TIMESTAMP_TBL WHERE d1 = timestamp(2) without time zone 'now'; COMMIT; DELETE FROM TIMESTAMP_TBL; -- verify uniform transaction time within transaction block BEGIN; INSERT INTO TIMESTAMP_TBL VALUES ('now'); SELECT pg_sleep(0.1); INSERT INTO TIMESTAMP_TBL VALUES ('now'); SELECT pg_sleep(0.1); SELECT count(*) AS two FROM TIMESTAMP_TBL WHERE d1 = timestamp(2) without time zone 'now'; COMMIT; TRUNCATE TIMESTAMP_TBL; -- Special values INSERT INTO TIMESTAMP_TBL VALUES ('-infinity'); INSERT INTO TIMESTAMP_TBL VALUES ('infinity'); INSERT INTO TIMESTAMP_TBL VALUES ('epoch'); -- Obsolete special values INSERT INTO TIMESTAMP_TBL VALUES ('invalid'); INSERT INTO TIMESTAMP_TBL VALUES ('undefined'); INSERT INTO TIMESTAMP_TBL VALUES ('current'); -- Postgres v6.0 standard output format INSERT INTO TIMESTAMP_TBL VALUES ('Mon Feb 10 17:32:01 1997 PST'); -- Variations on Postgres v6.1 standard output format INSERT INTO TIMESTAMP_TBL VALUES ('Mon Feb 10 17:32:01.000001 1997 PST'); INSERT INTO TIMESTAMP_TBL VALUES ('Mon Feb 10 17:32:01.999999 1997 PST'); INSERT INTO TIMESTAMP_TBL VALUES ('Mon Feb 10 17:32:01.4 1997 PST'); INSERT INTO TIMESTAMP_TBL VALUES ('Mon Feb 10 17:32:01.5 1997 PST'); INSERT INTO TIMESTAMP_TBL VALUES ('Mon Feb 10 17:32:01.6 1997 PST'); -- ISO 8601 format INSERT INTO TIMESTAMP_TBL VALUES ('1997-01-02'); INSERT INTO TIMESTAMP_TBL VALUES ('1997-01-02 03:04:05'); INSERT INTO TIMESTAMP_TBL VALUES ('1997-02-10 17:32:01-08'); INSERT INTO TIMESTAMP_TBL VALUES ('1997-02-10 17:32:01-0800'); INSERT INTO TIMESTAMP_TBL VALUES ('1997-02-10 17:32:01 -08:00'); INSERT INTO TIMESTAMP_TBL VALUES ('19970210 173201 -0800'); INSERT INTO TIMESTAMP_TBL VALUES ('1997-06-10 17:32:01 -07:00'); INSERT INTO TIMESTAMP_TBL VALUES ('2001-09-22T18:19:20'); -- POSIX format (note that the timezone abbrev is just decoration here) INSERT INTO TIMESTAMP_TBL VALUES ('2000-03-15 08:14:01 GMT+8'); INSERT INTO TIMESTAMP_TBL VALUES ('2000-03-15 13:14:02 GMT-1'); INSERT INTO TIMESTAMP_TBL VALUES ('2000-03-15 12:14:03 GMT-2'); INSERT INTO TIMESTAMP_TBL VALUES ('2000-03-15 03:14:04 PST+8'); INSERT INTO TIMESTAMP_TBL VALUES ('2000-03-15 02:14:05 MST+7:00'); -- Variations for acceptable input formats INSERT INTO TIMESTAMP_TBL VALUES ('Feb 10 17:32:01 1997 -0800'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 10 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 10 5:32PM 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('1997/02/10 17:32:01-0800'); INSERT INTO TIMESTAMP_TBL VALUES ('1997-02-10 17:32:01 PST'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb-10-1997 17:32:01 PST'); INSERT INTO TIMESTAMP_TBL VALUES ('02-10-1997 17:32:01 PST'); INSERT INTO TIMESTAMP_TBL VALUES ('19970210 173201 PST'); SET datestyle TO ymd; INSERT INTO TIMESTAMP_TBL VALUES ('97FEB10 5:32:01PM UTC'); INSERT INTO TIMESTAMP_TBL VALUES ('97/02/10 17:32:01 UTC'); RESET datestyle; INSERT INTO TIMESTAMP_TBL VALUES ('1997.041 17:32:01 UTC'); INSERT INTO TIMESTAMP_TBL VALUES ('19970210 173201 America/New_York'); -- this fails (even though TZ is a no-op, we still look it up) INSERT INTO TIMESTAMP_TBL VALUES ('19970710 173201 America/Does_not_exist'); -- Check date conversion and date arithmetic INSERT INTO TIMESTAMP_TBL VALUES ('1997-06-10 18:32:01 PDT'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 10 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 11 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 12 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 13 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 14 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 15 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 0097 BC'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 0097'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 0597'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 1097'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 1697'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 1797'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 1897'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 2097'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 28 17:32:01 1996'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 29 17:32:01 1996'); INSERT INTO TIMESTAMP_TBL VALUES ('Mar 01 17:32:01 1996'); INSERT INTO TIMESTAMP_TBL VALUES ('Dec 30 17:32:01 1996'); INSERT INTO TIMESTAMP_TBL VALUES ('Dec 31 17:32:01 1996'); INSERT INTO TIMESTAMP_TBL VALUES ('Jan 01 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 28 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 29 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Mar 01 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Dec 30 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Dec 31 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Dec 31 17:32:01 1999'); INSERT INTO TIMESTAMP_TBL VALUES ('Jan 01 17:32:01 2000'); INSERT INTO TIMESTAMP_TBL VALUES ('Dec 31 17:32:01 2000'); INSERT INTO TIMESTAMP_TBL VALUES ('Jan 01 17:32:01 2001'); -- Currently unsupported syntax and ranges INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 -0097'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 5097 BC'); SELECT '' AS "64", d1 FROM TIMESTAMP_TBL; -- Check behavior at the lower boundary of the timestamp range SELECT '4714-11-24 00:00:00 BC'::timestamp; SELECT '4714-11-23 23:59:59 BC'::timestamp; -- out of range -- The upper boundary differs between integer and float timestamps, so no check -- Demonstrate functions and operators SELECT '' AS "48", d1 FROM TIMESTAMP_TBL WHERE d1 > timestamp without time zone '1997-01-02'; SELECT '' AS "15", d1 FROM TIMESTAMP_TBL WHERE d1 < timestamp without time zone '1997-01-02'; SELECT '' AS one, d1 FROM TIMESTAMP_TBL WHERE d1 = timestamp without time zone '1997-01-02'; SELECT '' AS "63", d1 FROM TIMESTAMP_TBL WHERE d1 != timestamp without time zone '1997-01-02'; SELECT '' AS "16", d1 FROM TIMESTAMP_TBL WHERE d1 <= timestamp without time zone '1997-01-02'; SELECT '' AS "49", d1 FROM TIMESTAMP_TBL WHERE d1 >= timestamp without time zone '1997-01-02'; SELECT '' AS "54", d1 - timestamp without time zone '1997-01-02' AS diff FROM TIMESTAMP_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; SELECT '' AS date_trunc_week, date_trunc('week', timestamp '2004-02-29 15:44:17.71393') AS week_trunc; -- Test casting within a BETWEEN qualifier SELECT '' AS "54", d1 - timestamp without time zone '1997-01-02' AS diff FROM TIMESTAMP_TBL WHERE d1 BETWEEN timestamp without time zone '1902-01-01' AND timestamp without time zone '2038-01-01'; SELECT '' AS "54", d1 AS "timestamp", date_part('year', d1) AS year, date_part('month', d1) AS month, date_part('day', d1) AS day, date_part('hour', d1) AS hour, date_part('minute', d1) AS minute, date_part('second', d1) AS second FROM TIMESTAMP_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; SELECT '' AS "54", d1 AS "timestamp", date_part('quarter', d1) AS quarter, date_part('msec', d1) AS msec, date_part('usec', d1) AS usec FROM TIMESTAMP_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; SELECT '' AS "54", d1 AS "timestamp", date_part('isoyear', d1) AS isoyear, date_part('week', d1) AS week, date_part('dow', d1) AS dow FROM TIMESTAMP_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; -- TO_CHAR() SELECT '' AS to_char_1, to_char(d1, 'DAY Day day DY Dy dy MONTH Month month RM MON Mon mon') FROM TIMESTAMP_TBL; SELECT '' AS to_char_2, to_char(d1, 'FMDAY FMDay FMday FMMONTH FMMonth FMmonth FMRM') FROM TIMESTAMP_TBL; SELECT '' AS to_char_3, to_char(d1, 'Y,YYY YYYY YYY YY Y CC Q MM WW DDD DD D J') FROM TIMESTAMP_TBL; SELECT '' AS to_char_4, to_char(d1, 'FMY,YYY FMYYYY FMYYY FMYY FMY FMCC FMQ FMMM FMWW FMDDD FMDD FMD FMJ') FROM TIMESTAMP_TBL; SELECT '' AS to_char_5, to_char(d1, 'HH HH12 HH24 MI SS SSSS') FROM TIMESTAMP_TBL; SELECT '' AS to_char_6, to_char(d1, E'"HH:MI:SS is" HH:MI:SS "\\"text between quote marks\\""') FROM TIMESTAMP_TBL; SELECT '' AS to_char_7, to_char(d1, 'HH24--text--MI--text--SS') FROM TIMESTAMP_TBL; SELECT '' AS to_char_8, to_char(d1, 'YYYYTH YYYYth Jth') FROM TIMESTAMP_TBL; SELECT '' AS to_char_9, to_char(d1, 'YYYY A.D. YYYY a.d. YYYY bc HH:MI:SS P.M. HH:MI:SS p.m. HH:MI:SS pm') FROM TIMESTAMP_TBL; SELECT '' AS to_char_10, to_char(d1, 'IYYY IYY IY I IW IDDD ID') FROM TIMESTAMP_TBL; SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID') FROM TIMESTAMP_TBL; -- timestamp numeric fields constructor SELECT make_timestamp (2014, 12, 28, 6, 30, 45.887); pgFormatter-4.2/t/pg-test-files/expected/timestamptz.sql000066400000000000000000000570421361326045100235130ustar00rootroot00000000000000-- -- TIMESTAMPTZ -- CREATE TABLE TIMESTAMPTZ_TBL ( d1 timestamp(2 ) with time zone ); -- Test shorthand input values -- We can't just "select" the results since they aren't constants; test for -- equality instead. We can do that by running the test inside a transaction -- block, within which the value of 'now' shouldn't change. We also check -- that 'now' *does* change over a reasonable interval such as 100 msec. -- NOTE: it is possible for this part of the test to fail if the transaction -- block is entered exactly at local midnight; then 'now' and 'today' have -- the same values and the counts will come out different. INSERT INTO TIMESTAMPTZ_TBL VALUES ('now'); SELECT pg_sleep(0.1); BEGIN; INSERT INTO TIMESTAMPTZ_TBL VALUES ('now'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('today'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('yesterday'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('tomorrow'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('tomorrow EST'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('tomorrow zulu'); SELECT count(*) AS One FROM TIMESTAMPTZ_TBL WHERE d1 = timestamp WITH time zone 'today'; SELECT count(*) AS One FROM TIMESTAMPTZ_TBL WHERE d1 = timestamp WITH time zone 'tomorrow'; SELECT count(*) AS One FROM TIMESTAMPTZ_TBL WHERE d1 = timestamp WITH time zone 'yesterday'; SELECT count(*) AS One FROM TIMESTAMPTZ_TBL WHERE d1 = timestamp(2 ) WITH time zone 'now'; COMMIT; DELETE FROM TIMESTAMPTZ_TBL; -- verify uniform transaction time within transaction block BEGIN; INSERT INTO TIMESTAMPTZ_TBL VALUES ('now'); SELECT pg_sleep(0.1); INSERT INTO TIMESTAMPTZ_TBL VALUES ('now'); SELECT pg_sleep(0.1); SELECT count(*) AS two FROM TIMESTAMPTZ_TBL WHERE d1 = timestamp(2 ) WITH time zone 'now'; COMMIT; DELETE FROM TIMESTAMPTZ_TBL; -- Special values INSERT INTO TIMESTAMPTZ_TBL VALUES ('-infinity'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('infinity'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('epoch'); -- Obsolete special values INSERT INTO TIMESTAMPTZ_TBL VALUES ('invalid'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('undefined'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('current'); -- Postgres v6.0 standard output format INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mon Feb 10 17:32:01 1997 PST'); -- Variations on Postgres v6.1 standard output format INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mon Feb 10 17:32:01.000001 1997 PST'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mon Feb 10 17:32:01.999999 1997 PST'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mon Feb 10 17:32:01.4 1997 PST'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mon Feb 10 17:32:01.5 1997 PST'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mon Feb 10 17:32:01.6 1997 PST'); -- ISO 8601 format INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-01-02'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-01-02 03:04:05'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-02-10 17:32:01-08'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-02-10 17:32:01-0800'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-02-10 17:32:01 -08:00'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('19970210 173201 -0800'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-06-10 17:32:01 -07:00'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('2001-09-22T18:19:20'); -- POSIX format (note that the timezone abbrev is just decoration here) INSERT INTO TIMESTAMPTZ_TBL VALUES ('2000-03-15 08:14:01 GMT+8'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('2000-03-15 13:14:02 GMT-1'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('2000-03-15 12:14:03 GMT-2'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('2000-03-15 03:14:04 PST+8'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('2000-03-15 02:14:05 MST+7:00'); -- Variations for acceptable input formats INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 10 17:32:01 1997 -0800'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 10 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 10 5:32PM 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997/02/10 17:32:01-0800'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-02-10 17:32:01 PST'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb-10-1997 17:32:01 PST'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('02-10-1997 17:32:01 PST'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('19970210 173201 PST'); SET datestyle TO ymd; INSERT INTO TIMESTAMPTZ_TBL VALUES ('97FEB10 5:32:01PM UTC'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('97/02/10 17:32:01 UTC'); RESET datestyle; INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997.041 17:32:01 UTC'); -- timestamps at different timezones INSERT INTO TIMESTAMPTZ_TBL VALUES ('19970210 173201 America/New_York'); SELECT '19970210 173201' AT TIME ZONE 'America/New_York'; INSERT INTO TIMESTAMPTZ_TBL VALUES ('19970710 173201 America/New_York'); SELECT '19970710 173201' AT TIME ZONE 'America/New_York'; INSERT INTO TIMESTAMPTZ_TBL VALUES ('19970710 173201 America/Does_not_exist'); SELECT '19970710 173201' AT TIME ZONE 'America/Does_not_exist'; -- Daylight saving time for timestamps beyond 32-bit time_t range. SELECT '20500710 173201 Europe/Helsinki'::timestamptz; -- DST SELECT '20500110 173201 Europe/Helsinki'::timestamptz; -- non-DST SELECT '205000-07-10 17:32:01 Europe/Helsinki'::timestamptz; -- DST SELECT '205000-01-10 17:32:01 Europe/Helsinki'::timestamptz; -- non-DST -- Check date conversion and date arithmetic INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-06-10 18:32:01 PDT'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 10 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 11 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 12 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 13 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 14 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 15 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 0097 BC'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 0097'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 0597'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 1097'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 1697'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 1797'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 1897'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 2097'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 28 17:32:01 1996'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 29 17:32:01 1996'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mar 01 17:32:01 1996'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Dec 30 17:32:01 1996'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Dec 31 17:32:01 1996'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Jan 01 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 28 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 29 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mar 01 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Dec 30 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Dec 31 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Dec 31 17:32:01 1999'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Jan 01 17:32:01 2000'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Dec 31 17:32:01 2000'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Jan 01 17:32:01 2001'); -- Currently unsupported syntax and ranges INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 -0097'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 5097 BC'); -- Alternative field order that we've historically supported (sort of) -- with regular and POSIXy timezone specs SELECT 'Wed Jul 11 10:51:14 America/New_York 2001'::timestamptz; SELECT 'Wed Jul 11 10:51:14 GMT-4 2001'::timestamptz; SELECT 'Wed Jul 11 10:51:14 GMT+4 2001'::timestamptz; SELECT 'Wed Jul 11 10:51:14 PST-03:00 2001'::timestamptz; SELECT 'Wed Jul 11 10:51:14 PST+03:00 2001'::timestamptz; SELECT '' AS "64", d1 FROM TIMESTAMPTZ_TBL; -- Check behavior at the lower boundary of the timestamp range SELECT '4714-11-24 00:00:00+00 BC'::timestamptz; SELECT '4714-11-23 16:00:00-08 BC'::timestamptz; SELECT 'Sun Nov 23 16:00:00 4714 PST BC'::timestamptz; SELECT '4714-11-23 23:59:59+00 BC'::timestamptz; -- out of range -- The upper boundary differs between integer and float timestamps, so no check -- Demonstrate functions and operators SELECT '' AS "48", d1 FROM TIMESTAMPTZ_TBL WHERE d1 > timestamp WITH time zone '1997-01-02'; SELECT '' AS "15", d1 FROM TIMESTAMPTZ_TBL WHERE d1 < timestamp WITH time zone '1997-01-02'; SELECT '' AS one, d1 FROM TIMESTAMPTZ_TBL WHERE d1 = timestamp WITH time zone '1997-01-02'; SELECT '' AS "63", d1 FROM TIMESTAMPTZ_TBL WHERE d1 != timestamp WITH time zone '1997-01-02'; SELECT '' AS "16", d1 FROM TIMESTAMPTZ_TBL WHERE d1 <= timestamp WITH time zone '1997-01-02'; SELECT '' AS "49", d1 FROM TIMESTAMPTZ_TBL WHERE d1 >= timestamp WITH time zone '1997-01-02'; SELECT '' AS "54", d1 - timestamp WITH time zone '1997-01-02' AS diff FROM TIMESTAMPTZ_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; SELECT '' AS date_trunc_week, date_trunc('week', timestamp WITH time zone '2004-02-29 15:44:17.71393') AS week_trunc; SELECT '' AS date_trunc_at_tz, date_trunc('day', timestamp WITH time zone '2001-02-16 20:38:40+00', 'Australia/Sydney') AS sydney_trunc; -- zone name SELECT '' AS date_trunc_at_tz, date_trunc('day', timestamp WITH time zone '2001-02-16 20:38:40+00', 'GMT') AS gmt_trunc; -- fixed-offset abbreviation SELECT '' AS date_trunc_at_tz, date_trunc('day', timestamp WITH time zone '2001-02-16 20:38:40+00', 'VET') AS vet_trunc; -- variable-offset abbreviation -- Test casting within a BETWEEN qualifier SELECT '' AS "54", d1 - timestamp WITH time zone '1997-01-02' AS diff FROM TIMESTAMPTZ_TBL WHERE d1 BETWEEN timestamp WITH time zone '1902-01-01' AND timestamp WITH time zone '2038-01-01'; SELECT '' AS "54", d1 AS timestamptz, date_part('year', d1) AS year, date_part('month', d1) AS month, date_part('day', d1) AS day, date_part('hour', d1) AS hour, date_part('minute', d1) AS minute, date_part('second', d1) AS second FROM TIMESTAMPTZ_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; SELECT '' AS "54", d1 AS timestamptz, date_part('quarter', d1) AS quarter, date_part('msec', d1) AS msec, date_part('usec', d1) AS usec FROM TIMESTAMPTZ_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; SELECT '' AS "54", d1 AS timestamptz, date_part('isoyear', d1) AS isoyear, date_part('week', d1) AS week, date_part('dow', d1) AS dow FROM TIMESTAMPTZ_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; -- TO_CHAR() SELECT '' AS to_char_1, to_char(d1, 'DAY Day day DY Dy dy MONTH Month month RM MON Mon mon') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_2, to_char(d1, 'FMDAY FMDay FMday FMMONTH FMMonth FMmonth FMRM') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_3, to_char(d1, 'Y,YYY YYYY YYY YY Y CC Q MM WW DDD DD D J') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_4, to_char(d1, 'FMY,YYY FMYYYY FMYYY FMYY FMY FMCC FMQ FMMM FMWW FMDDD FMDD FMD FMJ') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_5, to_char(d1, 'HH HH12 HH24 MI SS SSSS') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_6, to_char(d1, E'"HH:MI:SS is" HH:MI:SS "\\"text between quote marks\\""') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_7, to_char(d1, 'HH24--text--MI--text--SS') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_8, to_char(d1, 'YYYYTH YYYYth Jth') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_9, to_char(d1, 'YYYY A.D. YYYY a.d. YYYY bc HH:MI:SS P.M. HH:MI:SS p.m. HH:MI:SS pm') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_10, to_char(d1, 'IYYY IYY IY I IW IDDD ID') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID') FROM TIMESTAMPTZ_TBL; -- Check OF, TZH, TZM with various zone offsets, particularly fractional hours SET timezone = '00:00'; SELECT to_char(now(), 'OF') AS "OF", to_char(now(), 'TZH:TZM') AS "TZH:TZM"; SET timezone = '+02:00'; SELECT to_char(now(), 'OF') AS "OF", to_char(now(), 'TZH:TZM') AS "TZH:TZM"; SET timezone = '-13:00'; SELECT to_char(now(), 'OF') AS "OF", to_char(now(), 'TZH:TZM') AS "TZH:TZM"; SET timezone = '-00:30'; SELECT to_char(now(), 'OF') AS "OF", to_char(now(), 'TZH:TZM') AS "TZH:TZM"; SET timezone = '00:30'; SELECT to_char(now(), 'OF') AS "OF", to_char(now(), 'TZH:TZM') AS "TZH:TZM"; SET timezone = '-04:30'; SELECT to_char(now(), 'OF') AS "OF", to_char(now(), 'TZH:TZM') AS "TZH:TZM"; SET timezone = '04:30'; SELECT to_char(now(), 'OF') AS "OF", to_char(now(), 'TZH:TZM') AS "TZH:TZM"; SET timezone = '-04:15'; SELECT to_char(now(), 'OF') AS "OF", to_char(now(), 'TZH:TZM') AS "TZH:TZM"; SET timezone = '04:15'; SELECT to_char(now(), 'OF') AS "OF", to_char(now(), 'TZH:TZM') AS "TZH:TZM"; RESET timezone; CREATE TABLE TIMESTAMPTZ_TST ( a int, b timestamptz ); -- Test year field value with len > 4 INSERT INTO TIMESTAMPTZ_TST VALUES (1, 'Sat Mar 12 23:58:48 1000 IST'); INSERT INTO TIMESTAMPTZ_TST VALUES (2, 'Sat Mar 12 23:58:48 10000 IST'); INSERT INTO TIMESTAMPTZ_TST VALUES (3, 'Sat Mar 12 23:58:48 100000 IST'); INSERT INTO TIMESTAMPTZ_TST VALUES (3, '10000 Mar 12 23:58:48 IST'); INSERT INTO TIMESTAMPTZ_TST VALUES (4, '100000312 23:58:48 IST'); INSERT INTO TIMESTAMPTZ_TST VALUES (4, '1000000312 23:58:48 IST'); --Verify data SELECT * FROM TIMESTAMPTZ_TST ORDER BY a; --Cleanup DROP TABLE TIMESTAMPTZ_TST; -- test timestamptz constructors SET TimeZone TO 'America/New_York'; -- numeric timezone SELECT make_timestamptz (1973, 07, 15, 08, 15, 55.33); SELECT make_timestamptz (1973, 07, 15, 08, 15, 55.33, '+2'); SELECT make_timestamptz (1973, 07, 15, 08, 15, 55.33, '-2'); WITH tzs ( tz ) AS ( VALUES ('+1'), ('+1:'), ('+1:0'), ('+100'), ('+1:00'), ('+01:00'), ('+10'), ('+1000'), ('+10:'), ('+10:0'), ('+10:00'), ('+10:00:'), ('+10:00:1'), ('+10:00:01'), ('+10:00:10')) SELECT make_timestamptz (2010, 2, 27, 3, 45, 00, tz), tz FROM tzs; -- these should fail SELECT make_timestamptz (1973, 07, 15, 08, 15, 55.33, '2'); SELECT make_timestamptz (2014, 12, 10, 10, 10, 10, '+16'); SELECT make_timestamptz (2014, 12, 10, 10, 10, 10, '-16'); -- should be true SELECT make_timestamptz (1973, 07, 15, 08, 15, 55.33, '+2') = '1973-07-15 08:15:55.33+02'::timestamptz; -- full timezone names SELECT make_timestamptz (2014, 12, 10, 0, 0, 0, 'Europe/Prague') = timestamptz '2014-12-10 00:00:00 Europe/Prague'; SELECT make_timestamptz (2014, 12, 10, 0, 0, 0, 'Europe/Prague') AT TIME ZONE 'UTC'; SELECT make_timestamptz (1846, 12, 10, 0, 0, 0, 'Asia/Manila') AT TIME ZONE 'UTC'; SELECT make_timestamptz (1881, 12, 10, 0, 0, 0, 'Europe/Paris') AT TIME ZONE 'UTC'; SELECT make_timestamptz (1910, 12, 24, 0, 0, 0, 'Nehwon/Lankhmar'); -- abbreviations SELECT make_timestamptz (2008, 12, 10, 10, 10, 10, 'EST'); SELECT make_timestamptz (2008, 12, 10, 10, 10, 10, 'EDT'); SELECT make_timestamptz (2014, 12, 10, 10, 10, 10, 'PST8PDT'); RESET TimeZone; -- -- Test behavior with a dynamic (time-varying) timezone abbreviation. -- These tests rely on the knowledge that MSK (Europe/Moscow standard time) -- moved forwards in Mar 2011 and backwards again in Oct 2014. -- SET TimeZone TO 'UTC'; SELECT '2011-03-27 00:00:00 Europe/Moscow'::timestamptz; SELECT '2011-03-27 01:00:00 Europe/Moscow'::timestamptz; SELECT '2011-03-27 01:59:59 Europe/Moscow'::timestamptz; SELECT '2011-03-27 02:00:00 Europe/Moscow'::timestamptz; SELECT '2011-03-27 02:00:01 Europe/Moscow'::timestamptz; SELECT '2011-03-27 02:59:59 Europe/Moscow'::timestamptz; SELECT '2011-03-27 03:00:00 Europe/Moscow'::timestamptz; SELECT '2011-03-27 03:00:01 Europe/Moscow'::timestamptz; SELECT '2011-03-27 04:00:00 Europe/Moscow'::timestamptz; SELECT '2011-03-27 00:00:00 MSK'::timestamptz; SELECT '2011-03-27 01:00:00 MSK'::timestamptz; SELECT '2011-03-27 01:59:59 MSK'::timestamptz; SELECT '2011-03-27 02:00:00 MSK'::timestamptz; SELECT '2011-03-27 02:00:01 MSK'::timestamptz; SELECT '2011-03-27 02:59:59 MSK'::timestamptz; SELECT '2011-03-27 03:00:00 MSK'::timestamptz; SELECT '2011-03-27 03:00:01 MSK'::timestamptz; SELECT '2011-03-27 04:00:00 MSK'::timestamptz; SELECT '2014-10-26 00:00:00 Europe/Moscow'::timestamptz; SELECT '2014-10-26 00:59:59 Europe/Moscow'::timestamptz; SELECT '2014-10-26 01:00:00 Europe/Moscow'::timestamptz; SELECT '2014-10-26 01:00:01 Europe/Moscow'::timestamptz; SELECT '2014-10-26 02:00:00 Europe/Moscow'::timestamptz; SELECT '2014-10-26 00:00:00 MSK'::timestamptz; SELECT '2014-10-26 00:59:59 MSK'::timestamptz; SELECT '2014-10-26 01:00:00 MSK'::timestamptz; SELECT '2014-10-26 01:00:01 MSK'::timestamptz; SELECT '2014-10-26 02:00:00 MSK'::timestamptz; SELECT '2011-03-27 00:00:00'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 01:00:00'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 01:59:59'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 02:00:00'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 02:00:01'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 02:59:59'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 03:00:00'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 03:00:01'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 04:00:00'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 00:00:00'::timestamp AT TIME ZONE 'MSK'; SELECT '2011-03-27 01:00:00'::timestamp AT TIME ZONE 'MSK'; SELECT '2011-03-27 01:59:59'::timestamp AT TIME ZONE 'MSK'; SELECT '2011-03-27 02:00:00'::timestamp AT TIME ZONE 'MSK'; SELECT '2011-03-27 02:00:01'::timestamp AT TIME ZONE 'MSK'; SELECT '2011-03-27 02:59:59'::timestamp AT TIME ZONE 'MSK'; SELECT '2011-03-27 03:00:00'::timestamp AT TIME ZONE 'MSK'; SELECT '2011-03-27 03:00:01'::timestamp AT TIME ZONE 'MSK'; SELECT '2011-03-27 04:00:00'::timestamp AT TIME ZONE 'MSK'; SELECT '2014-10-26 00:00:00'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-26 00:59:59'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-26 01:00:00'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-26 01:00:01'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-26 02:00:00'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-26 00:00:00'::timestamp AT TIME ZONE 'MSK'; SELECT '2014-10-26 00:59:59'::timestamp AT TIME ZONE 'MSK'; SELECT '2014-10-26 01:00:00'::timestamp AT TIME ZONE 'MSK'; SELECT '2014-10-26 01:00:01'::timestamp AT TIME ZONE 'MSK'; SELECT '2014-10-26 02:00:00'::timestamp AT TIME ZONE 'MSK'; SELECT make_timestamptz (2014, 10, 26, 0, 0, 0, 'MSK'); SELECT make_timestamptz (2014, 10, 26, 1, 0, 0, 'MSK'); SELECT to_timestamp(0); -- 1970-01-01 00:00:00+00 SELECT to_timestamp(946684800); -- 2000-01-01 00:00:00+00 SELECT to_timestamp(1262349296.7890123); -- 2010-01-01 12:34:56.789012+00 -- edge cases SELECT to_timestamp(- 210866803200); -- 4714-11-24 00:00:00+00 BC -- upper limit varies between integer and float timestamps, so hard to test -- nonfinite values SELECT to_timestamp(' Infinity'::float); SELECT to_timestamp('-Infinity'::float); SELECT to_timestamp('NaN'::float); SET TimeZone TO 'Europe/Moscow'; SELECT '2011-03-26 21:00:00 UTC'::timestamptz; SELECT '2011-03-26 22:00:00 UTC'::timestamptz; SELECT '2011-03-26 22:59:59 UTC'::timestamptz; SELECT '2011-03-26 23:00:00 UTC'::timestamptz; SELECT '2011-03-26 23:00:01 UTC'::timestamptz; SELECT '2011-03-26 23:59:59 UTC'::timestamptz; SELECT '2011-03-27 00:00:00 UTC'::timestamptz; SELECT '2014-10-25 21:00:00 UTC'::timestamptz; SELECT '2014-10-25 21:59:59 UTC'::timestamptz; SELECT '2014-10-25 22:00:00 UTC'::timestamptz; SELECT '2014-10-25 22:00:01 UTC'::timestamptz; SELECT '2014-10-25 23:00:00 UTC'::timestamptz; RESET TimeZone; SELECT '2011-03-26 21:00:00 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-26 22:00:00 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-26 22:59:59 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-26 23:00:00 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-26 23:00:01 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-26 23:59:59 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 00:00:00 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-25 21:00:00 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-25 21:59:59 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-25 22:00:00 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-25 22:00:01 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-25 23:00:00 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-26 21:00:00 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2011-03-26 22:00:00 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2011-03-26 22:59:59 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2011-03-26 23:00:00 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2011-03-26 23:00:01 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2011-03-26 23:59:59 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2011-03-27 00:00:00 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2014-10-25 21:00:00 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2014-10-25 21:59:59 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2014-10-25 22:00:00 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2014-10-25 22:00:01 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2014-10-25 23:00:00 UTC'::timestamptz AT TIME ZONE 'MSK'; -- -- Test that AT TIME ZONE isn't misoptimized when using an index (bug #14504) -- CREATE temp TABLE tmptz ( f1 timestamptz PRIMARY KEY ); INSERT INTO tmptz VALUES ('2017-01-18 00:00+00'); EXPLAIN ( COSTS OFF ) SELECT * FROM tmptz WHERE f1 at time zone 'utc' = '2017-01-18 00:00'; SELECT * FROM tmptz WHERE f1 at time zone 'utc' = '2017-01-18 00:00'; pgFormatter-4.2/t/pg-test-files/expected/timetz.sql000066400000000000000000000030611361326045100224360ustar00rootroot00000000000000-- -- TIMETZ -- CREATE TABLE TIMETZ_TBL ( f1 time(2 ) with time zone ); INSERT INTO TIMETZ_TBL VALUES ('00:01 PDT'); INSERT INTO TIMETZ_TBL VALUES ('01:00 PDT'); INSERT INTO TIMETZ_TBL VALUES ('02:03 PDT'); INSERT INTO TIMETZ_TBL VALUES ('07:07 PST'); INSERT INTO TIMETZ_TBL VALUES ('08:08 EDT'); INSERT INTO TIMETZ_TBL VALUES ('11:59 PDT'); INSERT INTO TIMETZ_TBL VALUES ('12:00 PDT'); INSERT INTO TIMETZ_TBL VALUES ('12:01 PDT'); INSERT INTO TIMETZ_TBL VALUES ('23:59 PDT'); INSERT INTO TIMETZ_TBL VALUES ('11:59:59.99 PM PDT'); INSERT INTO TIMETZ_TBL VALUES ('2003-03-07 15:36:39 America/New_York'); INSERT INTO TIMETZ_TBL VALUES ('2003-07-07 15:36:39 America/New_York'); -- this should fail (the timezone offset is not known) INSERT INTO TIMETZ_TBL VALUES ('15:36:39 America/New_York'); SELECT f1 AS "Time TZ" FROM TIMETZ_TBL; SELECT f1 AS "Three" FROM TIMETZ_TBL WHERE f1 < '05:06:07-07'; SELECT f1 AS "Seven" FROM TIMETZ_TBL WHERE f1 > '05:06:07-07'; SELECT f1 AS "None" FROM TIMETZ_TBL WHERE f1 < '00:00-07'; SELECT f1 AS "Ten" FROM TIMETZ_TBL WHERE f1 >= '00:00-07'; -- -- TIME simple math -- -- We now make a distinction between time and intervals, -- and adding two times together makes no sense at all. -- Leave in one query to show that it is rejected, -- and do the rest of the testing in horology.sql -- where we do mixed-type arithmetic. - thomas 2000-12-02 SELECT f1 + time WITH time zone '00:01' AS "Illegal" FROM TIMETZ_TBL; pgFormatter-4.2/t/pg-test-files/expected/transactions.sql000066400000000000000000000363211361326045100236370ustar00rootroot00000000000000-- -- TRANSACTIONS -- BEGIN; SELECT * INTO TABLE xacttest FROM aggtest; INSERT INTO xacttest (a, b) VALUES (777, 777.777); END; -- should retrieve one value-- SELECT a FROM xacttest WHERE a > 100; BEGIN; CREATE TABLE disappear ( a int4 ); DELETE FROM aggtest; -- should be empty SELECT * FROM aggtest; ABORT; -- should not exist SELECT oid FROM pg_class WHERE relname = 'disappear'; -- should have members again SELECT * FROM aggtest; -- Read-only tests CREATE TABLE writetest ( a int ); CREATE TEMPORARY TABLE temptest ( a int ); BEGIN; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY, DEFERRABLE; -- ok SELECT * FROM writetest; -- ok SET TRANSACTION READ WRITE; --fail COMMIT; BEGIN; SET TRANSACTION READ ONLY; -- ok SET TRANSACTION READ WRITE; -- ok SET TRANSACTION READ ONLY; -- ok SELECT * FROM writetest; -- ok SAVEPOINT x; SET TRANSACTION READ ONLY; -- ok SELECT * FROM writetest; -- ok SET TRANSACTION READ ONLY; -- ok SET TRANSACTION READ WRITE; --fail COMMIT; BEGIN; SET TRANSACTION READ WRITE; -- ok SAVEPOINT x; SET TRANSACTION READ WRITE; -- ok SET TRANSACTION READ ONLY; -- ok SELECT * FROM writetest; -- ok SET TRANSACTION READ ONLY; -- ok SET TRANSACTION READ WRITE; --fail COMMIT; BEGIN; SET TRANSACTION READ WRITE; -- ok SAVEPOINT x; SET TRANSACTION READ ONLY; -- ok SELECT * FROM writetest; -- ok ROLLBACK TO SAVEPOINT x; SHOW transaction_read_only; -- off SAVEPOINT y; SET TRANSACTION READ ONLY; -- ok SELECT * FROM writetest; -- ok RELEASE SAVEPOINT y; SHOW transaction_read_only; -- off COMMIT; SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY; DROP TABLE writetest; -- fail INSERT INTO writetest VALUES (1); -- fail SELECT * FROM writetest; -- ok DELETE FROM temptest; -- ok UPDATE temptest SET a = 0 FROM writetest WHERE temptest.a = 1 AND writetest.a = temptest.a; -- ok PREPARE test AS UPDATE writetest SET a = 0; -- ok EXECUTE test; -- fail SELECT * FROM writetest, temptest; -- ok CREATE TABLE test AS SELECT * FROM writetest; -- fail START TRANSACTION READ WRITE; DROP TABLE writetest; -- ok COMMIT; -- Subtransactions, basic tests -- create & drop tables SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE; CREATE TABLE trans_foobar ( a int ); BEGIN; CREATE TABLE trans_foo ( a int ); SAVEPOINT one; DROP TABLE trans_foo; CREATE TABLE trans_bar ( a int ); ROLLBACK TO SAVEPOINT one; RELEASE SAVEPOINT one; SAVEPOINT two; CREATE TABLE trans_baz ( a int ); RELEASE SAVEPOINT two; DROP TABLE trans_foobar; CREATE TABLE trans_barbaz ( a int ); COMMIT; -- should exist: trans_barbaz, trans_baz, trans_foo SELECT * FROM trans_foo; -- should be empty SELECT * FROM trans_bar; -- shouldn't exist SELECT * FROM trans_barbaz; -- should be empty SELECT * FROM trans_baz; -- should be empty -- inserts BEGIN; INSERT INTO trans_foo VALUES (1); SAVEPOINT one; INSERT INTO trans_bar VALUES (1); ROLLBACK TO one; RELEASE SAVEPOINT one; SAVEPOINT two; INSERT INTO trans_barbaz VALUES (1); RELEASE two; SAVEPOINT three; SAVEPOINT four; INSERT INTO trans_foo VALUES (2); RELEASE SAVEPOINT four; ROLLBACK TO SAVEPOINT three; RELEASE SAVEPOINT three; INSERT INTO trans_foo VALUES (3); COMMIT; SELECT * FROM trans_foo; -- should have 1 and 3 SELECT * FROM trans_barbaz; -- should have 1 -- test whole-tree commit BEGIN; SAVEPOINT one; SELECT trans_foo; ROLLBACK TO SAVEPOINT one; RELEASE SAVEPOINT one; SAVEPOINT two; CREATE TABLE savepoints ( a int ); SAVEPOINT three; INSERT INTO savepoints VALUES (1); SAVEPOINT four; INSERT INTO savepoints VALUES (2); SAVEPOINT five; INSERT INTO savepoints VALUES (3); ROLLBACK TO SAVEPOINT five; COMMIT; COMMIT; -- should not be in a transaction block SELECT * FROM savepoints; -- test whole-tree rollback BEGIN; SAVEPOINT one; DELETE FROM savepoints WHERE a = 1; RELEASE SAVEPOINT one; SAVEPOINT two; DELETE FROM savepoints WHERE a = 1; SAVEPOINT three; DELETE FROM savepoints WHERE a = 2; ROLLBACK; COMMIT; -- should not be in a transaction block SELECT * FROM savepoints; -- test whole-tree commit on an aborted subtransaction BEGIN; INSERT INTO savepoints VALUES (4); SAVEPOINT one; INSERT INTO savepoints VALUES (5); SELECT trans_foo; COMMIT; SELECT * FROM savepoints; BEGIN; INSERT INTO savepoints VALUES (6); SAVEPOINT one; INSERT INTO savepoints VALUES (7); RELEASE SAVEPOINT one; INSERT INTO savepoints VALUES (8); COMMIT; -- rows 6 and 8 should have been created by the same xact SELECT a.xmin = b.xmin FROM savepoints a, savepoints b WHERE a.a = 6 AND b.a = 8; -- rows 6 and 7 should have been created by different xacts SELECT a.xmin = b.xmin FROM savepoints a, savepoints b WHERE a.a = 6 AND b.a = 7; BEGIN; INSERT INTO savepoints VALUES (9); SAVEPOINT one; INSERT INTO savepoints VALUES (10); ROLLBACK TO SAVEPOINT one; INSERT INTO savepoints VALUES (11); COMMIT; SELECT a FROM savepoints WHERE a IN (9, 10, 11); -- rows 9 and 11 should have been created by different xacts SELECT a.xmin = b.xmin FROM savepoints a, savepoints b WHERE a.a = 9 AND b.a = 11; BEGIN; INSERT INTO savepoints VALUES (12); SAVEPOINT one; INSERT INTO savepoints VALUES (13); SAVEPOINT two; INSERT INTO savepoints VALUES (14); ROLLBACK TO SAVEPOINT one; INSERT INTO savepoints VALUES (15); SAVEPOINT two; INSERT INTO savepoints VALUES (16); SAVEPOINT three; INSERT INTO savepoints VALUES (17); COMMIT; SELECT a FROM savepoints WHERE a BETWEEN 12 AND 17; BEGIN; INSERT INTO savepoints VALUES (18); SAVEPOINT one; INSERT INTO savepoints VALUES (19); SAVEPOINT two; INSERT INTO savepoints VALUES (20); ROLLBACK TO SAVEPOINT one; INSERT INTO savepoints VALUES (21); ROLLBACK TO SAVEPOINT one; INSERT INTO savepoints VALUES (22); COMMIT; SELECT a FROM savepoints WHERE a BETWEEN 18 AND 22; DROP TABLE savepoints; -- only in a transaction block: SAVEPOINT one; ROLLBACK TO SAVEPOINT one; RELEASE SAVEPOINT one; -- Only "rollback to" allowed in aborted state BEGIN; SAVEPOINT one; SELECT 0 / 0; SAVEPOINT two; -- ignored till the end of ... RELEASE SAVEPOINT one; -- ignored till the end of ... ROLLBACK TO SAVEPOINT one; SELECT 1; COMMIT; SELECT 1; -- this should work -- check non-transactional behavior of cursors BEGIN; DECLARE c CURSOR FOR SELECT unique2 FROM tenk1 ORDER BY unique2; SAVEPOINT one; FETCH 10 FROM c; ROLLBACK TO SAVEPOINT one; FETCH 10 FROM c; RELEASE SAVEPOINT one; FETCH 10 FROM c; CLOSE c; DECLARE c CURSOR FOR SELECT unique2 / 0 FROM tenk1 ORDER BY unique2; SAVEPOINT two; FETCH 10 FROM c; ROLLBACK TO SAVEPOINT two; -- c is now dead to the world ... FETCH 10 FROM c; ROLLBACK TO SAVEPOINT two; RELEASE SAVEPOINT two; FETCH 10 FROM c; COMMIT; -- -- Check that "stable" functions are really stable. They should not be -- able to see the partial results of the calling query. (Ideally we would -- also check that they don't see commits of concurrent transactions, but -- that's a mite hard to do within the limitations of pg_regress.) -- SELECT * FROM xacttest; CREATE OR REPLACE FUNCTION max_xacttest () RETURNS smallint LANGUAGE sql AS 'select max(a) from xacttest' STABLE; BEGIN; UPDATE xacttest SET a = max_xacttest () + 10 WHERE a > 0; SELECT * FROM xacttest; ROLLBACK; -- But a volatile function can see the partial results of the calling query CREATE OR REPLACE FUNCTION max_xacttest () RETURNS smallint LANGUAGE sql AS 'select max(a) from xacttest' VOLATILE; BEGIN; UPDATE xacttest SET a = max_xacttest () + 10 WHERE a > 0; SELECT * FROM xacttest; ROLLBACK; -- Now the same test with plpgsql (since it depends on SPI which is different) CREATE OR REPLACE FUNCTION max_xacttest () RETURNS smallint LANGUAGE plpgsql AS 'begin return max(a) from xacttest; end' STABLE; BEGIN; UPDATE xacttest SET a = max_xacttest () + 10 WHERE a > 0; SELECT * FROM xacttest; ROLLBACK; CREATE OR REPLACE FUNCTION max_xacttest () RETURNS smallint LANGUAGE plpgsql AS 'begin return max(a) from xacttest; end' VOLATILE; BEGIN; UPDATE xacttest SET a = max_xacttest () + 10 WHERE a > 0; SELECT * FROM xacttest; ROLLBACK; -- test case for problems with dropping an open relation during abort BEGIN; SAVEPOINT x; CREATE TABLE koju ( a int UNIQUE ); INSERT INTO koju VALUES (1); INSERT INTO koju VALUES (1); ROLLBACK TO x; CREATE TABLE koju ( a int UNIQUE ); INSERT INTO koju VALUES (1); INSERT INTO koju VALUES (1); ROLLBACK; DROP TABLE trans_foo; DROP TABLE trans_baz; DROP TABLE trans_barbaz; -- test case for problems with revalidating an open relation during abort CREATE FUNCTION inverse (int) RETURNS float8 AS $$ BEGIN ANALYZE revalidate_bug; RETURN 1::float8 / $1; exception WHEN division_by_zero THEN RETURN 0; END $$ LANGUAGE plpgsql VOLATILE; CREATE TABLE revalidate_bug ( c float8 UNIQUE ); INSERT INTO revalidate_bug VALUES (1); INSERT INTO revalidate_bug VALUES (inverse (0)); DROP TABLE revalidate_bug; DROP FUNCTION inverse (int); -- verify that cursors created during an aborted subtransaction are -- closed, but that we do not rollback the effect of any FETCHs -- performed in the aborted subtransaction BEGIN; SAVEPOINT x; CREATE TABLE abc ( a int ); INSERT INTO abc VALUES (5); INSERT INTO abc VALUES (10); DECLARE foo CURSOR FOR SELECT * FROM abc; FETCH FROM foo; ROLLBACK TO x; -- should fail FETCH FROM foo; COMMIT; BEGIN; CREATE TABLE abc ( a int ); INSERT INTO abc VALUES (5); INSERT INTO abc VALUES (10); INSERT INTO abc VALUES (15); DECLARE foo CURSOR FOR SELECT * FROM abc; FETCH FROM foo; SAVEPOINT x; FETCH FROM foo; ROLLBACK TO x; FETCH FROM foo; abort; -- Test for proper cleanup after a failure in a cursor portal -- that was created in an outer subtransaction CREATE FUNCTION invert (x float8) RETURNS float8 LANGUAGE plpgsql AS $$ BEGIN RETURN 1 / x; END $$; CREATE FUNCTION create_temp_tab () RETURNS text LANGUAGE plpgsql AS $$ BEGIN CREATE TEMP TABLE new_table ( f1 float8 ); -- case of interest is that we fail while holding an open -- relcache reference to new_table INSERT INTO new_table SELECT invert (0.0); RETURN 'foo'; END $$; BEGIN; DECLARE ok CURSOR FOR SELECT * FROM int8_tbl; DECLARE ctt CURSOR FOR SELECT create_temp_tab (); FETCH ok; SAVEPOINT s1; FETCH ok; -- should work FETCH ctt; -- error occurs here ROLLBACK TO s1; FETCH ok; -- should work FETCH ctt; -- must be rejected COMMIT; DROP FUNCTION create_temp_tab (); DROP FUNCTION invert (x float8); -- Tests for AND CHAIN CREATE TABLE abc ( a int ); -- set nondefault value so we have something to override below SET default_transaction_read_only = ON; START TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, DEFERRABLE; SHOW transaction_isolation; SHOW transaction_read_only; SHOW transaction_deferrable; INSERT INTO abc VALUES (1); INSERT INTO abc VALUES (2); COMMIT AND CHAIN; -- TBLOCK_END SHOW transaction_isolation; SHOW transaction_read_only; SHOW transaction_deferrable; INSERT INTO abc VALUES ('error'); INSERT INTO abc VALUES (3); -- check it's really aborted COMMIT AND CHAIN; -- TBLOCK_ABORT_END SHOW transaction_isolation; SHOW transaction_read_only; SHOW transaction_deferrable; INSERT INTO abc VALUES (4); COMMIT; START TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, DEFERRABLE; SHOW transaction_isolation; SHOW transaction_read_only; SHOW transaction_deferrable; SAVEPOINT x; INSERT INTO abc VALUES ('error'); COMMIT AND CHAIN; -- TBLOCK_ABORT_PENDING SHOW transaction_isolation; SHOW transaction_read_only; SHOW transaction_deferrable; INSERT INTO abc VALUES (5); COMMIT; -- different mix of options just for fun START TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ WRITE, NOT DEFERRABLE; SHOW transaction_isolation; SHOW transaction_read_only; SHOW transaction_deferrable; INSERT INTO abc VALUES (6); ROLLBACK AND CHAIN; -- TBLOCK_ABORT_PENDING SHOW transaction_isolation; SHOW transaction_read_only; SHOW transaction_deferrable; INSERT INTO abc VALUES ('error'); ROLLBACK AND CHAIN; -- TBLOCK_ABORT_END SHOW transaction_isolation; SHOW transaction_read_only; SHOW transaction_deferrable; ROLLBACK; SELECT * FROM abc ORDER BY 1; RESET default_transaction_read_only; DROP TABLE abc; -- Test assorted behaviors around the implicit transaction block created -- when multiple SQL commands are sent in a single Query message. These -- tests rely on the fact that psql will not break SQL commands apart at a -- backslash-quoted semicolon, but will send them as one Query. CREATE temp TABLE i_table ( f1 int ); -- psql will show only the last result in a multi-statement Query SELECT 1; SELECT 2; SELECT 3; -- this implicitly commits: INSERT INTO i_table VALUES (1); SELECT * FROM i_table; -- 1/0 error will cause rolling back the whole implicit transaction INSERT INTO i_table VALUES (2); SELECT * FROM i_table; SELECT 1 / 0; SELECT * FROM i_table; ROLLBACK; -- we are not in a transaction at this point -- can use regular begin/commit/rollback within a single Query BEGIN; INSERT INTO i_table VALUES (3); COMMIT; ROLLBACK; -- we are not in a transaction at this point BEGIN; INSERT INTO i_table VALUES (4); ROLLBACK; ROLLBACK; -- we are not in a transaction at this point -- begin converts implicit transaction into a regular one that -- can extend past the end of the Query SELECT 1; BEGIN; INSERT INTO i_table VALUES (5); COMMIT; SELECT 1; BEGIN; INSERT INTO i_table VALUES (6); ROLLBACK; -- commit in implicit-transaction state commits but issues a warning. INSERT INTO i_table VALUES (7); COMMIT; INSERT INTO i_table VALUES (8); SELECT 1 / 0; -- similarly, rollback aborts but issues a warning. INSERT INTO i_table VALUES (9); ROLLBACK; SELECT 2; SELECT * FROM i_table; ROLLBACK; -- we are not in a transaction at this point -- implicit transaction block is still a transaction block, for e.g. VACUUM SELECT 1; VACUUM; SELECT 1; COMMIT; VACUUM; -- we disallow savepoint-related commands in implicit-transaction state SELECT 1; SAVEPOINT sp; SELECT 1; COMMIT; SAVEPOINT sp; ROLLBACK TO SAVEPOINT sp; SELECT 2; SELECT 2; RELEASE SAVEPOINT sp; SELECT 3; -- but this is OK, because the BEGIN converts it to a regular xact SELECT 1; BEGIN; SAVEPOINT sp; ROLLBACK TO SAVEPOINT sp; COMMIT; -- Test for successful cleanup of an aborted transaction at session exit. -- THIS MUST BE THE LAST TEST IN THIS FILE. BEGIN; SELECT 1 / 0; ROLLBACK TO X; -- DO NOT ADD ANYTHING HERE. pgFormatter-4.2/t/pg-test-files/expected/triggers.sql000066400000000000000000002340611361326045100227560ustar00rootroot00000000000000-- -- TRIGGERS -- CREATE TABLE pkeys ( pkey1 int4 NOT NULL, pkey2 text NOT NULL ); CREATE TABLE fkeys ( fkey1 int4, fkey2 text, fkey3 int ); CREATE TABLE fkeys2 ( fkey21 int4, fkey22 text, pkey23 int NOT NULL ); CREATE INDEX fkeys_i ON fkeys (fkey1, fkey2); CREATE INDEX fkeys2_i ON fkeys2 (fkey21, fkey22); CREATE INDEX fkeys2p_i ON fkeys2 (pkey23); INSERT INTO pkeys VALUES (10, '1'); INSERT INTO pkeys VALUES (20, '2'); INSERT INTO pkeys VALUES (30, '3'); INSERT INTO pkeys VALUES (40, '4'); INSERT INTO pkeys VALUES (50, '5'); INSERT INTO pkeys VALUES (60, '6'); CREATE UNIQUE INDEX pkeys_i ON pkeys (pkey1, pkey2); -- -- For fkeys: -- (fkey1, fkey2) --> pkeys (pkey1, pkey2) -- (fkey3) --> fkeys2 (pkey23) -- CREATE TRIGGER check_fkeys_pkey_exist BEFORE INSERT OR UPDATE ON fkeys FOR EACH ROW EXECUTE FUNCTION check_primary_key ('fkey1', 'fkey2', 'pkeys', 'pkey1', 'pkey2'); CREATE TRIGGER check_fkeys_pkey2_exist BEFORE INSERT OR UPDATE ON fkeys FOR EACH ROW EXECUTE FUNCTION check_primary_key ('fkey3', 'fkeys2', 'pkey23'); -- -- For fkeys2: -- (fkey21, fkey22) --> pkeys (pkey1, pkey2) -- CREATE TRIGGER check_fkeys2_pkey_exist BEFORE INSERT OR UPDATE ON fkeys2 FOR EACH ROW EXECUTE PROCEDURE check_primary_key ('fkey21', 'fkey22', 'pkeys', 'pkey1', 'pkey2'); -- Test comments COMMENT ON TRIGGER check_fkeys2_pkey_bad ON fkeys2 IS 'wrong'; COMMENT ON TRIGGER check_fkeys2_pkey_exist ON fkeys2 IS 'right'; COMMENT ON TRIGGER check_fkeys2_pkey_exist ON fkeys2 IS NULL; -- -- For pkeys: -- ON DELETE/UPDATE (pkey1, pkey2) CASCADE: -- fkeys (fkey1, fkey2) and fkeys2 (fkey21, fkey22) -- CREATE TRIGGER check_pkeys_fkey_cascade BEFORE DELETE OR UPDATE ON pkeys FOR EACH ROW EXECUTE PROCEDURE check_foreign_key (2, 'cascade', 'pkey1', 'pkey2', 'fkeys', 'fkey1', 'fkey2', 'fkeys2', 'fkey21', 'fkey22'); -- -- For fkeys2: -- ON DELETE/UPDATE (pkey23) RESTRICT: -- fkeys (fkey3) -- CREATE TRIGGER check_fkeys2_fkey_restrict BEFORE DELETE OR UPDATE ON fkeys2 FOR EACH ROW EXECUTE PROCEDURE check_foreign_key (1, 'restrict', 'pkey23', 'fkeys', 'fkey3'); INSERT INTO fkeys2 VALUES (10, '1', 1); INSERT INTO fkeys2 VALUES (30, '3', 2); INSERT INTO fkeys2 VALUES (40, '4', 5); INSERT INTO fkeys2 VALUES (50, '5', 3); -- no key in pkeys INSERT INTO fkeys2 VALUES (70, '5', 3); INSERT INTO fkeys VALUES (10, '1', 2); INSERT INTO fkeys VALUES (30, '3', 3); INSERT INTO fkeys VALUES (40, '4', 2); INSERT INTO fkeys VALUES (50, '5', 2); -- no key in pkeys INSERT INTO fkeys VALUES (70, '5', 1); -- no key in fkeys2 INSERT INTO fkeys VALUES (60, '6', 4); DELETE FROM pkeys WHERE pkey1 = 30 AND pkey2 = '3'; DELETE FROM pkeys WHERE pkey1 = 40 AND pkey2 = '4'; UPDATE pkeys SET pkey1 = 7, pkey2 = '70' WHERE pkey1 = 50 AND pkey2 = '5'; UPDATE pkeys SET pkey1 = 7, pkey2 = '70' WHERE pkey1 = 10 AND pkey2 = '1'; SELECT trigger_name, event_manipulation, event_object_schema, event_object_table, action_order, action_condition, action_orientation, action_timing, action_reference_old_table, action_reference_new_table FROM information_schema.triggers WHERE event_object_table IN ('pkeys', 'fkeys', 'fkeys2') ORDER BY trigger_name COLLATE "C", 2; DROP TABLE pkeys; DROP TABLE fkeys; DROP TABLE fkeys2; -- Check behavior when trigger returns unmodified trigtuple CREATE TABLE trigtest ( f1 int, f2 text ); CREATE TRIGGER trigger_return_old BEFORE INSERT OR DELETE OR UPDATE ON trigtest FOR EACH ROW EXECUTE PROCEDURE trigger_return_old (); INSERT INTO trigtest VALUES (1, 'foo'); SELECT * FROM trigtest; UPDATE trigtest SET f2 = f2 || 'bar'; SELECT * FROM trigtest; DELETE FROM trigtest; SELECT * FROM trigtest; DROP TABLE trigtest; CREATE SEQUENCE ttdummy_seq INCREMENT 10 START 0 MINVALUE 0; CREATE TABLE tttest ( price_id int4, price_val int4, price_on int4, price_off int4 DEFAULT 999999 ); CREATE TRIGGER ttdummy BEFORE DELETE OR UPDATE ON tttest FOR EACH ROW EXECUTE PROCEDURE ttdummy (price_on, price_off); CREATE TRIGGER ttserial BEFORE INSERT OR UPDATE ON tttest FOR EACH ROW EXECUTE PROCEDURE autoinc (price_on, ttdummy_seq); INSERT INTO tttest VALUES (1, 1, NULL); INSERT INTO tttest VALUES (2, 2, NULL); INSERT INTO tttest VALUES (3, 3, 0); SELECT * FROM tttest; DELETE FROM tttest WHERE price_id = 2; SELECT * FROM tttest; -- what do we see ? -- get current prices SELECT * FROM tttest WHERE price_off = 999999; -- change price for price_id == 3 UPDATE tttest SET price_val = 30 WHERE price_id = 3; SELECT * FROM tttest; -- now we want to change pric_id in ALL tuples -- this gets us not what we need UPDATE tttest SET price_id = 5 WHERE price_id = 3; SELECT * FROM tttest; -- restore data as before last update: SELECT set_ttdummy (0); DELETE FROM tttest WHERE price_id = 5; UPDATE tttest SET price_off = 999999 WHERE price_val = 30; SELECT * FROM tttest; -- and try change price_id now! UPDATE tttest SET price_id = 5 WHERE price_id = 3; SELECT * FROM tttest; -- isn't it what we need ? SELECT set_ttdummy (1); -- we want to correct some "date" UPDATE tttest SET price_on = - 1 WHERE price_id = 1; -- but this doesn't work -- try in this way SELECT set_ttdummy (0); UPDATE tttest SET price_on = - 1 WHERE price_id = 1; SELECT * FROM tttest; -- isn't it what we need ? -- get price for price_id == 5 as it was @ "date" 35 SELECT * FROM tttest WHERE price_on <= 35 AND price_off > 35 AND price_id = 5; DROP TABLE tttest; DROP SEQUENCE ttdummy_seq; -- -- tests for per-statement triggers -- CREATE TABLE log_table ( tstamp timestamp DEFAULT timeofday() ::timestamp ); CREATE TABLE main_table ( a int UNIQUE, b int ); CREATE FUNCTION trigger_func () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RAISE NOTICE 'trigger_func(%) called: action = %, when = %, level = %', TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL; RETURN NULL; END; $$; CREATE TRIGGER before_ins_stmt_trig BEFORE INSERT ON main_table FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func ('before_ins_stmt'); CREATE TRIGGER after_ins_stmt_trig AFTER INSERT ON main_table FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func ('after_ins_stmt'); -- -- if neither 'FOR EACH ROW' nor 'FOR EACH STATEMENT' was specified, -- CREATE TRIGGER should default to 'FOR EACH STATEMENT' -- CREATE TRIGGER after_upd_stmt_trig AFTER UPDATE ON main_table EXECUTE PROCEDURE trigger_func ('after_upd_stmt'); -- Both insert and update statement level triggers (before and after) should -- fire. Doesn't fire UPDATE before trigger, but only because one isn't -- defined. INSERT INTO main_table (a, b) VALUES (5, 10) ON CONFLICT (a) DO UPDATE SET b = EXCLUDED.b; CREATE TRIGGER after_upd_row_trig AFTER UPDATE ON main_table FOR EACH ROW EXECUTE PROCEDURE trigger_func ('after_upd_row'); INSERT INTO main_table DEFAULT VALUES; UPDATE main_table SET a = a + 1 WHERE b < 30; -- UPDATE that effects zero rows should still call per-statement trigger UPDATE main_table SET a = a + 2 WHERE b > 100; -- constraint now unneeded ALTER TABLE main_table DROP CONSTRAINT main_table_a_key; SELECT * FROM main_table ORDER BY a, b; -- -- test triggers with WHEN clause -- CREATE TRIGGER modified_a BEFORE UPDATE OF a ON main_table FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE trigger_func ('modified_a'); CREATE TRIGGER modified_any BEFORE UPDATE OF a ON main_table FOR EACH ROW WHEN (OLD.* IS DISTINCT FROM NEW.*) EXECUTE PROCEDURE trigger_func ('modified_any'); CREATE TRIGGER insert_a AFTER INSERT ON main_table FOR EACH ROW WHEN (NEW.a = 123) EXECUTE PROCEDURE trigger_func ('insert_a'); CREATE TRIGGER delete_a AFTER DELETE ON main_table FOR EACH ROW WHEN (OLD.a = 123) EXECUTE PROCEDURE trigger_func ('delete_a'); CREATE TRIGGER insert_when BEFORE INSERT ON main_table FOR EACH STATEMENT WHEN (TRUE) EXECUTE PROCEDURE trigger_func ('insert_when'); CREATE TRIGGER delete_when AFTER DELETE ON main_table FOR EACH STATEMENT WHEN (TRUE) EXECUTE PROCEDURE trigger_func ('delete_when'); SELECT trigger_name, event_manipulation, event_object_schema, event_object_table, action_order, action_condition, action_orientation, action_timing, action_reference_old_table, action_reference_new_table FROM information_schema.triggers WHERE event_object_table IN ('main_table') ORDER BY trigger_name COLLATE "C", 2; INSERT INTO main_table (a) VALUES (123), (456); DELETE FROM main_table WHERE a IN (123, 456); UPDATE main_table SET a = 50, b = 60; SELECT * FROM main_table ORDER BY a, b; SELECT pg_get_triggerdef(oid, TRUE) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'modified_a'; SELECT pg_get_triggerdef(oid, FALSE) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'modified_a'; SELECT pg_get_triggerdef(oid, TRUE) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'modified_any'; -- Test RENAME TRIGGER ALTER TRIGGER modified_a ON main_table RENAME TO modified_modified_a; SELECT count(*) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'modified_a'; SELECT count(*) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'modified_modified_a'; DROP TRIGGER modified_modified_a ON main_table; DROP TRIGGER modified_any ON main_table; DROP TRIGGER insert_a ON main_table; DROP TRIGGER delete_a ON main_table; DROP TRIGGER insert_when ON main_table; DROP TRIGGER delete_when ON main_table; -- Test WHEN condition accessing system columns. CREATE TABLE table_with_oids ( a int ); INSERT INTO table_with_oids VALUES (1); CREATE TRIGGER oid_unchanged_trig AFTER UPDATE ON table_with_oids FOR EACH ROW WHEN (new.tableoid = old.tableoid AND new.tableoid <> 0) EXECUTE PROCEDURE trigger_func ('after_upd_oid_unchanged'); UPDATE table_with_oids SET a = a + 1; DROP TABLE table_with_oids; -- Test column-level triggers DROP TRIGGER after_upd_row_trig ON main_table; CREATE TRIGGER before_upd_a_row_trig BEFORE UPDATE OF a ON main_table FOR EACH ROW EXECUTE PROCEDURE trigger_func ('before_upd_a_row'); CREATE TRIGGER after_upd_b_row_trig AFTER UPDATE OF b ON main_table FOR EACH ROW EXECUTE PROCEDURE trigger_func ('after_upd_b_row'); CREATE TRIGGER after_upd_a_b_row_trig AFTER UPDATE OF a, b ON main_table FOR EACH ROW EXECUTE PROCEDURE trigger_func ('after_upd_a_b_row'); CREATE TRIGGER before_upd_a_stmt_trig BEFORE UPDATE OF a ON main_table FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func ('before_upd_a_stmt'); CREATE TRIGGER after_upd_b_stmt_trig AFTER UPDATE OF b ON main_table FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func ('after_upd_b_stmt'); SELECT pg_get_triggerdef(oid) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'after_upd_a_b_row_trig'; UPDATE main_table SET a = 50; UPDATE main_table SET b = 10; -- -- Test case for bug with BEFORE trigger followed by AFTER trigger with WHEN -- CREATE TABLE some_t ( some_col boolean NOT NULL ); CREATE FUNCTION dummy_update_func () RETURNS TRIGGER AS $$ BEGIN RAISE NOTICE 'dummy_update_func(%) called: action = %, old = %, new = %', TG_ARGV[0], TG_OP, OLD, NEW; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER some_trig_before BEFORE UPDATE ON some_t FOR EACH ROW EXECUTE PROCEDURE dummy_update_func ('before'); CREATE TRIGGER some_trig_aftera AFTER UPDATE ON some_t FOR EACH ROW WHEN (NOT OLD.some_col AND NEW.some_col) EXECUTE PROCEDURE dummy_update_func ('aftera'); CREATE TRIGGER some_trig_afterb AFTER UPDATE ON some_t FOR EACH ROW WHEN (NOT NEW.some_col) EXECUTE PROCEDURE dummy_update_func ('afterb'); INSERT INTO some_t VALUES (TRUE); UPDATE some_t SET some_col = TRUE; UPDATE some_t SET some_col = FALSE; UPDATE some_t SET some_col = TRUE; DROP TABLE some_t; -- bogus cases CREATE TRIGGER error_upd_and_col BEFORE UPDATE OR UPDATE OF a ON main_table FOR EACH ROW EXECUTE PROCEDURE trigger_func ('error_upd_and_col'); CREATE TRIGGER error_upd_a_a BEFORE UPDATE OF a, a ON main_table FOR EACH ROW EXECUTE PROCEDURE trigger_func ('error_upd_a_a'); CREATE TRIGGER error_ins_a BEFORE INSERT OF a ON main_table FOR EACH ROW EXECUTE PROCEDURE trigger_func ('error_ins_a'); CREATE TRIGGER error_ins_when BEFORE INSERT OR UPDATE ON main_table FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE trigger_func ('error_ins_old'); CREATE TRIGGER error_del_when BEFORE DELETE OR UPDATE ON main_table FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE trigger_func ('error_del_new'); CREATE TRIGGER error_del_when BEFORE INSERT OR UPDATE ON main_table FOR EACH ROW WHEN (NEW.tableoid <> 0) EXECUTE PROCEDURE trigger_func ('error_when_sys_column'); CREATE TRIGGER error_stmt_when BEFORE UPDATE OF a ON main_table FOR EACH STATEMENT WHEN (OLD.* IS DISTINCT FROM NEW.*) EXECUTE PROCEDURE trigger_func ('error_stmt_when'); -- check dependency restrictions ALTER TABLE main_table DROP COLUMN b; -- this should succeed, but we'll roll it back to keep the triggers around BEGIN; DROP TRIGGER after_upd_a_b_row_trig ON main_table; DROP TRIGGER after_upd_b_row_trig ON main_table; DROP TRIGGER after_upd_b_stmt_trig ON main_table; ALTER TABLE main_table DROP COLUMN b; ROLLBACK; -- Test enable/disable triggers CREATE TABLE trigtest ( i serial PRIMARY KEY ); -- test that disabling RI triggers works CREATE TABLE trigtest2 ( i int REFERENCES trigtest (i) ON DELETE CASCADE ); CREATE FUNCTION trigtest () RETURNS TRIGGER AS $$ BEGIN RAISE notice '% % % %', TG_RELNAME, TG_OP, TG_WHEN, TG_LEVEL; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trigtest_b_row_tg BEFORE INSERT OR UPDATE OR DELETE ON trigtest FOR EACH ROW EXECUTE PROCEDURE trigtest (); CREATE TRIGGER trigtest_a_row_tg AFTER INSERT OR UPDATE OR DELETE ON trigtest FOR EACH ROW EXECUTE PROCEDURE trigtest (); CREATE TRIGGER trigtest_b_stmt_tg BEFORE INSERT OR UPDATE OR DELETE ON trigtest FOR EACH statement EXECUTE PROCEDURE trigtest (); CREATE TRIGGER trigtest_a_stmt_tg AFTER INSERT OR UPDATE OR DELETE ON trigtest FOR EACH statement EXECUTE PROCEDURE trigtest (); INSERT INTO trigtest DEFAULT VALUES; ALTER TABLE trigtest disable TRIGGER trigtest_b_row_tg; INSERT INTO trigtest DEFAULT VALUES; ALTER TABLE trigtest disable TRIGGER USER; INSERT INTO trigtest DEFAULT VALUES; ALTER TABLE trigtest enable TRIGGER trigtest_a_stmt_tg; INSERT INTO trigtest DEFAULT VALUES; SET session_replication_role = REPLICA; INSERT INTO trigtest DEFAULT VALUES; -- does not trigger ALTER TABLE trigtest enable always TRIGGER trigtest_a_stmt_tg; INSERT INTO trigtest DEFAULT VALUES; -- now it does RESET session_replication_role; INSERT INTO trigtest2 VALUES (1); INSERT INTO trigtest2 VALUES (2); DELETE FROM trigtest WHERE i = 2; SELECT * FROM trigtest2; ALTER TABLE trigtest disable TRIGGER ALL; DELETE FROM trigtest WHERE i = 1; SELECT * FROM trigtest2; -- ensure we still insert, even when all triggers are disabled INSERT INTO trigtest DEFAULT VALUES; SELECT * FROM trigtest; DROP TABLE trigtest2; DROP TABLE trigtest; -- dump trigger data CREATE TABLE trigger_test ( i int, v varchar ); CREATE OR REPLACE FUNCTION trigger_data () RETURNS TRIGGER LANGUAGE plpgsql AS $$ DECLARE argstr text; relid text; BEGIN relid := TG_relid::regclass; -- plpgsql can't discover its trigger data in a hash like perl and python -- can, or by a sort of reflection like tcl can, -- so we have to hard code the names. RAISE NOTICE 'TG_NAME: %', TG_name; RAISE NOTICE 'TG_WHEN: %', TG_when; RAISE NOTICE 'TG_LEVEL: %', TG_level; RAISE NOTICE 'TG_OP: %', TG_op; RAISE NOTICE 'TG_RELID::regclass: %', relid; RAISE NOTICE 'TG_RELNAME: %', TG_relname; RAISE NOTICE 'TG_TABLE_NAME: %', TG_table_name; RAISE NOTICE 'TG_TABLE_SCHEMA: %', TG_table_schema; RAISE NOTICE 'TG_NARGS: %', TG_nargs; argstr := '['; FOR i IN 0..TG_nargs - 1 LOOP IF i > 0 THEN argstr := argstr || ', '; END IF; argstr := argstr || TG_argv[i]; END LOOP; argstr := argstr || ']'; RAISE NOTICE 'TG_ARGV: %', argstr; IF TG_OP != 'INSERT' THEN RAISE NOTICE 'OLD: %', OLD; END IF; IF TG_OP != 'DELETE' THEN RAISE NOTICE 'NEW: %', NEW; END IF; IF TG_OP = 'DELETE' THEN RETURN OLD; ELSE RETURN NEW; END IF; END; $$; CREATE TRIGGER show_trigger_data_trig BEFORE INSERT OR UPDATE OR DELETE ON trigger_test FOR EACH ROW EXECUTE PROCEDURE trigger_data (23, 'skidoo'); INSERT INTO trigger_test VALUES (1, 'insert'); UPDATE trigger_test SET v = 'update' WHERE i = 1; DELETE FROM trigger_test; DROP TRIGGER show_trigger_data_trig ON trigger_test; DROP FUNCTION trigger_data (); DROP TABLE trigger_test; -- -- Test use of row comparisons on OLD/NEW -- CREATE TABLE trigger_test ( f1 int, f2 text, f3 text ); -- this is the obvious (and wrong...) way to compare rows CREATE FUNCTION mytrigger () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN IF ROW (old.*) = ROW (new.*) THEN RAISE notice 'row % not changed', new.f1; ELSE RAISE notice 'row % changed', new.f1; END IF; RETURN new; END $$; CREATE TRIGGER t BEFORE UPDATE ON trigger_test FOR EACH ROW EXECUTE PROCEDURE mytrigger (); INSERT INTO trigger_test VALUES (1, 'foo', 'bar'); INSERT INTO trigger_test VALUES (2, 'baz', 'quux'); UPDATE trigger_test SET f3 = 'bar'; UPDATE trigger_test SET f3 = NULL; -- this demonstrates that the above isn't really working as desired: UPDATE trigger_test SET f3 = NULL; -- the right way when considering nulls is CREATE OR REPLACE FUNCTION mytrigger () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN IF ROW (old.*) IS DISTINCT FROM ROW (new.*) THEN RAISE notice 'row % changed', new.f1; ELSE RAISE notice 'row % not changed', new.f1; END IF; RETURN new; END $$; UPDATE trigger_test SET f3 = 'bar'; UPDATE trigger_test SET f3 = NULL; UPDATE trigger_test SET f3 = NULL; DROP TABLE trigger_test; DROP FUNCTION mytrigger (); -- Test snapshot management in serializable transactions involving triggers -- per bug report in 6bc73d4c0910042358k3d1adff3qa36f8df75198ecea@mail.gmail.com CREATE FUNCTION serializable_update_trig () RETURNS TRIGGER LANGUAGE plpgsql AS $$ DECLARE rec record; BEGIN new.description = 'updated in trigger'; RETURN new; END; $$; CREATE TABLE serializable_update_tab ( id int, filler text, description text ); CREATE TRIGGER serializable_update_trig BEFORE UPDATE ON serializable_update_tab FOR EACH ROW EXECUTE PROCEDURE serializable_update_trig (); INSERT INTO serializable_update_tab SELECT a, repeat('xyzxz', 100), 'new' FROM generate_series(1, 50) a; BEGIN; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; UPDATE serializable_update_tab SET description = 'no no', id = 1 WHERE id = 1; COMMIT; SELECT description FROM serializable_update_tab WHERE id = 1; DROP TABLE serializable_update_tab; -- minimal update trigger CREATE TABLE min_updates_test ( f1 text, f2 int, f3 int ); INSERT INTO min_updates_test VALUES ('a', 1, 2), ('b', '2', NULL); CREATE TRIGGER z_min_update BEFORE UPDATE ON min_updates_test FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger (); \set QUIET false UPDATE min_updates_test SET f1 = f1; UPDATE min_updates_test SET f2 = f2 + 1; UPDATE min_updates_test SET f3 = 2 WHERE f3 IS NULL; \set QUIET true SELECT * FROM min_updates_test; DROP TABLE min_updates_test; -- -- Test triggers on views -- CREATE VIEW main_view AS SELECT a, b FROM main_table; -- VIEW trigger function CREATE OR REPLACE FUNCTION view_trigger () RETURNS TRIGGER LANGUAGE plpgsql AS $$ DECLARE argstr text := ''; BEGIN FOR i IN 0..TG_nargs - 1 LOOP IF i > 0 THEN argstr := argstr || ', '; END IF; argstr := argstr || TG_argv[i]; END LOOP; RAISE notice '% % % % (%)', TG_RELNAME, TG_WHEN, TG_OP, TG_LEVEL, argstr; IF TG_LEVEL = 'ROW' THEN IF TG_OP = 'INSERT' THEN RAISE NOTICE 'NEW: %', NEW; INSERT INTO main_table VALUES (NEW.a, NEW.b); RETURN NEW; END IF; IF TG_OP = 'UPDATE' THEN RAISE NOTICE 'OLD: %, NEW: %', OLD, NEW; UPDATE main_table SET a = NEW.a, b = NEW.b WHERE a = OLD.a AND b = OLD.b; IF NOT FOUND THEN RETURN NULL; END IF; RETURN NEW; END IF; IF TG_OP = 'DELETE' THEN RAISE NOTICE 'OLD: %', OLD; DELETE FROM main_table WHERE a = OLD.a AND b = OLD.b; IF NOT FOUND THEN RETURN NULL; END IF; RETURN OLD; END IF; END IF; RETURN NULL; END; $$; -- Before row triggers aren't allowed on views CREATE TRIGGER invalid_trig BEFORE INSERT ON main_view FOR EACH ROW EXECUTE PROCEDURE trigger_func ('before_ins_row'); CREATE TRIGGER invalid_trig BEFORE UPDATE ON main_view FOR EACH ROW EXECUTE PROCEDURE trigger_func ('before_upd_row'); CREATE TRIGGER invalid_trig BEFORE DELETE ON main_view FOR EACH ROW EXECUTE PROCEDURE trigger_func ('before_del_row'); -- After row triggers aren't allowed on views CREATE TRIGGER invalid_trig AFTER INSERT ON main_view FOR EACH ROW EXECUTE PROCEDURE trigger_func ('before_ins_row'); CREATE TRIGGER invalid_trig AFTER UPDATE ON main_view FOR EACH ROW EXECUTE PROCEDURE trigger_func ('before_upd_row'); CREATE TRIGGER invalid_trig AFTER DELETE ON main_view FOR EACH ROW EXECUTE PROCEDURE trigger_func ('before_del_row'); -- Truncate triggers aren't allowed on views CREATE TRIGGER invalid_trig BEFORE TRUNCATE ON main_view EXECUTE PROCEDURE trigger_func ('before_tru_row'); CREATE TRIGGER invalid_trig AFTER TRUNCATE ON main_view EXECUTE PROCEDURE trigger_func ('before_tru_row'); -- INSTEAD OF triggers aren't allowed on tables CREATE TRIGGER invalid_trig INSTEAD OF INSERT ON main_table FOR EACH ROW EXECUTE PROCEDURE view_trigger ('instead_of_ins'); CREATE TRIGGER invalid_trig INSTEAD OF UPDATE ON main_table FOR EACH ROW EXECUTE PROCEDURE view_trigger ('instead_of_upd'); CREATE TRIGGER invalid_trig INSTEAD OF DELETE ON main_table FOR EACH ROW EXECUTE PROCEDURE view_trigger ('instead_of_del'); -- Don't support WHEN clauses with INSTEAD OF triggers CREATE TRIGGER invalid_trig INSTEAD OF UPDATE ON main_view FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE view_trigger ('instead_of_upd'); -- Don't support column-level INSTEAD OF triggers CREATE TRIGGER invalid_trig INSTEAD OF UPDATE OF a ON main_view FOR EACH ROW EXECUTE PROCEDURE view_trigger ('instead_of_upd'); -- Don't support statement-level INSTEAD OF triggers CREATE TRIGGER invalid_trig INSTEAD OF UPDATE ON main_view EXECUTE PROCEDURE view_trigger ('instead_of_upd'); -- Valid INSTEAD OF triggers CREATE TRIGGER instead_of_insert_trig INSTEAD OF INSERT ON main_view FOR EACH ROW EXECUTE PROCEDURE view_trigger ('instead_of_ins'); CREATE TRIGGER instead_of_update_trig INSTEAD OF UPDATE ON main_view FOR EACH ROW EXECUTE PROCEDURE view_trigger ('instead_of_upd'); CREATE TRIGGER instead_of_delete_trig INSTEAD OF DELETE ON main_view FOR EACH ROW EXECUTE PROCEDURE view_trigger ('instead_of_del'); -- Valid BEFORE statement VIEW triggers CREATE TRIGGER before_ins_stmt_trig BEFORE INSERT ON main_view FOR EACH STATEMENT EXECUTE PROCEDURE view_trigger ('before_view_ins_stmt'); CREATE TRIGGER before_upd_stmt_trig BEFORE UPDATE ON main_view FOR EACH STATEMENT EXECUTE PROCEDURE view_trigger ('before_view_upd_stmt'); CREATE TRIGGER before_del_stmt_trig BEFORE DELETE ON main_view FOR EACH STATEMENT EXECUTE PROCEDURE view_trigger ('before_view_del_stmt'); -- Valid AFTER statement VIEW triggers CREATE TRIGGER after_ins_stmt_trig AFTER INSERT ON main_view FOR EACH STATEMENT EXECUTE PROCEDURE view_trigger ('after_view_ins_stmt'); CREATE TRIGGER after_upd_stmt_trig AFTER UPDATE ON main_view FOR EACH STATEMENT EXECUTE PROCEDURE view_trigger ('after_view_upd_stmt'); CREATE TRIGGER after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE PROCEDURE view_trigger ('after_view_del_stmt'); \set QUIET false -- Insert into view using trigger INSERT INTO main_view VALUES (20, 30); INSERT INTO main_view VALUES (21, 31) RETURNING a, b; -- Table trigger will prevent updates UPDATE main_view SET b = 31 WHERE a = 20; UPDATE main_view SET b = 32 WHERE a = 21 AND b = 31 RETURNING a, b; -- Remove table trigger to allow updates DROP TRIGGER before_upd_a_row_trig ON main_table; UPDATE main_view SET b = 31 WHERE a = 20; UPDATE main_view SET b = 32 WHERE a = 21 AND b = 31 RETURNING a, b; -- Before and after stmt triggers should fire even when no rows are affected UPDATE main_view SET b = 0 WHERE FALSE; -- Delete from view using trigger DELETE FROM main_view WHERE a IN (20, 21); DELETE FROM main_view WHERE a = 31 RETURNING a, b; \set QUIET true -- Describe view should list triggers \d main_view -- Test dropping view triggers DROP TRIGGER instead_of_insert_trig ON main_view; DROP TRIGGER instead_of_delete_trig ON main_view; \d+ main_view DROP VIEW main_view; -- -- Test triggers on a join view -- CREATE TABLE country_table ( country_id serial PRIMARY KEY, country_name text UNIQUE NOT NULL, continent text NOT NULL ); INSERT INTO country_table (country_name, continent) VALUES ('Japan', 'Asia'), ('UK', 'Europe'), ('USA', 'North America') RETURNING *; CREATE TABLE city_table ( city_id serial PRIMARY KEY, city_name text NOT NULL, population bigint, country_id int REFERENCES country_table ); CREATE VIEW city_view AS SELECT city_id, city_name, population, country_name, continent FROM city_table ci LEFT JOIN country_table co ON co.country_id = ci.country_id; CREATE FUNCTION city_insert () RETURNS TRIGGER LANGUAGE plpgsql AS $$ DECLARE ctry_id int; BEGIN IF NEW.country_name IS NOT NULL THEN SELECT country_id, continent INTO ctry_id, NEW.continent FROM country_table WHERE country_name = NEW.country_name; IF NOT FOUND THEN RAISE exception 'No such country: "%"', NEW.country_name; END IF; ELSE NEW.continent := NULL; END IF; IF NEW.city_id IS NOT NULL THEN INSERT INTO city_table VALUES (NEW.city_id, NEW.city_name, NEW.population, ctry_id); ELSE INSERT INTO city_table (city_name, population, country_id) VALUES (NEW.city_name, NEW.population, ctry_id) RETURNING city_id INTO NEW.city_id; END IF; RETURN NEW; END; $$; CREATE TRIGGER city_insert_trig INSTEAD OF INSERT ON city_view FOR EACH ROW EXECUTE PROCEDURE city_insert (); CREATE FUNCTION city_delete () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN DELETE FROM city_table WHERE city_id = OLD.city_id; IF NOT FOUND THEN RETURN NULL; END IF; RETURN OLD; END; $$; CREATE TRIGGER city_delete_trig INSTEAD OF DELETE ON city_view FOR EACH ROW EXECUTE PROCEDURE city_delete (); CREATE FUNCTION city_update () RETURNS TRIGGER LANGUAGE plpgsql AS $$ DECLARE ctry_id int; BEGIN IF NEW.country_name IS DISTINCT FROM OLD.country_name THEN SELECT country_id, continent INTO ctry_id, NEW.continent FROM country_table WHERE country_name = NEW.country_name; IF NOT FOUND THEN RAISE exception 'No such country: "%"', NEW.country_name; END IF; UPDATE city_table SET city_name = NEW.city_name, population = NEW.population, country_id = ctry_id WHERE city_id = OLD.city_id; ELSE UPDATE city_table SET city_name = NEW.city_name, population = NEW.population WHERE city_id = OLD.city_id; NEW.continent := OLD.continent; END IF; IF NOT FOUND THEN RETURN NULL; END IF; RETURN NEW; END; $$; CREATE TRIGGER city_update_trig INSTEAD OF UPDATE ON city_view FOR EACH ROW EXECUTE PROCEDURE city_update (); \set QUIET false -- INSERT .. RETURNING INSERT INTO city_view (city_name) VALUES ('Tokyo') RETURNING *; INSERT INTO city_view (city_name, population) VALUES ('London', 7556900) RETURNING *; INSERT INTO city_view (city_name, country_name) VALUES ('Washington DC', 'USA') RETURNING *; INSERT INTO city_view (city_id, city_name) VALUES (123456, 'New York') RETURNING *; INSERT INTO city_view VALUES (234567, 'Birmingham', 1016800, 'UK', 'EU') RETURNING *; -- UPDATE .. RETURNING UPDATE city_view SET country_name = 'Japon' WHERE city_name = 'Tokyo'; -- error UPDATE city_view SET country_name = 'Japan' WHERE city_name = 'Takyo'; -- no match UPDATE city_view SET country_name = 'Japan' WHERE city_name = 'Tokyo' RETURNING *; -- OK UPDATE city_view SET population = 13010279 WHERE city_name = 'Tokyo' RETURNING *; UPDATE city_view SET country_name = 'UK' WHERE city_name = 'New York' RETURNING *; UPDATE city_view SET country_name = 'USA', population = 8391881 WHERE city_name = 'New York' RETURNING *; UPDATE city_view SET continent = 'EU' WHERE continent = 'Europe' RETURNING *; UPDATE city_view v1 SET country_name = v2.country_name FROM city_view v2 WHERE v2.city_name = 'Birmingham' AND v1.city_name = 'London' RETURNING *; -- DELETE .. RETURNING DELETE FROM city_view WHERE city_name = 'Birmingham' RETURNING *; \set QUIET true -- read-only view with WHERE clause CREATE VIEW european_city_view AS SELECT * FROM city_view WHERE continent = 'Europe'; SELECT count(*) FROM european_city_view; CREATE FUNCTION no_op_trig_fn () RETURNS TRIGGER LANGUAGE plpgsql AS 'begin RETURN NULL; end' ; CREATE TRIGGER no_op_trig INSTEAD OF INSERT OR UPDATE OR DELETE ON european_city_view FOR EACH ROW EXECUTE PROCEDURE no_op_trig_fn (); \set QUIET false INSERT INTO european_city_view VALUES (0, 'x', 10000, 'y', 'z'); UPDATE european_city_view SET population = 10000; DELETE FROM european_city_view; \set QUIET true -- rules bypassing no-op triggers CREATE RULE european_city_insert_rule AS ON INSERT TO european_city_view DO INSTEAD INSERT INTO city_view VALUES (NEW.city_id, NEW.city_name, NEW.population, NEW.country_name, NEW.continent) RETURNING *; CREATE RULE european_city_update_rule AS ON UPDATE TO european_city_view DO INSTEAD UPDATE city_view SET city_name = NEW.city_name, population = NEW.population, country_name = NEW.country_name WHERE city_id = OLD.city_id RETURNING NEW.*; CREATE RULE european_city_delete_rule AS ON DELETE TO european_city_view DO INSTEAD DELETE FROM city_view WHERE city_id = OLD.city_id RETURNING *; \set QUIET false -- INSERT not limited by view's WHERE clause, but UPDATE AND DELETE are INSERT INTO european_city_view (city_name, country_name) VALUES ('Cambridge', 'USA') RETURNING *; UPDATE european_city_view SET country_name = 'UK' WHERE city_name = 'Cambridge'; DELETE FROM european_city_view WHERE city_name = 'Cambridge'; -- UPDATE and DELETE via rule and trigger UPDATE city_view SET country_name = 'UK' WHERE city_name = 'Cambridge' RETURNING *; UPDATE european_city_view SET population = 122800 WHERE city_name = 'Cambridge' RETURNING *; DELETE FROM european_city_view WHERE city_name = 'Cambridge' RETURNING *; -- join UPDATE test UPDATE city_view v SET population = 599657 FROM city_table ci, country_table co WHERE ci.city_name = 'Washington DC' AND co.country_name = 'USA' AND v.city_id = ci.city_id AND v.country_name = co.country_name RETURNING co.country_id, v.country_name, v.city_id, v.city_name, v.population; \set QUIET true SELECT * FROM city_view; DROP TABLE city_table CASCADE; DROP TABLE country_table; -- Test pg_trigger_depth() CREATE TABLE depth_a ( id int NOT NULL PRIMARY KEY ); CREATE TABLE depth_b ( id int NOT NULL PRIMARY KEY ); CREATE TABLE depth_c ( id int NOT NULL PRIMARY KEY ); CREATE FUNCTION depth_a_tf () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RAISE notice '%: depth = %', tg_name, pg_trigger_depth(); INSERT INTO depth_b VALUES (new.id); RAISE notice '%: depth = %', tg_name, pg_trigger_depth(); RETURN new; END; $$; CREATE TRIGGER depth_a_tr BEFORE INSERT ON depth_a FOR EACH ROW EXECUTE PROCEDURE depth_a_tf (); CREATE FUNCTION depth_b_tf () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RAISE notice '%: depth = %', tg_name, pg_trigger_depth(); BEGIN EXECUTE 'insert into depth_c values (' || new.id::text || ')'; exception WHEN sqlstate 'U9999' THEN RAISE notice 'SQLSTATE = U9999: depth = %', pg_trigger_depth(); END; RAISE notice '%: depth = %', tg_name, pg_trigger_depth(); IF new.id = 1 THEN EXECUTE 'insert into depth_c values (' || new.id::text || ')'; END IF; RETURN new; END; $$; CREATE TRIGGER depth_b_tr BEFORE INSERT ON depth_b FOR EACH ROW EXECUTE PROCEDURE depth_b_tf (); CREATE FUNCTION depth_c_tf () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RAISE notice '%: depth = %', tg_name, pg_trigger_depth(); IF new.id = 1 THEN RAISE exception sqlstate 'U9999'; END IF; RAISE notice '%: depth = %', tg_name, pg_trigger_depth(); RETURN new; END; $$; CREATE TRIGGER depth_c_tr BEFORE INSERT ON depth_c FOR EACH ROW EXECUTE PROCEDURE depth_c_tf (); SELECT pg_trigger_depth(); INSERT INTO depth_a VALUES (1); SELECT pg_trigger_depth(); INSERT INTO depth_a VALUES (2); SELECT pg_trigger_depth(); DROP TABLE depth_a, depth_b, depth_c; DROP FUNCTION depth_a_tf (); DROP FUNCTION depth_b_tf (); DROP FUNCTION depth_c_tf (); -- -- Test updates to rows during firing of BEFORE ROW triggers. -- As of 9.2, such cases should be rejected (see bug #6123). -- CREATE temp TABLE parent ( aid int NOT NULL PRIMARY KEY, val1 text, val2 text, val3 text, val4 text, bcnt int NOT NULL DEFAULT 0 ); CREATE temp TABLE child ( bid int NOT NULL PRIMARY KEY, aid int NOT NULL, val1 text ); CREATE FUNCTION parent_upd_func () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN IF old.val1 <> new.val1 THEN new.val2 = new.val1; DELETE FROM child WHERE child.aid = new.aid AND child.val1 = new.val1; END IF; RETURN new; END; $$; CREATE TRIGGER parent_upd_trig BEFORE UPDATE ON parent FOR EACH ROW EXECUTE PROCEDURE parent_upd_func (); CREATE FUNCTION parent_del_func () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN DELETE FROM child WHERE aid = old.aid; RETURN old; END; $$; CREATE TRIGGER parent_del_trig BEFORE DELETE ON parent FOR EACH ROW EXECUTE PROCEDURE parent_del_func (); CREATE FUNCTION child_ins_func () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN UPDATE parent SET bcnt = bcnt + 1 WHERE aid = new.aid; RETURN new; END; $$; CREATE TRIGGER child_ins_trig AFTER INSERT ON child FOR EACH ROW EXECUTE PROCEDURE child_ins_func (); CREATE FUNCTION child_del_func () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN UPDATE parent SET bcnt = bcnt - 1 WHERE aid = old.aid; RETURN old; END; $$; CREATE TRIGGER child_del_trig AFTER DELETE ON child FOR EACH ROW EXECUTE PROCEDURE child_del_func (); INSERT INTO parent VALUES (1, 'a', 'a', 'a', 'a', 0); INSERT INTO child VALUES (10, 1, 'b'); SELECT * FROM parent; SELECT * FROM child; UPDATE parent SET val1 = 'b' WHERE aid = 1; -- should fail SELECT * FROM parent; SELECT * FROM child; DELETE FROM parent WHERE aid = 1; -- should fail SELECT * FROM parent; SELECT * FROM child; -- replace the trigger function with one that restarts the deletion after -- having modified a child CREATE OR REPLACE FUNCTION parent_del_func () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN DELETE FROM child WHERE aid = old.aid; IF found THEN DELETE FROM parent WHERE aid = old.aid; RETURN NULL; -- cancel outer deletion END IF; RETURN old; END; $$; DELETE FROM parent WHERE aid = 1; SELECT * FROM parent; SELECT * FROM child; DROP TABLE parent, child; DROP FUNCTION parent_upd_func (); DROP FUNCTION parent_del_func (); DROP FUNCTION child_ins_func (); DROP FUNCTION child_del_func (); -- similar case, but with a self-referencing FK so that parent and child -- rows can be affected by a single operation CREATE temp TABLE self_ref_trigger ( id int PRIMARY KEY, parent int REFERENCES self_ref_trigger, data text, nchildren int NOT NULL DEFAULT 0 ); CREATE FUNCTION self_ref_trigger_ins_func () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN IF new.parent IS NOT NULL THEN UPDATE self_ref_trigger SET nchildren = nchildren + 1 WHERE id = new.parent; END IF; RETURN new; END; $$; CREATE TRIGGER self_ref_trigger_ins_trig BEFORE INSERT ON self_ref_trigger FOR EACH ROW EXECUTE PROCEDURE self_ref_trigger_ins_func (); CREATE FUNCTION self_ref_trigger_del_func () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN IF old.parent IS NOT NULL THEN UPDATE self_ref_trigger SET nchildren = nchildren - 1 WHERE id = old.parent; END IF; RETURN old; END; $$; CREATE TRIGGER self_ref_trigger_del_trig BEFORE DELETE ON self_ref_trigger FOR EACH ROW EXECUTE PROCEDURE self_ref_trigger_del_func (); INSERT INTO self_ref_trigger VALUES (1, NULL, 'root'); INSERT INTO self_ref_trigger VALUES (2, 1, 'root child A'); INSERT INTO self_ref_trigger VALUES (3, 1, 'root child B'); INSERT INTO self_ref_trigger VALUES (4, 2, 'grandchild 1'); INSERT INTO self_ref_trigger VALUES (5, 3, 'grandchild 2'); UPDATE self_ref_trigger SET data = 'root!' WHERE id = 1; SELECT * FROM self_ref_trigger; DELETE FROM self_ref_trigger; SELECT * FROM self_ref_trigger; DROP TABLE self_ref_trigger; DROP FUNCTION self_ref_trigger_ins_func (); DROP FUNCTION self_ref_trigger_del_func (); -- -- Check that statement triggers work correctly even with all children excluded -- CREATE TABLE stmt_trig_on_empty_upd ( a int ); CREATE TABLE stmt_trig_on_empty_upd1 () INHERITS ( stmt_trig_on_empty_upd ); CREATE FUNCTION update_stmt_notice () RETURNS TRIGGER AS $$ BEGIN RAISE notice 'updating %', TG_TABLE_NAME; RETURN NULL; END; $$ LANGUAGE plpgsql; CREATE TRIGGER before_stmt_trigger BEFORE UPDATE ON stmt_trig_on_empty_upd EXECUTE PROCEDURE update_stmt_notice (); CREATE TRIGGER before_stmt_trigger BEFORE UPDATE ON stmt_trig_on_empty_upd1 EXECUTE PROCEDURE update_stmt_notice (); -- inherited no-op update UPDATE stmt_trig_on_empty_upd SET a = a WHERE FALSE RETURNING a + 1 AS aa; -- simple no-op update UPDATE stmt_trig_on_empty_upd1 SET a = a WHERE FALSE RETURNING a + 1 AS aa; DROP TABLE stmt_trig_on_empty_upd CASCADE; DROP FUNCTION update_stmt_notice (); -- -- Check that index creation (or DDL in general) is prohibited in a trigger -- CREATE TABLE trigger_ddl_table ( col1 integer, col2 integer ); CREATE FUNCTION trigger_ddl_func () RETURNS TRIGGER AS $$ BEGIN ALTER TABLE trigger_ddl_table ADD PRIMARY KEY (col1); RETURN new; END $$ LANGUAGE plpgsql; CREATE TRIGGER trigger_ddl_func BEFORE INSERT ON trigger_ddl_table FOR EACH ROW EXECUTE PROCEDURE trigger_ddl_func (); INSERT INTO trigger_ddl_table VALUES (1, 42); -- fail CREATE OR REPLACE FUNCTION trigger_ddl_func () RETURNS TRIGGER AS $$ BEGIN CREATE INDEX ON trigger_ddl_table (col2); RETURN new; END $$ LANGUAGE plpgsql; INSERT INTO trigger_ddl_table VALUES (1, 42); -- fail DROP TABLE trigger_ddl_table; DROP FUNCTION trigger_ddl_func (); -- -- Verify behavior of before and after triggers with INSERT...ON CONFLICT -- DO UPDATE -- CREATE TABLE upsert ( KEY int4 PRIMARY KEY, color text ); CREATE FUNCTION upsert_before_func () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN IF (TG_OP = 'UPDATE') THEN RAISE warning 'before update (old): %', old.*::text; RAISE warning 'before update (new): %', new.*::text; elsif (TG_OP = 'INSERT') THEN RAISE warning 'before insert (new): %', new.*::text; IF new.key % 2 = 0 THEN new.key := new.key + 1; new.color := new.color || ' trig modified'; RAISE warning 'before insert (new, modified): %', new.*::text; END IF; END IF; RETURN new; END; $$; CREATE TRIGGER upsert_before_trig BEFORE INSERT OR UPDATE ON upsert FOR EACH ROW EXECUTE PROCEDURE upsert_before_func (); CREATE FUNCTION upsert_after_func () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN IF (TG_OP = 'UPDATE') THEN RAISE warning 'after update (old): %', old.*::text; RAISE warning 'after update (new): %', new.*::text; elsif (TG_OP = 'INSERT') THEN RAISE warning 'after insert (new): %', new.*::text; END IF; RETURN NULL; END; $$; CREATE TRIGGER upsert_after_trig AFTER INSERT OR UPDATE ON upsert FOR EACH ROW EXECUTE PROCEDURE upsert_after_func (); INSERT INTO upsert VALUES (1, 'black') ON CONFLICT (KEY) DO UPDATE SET color = 'updated ' || upsert.color; INSERT INTO upsert VALUES (2, 'red') ON CONFLICT (KEY) DO UPDATE SET color = 'updated ' || upsert.color; INSERT INTO upsert VALUES (3, 'orange') ON CONFLICT (KEY) DO UPDATE SET color = 'updated ' || upsert.color; INSERT INTO upsert VALUES (4, 'green') ON CONFLICT (KEY) DO UPDATE SET color = 'updated ' || upsert.color; INSERT INTO upsert VALUES (5, 'purple') ON CONFLICT (KEY) DO UPDATE SET color = 'updated ' || upsert.color; INSERT INTO upsert VALUES (6, 'white') ON CONFLICT (KEY) DO UPDATE SET color = 'updated ' || upsert.color; INSERT INTO upsert VALUES (7, 'pink') ON CONFLICT (KEY) DO UPDATE SET color = 'updated ' || upsert.color; INSERT INTO upsert VALUES (8, 'yellow') ON CONFLICT (KEY) DO UPDATE SET color = 'updated ' || upsert.color; SELECT * FROM upsert; DROP TABLE upsert; DROP FUNCTION upsert_before_func (); DROP FUNCTION upsert_after_func (); -- -- Verify that triggers with transition tables are not allowed on -- views -- CREATE TABLE my_table ( i int ); CREATE VIEW my_view AS SELECT * FROM my_table; CREATE FUNCTION my_trigger_function () RETURNS TRIGGER AS $$ BEGIN END; $$ LANGUAGE plpgsql; CREATE TRIGGER my_trigger AFTER UPDATE ON my_view referencing old TABLE AS old_table FOR EACH statement EXECUTE PROCEDURE my_trigger_function (); DROP FUNCTION my_trigger_function (); DROP VIEW my_view; DROP TABLE my_table; -- -- Verify cases that are unsupported with partitioned tables -- CREATE TABLE parted_trig ( a int ) PARTITION BY LIST (a); CREATE FUNCTION trigger_nothing () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN END; $$; CREATE TRIGGER failed BEFORE INSERT OR UPDATE OR DELETE ON parted_trig FOR EACH ROW EXECUTE PROCEDURE trigger_nothing (); CREATE TRIGGER failed INSTEAD OF UPDATE ON parted_trig FOR EACH ROW EXECUTE PROCEDURE trigger_nothing (); CREATE TRIGGER failed AFTER UPDATE ON parted_trig referencing old TABLE AS old_table FOR EACH ROW EXECUTE PROCEDURE trigger_nothing (); DROP TABLE parted_trig; -- -- Verify trigger creation for partitioned tables, and drop behavior -- CREATE TABLE trigpart ( a int, b int ) PARTITION BY RANGE (a); CREATE TABLE trigpart1 PARTITION OF trigpart FOR VALUES FROM (0) TO (1000); CREATE TRIGGER trg1 AFTER INSERT ON trigpart FOR EACH ROW EXECUTE PROCEDURE trigger_nothing (); CREATE TABLE trigpart2 PARTITION OF trigpart FOR VALUES FROM (1000) TO (2000); CREATE TABLE trigpart3 ( LIKE trigpart ); ALTER TABLE trigpart ATTACH PARTITION trigpart3 FOR VALUES FROM (2000) TO (3000); SELECT tgrelid::regclass, tgname, tgfoid::regproc FROM pg_trigger WHERE tgrelid::regclass::text LIKE 'trigpart%' ORDER BY tgrelid::regclass::text; DROP TRIGGER trg1 ON trigpart1; -- fail DROP TRIGGER trg1 ON trigpart2; -- fail DROP TRIGGER trg1 ON trigpart3; -- fail DROP TABLE trigpart2; -- ok, trigger should be gone in that partition SELECT tgrelid::regclass, tgname, tgfoid::regproc FROM pg_trigger WHERE tgrelid::regclass::text LIKE 'trigpart%' ORDER BY tgrelid::regclass::text; DROP TRIGGER trg1 ON trigpart; -- ok, all gone SELECT tgrelid::regclass, tgname, tgfoid::regproc FROM pg_trigger WHERE tgrelid::regclass::text LIKE 'trigpart%' ORDER BY tgrelid::regclass::text; DROP TABLE trigpart; DROP FUNCTION trigger_nothing (); -- -- Verify that triggers are fired for partitioned tables -- CREATE TABLE parted_stmt_trig ( a int ) PARTITION BY LIST (a); CREATE TABLE parted_stmt_trig1 PARTITION OF parted_stmt_trig FOR VALUES IN (1); CREATE TABLE parted_stmt_trig2 PARTITION OF parted_stmt_trig FOR VALUES IN (2); CREATE TABLE parted2_stmt_trig ( a int ) PARTITION BY LIST (a); CREATE TABLE parted2_stmt_trig1 PARTITION OF parted2_stmt_trig FOR VALUES IN (1); CREATE TABLE parted2_stmt_trig2 PARTITION OF parted2_stmt_trig FOR VALUES IN (2); CREATE OR REPLACE FUNCTION trigger_notice () RETURNS TRIGGER AS $$ BEGIN RAISE notice 'trigger % on % % % for %', TG_NAME, TG_TABLE_NAME, TG_WHEN, TG_OP, TG_LEVEL; IF TG_LEVEL = 'ROW' THEN RETURN NEW; END IF; RETURN NULL; END; $$ LANGUAGE plpgsql; -- insert/update/delete statement-level triggers on the parent CREATE TRIGGER trig_ins_before BEFORE INSERT ON parted_stmt_trig FOR EACH statement EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER trig_ins_after AFTER INSERT ON parted_stmt_trig FOR EACH statement EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER trig_upd_before BEFORE UPDATE ON parted_stmt_trig FOR EACH statement EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER trig_upd_after AFTER UPDATE ON parted_stmt_trig FOR EACH statement EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER trig_del_before BEFORE DELETE ON parted_stmt_trig FOR EACH statement EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER trig_del_after AFTER DELETE ON parted_stmt_trig FOR EACH statement EXECUTE PROCEDURE trigger_notice (); -- insert/update/delete row-level triggers on the parent CREATE TRIGGER trig_ins_after_parent AFTER INSERT ON parted_stmt_trig FOR EACH ROW EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER trig_upd_after_parent AFTER UPDATE ON parted_stmt_trig FOR EACH ROW EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER trig_del_after_parent AFTER DELETE ON parted_stmt_trig FOR EACH ROW EXECUTE PROCEDURE trigger_notice (); -- insert/update/delete row-level triggers on the first partition CREATE TRIGGER trig_ins_before_child BEFORE INSERT ON parted_stmt_trig1 FOR EACH ROW EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER trig_ins_after_child AFTER INSERT ON parted_stmt_trig1 FOR EACH ROW EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER trig_upd_before_child BEFORE UPDATE ON parted_stmt_trig1 FOR EACH ROW EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER trig_upd_after_child AFTER UPDATE ON parted_stmt_trig1 FOR EACH ROW EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER trig_del_before_child BEFORE DELETE ON parted_stmt_trig1 FOR EACH ROW EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER trig_del_after_child AFTER DELETE ON parted_stmt_trig1 FOR EACH ROW EXECUTE PROCEDURE trigger_notice (); -- insert/update/delete statement-level triggers on the parent CREATE TRIGGER trig_ins_before_3 BEFORE INSERT ON parted2_stmt_trig FOR EACH statement EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER trig_ins_after_3 AFTER INSERT ON parted2_stmt_trig FOR EACH statement EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER trig_upd_before_3 BEFORE UPDATE ON parted2_stmt_trig FOR EACH statement EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER trig_upd_after_3 AFTER UPDATE ON parted2_stmt_trig FOR EACH statement EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER trig_del_before_3 BEFORE DELETE ON parted2_stmt_trig FOR EACH statement EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER trig_del_after_3 AFTER DELETE ON parted2_stmt_trig FOR EACH statement EXECUTE PROCEDURE trigger_notice (); WITH ins ( a ) AS ( INSERT INTO parted2_stmt_trig VALUES (1), (2) RETURNING a) INSERT INTO parted_stmt_trig SELECT a FROM ins RETURNING tableoid::regclass, a; WITH upd AS ( UPDATE parted2_stmt_trig SET a = a) UPDATE parted_stmt_trig SET a = a; DELETE FROM parted_stmt_trig; -- Disabling a trigger in the parent table should disable children triggers too ALTER TABLE parted_stmt_trig disable TRIGGER trig_ins_after_parent; INSERT INTO parted_stmt_trig VALUES (1); ALTER TABLE parted_stmt_trig enable TRIGGER trig_ins_after_parent; INSERT INTO parted_stmt_trig VALUES (1); DROP TABLE parted_stmt_trig, parted2_stmt_trig; -- Verify that triggers fire in alphabetical order CREATE TABLE parted_trig ( a int ) PARTITION BY RANGE (a); CREATE TABLE parted_trig_1 PARTITION OF parted_trig FOR VALUES FROM (0) TO (1000) PARTITION BY RANGE (a); CREATE TABLE parted_trig_1_1 PARTITION OF parted_trig_1 FOR VALUES FROM (0) TO (100); CREATE TABLE parted_trig_2 PARTITION OF parted_trig FOR VALUES FROM (1000) TO (2000); CREATE TRIGGER zzz AFTER INSERT ON parted_trig FOR EACH ROW EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER mmm AFTER INSERT ON parted_trig_1_1 FOR EACH ROW EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER aaa AFTER INSERT ON parted_trig_1 FOR EACH ROW EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER bbb AFTER INSERT ON parted_trig FOR EACH ROW EXECUTE PROCEDURE trigger_notice (); CREATE TRIGGER qqq AFTER INSERT ON parted_trig_1_1 FOR EACH ROW EXECUTE PROCEDURE trigger_notice (); INSERT INTO parted_trig VALUES (50), (1500); DROP TABLE parted_trig; -- test irregular partitions (i.e., different column definitions), -- including that the WHEN clause works CREATE FUNCTION bark (text) RETURNS bool LANGUAGE plpgsql IMMUTABLE AS $$ BEGIN RAISE notice '% <- woof!', $1; RETURN TRUE; END; $$; CREATE OR REPLACE FUNCTION trigger_notice_ab () RETURNS TRIGGER AS $$ BEGIN RAISE notice 'trigger % on % % % for %: (a,b)=(%,%)', TG_NAME, TG_TABLE_NAME, TG_WHEN, TG_OP, TG_LEVEL, NEW.a, NEW.b; IF TG_LEVEL = 'ROW' THEN RETURN NEW; END IF; RETURN NULL; END; $$ LANGUAGE plpgsql; CREATE TABLE parted_irreg_ancestor ( fd text, b text, fd2 int, fd3 int, a int ) PARTITION BY RANGE (b); ALTER TABLE parted_irreg_ancestor DROP COLUMN fd, DROP COLUMN fd2, DROP COLUMN fd3; CREATE TABLE parted_irreg ( fd int, a int, fd2 int, b text ) PARTITION BY RANGE (b); ALTER TABLE parted_irreg DROP COLUMN fd, DROP COLUMN fd2; ALTER TABLE parted_irreg_ancestor ATTACH PARTITION parted_irreg FOR VALUES FROM ('aaaa') TO ('zzzz'); CREATE TABLE parted1_irreg ( b text, fd int, a int ); ALTER TABLE parted1_irreg DROP COLUMN fd; ALTER TABLE parted_irreg ATTACH PARTITION parted1_irreg FOR VALUES FROM ('aaaa') TO ('bbbb'); CREATE TRIGGER parted_trig AFTER INSERT ON parted_irreg FOR EACH ROW EXECUTE PROCEDURE trigger_notice_ab (); CREATE TRIGGER parted_trig_odd AFTER INSERT ON parted_irreg FOR EACH ROW WHEN (bark (new.b) AND new.a % 2 = 1) EXECUTE PROCEDURE trigger_notice_ab (); -- we should hear barking for every insert, but parted_trig_odd only emits -- noise for odd values of a. parted_trig does it for all inserts. INSERT INTO parted_irreg VALUES (1, 'aardvark'), (2, 'aanimals'); INSERT INTO parted1_irreg VALUES ('aardwolf', 2); INSERT INTO parted_irreg_ancestor VALUES ('aasvogel', 3); DROP TABLE parted_irreg_ancestor; -- -- Constraint triggers and partitioned tables CREATE TABLE parted_constr_ancestor ( a int, b text ) PARTITION BY RANGE (b); CREATE TABLE parted_constr ( a int, b text ) PARTITION BY RANGE (b); ALTER TABLE parted_constr_ancestor ATTACH PARTITION parted_constr FOR VALUES FROM ('aaaa') TO ('zzzz'); CREATE TABLE parted1_constr ( a int, b text ); ALTER TABLE parted_constr ATTACH PARTITION parted1_constr FOR VALUES FROM ('aaaa') TO ('bbbb'); CREATE CONSTRAINT TRIGGER parted_trig AFTER INSERT ON parted_constr_ancestor DEFERRABLE FOR EACH ROW EXECUTE PROCEDURE trigger_notice_ab (); CREATE CONSTRAINT TRIGGER parted_trig_two AFTER INSERT ON parted_constr DEFERRABLE INITIALLY DEFERRED FOR EACH ROW WHEN (bark (new.b) AND new.a % 2 = 1) EXECUTE PROCEDURE trigger_notice_ab (); -- The immediate constraint is fired immediately; the WHEN clause of the -- deferred constraint is also called immediately. The deferred constraint -- is fired at commit time. BEGIN; INSERT INTO parted_constr VALUES (1, 'aardvark'); INSERT INTO parted1_constr VALUES (2, 'aardwolf'); INSERT INTO parted_constr_ancestor VALUES (3, 'aasvogel'); COMMIT; -- The WHEN clause is immediate, and both constraint triggers are fired at -- commit time. BEGIN; SET constraints parted_trig DEFERRED; INSERT INTO parted_constr VALUES (1, 'aardvark'); INSERT INTO parted1_constr VALUES (2, 'aardwolf'), (3, 'aasvogel'); COMMIT; DROP TABLE parted_constr_ancestor; DROP FUNCTION bark (text); -- Test that the WHEN clause is set properly to partitions CREATE TABLE parted_trigger ( a int, b text ) PARTITION BY RANGE (a); CREATE TABLE parted_trigger_1 PARTITION OF parted_trigger FOR VALUES FROM (0) TO (1000); CREATE TABLE parted_trigger_2 ( drp int, a int, b text ); ALTER TABLE parted_trigger_2 DROP COLUMN drp; ALTER TABLE parted_trigger ATTACH PARTITION parted_trigger_2 FOR VALUES FROM (1000) TO (2000); CREATE TRIGGER parted_trigger AFTER UPDATE ON parted_trigger FOR EACH ROW WHEN (new.a % 2 = 1 AND length(old.b) >= 2) EXECUTE PROCEDURE trigger_notice_ab (); CREATE TABLE parted_trigger_3 ( b text, a int ) PARTITION BY RANGE (length(b)); CREATE TABLE parted_trigger_3_1 PARTITION OF parted_trigger_3 FOR VALUES FROM (1) TO (3); CREATE TABLE parted_trigger_3_2 PARTITION OF parted_trigger_3 FOR VALUES FROM (3) TO (5); ALTER TABLE parted_trigger ATTACH PARTITION parted_trigger_3 FOR VALUES FROM (2000) TO (3000); INSERT INTO parted_trigger VALUES (0, 'a'), (1, 'bbb'), (2, 'bcd'), (3, 'c'), (1000, 'c'), (1001, 'ddd'), (1002, 'efg'), (1003, 'f'), (2000, 'e'), (2001, 'fff'), (2002, 'ghi'), (2003, 'h'); UPDATE parted_trigger SET a = a + 2; -- notice for odd 'a' values, long 'b' values DROP TABLE parted_trigger; -- try a constraint trigger, also CREATE TABLE parted_referenced ( a int ); CREATE TABLE unparted_trigger ( a int, b text ); -- for comparison purposes CREATE TABLE parted_trigger ( a int, b text ) PARTITION BY RANGE (a); CREATE TABLE parted_trigger_1 PARTITION OF parted_trigger FOR VALUES FROM (0) TO (1000); CREATE TABLE parted_trigger_2 ( drp int, a int, b text ); ALTER TABLE parted_trigger_2 DROP COLUMN drp; ALTER TABLE parted_trigger ATTACH PARTITION parted_trigger_2 FOR VALUES FROM (1000) TO (2000); CREATE CONSTRAINT TRIGGER parted_trigger AFTER UPDATE ON parted_trigger FROM parted_referenced FOR EACH ROW EXECUTE PROCEDURE trigger_notice_ab (); CREATE CONSTRAINT TRIGGER parted_trigger AFTER UPDATE ON unparted_trigger FROM parted_referenced FOR EACH ROW EXECUTE PROCEDURE trigger_notice_ab (); CREATE TABLE parted_trigger_3 ( b text, a int ) PARTITION BY RANGE (length(b)); CREATE TABLE parted_trigger_3_1 PARTITION OF parted_trigger_3 FOR VALUES FROM (1) TO (3); CREATE TABLE parted_trigger_3_2 PARTITION OF parted_trigger_3 FOR VALUES FROM (3) TO (5); ALTER TABLE parted_trigger ATTACH PARTITION parted_trigger_3 FOR VALUES FROM (2000) TO (3000); SELECT tgname, conname, t.tgrelid::regclass, t.tgconstrrelid::regclass, c.conrelid::regclass, c.confrelid::regclass FROM pg_trigger t JOIN pg_constraint c ON (t.tgconstraint = c.oid) WHERE tgname = 'parted_trigger' ORDER BY t.tgrelid::regclass::text; DROP TABLE parted_referenced, parted_trigger, unparted_trigger; -- verify that the "AFTER UPDATE OF columns" event is propagated correctly CREATE TABLE parted_trigger ( a int, b text ) PARTITION BY RANGE (a); CREATE TABLE parted_trigger_1 PARTITION OF parted_trigger FOR VALUES FROM (0) TO (1000); CREATE TABLE parted_trigger_2 ( drp int, a int, b text ); ALTER TABLE parted_trigger_2 DROP COLUMN drp; ALTER TABLE parted_trigger ATTACH PARTITION parted_trigger_2 FOR VALUES FROM (1000) TO (2000); CREATE TRIGGER parted_trigger AFTER UPDATE OF b ON parted_trigger FOR EACH ROW EXECUTE PROCEDURE trigger_notice_ab (); CREATE TABLE parted_trigger_3 ( b text, a int ) PARTITION BY RANGE (length(b)); CREATE TABLE parted_trigger_3_1 PARTITION OF parted_trigger_3 FOR VALUES FROM (1) TO (4); CREATE TABLE parted_trigger_3_2 PARTITION OF parted_trigger_3 FOR VALUES FROM (4) TO (8); ALTER TABLE parted_trigger ATTACH PARTITION parted_trigger_3 FOR VALUES FROM (2000) TO (3000); INSERT INTO parted_trigger VALUES (0, 'a'), (1000, 'c'), (2000, 'e'), (2001, 'eeee'); UPDATE parted_trigger SET a = a + 2; -- no notices here UPDATE parted_trigger SET b = b || 'b'; -- all triggers should fire DROP TABLE parted_trigger; DROP FUNCTION trigger_notice_ab (); -- Make sure we don't end up with unnecessary copies of triggers, when -- cloning them. CREATE TABLE trg_clone ( a int ) PARTITION BY RANGE (a); CREATE TABLE trg_clone1 PARTITION OF trg_clone FOR VALUES FROM (0) TO (1000); ALTER TABLE trg_clone ADD CONSTRAINT uniq UNIQUE (a) DEFERRABLE; CREATE TABLE trg_clone2 PARTITION OF trg_clone FOR VALUES FROM (1000) TO (2000); CREATE TABLE trg_clone3 PARTITION OF trg_clone FOR VALUES FROM (2000) TO (3000) PARTITION BY RANGE (a); CREATE TABLE trg_clone_3_3 PARTITION OF trg_clone3 FOR VALUES FROM (2000) TO (2100); SELECT tgrelid::regclass, count(*) FROM pg_trigger WHERE tgrelid::regclass IN ('trg_clone', 'trg_clone1', 'trg_clone2', 'trg_clone3', 'trg_clone_3_3') GROUP BY tgrelid::regclass ORDER BY tgrelid::regclass; DROP TABLE trg_clone; -- -- Test the interaction between transition tables and both kinds of -- inheritance. We'll dump the contents of the transition tables in a -- format that shows the attribute order, so that we can distinguish -- tuple formats (though not dropped attributes). -- CREATE OR REPLACE FUNCTION dump_insert () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RAISE notice 'trigger = %, new table = %', TG_NAME, ( SELECT string_agg(new_table::text, ', ' ORDER BY a) FROM new_table); RETURN NULL; END; $$; CREATE OR REPLACE FUNCTION dump_update () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RAISE notice 'trigger = %, old table = %, new table = %', TG_NAME, ( SELECT string_agg(old_table::text, ', ' ORDER BY a) FROM old_table), ( SELECT string_agg(new_table::text, ', ' ORDER BY a) FROM new_table); RETURN NULL; END; $$; CREATE OR REPLACE FUNCTION dump_delete () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RAISE notice 'trigger = %, old table = %', TG_NAME, ( SELECT string_agg(old_table::text, ', ' ORDER BY a) FROM old_table); RETURN NULL; END; $$; -- -- Verify behavior of statement triggers on partition hierarchy with -- transition tables. Tuples should appear to each trigger in the -- format of the relation the trigger is attached to. -- -- set up a partition hierarchy with some different TupleDescriptors CREATE TABLE parent ( a text, b int ) PARTITION BY LIST (a); -- a child matching parent CREATE TABLE child1 PARTITION OF parent FOR VALUES IN ('AAA'); -- a child with a dropped column CREATE TABLE child2 ( x int, a text, b int ); ALTER TABLE child2 DROP COLUMN x; ALTER TABLE parent ATTACH PARTITION child2 FOR VALUES IN ('BBB'); -- a child with a different column order CREATE TABLE child3 ( b int, a text ); ALTER TABLE parent ATTACH PARTITION child3 FOR VALUES IN ('CCC'); CREATE TRIGGER parent_insert_trig AFTER INSERT ON parent referencing new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_insert (); CREATE TRIGGER parent_update_trig AFTER UPDATE ON parent referencing old TABLE AS old_table new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_update (); CREATE TRIGGER parent_delete_trig AFTER DELETE ON parent referencing old TABLE AS old_table FOR EACH statement EXECUTE PROCEDURE dump_delete (); CREATE TRIGGER child1_insert_trig AFTER INSERT ON child1 referencing new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_insert (); CREATE TRIGGER child1_update_trig AFTER UPDATE ON child1 referencing old TABLE AS old_table new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_update (); CREATE TRIGGER child1_delete_trig AFTER DELETE ON child1 referencing old TABLE AS old_table FOR EACH statement EXECUTE PROCEDURE dump_delete (); CREATE TRIGGER child2_insert_trig AFTER INSERT ON child2 referencing new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_insert (); CREATE TRIGGER child2_update_trig AFTER UPDATE ON child2 referencing old TABLE AS old_table new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_update (); CREATE TRIGGER child2_delete_trig AFTER DELETE ON child2 referencing old TABLE AS old_table FOR EACH statement EXECUTE PROCEDURE dump_delete (); CREATE TRIGGER child3_insert_trig AFTER INSERT ON child3 referencing new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_insert (); CREATE TRIGGER child3_update_trig AFTER UPDATE ON child3 referencing old TABLE AS old_table new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_update (); CREATE TRIGGER child3_delete_trig AFTER DELETE ON child3 referencing old TABLE AS old_table FOR EACH statement EXECUTE PROCEDURE dump_delete (); SELECT trigger_name, event_manipulation, event_object_schema, event_object_table, action_order, action_condition, action_orientation, action_timing, action_reference_old_table, action_reference_new_table FROM information_schema.triggers WHERE event_object_table IN ('parent', 'child1', 'child2', 'child3') ORDER BY trigger_name COLLATE "C", 2; -- insert directly into children sees respective child-format tuples INSERT INTO child1 VALUES ('AAA', 42); INSERT INTO child2 VALUES ('BBB', 42); INSERT INTO child3 VALUES (42, 'CCC'); -- update via parent sees parent-format tuples UPDATE parent SET b = b + 1; -- delete via parent sees parent-format tuples DELETE FROM parent; -- insert into parent sees parent-format tuples INSERT INTO parent VALUES ('AAA', 42); INSERT INTO parent VALUES ('BBB', 42); INSERT INTO parent VALUES ('CCC', 42); -- delete from children sees respective child-format tuples DELETE FROM child1; DELETE FROM child2; DELETE FROM child3; -- DML affecting parent sees tuples collected from children even if -- there is no transition table trigger on the children DROP TRIGGER child1_insert_trig ON child1; DROP TRIGGER child1_update_trig ON child1; DROP TRIGGER child1_delete_trig ON child1; DROP TRIGGER child2_insert_trig ON child2; DROP TRIGGER child2_update_trig ON child2; DROP TRIGGER child2_delete_trig ON child2; DROP TRIGGER child3_insert_trig ON child3; DROP TRIGGER child3_update_trig ON child3; DROP TRIGGER child3_delete_trig ON child3; DELETE FROM parent; -- insert into parent with a before trigger on a child tuple before -- insertion, and we capture the newly modified row in parent format CREATE OR REPLACE FUNCTION intercept_insert () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN new.b = new.b + 1000; RETURN new; END; $$; CREATE TRIGGER intercept_insert_child3 BEFORE INSERT ON child3 FOR EACH ROW EXECUTE PROCEDURE intercept_insert (); -- insert, parent trigger sees post-modification parent-format tuple INSERT INTO parent VALUES ('AAA', 42), ('BBB', 42), ('CCC', 66); DROP TABLE child1, child2, child3, parent; DROP FUNCTION intercept_insert (); -- -- Verify prohibition of row triggers with transition triggers on -- partitions -- CREATE TABLE parent ( a text, b int ) PARTITION BY LIST (a); CREATE TABLE child PARTITION OF parent FOR VALUES IN ('AAA'); -- adding row trigger with transition table fails CREATE TRIGGER child_row_trig AFTER INSERT ON child referencing new TABLE AS new_table FOR EACH ROW EXECUTE PROCEDURE dump_insert (); -- detaching it first works ALTER TABLE parent DETACH PARTITION child; CREATE TRIGGER child_row_trig AFTER INSERT ON child referencing new TABLE AS new_table FOR EACH ROW EXECUTE PROCEDURE dump_insert (); -- but now we're not allowed to reattach it ALTER TABLE parent ATTACH PARTITION child FOR VALUES IN ('AAA'); -- drop the trigger, and now we're allowed to attach it again DROP TRIGGER child_row_trig ON child; ALTER TABLE parent ATTACH PARTITION child FOR VALUES IN ('AAA'); DROP TABLE child, parent; -- -- Verify behavior of statement triggers on (non-partition) -- inheritance hierarchy with transition tables; similar to the -- partition case, except there is no rerouting on insertion and child -- tables can have extra columns -- -- set up inheritance hierarchy with different TupleDescriptors CREATE TABLE parent ( a text, b int ); -- a child matching parent CREATE TABLE child1 () INHERITS ( parent ); -- a child with a different column order CREATE TABLE child2 ( b int, a text ); ALTER TABLE child2 inherit parent; -- a child with an extra column CREATE TABLE child3 ( c text ) INHERITS ( parent ); CREATE TRIGGER parent_insert_trig AFTER INSERT ON parent referencing new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_insert (); CREATE TRIGGER parent_update_trig AFTER UPDATE ON parent referencing old TABLE AS old_table new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_update (); CREATE TRIGGER parent_delete_trig AFTER DELETE ON parent referencing old TABLE AS old_table FOR EACH statement EXECUTE PROCEDURE dump_delete (); CREATE TRIGGER child1_insert_trig AFTER INSERT ON child1 referencing new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_insert (); CREATE TRIGGER child1_update_trig AFTER UPDATE ON child1 referencing old TABLE AS old_table new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_update (); CREATE TRIGGER child1_delete_trig AFTER DELETE ON child1 referencing old TABLE AS old_table FOR EACH statement EXECUTE PROCEDURE dump_delete (); CREATE TRIGGER child2_insert_trig AFTER INSERT ON child2 referencing new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_insert (); CREATE TRIGGER child2_update_trig AFTER UPDATE ON child2 referencing old TABLE AS old_table new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_update (); CREATE TRIGGER child2_delete_trig AFTER DELETE ON child2 referencing old TABLE AS old_table FOR EACH statement EXECUTE PROCEDURE dump_delete (); CREATE TRIGGER child3_insert_trig AFTER INSERT ON child3 referencing new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_insert (); CREATE TRIGGER child3_update_trig AFTER UPDATE ON child3 referencing old TABLE AS old_table new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_update (); CREATE TRIGGER child3_delete_trig AFTER DELETE ON child3 referencing old TABLE AS old_table FOR EACH statement EXECUTE PROCEDURE dump_delete (); -- insert directly into children sees respective child-format tuples INSERT INTO child1 VALUES ('AAA', 42); INSERT INTO child2 VALUES (42, 'BBB'); INSERT INTO child3 VALUES ('CCC', 42, 'foo'); -- update via parent sees parent-format tuples UPDATE parent SET b = b + 1; -- delete via parent sees parent-format tuples DELETE FROM parent; -- reinsert values into children for next test... INSERT INTO child1 VALUES ('AAA', 42); INSERT INTO child2 VALUES (42, 'BBB'); INSERT INTO child3 VALUES ('CCC', 42, 'foo'); -- delete from children sees respective child-format tuples DELETE FROM child1; DELETE FROM child2; DELETE FROM child3; -- same behavior for copy if there is an index (interesting because rows are -- captured by a different code path in copy.c if there are indexes) CREATE INDEX ON parent (b); -- DML affecting parent sees tuples collected from children even if -- there is no transition table trigger on the children DROP TRIGGER child1_insert_trig ON child1; DROP TRIGGER child1_update_trig ON child1; DROP TRIGGER child1_delete_trig ON child1; DROP TRIGGER child2_insert_trig ON child2; DROP TRIGGER child2_update_trig ON child2; DROP TRIGGER child2_delete_trig ON child2; DROP TRIGGER child3_insert_trig ON child3; DROP TRIGGER child3_update_trig ON child3; DROP TRIGGER child3_delete_trig ON child3; DELETE FROM parent; DROP TABLE child1, child2, child3, parent; -- -- Verify prohibition of row triggers with transition triggers on -- inheritance children -- CREATE TABLE parent ( a text, b int ); CREATE TABLE child () INHERITS ( parent ); -- adding row trigger with transition table fails CREATE TRIGGER child_row_trig AFTER INSERT ON child referencing new TABLE AS new_table FOR EACH ROW EXECUTE PROCEDURE dump_insert (); -- disinheriting it first works ALTER TABLE child NO inherit parent; CREATE TRIGGER child_row_trig AFTER INSERT ON child referencing new TABLE AS new_table FOR EACH ROW EXECUTE PROCEDURE dump_insert (); -- but now we're not allowed to make it inherit anymore ALTER TABLE child inherit parent; -- drop the trigger, and now we're allowed to make it inherit again DROP TRIGGER child_row_trig ON child; ALTER TABLE child inherit parent; DROP TABLE child, parent; -- -- Verify behavior of queries with wCTEs, where multiple transition -- tuplestores can be active at the same time because there are -- multiple DML statements that might fire triggers with transition -- tables -- CREATE TABLE table1 ( a int ); CREATE TABLE table2 ( a text ); CREATE TRIGGER table1_trig AFTER INSERT ON table1 referencing new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_insert (); CREATE TRIGGER table2_trig AFTER INSERT ON table2 referencing new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_insert (); WITH wcte AS ( INSERT INTO table1 VALUES (42)) INSERT INTO table2 VALUES ('hello world'); WITH wcte AS ( INSERT INTO table1 VALUES (43)) INSERT INTO table1 VALUES (44); SELECT * FROM table1; SELECT * FROM table2; DROP TABLE table1; DROP TABLE table2; -- -- Verify behavior of INSERT ... ON CONFLICT DO UPDATE ... with -- transition tables. -- CREATE TABLE my_table ( a int PRIMARY KEY, b text ); CREATE TRIGGER my_table_insert_trig AFTER INSERT ON my_table referencing new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_insert (); CREATE TRIGGER my_table_update_trig AFTER UPDATE ON my_table referencing old TABLE AS old_table new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_update (); -- inserts only INSERT INTO my_table VALUES (1, 'AAA'), (2, 'BBB') ON CONFLICT (a) DO UPDATE SET b = my_table.b || ':' || excluded.b; -- mixture of inserts and updates INSERT INTO my_table VALUES (1, 'AAA'), (2, 'BBB'), (3, 'CCC'), (4, 'DDD') ON CONFLICT (a) DO UPDATE SET b = my_table.b || ':' || excluded.b; -- updates only INSERT INTO my_table VALUES (3, 'CCC'), (4, 'DDD') ON CONFLICT (a) DO UPDATE SET b = my_table.b || ':' || excluded.b; -- -- now using a partitioned table -- CREATE TABLE iocdu_tt_parted ( a int PRIMARY KEY, b text ) PARTITION BY LIST (a); CREATE TABLE iocdu_tt_parted1 PARTITION OF iocdu_tt_parted FOR VALUES IN (1); CREATE TABLE iocdu_tt_parted2 PARTITION OF iocdu_tt_parted FOR VALUES IN (2); CREATE TABLE iocdu_tt_parted3 PARTITION OF iocdu_tt_parted FOR VALUES IN (3); CREATE TABLE iocdu_tt_parted4 PARTITION OF iocdu_tt_parted FOR VALUES IN (4); CREATE TRIGGER iocdu_tt_parted_insert_trig AFTER INSERT ON iocdu_tt_parted referencing new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_insert (); CREATE TRIGGER iocdu_tt_parted_update_trig AFTER UPDATE ON iocdu_tt_parted referencing old TABLE AS old_table new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_update (); -- inserts only INSERT INTO iocdu_tt_parted VALUES (1, 'AAA'), (2, 'BBB') ON CONFLICT (a) DO UPDATE SET b = iocdu_tt_parted.b || ':' || excluded.b; -- mixture of inserts and updates INSERT INTO iocdu_tt_parted VALUES (1, 'AAA'), (2, 'BBB'), (3, 'CCC'), (4, 'DDD') ON CONFLICT (a) DO UPDATE SET b = iocdu_tt_parted.b || ':' || excluded.b; -- updates only INSERT INTO iocdu_tt_parted VALUES (3, 'CCC'), (4, 'DDD') ON CONFLICT (a) DO UPDATE SET b = iocdu_tt_parted.b || ':' || excluded.b; DROP TABLE iocdu_tt_parted; -- -- Verify that you can't create a trigger with transition tables for -- more than one event. -- CREATE TRIGGER my_table_multievent_trig AFTER INSERT OR UPDATE ON my_table referencing new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_insert (); -- -- Verify that you can't create a trigger with transition tables with -- a column list. -- CREATE TRIGGER my_table_col_update_trig AFTER UPDATE OF b ON my_table referencing new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_insert (); DROP TABLE my_table; -- -- Test firing of triggers with transition tables by foreign key cascades -- CREATE TABLE refd_table ( a int PRIMARY KEY, b text ); CREATE TABLE trig_table ( a int, b text, FOREIGN KEY (a) REFERENCES refd_table ON UPDATE CASCADE ON DELETE CASCADE ); CREATE TRIGGER trig_table_before_trig BEFORE INSERT OR UPDATE OR DELETE ON trig_table FOR EACH statement EXECUTE PROCEDURE trigger_func ('trig_table'); CREATE TRIGGER trig_table_insert_trig AFTER INSERT ON trig_table referencing new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_insert (); CREATE TRIGGER trig_table_update_trig AFTER UPDATE ON trig_table referencing old TABLE AS old_table new TABLE AS new_table FOR EACH statement EXECUTE PROCEDURE dump_update (); CREATE TRIGGER trig_table_delete_trig AFTER DELETE ON trig_table referencing old TABLE AS old_table FOR EACH statement EXECUTE PROCEDURE dump_delete (); INSERT INTO refd_table VALUES (1, 'one'), (2, 'two'), (3, 'three'); INSERT INTO trig_table VALUES (1, 'one a'), (1, 'one b'), (2, 'two a'), (2, 'two b'), (3, 'three a'), (3, 'three b'); UPDATE refd_table SET a = 11 WHERE b = 'one'; SELECT * FROM trig_table; DELETE FROM refd_table WHERE length(b) = 3; SELECT * FROM trig_table; DROP TABLE refd_table, trig_table; -- -- self-referential FKs are even more fun -- CREATE TABLE self_ref ( a int PRIMARY KEY, b int REFERENCES self_ref (a) ON DELETE CASCADE ); CREATE TRIGGER self_ref_before_trig BEFORE DELETE ON self_ref FOR EACH statement EXECUTE PROCEDURE trigger_func ('self_ref'); CREATE TRIGGER self_ref_r_trig AFTER DELETE ON self_ref referencing old TABLE AS old_table FOR EACH ROW EXECUTE PROCEDURE dump_delete (); CREATE TRIGGER self_ref_s_trig AFTER DELETE ON self_ref referencing old TABLE AS old_table FOR EACH statement EXECUTE PROCEDURE dump_delete (); INSERT INTO self_ref VALUES (1, NULL), (2, 1), (3, 2); DELETE FROM self_ref WHERE a = 1; -- without AR trigger, cascaded deletes all end up in one transition table DROP TRIGGER self_ref_r_trig ON self_ref; INSERT INTO self_ref VALUES (1, NULL), (2, 1), (3, 2), (4, 3); DELETE FROM self_ref WHERE a = 1; DROP TABLE self_ref; -- cleanup DROP FUNCTION dump_insert (); DROP FUNCTION dump_update (); DROP FUNCTION dump_delete (); pgFormatter-4.2/t/pg-test-files/expected/truncate.sql000066400000000000000000000217431361326045100227560ustar00rootroot00000000000000-- Test basic TRUNCATE functionality. CREATE TABLE truncate_a ( col1 integer PRIMARY KEY ); INSERT INTO truncate_a VALUES (1); INSERT INTO truncate_a VALUES (2); SELECT * FROM truncate_a; -- Roll truncate back BEGIN; TRUNCATE truncate_a; ROLLBACK; SELECT * FROM truncate_a; -- Commit the truncate this time BEGIN; TRUNCATE truncate_a; COMMIT; SELECT * FROM truncate_a; -- Test foreign-key checks CREATE TABLE trunc_b ( a int REFERENCES truncate_a ); CREATE TABLE trunc_c ( a serial PRIMARY KEY ); CREATE TABLE trunc_d ( a int REFERENCES trunc_c ); CREATE TABLE trunc_e ( a int REFERENCES truncate_a, b int REFERENCES trunc_c ); TRUNCATE TABLE truncate_a; -- fail TRUNCATE TABLE truncate_a, trunc_b; -- fail TRUNCATE TABLE truncate_a, trunc_b, trunc_e; -- ok TRUNCATE TABLE truncate_a, trunc_e; -- fail TRUNCATE TABLE trunc_c; -- fail TRUNCATE TABLE trunc_c, trunc_d; -- fail TRUNCATE TABLE trunc_c, trunc_d, trunc_e; -- ok TRUNCATE TABLE trunc_c, trunc_d, trunc_e, truncate_a; -- fail TRUNCATE TABLE trunc_c, trunc_d, trunc_e, truncate_a, trunc_b; -- ok TRUNCATE TABLE truncate_a RESTRICT; -- fail TRUNCATE TABLE truncate_a CASCADE; -- ok -- circular references ALTER TABLE truncate_a ADD FOREIGN KEY (col1) REFERENCES trunc_c; -- Add some data to verify that truncating actually works ... INSERT INTO trunc_c VALUES (1); INSERT INTO truncate_a VALUES (1); INSERT INTO trunc_b VALUES (1); INSERT INTO trunc_d VALUES (1); INSERT INTO trunc_e VALUES (1, 1); TRUNCATE TABLE trunc_c; TRUNCATE TABLE trunc_c, truncate_a; TRUNCATE TABLE trunc_c, truncate_a, trunc_d; TRUNCATE TABLE trunc_c, truncate_a, trunc_d, trunc_e; TRUNCATE TABLE trunc_c, truncate_a, trunc_d, trunc_e, trunc_b; -- Verify that truncating did actually work SELECT * FROM truncate_a UNION ALL SELECT * FROM trunc_c UNION ALL SELECT * FROM trunc_b UNION ALL SELECT * FROM trunc_d; SELECT * FROM trunc_e; -- Add data again to test TRUNCATE ... CASCADE INSERT INTO trunc_c VALUES (1); INSERT INTO truncate_a VALUES (1); INSERT INTO trunc_b VALUES (1); INSERT INTO trunc_d VALUES (1); INSERT INTO trunc_e VALUES (1, 1); TRUNCATE TABLE trunc_c CASCADE; -- ok SELECT * FROM truncate_a UNION ALL SELECT * FROM trunc_c UNION ALL SELECT * FROM trunc_b UNION ALL SELECT * FROM trunc_d; SELECT * FROM trunc_e; DROP TABLE truncate_a, trunc_c, trunc_b, trunc_d, trunc_e CASCADE; -- Test TRUNCATE with inheritance CREATE TABLE trunc_f ( col1 integer PRIMARY KEY ); INSERT INTO trunc_f VALUES (1); INSERT INTO trunc_f VALUES (2); CREATE TABLE trunc_fa ( col2a text ) INHERITS ( trunc_f ); INSERT INTO trunc_fa VALUES (3, 'three'); CREATE TABLE trunc_fb ( col2b int ) INHERITS ( trunc_f ); INSERT INTO trunc_fb VALUES (4, 444); CREATE TABLE trunc_faa ( col3 text ) INHERITS ( trunc_fa ); INSERT INTO trunc_faa VALUES (5, 'five', 'FIVE'); BEGIN; SELECT * FROM trunc_f; TRUNCATE trunc_f; SELECT * FROM trunc_f; ROLLBACK; BEGIN; SELECT * FROM trunc_f; TRUNCATE ONLY trunc_f; SELECT * FROM trunc_f; ROLLBACK; BEGIN; SELECT * FROM trunc_f; SELECT * FROM trunc_fa; SELECT * FROM trunc_faa; TRUNCATE ONLY trunc_fb, ONLY trunc_fa; SELECT * FROM trunc_f; SELECT * FROM trunc_fa; SELECT * FROM trunc_faa; ROLLBACK; BEGIN; SELECT * FROM trunc_f; SELECT * FROM trunc_fa; SELECT * FROM trunc_faa; TRUNCATE ONLY trunc_fb, trunc_fa; SELECT * FROM trunc_f; SELECT * FROM trunc_fa; SELECT * FROM trunc_faa; ROLLBACK; DROP TABLE trunc_f CASCADE; -- Test ON TRUNCATE triggers CREATE TABLE trunc_trigger_test ( f1 int, f2 text, f3 text ); CREATE TABLE trunc_trigger_log ( tgop text, tglevel text, tgwhen text, tgargv text, tgtable name, rowcount bigint ); CREATE FUNCTION trunctrigger () RETURNS TRIGGER AS $$ DECLARE c bigint; BEGIN EXECUTE 'select count(*) from ' || quote_ident(tg_table_name) INTO c; INSERT INTO trunc_trigger_log VALUES (TG_OP, TG_LEVEL, TG_WHEN, TG_ARGV[0], tg_table_name, c); RETURN NULL; END; $$ LANGUAGE plpgsql; -- basic before trigger INSERT INTO trunc_trigger_test VALUES (1, 'foo', 'bar'), (2, 'baz', 'quux'); CREATE TRIGGER t BEFORE TRUNCATE ON trunc_trigger_test FOR EACH STATEMENT EXECUTE PROCEDURE trunctrigger ('before trigger truncate'); SELECT count(*) AS "Row count in test table" FROM trunc_trigger_test; SELECT * FROM trunc_trigger_log; TRUNCATE trunc_trigger_test; SELECT count(*) AS "Row count in test table" FROM trunc_trigger_test; SELECT * FROM trunc_trigger_log; DROP TRIGGER t ON trunc_trigger_test; TRUNCATE trunc_trigger_log; -- same test with an after trigger INSERT INTO trunc_trigger_test VALUES (1, 'foo', 'bar'), (2, 'baz', 'quux'); CREATE TRIGGER tt AFTER TRUNCATE ON trunc_trigger_test FOR EACH STATEMENT EXECUTE PROCEDURE trunctrigger ('after trigger truncate'); SELECT count(*) AS "Row count in test table" FROM trunc_trigger_test; SELECT * FROM trunc_trigger_log; TRUNCATE trunc_trigger_test; SELECT count(*) AS "Row count in test table" FROM trunc_trigger_test; SELECT * FROM trunc_trigger_log; DROP TABLE trunc_trigger_test; DROP TABLE trunc_trigger_log; DROP FUNCTION trunctrigger (); -- test TRUNCATE ... RESTART IDENTITY CREATE SEQUENCE truncate_a_id1 START WITH 33; CREATE TABLE truncate_a ( id serial, id1 integer DEFAULT nextval('truncate_a_id1') ); ALTER SEQUENCE truncate_a_id1 OWNED BY truncate_a.id1; INSERT INTO truncate_a DEFAULT VALUES; INSERT INTO truncate_a DEFAULT VALUES; SELECT * FROM truncate_a; TRUNCATE truncate_a; INSERT INTO truncate_a DEFAULT VALUES; INSERT INTO truncate_a DEFAULT VALUES; SELECT * FROM truncate_a; TRUNCATE truncate_a RESTART IDENTITY; INSERT INTO truncate_a DEFAULT VALUES; INSERT INTO truncate_a DEFAULT VALUES; SELECT * FROM truncate_a; CREATE TABLE truncate_b ( id int GENERATED ALWAYS AS IDENTITY (START WITH 44) ); INSERT INTO truncate_b DEFAULT VALUES; INSERT INTO truncate_b DEFAULT VALUES; SELECT * FROM truncate_b; TRUNCATE truncate_b; INSERT INTO truncate_b DEFAULT VALUES; INSERT INTO truncate_b DEFAULT VALUES; SELECT * FROM truncate_b; TRUNCATE truncate_b RESTART IDENTITY; INSERT INTO truncate_b DEFAULT VALUES; INSERT INTO truncate_b DEFAULT VALUES; SELECT * FROM truncate_b; -- check rollback of a RESTART IDENTITY operation BEGIN; TRUNCATE truncate_a RESTART IDENTITY; INSERT INTO truncate_a DEFAULT VALUES; SELECT * FROM truncate_a; ROLLBACK; INSERT INTO truncate_a DEFAULT VALUES; INSERT INTO truncate_a DEFAULT VALUES; SELECT * FROM truncate_a; DROP TABLE truncate_a; SELECT nextval('truncate_a_id1'); -- fail, seq should have been dropped -- partitioned table CREATE TABLE truncparted ( a int, b char ) PARTITION BY LIST (a); -- error, can't truncate a partitioned table TRUNCATE ONLY truncparted; CREATE TABLE truncparted1 PARTITION OF truncparted FOR VALUES IN (1); INSERT INTO truncparted VALUES (1, 'a'); -- error, must truncate partitions TRUNCATE ONLY truncparted; TRUNCATE truncparted; DROP TABLE truncparted; -- foreign key on partitioned table: partition key is referencing column. -- Make sure truncate did execute on all tables CREATE FUNCTION tp_ins_data () RETURNS void LANGUAGE plpgsql AS $$ BEGIN INSERT INTO truncprim VALUES (1), (100), (150); INSERT INTO truncpart VALUES (1), (100), (150); END $$; CREATE FUNCTION tp_chk_data (OUT pktb regclass, OUT pkval int, OUT fktb regclass, OUT fkval int) RETURNS SETOF record LANGUAGE plpgsql AS $$ BEGIN RETURN QUERY SELECT pk.tableoid::regclass, pk.a, fk.tableoid::regclass, fk.a FROM truncprim pk FULL JOIN truncpart fk USING (a) ORDER BY 2, 4; END $$; CREATE TABLE truncprim ( a int PRIMARY KEY ); CREATE TABLE truncpart ( a int REFERENCES truncprim ) PARTITION BY RANGE (a); CREATE TABLE truncpart_1 PARTITION OF truncpart FOR VALUES FROM (0) TO (100); CREATE TABLE truncpart_2 PARTITION OF truncpart FOR VALUES FROM (100) TO (200) PARTITION BY RANGE (a); CREATE TABLE truncpart_2_1 PARTITION OF truncpart_2 FOR VALUES FROM (100) TO (150); CREATE TABLE truncpart_2_d PARTITION OF truncpart_2 DEFAULT; TRUNCATE TABLE truncprim; -- should fail SELECT tp_ins_data (); -- should truncate everything TRUNCATE TABLE truncprim, truncpart; SELECT * FROM tp_chk_data (); SELECT tp_ins_data (); -- should truncate everything TRUNCATE TABLE truncprim CASCADE; SELECT * FROM tp_chk_data (); SELECT tp_ins_data (); -- should truncate all partitions TRUNCATE TABLE truncpart; SELECT * FROM tp_chk_data (); DROP TABLE truncprim, truncpart; DROP FUNCTION tp_ins_data (), tp_chk_data (); pgFormatter-4.2/t/pg-test-files/expected/tsdicts.sql000066400000000000000000000177371361326045100226160ustar00rootroot00000000000000--Test text search dictionaries and configurations -- Test ISpell dictionary with ispell affix file CREATE TEXT SEARCH DICTIONARY ispell ( TEMPLATE = ispell, DictFile = ispell_sample, AffFile = ispell_sample ); SELECT ts_lexize('ispell', 'skies'); SELECT ts_lexize('ispell', 'bookings'); SELECT ts_lexize('ispell', 'booking'); SELECT ts_lexize('ispell', 'foot'); SELECT ts_lexize('ispell', 'foots'); SELECT ts_lexize('ispell', 'rebookings'); SELECT ts_lexize('ispell', 'rebooking'); SELECT ts_lexize('ispell', 'rebook'); SELECT ts_lexize('ispell', 'unbookings'); SELECT ts_lexize('ispell', 'unbooking'); SELECT ts_lexize('ispell', 'unbook'); SELECT ts_lexize('ispell', 'footklubber'); SELECT ts_lexize('ispell', 'footballklubber'); SELECT ts_lexize('ispell', 'ballyklubber'); SELECT ts_lexize('ispell', 'footballyklubber'); -- Test ISpell dictionary with hunspell affix file CREATE TEXT SEARCH DICTIONARY hunspell ( TEMPLATE = ispell, DictFile = ispell_sample, AffFile = hunspell_sample ); SELECT ts_lexize('hunspell', 'skies'); SELECT ts_lexize('hunspell', 'bookings'); SELECT ts_lexize('hunspell', 'booking'); SELECT ts_lexize('hunspell', 'foot'); SELECT ts_lexize('hunspell', 'foots'); SELECT ts_lexize('hunspell', 'rebookings'); SELECT ts_lexize('hunspell', 'rebooking'); SELECT ts_lexize('hunspell', 'rebook'); SELECT ts_lexize('hunspell', 'unbookings'); SELECT ts_lexize('hunspell', 'unbooking'); SELECT ts_lexize('hunspell', 'unbook'); SELECT ts_lexize('hunspell', 'footklubber'); SELECT ts_lexize('hunspell', 'footballklubber'); SELECT ts_lexize('hunspell', 'ballyklubber'); SELECT ts_lexize('hunspell', 'footballyklubber'); -- Test ISpell dictionary with hunspell affix file with FLAG long parameter CREATE TEXT SEARCH DICTIONARY hunspell_long ( TEMPLATE = ispell, DictFile = hunspell_sample_long, AffFile = hunspell_sample_long ); SELECT ts_lexize('hunspell_long', 'skies'); SELECT ts_lexize('hunspell_long', 'bookings'); SELECT ts_lexize('hunspell_long', 'booking'); SELECT ts_lexize('hunspell_long', 'foot'); SELECT ts_lexize('hunspell_long', 'foots'); SELECT ts_lexize('hunspell_long', 'rebookings'); SELECT ts_lexize('hunspell_long', 'rebooking'); SELECT ts_lexize('hunspell_long', 'rebook'); SELECT ts_lexize('hunspell_long', 'unbookings'); SELECT ts_lexize('hunspell_long', 'unbooking'); SELECT ts_lexize('hunspell_long', 'unbook'); SELECT ts_lexize('hunspell_long', 'booked'); SELECT ts_lexize('hunspell_long', 'footklubber'); SELECT ts_lexize('hunspell_long', 'footballklubber'); SELECT ts_lexize('hunspell_long', 'ballyklubber'); SELECT ts_lexize('hunspell_long', 'ballsklubber'); SELECT ts_lexize('hunspell_long', 'footballyklubber'); SELECT ts_lexize('hunspell_long', 'ex-machina'); -- Test ISpell dictionary with hunspell affix file with FLAG num parameter CREATE TEXT SEARCH DICTIONARY hunspell_num ( TEMPLATE = ispell, DictFile = hunspell_sample_num, AffFile = hunspell_sample_num ); SELECT ts_lexize('hunspell_num', 'skies'); SELECT ts_lexize('hunspell_num', 'sk'); SELECT ts_lexize('hunspell_num', 'bookings'); SELECT ts_lexize('hunspell_num', 'booking'); SELECT ts_lexize('hunspell_num', 'foot'); SELECT ts_lexize('hunspell_num', 'foots'); SELECT ts_lexize('hunspell_num', 'rebookings'); SELECT ts_lexize('hunspell_num', 'rebooking'); SELECT ts_lexize('hunspell_num', 'rebook'); SELECT ts_lexize('hunspell_num', 'unbookings'); SELECT ts_lexize('hunspell_num', 'unbooking'); SELECT ts_lexize('hunspell_num', 'unbook'); SELECT ts_lexize('hunspell_num', 'booked'); SELECT ts_lexize('hunspell_num', 'footklubber'); SELECT ts_lexize('hunspell_num', 'footballklubber'); SELECT ts_lexize('hunspell_num', 'ballyklubber'); SELECT ts_lexize('hunspell_num', 'footballyklubber'); -- Synonym dictionary CREATE TEXT SEARCH DICTIONARY synonym ( TEMPLATE = synonym, Synonyms = synonym_sample ); SELECT ts_lexize('synonym', 'PoStGrEs'); SELECT ts_lexize('synonym', 'Gogle'); SELECT ts_lexize('synonym', 'indices'); -- Create and simple test thesaurus dictionary -- More tests in configuration checks because ts_lexize() -- cannot pass more than one word to thesaurus. CREATE TEXT SEARCH DICTIONARY thesaurus ( TEMPLATE = thesaurus, DictFile = thesaurus_sample, Dictionary = english_stem ); SELECT ts_lexize('thesaurus', 'one'); -- Test ispell dictionary in configuration CREATE TEXT SEARCH CONFIGURATION ispell_tst ( COPY = english ); ALTER TEXT SEARCH CONFIGURATION ispell_tst ALTER MAPPING FOR word, numword, asciiword, hword, numhword, asciihword, hword_part, hword_numpart, hword_asciipart WITH ispell, english_stem; SELECT to_tsvector('ispell_tst', 'Booking the skies after rebookings for footballklubber from a foot'); SELECT to_tsquery('ispell_tst', 'footballklubber'); SELECT to_tsquery('ispell_tst', 'footballyklubber:b & rebookings:A & sky'); -- Test ispell dictionary with hunspell affix in configuration CREATE TEXT SEARCH CONFIGURATION hunspell_tst ( COPY = ispell_tst ); ALTER TEXT SEARCH CONFIGURATION hunspell_tst ALTER MAPPING REPLACE ispell WITH hunspell; SELECT to_tsvector('hunspell_tst', 'Booking the skies after rebookings for footballklubber from a foot'); SELECT to_tsquery('hunspell_tst', 'footballklubber'); SELECT to_tsquery('hunspell_tst', 'footballyklubber:b & rebookings:A & sky'); SELECT to_tsquery('hunspell_tst', 'footballyklubber:b <-> sky'); SELECT phraseto_tsquery ('hunspell_tst', 'footballyklubber sky'); -- Test ispell dictionary with hunspell affix with FLAG long in configuration ALTER TEXT SEARCH CONFIGURATION hunspell_tst ALTER MAPPING REPLACE hunspell WITH hunspell_long; SELECT to_tsvector('hunspell_tst', 'Booking the skies after rebookings for footballklubber from a foot'); SELECT to_tsquery('hunspell_tst', 'footballklubber'); SELECT to_tsquery('hunspell_tst', 'footballyklubber:b & rebookings:A & sky'); -- Test ispell dictionary with hunspell affix with FLAG num in configuration ALTER TEXT SEARCH CONFIGURATION hunspell_tst ALTER MAPPING REPLACE hunspell_long WITH hunspell_num; SELECT to_tsvector('hunspell_tst', 'Booking the skies after rebookings for footballklubber from a foot'); SELECT to_tsquery('hunspell_tst', 'footballklubber'); SELECT to_tsquery('hunspell_tst', 'footballyklubber:b & rebookings:A & sky'); -- Test synonym dictionary in configuration CREATE TEXT SEARCH CONFIGURATION synonym_tst ( COPY = english ); ALTER TEXT SEARCH CONFIGURATION synonym_tst ALTER MAPPING FOR asciiword, hword_asciipart, asciihword WITH synonym, english_stem; SELECT to_tsvector('synonym_tst', 'Postgresql is often called as postgres or pgsql and pronounced as postgre'); SELECT to_tsvector('synonym_tst', 'Most common mistake is to write Gogle instead of Google'); SELECT to_tsvector('synonym_tst', 'Indexes or indices - Which is right plural form of index?'); SELECT to_tsquery('synonym_tst', 'Index & indices'); -- test thesaurus in configuration -- see thesaurus_sample.ths to understand 'odd' resulting tsvector CREATE TEXT SEARCH CONFIGURATION thesaurus_tst ( COPY = synonym_tst ); ALTER TEXT SEARCH CONFIGURATION thesaurus_tst ALTER MAPPING FOR asciiword, hword_asciipart, asciihword WITH synonym, thesaurus, english_stem; SELECT to_tsvector('thesaurus_tst', 'one postgres one two one two three one'); SELECT to_tsvector('thesaurus_tst', 'Supernovae star is very new star and usually called supernovae (abbreviation SN)'); SELECT to_tsvector('thesaurus_tst', 'Booking tickets is looking like a booking a tickets'); -- invalid: non-lowercase quoted identifiers CREATE TEXT SEARCH DICTIONARY tsdict_case ( TEMPLATE = ispell, "DictFile" = ispell_sample, "AffFile" = ispell_sample ); pgFormatter-4.2/t/pg-test-files/expected/tsearch.sql000066400000000000000000000720321361326045100225570ustar00rootroot00000000000000-- -- Sanity checks for text search catalogs -- -- NB: we assume the oidjoins test will have caught any dangling links, -- that is OID or REGPROC fields that are not zero and do not match some -- row in the linked-to table. However, if we want to enforce that a link -- field can't be 0, we have to check it here. -- Find unexpected zero link entries SELECT oid, prsname FROM pg_ts_parser WHERE prsnamespace = 0 OR prsstart = 0 OR prstoken = 0 OR prsend = 0 OR -- prsheadline is optional prslextype = 0; SELECT oid, dictname FROM pg_ts_dict WHERE dictnamespace = 0 OR dictowner = 0 OR dicttemplate = 0; SELECT oid, tmplname FROM pg_ts_template WHERE tmplnamespace = 0 OR tmpllexize = 0; -- tmplinit is optional SELECT oid, cfgname FROM pg_ts_config WHERE cfgnamespace = 0 OR cfgowner = 0 OR cfgparser = 0; SELECT mapcfg, maptokentype, mapseqno FROM pg_ts_config_map WHERE mapcfg = 0 OR mapdict = 0; -- Look for pg_ts_config_map entries that aren't one of parser's token types SELECT * FROM ( SELECT oid AS cfgid, (ts_token_type(cfgparser)).tokid AS tokid FROM pg_ts_config) AS tt RIGHT JOIN pg_ts_config_map AS m ON (tt.cfgid = m.mapcfg AND tt.tokid = m.maptokentype) WHERE tt.cfgid IS NULL OR tt.tokid IS NULL; -- test basic text search behavior without indexes, then with SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*'; SELECT count(*) FROM test_tsvector WHERE a @@ ANY ('{wr,qh}'); SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme'; SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme'; CREATE INDEX wowidx ON test_tsvector USING gist (a); SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = OFF; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*'; SELECT count(*) FROM test_tsvector WHERE a @@ ANY ('{wr,qh}'); SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme'; SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme'; SET enable_indexscan = OFF; SET enable_bitmapscan = ON; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*'; SELECT count(*) FROM test_tsvector WHERE a @@ ANY ('{wr,qh}'); SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme'; SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme'; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; DROP INDEX wowidx; CREATE INDEX wowidx ON test_tsvector USING gin (a); SET enable_seqscan = OFF; -- GIN only supports bitmapscan, so no need to test plain indexscan EXPLAIN ( COSTS OFF ) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*'; SELECT count(*) FROM test_tsvector WHERE a @@ ANY ('{wr,qh}'); SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme'; SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme'; RESET enable_seqscan; INSERT INTO test_tsvector VALUES ('???', 'DFG:1A,2B,6C,10 FGH'); SELECT * FROM ts_stat('SELECT a FROM test_tsvector') ORDER BY ndoc DESC, nentry DESC, word LIMIT 10; SELECT * FROM ts_stat('SELECT a FROM test_tsvector', 'AB') ORDER BY ndoc DESC, nentry DESC, word; --dictionaries and to_tsvector SELECT ts_lexize('english_stem', 'skies'); SELECT ts_lexize('english_stem', 'identity'); SELECT * FROM ts_token_type('default'); SELECT * FROM ts_parse('default', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net teodor@123-stack.net 123_teodor@stack.net 123-teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 /usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 wow < jqw <> qwerty'); SELECT to_tsvector('english', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net teodor@123-stack.net 123_teodor@stack.net 123-teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 /usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 wow < jqw <> qwerty'); SELECT length(to_tsvector('english', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net teodor@123-stack.net 123_teodor@stack.net 123-teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 /usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 wow < jqw <> qwerty')); -- ts_debug SELECT * FROM ts_debug('english', 'abc&nm1;def©ghiõjkl'); -- check parsing of URLs SELECT * FROM ts_debug('english', 'http://www.harewoodsolutions.co.uk/press.aspx'); SELECT * FROM ts_debug('english', 'http://aew.wer0c.ewr/id?ad=qwe&dw'); SELECT * FROM ts_debug('english', 'http://5aew.werc.ewr:8100/?'); SELECT * FROM ts_debug('english', '5aew.werc.ewr:8100/?xx'); SELECT token, alias, dictionaries, dictionaries IS NULL AS dnull, array_dims(dictionaries) AS ddims, lexemes, lexemes IS NULL AS lnull, array_dims(lexemes) AS ldims FROM ts_debug('english', 'a title'); -- to_tsquery SELECT to_tsquery('english', 'qwe & sKies '); SELECT to_tsquery('simple', 'qwe & sKies '); SELECT to_tsquery('english', '''the wether'':dc & '' sKies '':BC '); SELECT to_tsquery('english', 'asd&(and|fghj)'); SELECT to_tsquery('english', '(asd&and)|fghj'); SELECT to_tsquery('english', '(asd&!and)|fghj'); SELECT to_tsquery('english', '(the|and&(i&1))&fghj'); SELECT plainto_tsquery('english', 'the and z 1))& fghj'); SELECT plainto_tsquery('english', 'foo bar') && plainto_tsquery('english', 'asd'); SELECT plainto_tsquery('english', 'foo bar') || plainto_tsquery('english', 'asd fg'); SELECT plainto_tsquery('english', 'foo bar') || !! plainto_tsquery('english', 'asd fg'); SELECT plainto_tsquery('english', 'foo bar') && 'asd | fg'; -- Check stop word deletion, a and s are stop-words SELECT to_tsquery('english', '!(a & !b) & c'); SELECT to_tsquery('english', '!(a & !b)'); SELECT to_tsquery('english', '(1 <-> 2) <-> a'); SELECT to_tsquery('english', '(1 <-> a) <-> 2'); SELECT to_tsquery('english', '(a <-> 1) <-> 2'); SELECT to_tsquery('english', 'a <-> (1 <-> 2)'); SELECT to_tsquery('english', '1 <-> (a <-> 2)'); SELECT to_tsquery('english', '1 <-> (2 <-> a)'); SELECT to_tsquery('english', '(1 <-> 2) <3> a'); SELECT to_tsquery('english', '(1 <-> a) <3> 2'); SELECT to_tsquery('english', '(a <-> 1) <3> 2'); SELECT to_tsquery('english', 'a <3> (1 <-> 2)'); SELECT to_tsquery('english', '1 <3> (a <-> 2)'); SELECT to_tsquery('english', '1 <3> (2 <-> a)'); SELECT to_tsquery('english', '(1 <3> 2) <-> a'); SELECT to_tsquery('english', '(1 <3> a) <-> 2'); SELECT to_tsquery('english', '(a <3> 1) <-> 2'); SELECT to_tsquery('english', 'a <-> (1 <3> 2)'); SELECT to_tsquery('english', '1 <-> (a <3> 2)'); SELECT to_tsquery('english', '1 <-> (2 <3> a)'); SELECT to_tsquery('english', '((a <-> 1) <-> 2) <-> s'); SELECT to_tsquery('english', '(2 <-> (a <-> 1)) <-> s'); SELECT to_tsquery('english', '((1 <-> a) <-> 2) <-> s'); SELECT to_tsquery('english', '(2 <-> (1 <-> a)) <-> s'); SELECT to_tsquery('english', 's <-> ((a <-> 1) <-> 2)'); SELECT to_tsquery('english', 's <-> (2 <-> (a <-> 1))'); SELECT to_tsquery('english', 's <-> ((1 <-> a) <-> 2)'); SELECT to_tsquery('english', 's <-> (2 <-> (1 <-> a))'); SELECT to_tsquery('english', '((a <-> 1) <-> s) <-> 2'); SELECT to_tsquery('english', '(s <-> (a <-> 1)) <-> 2'); SELECT to_tsquery('english', '((1 <-> a) <-> s) <-> 2'); SELECT to_tsquery('english', '(s <-> (1 <-> a)) <-> 2'); SELECT to_tsquery('english', '2 <-> ((a <-> 1) <-> s)'); SELECT to_tsquery('english', '2 <-> (s <-> (a <-> 1))'); SELECT to_tsquery('english', '2 <-> ((1 <-> a) <-> s)'); SELECT to_tsquery('english', '2 <-> (s <-> (1 <-> a))'); SELECT to_tsquery('english', 'foo <-> (a <-> (the <-> bar))'); SELECT to_tsquery('english', '((foo <-> a) <-> the) <-> bar'); SELECT to_tsquery('english', 'foo <-> a <-> the <-> bar'); SELECT phraseto_tsquery ('english', 'PostgreSQL can be extended by the user in many ways'); SELECT ts_rank_cd(to_tsvector('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) '), to_tsquery('english', 'paint&water')); SELECT ts_rank_cd(to_tsvector('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) '), to_tsquery('english', 'breath&motion&water')); SELECT ts_rank_cd(to_tsvector('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) '), to_tsquery('english', 'ocean')); SELECT ts_rank_cd(to_tsvector('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) '), to_tsquery('english', 'painted <-> Ship')); SELECT ts_rank_cd(strip(to_tsvector('both stripped')), to_tsquery('both & stripped')); SELECT ts_rank_cd(to_tsvector('unstripped') || strip(to_tsvector('stripped')), to_tsquery('unstripped & stripped')); --headline tests SELECT ts_headline('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) ', to_tsquery('english', 'paint&water')); SELECT ts_headline('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) ', to_tsquery('english', 'breath&motion&water')); SELECT ts_headline('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) ', to_tsquery('english', 'ocean')); SELECT ts_headline('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) ', phraseto_tsquery ('english', 'painted Ocean')); SELECT ts_headline('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) ', phraseto_tsquery ('english', 'idle as a painted Ship')); SELECT ts_headline('english', ' Sea view wow foo bar qq YES   ff-bg ', to_tsquery('english', 'sea&foo'), 'HighlightAll=true'); SELECT ts_headline('simple', '1 2 3 1 3'::text, '1 <-> 3', 'MaxWords=2, MinWords=1'); SELECT ts_headline('simple', '1 2 3 1 3'::text, '1 & 3', 'MaxWords=4, MinWords=1'); SELECT ts_headline('simple', '1 2 3 1 3'::text, '1 <-> 3', 'MaxWords=4, MinWords=1'); --Check if headline fragments work SELECT ts_headline('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) ', to_tsquery('english', 'ocean'), 'MaxFragments=1'); --Check if more than one fragments are displayed SELECT ts_headline('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) ', to_tsquery('english', 'Coleridge & stuck'), 'MaxFragments=2'); --Fragments when there all query words are not in the document SELECT ts_headline('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) ', to_tsquery('english', 'ocean & seahorse'), 'MaxFragments=1'); --FragmentDelimiter option SELECT ts_headline('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) ', to_tsquery('english', 'Coleridge & stuck'), 'MaxFragments=2,FragmentDelimiter=***'); --Rewrite sub system CREATE TABLE test_tsquery ( txtkeyword text, txtsample text ); \set ECHO none \set ECHO all ALTER TABLE test_tsquery ADD COLUMN keyword tsquery; UPDATE test_tsquery SET keyword = to_tsquery('english', txtkeyword); ALTER TABLE test_tsquery ADD COLUMN sample tsquery; UPDATE test_tsquery SET sample = to_tsquery('english', txtsample::text); SELECT COUNT(*) FROM test_tsquery WHERE keyword < 'new & york'; SELECT COUNT(*) FROM test_tsquery WHERE keyword <= 'new & york'; SELECT COUNT(*) FROM test_tsquery WHERE keyword = 'new & york'; SELECT COUNT(*) FROM test_tsquery WHERE keyword >= 'new & york'; SELECT COUNT(*) FROM test_tsquery WHERE keyword > 'new & york'; CREATE UNIQUE INDEX bt_tsq ON test_tsquery (keyword); SET enable_seqscan = OFF; SELECT COUNT(*) FROM test_tsquery WHERE keyword < 'new & york'; SELECT COUNT(*) FROM test_tsquery WHERE keyword <= 'new & york'; SELECT COUNT(*) FROM test_tsquery WHERE keyword = 'new & york'; SELECT COUNT(*) FROM test_tsquery WHERE keyword >= 'new & york'; SELECT COUNT(*) FROM test_tsquery WHERE keyword > 'new & york'; RESET enable_seqscan; SELECT ts_rewrite('foo & bar & qq & new & york', 'new & york'::tsquery, 'big & apple | nyc | new & york & city'); SELECT ts_rewrite(ts_rewrite('new & !york ', 'york', '!jersey'), 'jersey', 'mexico'); SELECT ts_rewrite('moscow', 'SELECT keyword, sample FROM test_tsquery'::text); SELECT ts_rewrite('moscow & hotel', 'SELECT keyword, sample FROM test_tsquery'::text); SELECT ts_rewrite('bar & new & qq & foo & york', 'SELECT keyword, sample FROM test_tsquery'::text); SELECT ts_rewrite('moscow', 'SELECT keyword, sample FROM test_tsquery'); SELECT ts_rewrite('moscow & hotel', 'SELECT keyword, sample FROM test_tsquery'); SELECT ts_rewrite('bar & new & qq & foo & york', 'SELECT keyword, sample FROM test_tsquery'); SELECT ts_rewrite('1 & (2 <-> 3)', 'SELECT keyword, sample FROM test_tsquery'::text); SELECT ts_rewrite('1 & (2 <2> 3)', 'SELECT keyword, sample FROM test_tsquery'::text); SELECT ts_rewrite('5 <-> (1 & (2 <-> 3))', 'SELECT keyword, sample FROM test_tsquery'::text); SELECT ts_rewrite('5 <-> (6 | 8)', 'SELECT keyword, sample FROM test_tsquery'::text); -- Check empty substitution SELECT ts_rewrite(to_tsquery('5 & (6 | 5)'), to_tsquery('5'), to_tsquery('')); SELECT ts_rewrite(to_tsquery('!5'), to_tsquery('5'), to_tsquery('')); SELECT keyword FROM test_tsquery WHERE keyword @> 'new'; SELECT keyword FROM test_tsquery WHERE keyword @> 'moscow'; SELECT keyword FROM test_tsquery WHERE keyword <@ 'new'; SELECT keyword FROM test_tsquery WHERE keyword <@ 'moscow'; SELECT ts_rewrite(query, 'SELECT keyword, sample FROM test_tsquery') FROM to_tsquery('english', 'moscow') AS query; SELECT ts_rewrite(query, 'SELECT keyword, sample FROM test_tsquery') FROM to_tsquery('english', 'moscow & hotel') AS query; SELECT ts_rewrite(query, 'SELECT keyword, sample FROM test_tsquery') FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query; SELECT ts_rewrite(query, 'SELECT keyword, sample FROM test_tsquery') FROM to_tsquery('english', 'moscow') AS query; SELECT ts_rewrite(query, 'SELECT keyword, sample FROM test_tsquery') FROM to_tsquery('english', 'moscow & hotel') AS query; SELECT ts_rewrite(query, 'SELECT keyword, sample FROM test_tsquery') FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query; CREATE INDEX qq ON test_tsquery USING gist (keyword tsquery_ops); SET enable_seqscan = OFF; SELECT keyword FROM test_tsquery WHERE keyword @> 'new'; SELECT keyword FROM test_tsquery WHERE keyword @> 'moscow'; SELECT keyword FROM test_tsquery WHERE keyword <@ 'new'; SELECT keyword FROM test_tsquery WHERE keyword <@ 'moscow'; SELECT ts_rewrite(query, 'SELECT keyword, sample FROM test_tsquery') FROM to_tsquery('english', 'moscow') AS query; SELECT ts_rewrite(query, 'SELECT keyword, sample FROM test_tsquery') FROM to_tsquery('english', 'moscow & hotel') AS query; SELECT ts_rewrite(query, 'SELECT keyword, sample FROM test_tsquery') FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query; SELECT ts_rewrite(query, 'SELECT keyword, sample FROM test_tsquery') FROM to_tsquery('english', 'moscow') AS query; SELECT ts_rewrite(query, 'SELECT keyword, sample FROM test_tsquery') FROM to_tsquery('english', 'moscow & hotel') AS query; SELECT ts_rewrite(query, 'SELECT keyword, sample FROM test_tsquery') FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query; SELECT ts_rewrite(tsquery_phrase ('foo', 'foo'), 'foo', 'bar | baz'); SELECT to_tsvector('foo bar') @@ ts_rewrite(tsquery_phrase ('foo', 'foo'), 'foo', 'bar | baz'); SELECT to_tsvector('bar baz') @@ ts_rewrite(tsquery_phrase ('foo', 'foo'), 'foo', 'bar | baz'); RESET enable_seqscan; --test GUC SET default_text_search_config = simple; SELECT to_tsvector('SKIES My booKs'); SELECT plainto_tsquery('SKIES My booKs'); SELECT to_tsquery('SKIES & My | booKs'); SET default_text_search_config = english; SELECT to_tsvector('SKIES My booKs'); SELECT plainto_tsquery('SKIES My booKs'); SELECT to_tsquery('SKIES & My | booKs'); --trigger CREATE TRIGGER tsvectorupdate BEFORE UPDATE OR INSERT ON test_tsvector FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger (a, 'pg_catalog.english', t); SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); INSERT INTO test_tsvector (t) VALUES ('345 qwerty'); SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); UPDATE test_tsvector SET t = NULL WHERE t = '345 qwerty'; SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); INSERT INTO test_tsvector (t) VALUES ('345 qwerty'); SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); -- test finding items in GIN's pending list CREATE temp TABLE pendtest ( ts tsvector ); CREATE INDEX pendtest_idx ON pendtest USING gin (ts); INSERT INTO pendtest VALUES (to_tsvector('Lore ipsam')); INSERT INTO pendtest VALUES (to_tsvector('Lore ipsum')); SELECT * FROM pendtest WHERE 'ipsu:*'::tsquery @@ ts; SELECT * FROM pendtest WHERE 'ipsa:*'::tsquery @@ ts; SELECT * FROM pendtest WHERE 'ips:*'::tsquery @@ ts; SELECT * FROM pendtest WHERE 'ipt:*'::tsquery @@ ts; SELECT * FROM pendtest WHERE 'ipi:*'::tsquery @@ ts; --check OP_PHRASE on index CREATE temp TABLE phrase_index_test ( fts tsvector ); INSERT INTO phrase_index_test VALUES ('A fat cat has just eaten a rat.'); INSERT INTO phrase_index_test VALUES (to_tsvector('english', 'A fat cat has just eaten a rat.')); CREATE INDEX phrase_index_test_idx ON phrase_index_test USING gin (fts); SET enable_seqscan = OFF; SELECT * FROM phrase_index_test WHERE fts @@ phraseto_tsquery ('english', 'fat cat'); SET enable_seqscan = ON; -- test websearch_to_tsquery function SELECT websearch_to_tsquery ('simple', 'I have a fat:*ABCD cat'); SELECT websearch_to_tsquery ('simple', 'orange:**AABBCCDD'); SELECT websearch_to_tsquery ('simple', 'fat:A!cat:B|rat:C<'); SELECT websearch_to_tsquery ('simple', 'fat:A : cat:B'); SELECT websearch_to_tsquery ('simple', 'fat*rat'); SELECT websearch_to_tsquery ('simple', 'fat-rat'); SELECT websearch_to_tsquery ('simple', 'fat_rat'); -- weights are completely ignored SELECT websearch_to_tsquery ('simple', 'abc : def'); SELECT websearch_to_tsquery ('simple', 'abc:def'); SELECT websearch_to_tsquery ('simple', 'a:::b'); SELECT websearch_to_tsquery ('simple', 'abc:d'); SELECT websearch_to_tsquery ('simple', ':'); -- these operators are ignored SELECT websearch_to_tsquery ('simple', 'abc & def'); SELECT websearch_to_tsquery ('simple', 'abc | def'); SELECT websearch_to_tsquery ('simple', 'abc <-> def'); SELECT websearch_to_tsquery ('simple', 'abc (pg or class)'); -- NOT is ignored in quotes SELECT websearch_to_tsquery ('english', 'My brand new smartphone'); SELECT websearch_to_tsquery ('english', 'My brand "new smartphone"'); SELECT websearch_to_tsquery ('english', 'My brand "new -smartphone"'); -- test OR operator SELECT websearch_to_tsquery ('simple', 'cat or rat'); SELECT websearch_to_tsquery ('simple', 'cat OR rat'); SELECT websearch_to_tsquery ('simple', 'cat "OR" rat'); SELECT websearch_to_tsquery ('simple', 'cat OR'); SELECT websearch_to_tsquery ('simple', 'OR rat'); SELECT websearch_to_tsquery ('simple', '"fat cat OR rat"'); SELECT websearch_to_tsquery ('simple', 'fat (cat OR rat'); SELECT websearch_to_tsquery ('simple', 'or OR or'); -- OR is an operator here ... SELECT websearch_to_tsquery ('simple', '"fat cat"or"fat rat"'); SELECT websearch_to_tsquery ('simple', 'fat or(rat'); SELECT websearch_to_tsquery ('simple', 'fat or)rat'); SELECT websearch_to_tsquery ('simple', 'fat or&rat'); SELECT websearch_to_tsquery ('simple', 'fat or|rat'); SELECT websearch_to_tsquery ('simple', 'fat or!rat'); SELECT websearch_to_tsquery ('simple', 'fat orrat'); SELECT websearch_to_tsquery ('simple', 'fat or '); -- ... but not here SELECT websearch_to_tsquery ('simple', 'abc orange'); SELECT websearch_to_tsquery ('simple', 'abc OR1234'); SELECT websearch_to_tsquery ('simple', 'abc or-abc'); SELECT websearch_to_tsquery ('simple', 'abc OR_abc'); -- test quotes SELECT websearch_to_tsquery ('english', '"pg_class pg'); SELECT websearch_to_tsquery ('english', 'pg_class pg"'); SELECT websearch_to_tsquery ('english', '"pg_class pg"'); SELECT websearch_to_tsquery ('english', 'abc "pg_class pg"'); SELECT websearch_to_tsquery ('english', '"pg_class pg" def'); SELECT websearch_to_tsquery ('english', 'abc "pg pg_class pg" def'); SELECT websearch_to_tsquery ('english', ' or "pg pg_class pg" or '); SELECT websearch_to_tsquery ('english', '""pg pg_class pg""'); SELECT websearch_to_tsquery ('english', 'abc """"" def'); SELECT websearch_to_tsquery ('english', 'cat -"fat rat"'); SELECT websearch_to_tsquery ('english', 'cat -"fat rat" cheese'); SELECT websearch_to_tsquery ('english', 'abc "def -"'); SELECT websearch_to_tsquery ('english', 'abc "def :"'); SELECT websearch_to_tsquery ('english', '"A fat cat" has just eaten a -rat.'); SELECT websearch_to_tsquery ('english', '"A fat cat" has just eaten OR !rat.'); SELECT websearch_to_tsquery ('english', '"A fat cat" has just (+eaten OR -rat)'); SELECT websearch_to_tsquery ('english', 'this is ----fine'); SELECT websearch_to_tsquery ('english', '(()) )))) this ||| is && -fine, "dear friend" OR good'); SELECT websearch_to_tsquery ('english', 'an old <-> cat " is fine &&& too'); SELECT websearch_to_tsquery ('english', '"A the" OR just on'); SELECT websearch_to_tsquery ('english', '"a fat cat" ate a rat'); SELECT to_tsvector('english', 'A fat cat ate a rat') @@ websearch_to_tsquery ('english', '"a fat cat" ate a rat'); SELECT to_tsvector('english', 'A fat grey cat ate a rat') @@ websearch_to_tsquery ('english', '"a fat cat" ate a rat'); -- cases handled by gettoken_tsvector() SELECT websearch_to_tsquery (''''); SELECT websearch_to_tsquery ('''abc''''def'''); SELECT websearch_to_tsquery ('\abc'); SELECT websearch_to_tsquery ('\'); pgFormatter-4.2/t/pg-test-files/expected/tsrf.sql000066400000000000000000000227021361326045100221030ustar00rootroot00000000000000-- -- tsrf - targetlist set returning function tests -- -- simple srf SELECT generate_series(1, 3); -- parallel iteration SELECT generate_series(1, 3), generate_series(3, 5); -- parallel iteration, different number of rows SELECT generate_series(1, 2), generate_series(1, 4); -- srf, with SRF argument SELECT generate_series(1, generate_series(1, 3)); -- but we've traditionally rejected the same in FROM SELECT * FROM generate_series(1, generate_series(1, 3)); -- srf, with two SRF arguments SELECT generate_series(generate_series(1, 3), generate_series(2, 4)); -- check proper nesting of SRFs in different expressions EXPLAIN ( VERBOSE, COSTS OFF ) SELECT generate_series(1, generate_series(1, 3)), generate_series(2, 4); SELECT generate_series(1, generate_series(1, 3)), generate_series(2, 4); CREATE TABLE few ( id int, dataa text, datab text ); INSERT INTO few VALUES (1, 'a', 'foo'), (2, 'a', 'bar'), (3, 'b', 'bar'); -- SRF with a provably-dummy relation EXPLAIN ( VERBOSE, COSTS OFF ) SELECT unnest(ARRAY[1, 2]) FROM few WHERE FALSE; SELECT unnest(ARRAY[1, 2]) FROM few WHERE FALSE; -- SRF shouldn't prevent upper query from recognizing lower as dummy EXPLAIN ( VERBOSE, COSTS OFF ) SELECT * FROM few f1, ( SELECT unnest(ARRAY[1, 2]) FROM few f2 WHERE FALSE OFFSET 0) ss; SELECT * FROM few f1, ( SELECT unnest(ARRAY[1, 2]) FROM few f2 WHERE FALSE OFFSET 0) ss; -- SRF output order of sorting is maintained, if SRF is not referenced SELECT few.id, generate_series(1, 3) g FROM few ORDER BY id DESC; -- but SRFs can be referenced in sort SELECT few.id, generate_series(1, 3) g FROM few ORDER BY id, g DESC; SELECT few.id, generate_series(1, 3) g FROM few ORDER BY id, generate_series(1, 3) DESC; -- it's weird to have ORDER BYs that increase the number of results SELECT few.id FROM few ORDER BY id, generate_series(1, 3) DESC; -- SRFs are computed after aggregation SET enable_hashagg TO 0; -- stable output order SELECT few.dataa, count(*), min(id), max(id), unnest('{1,1,3}'::int[]) FROM few WHERE few.id = 1 GROUP BY few.dataa; -- unless referenced in GROUP BY clause SELECT few.dataa, count(*), min(id), max(id), unnest('{1,1,3}'::int[]) FROM few WHERE few.id = 1 GROUP BY few.dataa, unnest('{1,1,3}'::int[]); SELECT few.dataa, count(*), min(id), max(id), unnest('{1,1,3}'::int[]) FROM few WHERE few.id = 1 GROUP BY few.dataa, 5; RESET enable_hashagg; -- check HAVING works when GROUP BY does [not] reference SRF output SELECT dataa, generate_series(1, 1), count(*) FROM few GROUP BY 1 HAVING count(*) > 1; SELECT dataa, generate_series(1, 1), count(*) FROM few GROUP BY 1, 2 HAVING count(*) > 1; -- it's weird to have GROUP BYs that increase the number of results SELECT few.dataa, count(*) FROM few WHERE dataa = 'a' GROUP BY few.dataa ORDER BY 2; SELECT few.dataa, count(*) FROM few WHERE dataa = 'a' GROUP BY few.dataa, unnest('{1,1,3}'::int[]) ORDER BY 2; -- SRFs are not allowed if they'd need to be conditionally executed SELECT q1, CASE WHEN q1 > 0 THEN generate_series(1, 3) ELSE 0 END FROM int8_tbl; SELECT q1, coalesce(generate_series(1, 3), 0) FROM int8_tbl; -- SRFs are not allowed in aggregate arguments SELECT min(generate_series(1, 3)) FROM few; -- ... unless they're within a sub-select SELECT sum((3 = ANY ( SELECT generate_series(1, 4)))::int); SELECT sum((3 = ANY ( SELECT lag(x) OVER (ORDER BY x) FROM generate_series(1, 4) x))::int); -- SRFs are not allowed in window function arguments, either SELECT min(generate_series(1, 3)) OVER () FROM few; -- SRFs are normally computed after window functions SELECT id, lag(id) OVER (), count(*) OVER (), generate_series(1, 3) FROM few; -- unless referencing SRFs SELECT SUM(count(*)) OVER (PARTITION BY generate_series(1, 3) ORDER BY generate_series(1, 3)), generate_series(1, 3) g FROM few GROUP BY g; -- sorting + grouping SELECT few.dataa, count(*), min(id), max(id), generate_series(1, 3) FROM few GROUP BY few.dataa ORDER BY 5, 1; -- grouping sets are a bit special, they produce NULLs in columns not actually NULL SET enable_hashagg = FALSE; SELECT dataa, datab b, generate_series(1, 2) g, count(*) FROM few GROUP BY CUBE (dataa, datab); SELECT dataa, datab b, generate_series(1, 2) g, count(*) FROM few GROUP BY CUBE (dataa, datab) ORDER BY dataa; SELECT dataa, datab b, generate_series(1, 2) g, count(*) FROM few GROUP BY CUBE (dataa, datab) ORDER BY g; SELECT dataa, datab b, generate_series(1, 2) g, count(*) FROM few GROUP BY CUBE (dataa, datab, g); SELECT dataa, datab b, generate_series(1, 2) g, count(*) FROM few GROUP BY CUBE (dataa, datab, g) ORDER BY dataa; SELECT dataa, datab b, generate_series(1, 2) g, count(*) FROM few GROUP BY CUBE (dataa, datab, g) ORDER BY g; RESET enable_hashagg; -- case with degenerate ORDER BY EXPLAIN ( VERBOSE, COSTS OFF ) SELECT 'foo' AS f, generate_series(1, 2) AS g FROM few ORDER BY 1; SELECT 'foo' AS f, generate_series(1, 2) AS g FROM few ORDER BY 1; -- data modification CREATE TABLE fewmore AS SELECT generate_series(1, 3) AS data; INSERT INTO fewmore VALUES (generate_series(4, 5)); SELECT * FROM fewmore; -- SRFs are not allowed in UPDATE (they once were, but it was nonsense) UPDATE fewmore SET data = generate_series(4, 9); -- SRFs are not allowed in RETURNING INSERT INTO fewmore VALUES (1) RETURNING generate_series(1, 3); -- nor standalone VALUES (but surely this is a bug?) VALUES (1, generate_series(1, 2)); -- We allow tSRFs that are not at top level SELECT int4mul(generate_series(1, 2), 10); SELECT generate_series(1, 3) IS DISTINCT FROM 2; -- but SRFs in function RTEs must be at top level (annoying restriction) SELECT * FROM int4mul(generate_series(1, 2), 10); -- DISTINCT ON is evaluated before tSRF evaluation if SRF is not -- referenced either in ORDER BY or in the DISTINCT ON list. The ORDER -- BY reference can be implicitly generated, if there's no other ORDER BY. -- implicit reference (via implicit ORDER) to all columns SELECT DISTINCT ON (a) a, b, generate_series(1, 3) g FROM ( VALUES (3, 2), (3, 1), (1, 1), (1, 4), (5, 3), (5, 1)) AS t (a, b); -- unreferenced in DISTINCT ON or ORDER BY SELECT DISTINCT ON (a) a, b, generate_series(1, 3) g FROM ( VALUES (3, 2), (3, 1), (1, 1), (1, 4), (5, 3), (5, 1)) AS t (a, b) ORDER BY a, b DESC; -- referenced in ORDER BY SELECT DISTINCT ON (a) a, b, generate_series(1, 3) g FROM ( VALUES (3, 2), (3, 1), (1, 1), (1, 4), (5, 3), (5, 1)) AS t (a, b) ORDER BY a, b DESC, g DESC; -- referenced in ORDER BY and DISTINCT ON SELECT DISTINCT ON (a, b, g) a, b, generate_series(1, 3) g FROM ( VALUES (3, 2), (3, 1), (1, 1), (1, 4), (5, 3), (5, 1)) AS t (a, b) ORDER BY a, b DESC, g DESC; -- only SRF mentioned in DISTINCT ON SELECT DISTINCT ON (g) a, b, generate_series(1, 3) g FROM ( VALUES (3, 2), (3, 1), (1, 1), (1, 4), (5, 3), (5, 1)) AS t (a, b); -- LIMIT / OFFSET is evaluated after SRF evaluation SELECT a, generate_series(1, 2) FROM ( VALUES (1), (2), (3)) r (a) LIMIT 2 OFFSET 2; -- SRFs are not allowed in LIMIT. SELECT 1 LIMIT generate_series(1, 3); -- tSRF in correlated subquery, referencing table outside SELECT ( SELECT generate_series(1, 3) LIMIT 1 OFFSET few.id) FROM few; -- tSRF in correlated subquery, referencing SRF outside SELECT ( SELECT generate_series(1, 3) LIMIT 1 OFFSET g.i) FROM generate_series(0, 3) g (i); -- Operators can return sets too CREATE OPERATOR |@|||@|||@| ( PROCEDURE = unnest, RIGHTARG = ANYARRAY ); SELECT | |@| | ARRAY[1, 2, 3]; -- Some fun cases involving duplicate SRF calls EXPLAIN ( VERBOSE, COSTS OFF ) SELECT generate_series(1, 3) AS x, generate_series(1, 3) + 1 AS xp1; SELECT generate_series(1, 3) AS x, generate_series(1, 3) + 1 AS xp1; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT generate_series(1, 3) + 1 ORDER BY generate_series(1, 3); SELECT generate_series(1, 3) + 1 ORDER BY generate_series(1, 3); -- Check that SRFs of same nesting level run in lockstep EXPLAIN ( VERBOSE, COSTS OFF ) SELECT generate_series(1, 3) AS x, generate_series(3, 6) + 1 AS y; SELECT generate_series(1, 3) AS x, generate_series(3, 6) + 1 AS y; -- Clean up DROP TABLE few; DROP TABLE fewmore; pgFormatter-4.2/t/pg-test-files/expected/tstypes.sql000066400000000000000000000323371361326045100226450ustar00rootroot00000000000000-- deal with numeric instability of ts_rank SET extra_float_digits = 0; --Base tsvector test SELECT '1'::tsvector; SELECT '1 '::tsvector; SELECT ' 1'::tsvector; SELECT ' 1 '::tsvector; SELECT '1 2'::tsvector; SELECT '''1 2'''::tsvector; SELECT E'''1 \\'' 2 '''::tsvector; SELECT E''' 1 '\' '2''3'::tsvector; SELECT E'''1 \\'' 2 '' 3 '::tsvector; SELECT E''' 1 '\' '2'' '' 3'' 4 '::tsvector; SELECT $$ '\\as' ab \c ab \c AB \c ab c$$::tsvector; SELECT tsvectorin(tsvectorout($$ '\\as' ab \c ab \c AB \c ab c$$::tsvector)); SELECT '''w'':4A,3B,2C,1D,5 a:8'; SELECT 'a:3A b:2a'::tsvector || 'ba:1234 a:1B'; --Base tsquery test SELECT '1'::tsquery; SELECT '1 '::tsquery; SELECT ' 1'::tsquery; SELECT ' 1 '::tsquery; SELECT '''1 2'''::tsquery; SELECT E'''1 \\'' 2 '''::tsquery; SELECT ' ! 1 '::tsquery; SELECT ' 1 | 2 '::tsquery; SELECT ' 1 | ! 2 '::tsquery; SELECT ' ! 1 | 2 '::tsquery; SELECT ' ! 1 | ! 2 '::tsquery; SELECT ' ! (! 1 | ! 2) '::tsquery; SELECT ' ! (! 1 | 2) '::tsquery; SELECT ' ! (1 | ! 2) '::tsquery; SELECT ' ! (1 | 2) '::tsquery; SELECT ' 1 & 2 '::tsquery; SELECT ' ! 1 & 2 '::tsquery; SELECT ' 1 & ! 2 '::tsquery; SELECT ' ! 1 & ! 2 '::tsquery; SELECT ' (1 & 2) '::tsquery; SELECT ' 1 & (2) '::tsquery; SELECT ' ! (1) & 2 '::tsquery; SELECT ' ! (1 & 2) '::tsquery; SELECT ' 1 | 2 & 3 '::tsquery; SELECT ' 1 | (2 & 3) '::tsquery; SELECT ' (1 | 2) & 3 '::tsquery; SELECT ' 1 | 2 & ! 3 '::tsquery; SELECT ' 1 | ! 2 & 3 '::tsquery; SELECT ' ! 1 | 2 & 3 '::tsquery; SELECT ' ! 1 | (2 & 3) '::tsquery; SELECT ' ! (1 | 2) & 3 '::tsquery; SELECT ' (! 1 | 2) & 3 '::tsquery; SELECT ' 1 | (2 | (4 | (5 | 6))) '::tsquery; SELECT ' 1 | 2 | 4 | 5 | 6 '::tsquery; SELECT ' 1 & (2 & (4 & (5 & 6))) '::tsquery; SELECT ' 1 & 2 & 4 & 5 & 6 '::tsquery; SELECT ' 1 & (2 & (4 & (5 | 6))) '::tsquery; SELECT ' 1 & (2 & (4 & (5 | ! 6))) '::tsquery; SELECT E' 1 & ('' 2 '' & ('' 4 '' & (| 5 | '' 6 '\' ' !|&'')))'::tsquery; SELECT $$ '\\as' $$::tsquery; SELECT 'a:* & nbb:*ac | doo:a* | goo'::tsquery; SELECT '!!b'::tsquery; SELECT '!!!b'::tsquery; SELECT '!(!b)'::tsquery; SELECT 'a & !!b'::tsquery; SELECT '!!a & b'::tsquery; SELECT '!!a & !!b'::tsquery; --comparisons SELECT 'a' < 'b & c'::tsquery AS "true"; SELECT 'a' > 'b & c'::tsquery AS "false"; SELECT 'a | f' < 'b & c'::tsquery AS "false"; SELECT 'a | ff' < 'b & c'::tsquery AS "false"; SELECT 'a | f | g' < 'b & c'::tsquery AS "false"; --concatenation SELECT numnode('new'::tsquery); SELECT numnode('new & york'::tsquery); SELECT numnode('new & york | qwery'::tsquery); SELECT 'foo & bar'::tsquery && 'asd'; SELECT 'foo & bar'::tsquery || 'asd & fg'; SELECT 'foo & bar'::tsquery || !! 'asd & fg'::tsquery; SELECT 'foo & bar'::tsquery && 'asd | fg'; SELECT 'a' <-> 'b & d'::tsquery; SELECT 'a & g' <-> 'b & d'::tsquery; SELECT 'a & g' <-> 'b | d'::tsquery; SELECT 'a & g' <-> 'b <-> d'::tsquery; SELECT tsquery_phrase ('a <3> g', 'b & d', 10); -- tsvector-tsquery operations SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca' AS "true"; SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:B' AS "true"; SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:A' AS "true"; SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:C' AS "false"; SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:CB' AS "true"; SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & c:*C' AS "false"; SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & c:*CB' AS "true"; SELECT 'a b:89 ca:23A,64b cb:80c d:34c'::tsvector @@ 'd:AC & c:*C' AS "true"; SELECT 'a b:89 ca:23A,64c cb:80b d:34c'::tsvector @@ 'd:AC & c:*C' AS "true"; SELECT 'a b:89 ca:23A,64c cb:80b d:34c'::tsvector @@ 'd:AC & c:*B' AS "true"; SELECT 'supernova'::tsvector @@ 'super'::tsquery AS "false"; SELECT 'supeanova supernova'::tsvector @@ 'super'::tsquery AS "false"; SELECT 'supeznova supernova'::tsvector @@ 'super'::tsquery AS "false"; SELECT 'supernova'::tsvector @@ 'super:*'::tsquery AS "true"; SELECT 'supeanova supernova'::tsvector @@ 'super:*'::tsquery AS "true"; SELECT 'supeznova supernova'::tsvector @@ 'super:*'::tsquery AS "true"; --phrase search SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <-> 2' AS "true"; SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <2> 2' AS "false"; SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <-> 3' AS "false"; SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <2> 3' AS "true"; SELECT to_tsvector('simple', '1 2 1 2') @@ '1 <3> 2' AS "true"; SELECT to_tsvector('simple', '1 2 11 3') @@ '1 <-> 3' AS "false"; SELECT to_tsvector('simple', '1 2 11 3') @@ '1:* <-> 3' AS "true"; SELECT to_tsvector('simple', '1 2 3 4') @@ '1 <-> 2 <-> 3' AS "true"; SELECT to_tsvector('simple', '1 2 3 4') @@ '(1 <-> 2) <-> 3' AS "true"; SELECT to_tsvector('simple', '1 2 3 4') @@ '1 <-> (2 <-> 3)' AS "true"; SELECT to_tsvector('simple', '1 2 3 4') @@ '1 <2> (2 <-> 3)' AS "false"; SELECT to_tsvector('simple', '1 2 1 2 3 4') @@ '(1 <-> 2) <-> 3' AS "true"; SELECT to_tsvector('simple', '1 2 1 2 3 4') @@ '1 <-> 2 <-> 3' AS "true"; -- without position data, phrase search does not match SELECT strip(to_tsvector('simple', '1 2 3 4')) @@ '1 <-> 2 <-> 3' AS "false"; SELECT to_tsvector('simple', 'q x q y') @@ 'q <-> (x & y)' AS "false"; SELECT to_tsvector('simple', 'q x') @@ 'q <-> (x | y <-> z)' AS "true"; SELECT to_tsvector('simple', 'q y') @@ 'q <-> (x | y <-> z)' AS "false"; SELECT to_tsvector('simple', 'q y z') @@ 'q <-> (x | y <-> z)' AS "true"; SELECT to_tsvector('simple', 'q y x') @@ 'q <-> (x | y <-> z)' AS "false"; SELECT to_tsvector('simple', 'q x y') @@ 'q <-> (x | y <-> z)' AS "true"; SELECT to_tsvector('simple', 'q x') @@ '(x | y <-> z) <-> q' AS "false"; SELECT to_tsvector('simple', 'x q') @@ '(x | y <-> z) <-> q' AS "true"; SELECT to_tsvector('simple', 'x y q') @@ '(x | y <-> z) <-> q' AS "false"; SELECT to_tsvector('simple', 'x y z') @@ '(x | y <-> z) <-> q' AS "false"; SELECT to_tsvector('simple', 'x y z q') @@ '(x | y <-> z) <-> q' AS "true"; SELECT to_tsvector('simple', 'y z q') @@ '(x | y <-> z) <-> q' AS "true"; SELECT to_tsvector('simple', 'y y q') @@ '(x | y <-> z) <-> q' AS "false"; SELECT to_tsvector('simple', 'y y q') @@ '(!x | y <-> z) <-> q' AS "true"; SELECT to_tsvector('simple', 'x y q') @@ '(!x | y <-> z) <-> q' AS "true"; SELECT to_tsvector('simple', 'y y q') @@ '(x | y <-> !z) <-> q' AS "true"; SELECT to_tsvector('simple', 'x q') @@ '(x | y <-> !z) <-> q' AS "true"; SELECT to_tsvector('simple', 'x q') @@ '(!x | y <-> z) <-> q' AS "false"; SELECT to_tsvector('simple', 'z q') @@ '(!x | y <-> z) <-> q' AS "true"; SELECT to_tsvector('simple', 'x y q y') @@ '!x <-> y' AS "true"; SELECT to_tsvector('simple', 'x y q y') @@ '!foo' AS "true"; SELECT to_tsvector('simple', '') @@ '!foo' AS "true"; --ranking SELECT ts_rank(' a:1 s:2C d g'::tsvector, 'a | s'); SELECT ts_rank(' a:1 sa:2C d g'::tsvector, 'a | s'); SELECT ts_rank(' a:1 sa:2C d g'::tsvector, 'a | s:*'); SELECT ts_rank(' a:1 sa:2C d g'::tsvector, 'a | sa:*'); SELECT ts_rank(' a:1 s:2B d g'::tsvector, 'a | s'); SELECT ts_rank(' a:1 s:2 d g'::tsvector, 'a | s'); SELECT ts_rank(' a:1 s:2C d g'::tsvector, 'a & s'); SELECT ts_rank(' a:1 s:2B d g'::tsvector, 'a & s'); SELECT ts_rank(' a:1 s:2 d g'::tsvector, 'a & s'); SELECT ts_rank_cd(' a:1 s:2C d g'::tsvector, 'a | s'); SELECT ts_rank_cd(' a:1 sa:2C d g'::tsvector, 'a | s'); SELECT ts_rank_cd(' a:1 sa:2C d g'::tsvector, 'a | s:*'); SELECT ts_rank_cd(' a:1 sa:2C d g'::tsvector, 'a | sa:*'); SELECT ts_rank_cd(' a:1 sa:3C sab:2c d g'::tsvector, 'a | sa:*'); SELECT ts_rank_cd(' a:1 s:2B d g'::tsvector, 'a | s'); SELECT ts_rank_cd(' a:1 s:2 d g'::tsvector, 'a | s'); SELECT ts_rank_cd(' a:1 s:2C d g'::tsvector, 'a & s'); SELECT ts_rank_cd(' a:1 s:2B d g'::tsvector, 'a & s'); SELECT ts_rank_cd(' a:1 s:2 d g'::tsvector, 'a & s'); SELECT ts_rank_cd(' a:1 s:2A d g'::tsvector, 'a <-> s'); SELECT ts_rank_cd(' a:1 s:2C d g'::tsvector, 'a <-> s'); SELECT ts_rank_cd(' a:1 s:2 d g'::tsvector, 'a <-> s'); SELECT ts_rank_cd(' a:1 s:2 d:2A g'::tsvector, 'a <-> s'); SELECT ts_rank_cd(' a:1 s:2,3A d:2A g'::tsvector, 'a <2> s:A'); SELECT ts_rank_cd(' a:1 b:2 s:3A d:2A g'::tsvector, 'a <2> s:A'); SELECT ts_rank_cd(' a:1 sa:2D sb:2A g'::tsvector, 'a <-> s:*'); SELECT ts_rank_cd(' a:1 sa:2A sb:2D g'::tsvector, 'a <-> s:*'); SELECT ts_rank_cd(' a:1 sa:2A sb:2D g'::tsvector, 'a <-> s:* <-> sa:A'); SELECT ts_rank_cd(' a:1 sa:2A sb:2D g'::tsvector, 'a <-> s:* <-> sa:B'); SELECT 'a:1 b:2'::tsvector @@ 'a <-> b'::tsquery AS "true"; SELECT 'a:1 b:2'::tsvector @@ 'a <0> b'::tsquery AS "false"; SELECT 'a:1 b:2'::tsvector @@ 'a <1> b'::tsquery AS "true"; SELECT 'a:1 b:2'::tsvector @@ 'a <2> b'::tsquery AS "false"; SELECT 'a:1 b:3'::tsvector @@ 'a <-> b'::tsquery AS "false"; SELECT 'a:1 b:3'::tsvector @@ 'a <0> b'::tsquery AS "false"; SELECT 'a:1 b:3'::tsvector @@ 'a <1> b'::tsquery AS "false"; SELECT 'a:1 b:3'::tsvector @@ 'a <2> b'::tsquery AS "true"; SELECT 'a:1 b:3'::tsvector @@ 'a <3> b'::tsquery AS "false"; SELECT 'a:1 b:3'::tsvector @@ 'a <0> a:*'::tsquery AS "true"; -- tsvector editing operations SELECT strip('w:12B w:13* w:12,5,6 a:1,3* a:3 w asd:1dc asd'::tsvector); SELECT strip('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector); SELECT strip('base hidden rebel spaceship strike'::tsvector); SELECT ts_delete (to_tsvector('english', 'Rebel spaceships, striking from a hidden base'), 'spaceship'); SELECT ts_delete ('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector, 'base'); SELECT ts_delete ('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector, 'bas'); SELECT ts_delete ('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector, 'bases'); SELECT ts_delete ('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector, 'spaceship'); SELECT ts_delete ('base hidden rebel spaceship strike'::tsvector, 'spaceship'); SELECT ts_delete ('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector, ARRAY['spaceship', 'rebel']); SELECT ts_delete ('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector, ARRAY['spaceships', 'rebel']); SELECT ts_delete ('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector, ARRAY['spaceshi', 'rebel']); SELECT ts_delete ('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector, ARRAY['spaceship', 'leya', 'rebel']); SELECT ts_delete ('base hidden rebel spaceship strike'::tsvector, ARRAY['spaceship', 'leya', 'rebel']); SELECT ts_delete ('base hidden rebel spaceship strike'::tsvector, ARRAY['spaceship', 'leya', 'rebel', 'rebel']); SELECT ts_delete ('base hidden rebel spaceship strike'::tsvector, ARRAY['spaceship', 'leya', 'rebel', NULL]); SELECT unnest('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector); SELECT unnest('base hidden rebel spaceship strike'::tsvector); SELECT * FROM unnest('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector); SELECT * FROM unnest('base hidden rebel spaceship strike'::tsvector); SELECT lexeme, positions[1] FROM unnest('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector); SELECT tsvector_to_array ('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector); SELECT tsvector_to_array ('base hidden rebel spaceship strike'::tsvector); SELECT array_to_tsvector (ARRAY['base', 'hidden', 'rebel', 'spaceship', 'strike']); SELECT array_to_tsvector (ARRAY['base', 'hidden', 'rebel', 'spaceship', NULL]); -- array_to_tsvector must sort and de-dup SELECT array_to_tsvector (ARRAY['foo', 'bar', 'baz', 'bar']); SELECT setweight('w:12B w:13* w:12,5,6 a:1,3* a:3 w asd:1dc asd zxc:81,567,222A'::tsvector, 'c'); SELECT setweight('a:1,3A asd:1C w:5,6,12B,13A zxc:81,222A,567'::tsvector, 'c'); SELECT setweight('a:1,3A asd:1C w:5,6,12B,13A zxc:81,222A,567'::tsvector, 'c', '{a}'); SELECT setweight('a:1,3A asd:1C w:5,6,12B,13A zxc:81,222A,567'::tsvector, 'c', '{a}'); SELECT setweight('a:1,3A asd:1C w:5,6,12B,13A zxc:81,222A,567'::tsvector, 'c', '{a,zxc}'); SELECT setweight('a asd w:5,6,12B,13A zxc'::tsvector, 'c', '{a,zxc}'); SELECT setweight('a asd w:5,6,12B,13A zxc'::tsvector, 'c', ARRAY['a', 'zxc', NULL]); SELECT ts_filter ('base:7A empir:17 evil:15 first:11 galact:16 hidden:6A rebel:1A spaceship:2A strike:3A victori:12 won:9'::tsvector, '{a}'); SELECT ts_filter ('base hidden rebel spaceship strike'::tsvector, '{a}'); SELECT ts_filter ('base hidden rebel spaceship strike'::tsvector, '{a,b,NULL}'); pgFormatter-4.2/t/pg-test-files/expected/txid.sql000066400000000000000000000062541361326045100221010ustar00rootroot00000000000000-- txid_snapshot data type and related functions -- i/o SELECT '12:13:'::txid_snapshot; SELECT '12:18:14,16'::txid_snapshot; SELECT '12:16:14,14'::txid_snapshot; -- errors SELECT '31:12:'::txid_snapshot; SELECT '0:1:'::txid_snapshot; SELECT '12:13:0'::txid_snapshot; SELECT '12:16:14,13'::txid_snapshot; CREATE temp TABLE snapshot_test ( nr integer, snap txid_snapshot ); INSERT INTO snapshot_test VALUES (1, '12:13:'); INSERT INTO snapshot_test VALUES (2, '12:20:13,15,18'); INSERT INTO snapshot_test VALUES (3, '100001:100009:100005,100007,100008'); INSERT INTO snapshot_test VALUES (4, '100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131'); SELECT snap FROM snapshot_test ORDER BY nr; SELECT txid_snapshot_xmin(snap), txid_snapshot_xmax(snap), txid_snapshot_xip(snap) FROM snapshot_test ORDER BY nr; SELECT id, txid_visible_in_snapshot(id, snap) FROM snapshot_test, generate_series(11, 21) id WHERE nr = 2; -- test bsearch SELECT id, txid_visible_in_snapshot(id, snap) FROM snapshot_test, generate_series(90, 160) id WHERE nr = 4; -- test current values also SELECT txid_current() >= txid_snapshot_xmin(txid_current_snapshot()); -- we can't assume current is always less than xmax, however SELECT txid_visible_in_snapshot(txid_current(), txid_current_snapshot()); -- test 64bitness SELECT txid_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013'; SELECT txid_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013'); SELECT txid_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013'); -- test 64bit overflow SELECT txid_snapshot '1:9223372036854775807:3'; SELECT txid_snapshot '1:9223372036854775808:3'; -- test txid_current_if_assigned BEGIN; SELECT txid_current_if_assigned () IS NULL; SELECT txid_current() \gset SELECT txid_current_if_assigned () IS NOT DISTINCT FROM BIGINT :'txid_current'; COMMIT; -- test xid status functions BEGIN; SELECT txid_current() AS COMMITTED \gset COMMIT; BEGIN; SELECT txid_current() AS rolledback \gset ROLLBACK; BEGIN; SELECT txid_current() AS inprogress \gset SELECT txid_status (:committed) AS COMMITTED; SELECT txid_status (:rolledback) AS rolledback; SELECT txid_status (:inprogress) AS inprogress; SELECT txid_status (1); -- BootstrapTransactionId is always committed SELECT txid_status (2); -- FrozenTransactionId is always committed SELECT txid_status (3); -- in regress testing FirstNormalTransactionId will always be behind oldestXmin COMMIT; BEGIN; CREATE FUNCTION test_future_xid_status (bigint) RETURNS void LANGUAGE plpgsql AS $$ BEGIN PERFORM txid_status ($1); RAISE EXCEPTION 'didn''t ERROR at xid in the future as expected'; EXCEPTION WHEN invalid_parameter_value THEN RAISE NOTICE 'Got expected error for xid in the future'; END; $$; SELECT test_future_xid_status (:inprogress + 10000); ROLLBACK; pgFormatter-4.2/t/pg-test-files/expected/type_sanity.sql000066400000000000000000000460171361326045100235020ustar00rootroot00000000000000-- -- TYPE_SANITY -- Sanity checks for common errors in making type-related system tables: -- pg_type, pg_class, pg_attribute, pg_range. -- -- None of the SELECTs here should ever find any matching entries, -- so the expected output is easy to maintain ;-). -- A test failure indicates someone messed up an entry in the system tables. -- -- NB: we assume the oidjoins test will have caught any dangling links, -- that is OID or REGPROC fields that are not zero and do not match some -- row in the linked-to table. However, if we want to enforce that a link -- field can't be 0, we have to check it here. -- **************** pg_type **************** -- Look for illegal values in pg_type fields. SELECT p1.oid, p1.typname FROM pg_type AS p1 WHERE p1.typnamespace = 0 OR (p1.typlen <= 0 AND p1.typlen != - 1 AND p1.typlen != - 2) OR (p1.typtype NOT IN ('b', 'c', 'd', 'e', 'p', 'r')) OR NOT p1.typisdefined OR (p1.typalign NOT IN ('c', 's', 'i', 'd')) OR (p1.typstorage NOT IN ('p', 'x', 'e', 'm')); -- Look for "pass by value" types that can't be passed by value. SELECT p1.oid, p1.typname FROM pg_type AS p1 WHERE p1.typbyval AND (p1.typlen != 1 OR p1.typalign != 'c') AND (p1.typlen != 2 OR p1.typalign != 's') AND (p1.typlen != 4 OR p1.typalign != 'i') AND (p1.typlen != 8 OR p1.typalign != 'd'); -- Look for "toastable" types that aren't varlena. SELECT p1.oid, p1.typname FROM pg_type AS p1 WHERE p1.typstorage != 'p' AND (p1.typbyval OR p1.typlen != - 1); -- Look for complex types that do not have a typrelid entry, -- or basic types that do. SELECT p1.oid, p1.typname FROM pg_type AS p1 WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR (p1.typtype != 'c' AND p1.typrelid != 0); -- Look for types that should have an array type according to their typtype, -- but don't. We exclude composites here because we have not bothered to -- make array types corresponding to the system catalogs' rowtypes. -- NOTE: as of v10, this check finds pg_node_tree, pg_ndistinct, smgr. SELECT p1.oid, p1.typname FROM pg_type AS p1 WHERE p1.typtype NOT IN ('c', 'd', 'p') AND p1.typname NOT LIKE E'\\_%' AND NOT EXISTS ( SELECT 1 FROM pg_type AS p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid AND p1.typarray = p2.oid); -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname AS BASETYPE, p2.typname AS arraytype, p2.typelem, p2.typlen FROM pg_type p1 LEFT JOIN pg_type p2 ON (p1.typarray = p2.oid) WHERE p1.typarray <> 0 AND (p2.oid IS NULL OR p2.typelem <> p1.oid OR p2.typlen <> - 1); -- Look for range types that do not have a pg_range entry SELECT p1.oid, p1.typname FROM pg_type AS p1 WHERE p1.typtype = 'r' AND NOT EXISTS ( SELECT 1 FROM pg_range r WHERE rngtypid = p1.oid); -- Look for range types whose typalign isn't sufficient SELECT p1.oid, p1.typname, p1.typalign, p2.typname, p2.typalign FROM pg_type AS p1 LEFT JOIN pg_range AS r ON rngtypid = p1.oid LEFT JOIN pg_type AS p2 ON rngsubtype = p2.oid WHERE p1.typtype = 'r' AND (p1.typalign != ( CASE WHEN p2.typalign = 'd' THEN 'd'::"char" ELSE 'i'::"char" END) OR p2.oid IS NULL); -- Text conversion routines must be provided. SELECT p1.oid, p1.typname FROM pg_type AS p1 WHERE (p1.typinput = 0 OR p1.typoutput = 0); -- Check for bogus typinput routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typinput = p2.oid AND NOT ((p2.pronargs = 1 AND p2.proargtypes[0] = 'cstring'::regtype) OR (p2.pronargs = 2 AND p2.proargtypes[0] = 'cstring'::regtype AND p2.proargtypes[1] = 'oid'::regtype) OR (p2.pronargs = 3 AND p2.proargtypes[0] = 'cstring'::regtype AND p2.proargtypes[1] = 'oid'::regtype AND p2.proargtypes[2] = 'int4'::regtype)); -- Check for type of the variadic array parameter's elements. -- provariadic should be ANYOID if the type of the last element is ANYOID, -- ANYELEMENTOID if the type of the last element is ANYARRAYOID, and otherwise -- the element type corresponding to the array type. SELECT oid::regprocedure, provariadic::regtype, proargtypes::regtype[] FROM pg_proc WHERE provariadic != 0 AND CASE proargtypes[array_length(proargtypes, 1) - 1] WHEN 2276 THEN 2276 -- any -> any WHEN 2277 THEN 2283 -- anyarray -> anyelement ELSE ( SELECT t.oid FROM pg_type t WHERE t.typarray = proargtypes[array_length(proargtypes, 1) - 1]) END != provariadic; -- Check that all and only those functions with a variadic type have -- a variadic argument. SELECT oid::regprocedure, proargmodes, provariadic FROM pg_proc WHERE (proargmodes IS NOT NULL AND 'v' = ANY (proargmodes)) IS DISTINCT FROM (provariadic != 0); -- As of 8.0, this check finds refcursor, which is borrowing -- other types' I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typinput = p2.oid AND p1.typtype IN ('b', 'p') AND NOT (p1.typelem != 0 AND p1.typlen < 0) AND NOT (p2.prorettype = p1.oid AND NOT p2.proretset) ORDER BY 1; -- Varlena array types will point to array_in -- Exception as of 8.1: int2vector and oidvector have their own I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typinput = p2.oid AND (p1.typelem != 0 AND p1.typlen < 0) AND NOT (p2.oid = 'array_in'::regproc) ORDER BY 1; -- typinput routines should not be volatile SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's'); -- Composites, domains, enums, ranges should all use the same input routines SELECT DISTINCT typtype, typinput FROM pg_type AS p1 WHERE p1.typtype NOT IN ('b', 'p') ORDER BY 1; -- Check for bogus typoutput routines -- As of 8.0, this check finds refcursor, which is borrowing -- other types' I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typoutput = p2.oid AND p1.typtype IN ('b', 'p') AND NOT (p2.pronargs = 1 AND (p2.proargtypes[0] = p1.oid OR (p2.oid = 'array_out'::regproc AND p1.typelem != 0 AND p1.typlen = - 1))) ORDER BY 1; SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typoutput = p2.oid AND NOT (p2.prorettype = 'cstring'::regtype AND NOT p2.proretset); -- typoutput routines should not be volatile SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's'); -- Composites, enums, ranges should all use the same output routines SELECT DISTINCT typtype, typoutput FROM pg_type AS p1 WHERE p1.typtype NOT IN ('b', 'd', 'p') ORDER BY 1; -- Domains should have same typoutput as their base types SELECT p1.oid, p1.typname, p2.oid, p2.typname FROM pg_type AS p1 LEFT JOIN pg_type AS p2 ON p1.typbasetype = p2.oid WHERE p1.typtype = 'd' AND p1.typoutput IS DISTINCT FROM p2.typoutput; -- Check for bogus typreceive routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typreceive = p2.oid AND NOT ((p2.pronargs = 1 AND p2.proargtypes[0] = 'internal'::regtype) OR (p2.pronargs = 2 AND p2.proargtypes[0] = 'internal'::regtype AND p2.proargtypes[1] = 'oid'::regtype) OR (p2.pronargs = 3 AND p2.proargtypes[0] = 'internal'::regtype AND p2.proargtypes[1] = 'oid'::regtype AND p2.proargtypes[2] = 'int4'::regtype)); -- As of 7.4, this check finds refcursor, which is borrowing -- other types' I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typreceive = p2.oid AND p1.typtype IN ('b', 'p') AND NOT (p1.typelem != 0 AND p1.typlen < 0) AND NOT (p2.prorettype = p1.oid AND NOT p2.proretset) ORDER BY 1; -- Varlena array types will point to array_recv -- Exception as of 8.1: int2vector and oidvector have their own I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typreceive = p2.oid AND (p1.typelem != 0 AND p1.typlen < 0) AND NOT (p2.oid = 'array_recv'::regproc) ORDER BY 1; -- Suspicious if typreceive doesn't take same number of args as typinput SELECT p1.oid, p1.typname, p2.oid, p2.proname, p3.oid, p3.proname FROM pg_type AS p1, pg_proc AS p2, pg_proc AS p3 WHERE p1.typinput = p2.oid AND p1.typreceive = p3.oid AND p2.pronargs != p3.pronargs; -- typreceive routines should not be volatile SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's'); -- Composites, domains, enums, ranges should all use the same receive routines SELECT DISTINCT typtype, typreceive FROM pg_type AS p1 WHERE p1.typtype NOT IN ('b', 'p') ORDER BY 1; -- Check for bogus typsend routines -- As of 7.4, this check finds refcursor, which is borrowing -- other types' I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typsend = p2.oid AND p1.typtype IN ('b', 'p') AND NOT (p2.pronargs = 1 AND (p2.proargtypes[0] = p1.oid OR (p2.oid = 'array_send'::regproc AND p1.typelem != 0 AND p1.typlen = - 1))) ORDER BY 1; SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typsend = p2.oid AND NOT (p2.prorettype = 'bytea'::regtype AND NOT p2.proretset); -- typsend routines should not be volatile SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's'); -- Composites, enums, ranges should all use the same send routines SELECT DISTINCT typtype, typsend FROM pg_type AS p1 WHERE p1.typtype NOT IN ('b', 'd', 'p') ORDER BY 1; -- Domains should have same typsend as their base types SELECT p1.oid, p1.typname, p2.oid, p2.typname FROM pg_type AS p1 LEFT JOIN pg_type AS p2 ON p1.typbasetype = p2.oid WHERE p1.typtype = 'd' AND p1.typsend IS DISTINCT FROM p2.typsend; -- Check for bogus typmodin routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typmodin = p2.oid AND NOT (p2.pronargs = 1 AND p2.proargtypes[0] = 'cstring[]'::regtype AND p2.prorettype = 'int4'::regtype AND NOT p2.proretset); -- typmodin routines should not be volatile SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typmodin = p2.oid AND p2.provolatile NOT IN ('i', 's'); -- Check for bogus typmodout routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typmodout = p2.oid AND NOT (p2.pronargs = 1 AND p2.proargtypes[0] = 'int4'::regtype AND p2.prorettype = 'cstring'::regtype AND NOT p2.proretset); -- typmodout routines should not be volatile SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typmodout = p2.oid AND p2.provolatile NOT IN ('i', 's'); -- Array types should have same typmodin/out as their element types SELECT p1.oid, p1.typname, p2.oid, p2.typname FROM pg_type AS p1, pg_type AS p2 WHERE p1.typelem = p2.oid AND NOT (p1.typmodin = p2.typmodin AND p1.typmodout = p2.typmodout); -- Array types should have same typdelim as their element types SELECT p1.oid, p1.typname, p2.oid, p2.typname FROM pg_type AS p1, pg_type AS p2 WHERE p1.typarray = p2.oid AND NOT (p1.typdelim = p2.typdelim); -- Look for array types whose typalign isn't sufficient SELECT p1.oid, p1.typname, p1.typalign, p2.typname, p2.typalign FROM pg_type AS p1, pg_type AS p2 WHERE p1.typarray = p2.oid AND p2.typalign != ( CASE WHEN p1.typalign = 'd' THEN 'd'::"char" ELSE 'i'::"char" END); -- Check for bogus typanalyze routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typanalyze = p2.oid AND NOT (p2.pronargs = 1 AND p2.proargtypes[0] = 'internal'::regtype AND p2.prorettype = 'bool'::regtype AND NOT p2.proretset); -- there does not seem to be a reason to care about volatility of typanalyze -- domains inherit their base type's typanalyze SELECT d.oid, d.typname, d.typanalyze, t.oid, t.typname, t.typanalyze FROM pg_type d JOIN pg_type t ON d.typbasetype = t.oid WHERE d.typanalyze != t.typanalyze; -- range_typanalyze should be used for all and only range types -- (but exclude domains, which we checked above) SELECT t.oid, t.typname, t.typanalyze FROM pg_type t LEFT JOIN pg_range r ON t.oid = r.rngtypid WHERE t.typbasetype = 0 AND (t.typanalyze = 'range_typanalyze'::regproc) != (r.rngtypid IS NOT NULL); -- array_typanalyze should be used for all and only array types -- (but exclude domains, which we checked above) -- As of 9.2 this finds int2vector and oidvector, which are weird anyway SELECT t.oid, t.typname, t.typanalyze FROM pg_type t WHERE t.typbasetype = 0 AND (t.typanalyze = 'array_typanalyze'::regproc) != (typelem != 0 AND typlen < 0) ORDER BY 1; -- **************** pg_class **************** -- Look for illegal values in pg_class fields SELECT p1.oid, p1.relname FROM pg_class AS p1 WHERE relkind NOT IN ('r', 'i', 'S', 't', 'v', 'm', 'c', 'f', 'p') OR relpersistence NOT IN ('p', 'u', 't') OR relreplident NOT IN ('d', 'n', 'f', 'i'); -- All tables and indexes should have an access method. SELECT p1.oid, p1.relname FROM pg_class AS p1 WHERE p1.relkind NOT IN ('S', 'v', 'f', 'c') AND p1.relam = 0; -- Conversely, sequences, views, types shouldn't have them SELECT p1.oid, p1.relname FROM pg_class AS p1 WHERE p1.relkind IN ('S', 'v', 'f', 'c') AND p1.relam != 0; -- Indexes should have AMs of type 'i' SELECT pc.oid, pc.relname, pa.amname, pa.amtype FROM pg_class AS pc JOIN pg_am AS pa ON (pc.relam = pa.oid) WHERE pc.relkind IN ('i') AND pa.amtype != 'i'; -- Tables, matviews etc should have AMs of type 't' SELECT pc.oid, pc.relname, pa.amname, pa.amtype FROM pg_class AS pc JOIN pg_am AS pa ON (pc.relam = pa.oid) WHERE pc.relkind IN ('r', 't', 'm') AND pa.amtype != 't'; -- **************** pg_attribute **************** -- Look for illegal values in pg_attribute fields SELECT p1.attrelid, p1.attname FROM pg_attribute AS p1 WHERE p1.attrelid = 0 OR p1.atttypid = 0 OR p1.attnum = 0 OR p1.attcacheoff != - 1 OR p1.attinhcount < 0 OR (p1.attinhcount = 0 AND NOT p1.attislocal); -- Cross-check attnum against parent relation SELECT p1.attrelid, p1.attname, p2.oid, p2.relname FROM pg_attribute AS p1, pg_class AS p2 WHERE p1.attrelid = p2.oid AND p1.attnum > p2.relnatts; -- Detect missing pg_attribute entries: should have as many non-system -- attributes as parent relation expects SELECT p1.oid, p1.relname FROM pg_class AS p1 WHERE p1.relnatts != ( SELECT count(*) FROM pg_attribute AS p2 WHERE p2.attrelid = p1.oid AND p2.attnum > 0); -- Cross-check against pg_type entry -- NOTE: we allow attstorage to be 'plain' even when typstorage is not; -- this is mainly for toast tables. SELECT p1.attrelid, p1.attname, p2.oid, p2.typname FROM pg_attribute AS p1, pg_type AS p2 WHERE p1.atttypid = p2.oid AND (p1.attlen != p2.typlen OR p1.attalign != p2.typalign OR p1.attbyval != p2.typbyval OR (p1.attstorage != p2.typstorage AND p1.attstorage != 'p')); -- **************** pg_range **************** -- Look for illegal values in pg_range fields. SELECT p1.rngtypid, p1.rngsubtype FROM pg_range AS p1 WHERE p1.rngtypid = 0 OR p1.rngsubtype = 0 OR p1.rngsubopc = 0; -- rngcollation should be specified iff subtype is collatable SELECT p1.rngtypid, p1.rngsubtype, p1.rngcollation, t.typcollation FROM pg_range p1 JOIN pg_type t ON t.oid = p1.rngsubtype WHERE (rngcollation = 0) != (typcollation = 0); -- opclass had better be a btree opclass accepting the subtype. -- We must allow anyarray matches, cf opr_sanity's binary_coercible() SELECT p1.rngtypid, p1.rngsubtype, o.opcmethod, o.opcname FROM pg_range p1 JOIN pg_opclass o ON o.oid = p1.rngsubopc WHERE o.opcmethod != 403 OR ((o.opcintype != p1.rngsubtype) AND NOT (o.opcintype = 'pg_catalog.anyarray'::regtype AND EXISTS ( SELECT 1 FROM pg_catalog.pg_type WHERE oid = p1.rngsubtype AND typelem != 0 AND typlen = - 1))); -- canonical function, if any, had better match the range type SELECT p1.rngtypid, p1.rngsubtype, p.proname FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngcanonical WHERE pronargs != 1 OR proargtypes[0] != rngtypid OR prorettype != rngtypid; -- subdiff function, if any, had better match the subtype SELECT p1.rngtypid, p1.rngsubtype, p.proname FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff WHERE pronargs != 2 OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype OR prorettype != 'pg_catalog.float8'::regtype; pgFormatter-4.2/t/pg-test-files/expected/typed_table.sql000066400000000000000000000035771361326045100234320ustar00rootroot00000000000000CREATE TABLE ttable1 OF nothing; CREATE TYPE person_type AS ( id int, name text ); CREATE TABLE persons OF person_type; CREATE TABLE IF NOT EXISTS persons OF person_type; SELECT * FROM persons; \d persons CREATE FUNCTION get_all_persons () RETURNS SETOF person_type LANGUAGE SQL AS $$ SELECT * FROM persons; $$; SELECT * FROM get_all_persons (); -- certain ALTER TABLE operations on typed tables are not allowed ALTER TABLE persons ADD COLUMN comment text; ALTER TABLE persons DROP COLUMN name; ALTER TABLE persons RENAME COLUMN id TO num; ALTER TABLE persons ALTER COLUMN name TYPE varchar; CREATE TABLE stuff ( id int ); ALTER TABLE persons INHERIT stuff; CREATE TABLE personsx OF person_type ( myname WITH OPTIONS NOT NULL ); -- error CREATE TABLE persons2 OF person_type ( id WITH OPTIONS PRIMARY KEY, UNIQUE ( name) ); \d persons2 CREATE TABLE persons3 OF person_type ( PRIMARY KEY (id), name WITH OPTIONS DEFAULT '' ); \d persons3 CREATE TABLE persons4 OF person_type ( name WITH OPTIONS NOT NULL, name WITH OPTIONS DEFAULT '' -- error, specified more than once ); DROP TYPE person_type RESTRICT; DROP TYPE person_type CASCADE; CREATE TABLE persons5 OF stuff; -- only CREATE TYPE AS types may be used DROP TABLE stuff; -- implicit casting CREATE TYPE person_type AS ( id int, name text ); CREATE TABLE persons OF person_type; INSERT INTO persons VALUES (1, 'test'); CREATE FUNCTION namelen (person_type) RETURNS int LANGUAGE SQL AS $$ SELECT length($1.name) $$; SELECT id, namelen (persons) FROM persons; CREATE TABLE persons2 OF person_type ( id WITH OPTIONS PRIMARY KEY, UNIQUE ( name) ); \d persons2 CREATE TABLE persons3 OF person_type ( PRIMARY KEY (id), name NOT NULL DEFAULT '' ); \d persons3 pgFormatter-4.2/t/pg-test-files/expected/union.sql000066400000000000000000000345561361326045100222670ustar00rootroot00000000000000-- -- UNION (also INTERSECT, EXCEPT) -- -- Simple UNION constructs SELECT 1 AS two UNION SELECT 2 ORDER BY 1; SELECT 1 AS one UNION SELECT 1 ORDER BY 1; SELECT 1 AS two UNION ALL SELECT 2; SELECT 1 AS two UNION ALL SELECT 1; SELECT 1 AS three UNION SELECT 2 UNION SELECT 3 ORDER BY 1; SELECT 1 AS two UNION SELECT 2 UNION SELECT 2 ORDER BY 1; SELECT 1 AS three UNION SELECT 2 UNION ALL SELECT 2 ORDER BY 1; SELECT 1.1 AS two UNION SELECT 2.2 ORDER BY 1; -- Mixed types SELECT 1.1 AS two UNION SELECT 2 ORDER BY 1; SELECT 1 AS two UNION SELECT 2.2 ORDER BY 1; SELECT 1 AS one UNION SELECT 1.0::float8 ORDER BY 1; SELECT 1.1 AS two UNION ALL SELECT 2 ORDER BY 1; SELECT 1.0::float8 AS two UNION ALL SELECT 1 ORDER BY 1; SELECT 1.1 AS three UNION SELECT 2 UNION SELECT 3 ORDER BY 1; SELECT 1.1::float8 AS two UNION SELECT 2 UNION SELECT 2.0::float8 ORDER BY 1; SELECT 1.1 AS three UNION SELECT 2 UNION ALL SELECT 2 ORDER BY 1; SELECT 1.1 AS two UNION ( SELECT 2 UNION ALL SELECT 2) ORDER BY 1; -- -- Try testing from tables... -- SELECT f1 AS five FROM FLOAT8_TBL UNION SELECT f1 FROM FLOAT8_TBL ORDER BY 1; SELECT f1 AS ten FROM FLOAT8_TBL UNION ALL SELECT f1 FROM FLOAT8_TBL; SELECT f1 AS nine FROM FLOAT8_TBL UNION SELECT f1 FROM INT4_TBL ORDER BY 1; SELECT f1 AS ten FROM FLOAT8_TBL UNION ALL SELECT f1 FROM INT4_TBL; SELECT f1 AS five FROM FLOAT8_TBL WHERE f1 BETWEEN - 1e6 AND 1e6 UNION SELECT f1 FROM INT4_TBL WHERE f1 BETWEEN 0 AND 1000000 ORDER BY 1; SELECT CAST(f1 AS char(4)) AS three FROM VARCHAR_TBL UNION SELECT f1 FROM CHAR_TBL ORDER BY 1; SELECT f1 AS three FROM VARCHAR_TBL UNION SELECT CAST(f1 AS varchar) FROM CHAR_TBL ORDER BY 1; SELECT f1 AS eight FROM VARCHAR_TBL UNION ALL SELECT f1 FROM CHAR_TBL; SELECT f1 AS five FROM TEXT_TBL UNION SELECT f1 FROM VARCHAR_TBL UNION SELECT TRIM(TRAILING FROM f1) FROM CHAR_TBL ORDER BY 1; -- -- INTERSECT and EXCEPT -- SELECT q2 FROM int8_tbl INTERSECT SELECT q1 FROM int8_tbl ORDER BY 1; SELECT q2 FROM int8_tbl INTERSECT ALL SELECT q1 FROM int8_tbl ORDER BY 1; SELECT q2 FROM int8_tbl EXCEPT SELECT q1 FROM int8_tbl ORDER BY 1; SELECT q2 FROM int8_tbl EXCEPT ALL SELECT q1 FROM int8_tbl ORDER BY 1; SELECT q2 FROM int8_tbl EXCEPT ALL SELECT DISTINCT q1 FROM int8_tbl ORDER BY 1; SELECT q1 FROM int8_tbl EXCEPT SELECT q2 FROM int8_tbl ORDER BY 1; SELECT q1 FROM int8_tbl EXCEPT ALL SELECT q2 FROM int8_tbl ORDER BY 1; SELECT q1 FROM int8_tbl EXCEPT ALL SELECT DISTINCT q2 FROM int8_tbl ORDER BY 1; SELECT q1 FROM int8_tbl EXCEPT ALL SELECT q1 FROM int8_tbl FOR NO KEY UPDATE; -- nested cases ( SELECT 1, 2, 3 UNION SELECT 4, 5, 6) INTERSECT SELECT 4, 5, 6; ( SELECT 1, 2, 3 UNION SELECT 4, 5, 6 ORDER BY 1, 2) INTERSECT SELECT 4, 5, 6; ( SELECT 1, 2, 3 UNION SELECT 4, 5, 6) EXCEPT SELECT 4, 5, 6; ( SELECT 1, 2, 3 UNION SELECT 4, 5, 6 ORDER BY 1, 2) EXCEPT SELECT 4, 5, 6; -- exercise both hashed and sorted implementations of INTERSECT/EXCEPT SET enable_hashagg TO ON; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM ( SELECT unique1 FROM tenk1 INTERSECT SELECT fivethous FROM tenk1) ss; SELECT count(*) FROM ( SELECT unique1 FROM tenk1 INTERSECT SELECT fivethous FROM tenk1) ss; EXPLAIN ( COSTS OFF ) SELECT unique1 FROM tenk1 EXCEPT SELECT unique2 FROM tenk1 WHERE unique2 != 10; SELECT unique1 FROM tenk1 EXCEPT SELECT unique2 FROM tenk1 WHERE unique2 != 10; SET enable_hashagg TO OFF; EXPLAIN ( COSTS OFF ) SELECT count(*) FROM ( SELECT unique1 FROM tenk1 INTERSECT SELECT fivethous FROM tenk1) ss; SELECT count(*) FROM ( SELECT unique1 FROM tenk1 INTERSECT SELECT fivethous FROM tenk1) ss; EXPLAIN ( COSTS OFF ) SELECT unique1 FROM tenk1 EXCEPT SELECT unique2 FROM tenk1 WHERE unique2 != 10; SELECT unique1 FROM tenk1 EXCEPT SELECT unique2 FROM tenk1 WHERE unique2 != 10; RESET enable_hashagg; -- -- Mixed types -- SELECT f1 FROM float8_tbl INTERSECT SELECT f1 FROM int4_tbl ORDER BY 1; SELECT f1 FROM float8_tbl EXCEPT SELECT f1 FROM int4_tbl ORDER BY 1; -- -- Operator precedence and (((((extra))))) parentheses -- SELECT q1 FROM int8_tbl INTERSECT SELECT q2 FROM int8_tbl UNION ALL SELECT q2 FROM int8_tbl ORDER BY 1; SELECT q1 FROM int8_tbl INTERSECT ((( SELECT q2 FROM int8_tbl UNION ALL SELECT q2 FROM int8_tbl))) ORDER BY 1; ((( SELECT q1 FROM int8_tbl INTERSECT SELECT q2 FROM int8_tbl ORDER BY 1))) UNION ALL SELECT q2 FROM int8_tbl; SELECT q1 FROM int8_tbl UNION ALL SELECT q2 FROM int8_tbl EXCEPT SELECT q1 FROM int8_tbl ORDER BY 1; SELECT q1 FROM int8_tbl UNION ALL ((( SELECT q2 FROM int8_tbl EXCEPT SELECT q1 FROM int8_tbl ORDER BY 1))); ((( SELECT q1 FROM int8_tbl UNION ALL SELECT q2 FROM int8_tbl))) EXCEPT SELECT q1 FROM int8_tbl ORDER BY 1; -- -- Subqueries with ORDER BY & LIMIT clauses -- -- In this syntax, ORDER BY/LIMIT apply to the result of the EXCEPT SELECT q1, q2 FROM int8_tbl EXCEPT SELECT q2, q1 FROM int8_tbl ORDER BY q2, q1; -- This should fail, because q2 isn't a name of an EXCEPT output column SELECT q1 FROM int8_tbl EXCEPT SELECT q2 FROM int8_tbl ORDER BY q2 LIMIT 1; -- But this should work: SELECT q1 FROM int8_tbl EXCEPT ((( SELECT q2 FROM int8_tbl ORDER BY q2 LIMIT 1))) ORDER BY 1; -- -- New syntaxes (7.1) permit new tests -- ((((( SELECT * FROM int8_tbl))))); -- -- Check behavior with empty select list (allowed since 9.4) -- SELECT UNION SELECT ; SELECT INTERSECT SELECT ; SELECT EXCEPT SELECT ; -- check hashed implementation SET enable_hashagg = TRUE; SET enable_sort = FALSE; EXPLAIN ( COSTS OFF ) SELECT FROM generate_series(1, 5) UNION SELECT FROM generate_series(1, 3); EXPLAIN ( COSTS OFF ) SELECT FROM generate_series(1, 5) INTERSECT SELECT FROM generate_series(1, 3); SELECT FROM generate_series(1, 5) UNION SELECT FROM generate_series(1, 3); SELECT FROM generate_series(1, 5) UNION ALL SELECT FROM generate_series(1, 3); SELECT FROM generate_series(1, 5) INTERSECT SELECT FROM generate_series(1, 3); SELECT FROM generate_series(1, 5) INTERSECT ALL SELECT FROM generate_series(1, 3); SELECT FROM generate_series(1, 5) EXCEPT SELECT FROM generate_series(1, 3); SELECT FROM generate_series(1, 5) EXCEPT ALL SELECT FROM generate_series(1, 3); -- check sorted implementation SET enable_hashagg = FALSE; SET enable_sort = TRUE; EXPLAIN ( COSTS OFF ) SELECT FROM generate_series(1, 5) UNION SELECT FROM generate_series(1, 3); EXPLAIN ( COSTS OFF ) SELECT FROM generate_series(1, 5) INTERSECT SELECT FROM generate_series(1, 3); SELECT FROM generate_series(1, 5) UNION SELECT FROM generate_series(1, 3); SELECT FROM generate_series(1, 5) UNION ALL SELECT FROM generate_series(1, 3); SELECT FROM generate_series(1, 5) INTERSECT SELECT FROM generate_series(1, 3); SELECT FROM generate_series(1, 5) INTERSECT ALL SELECT FROM generate_series(1, 3); SELECT FROM generate_series(1, 5) EXCEPT SELECT FROM generate_series(1, 3); SELECT FROM generate_series(1, 5) EXCEPT ALL SELECT FROM generate_series(1, 3); RESET enable_hashagg; RESET enable_sort; -- -- Check handling of a case with unknown constants. We don't guarantee -- an undecorated constant will work in all cases, but historically this -- usage has worked, so test we don't break it. -- SELECT a.f1 FROM ( SELECT 'test' AS f1 FROM varchar_tbl) a UNION SELECT b.f1 FROM ( SELECT f1 FROM varchar_tbl) b ORDER BY 1; -- This should fail, but it should produce an error cursor SELECT '3.4'::numeric UNION SELECT 'foo'; -- -- Test that expression-index constraints can be pushed down through -- UNION or UNION ALL -- CREATE TEMP TABLE t1 ( a text, b text ); CREATE INDEX t1_ab_idx ON t1 ((a || b)); CREATE TEMP TABLE t2 ( ab text PRIMARY KEY ); INSERT INTO t1 VALUES ('a', 'b'), ('x', 'y'); INSERT INTO t2 VALUES ('ab'), ('xy'); SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = OFF; EXPLAIN ( COSTS OFF ) SELECT * FROM ( SELECT a || b AS ab FROM t1 UNION ALL SELECT * FROM t2) t WHERE ab = 'ab'; EXPLAIN ( COSTS OFF ) SELECT * FROM ( SELECT a || b AS ab FROM t1 UNION SELECT * FROM t2) t WHERE ab = 'ab'; -- -- Test that ORDER BY for UNION ALL can be pushed down to inheritance -- children. -- CREATE TEMP TABLE t1c ( b text, a text ); ALTER TABLE t1c INHERIT t1; CREATE TEMP TABLE t2c ( PRIMARY KEY (ab) ) INHERITS ( t2 ); INSERT INTO t1c VALUES ('v', 'w'), ('c', 'd'), ('m', 'n'), ('e', 'f'); INSERT INTO t2c VALUES ('vw'), ('cd'), ('mn'), ('ef'); CREATE INDEX t1c_ab_idx ON t1c ((a || b)); SET enable_seqscan = ON; SET enable_indexonlyscan = OFF; EXPLAIN ( COSTS OFF ) SELECT * FROM ( SELECT a || b AS ab FROM t1 UNION ALL SELECT ab FROM t2) t ORDER BY 1 LIMIT 8; SELECT * FROM ( SELECT a || b AS ab FROM t1 UNION ALL SELECT ab FROM t2) t ORDER BY 1 LIMIT 8; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; -- This simpler variant of the above test has been observed to fail differently CREATE TABLE events ( event_id int PRIMARY KEY ); CREATE TABLE other_events ( event_id int PRIMARY KEY ); CREATE TABLE events_child () INHERITS ( events ); EXPLAIN ( COSTS OFF ) SELECT event_id FROM ( SELECT event_id FROM events UNION ALL SELECT event_id FROM other_events) ss ORDER BY event_id; DROP TABLE events_child, events, other_events; RESET enable_indexonlyscan; -- Test constraint exclusion of UNION ALL subqueries EXPLAIN ( COSTS OFF ) SELECT * FROM ( SELECT 1 AS t, * FROM tenk1 a UNION ALL SELECT 2 AS t, * FROM tenk1 b) c WHERE t = 2; -- Test that we push quals into UNION sub-selects only when it's safe EXPLAIN ( COSTS OFF ) SELECT * FROM ( SELECT 1 AS t, 2 AS x UNION SELECT 2 AS t, 4 AS x) ss WHERE x < 4 ORDER BY x; SELECT * FROM ( SELECT 1 AS t, 2 AS x UNION SELECT 2 AS t, 4 AS x) ss WHERE x < 4 ORDER BY x; EXPLAIN ( COSTS OFF ) SELECT * FROM ( SELECT 1 AS t, generate_series(1, 10) AS x UNION SELECT 2 AS t, 4 AS x) ss WHERE x < 4 ORDER BY x; SELECT * FROM ( SELECT 1 AS t, generate_series(1, 10) AS x UNION SELECT 2 AS t, 4 AS x) ss WHERE x < 4 ORDER BY x; EXPLAIN ( COSTS OFF ) SELECT * FROM ( SELECT 1 AS t, (random() * 3)::int AS x UNION SELECT 2 AS t, 4 AS x) ss WHERE x > 3 ORDER BY x; SELECT * FROM ( SELECT 1 AS t, (random() * 3)::int AS x UNION SELECT 2 AS t, 4 AS x) ss WHERE x > 3 ORDER BY x; -- Test proper handling of parameterized appendrel paths when the -- potential join qual is expensive CREATE FUNCTION expensivefunc (int) RETURNS int LANGUAGE plpgsql IMMUTABLE STRICT COST 10000 AS $$ BEGIN RETURN $1; END $$; CREATE temp TABLE t3 AS SELECT generate_series(- 1000, 1000) AS x; CREATE INDEX t3i ON t3 (expensivefunc (x)); ANALYZE t3; EXPLAIN ( COSTS OFF ) SELECT * FROM ( SELECT * FROM t3 a UNION ALL SELECT * FROM t3 b) ss JOIN int4_tbl ON f1 = expensivefunc (x); SELECT * FROM ( SELECT * FROM t3 a UNION ALL SELECT * FROM t3 b) ss JOIN int4_tbl ON f1 = expensivefunc (x); DROP TABLE t3; DROP FUNCTION expensivefunc (int); -- Test handling of appendrel quals that const-simplify into an AND EXPLAIN ( COSTS OFF ) SELECT * FROM ( SELECT *, 0 AS x FROM int8_tbl a UNION ALL SELECT *, 1 AS x FROM int8_tbl b) ss WHERE (x = 0) OR (q1 >= q2 AND q1 <= q2); SELECT * FROM ( SELECT *, 0 AS x FROM int8_tbl a UNION ALL SELECT *, 1 AS x FROM int8_tbl b) ss WHERE (x = 0) OR (q1 >= q2 AND q1 <= q2); pgFormatter-4.2/t/pg-test-files/expected/updatable_views.sql000066400000000000000000001651171361326045100243130ustar00rootroot00000000000000-- -- UPDATABLE VIEWS -- -- avoid bit-exact output here because operations may not be bit-exact. SET extra_float_digits = 0; -- check that non-updatable views and columns are rejected with useful error -- messages CREATE TABLE base_tbl ( a int PRIMARY KEY, b text DEFAULT 'Unspecified' ); INSERT INTO base_tbl SELECT i, 'Row ' || i FROM generate_series(- 2, 2) g (i); CREATE VIEW ro_view1 AS SELECT DISTINCT a, b FROM base_tbl; -- DISTINCT not supported CREATE VIEW ro_view2 AS SELECT a, b FROM base_tbl GROUP BY a, b; -- GROUP BY not supported CREATE VIEW ro_view3 AS SELECT 1 FROM base_tbl HAVING max(a) > 0; -- HAVING not supported CREATE VIEW ro_view4 AS SELECT count(*) FROM base_tbl; -- Aggregate functions not supported CREATE VIEW ro_view5 AS SELECT a, rank() OVER () FROM base_tbl; -- Window functions not supported CREATE VIEW ro_view6 AS SELECT a, b FROM base_tbl UNION SELECT - a, b FROM base_tbl; -- Set ops not supported CREATE VIEW ro_view7 AS WITH t AS ( SELECT a, b FROM base_tbl ) SELECT * FROM t; -- WITH not supported CREATE VIEW ro_view8 AS SELECT a, b FROM base_tbl ORDER BY a OFFSET 1; -- OFFSET not supported CREATE VIEW ro_view9 AS SELECT a, b FROM base_tbl ORDER BY a LIMIT 1; -- LIMIT not supported CREATE VIEW ro_view10 AS SELECT 1 AS a; -- No base relations CREATE VIEW ro_view11 AS SELECT b1.a, b2.b FROM base_tbl b1, base_tbl b2; -- Multiple base relations CREATE VIEW ro_view12 AS SELECT * FROM generate_series(1, 10) AS g (a); -- SRF in rangetable CREATE VIEW ro_view13 AS SELECT a, b FROM ( SELECT * FROM base_tbl) AS t; -- Subselect in rangetable CREATE VIEW rw_view14 AS SELECT ctid, a, b FROM base_tbl; -- System columns may be part of an updatable view CREATE VIEW rw_view15 AS SELECT a, upper(b) FROM base_tbl; -- Expression/function may be part of an updatable view CREATE VIEW rw_view16 AS SELECT a, b, a AS aa FROM base_tbl; -- Repeated column may be part of an updatable view CREATE VIEW ro_view17 AS SELECT * FROM ro_view1; -- Base relation not updatable CREATE VIEW ro_view18 AS SELECT * FROM ( VALUES (1)) AS tmp (a); -- VALUES in rangetable CREATE SEQUENCE uv_seq; CREATE VIEW ro_view19 AS SELECT * FROM uv_seq; -- View based on a sequence CREATE VIEW ro_view20 AS SELECT a, b, generate_series(1, a) g FROM base_tbl; -- SRF in targetlist not supported SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE E'r_\\_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name LIKE E'r_\\_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE E'r_\\_view%' ORDER BY table_name, ordinal_position; -- Read-only views DELETE FROM ro_view1; DELETE FROM ro_view2; DELETE FROM ro_view3; DELETE FROM ro_view4; DELETE FROM ro_view5; DELETE FROM ro_view6; UPDATE ro_view7 SET a = a + 1; UPDATE ro_view8 SET a = a + 1; UPDATE ro_view9 SET a = a + 1; UPDATE ro_view10 SET a = a + 1; UPDATE ro_view11 SET a = a + 1; UPDATE ro_view12 SET a = a + 1; INSERT INTO ro_view13 VALUES (3, 'Row 3'); -- Partially updatable view INSERT INTO rw_view14 VALUES (NULL, 3, 'Row 3'); -- should fail INSERT INTO rw_view14 (a, b) VALUES (3, 'Row 3'); -- should be OK UPDATE rw_view14 SET ctid = NULL WHERE a = 3; -- should fail UPDATE rw_view14 SET b = 'ROW 3' WHERE a = 3; -- should be OK SELECT * FROM base_tbl; DELETE FROM rw_view14 WHERE a = 3; -- should be OK -- Partially updatable view INSERT INTO rw_view15 VALUES (3, 'ROW 3'); -- should fail INSERT INTO rw_view15 (a) VALUES (3); -- should be OK INSERT INTO rw_view15 (a) VALUES (3) ON CONFLICT DO NOTHING; -- succeeds SELECT * FROM rw_view15; INSERT INTO rw_view15 (a) VALUES (3) ON CONFLICT (a) DO NOTHING; -- succeeds SELECT * FROM rw_view15; INSERT INTO rw_view15 (a) VALUES (3) ON CONFLICT (a) DO UPDATE SET a = excluded.a; -- succeeds SELECT * FROM rw_view15; INSERT INTO rw_view15 (a) VALUES (3) ON CONFLICT (a) DO UPDATE SET upper = 'blarg'; -- fails SELECT * FROM rw_view15; SELECT * FROM rw_view15; ALTER VIEW rw_view15 ALTER COLUMN upper SET DEFAULT 'NOT SET'; INSERT INTO rw_view15 (a) VALUES (4); -- should fail UPDATE rw_view15 SET upper = 'ROW 3' WHERE a = 3; -- should fail UPDATE rw_view15 SET upper = DEFAULT WHERE a = 3; -- should fail UPDATE rw_view15 SET a = 4 WHERE a = 3; -- should be OK SELECT * FROM base_tbl; DELETE FROM rw_view15 WHERE a = 4; -- should be OK -- Partially updatable view INSERT INTO rw_view16 VALUES (3, 'Row 3', 3); -- should fail INSERT INTO rw_view16 (a, b) VALUES (3, 'Row 3'); -- should be OK UPDATE rw_view16 SET a = 3, aa = - 3 WHERE a = 3; -- should fail UPDATE rw_view16 SET aa = - 3 WHERE a = 3; -- should be OK SELECT * FROM base_tbl; DELETE FROM rw_view16 WHERE a = - 3; -- should be OK -- Read-only views INSERT INTO ro_view17 VALUES (3, 'ROW 3'); DELETE FROM ro_view18; UPDATE ro_view19 SET last_value = 1000; UPDATE ro_view20 SET b = upper(b); DROP TABLE base_tbl CASCADE; DROP VIEW ro_view10, ro_view12, ro_view18; DROP SEQUENCE uv_seq CASCADE; -- simple updatable view CREATE TABLE base_tbl ( a int PRIMARY KEY, b text DEFAULT 'Unspecified' ); INSERT INTO base_tbl SELECT i, 'Row ' || i FROM generate_series(- 2, 2) g (i); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a > 0; SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name = 'rw_view1'; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name = 'rw_view1'; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name = 'rw_view1' ORDER BY ordinal_position; INSERT INTO rw_view1 VALUES (3, 'Row 3'); INSERT INTO rw_view1 (a) VALUES (4); UPDATE rw_view1 SET a = 5 WHERE a = 4; DELETE FROM rw_view1 WHERE b = 'Row 2'; SELECT * FROM base_tbl; EXPLAIN ( COSTS OFF ) UPDATE rw_view1 SET a = 6 WHERE a = 5; EXPLAIN ( COSTS OFF ) DELETE FROM rw_view1 WHERE a = 5; DROP TABLE base_tbl CASCADE; -- view on top of view CREATE TABLE base_tbl ( a int PRIMARY KEY, b text DEFAULT 'Unspecified' ); INSERT INTO base_tbl SELECT i, 'Row ' || i FROM generate_series(- 2, 2) g (i); CREATE VIEW rw_view1 AS SELECT b AS bb, a AS aa FROM base_tbl WHERE a > 0; CREATE VIEW rw_view2 AS SELECT aa AS aaa, bb AS bbb FROM rw_view1 WHERE aa < 10; SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name = 'rw_view2'; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name = 'rw_view2'; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name = 'rw_view2' ORDER BY ordinal_position; INSERT INTO rw_view2 VALUES (3, 'Row 3'); INSERT INTO rw_view2 (aaa) VALUES (4); SELECT * FROM rw_view2; UPDATE rw_view2 SET bbb = 'Row 4' WHERE aaa = 4; DELETE FROM rw_view2 WHERE aaa = 2; SELECT * FROM rw_view2; EXPLAIN ( COSTS OFF ) UPDATE rw_view2 SET aaa = 5 WHERE aaa = 4; EXPLAIN ( COSTS OFF ) DELETE FROM rw_view2 WHERE aaa = 4; DROP TABLE base_tbl CASCADE; -- view on top of view with rules CREATE TABLE base_tbl ( a int PRIMARY KEY, b text DEFAULT 'Unspecified' ); INSERT INTO base_tbl SELECT i, 'Row ' || i FROM generate_series(- 2, 2) g (i); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a > 0 OFFSET 0; -- not updatable without rules/triggers CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10; SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE 'rw_view%' ORDER BY table_name, ordinal_position; CREATE RULE rw_view1_ins_rule AS ON INSERT TO rw_view1 DO INSTEAD INSERT INTO base_tbl VALUES (NEW.a, NEW.b) RETURNING *; SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE 'rw_view%' ORDER BY table_name, ordinal_position; CREATE RULE rw_view1_upd_rule AS ON UPDATE TO rw_view1 DO INSTEAD UPDATE base_tbl SET b = NEW.b WHERE a = OLD.a RETURNING NEW.*; SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE 'rw_view%' ORDER BY table_name, ordinal_position; CREATE RULE rw_view1_del_rule AS ON DELETE TO rw_view1 DO INSTEAD DELETE FROM base_tbl WHERE a = OLD.a RETURNING OLD.*; SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE 'rw_view%' ORDER BY table_name, ordinal_position; INSERT INTO rw_view2 VALUES (3, 'Row 3') RETURNING *; UPDATE rw_view2 SET b = 'Row three' WHERE a = 3 RETURNING *; SELECT * FROM rw_view2; DELETE FROM rw_view2 WHERE a = 3 RETURNING *; SELECT * FROM rw_view2; EXPLAIN ( COSTS OFF ) UPDATE rw_view2 SET a = 3 WHERE a = 2; EXPLAIN ( COSTS OFF ) DELETE FROM rw_view2 WHERE a = 2; DROP TABLE base_tbl CASCADE; -- view on top of view with triggers CREATE TABLE base_tbl ( a int PRIMARY KEY, b text DEFAULT 'Unspecified' ); INSERT INTO base_tbl SELECT i, 'Row ' || i FROM generate_series(- 2, 2) g (i); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a > 0 OFFSET 0; -- not updatable without rules/triggers CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10; SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into, is_trigger_updatable, is_trigger_deletable, is_trigger_insertable_into FROM information_schema.views WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE 'rw_view%' ORDER BY table_name, ordinal_position; CREATE FUNCTION rw_view1_trig_fn () RETURNS TRIGGER AS $$ BEGIN IF TG_OP = 'INSERT' THEN INSERT INTO base_tbl VALUES (NEW.a, NEW.b); RETURN NEW; ELSIF TG_OP = 'UPDATE' THEN UPDATE base_tbl SET b = NEW.b WHERE a = OLD.a; RETURN NEW; ELSIF TG_OP = 'DELETE' THEN DELETE FROM base_tbl WHERE a = OLD.a; RETURN OLD; END IF; END; $$ LANGUAGE plpgsql; CREATE TRIGGER rw_view1_ins_trig INSTEAD OF INSERT ON rw_view1 FOR EACH ROW EXECUTE PROCEDURE rw_view1_trig_fn (); SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into, is_trigger_updatable, is_trigger_deletable, is_trigger_insertable_into FROM information_schema.views WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE 'rw_view%' ORDER BY table_name, ordinal_position; CREATE TRIGGER rw_view1_upd_trig INSTEAD OF UPDATE ON rw_view1 FOR EACH ROW EXECUTE PROCEDURE rw_view1_trig_fn (); SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into, is_trigger_updatable, is_trigger_deletable, is_trigger_insertable_into FROM information_schema.views WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE 'rw_view%' ORDER BY table_name, ordinal_position; CREATE TRIGGER rw_view1_del_trig INSTEAD OF DELETE ON rw_view1 FOR EACH ROW EXECUTE PROCEDURE rw_view1_trig_fn (); SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into, is_trigger_updatable, is_trigger_deletable, is_trigger_insertable_into FROM information_schema.views WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE 'rw_view%' ORDER BY table_name, ordinal_position; INSERT INTO rw_view2 VALUES (3, 'Row 3') RETURNING *; UPDATE rw_view2 SET b = 'Row three' WHERE a = 3 RETURNING *; SELECT * FROM rw_view2; DELETE FROM rw_view2 WHERE a = 3 RETURNING *; SELECT * FROM rw_view2; EXPLAIN ( COSTS OFF ) UPDATE rw_view2 SET a = 3 WHERE a = 2; EXPLAIN ( COSTS OFF ) DELETE FROM rw_view2 WHERE a = 2; DROP TABLE base_tbl CASCADE; DROP FUNCTION rw_view1_trig_fn (); -- update using whole row from view CREATE TABLE base_tbl ( a int PRIMARY KEY, b text DEFAULT 'Unspecified' ); INSERT INTO base_tbl SELECT i, 'Row ' || i FROM generate_series(- 2, 2) g (i); CREATE VIEW rw_view1 AS SELECT b AS bb, a AS aa FROM base_tbl; CREATE FUNCTION rw_view1_aa (x rw_view1) RETURNS int AS $$ SELECT x.aa $$ LANGUAGE sql; UPDATE rw_view1 v SET bb = 'Updated row 2' WHERE rw_view1_aa (v) = 2 RETURNING rw_view1_aa (v), v.bb; SELECT * FROM base_tbl; EXPLAIN ( COSTS OFF ) UPDATE rw_view1 v SET bb = 'Updated row 2' WHERE rw_view1_aa (v) = 2 RETURNING rw_view1_aa (v), v.bb; DROP TABLE base_tbl CASCADE; -- permissions checks CREATE USER regress_view_user1; CREATE USER regress_view_user2; SET SESSION AUTHORIZATION regress_view_user1; CREATE TABLE base_tbl ( a int, b text, c float ); INSERT INTO base_tbl VALUES (1, 'Row 1', 1.0); CREATE VIEW rw_view1 AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl; INSERT INTO rw_view1 VALUES ('Row 2', 2.0, 2); GRANT SELECT ON base_tbl TO regress_view_user2; GRANT SELECT ON rw_view1 TO regress_view_user2; GRANT UPDATE (a, c) ON base_tbl TO regress_view_user2; GRANT UPDATE (bb, cc) ON rw_view1 TO regress_view_user2; RESET SESSION AUTHORIZATION; SET SESSION AUTHORIZATION regress_view_user2; CREATE VIEW rw_view2 AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl; SELECT * FROM base_tbl; -- ok SELECT * FROM rw_view1; -- ok SELECT * FROM rw_view2; -- ok INSERT INTO base_tbl VALUES (3, 'Row 3', 3.0); -- not allowed INSERT INTO rw_view1 VALUES ('Row 3', 3.0, 3); -- not allowed INSERT INTO rw_view2 VALUES ('Row 3', 3.0, 3); -- not allowed UPDATE base_tbl SET a = a, c = c; -- ok UPDATE base_tbl SET b = b; -- not allowed UPDATE rw_view1 SET bb = bb, cc = cc; -- ok UPDATE rw_view1 SET aa = aa; -- not allowed UPDATE rw_view2 SET aa = aa, cc = cc; -- ok UPDATE rw_view2 SET bb = bb; -- not allowed DELETE FROM base_tbl; -- not allowed DELETE FROM rw_view1; -- not allowed DELETE FROM rw_view2; -- not allowed RESET SESSION AUTHORIZATION; SET SESSION AUTHORIZATION regress_view_user1; GRANT INSERT, DELETE ON base_tbl TO regress_view_user2; RESET SESSION AUTHORIZATION; SET SESSION AUTHORIZATION regress_view_user2; INSERT INTO base_tbl VALUES (3, 'Row 3', 3.0); -- ok INSERT INTO rw_view1 VALUES ('Row 4', 4.0, 4); -- not allowed INSERT INTO rw_view2 VALUES ('Row 4', 4.0, 4); -- ok DELETE FROM base_tbl WHERE a = 1; -- ok DELETE FROM rw_view1 WHERE aa = 2; -- not allowed DELETE FROM rw_view2 WHERE aa = 2; -- ok SELECT * FROM base_tbl; RESET SESSION AUTHORIZATION; SET SESSION AUTHORIZATION regress_view_user1; REVOKE INSERT, DELETE ON base_tbl FROM regress_view_user2; GRANT INSERT, DELETE ON rw_view1 TO regress_view_user2; RESET SESSION AUTHORIZATION; SET SESSION AUTHORIZATION regress_view_user2; INSERT INTO base_tbl VALUES (5, 'Row 5', 5.0); -- not allowed INSERT INTO rw_view1 VALUES ('Row 5', 5.0, 5); -- ok INSERT INTO rw_view2 VALUES ('Row 6', 6.0, 6); -- not allowed DELETE FROM base_tbl WHERE a = 3; -- not allowed DELETE FROM rw_view1 WHERE aa = 3; -- ok DELETE FROM rw_view2 WHERE aa = 4; -- not allowed SELECT * FROM base_tbl; RESET SESSION AUTHORIZATION; DROP TABLE base_tbl CASCADE; -- nested-view permissions CREATE TABLE base_tbl ( a int, b text, c float ); INSERT INTO base_tbl VALUES (1, 'Row 1', 1.0); SET SESSION AUTHORIZATION regress_view_user1; CREATE VIEW rw_view1 AS SELECT * FROM base_tbl; SELECT * FROM rw_view1; -- not allowed SELECT * FROM rw_view1 FOR UPDATE; -- not allowed UPDATE rw_view1 SET b = 'foo' WHERE a = 1; -- not allowed SET SESSION AUTHORIZATION regress_view_user2; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1; SELECT * FROM rw_view2; -- not allowed SELECT * FROM rw_view2 FOR UPDATE; -- not allowed UPDATE rw_view2 SET b = 'bar' WHERE a = 1; -- not allowed RESET SESSION AUTHORIZATION; GRANT SELECT ON base_tbl TO regress_view_user1; SET SESSION AUTHORIZATION regress_view_user1; SELECT * FROM rw_view1; SELECT * FROM rw_view1 FOR UPDATE; -- not allowed UPDATE rw_view1 SET b = 'foo' WHERE a = 1; -- not allowed SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; -- not allowed SELECT * FROM rw_view2 FOR UPDATE; -- not allowed UPDATE rw_view2 SET b = 'bar' WHERE a = 1; -- not allowed SET SESSION AUTHORIZATION regress_view_user1; GRANT SELECT ON rw_view1 TO regress_view_user2; SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; SELECT * FROM rw_view2 FOR UPDATE; -- not allowed UPDATE rw_view2 SET b = 'bar' WHERE a = 1; -- not allowed RESET SESSION AUTHORIZATION; GRANT UPDATE ON base_tbl TO regress_view_user1; SET SESSION AUTHORIZATION regress_view_user1; SELECT * FROM rw_view1; SELECT * FROM rw_view1 FOR UPDATE; UPDATE rw_view1 SET b = 'foo' WHERE a = 1; SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; SELECT * FROM rw_view2 FOR UPDATE; -- not allowed UPDATE rw_view2 SET b = 'bar' WHERE a = 1; -- not allowed SET SESSION AUTHORIZATION regress_view_user1; GRANT UPDATE ON rw_view1 TO regress_view_user2; SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; SELECT * FROM rw_view2 FOR UPDATE; UPDATE rw_view2 SET b = 'bar' WHERE a = 1; RESET SESSION AUTHORIZATION; REVOKE UPDATE ON base_tbl FROM regress_view_user1; SET SESSION AUTHORIZATION regress_view_user1; SELECT * FROM rw_view1; SELECT * FROM rw_view1 FOR UPDATE; -- not allowed UPDATE rw_view1 SET b = 'foo' WHERE a = 1; -- not allowed SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; SELECT * FROM rw_view2 FOR UPDATE; -- not allowed UPDATE rw_view2 SET b = 'bar' WHERE a = 1; -- not allowed RESET SESSION AUTHORIZATION; DROP TABLE base_tbl CASCADE; DROP USER regress_view_user1; DROP USER regress_view_user2; -- column defaults CREATE TABLE base_tbl ( a int PRIMARY KEY, b text DEFAULT 'Unspecified', c serial ); INSERT INTO base_tbl VALUES (1, 'Row 1'); INSERT INTO base_tbl VALUES (2, 'Row 2'); INSERT INTO base_tbl VALUES (3); CREATE VIEW rw_view1 AS SELECT a AS aa, b AS bb FROM base_tbl; ALTER VIEW rw_view1 ALTER COLUMN bb SET DEFAULT 'View default'; INSERT INTO rw_view1 VALUES (4, 'Row 4'); INSERT INTO rw_view1 (aa) VALUES (5); SELECT * FROM base_tbl; DROP TABLE base_tbl CASCADE; -- Table having triggers CREATE TABLE base_tbl ( a int PRIMARY KEY, b text DEFAULT 'Unspecified' ); INSERT INTO base_tbl VALUES (1, 'Row 1'); INSERT INTO base_tbl VALUES (2, 'Row 2'); CREATE FUNCTION rw_view1_trig_fn () RETURNS TRIGGER AS $$ BEGIN IF TG_OP = 'INSERT' THEN UPDATE base_tbl SET b = NEW.b WHERE a = 1; RETURN NULL; END IF; RETURN NULL; END; $$ LANGUAGE plpgsql; CREATE TRIGGER rw_view1_ins_trig AFTER INSERT ON base_tbl FOR EACH ROW EXECUTE PROCEDURE rw_view1_trig_fn (); CREATE VIEW rw_view1 AS SELECT a AS aa, b AS bb FROM base_tbl; INSERT INTO rw_view1 VALUES (3, 'Row 3'); SELECT * FROM base_tbl; DROP VIEW rw_view1; DROP TRIGGER rw_view1_ins_trig ON base_tbl; DROP FUNCTION rw_view1_trig_fn (); DROP TABLE base_tbl; -- view with ORDER BY CREATE TABLE base_tbl ( a int, b int ); INSERT INTO base_tbl VALUES (1, 2), (4, 5), (3, - 3); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl ORDER BY a + b; SELECT * FROM rw_view1; INSERT INTO rw_view1 VALUES (7, - 8); SELECT * FROM rw_view1; EXPLAIN ( VERBOSE, COSTS OFF ) UPDATE rw_view1 SET b = b + 1 RETURNING *; UPDATE rw_view1 SET b = b + 1 RETURNING *; SELECT * FROM rw_view1; DROP TABLE base_tbl CASCADE; -- multiple array-column updates CREATE TABLE base_tbl ( a int, arr int[] ); INSERT INTO base_tbl VALUES (1, ARRAY[2]), (3, ARRAY[4]); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl; UPDATE rw_view1 SET arr[1] = 42, arr[2] = 77 WHERE a = 3; SELECT * FROM rw_view1; DROP TABLE base_tbl CASCADE; -- views with updatable and non-updatable columns CREATE TABLE base_tbl ( a float ); INSERT INTO base_tbl SELECT i / 10.0 FROM generate_series(1, 10) g (i); CREATE VIEW rw_view1 AS SELECT ctid, sin(a) s, a, cos(a) c FROM base_tbl WHERE a != 0 ORDER BY abs(a); INSERT INTO rw_view1 VALUES (NULL, NULL, 1.1, NULL); -- should fail INSERT INTO rw_view1 (s, c, a) VALUES (NULL, NULL, 1.1); -- should fail INSERT INTO rw_view1 (a) VALUES (1.1) RETURNING a, s, c; -- OK UPDATE rw_view1 SET s = s WHERE a = 1.1; -- should fail UPDATE rw_view1 SET a = 1.05 WHERE a = 1.1 RETURNING s; -- OK DELETE FROM rw_view1 WHERE a = 1.05; -- OK CREATE VIEW rw_view2 AS SELECT s, c, s / c t, a base_a, ctid FROM rw_view1; INSERT INTO rw_view2 VALUES (NULL, NULL, NULL, 1.1, NULL); -- should fail INSERT INTO rw_view2 (s, c, base_a) VALUES (NULL, NULL, 1.1); -- should fail INSERT INTO rw_view2 (base_a) VALUES (1.1) RETURNING t; -- OK UPDATE rw_view2 SET s = s WHERE base_a = 1.1; -- should fail UPDATE rw_view2 SET t = t WHERE base_a = 1.1; -- should fail UPDATE rw_view2 SET base_a = 1.05 WHERE base_a = 1.1; -- OK DELETE FROM rw_view2 WHERE base_a = 1.05 RETURNING base_a, s, c, t; -- OK CREATE VIEW rw_view3 AS SELECT s, c, s / c t, ctid FROM rw_view1; INSERT INTO rw_view3 VALUES (NULL, NULL, NULL, NULL); -- should fail INSERT INTO rw_view3 (s) VALUES (NULL); -- should fail UPDATE rw_view3 SET s = s; -- should fail DELETE FROM rw_view3 WHERE s = sin(0.1); -- should be OK SELECT * FROM base_tbl ORDER BY a; SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE E'r_\\_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name LIKE E'r_\\_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE E'r_\\_view%' ORDER BY table_name, ordinal_position; SELECT events & 4 != 0 AS upd, events & 8 != 0 AS ins, events & 16 != 0 AS del FROM pg_catalog.pg_relation_is_updatable ('rw_view3'::regclass, FALSE) t (events); DROP TABLE base_tbl CASCADE; -- inheritance tests CREATE TABLE base_tbl_parent ( a int ); CREATE TABLE base_tbl_child ( CHECK (a > 0) ) INHERITS ( base_tbl_parent ); INSERT INTO base_tbl_parent SELECT * FROM generate_series(- 8, - 1); INSERT INTO base_tbl_child SELECT * FROM generate_series(1, 8); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl_parent; CREATE VIEW rw_view2 AS SELECT * FROM ONLY base_tbl_parent; SELECT * FROM rw_view1 ORDER BY a; SELECT * FROM ONLY rw_view1 ORDER BY a; SELECT * FROM rw_view2 ORDER BY a; INSERT INTO rw_view1 VALUES (- 100), (100); INSERT INTO rw_view2 VALUES (- 200), (200); UPDATE rw_view1 SET a = a * 10 WHERE a IN (- 1, 1); -- Should produce -10 and 10 UPDATE ONLY rw_view1 SET a = a * 10 WHERE a IN (- 2, 2); -- Should produce -20 and 20 UPDATE rw_view2 SET a = a * 10 WHERE a IN (- 3, 3); -- Should produce -30 only UPDATE ONLY rw_view2 SET a = a * 10 WHERE a IN (- 4, 4); -- Should produce -40 only DELETE FROM rw_view1 WHERE a IN (- 5, 5); -- Should delete -5 and 5 DELETE FROM ONLY rw_view1 WHERE a IN (- 6, 6); -- Should delete -6 and 6 DELETE FROM rw_view2 WHERE a IN (- 7, 7); -- Should delete -7 only DELETE FROM ONLY rw_view2 WHERE a IN (- 8, 8); -- Should delete -8 only SELECT * FROM ONLY base_tbl_parent ORDER BY a; SELECT * FROM base_tbl_child ORDER BY a; CREATE TABLE other_tbl_parent ( id int ); CREATE TABLE other_tbl_child () INHERITS ( other_tbl_parent ); INSERT INTO other_tbl_parent VALUES (7), (200); INSERT INTO other_tbl_child VALUES (8), (100); EXPLAIN ( COSTS OFF ) UPDATE rw_view1 SET a = a + 1000 FROM other_tbl_parent WHERE a = id; UPDATE rw_view1 SET a = a + 1000 FROM other_tbl_parent WHERE a = id; SELECT * FROM ONLY base_tbl_parent ORDER BY a; SELECT * FROM base_tbl_child ORDER BY a; DROP TABLE base_tbl_parent, base_tbl_child CASCADE; DROP TABLE other_tbl_parent CASCADE; -- simple WITH CHECK OPTION CREATE TABLE base_tbl ( a int, b int DEFAULT 10 ); INSERT INTO base_tbl VALUES (1, 2), (2, 3), (1, - 1); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b WITH LOCAL CHECK OPTION; \d+ rw_view1 SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; INSERT INTO rw_view1 VALUES (3, 4); -- ok INSERT INTO rw_view1 VALUES (4, 3); -- should fail INSERT INTO rw_view1 VALUES (5, NULL); -- should fail UPDATE rw_view1 SET b = 5 WHERE a = 3; -- ok UPDATE rw_view1 SET b = - 5 WHERE a = 3; -- should fail INSERT INTO rw_view1 (a) VALUES (9); -- ok INSERT INTO rw_view1 (a) VALUES (10); -- should fail SELECT * FROM base_tbl; DROP TABLE base_tbl CASCADE; -- WITH LOCAL/CASCADED CHECK OPTION CREATE TABLE base_tbl ( a int ); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a > 0; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 WITH CHECK OPTION; -- implicitly cascaded \d+ rw_view2 SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; INSERT INTO rw_view2 VALUES (- 5); -- should fail INSERT INTO rw_view2 VALUES (5); -- ok INSERT INTO rw_view2 VALUES (15); -- should fail SELECT * FROM base_tbl; UPDATE rw_view2 SET a = a - 10; -- should fail UPDATE rw_view2 SET a = a + 10; -- should fail CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 WITH LOCAL CHECK OPTION; \d+ rw_view2 SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; INSERT INTO rw_view2 VALUES (- 10); -- ok, but not in view INSERT INTO rw_view2 VALUES (20); -- should fail SELECT * FROM base_tbl; ALTER VIEW rw_view1 SET (check_option = here); -- invalid ALTER VIEW rw_view1 SET (check_option = local); INSERT INTO rw_view2 VALUES (- 20); -- should fail INSERT INTO rw_view2 VALUES (30); -- should fail ALTER VIEW rw_view2 RESET (check_option); \d+ rw_view2 SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; INSERT INTO rw_view2 VALUES (30); -- ok, but not in view SELECT * FROM base_tbl; DROP TABLE base_tbl CASCADE; -- WITH CHECK OPTION with no local view qual CREATE TABLE base_tbl ( a int ); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; INSERT INTO rw_view1 VALUES (- 1); -- ok INSERT INTO rw_view1 VALUES (1); -- ok INSERT INTO rw_view2 VALUES (- 2); -- ok, but not in view INSERT INTO rw_view2 VALUES (2); -- ok INSERT INTO rw_view3 VALUES (- 3); -- should fail INSERT INTO rw_view3 VALUES (3); -- ok DROP TABLE base_tbl CASCADE; -- WITH CHECK OPTION with scalar array ops CREATE TABLE base_tbl ( a int, b int[] ); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a = ANY (b ) WITH CHECK OPTION; INSERT INTO rw_view1 VALUES (1, ARRAY[1, 2, 3]); -- ok INSERT INTO rw_view1 VALUES (10, ARRAY[4, 5]); -- should fail UPDATE rw_view1 SET b[2] = - b[2] WHERE a = 1; -- ok UPDATE rw_view1 SET b[1] = - b[1] WHERE a = 1; -- should fail PREPARE ins (int, int[]) AS INSERT INTO rw_view1 VALUES ($1, $2); EXECUTE ins (2, ARRAY[1, 2, 3]); -- ok EXECUTE ins (10, ARRAY[4, 5]); -- should fail DEALLOCATE PREPARE ins; DROP TABLE base_tbl CASCADE; -- WITH CHECK OPTION with subquery CREATE TABLE base_tbl ( a int ); CREATE TABLE ref_tbl ( a int PRIMARY KEY ); INSERT INTO ref_tbl SELECT * FROM generate_series(1, 10); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl b WHERE EXISTS ( SELECT 1 FROM ref_tbl r WHERE r.a = b.a ) WITH CHECK OPTION; INSERT INTO rw_view1 VALUES (5); -- ok INSERT INTO rw_view1 VALUES (15); -- should fail UPDATE rw_view1 SET a = a + 5; -- ok UPDATE rw_view1 SET a = a + 5; -- should fail EXPLAIN ( COSTS OFF ) INSERT INTO rw_view1 VALUES (5); EXPLAIN ( COSTS OFF ) UPDATE rw_view1 SET a = a + 5; DROP TABLE base_tbl, ref_tbl CASCADE; -- WITH CHECK OPTION with BEFORE trigger on base table CREATE TABLE base_tbl ( a int, b int ); CREATE FUNCTION base_tbl_trig_fn () RETURNS TRIGGER AS $$ BEGIN NEW.b := 10; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER base_tbl_trig BEFORE INSERT OR UPDATE ON base_tbl FOR EACH ROW EXECUTE PROCEDURE base_tbl_trig_fn (); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b WITH CHECK OPTION; INSERT INTO rw_view1 VALUES (5, 0); -- ok INSERT INTO rw_view1 VALUES (15, 20); -- should fail UPDATE rw_view1 SET a = 20, b = 30; -- should fail DROP TABLE base_tbl CASCADE; DROP FUNCTION base_tbl_trig_fn (); -- WITH LOCAL CHECK OPTION with INSTEAD OF trigger on base view CREATE TABLE base_tbl ( a int, b int ); CREATE VIEW rw_view1 AS SELECT a FROM base_tbl WHERE a < b; CREATE FUNCTION rw_view1_trig_fn () RETURNS TRIGGER AS $$ BEGIN IF TG_OP = 'INSERT' THEN INSERT INTO base_tbl VALUES (NEW.a, 10); RETURN NEW; ELSIF TG_OP = 'UPDATE' THEN UPDATE base_tbl SET a = NEW.a WHERE a = OLD.a; RETURN NEW; ELSIF TG_OP = 'DELETE' THEN DELETE FROM base_tbl WHERE a = OLD.a; RETURN OLD; END IF; END; $$ LANGUAGE plpgsql; CREATE TRIGGER rw_view1_trig INSTEAD OF INSERT OR UPDATE OR DELETE ON rw_view1 FOR EACH ROW EXECUTE PROCEDURE rw_view1_trig_fn (); CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0 WITH LOCAL CHECK OPTION; INSERT INTO rw_view2 VALUES (- 5); -- should fail INSERT INTO rw_view2 VALUES (5); -- ok INSERT INTO rw_view2 VALUES (50); -- ok, but not in view UPDATE rw_view2 SET a = a - 10; -- should fail SELECT * FROM base_tbl; -- Check option won't cascade down to base view with INSTEAD OF triggers ALTER VIEW rw_view2 SET (check_option = cascaded); INSERT INTO rw_view2 VALUES (100); -- ok, but not in view (doesn't fail rw_view1's check) UPDATE rw_view2 SET a = 200 WHERE a = 5; -- ok, but not in view (doesn't fail rw_view1's check) SELECT * FROM base_tbl; -- Neither local nor cascaded check options work with INSTEAD rules DROP TRIGGER rw_view1_trig ON rw_view1; CREATE RULE rw_view1_ins_rule AS ON INSERT TO rw_view1 DO INSTEAD INSERT INTO base_tbl VALUES (NEW.a, 10); CREATE RULE rw_view1_upd_rule AS ON UPDATE TO rw_view1 DO INSTEAD UPDATE base_tbl SET a = NEW.a WHERE a = OLD.a; INSERT INTO rw_view2 VALUES (- 10); -- ok, but not in view (doesn't fail rw_view2's check) INSERT INTO rw_view2 VALUES (5); -- ok INSERT INTO rw_view2 VALUES (20); -- ok, but not in view (doesn't fail rw_view1's check) UPDATE rw_view2 SET a = 30 WHERE a = 5; -- ok, but not in view (doesn't fail rw_view1's check) INSERT INTO rw_view2 VALUES (5); -- ok UPDATE rw_view2 SET a = - 5 WHERE a = 5; -- ok, but not in view (doesn't fail rw_view2's check) SELECT * FROM base_tbl; DROP TABLE base_tbl CASCADE; DROP FUNCTION rw_view1_trig_fn (); CREATE TABLE base_tbl ( a int ); CREATE VIEW rw_view1 AS SELECT a, 10 AS b FROM base_tbl; CREATE RULE rw_view1_ins_rule AS ON INSERT TO rw_view1 DO INSTEAD INSERT INTO base_tbl VALUES (NEW.a); CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > b WITH LOCAL CHECK OPTION; INSERT INTO rw_view2 VALUES (2, 3); -- ok, but not in view (doesn't fail rw_view2's check) DROP TABLE base_tbl CASCADE; -- security barrier view CREATE TABLE base_tbl ( person text, visibility text ); INSERT INTO base_tbl VALUES ('Tom', 'public'), ('Dick', 'private'), ('Harry', 'public'); CREATE VIEW rw_view1 AS SELECT person FROM base_tbl WHERE visibility = 'public'; CREATE FUNCTION snoop (anyelement) RETURNS boolean AS $$ BEGIN RAISE NOTICE 'snooped value: %', $1; RETURN TRUE; END; $$ LANGUAGE plpgsql COST 0.000001; CREATE OR REPLACE FUNCTION LEAKPROOF (anyelement) RETURNS boolean AS $$ BEGIN RETURN TRUE; END; $$ LANGUAGE plpgsql STRICT IMMUTABLE LEAKPROOF; SELECT * FROM rw_view1 WHERE snoop (person); UPDATE rw_view1 SET person = person WHERE snoop (person); DELETE FROM rw_view1 WHERE NOT snoop (person); ALTER VIEW rw_view1 SET (security_barrier = TRUE); SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name = 'rw_view1'; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name = 'rw_view1'; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name = 'rw_view1' ORDER BY ordinal_position; SELECT * FROM rw_view1 WHERE snoop (person); UPDATE rw_view1 SET person = person WHERE snoop (person); DELETE FROM rw_view1 WHERE NOT snoop (person); EXPLAIN ( COSTS OFF ) SELECT * FROM rw_view1 WHERE snoop (person); EXPLAIN ( COSTS OFF ) UPDATE rw_view1 SET person = person WHERE snoop (person); EXPLAIN ( COSTS OFF ) DELETE FROM rw_view1 WHERE NOT snoop (person); -- security barrier view on top of security barrier view CREATE VIEW rw_view2 WITH ( security_barrier = TRUE ) AS SELECT * FROM rw_view1 WHERE snoop ( person); SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name = 'rw_view2'; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name = 'rw_view2'; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name = 'rw_view2' ORDER BY ordinal_position; SELECT * FROM rw_view2 WHERE snoop (person); UPDATE rw_view2 SET person = person WHERE snoop (person); DELETE FROM rw_view2 WHERE NOT snoop (person); EXPLAIN ( COSTS OFF ) SELECT * FROM rw_view2 WHERE snoop (person); EXPLAIN ( COSTS OFF ) UPDATE rw_view2 SET person = person WHERE snoop (person); EXPLAIN ( COSTS OFF ) DELETE FROM rw_view2 WHERE NOT snoop (person); DROP TABLE base_tbl CASCADE; -- security barrier view on top of table with rules CREATE TABLE base_tbl ( id int PRIMARY KEY, data text, deleted boolean ); INSERT INTO base_tbl VALUES (1, 'Row 1', FALSE), (2, 'Row 2', TRUE); CREATE RULE base_tbl_ins_rule AS ON INSERT TO base_tbl WHERE EXISTS ( SELECT 1 FROM base_tbl t WHERE t.id = new.id) DO INSTEAD UPDATE base_tbl SET data = new.data, deleted = FALSE WHERE id = new.id; CREATE RULE base_tbl_del_rule AS ON DELETE TO base_tbl DO INSTEAD UPDATE base_tbl SET deleted = TRUE WHERE id = old.id; CREATE VIEW rw_view1 WITH ( security_barrier = TRUE ) AS SELECT id, data FROM base_tbl WHERE NOT deleted; SELECT * FROM rw_view1; EXPLAIN ( COSTS OFF ) DELETE FROM rw_view1 WHERE id = 1 AND snoop (data); DELETE FROM rw_view1 WHERE id = 1 AND snoop (data); EXPLAIN ( COSTS OFF ) INSERT INTO rw_view1 VALUES (2, 'New row 2'); INSERT INTO rw_view1 VALUES (2, 'New row 2'); SELECT * FROM base_tbl; DROP TABLE base_tbl CASCADE; -- security barrier view based on inheritance set CREATE TABLE t1 ( a int, b float, c text ); CREATE INDEX t1_a_idx ON t1 (a); INSERT INTO t1 SELECT i, i, 't1' FROM generate_series(1, 10) g (i); ANALYZE t1; CREATE TABLE t11 ( d text ) INHERITS ( t1 ); CREATE INDEX t11_a_idx ON t11 (a); INSERT INTO t11 SELECT i, i, 't11', 't11d' FROM generate_series(1, 10) g (i); ANALYZE t11; CREATE TABLE t12 ( e int[] ) INHERITS ( t1 ); CREATE INDEX t12_a_idx ON t12 (a); INSERT INTO t12 SELECT i, i, 't12', '{1,2}'::int[] FROM generate_series(1, 10) g (i); ANALYZE t12; CREATE TABLE t111 () INHERITS ( t11, t12 ); CREATE INDEX t111_a_idx ON t111 (a); INSERT INTO t111 SELECT i, i, 't111', 't111d', '{1,1,1}'::int[] FROM generate_series(1, 10) g (i); ANALYZE t111; CREATE VIEW v1 WITH ( security_barrier = TRUE ) AS SELECT *, ( SELECT d FROM t11 WHERE t11.a = t1.a LIMIT 1 ) AS d FROM t1 WHERE a > 5 AND EXISTS ( SELECT 1 FROM t12 WHERE t12.a = t1.a); SELECT * FROM v1 WHERE a = 3; -- should not see anything SELECT * FROM v1 WHERE a = 8; EXPLAIN ( VERBOSE, COSTS OFF ) UPDATE v1 SET a = 100 WHERE snoop (a) AND LEAKPROOF (a) AND a < 7 AND a != 6; UPDATE v1 SET a = 100 WHERE snoop (a) AND LEAKPROOF (a) AND a < 7 AND a != 6; SELECT * FROM v1 WHERE a = 100; -- Nothing should have been changed to 100 SELECT * FROM t1 WHERE a = 100; -- Nothing should have been changed to 100 EXPLAIN ( VERBOSE, COSTS OFF ) UPDATE v1 SET a = a + 1 WHERE snoop (a) AND LEAKPROOF (a) AND a = 8; UPDATE v1 SET a = a + 1 WHERE snoop (a) AND LEAKPROOF (a) AND a = 8; SELECT * FROM v1 WHERE b = 8; DELETE FROM v1 WHERE snoop (a) AND LEAKPROOF (a); -- should not delete everything, just where a>5 TABLE t1; -- verify all a<=5 are intact DROP TABLE t1, t11, t12, t111 CASCADE; DROP FUNCTION snoop (anyelement); DROP FUNCTION LEAKPROOF (anyelement); CREATE TABLE tx1 ( a integer ); CREATE TABLE tx2 ( b integer ); CREATE TABLE tx3 ( c integer ); CREATE VIEW vx1 AS SELECT a FROM tx1 WHERE EXISTS ( SELECT 1 FROM tx2 JOIN tx3 ON b = c); INSERT INTO vx1 VALUES (1); SELECT * FROM tx1; SELECT * FROM vx1; DROP VIEW vx1; DROP TABLE tx1; DROP TABLE tx2; DROP TABLE tx3; CREATE TABLE tx1 ( a integer ); CREATE TABLE tx2 ( b integer ); CREATE TABLE tx3 ( c integer ); CREATE VIEW vx1 AS SELECT a FROM tx1 WHERE EXISTS ( SELECT 1 FROM tx2 JOIN tx3 ON b = c); INSERT INTO vx1 VALUES (1); INSERT INTO vx1 VALUES (1); SELECT * FROM tx1; SELECT * FROM vx1; DROP VIEW vx1; DROP TABLE tx1; DROP TABLE tx2; DROP TABLE tx3; CREATE TABLE tx1 ( a integer, b integer ); CREATE TABLE tx2 ( b integer, c integer ); CREATE TABLE tx3 ( c integer, d integer ); ALTER TABLE tx1 DROP COLUMN b; ALTER TABLE tx2 DROP COLUMN c; ALTER TABLE tx3 DROP COLUMN d; CREATE VIEW vx1 AS SELECT a FROM tx1 WHERE EXISTS ( SELECT 1 FROM tx2 JOIN tx3 ON b = c); INSERT INTO vx1 VALUES (1); INSERT INTO vx1 VALUES (1); SELECT * FROM tx1; SELECT * FROM vx1; DROP VIEW vx1; DROP TABLE tx1; DROP TABLE tx2; DROP TABLE tx3; -- -- Test handling of vars from correlated subqueries in quals from outer -- security barrier views, per bug #13988 -- CREATE TABLE t1 ( a int, b text, c int ); INSERT INTO t1 VALUES (1, 'one', 10); CREATE TABLE t2 ( cc int ); INSERT INTO t2 VALUES (10), (20); CREATE VIEW v1 WITH ( security_barrier = TRUE ) AS SELECT * FROM t1 WHERE ( a > 0 ) WITH CHECK OPTION; CREATE VIEW v2 WITH ( security_barrier = TRUE ) AS SELECT * FROM v1 WHERE EXISTS ( SELECT 1 FROM t2 WHERE t2.cc = v1.c ) WITH CHECK OPTION; INSERT INTO v2 VALUES (2, 'two', 20); -- ok INSERT INTO v2 VALUES (- 2, 'minus two', 20); -- not allowed INSERT INTO v2 VALUES (3, 'three', 30); -- not allowed UPDATE v2 SET b = 'ONE' WHERE a = 1; -- ok UPDATE v2 SET a = - 1 WHERE a = 1; -- not allowed UPDATE v2 SET c = 30 WHERE a = 1; -- not allowed DELETE FROM v2 WHERE a = 2; -- ok SELECT * FROM v2; DROP VIEW v2; DROP VIEW v1; DROP TABLE t2; DROP TABLE t1; -- -- Test CREATE OR REPLACE VIEW turning a non-updatable view into an -- auto-updatable view and adding check options in a single step -- CREATE TABLE t1 ( a int, b text ); CREATE VIEW v1 AS SELECT NULL::int AS a; CREATE OR REPLACE VIEW v1 AS SELECT * FROM t1 WHERE a > 0 WITH CHECK OPTION; INSERT INTO v1 VALUES (1, 'ok'); -- ok INSERT INTO v1 VALUES (- 1, 'invalid'); -- should fail DROP VIEW v1; DROP TABLE t1; -- check that an auto-updatable view on a partitioned table works correctly CREATE TABLE uv_pt ( a int, b int, v varchar ) PARTITION BY RANGE (a, b); CREATE TABLE uv_pt1 ( b int NOT NULL, v varchar, a int NOT NULL ) PARTITION BY RANGE (b); CREATE TABLE uv_pt11 ( LIKE uv_pt1 ); ALTER TABLE uv_pt11 DROP a; ALTER TABLE uv_pt11 ADD a int; ALTER TABLE uv_pt11 DROP a; ALTER TABLE uv_pt11 ADD a int NOT NULL; ALTER TABLE uv_pt1 ATTACH PARTITION uv_pt11 FOR VALUES FROM (2) TO (5); ALTER TABLE uv_pt ATTACH PARTITION uv_pt1 FOR VALUES FROM (1, 2) TO (1, 10); CREATE VIEW uv_ptv AS SELECT * FROM uv_pt; SELECT events & 4 != 0 AS upd, events & 8 != 0 AS ins, events & 16 != 0 AS del FROM pg_catalog.pg_relation_is_updatable ('uv_pt'::regclass, FALSE) t (events); SELECT pg_catalog.pg_column_is_updatable ('uv_pt'::regclass, 1::smallint, FALSE); SELECT pg_catalog.pg_column_is_updatable ('uv_pt'::regclass, 2::smallint, FALSE); SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name = 'uv_ptv'; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name = 'uv_ptv' ORDER BY column_name; INSERT INTO uv_ptv VALUES (1, 2); SELECT tableoid::regclass, * FROM uv_pt; CREATE VIEW uv_ptv_wco AS SELECT * FROM uv_pt WHERE a = 0 WITH CHECK option; INSERT INTO uv_ptv_wco VALUES (1, 2); DROP VIEW uv_ptv, uv_ptv_wco; DROP TABLE uv_pt, uv_pt1, uv_pt11; -- check that wholerow vars appearing in WITH CHECK OPTION constraint expressions -- work fine with partitioned tables CREATE TABLE wcowrtest ( a int ) PARTITION BY LIST (a); CREATE TABLE wcowrtest1 PARTITION OF wcowrtest FOR VALUES IN (1); CREATE VIEW wcowrtest_v AS SELECT * FROM wcowrtest WHERE wcowrtest = '(2)'::wcowrtest WITH CHECK option; INSERT INTO wcowrtest_v VALUES (1); ALTER TABLE wcowrtest ADD b text; CREATE TABLE wcowrtest2 ( b text, c int, a int ); ALTER TABLE wcowrtest2 DROP c; ALTER TABLE wcowrtest ATTACH PARTITION wcowrtest2 FOR VALUES IN (2); CREATE TABLE sometable ( a int, b text ); INSERT INTO sometable VALUES (1, 'a'), (2, 'b'); CREATE VIEW wcowrtest_v2 AS SELECT * FROM wcowrtest r WHERE r IN ( SELECT s FROM sometable s WHERE r.a = s.a ) WITH CHECK option; -- WITH CHECK qual will be processed with wcowrtest2's -- rowtype after tuple-routing INSERT INTO wcowrtest_v2 VALUES (2, 'no such row in sometable'); DROP VIEW wcowrtest_v, wcowrtest_v2; DROP TABLE wcowrtest, sometable; -- Check INSERT .. ON CONFLICT DO UPDATE works correctly when the view's -- columns are named and ordered differently than the underlying table's. CREATE TABLE uv_iocu_tab ( a text UNIQUE, b float ); INSERT INTO uv_iocu_tab VALUES ('xyxyxy', 0); CREATE VIEW uv_iocu_view AS SELECT b, b + 1 AS c, a, '2.0'::text AS two FROM uv_iocu_tab; INSERT INTO uv_iocu_view (a, b) VALUES ('xyxyxy', 1) ON CONFLICT (a) DO UPDATE SET b = uv_iocu_view.b; SELECT * FROM uv_iocu_tab; INSERT INTO uv_iocu_view (a, b) VALUES ('xyxyxy', 1) ON CONFLICT (a) DO UPDATE SET b = excluded.b; SELECT * FROM uv_iocu_tab; -- OK to access view columns that are not present in underlying base -- relation in the ON CONFLICT portion of the query INSERT INTO uv_iocu_view (a, b) VALUES ('xyxyxy', 3) ON CONFLICT (a) DO UPDATE SET b = cast(excluded.two AS float); SELECT * FROM uv_iocu_tab; EXPLAIN ( COSTS OFF ) INSERT INTO uv_iocu_view (a, b) VALUES ('xyxyxy', 3) ON CONFLICT (a) DO UPDATE SET b = excluded.b WHERE excluded.c > 0; INSERT INTO uv_iocu_view (a, b) VALUES ('xyxyxy', 3) ON CONFLICT (a) DO UPDATE SET b = excluded.b WHERE excluded.c > 0; SELECT * FROM uv_iocu_tab; DROP VIEW uv_iocu_view; DROP TABLE uv_iocu_tab; -- Test whole-row references to the view CREATE TABLE uv_iocu_tab ( a int UNIQUE, b text ); CREATE VIEW uv_iocu_view AS SELECT b AS bb, a AS aa, uv_iocu_tab::text AS cc FROM uv_iocu_tab; INSERT INTO uv_iocu_view (aa, bb) VALUES (1, 'x'); EXPLAIN ( COSTS OFF ) INSERT INTO uv_iocu_view (aa, bb) VALUES (1, 'y') ON CONFLICT (aa) DO UPDATE SET bb = 'Rejected: ' || excluded.* WHERE excluded.aa > 0 AND excluded.bb != '' AND excluded.cc IS NOT NULL; INSERT INTO uv_iocu_view (aa, bb) VALUES (1, 'y') ON CONFLICT (aa) DO UPDATE SET bb = 'Rejected: ' || excluded.* WHERE excluded.aa > 0 AND excluded.bb != '' AND excluded.cc IS NOT NULL; SELECT * FROM uv_iocu_view; -- Test omitting a column of the base relation DELETE FROM uv_iocu_view; INSERT INTO uv_iocu_view (aa, bb) VALUES (1, 'x'); INSERT INTO uv_iocu_view (aa) VALUES (1) ON CONFLICT (aa) DO UPDATE SET bb = 'Rejected: ' || excluded.*; SELECT * FROM uv_iocu_view; ALTER TABLE uv_iocu_tab ALTER COLUMN b SET DEFAULT 'table default'; INSERT INTO uv_iocu_view (aa) VALUES (1) ON CONFLICT (aa) DO UPDATE SET bb = 'Rejected: ' || excluded.*; SELECT * FROM uv_iocu_view; ALTER VIEW uv_iocu_view ALTER COLUMN bb SET DEFAULT 'view default'; INSERT INTO uv_iocu_view (aa) VALUES (1) ON CONFLICT (aa) DO UPDATE SET bb = 'Rejected: ' || excluded.*; SELECT * FROM uv_iocu_view; -- Should fail to update non-updatable columns INSERT INTO uv_iocu_view (aa) VALUES (1) ON CONFLICT (aa) DO UPDATE SET cc = 'XXX'; DROP VIEW uv_iocu_view; DROP TABLE uv_iocu_tab; -- ON CONFLICT DO UPDATE permissions checks CREATE USER regress_view_user1; CREATE USER regress_view_user2; SET session AUTHORIZATION regress_view_user1; CREATE TABLE base_tbl ( a int UNIQUE, b text, c float ); INSERT INTO base_tbl VALUES (1, 'xxx', 1.0); CREATE VIEW rw_view1 AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl; GRANT SELECT (aa, bb) ON rw_view1 TO regress_view_user2; GRANT INSERT ON rw_view1 TO regress_view_user2; GRANT UPDATE (bb) ON rw_view1 TO regress_view_user2; SET session AUTHORIZATION regress_view_user2; INSERT INTO rw_view1 VALUES ('yyy', 2.0, 1) ON CONFLICT (aa) DO UPDATE SET bb = excluded.cc; -- Not allowed INSERT INTO rw_view1 VALUES ('yyy', 2.0, 1) ON CONFLICT (aa) DO UPDATE SET bb = rw_view1.cc; -- Not allowed INSERT INTO rw_view1 VALUES ('yyy', 2.0, 1) ON CONFLICT (aa) DO UPDATE SET bb = excluded.bb; -- OK INSERT INTO rw_view1 VALUES ('zzz', 2.0, 1) ON CONFLICT (aa) DO UPDATE SET bb = rw_view1.bb || 'xxx'; -- OK INSERT INTO rw_view1 VALUES ('zzz', 2.0, 1) ON CONFLICT (aa) DO UPDATE SET cc = 3.0; -- Not allowed RESET session AUTHORIZATION; SELECT * FROM base_tbl; SET session AUTHORIZATION regress_view_user1; GRANT SELECT (a, b) ON base_tbl TO regress_view_user2; GRANT INSERT (a, b) ON base_tbl TO regress_view_user2; GRANT UPDATE (a, b) ON base_tbl TO regress_view_user2; SET session AUTHORIZATION regress_view_user2; CREATE VIEW rw_view2 AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl; INSERT INTO rw_view2 (aa, bb) VALUES (1, 'xxx') ON CONFLICT (aa) DO UPDATE SET bb = excluded.bb; -- Not allowed CREATE VIEW rw_view3 AS SELECT b AS bb, a AS aa FROM base_tbl; INSERT INTO rw_view3 (aa, bb) VALUES (1, 'xxx') ON CONFLICT (aa) DO UPDATE SET bb = excluded.bb; -- OK RESET session AUTHORIZATION; SELECT * FROM base_tbl; SET session AUTHORIZATION regress_view_user2; CREATE VIEW rw_view4 AS SELECT aa, bb, cc FROM rw_view1; INSERT INTO rw_view4 (aa, bb) VALUES (1, 'yyy') ON CONFLICT (aa) DO UPDATE SET bb = excluded.bb; -- Not allowed CREATE VIEW rw_view5 AS SELECT aa, bb FROM rw_view1; INSERT INTO rw_view5 (aa, bb) VALUES (1, 'yyy') ON CONFLICT (aa) DO UPDATE SET bb = excluded.bb; -- OK RESET session AUTHORIZATION; SELECT * FROM base_tbl; DROP VIEW rw_view5; DROP VIEW rw_view4; DROP VIEW rw_view3; DROP VIEW rw_view2; DROP VIEW rw_view1; DROP TABLE base_tbl; DROP USER regress_view_user1; DROP USER regress_view_user2; -- Test single- and multi-row inserts with table and view defaults. -- Table defaults should be used, unless overridden by view defaults. CREATE TABLE base_tab_def ( a int, b text DEFAULT 'Table default', c text DEFAULT 'Table default', d text, e text ); CREATE VIEW base_tab_def_view AS SELECT * FROM base_tab_def; ALTER VIEW base_tab_def_view ALTER b SET DEFAULT 'View default'; ALTER VIEW base_tab_def_view ALTER d SET DEFAULT 'View default'; INSERT INTO base_tab_def VALUES (1); INSERT INTO base_tab_def VALUES (2), (3); INSERT INTO base_tab_def VALUES (4, DEFAULT, DEFAULT, DEFAULT, DEFAULT); INSERT INTO base_tab_def VALUES (5, DEFAULT, DEFAULT, DEFAULT, DEFAULT), (6, DEFAULT, DEFAULT, DEFAULT, DEFAULT); INSERT INTO base_tab_def_view VALUES (11); INSERT INTO base_tab_def_view VALUES (12), (13); INSERT INTO base_tab_def_view VALUES (14, DEFAULT, DEFAULT, DEFAULT, DEFAULT); INSERT INTO base_tab_def_view VALUES (15, DEFAULT, DEFAULT, DEFAULT, DEFAULT), (16, DEFAULT, DEFAULT, DEFAULT, DEFAULT); INSERT INTO base_tab_def_view VALUES (17), (DEFAULT); SELECT * FROM base_tab_def ORDER BY a; -- Adding an INSTEAD OF trigger should cause NULLs to be inserted instead of -- table defaults, where there are no view defaults. CREATE FUNCTION base_tab_def_view_instrig_func () RETURNS TRIGGER AS $$ BEGIN INSERT INTO base_tab_def VALUES (new.a, new.b, new.c, new.d, new.e); RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER base_tab_def_view_instrig INSTEAD OF insert ON base_tab_def_view FOR EACH ROW EXECUTE FUNCTION base_tab_def_view_instrig_func (); TRUNCATE base_tab_def; INSERT INTO base_tab_def VALUES (1); INSERT INTO base_tab_def VALUES (2), (3); INSERT INTO base_tab_def VALUES (4, DEFAULT, DEFAULT, DEFAULT, DEFAULT); INSERT INTO base_tab_def VALUES (5, DEFAULT, DEFAULT, DEFAULT, DEFAULT), (6, DEFAULT, DEFAULT, DEFAULT, DEFAULT); INSERT INTO base_tab_def_view VALUES (11); INSERT INTO base_tab_def_view VALUES (12), (13); INSERT INTO base_tab_def_view VALUES (14, DEFAULT, DEFAULT, DEFAULT, DEFAULT); INSERT INTO base_tab_def_view VALUES (15, DEFAULT, DEFAULT, DEFAULT, DEFAULT), (16, DEFAULT, DEFAULT, DEFAULT, DEFAULT); INSERT INTO base_tab_def_view VALUES (17), (DEFAULT); SELECT * FROM base_tab_def ORDER BY a; -- Using an unconditional DO INSTEAD rule should also cause NULLs to be -- inserted where there are no view defaults. DROP TRIGGER base_tab_def_view_instrig ON base_tab_def_view; DROP FUNCTION base_tab_def_view_instrig_func; CREATE RULE base_tab_def_view_ins_rule AS ON INSERT TO base_tab_def_view DO INSTEAD INSERT INTO base_tab_def VALUES (new.a, new.b, new.c, new.d, new.e); TRUNCATE base_tab_def; INSERT INTO base_tab_def VALUES (1); INSERT INTO base_tab_def VALUES (2), (3); INSERT INTO base_tab_def VALUES (4, DEFAULT, DEFAULT, DEFAULT, DEFAULT); INSERT INTO base_tab_def VALUES (5, DEFAULT, DEFAULT, DEFAULT, DEFAULT), (6, DEFAULT, DEFAULT, DEFAULT, DEFAULT); INSERT INTO base_tab_def_view VALUES (11); INSERT INTO base_tab_def_view VALUES (12), (13); INSERT INTO base_tab_def_view VALUES (14, DEFAULT, DEFAULT, DEFAULT, DEFAULT); INSERT INTO base_tab_def_view VALUES (15, DEFAULT, DEFAULT, DEFAULT, DEFAULT), (16, DEFAULT, DEFAULT, DEFAULT, DEFAULT); INSERT INTO base_tab_def_view VALUES (17), (DEFAULT); SELECT * FROM base_tab_def ORDER BY a; -- A DO ALSO rule should cause each row to be inserted twice. The first -- insert should behave the same as an auto-updatable view (using table -- defaults, unless overridden by view defaults). The second insert should -- behave the same as a rule-updatable view (inserting NULLs where there are -- no view defaults). DROP RULE base_tab_def_view_ins_rule ON base_tab_def_view; CREATE RULE base_tab_def_view_ins_rule AS ON INSERT TO base_tab_def_view DO also INSERT INTO base_tab_def VALUES (new.a, new.b, new.c, new.d, new.e); TRUNCATE base_tab_def; INSERT INTO base_tab_def VALUES (1); INSERT INTO base_tab_def VALUES (2), (3); INSERT INTO base_tab_def VALUES (4, DEFAULT, DEFAULT, DEFAULT, DEFAULT); INSERT INTO base_tab_def VALUES (5, DEFAULT, DEFAULT, DEFAULT, DEFAULT), (6, DEFAULT, DEFAULT, DEFAULT, DEFAULT); INSERT INTO base_tab_def_view VALUES (11); INSERT INTO base_tab_def_view VALUES (12), (13); INSERT INTO base_tab_def_view VALUES (14, DEFAULT, DEFAULT, DEFAULT, DEFAULT); INSERT INTO base_tab_def_view VALUES (15, DEFAULT, DEFAULT, DEFAULT, DEFAULT), (16, DEFAULT, DEFAULT, DEFAULT, DEFAULT); INSERT INTO base_tab_def_view VALUES (17), (DEFAULT); SELECT * FROM base_tab_def ORDER BY a, c NULLS LAST; DROP VIEW base_tab_def_view; DROP TABLE base_tab_def; -- Test defaults with array assignments CREATE TABLE base_tab ( a serial, b int[], c text, d text DEFAULT 'Table default' ); CREATE VIEW base_tab_view AS SELECT c, a, b FROM base_tab; ALTER VIEW base_tab_view ALTER COLUMN c SET DEFAULT 'View default'; INSERT INTO base_tab_view (b[1], b[2], c, b[5], b[4], a, b[3]) VALUES (1, 2, DEFAULT, 5, 4, DEFAULT, 3), (10, 11, 'C value', 14, 13, 100, 12); SELECT * FROM base_tab ORDER BY a; DROP VIEW base_tab_view; DROP TABLE base_tab; pgFormatter-4.2/t/pg-test-files/expected/update.sql000066400000000000000000000637621361326045100224220ustar00rootroot00000000000000-- -- UPDATE syntax tests -- CREATE TABLE update_test ( a int DEFAULT 10, b int, c text ); CREATE TABLE upsert_test ( a int PRIMARY KEY, b text ); INSERT INTO update_test VALUES (5, 10, 'foo'); INSERT INTO update_test (b, a) VALUES (15, 10); SELECT * FROM update_test; UPDATE update_test SET a = DEFAULT, b = DEFAULT; SELECT * FROM update_test; -- aliases for the UPDATE target table UPDATE update_test AS t SET b = 10 WHERE t.a = 10; SELECT * FROM update_test; UPDATE update_test t SET b = t.b + 10 WHERE t.a = 10; SELECT * FROM update_test; -- -- Test VALUES in FROM -- UPDATE update_test SET a = v.i FROM ( VALUES (100, 20)) AS v (i, j) WHERE update_test.b = v.j; SELECT * FROM update_test; -- fail, wrong data type: UPDATE update_test SET a = v.* FROM ( VALUES (100, 20)) AS v (i, j) WHERE update_test.b = v.j; -- -- Test multiple-set-clause syntax -- INSERT INTO update_test SELECT a, b + 1, c FROM update_test; SELECT * FROM update_test; UPDATE update_test SET (c, b, a) = ('bugle', b + 11, DEFAULT) WHERE c = 'foo'; SELECT * FROM update_test; UPDATE update_test SET (c, b) = ('car', a + b), a = a + 1 WHERE a = 10; SELECT * FROM update_test; -- fail, multi assignment to same column: UPDATE update_test SET (c, b) = ('car', a + b), b = a + 1 WHERE a = 10; -- uncorrelated sub-select: UPDATE update_test SET (b, a) = ( SELECT a, b FROM update_test WHERE b = 41 AND c = 'car') WHERE a = 100 AND b = 20; SELECT * FROM update_test; -- correlated sub-select: UPDATE update_test o SET (b, a) = ( SELECT a + 1, b FROM update_test i WHERE i.a = o.a AND i.b = o.b AND i.c IS NOT DISTINCT FROM o.c); SELECT * FROM update_test; -- fail, multiple rows supplied: UPDATE update_test SET (b, a) = ( SELECT a + 1, b FROM update_test); -- set to null if no rows supplied: UPDATE update_test SET (b, a) = ( SELECT a + 1, b FROM update_test WHERE a = 1000) WHERE a = 11; SELECT * FROM update_test; -- *-expansion should work in this context: UPDATE update_test SET (a, b) = ROW (v.*) FROM ( VALUES (21, 100)) AS v (i, j) WHERE update_test.a = v.i; -- you might expect this to work, but syntactically it's not a RowExpr: UPDATE update_test SET (a, b) = (v.*) FROM ( VALUES (21, 101)) AS v (i, j) WHERE update_test.a = v.i; -- if an alias for the target table is specified, don't allow references -- to the original table name UPDATE update_test AS t SET b = update_test.b + 10 WHERE t.a = 10; -- Make sure that we can update to a TOASTed value. UPDATE update_test SET c = repeat('x', 10000) WHERE c = 'car'; SELECT a, b, char_length(c) FROM update_test; -- Check multi-assignment with a Result node to handle a one-time filter. EXPLAIN ( VERBOSE, COSTS OFF ) UPDATE update_test t SET (a, b) = ( SELECT b, a FROM update_test s WHERE s.a = t.a) WHERE CURRENT_USER = SESSION_USER; UPDATE update_test t SET (a, b) = ( SELECT b, a FROM update_test s WHERE s.a = t.a) WHERE CURRENT_USER = SESSION_USER; SELECT a, b, char_length(c) FROM update_test; -- Test ON CONFLICT DO UPDATE INSERT INTO upsert_test VALUES (1, 'Boo'); -- uncorrelated sub-select: WITH aaa AS ( SELECT 1 AS a, 'Foo' AS b) INSERT INTO upsert_test VALUES (1, 'Bar') ON CONFLICT (a) DO UPDATE SET (b, a) = ( SELECT b, a FROM aaa) RETURNING *; -- correlated sub-select: INSERT INTO upsert_test VALUES (1, 'Baz') ON CONFLICT (a) DO UPDATE SET (b, a) = ( SELECT b || ', Correlated', a FROM upsert_test i WHERE i.a = upsert_test.a) RETURNING *; -- correlated sub-select (EXCLUDED.* alias): INSERT INTO upsert_test VALUES (1, 'Bat') ON CONFLICT (a) DO UPDATE SET (b, a) = ( SELECT b || ', Excluded', a FROM upsert_test i WHERE i.a = excluded.a) RETURNING *; DROP TABLE update_test; DROP TABLE upsert_test; --------------------------- -- UPDATE with row movement --------------------------- -- When a partitioned table receives an UPDATE to the partitioned key and the -- new values no longer meet the partition's bound, the row must be moved to -- the correct partition for the new partition key (if one exists). We must -- also ensure that updatable views on partitioned tables properly enforce any -- WITH CHECK OPTION that is defined. The situation with triggers in this case -- also requires thorough testing as partition key updates causing row -- movement convert UPDATEs into DELETE+INSERT. CREATE TABLE range_parted ( a text, b bigint, c numeric, d int, e varchar ) PARTITION BY RANGE (a, b); -- Create partitions intentionally in descending bound order, so as to test -- that update-row-movement works with the leaf partitions not in bound order. CREATE TABLE part_b_20_b_30 ( e varchar, c numeric, a text, b bigint, d int ); ALTER TABLE range_parted ATTACH PARTITION part_b_20_b_30 FOR VALUES FROM ('b', 20) TO ('b', 30); CREATE TABLE part_b_10_b_20 ( e varchar, c numeric, a text, b bigint, d int ) PARTITION BY RANGE (c); CREATE TABLE part_b_1_b_10 PARTITION OF range_parted FOR VALUES FROM ('b', 1) TO ('b', 10); ALTER TABLE range_parted ATTACH PARTITION part_b_10_b_20 FOR VALUES FROM ('b', 10) TO ('b', 20); CREATE TABLE part_a_10_a_20 PARTITION OF range_parted FOR VALUES FROM ('a', 10) TO ('a', 20); CREATE TABLE part_a_1_a_10 PARTITION OF range_parted FOR VALUES FROM ('a', 1) TO ('a', 10); -- Check that partition-key UPDATE works sanely on a partitioned table that -- does not have any child partitions. UPDATE part_b_10_b_20 SET b = b - 6; -- Create some more partitions following the above pattern of descending bound -- order, but let's make the situation a bit more complex by having the -- attribute numbers of the columns vary from their parent partition. CREATE TABLE part_c_100_200 ( e varchar, c numeric, a text, b bigint, d int ) PARTITION BY RANGE (abs(d)); ALTER TABLE part_c_100_200 DROP COLUMN e, DROP COLUMN c, DROP COLUMN a; ALTER TABLE part_c_100_200 ADD COLUMN c numeric, ADD COLUMN e varchar, ADD COLUMN a text; ALTER TABLE part_c_100_200 DROP COLUMN b; ALTER TABLE part_c_100_200 ADD COLUMN b bigint; CREATE TABLE part_d_1_15 PARTITION OF part_c_100_200 FOR VALUES FROM (1) TO (15); CREATE TABLE part_d_15_20 PARTITION OF part_c_100_200 FOR VALUES FROM (15) TO (20); ALTER TABLE part_b_10_b_20 ATTACH PARTITION part_c_100_200 FOR VALUES FROM (100) TO (200); CREATE TABLE part_c_1_100 ( e varchar, d int, c numeric, b bigint, a text ); ALTER TABLE part_b_10_b_20 ATTACH PARTITION part_c_1_100 FOR VALUES FROM (1) TO (100); \set init_range_parted 'truncate range_parted; insert into range_parted VALUES (''a'', 1, 1, 1), (''a'', 10, 200, 1), (''b'', 12, 96, 1), (''b'', 13, 97, 2), (''b'', 15, 105, 16), (''b'', 17, 105, 19)' \set show_data 'select tableoid::regclass::text COLLATE "C" partname, * from range_parted ORDER BY 1, 2, 3, 4, 5, 6' :init_range_parted; :show_data; -- The order of subplans should be in bound order EXPLAIN ( COSTS OFF ) UPDATE range_parted SET c = c - 50 WHERE c > 97; -- fail, row movement happens only within the partition subtree. UPDATE part_c_100_200 SET c = c - 20, d = c WHERE c = 105; -- fail, no partition key update, so no attempt to move tuple, -- but "a = 'a'" violates partition constraint enforced by root partition) UPDATE part_b_10_b_20 SET a = 'a'; -- ok, partition key update, no constraint violation UPDATE range_parted SET d = d - 10 WHERE d > 10; -- ok, no partition key update, no constraint violation UPDATE range_parted SET e = d; -- No row found UPDATE part_c_1_100 SET c = c + 20 WHERE c = 98; -- ok, row movement UPDATE part_b_10_b_20 SET c = c + 20 RETURNING c, b, a; :show_data; -- fail, row movement happens only within the partition subtree. UPDATE part_b_10_b_20 SET b = b - 6 WHERE c > 116 RETURNING *; -- ok, row movement, with subset of rows moved into different partition. UPDATE range_parted SET b = b - 6 WHERE c > 116 RETURNING a, b + c; :show_data; -- Common table needed for multiple test scenarios. CREATE TABLE mintab ( c1 int ); INSERT INTO mintab VALUES (120); -- update partition key using updatable view. CREATE VIEW upview AS SELECT * FROM range_parted WHERE ( SELECT c > c1 FROM mintab ) WITH CHECK OPTION; -- ok UPDATE upview SET c = 199 WHERE b = 4; -- fail, check option violation UPDATE upview SET c = 120 WHERE b = 4; -- fail, row movement with check option violation UPDATE upview SET a = 'b', b = 15, c = 120 WHERE b = 4; -- ok, row movement, check option passes UPDATE upview SET a = 'b', b = 15 WHERE b = 4; :show_data; -- cleanup DROP VIEW upview; -- RETURNING having whole-row vars. :init_range_parted; UPDATE range_parted SET c = 95 WHERE a = 'b' AND b > 10 AND c > 100 RETURNING (range_parted), *; :show_data; -- Transition tables with update row movement :init_range_parted; CREATE FUNCTION trans_updatetrigfunc () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RAISE notice 'trigger = %, old table = %, new table = %', TG_NAME, ( SELECT string_agg(old_table::text, ', ' ORDER BY a) FROM old_table), ( SELECT string_agg(new_table::text, ', ' ORDER BY a) FROM new_table); RETURN NULL; END; $$; CREATE TRIGGER trans_updatetrig AFTER UPDATE ON range_parted REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table FOR EACH STATEMENT EXECUTE PROCEDURE trans_updatetrigfunc (); UPDATE range_parted SET c = ( CASE WHEN c = 96 THEN 110 ELSE c + 1 END) WHERE a = 'b' AND b > 10 AND c >= 96; :show_data; :init_range_parted; -- Enabling OLD TABLE capture for both DELETE as well as UPDATE stmt triggers -- should not cause DELETEd rows to be captured twice. Similar thing for -- INSERT triggers and inserted rows. CREATE TRIGGER trans_deletetrig AFTER DELETE ON range_parted REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE PROCEDURE trans_updatetrigfunc (); CREATE TRIGGER trans_inserttrig AFTER INSERT ON range_parted REFERENCING NEW TABLE AS new_table FOR EACH STATEMENT EXECUTE PROCEDURE trans_updatetrigfunc (); UPDATE range_parted SET c = c + 50 WHERE a = 'b' AND b > 10 AND c >= 96; :show_data; DROP TRIGGER trans_deletetrig ON range_parted; DROP TRIGGER trans_inserttrig ON range_parted; -- Don't drop trans_updatetrig yet. It is required below. -- Test with transition tuple conversion happening for rows moved into the -- new partition. This requires a trigger that references transition table -- (we already have trans_updatetrig). For inserted rows, the conversion -- is not usually needed, because the original tuple is already compatible with -- the desired transition tuple format. But conversion happens when there is a -- BR trigger because the trigger can change the inserted row. So install a -- BR triggers on those child partitions where the rows will be moved. CREATE FUNCTION func_parted_mod_b () RETURNS TRIGGER AS $$ BEGIN NEW.b = NEW.b + 1; RETURN NEW; END $$ LANGUAGE plpgsql; CREATE TRIGGER trig_c1_100 BEFORE UPDATE OR INSERT ON part_c_1_100 FOR EACH ROW EXECUTE PROCEDURE func_parted_mod_b (); CREATE TRIGGER trig_d1_15 BEFORE UPDATE OR INSERT ON part_d_1_15 FOR EACH ROW EXECUTE PROCEDURE func_parted_mod_b (); CREATE TRIGGER trig_d15_20 BEFORE UPDATE OR INSERT ON part_d_15_20 FOR EACH ROW EXECUTE PROCEDURE func_parted_mod_b (); :init_range_parted; UPDATE range_parted SET c = ( CASE WHEN c = 96 THEN 110 ELSE c + 1 END) WHERE a = 'b' AND b > 10 AND c >= 96; :show_data; :init_range_parted; UPDATE range_parted SET c = c + 50 WHERE a = 'b' AND b > 10 AND c >= 96; :show_data; -- Case where per-partition tuple conversion map array is allocated, but the -- map is not required for the particular tuple that is routed, thanks to -- matching table attributes of the partition and the target table. :init_range_parted; UPDATE range_parted SET b = 15 WHERE b = 1; :show_data; DROP TRIGGER trans_updatetrig ON range_parted; DROP TRIGGER trig_c1_100 ON part_c_1_100; DROP TRIGGER trig_d1_15 ON part_d_1_15; DROP TRIGGER trig_d15_20 ON part_d_15_20; DROP FUNCTION func_parted_mod_b (); -- RLS policies with update-row-movement ----------------------------------------- ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY; CREATE USER regress_range_parted_user; GRANT ALL ON range_parted, mintab TO regress_range_parted_user; CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (TRUE); CREATE POLICY policy_range_parted ON range_parted FOR UPDATE USING (TRUE) WITH CHECK (c % 2 = 0); :init_range_parted; SET SESSION AUTHORIZATION regress_range_parted_user; -- This should fail with RLS violation error while moving row from -- part_a_10_a_20 to part_d_1_15, because we are setting 'c' to an odd number. UPDATE range_parted SET a = 'b', c = 151 WHERE a = 'a' AND c = 200; RESET SESSION AUTHORIZATION; -- Create a trigger on part_d_1_15 CREATE FUNCTION func_d_1_15 () RETURNS TRIGGER AS $$ BEGIN NEW.c = NEW.c + 1; -- Make even numbers odd, or vice versa RETURN NEW; END $$ LANGUAGE plpgsql; CREATE TRIGGER trig_d_1_15 BEFORE INSERT ON part_d_1_15 FOR EACH ROW EXECUTE PROCEDURE func_d_1_15 (); :init_range_parted; SET SESSION AUTHORIZATION regress_range_parted_user; -- Here, RLS checks should succeed while moving row from part_a_10_a_20 to -- part_d_1_15. Even though the UPDATE is setting 'c' to an odd number, the -- trigger at the destination partition again makes it an even number. UPDATE range_parted SET a = 'b', c = 151 WHERE a = 'a' AND c = 200; RESET SESSION AUTHORIZATION; :init_range_parted; SET SESSION AUTHORIZATION regress_range_parted_user; -- This should fail with RLS violation error. Even though the UPDATE is setting -- 'c' to an even number, the trigger at the destination partition again makes -- it an odd number. UPDATE range_parted SET a = 'b', c = 150 WHERE a = 'a' AND c = 200; -- Cleanup RESET SESSION AUTHORIZATION; DROP TRIGGER trig_d_1_15 ON part_d_1_15; DROP FUNCTION func_d_1_15 (); -- Policy expression contains SubPlan RESET SESSION AUTHORIZATION; :init_range_parted; CREATE POLICY policy_range_parted_subplan ON range_parted AS RESTRICTIVE FOR UPDATE USING (TRUE) WITH CHECK ((SELECT range_parted.c <= c1 FROM mintab)); SET SESSION AUTHORIZATION regress_range_parted_user; -- fail, mintab has row with c1 = 120 UPDATE range_parted SET a = 'b', c = 122 WHERE a = 'a' AND c = 200; -- ok UPDATE range_parted SET a = 'b', c = 120 WHERE a = 'a' AND c = 200; -- RLS policy expression contains whole row. RESET SESSION AUTHORIZATION; :init_range_parted; CREATE POLICY policy_range_parted_wholerow ON range_parted AS RESTRICTIVE FOR UPDATE USING (TRUE) WITH CHECK (range_parted = ROW ('b', 10, 112, 1, NULL)::range_parted); SET SESSION AUTHORIZATION regress_range_parted_user; -- ok, should pass the RLS check UPDATE range_parted SET a = 'b', c = 112 WHERE a = 'a' AND c = 200; RESET SESSION AUTHORIZATION; :init_range_parted; SET SESSION AUTHORIZATION regress_range_parted_user; -- fail, the whole row RLS check should fail UPDATE range_parted SET a = 'b', c = 116 WHERE a = 'a' AND c = 200; -- Cleanup RESET SESSION AUTHORIZATION; DROP POLICY policy_range_parted ON range_parted; DROP POLICY policy_range_parted_subplan ON range_parted; DROP POLICY policy_range_parted_wholerow ON range_parted; REVOKE ALL ON range_parted, mintab FROM regress_range_parted_user; DROP USER regress_range_parted_user; DROP TABLE mintab; -- statement triggers with update row movement --------------------------------------------------- :init_range_parted; CREATE FUNCTION trigfunc () RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RAISE notice 'trigger = % fired on table % during %', TG_NAME, TG_TABLE_NAME, TG_OP; RETURN NULL; END; $$; -- Triggers on root partition CREATE TRIGGER parent_delete_trig AFTER DELETE ON range_parted FOR EACH statement EXECUTE PROCEDURE trigfunc (); CREATE TRIGGER parent_update_trig AFTER UPDATE ON range_parted FOR EACH statement EXECUTE PROCEDURE trigfunc (); CREATE TRIGGER parent_insert_trig AFTER INSERT ON range_parted FOR EACH statement EXECUTE PROCEDURE trigfunc (); -- Triggers on leaf partition part_c_1_100 CREATE TRIGGER c1_delete_trig AFTER DELETE ON part_c_1_100 FOR EACH statement EXECUTE PROCEDURE trigfunc (); CREATE TRIGGER c1_update_trig AFTER UPDATE ON part_c_1_100 FOR EACH statement EXECUTE PROCEDURE trigfunc (); CREATE TRIGGER c1_insert_trig AFTER INSERT ON part_c_1_100 FOR EACH statement EXECUTE PROCEDURE trigfunc (); -- Triggers on leaf partition part_d_1_15 CREATE TRIGGER d1_delete_trig AFTER DELETE ON part_d_1_15 FOR EACH statement EXECUTE PROCEDURE trigfunc (); CREATE TRIGGER d1_update_trig AFTER UPDATE ON part_d_1_15 FOR EACH statement EXECUTE PROCEDURE trigfunc (); CREATE TRIGGER d1_insert_trig AFTER INSERT ON part_d_1_15 FOR EACH statement EXECUTE PROCEDURE trigfunc (); -- Triggers on leaf partition part_d_15_20 CREATE TRIGGER d15_delete_trig AFTER DELETE ON part_d_15_20 FOR EACH statement EXECUTE PROCEDURE trigfunc (); CREATE TRIGGER d15_update_trig AFTER UPDATE ON part_d_15_20 FOR EACH statement EXECUTE PROCEDURE trigfunc (); CREATE TRIGGER d15_insert_trig AFTER INSERT ON part_d_15_20 FOR EACH statement EXECUTE PROCEDURE trigfunc (); -- Move all rows from part_c_100_200 to part_c_1_100. None of the delete or -- insert statement triggers should be fired. UPDATE range_parted SET c = c - 50 WHERE c > 97; :show_data; DROP TRIGGER parent_delete_trig ON range_parted; DROP TRIGGER parent_update_trig ON range_parted; DROP TRIGGER parent_insert_trig ON range_parted; DROP TRIGGER c1_delete_trig ON part_c_1_100; DROP TRIGGER c1_update_trig ON part_c_1_100; DROP TRIGGER c1_insert_trig ON part_c_1_100; DROP TRIGGER d1_delete_trig ON part_d_1_15; DROP TRIGGER d1_update_trig ON part_d_1_15; DROP TRIGGER d1_insert_trig ON part_d_1_15; DROP TRIGGER d15_delete_trig ON part_d_15_20; DROP TRIGGER d15_update_trig ON part_d_15_20; DROP TRIGGER d15_insert_trig ON part_d_15_20; -- Creating default partition for range :init_range_parted; CREATE TABLE part_def PARTITION OF range_parted DEFAULT; \d+ part_def INSERT INTO range_parted VALUES ('c', 9); -- ok UPDATE part_def SET a = 'd' WHERE a = 'c'; -- fail UPDATE part_def SET a = 'a' WHERE a = 'd'; :show_data; -- Update row movement from non-default to default partition. -- fail, default partition is not under part_a_10_a_20; UPDATE part_a_10_a_20 SET a = 'ad' WHERE a = 'a'; -- ok UPDATE range_parted SET a = 'ad' WHERE a = 'a'; UPDATE range_parted SET a = 'bd' WHERE a = 'b'; :show_data; -- Update row movement from default to non-default partitions. -- ok UPDATE range_parted SET a = 'a' WHERE a = 'ad'; UPDATE range_parted SET a = 'b' WHERE a = 'bd'; :show_data; -- Cleanup: range_parted no longer needed. DROP TABLE range_parted; CREATE TABLE list_parted ( a text, b int ) PARTITION BY LIST (a); CREATE TABLE list_part1 PARTITION OF list_parted FOR VALUES IN ('a', 'b'); CREATE TABLE list_default PARTITION OF list_parted DEFAULT; INSERT INTO list_part1 VALUES ('a', 1); INSERT INTO list_default VALUES ('d', 10); -- fail UPDATE list_default SET a = 'a' WHERE a = 'd'; -- ok UPDATE list_default SET a = 'x' WHERE a = 'd'; DROP TABLE list_parted; -------------- -- Some more update-partition-key test scenarios below. This time use list -- partitions. -------------- -- Setup for list partitions CREATE TABLE list_parted ( a numeric, b int, c int8 ) PARTITION BY LIST (a); CREATE TABLE sub_parted PARTITION OF list_parted FOR VALUES IN (1) PARTITION BY LIST (b); CREATE TABLE sub_part1 ( b int, c int8, a numeric ); ALTER TABLE sub_parted ATTACH PARTITION sub_part1 FOR VALUES IN (1); CREATE TABLE sub_part2 ( b int, c int8, a numeric ); ALTER TABLE sub_parted ATTACH PARTITION sub_part2 FOR VALUES IN (2); CREATE TABLE list_part1 ( a numeric, b int, c int8 ); ALTER TABLE list_parted ATTACH PARTITION list_part1 FOR VALUES IN (2, 3); INSERT INTO list_parted VALUES (2, 5, 50); INSERT INTO list_parted VALUES (3, 6, 60); INSERT INTO sub_parted VALUES (1, 1, 60); INSERT INTO sub_parted VALUES (1, 2, 10); -- Test partition constraint violation when intermediate ancestor is used and -- constraint is inherited from upper root. UPDATE sub_parted SET a = 2 WHERE c = 10; -- Test update-partition-key, where the unpruned partitions do not have their -- partition keys updated. SELECT tableoid::regclass::text, * FROM list_parted WHERE a = 2 ORDER BY 1; UPDATE list_parted SET b = c + a WHERE a = 2; SELECT tableoid::regclass::text, * FROM list_parted WHERE a = 2 ORDER BY 1; -- Test the case where BR UPDATE triggers change the partition key. CREATE FUNCTION func_parted_mod_b () RETURNS TRIGGER AS $$ BEGIN NEW.b = 2; -- This is changing partition key column. RETURN NEW; END $$ LANGUAGE plpgsql; CREATE TRIGGER parted_mod_b BEFORE UPDATE ON sub_part1 FOR EACH ROW EXECUTE PROCEDURE func_parted_mod_b (); SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; -- This should do the tuple routing even though there is no explicit -- partition-key update, because there is a trigger on sub_part1. UPDATE list_parted SET c = 70 WHERE b = 1; SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; DROP TRIGGER parted_mod_b ON sub_part1; -- If BR DELETE trigger prevented DELETE from happening, we should also skip -- the INSERT if that delete is part of UPDATE=>DELETE+INSERT. CREATE OR REPLACE FUNCTION func_parted_mod_b () RETURNS TRIGGER AS $$ BEGIN RAISE notice 'Trigger: Got OLD row %, but returning NULL', OLD; RETURN NULL; END $$ LANGUAGE plpgsql; CREATE TRIGGER trig_skip_delete BEFORE DELETE ON sub_part2 FOR EACH ROW EXECUTE PROCEDURE func_parted_mod_b (); UPDATE list_parted SET b = 1 WHERE c = 70; SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; -- Drop the trigger. Now the row should be moved. DROP TRIGGER trig_skip_delete ON sub_part2; UPDATE list_parted SET b = 1 WHERE c = 70; SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; DROP FUNCTION func_parted_mod_b (); -- UPDATE partition-key with FROM clause. If join produces multiple output -- rows for the same row to be modified, we should tuple-route the row only -- once. There should not be any rows inserted. CREATE TABLE non_parted ( id int ); INSERT INTO non_parted VALUES (1), (1), (1), (2), (2), (2), (3), (3), (3); UPDATE list_parted t1 SET a = 2 FROM non_parted t2 WHERE t1.a = t2.id AND a = 1; SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; DROP TABLE non_parted; -- Cleanup: list_parted no longer needed. DROP TABLE list_parted; -- create custom operator class and hash function, for the same reason -- explained in alter_table.sql CREATE OR REPLACE FUNCTION dummy_hashint4 (a int4, seed int8) RETURNS int8 AS $$ BEGIN RETURN (a + seed); END; $$ LANGUAGE 'plpgsql' IMMUTABLE; CREATE OPERATOR class custom_opclass FOR TYPE int4 USING HASH AS OPERATOR 1 =, FUNCTION 2 dummy_hashint4 (int4, int8 ); CREATE TABLE hash_parted ( a int, b int ) PARTITION BY HASH (a custom_opclass, b custom_opclass); CREATE TABLE hpart1 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 2, REMAINDER 1); CREATE TABLE hpart2 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 4, REMAINDER 2); CREATE TABLE hpart3 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 8, REMAINDER 0); CREATE TABLE hpart4 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 8, REMAINDER 4); INSERT INTO hpart1 VALUES (1, 1); INSERT INTO hpart2 VALUES (2, 5); INSERT INTO hpart4 VALUES (3, 4); -- fail UPDATE hpart1 SET a = 3, b = 4 WHERE a = 1; -- ok, row movement UPDATE hash_parted SET b = b - 1 WHERE b = 1; -- ok UPDATE hash_parted SET b = b + 8 WHERE b = 1; -- cleanup DROP TABLE hash_parted; DROP OPERATOR class custom_opclass USING HASH; DROP FUNCTION dummy_hashint4 (a int4, seed int8); pgFormatter-4.2/t/pg-test-files/expected/uuid.sql000066400000000000000000000062641361326045100221000ustar00rootroot00000000000000-- regression test for the uuid datatype -- creating test tables CREATE TABLE guid1 ( guid_field uuid, text_field text DEFAULT (now()) ); CREATE TABLE guid2 ( guid_field uuid, text_field text DEFAULT (now()) ); -- inserting invalid data tests -- too long INSERT INTO guid1 (guid_field) VALUES ('11111111-1111-1111-1111-111111111111F'); -- too short INSERT INTO guid1 (guid_field) VALUES ('{11111111-1111-1111-1111-11111111111}'); -- valid data but invalid format INSERT INTO guid1 (guid_field) VALUES ('111-11111-1111-1111-1111-111111111111'); INSERT INTO guid1 (guid_field) VALUES ('{22222222-2222-2222-2222-222222222222 '); -- invalid data INSERT INTO guid1 (guid_field) VALUES ('11111111-1111-1111-G111-111111111111'); INSERT INTO guid1 (guid_field) VALUES ('11+11111-1111-1111-1111-111111111111'); --inserting three input formats INSERT INTO guid1 (guid_field) VALUES ('11111111-1111-1111-1111-111111111111'); INSERT INTO guid1 (guid_field) VALUES ('{22222222-2222-2222-2222-222222222222}'); INSERT INTO guid1 (guid_field) VALUES ('3f3e3c3b3a3039383736353433a2313e'); -- retrieving the inserted data SELECT guid_field FROM guid1; -- ordering test SELECT guid_field FROM guid1 ORDER BY guid_field ASC; SELECT guid_field FROM guid1 ORDER BY guid_field DESC; -- = operator test SELECT COUNT(*) FROM guid1 WHERE guid_field = '3f3e3c3b-3a30-3938-3736-353433a2313e'; -- <> operator test SELECT COUNT(*) FROM guid1 WHERE guid_field <> '11111111111111111111111111111111'; -- < operator test SELECT COUNT(*) FROM guid1 WHERE guid_field < '22222222-2222-2222-2222-222222222222'; -- <= operator test SELECT COUNT(*) FROM guid1 WHERE guid_field <= '22222222-2222-2222-2222-222222222222'; -- > operator test SELECT COUNT(*) FROM guid1 WHERE guid_field > '22222222-2222-2222-2222-222222222222'; -- >= operator test SELECT COUNT(*) FROM guid1 WHERE guid_field >= '22222222-2222-2222-2222-222222222222'; -- btree and hash index creation test CREATE INDEX guid1_btree ON guid1 USING BTREE (guid_field); CREATE INDEX guid1_hash ON guid1 USING HASH (guid_field); -- unique index test CREATE UNIQUE INDEX guid1_unique_BTREE ON guid1 USING BTREE (guid_field); -- should fail INSERT INTO guid1 (guid_field) VALUES ('11111111-1111-1111-1111-111111111111'); -- check to see whether the new indexes are actually there SELECT count(*) FROM pg_class WHERE relkind = 'i' AND relname LIKE 'guid%'; -- populating the test tables with additional records INSERT INTO guid1 (guid_field) VALUES ('44444444-4444-4444-4444-444444444444'); INSERT INTO guid2 (guid_field) VALUES ('11111111-1111-1111-1111-111111111111'); INSERT INTO guid2 (guid_field) VALUES ('{22222222-2222-2222-2222-222222222222}'); INSERT INTO guid2 (guid_field) VALUES ('3f3e3c3b3a3039383736353433a2313e'); -- join test SELECT COUNT(*) FROM guid1 g1 INNER JOIN guid2 g2 ON g1.guid_field = g2.guid_field; SELECT COUNT(*) FROM guid1 g1 LEFT JOIN guid2 g2 ON g1.guid_field = g2.guid_field WHERE g2.guid_field IS NULL; -- clean up DROP TABLE guid1, guid2 CASCADE; pgFormatter-4.2/t/pg-test-files/expected/vacuum.sql000066400000000000000000000152571361326045100224340ustar00rootroot00000000000000-- -- VACUUM -- CREATE TABLE vactst ( i int ); INSERT INTO vactst VALUES (1); INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst VALUES (0); SELECT count(*) FROM vactst; DELETE FROM vactst WHERE i != 0; SELECT * FROM vactst; VACUUM FULL vactst; UPDATE vactst SET i = i + 1; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst VALUES (0); SELECT count(*) FROM vactst; DELETE FROM vactst WHERE i != 0; VACUUM ( FULL) vactst; DELETE FROM vactst; SELECT * FROM vactst; VACUUM ( FULL, FREEZE) vactst; VACUUM (ANALYZE, FULL) vactst; CREATE TABLE vaccluster ( i int PRIMARY KEY ); ALTER TABLE vaccluster CLUSTER ON vaccluster_pkey; CLUSTER vaccluster; CREATE FUNCTION do_analyze () RETURNS VOID VOLATILE LANGUAGE SQL AS 'ANALYZE pg_am' ; CREATE FUNCTION wrap_do_analyze (c int) RETURNS INT IMMUTABLE LANGUAGE SQL AS 'SELECT $1 FROM do_analyze()' ; CREATE INDEX ON vaccluster (wrap_do_analyze (i)); INSERT INTO vaccluster VALUES (1), (2); ANALYZE vaccluster; VACUUM FULL pg_am; VACUUM FULL pg_class; VACUUM FULL pg_database; VACUUM FULL vaccluster; VACUUM FULL vactst; VACUUM (DISABLE_PAGE_SKIPPING) vaccluster; -- INDEX_CLEANUP option CREATE TABLE no_index_cleanup ( i int PRIMARY KEY ) WITH ( vacuum_index_cleanup = FALSE ); VACUUM (INDEX_CLEANUP FALSE) vaccluster; VACUUM (INDEX_CLEANUP FALSE) vactst; -- index cleanup option is ignored if no indexes VACUUM (INDEX_CLEANUP FALSE, FREEZE TRUE) vaccluster; -- index cleanup option is ignored if VACUUM FULL VACUUM (INDEX_CLEANUP TRUE, FULL TRUE) no_index_cleanup; VACUUM ( FULL TRUE) no_index_cleanup; -- partitioned table CREATE TABLE vacparted ( a int, b char ) PARTITION BY LIST (a); CREATE TABLE vacparted1 PARTITION OF vacparted FOR VALUES IN (1); INSERT INTO vacparted VALUES (1, 'a'); UPDATE vacparted SET b = 'b'; VACUUM (ANALYZE) vacparted; VACUUM ( FULL) vacparted; VACUUM (FREEZE) vacparted; -- check behavior with duplicate column mentions VACUUM ANALYZE vacparted (a, b, a); ANALYZE vacparted (a, b, b); -- multiple tables specified VACUUM vaccluster, vactst; VACUUM vacparted, does_not_exist; VACUUM (FREEZE) vacparted, vaccluster, vactst; VACUUM (FREEZE) does_not_exist, vaccluster; VACUUM ANALYZE vactst, vacparted (a); VACUUM ANALYZE vactst (does_not_exist), vacparted (b); VACUUM FULL vacparted, vactst; VACUUM FULL vactst, vacparted (a, b), vaccluster (i); ANALYZE vactst, vacparted; ANALYZE vacparted (b), vactst; ANALYZE vactst, does_not_exist, vacparted; ANALYZE vactst (i), vacparted (does_not_exist); -- parenthesized syntax for ANALYZE ANALYZE (VERBOSE) does_not_exist; ANALYZE (nonexistent - arg) does_not_exist; ANALYZE (nonexistentarg) does_not_exit; -- ensure argument order independence, and that SKIP_LOCKED on non-existing -- relation still errors out. ANALYZE (SKIP_LOCKED, VERBOSE) does_not_exist; ANALYZE (VERBOSE, SKIP_LOCKED) does_not_exist; -- SKIP_LOCKED option VACUUM (SKIP_LOCKED) vactst; VACUUM (SKIP_LOCKED, FULL) vactst; ANALYZE (SKIP_LOCKED) vactst; DROP TABLE vaccluster; DROP TABLE vactst; DROP TABLE vacparted; DROP TABLE no_index_cleanup; -- relation ownership, WARNING logs generated as all are skipped. CREATE TABLE vacowned ( a int ); CREATE TABLE vacowned_parted ( a int ) PARTITION BY LIST (a); CREATE TABLE vacowned_part1 PARTITION OF vacowned_parted FOR VALUES IN (1); CREATE TABLE vacowned_part2 PARTITION OF vacowned_parted FOR VALUES IN (2); CREATE ROLE regress_vacuum; SET ROLE regress_vacuum; -- Simple table VACUUM vacowned; ANALYZE vacowned; VACUUM (ANALYZE) vacowned; -- Catalog VACUUM pg_catalog.pg_class; ANALYZE pg_catalog.pg_class; VACUUM (ANALYZE) pg_catalog.pg_class; -- Shared catalog VACUUM pg_catalog.pg_authid; ANALYZE pg_catalog.pg_authid; VACUUM (ANALYZE) pg_catalog.pg_authid; -- Partitioned table and its partitions, nothing owned by other user. -- Relations are not listed in a single command to test ownership -- independently. VACUUM vacowned_parted; VACUUM vacowned_part1; VACUUM vacowned_part2; ANALYZE vacowned_parted; ANALYZE vacowned_part1; ANALYZE vacowned_part2; VACUUM (ANALYZE) vacowned_parted; VACUUM (ANALYZE) vacowned_part1; VACUUM (ANALYZE) vacowned_part2; RESET ROLE; -- Partitioned table and one partition owned by other user. ALTER TABLE vacowned_parted OWNER TO regress_vacuum; ALTER TABLE vacowned_part1 OWNER TO regress_vacuum; SET ROLE regress_vacuum; VACUUM vacowned_parted; VACUUM vacowned_part1; VACUUM vacowned_part2; ANALYZE vacowned_parted; ANALYZE vacowned_part1; ANALYZE vacowned_part2; VACUUM (ANALYZE) vacowned_parted; VACUUM (ANALYZE) vacowned_part1; VACUUM (ANALYZE) vacowned_part2; RESET ROLE; -- Only one partition owned by other user. ALTER TABLE vacowned_parted OWNER TO CURRENT_USER; SET ROLE regress_vacuum; VACUUM vacowned_parted; VACUUM vacowned_part1; VACUUM vacowned_part2; ANALYZE vacowned_parted; ANALYZE vacowned_part1; ANALYZE vacowned_part2; VACUUM (ANALYZE) vacowned_parted; VACUUM (ANALYZE) vacowned_part1; VACUUM (ANALYZE) vacowned_part2; RESET ROLE; -- Only partitioned table owned by other user. ALTER TABLE vacowned_parted OWNER TO regress_vacuum; ALTER TABLE vacowned_part1 OWNER TO CURRENT_USER; SET ROLE regress_vacuum; VACUUM vacowned_parted; VACUUM vacowned_part1; VACUUM vacowned_part2; ANALYZE vacowned_parted; ANALYZE vacowned_part1; ANALYZE vacowned_part2; VACUUM (ANALYZE) vacowned_parted; VACUUM (ANALYZE) vacowned_part1; VACUUM (ANALYZE) vacowned_part2; RESET ROLE; DROP TABLE vacowned; DROP TABLE vacowned_parted; DROP ROLE regress_vacuum; pgFormatter-4.2/t/pg-test-files/expected/varchar.sql000066400000000000000000000027461361326045100225610ustar00rootroot00000000000000-- -- VARCHAR -- CREATE TABLE VARCHAR_TBL ( f1 varchar(1) ); INSERT INTO VARCHAR_TBL (f1) VALUES ('a'); INSERT INTO VARCHAR_TBL (f1) VALUES ('A'); -- any of the following three input formats are acceptable INSERT INTO VARCHAR_TBL (f1) VALUES ('1'); INSERT INTO VARCHAR_TBL (f1) VALUES (2); INSERT INTO VARCHAR_TBL (f1) VALUES ('3'); -- zero-length char INSERT INTO VARCHAR_TBL (f1) VALUES (''); -- try varchar's of greater than 1 length INSERT INTO VARCHAR_TBL (f1) VALUES ('cd'); INSERT INTO VARCHAR_TBL (f1) VALUES ('c '); SELECT '' AS seven, * FROM VARCHAR_TBL; SELECT '' AS six, c.* FROM VARCHAR_TBL c WHERE c.f1 <> 'a'; SELECT '' AS one, c.* FROM VARCHAR_TBL c WHERE c.f1 = 'a'; SELECT '' AS five, c.* FROM VARCHAR_TBL c WHERE c.f1 < 'a'; SELECT '' AS six, c.* FROM VARCHAR_TBL c WHERE c.f1 <= 'a'; SELECT '' AS one, c.* FROM VARCHAR_TBL c WHERE c.f1 > 'a'; SELECT '' AS two, c.* FROM VARCHAR_TBL c WHERE c.f1 >= 'a'; DROP TABLE VARCHAR_TBL; -- -- Now test longer arrays of char -- CREATE TABLE VARCHAR_TBL ( f1 varchar(4) ); INSERT INTO VARCHAR_TBL (f1) VALUES ('a'); INSERT INTO VARCHAR_TBL (f1) VALUES ('ab'); INSERT INTO VARCHAR_TBL (f1) VALUES ('abcd'); INSERT INTO VARCHAR_TBL (f1) VALUES ('abcde'); INSERT INTO VARCHAR_TBL (f1) VALUES ('abcd '); SELECT '' AS four, * FROM VARCHAR_TBL; pgFormatter-4.2/t/pg-test-files/expected/window.sql000066400000000000000000001545631361326045100224470ustar00rootroot00000000000000-- -- WINDOW FUNCTIONS -- CREATE TEMPORARY TABLE empsalary ( depname varchar, empno bigint, salary int, enroll_date date ); INSERT INTO empsalary VALUES ('develop', 10, 5200, '2007-08-01'), ('sales', 1, 5000, '2006-10-01'), ('personnel', 5, 3500, '2007-12-10'), ('sales', 4, 4800, '2007-08-08'), ('personnel', 2, 3900, '2006-12-23'), ('develop', 7, 4200, '2008-01-01'), ('develop', 9, 4500, '2008-01-01'), ('sales', 3, 4800, '2007-08-01'), ('develop', 8, 6000, '2006-10-01'), ('develop', 11, 5200, '2007-08-15'); SELECT depname, empno, salary, sum(salary) OVER (PARTITION BY depname) FROM empsalary ORDER BY depname, salary; SELECT depname, empno, salary, rank() OVER (PARTITION BY depname ORDER BY salary) FROM empsalary; -- with GROUP BY SELECT four, ten, SUM(SUM(four)) OVER (PARTITION BY four), AVG(ten) FROM tenk1 GROUP BY four, ten ORDER BY four, ten; SELECT depname, empno, salary, sum(salary) OVER w FROM empsalary WINDOW w AS (PARTITION BY depname); SELECT depname, empno, salary, rank() OVER w FROM empsalary WINDOW w AS (PARTITION BY depname ORDER BY salary) ORDER BY rank() OVER w; -- empty window specification SELECT COUNT(*) OVER () FROM tenk1 WHERE unique2 < 10; SELECT COUNT(*) OVER w FROM tenk1 WHERE unique2 < 10 WINDOW w AS (); -- no window operation SELECT four FROM tenk1 WHERE FALSE WINDOW w AS (PARTITION BY ten); -- cumulative aggregate SELECT sum(four) OVER (PARTITION BY ten ORDER BY unique2) AS sum_1, ten, four FROM tenk1 WHERE unique2 < 10; SELECT row_number() OVER (ORDER BY unique2) FROM tenk1 WHERE unique2 < 10; SELECT rank() OVER (PARTITION BY four ORDER BY ten) AS rank_1, ten, four FROM tenk1 WHERE unique2 < 10; SELECT dense_rank() OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT percent_rank() OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT cume_dist() OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT ntile(3) OVER (ORDER BY ten, four), ten, four FROM tenk1 WHERE unique2 < 10; SELECT ntile(NULL) OVER (ORDER BY ten, four), ten, four FROM tenk1 LIMIT 2; SELECT lag(ten) OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT lag(ten, four) OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT lag(ten, four, 0) OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT lead(ten) OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT lead(ten * 2, 1) OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT lead(ten * 2, 1, - 1) OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT first_value(ten) OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; -- last_value returns the last row of the frame, which is CURRENT ROW in ORDER BY window. SELECT last_value(four) OVER (ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT last_value(ten) OVER (PARTITION BY four), ten, four FROM ( SELECT * FROM tenk1 WHERE unique2 < 10 ORDER BY four, ten) s ORDER BY four, ten; SELECT nth_value(ten, four + 1) OVER (PARTITION BY four), ten, four FROM ( SELECT * FROM tenk1 WHERE unique2 < 10 ORDER BY four, ten) s; SELECT ten, two, sum(hundred) AS gsum, sum(sum(hundred)) OVER (PARTITION BY two ORDER BY ten) AS wsum FROM tenk1 GROUP BY ten, two; SELECT count(*) OVER (PARTITION BY four), four FROM ( SELECT * FROM tenk1 WHERE two = 1) s WHERE unique2 < 10; SELECT (count(*) OVER (PARTITION BY four ORDER BY ten) + sum(hundred) OVER (PARTITION BY four ORDER BY ten))::varchar AS cntsum FROM tenk1 WHERE unique2 < 10; -- opexpr with different windows evaluation. SELECT * FROM ( SELECT count(*) OVER (PARTITION BY four ORDER BY ten) + sum(hundred) OVER (PARTITION BY two ORDER BY ten) AS total, count(*) OVER (PARTITION BY four ORDER BY ten) AS fourcount, sum(hundred) OVER (PARTITION BY two ORDER BY ten) AS twosum FROM tenk1) sub WHERE total <> fourcount + twosum; SELECT avg(four) OVER (PARTITION BY four ORDER BY thousand / 100) FROM tenk1 WHERE unique2 < 10; SELECT ten, two, sum(hundred) AS gsum, sum(sum(hundred)) OVER win AS wsum FROM tenk1 GROUP BY ten, two WINDOW win AS (PARTITION BY two ORDER BY ten); -- more than one window with GROUP BY SELECT sum(salary), row_number() OVER (ORDER BY depname), sum(sum(salary)) OVER (ORDER BY depname DESC) FROM empsalary GROUP BY depname; -- identical windows with different names SELECT sum(salary) OVER w1, count(*) OVER w2 FROM empsalary WINDOW w1 AS (ORDER BY salary), w2 AS ( ORDER BY salary); -- subplan SELECT lead(ten, ( SELECT two FROM tenk1 WHERE s.unique2 = unique2)) OVER (PARTITION BY four ORDER BY ten) FROM tenk1 s WHERE unique2 < 10; -- empty table SELECT count(*) OVER (PARTITION BY four) FROM ( SELECT * FROM tenk1 WHERE FALSE) s; -- mixture of agg/wfunc in the same window SELECT sum(salary) OVER w, rank() OVER w FROM empsalary WINDOW w AS (PARTITION BY depname ORDER BY salary DESC); -- strict aggs SELECT empno, depname, salary, bonus, depadj, MIN(bonus) OVER (ORDER BY empno), MAX(depadj) OVER () FROM ( SELECT *, CASE WHEN enroll_date < '2008-01-01' THEN 2008 - extract(YEAR FROM enroll_date) END * 500 AS bonus, CASE WHEN AVG(salary) OVER (PARTITION BY depname) < salary THEN 200 END AS depadj FROM empsalary) s; -- window function over ungrouped agg over empty row set (bug before 9.1) SELECT SUM(COUNT(f1)) OVER () FROM int4_tbl WHERE f1 = 42; -- window function with ORDER BY an expression involving aggregates (9.1 bug) SELECT ten, sum(unique1) + sum(unique2) AS res, rank() OVER (ORDER BY sum(unique1) + sum(unique2)) AS rank FROM tenk1 GROUP BY ten ORDER BY ten; -- window and aggregate with GROUP BY expression (9.2 bug) EXPLAIN ( COSTS OFF ) SELECT first_value(max(x)) OVER (), y FROM ( SELECT unique1 AS x, ten + four AS y FROM tenk1) ss GROUP BY y; -- test non-default frame specifications SELECT four, ten, sum(ten) OVER (PARTITION BY four ORDER BY ten), last_value(ten) OVER (PARTITION BY four ORDER BY ten) FROM ( SELECT DISTINCT ten, four FROM tenk1) ss; SELECT four, ten, sum(ten) OVER (PARTITION BY four ORDER BY ten RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), last_value(ten) OVER (PARTITION BY four ORDER BY ten RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM ( SELECT DISTINCT ten, four FROM tenk1) ss; SELECT four, ten, sum(ten) OVER (PARTITION BY four ORDER BY ten RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING), last_value(ten) OVER (PARTITION BY four ORDER BY ten RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) FROM ( SELECT DISTINCT ten, four FROM tenk1) ss; SELECT four, ten / 4 AS two, sum(ten / 4) OVER (PARTITION BY four ORDER BY ten / 4 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), last_value(ten / 4) OVER (PARTITION BY four ORDER BY ten / 4 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM ( SELECT DISTINCT ten, four FROM tenk1) ss; SELECT four, ten / 4 AS two, sum(ten / 4) OVER (PARTITION BY four ORDER BY ten / 4 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), last_value(ten / 4) OVER (PARTITION BY four ORDER BY ten / 4 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM ( SELECT DISTINCT ten, four FROM tenk1) ss; SELECT sum(unique1) OVER (ORDER BY four RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING EXCLUDE NO OTHERS), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING EXCLUDE CURRENT ROW), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING EXCLUDE GROUP), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING EXCLUDE TIES), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT first_value(unique1) OVER (ORDER BY four ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING EXCLUDE CURRENT ROW), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT first_value(unique1) OVER (ORDER BY four ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING EXCLUDE GROUP), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT first_value(unique1) OVER (ORDER BY four ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING EXCLUDE TIES), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT last_value(unique1) OVER (ORDER BY four ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING EXCLUDE CURRENT ROW), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT last_value(unique1) OVER (ORDER BY four ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING EXCLUDE GROUP), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT last_value(unique1) OVER (ORDER BY four ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING EXCLUDE TIES), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ROWS BETWEEN 2 PRECEDING AND 1 PRECEDING), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ROWS BETWEEN 1 FOLLOWING AND 3 FOLLOWING), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (w RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING), unique1, four FROM tenk1 WHERE unique1 < 10 WINDOW w AS (ORDER BY four); SELECT sum(unique1) OVER (w RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW), unique1, four FROM tenk1 WHERE unique1 < 10 WINDOW w AS (ORDER BY four); SELECT sum(unique1) OVER (w RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE GROUP), unique1, four FROM tenk1 WHERE unique1 < 10 WINDOW w AS (ORDER BY four); SELECT sum(unique1) OVER (w RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE TIES), unique1, four FROM tenk1 WHERE unique1 < 10 WINDOW w AS (ORDER BY four); SELECT first_value(unique1) OVER w, nth_value(unique1, 2) OVER w AS nth_2, last_value(unique1) OVER w, unique1, four FROM tenk1 WHERE unique1 < 10 WINDOW w AS (ORDER BY four RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING); SELECT sum(unique1) OVER (ORDER BY unique1 ROWS ( SELECT unique1 FROM tenk1 ORDER BY unique1 LIMIT 1) + 1 PRECEDING), unique1 FROM tenk1 WHERE unique1 < 10; CREATE TEMP VIEW v_window AS SELECT i, sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows FROM generate_series(1, 10) i; SELECT * FROM v_window; SELECT pg_get_viewdef('v_window'); CREATE OR REPLACE TEMP VIEW v_window AS SELECT i, sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows FROM generate_series(1, 10) i; SELECT * FROM v_window; SELECT pg_get_viewdef('v_window'); CREATE OR REPLACE TEMP VIEW v_window AS SELECT i, sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows FROM generate_series(1, 10) i; SELECT * FROM v_window; SELECT pg_get_viewdef('v_window'); CREATE OR REPLACE TEMP VIEW v_window AS SELECT i, sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows FROM generate_series(1, 10) i; SELECT * FROM v_window; SELECT pg_get_viewdef('v_window'); CREATE OR REPLACE TEMP VIEW v_window AS SELECT i, sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE NO OTHERS) AS sum_rows FROM generate_series(1, 10) i; SELECT * FROM v_window; SELECT pg_get_viewdef('v_window'); CREATE OR REPLACE TEMP VIEW v_window AS SELECT i, sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows FROM generate_series(1, 10) i; SELECT * FROM v_window; SELECT pg_get_viewdef('v_window'); DROP VIEW v_window; CREATE TEMP VIEW v_window AS SELECT i, min(i) OVER (ORDER BY i RANGE BETWEEN '1 day' PRECEDING AND '10 days' FOLLOWING) AS min_i FROM generate_series(now(), now() + '100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); -- RANGE offset PRECEDING/FOLLOWING tests SELECT sum(unique1) OVER (ORDER BY four RANGE BETWEEN 2::int8 PRECEDING AND 1::int2 PRECEDING), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ORDER BY four DESC RANGE BETWEEN 2::int8 PRECEDING AND 1::int2 PRECEDING), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ORDER BY four RANGE BETWEEN 2::int8 PRECEDING AND 1::int2 PRECEDING EXCLUDE NO OTHERS), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ORDER BY four RANGE BETWEEN 2::int8 PRECEDING AND 1::int2 PRECEDING EXCLUDE CURRENT ROW), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ORDER BY four RANGE BETWEEN 2::int8 PRECEDING AND 1::int2 PRECEDING EXCLUDE GROUP), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ORDER BY four RANGE BETWEEN 2::int8 PRECEDING AND 1::int2 PRECEDING EXCLUDE TIES), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ORDER BY four RANGE BETWEEN 2::int8 PRECEDING AND 6::int2 FOLLOWING EXCLUDE TIES), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ORDER BY four RANGE BETWEEN 2::int8 PRECEDING AND 6::int2 FOLLOWING EXCLUDE GROUP), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (PARTITION BY four ORDER BY unique1 RANGE BETWEEN 5::int8 PRECEDING AND 6::int2 FOLLOWING), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (PARTITION BY four ORDER BY unique1 RANGE BETWEEN 5::int8 PRECEDING AND 6::int2 FOLLOWING EXCLUDE CURRENT ROW), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(salary) OVER (ORDER BY enroll_date RANGE BETWEEN '1 year'::interval PRECEDING AND '1 year'::interval FOLLOWING), salary, enroll_date FROM empsalary; SELECT sum(salary) OVER (ORDER BY enroll_date DESC RANGE BETWEEN '1 year'::interval PRECEDING AND '1 year'::interval FOLLOWING), salary, enroll_date FROM empsalary; SELECT sum(salary) OVER (ORDER BY enroll_date DESC RANGE BETWEEN '1 year'::interval FOLLOWING AND '1 year'::interval FOLLOWING), salary, enroll_date FROM empsalary; SELECT sum(salary) OVER (ORDER BY enroll_date RANGE BETWEEN '1 year'::interval PRECEDING AND '1 year'::interval FOLLOWING EXCLUDE CURRENT ROW), salary, enroll_date FROM empsalary; SELECT sum(salary) OVER (ORDER BY enroll_date RANGE BETWEEN '1 year'::interval PRECEDING AND '1 year'::interval FOLLOWING EXCLUDE GROUP), salary, enroll_date FROM empsalary; SELECT sum(salary) OVER (ORDER BY enroll_date RANGE BETWEEN '1 year'::interval PRECEDING AND '1 year'::interval FOLLOWING EXCLUDE TIES), salary, enroll_date FROM empsalary; SELECT first_value(salary) OVER (ORDER BY salary RANGE BETWEEN 1000 PRECEDING AND 1000 FOLLOWING), lead(salary) OVER (ORDER BY salary RANGE BETWEEN 1000 PRECEDING AND 1000 FOLLOWING), nth_value(salary, 1) OVER (ORDER BY salary RANGE BETWEEN 1000 PRECEDING AND 1000 FOLLOWING), salary FROM empsalary; SELECT last_value(salary) OVER (ORDER BY salary RANGE BETWEEN 1000 PRECEDING AND 1000 FOLLOWING), lag(salary) OVER (ORDER BY salary RANGE BETWEEN 1000 PRECEDING AND 1000 FOLLOWING), salary FROM empsalary; SELECT first_value(salary) OVER (ORDER BY salary RANGE BETWEEN 1000 FOLLOWING AND 3000 FOLLOWING EXCLUDE CURRENT ROW), lead(salary) OVER (ORDER BY salary RANGE BETWEEN 1000 FOLLOWING AND 3000 FOLLOWING EXCLUDE TIES), nth_value(salary, 1) OVER (ORDER BY salary RANGE BETWEEN 1000 FOLLOWING AND 3000 FOLLOWING EXCLUDE TIES), salary FROM empsalary; SELECT last_value(salary) OVER (ORDER BY salary RANGE BETWEEN 1000 FOLLOWING AND 3000 FOLLOWING EXCLUDE GROUP), lag(salary) OVER (ORDER BY salary RANGE BETWEEN 1000 FOLLOWING AND 3000 FOLLOWING EXCLUDE GROUP), salary FROM empsalary; SELECT first_value(salary) OVER (ORDER BY enroll_date RANGE BETWEEN UNBOUNDED PRECEDING AND '1 year'::interval FOLLOWING EXCLUDE TIES), last_value(salary) OVER (ORDER BY enroll_date RANGE BETWEEN UNBOUNDED PRECEDING AND '1 year'::interval FOLLOWING), salary, enroll_date FROM empsalary; SELECT first_value(salary) OVER (ORDER BY enroll_date RANGE BETWEEN UNBOUNDED PRECEDING AND '1 year'::interval FOLLOWING EXCLUDE TIES), last_value(salary) OVER (ORDER BY enroll_date RANGE BETWEEN UNBOUNDED PRECEDING AND '1 year'::interval FOLLOWING EXCLUDE TIES), salary, enroll_date FROM empsalary; SELECT first_value(salary) OVER (ORDER BY enroll_date RANGE BETWEEN UNBOUNDED PRECEDING AND '1 year'::interval FOLLOWING EXCLUDE GROUP), last_value(salary) OVER (ORDER BY enroll_date RANGE BETWEEN UNBOUNDED PRECEDING AND '1 year'::interval FOLLOWING EXCLUDE GROUP), salary, enroll_date FROM empsalary; SELECT first_value(salary) OVER (ORDER BY enroll_date RANGE BETWEEN UNBOUNDED PRECEDING AND '1 year'::interval FOLLOWING EXCLUDE CURRENT ROW), last_value(salary) OVER (ORDER BY enroll_date RANGE BETWEEN UNBOUNDED PRECEDING AND '1 year'::interval FOLLOWING EXCLUDE CURRENT ROW), salary, enroll_date FROM empsalary; -- RANGE offset PRECEDING/FOLLOWING with null values SELECT x, y, first_value(y) OVER w, last_value(y) OVER w FROM ( SELECT x, x AS y FROM generate_series(1, 5) AS x UNION ALL SELECT NULL, 42 UNION ALL SELECT NULL, 43) ss WINDOW w AS (ORDER BY x ASC nulls FIRST RANGE BETWEEN 2 PRECEDING AND 2 FOLLOWING); SELECT x, y, first_value(y) OVER w, last_value(y) OVER w FROM ( SELECT x, x AS y FROM generate_series(1, 5) AS x UNION ALL SELECT NULL, 42 UNION ALL SELECT NULL, 43) ss WINDOW w AS (ORDER BY x ASC nulls LAST RANGE BETWEEN 2 PRECEDING AND 2 FOLLOWING); SELECT x, y, first_value(y) OVER w, last_value(y) OVER w FROM ( SELECT x, x AS y FROM generate_series(1, 5) AS x UNION ALL SELECT NULL, 42 UNION ALL SELECT NULL, 43) ss WINDOW w AS (ORDER BY x DESC nulls FIRST RANGE BETWEEN 2 PRECEDING AND 2 FOLLOWING); SELECT x, y, first_value(y) OVER w, last_value(y) OVER w FROM ( SELECT x, x AS y FROM generate_series(1, 5) AS x UNION ALL SELECT NULL, 42 UNION ALL SELECT NULL, 43) ss WINDOW w AS (ORDER BY x DESC nulls LAST RANGE BETWEEN 2 PRECEDING AND 2 FOLLOWING); -- Check overflow behavior for various integer sizes SELECT x, last_value(x) OVER (ORDER BY x::smallint RANGE BETWEEN CURRENT ROW AND 2147450884 FOLLOWING) FROM generate_series(32764, 32766) x; SELECT x, last_value(x) OVER (ORDER BY x::smallint DESC RANGE BETWEEN CURRENT ROW AND 2147450885 FOLLOWING) FROM generate_series(- 32766, - 32764) x; SELECT x, last_value(x) OVER (ORDER BY x RANGE BETWEEN CURRENT ROW AND 4 FOLLOWING) FROM generate_series(2147483644, 2147483646) x; SELECT x, last_value(x) OVER (ORDER BY x DESC RANGE BETWEEN CURRENT ROW AND 5 FOLLOWING) FROM generate_series(- 2147483646, - 2147483644) x; SELECT x, last_value(x) OVER (ORDER BY x RANGE BETWEEN CURRENT ROW AND 4 FOLLOWING) FROM generate_series(9223372036854775804, 9223372036854775806) x; SELECT x, last_value(x) OVER (ORDER BY x DESC RANGE BETWEEN CURRENT ROW AND 5 FOLLOWING) FROM generate_series(- 9223372036854775806, - 9223372036854775804) x; -- Test in_range for other numeric datatypes CREATE temp TABLE numerics ( id int, f_float4 float4, f_float8 float8, f_numeric numeric ); INSERT INTO numerics VALUES (0, '-infinity', '-infinity', '-1000'), -- numeric type lacks infinities (1, - 3, - 3, - 3), (2, - 1, - 1, - 1), (3, 0, 0, 0), (4, 1.1, 1.1, 1.1), (5, 1.12, 1.12, 1.12), (6, 2, 2, 2), (7, 100, 100, 100), (8, 'infinity', 'infinity', '1000'), (9, 'NaN', 'NaN', 'NaN'); SELECT id, f_float4, first_value(id) OVER w, last_value(id) OVER w FROM numerics WINDOW w AS (ORDER BY f_float4 RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING); SELECT id, f_float4, first_value(id) OVER w, last_value(id) OVER w FROM numerics WINDOW w AS (ORDER BY f_float4 RANGE BETWEEN 1 PRECEDING AND 1.1::float4 FOLLOWING); SELECT id, f_float4, first_value(id) OVER w, last_value(id) OVER w FROM numerics WINDOW w AS (ORDER BY f_float4 RANGE BETWEEN 'inf' PRECEDING AND 'inf' FOLLOWING); SELECT id, f_float4, first_value(id) OVER w, last_value(id) OVER w FROM numerics WINDOW w AS (ORDER BY f_float4 RANGE BETWEEN 1.1 PRECEDING AND 'NaN' FOLLOWING); -- error, NaN disallowed SELECT id, f_float8, first_value(id) OVER w, last_value(id) OVER w FROM numerics WINDOW w AS (ORDER BY f_float8 RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING); SELECT id, f_float8, first_value(id) OVER w, last_value(id) OVER w FROM numerics WINDOW w AS (ORDER BY f_float8 RANGE BETWEEN 1 PRECEDING AND 1.1::float8 FOLLOWING); SELECT id, f_float8, first_value(id) OVER w, last_value(id) OVER w FROM numerics WINDOW w AS (ORDER BY f_float8 RANGE BETWEEN 'inf' PRECEDING AND 'inf' FOLLOWING); SELECT id, f_float8, first_value(id) OVER w, last_value(id) OVER w FROM numerics WINDOW w AS (ORDER BY f_float8 RANGE BETWEEN 1.1 PRECEDING AND 'NaN' FOLLOWING); -- error, NaN disallowed SELECT id, f_numeric, first_value(id) OVER w, last_value(id) OVER w FROM numerics WINDOW w AS (ORDER BY f_numeric RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING); SELECT id, f_numeric, first_value(id) OVER w, last_value(id) OVER w FROM numerics WINDOW w AS (ORDER BY f_numeric RANGE BETWEEN 1 PRECEDING AND 1.1::numeric FOLLOWING); SELECT id, f_numeric, first_value(id) OVER w, last_value(id) OVER w FROM numerics WINDOW w AS (ORDER BY f_numeric RANGE BETWEEN 1 PRECEDING AND 1.1::float8 FOLLOWING); -- currently unsupported SELECT id, f_numeric, first_value(id) OVER w, last_value(id) OVER w FROM numerics WINDOW w AS (ORDER BY f_numeric RANGE BETWEEN 1.1 PRECEDING AND 'NaN' FOLLOWING); -- error, NaN disallowed -- Test in_range for other datetime datatypes CREATE temp TABLE datetimes ( id int, f_time time, f_timetz timetz, f_interval interval, f_timestamptz timestamptz, f_timestamp timestamp ); INSERT INTO datetimes VALUES (1, '11:00', '11:00 BST', '1 year', '2000-10-19 10:23:54+01', '2000-10-19 10:23:54'), (2, '12:00', '12:00 BST', '2 years', '2001-10-19 10:23:54+01', '2001-10-19 10:23:54'), (3, '13:00', '13:00 BST', '3 years', '2001-10-19 10:23:54+01', '2001-10-19 10:23:54'), (4, '14:00', '14:00 BST', '4 years', '2002-10-19 10:23:54+01', '2002-10-19 10:23:54'), (5, '15:00', '15:00 BST', '5 years', '2003-10-19 10:23:54+01', '2003-10-19 10:23:54'), (6, '15:00', '15:00 BST', '5 years', '2004-10-19 10:23:54+01', '2004-10-19 10:23:54'), (7, '17:00', '17:00 BST', '7 years', '2005-10-19 10:23:54+01', '2005-10-19 10:23:54'), (8, '18:00', '18:00 BST', '8 years', '2006-10-19 10:23:54+01', '2006-10-19 10:23:54'), (9, '19:00', '19:00 BST', '9 years', '2007-10-19 10:23:54+01', '2007-10-19 10:23:54'), (10, '20:00', '20:00 BST', '10 years', '2008-10-19 10:23:54+01', '2008-10-19 10:23:54'); SELECT id, f_time, first_value(id) OVER w, last_value(id) OVER w FROM datetimes WINDOW w AS (ORDER BY f_time RANGE BETWEEN '70 min'::interval PRECEDING AND '2 hours'::interval FOLLOWING); SELECT id, f_time, first_value(id) OVER w, last_value(id) OVER w FROM datetimes WINDOW w AS (ORDER BY f_time DESC RANGE BETWEEN '70 min' PRECEDING AND '2 hours' FOLLOWING); SELECT id, f_timetz, first_value(id) OVER w, last_value(id) OVER w FROM datetimes WINDOW w AS (ORDER BY f_timetz RANGE BETWEEN '70 min'::interval PRECEDING AND '2 hours'::interval FOLLOWING); SELECT id, f_timetz, first_value(id) OVER w, last_value(id) OVER w FROM datetimes WINDOW w AS (ORDER BY f_timetz DESC RANGE BETWEEN '70 min' PRECEDING AND '2 hours' FOLLOWING); SELECT id, f_interval, first_value(id) OVER w, last_value(id) OVER w FROM datetimes WINDOW w AS (ORDER BY f_interval RANGE BETWEEN '1 year'::interval PRECEDING AND '1 year'::interval FOLLOWING); SELECT id, f_interval, first_value(id) OVER w, last_value(id) OVER w FROM datetimes WINDOW w AS (ORDER BY f_interval DESC RANGE BETWEEN '1 year' PRECEDING AND '1 year' FOLLOWING); SELECT id, f_timestamptz, first_value(id) OVER w, last_value(id) OVER w FROM datetimes WINDOW w AS (ORDER BY f_timestamptz RANGE BETWEEN '1 year'::interval PRECEDING AND '1 year'::interval FOLLOWING); SELECT id, f_timestamptz, first_value(id) OVER w, last_value(id) OVER w FROM datetimes WINDOW w AS (ORDER BY f_timestamptz DESC RANGE BETWEEN '1 year' PRECEDING AND '1 year' FOLLOWING); SELECT id, f_timestamp, first_value(id) OVER w, last_value(id) OVER w FROM datetimes WINDOW w AS (ORDER BY f_timestamp RANGE BETWEEN '1 year'::interval PRECEDING AND '1 year'::interval FOLLOWING); SELECT id, f_timestamp, first_value(id) OVER w, last_value(id) OVER w FROM datetimes WINDOW w AS (ORDER BY f_timestamp DESC RANGE BETWEEN '1 year' PRECEDING AND '1 year' FOLLOWING); -- RANGE offset PRECEDING/FOLLOWING error cases SELECT sum(salary) OVER (ORDER BY enroll_date, salary RANGE BETWEEN '1 year'::interval PRECEDING AND '2 years'::interval FOLLOWING EXCLUDE TIES), salary, enroll_date FROM empsalary; SELECT sum(salary) OVER (RANGE BETWEEN '1 year'::interval PRECEDING AND '2 years'::interval FOLLOWING EXCLUDE TIES), salary, enroll_date FROM empsalary; SELECT sum(salary) OVER (ORDER BY depname RANGE BETWEEN '1 year'::interval PRECEDING AND '2 years'::interval FOLLOWING EXCLUDE TIES), salary, enroll_date FROM empsalary; SELECT max(enroll_date) OVER (ORDER BY enroll_date RANGE BETWEEN 1 PRECEDING AND 2 FOLLOWING EXCLUDE TIES), salary, enroll_date FROM empsalary; SELECT max(enroll_date) OVER (ORDER BY salary RANGE BETWEEN - 1 PRECEDING AND 2 FOLLOWING EXCLUDE TIES), salary, enroll_date FROM empsalary; SELECT max(enroll_date) OVER (ORDER BY salary RANGE BETWEEN 1 PRECEDING AND - 2 FOLLOWING EXCLUDE TIES), salary, enroll_date FROM empsalary; SELECT max(enroll_date) OVER (ORDER BY salary RANGE BETWEEN '1 year'::interval PRECEDING AND '2 years'::interval FOLLOWING EXCLUDE TIES), salary, enroll_date FROM empsalary; SELECT max(enroll_date) OVER (ORDER BY enroll_date RANGE BETWEEN '1 year'::interval PRECEDING AND '-2 years'::interval FOLLOWING EXCLUDE TIES), salary, enroll_date FROM empsalary; -- GROUPS tests SELECT sum(unique1) OVER (ORDER BY four GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ORDER BY four GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ORDER BY four GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ORDER BY four GROUPS BETWEEN 1 PRECEDING AND UNBOUNDED FOLLOWING), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ORDER BY four GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ORDER BY four GROUPS BETWEEN UNBOUNDED PRECEDING AND 2 FOLLOWING), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ORDER BY four GROUPS BETWEEN 2 PRECEDING AND 1 PRECEDING), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ORDER BY four GROUPS BETWEEN 2 PRECEDING AND 1 FOLLOWING), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ORDER BY four GROUPS BETWEEN 0 PRECEDING AND 0 FOLLOWING), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ORDER BY four GROUPS BETWEEN 2 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ORDER BY four GROUPS BETWEEN 2 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (ORDER BY four GROUPS BETWEEN 2 PRECEDING AND 1 FOLLOWING EXCLUDE TIES), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (PARTITION BY ten ORDER BY four GROUPS BETWEEN 0 PRECEDING AND 0 FOLLOWING), unique1, four, ten FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (PARTITION BY ten ORDER BY four GROUPS BETWEEN 0 PRECEDING AND 0 FOLLOWING EXCLUDE CURRENT ROW), unique1, four, ten FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (PARTITION BY ten ORDER BY four GROUPS BETWEEN 0 PRECEDING AND 0 FOLLOWING EXCLUDE GROUP), unique1, four, ten FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) OVER (PARTITION BY ten ORDER BY four GROUPS BETWEEN 0 PRECEDING AND 0 FOLLOWING EXCLUDE TIES), unique1, four, ten FROM tenk1 WHERE unique1 < 10; SELECT first_value(salary) OVER (ORDER BY enroll_date GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING), lead(salary) OVER (ORDER BY enroll_date GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING), nth_value(salary, 1) OVER (ORDER BY enroll_date GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING), salary, enroll_date FROM empsalary; SELECT last_value(salary) OVER (ORDER BY enroll_date GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING), lag(salary) OVER (ORDER BY enroll_date GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING), salary, enroll_date FROM empsalary; SELECT first_value(salary) OVER (ORDER BY enroll_date GROUPS BETWEEN 1 FOLLOWING AND 3 FOLLOWING EXCLUDE CURRENT ROW), lead(salary) OVER (ORDER BY enroll_date GROUPS BETWEEN 1 FOLLOWING AND 3 FOLLOWING EXCLUDE TIES), nth_value(salary, 1) OVER (ORDER BY enroll_date GROUPS BETWEEN 1 FOLLOWING AND 3 FOLLOWING EXCLUDE TIES), salary, enroll_date FROM empsalary; SELECT last_value(salary) OVER (ORDER BY enroll_date GROUPS BETWEEN 1 FOLLOWING AND 3 FOLLOWING EXCLUDE GROUP), lag(salary) OVER (ORDER BY enroll_date GROUPS BETWEEN 1 FOLLOWING AND 3 FOLLOWING EXCLUDE GROUP), salary, enroll_date FROM empsalary; -- Show differences in offset interpretation between ROWS, RANGE, and GROUPS WITH cte ( x ) AS ( SELECT * FROM generate_series(1, 35, 2)) SELECT x, (sum(x) OVER w) FROM cte WINDOW w AS (ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING); WITH cte ( x ) AS ( SELECT * FROM generate_series(1, 35, 2)) SELECT x, (sum(x) OVER w) FROM cte WINDOW w AS (ORDER BY x RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING); WITH cte ( x ) AS ( SELECT * FROM generate_series(1, 35, 2)) SELECT x, (sum(x) OVER w) FROM cte WINDOW w AS (ORDER BY x GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING); WITH cte ( x ) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT * FROM generate_series(5, 49, 2)) SELECT x, (sum(x) OVER w) FROM cte WINDOW w AS (ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING); WITH cte ( x ) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT * FROM generate_series(5, 49, 2)) SELECT x, (sum(x) OVER w) FROM cte WINDOW w AS (ORDER BY x RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING); WITH cte ( x ) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT * FROM generate_series(5, 49, 2)) SELECT x, (sum(x) OVER w) FROM cte WINDOW w AS (ORDER BY x GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING); -- with UNION SELECT count(*) OVER (PARTITION BY four) FROM ( SELECT * FROM tenk1 UNION ALL SELECT * FROM tenk2) s LIMIT 0; -- check some degenerate cases CREATE temp TABLE t1 ( f1 int, f2 int8 ); INSERT INTO t1 VALUES (1, 1), (1, 2), (2, 2); SELECT f1, sum(f1) OVER (PARTITION BY f1 RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t1 WHERE f1 = f2; -- error, must have order by EXPLAIN ( COSTS OFF ) SELECT f1, sum(f1) OVER (PARTITION BY f1 ORDER BY f2 RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t1 WHERE f1 = f2; SELECT f1, sum(f1) OVER (PARTITION BY f1 ORDER BY f2 RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t1 WHERE f1 = f2; SELECT f1, sum(f1) OVER (PARTITION BY f1, f1 ORDER BY f2 RANGE BETWEEN 2 PRECEDING AND 1 PRECEDING) FROM t1 WHERE f1 = f2; SELECT f1, sum(f1) OVER (PARTITION BY f1, f2 ORDER BY f2 RANGE BETWEEN 1 FOLLOWING AND 2 FOLLOWING) FROM t1 WHERE f1 = f2; SELECT f1, sum(f1) OVER (PARTITION BY f1 GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t1 WHERE f1 = f2; -- error, must have order by EXPLAIN ( COSTS OFF ) SELECT f1, sum(f1) OVER (PARTITION BY f1 ORDER BY f2 GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t1 WHERE f1 = f2; SELECT f1, sum(f1) OVER (PARTITION BY f1 ORDER BY f2 GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t1 WHERE f1 = f2; SELECT f1, sum(f1) OVER (PARTITION BY f1, f1 ORDER BY f2 GROUPS BETWEEN 2 PRECEDING AND 1 PRECEDING) FROM t1 WHERE f1 = f2; SELECT f1, sum(f1) OVER (PARTITION BY f1, f2 ORDER BY f2 GROUPS BETWEEN 1 FOLLOWING AND 2 FOLLOWING) FROM t1 WHERE f1 = f2; -- ordering by a non-integer constant is allowed SELECT rank() OVER (ORDER BY length('abc')); -- can't order by another window function SELECT rank() OVER (ORDER BY rank() OVER (ORDER BY random())); -- some other errors SELECT * FROM empsalary WHERE row_number() OVER (ORDER BY salary) < 10; SELECT * FROM empsalary INNER JOIN tenk1 ON row_number() OVER (ORDER BY salary) < 10; SELECT rank() OVER (ORDER BY 1), count(*) FROM empsalary GROUP BY 1; SELECT * FROM rank() OVER (ORDER BY random()); DELETE FROM empsalary WHERE (rank() OVER (ORDER BY random())) > 10; DELETE FROM empsalary RETURNING rank() OVER (ORDER BY random()); SELECT count(*) OVER w FROM tenk1 WINDOW w AS (ORDER BY unique1), w AS ( ORDER BY unique1); SELECT rank() OVER (PARTITION BY four, ORDER BY ten) FROM tenk1; SELECT count() OVER () FROM tenk1; SELECT generate_series(1, 100) OVER () FROM empsalary; SELECT ntile(0) OVER (ORDER BY ten), ten, four FROM tenk1; SELECT nth_value(four, 0) OVER (ORDER BY ten), ten, four FROM tenk1; -- filter SELECT sum(salary), row_number() OVER (ORDER BY depname), sum(sum(salary) FILTER (WHERE enroll_date > '2007-01-01')) FILTER (WHERE depname <> 'sales') OVER (ORDER BY depname DESC) AS "filtered_sum", depname FROM empsalary GROUP BY depname; -- Test pushdown of quals into a subquery containing window functions -- pushdown is safe because all PARTITION BY clauses include depname: EXPLAIN ( COSTS OFF ) SELECT * FROM ( SELECT depname, sum(salary) OVER (PARTITION BY depname) depsalary, min(salary) OVER (PARTITION BY depname || 'A', depname) depminsalary FROM empsalary) emp WHERE depname = 'sales'; -- pushdown is unsafe because there's a PARTITION BY clause without depname: EXPLAIN ( COSTS OFF ) SELECT * FROM ( SELECT depname, sum(salary) OVER (PARTITION BY enroll_date) enroll_salary, min(salary) OVER (PARTITION BY depname) depminsalary FROM empsalary) emp WHERE depname = 'sales'; -- Test Sort node collapsing EXPLAIN ( COSTS OFF ) SELECT * FROM ( SELECT depname, sum(salary) OVER (PARTITION BY depname ORDER BY empno) depsalary, min(salary) OVER (PARTITION BY depname, empno ORDER BY enroll_date) depminsalary FROM empsalary) emp WHERE depname = 'sales'; -- Test Sort node reordering EXPLAIN ( COSTS OFF ) SELECT lead(1) OVER (PARTITION BY depname ORDER BY salary, enroll_date), lag(1) OVER (PARTITION BY depname ORDER BY salary, enroll_date, empno) FROM empsalary; -- cleanup DROP TABLE empsalary; -- test user-defined window function with named args and default args CREATE FUNCTION nth_value_def (val anyelement, n integer = 1) RETURNS anyelement LANGUAGE internal WINDOW IMMUTABLE STRICT AS 'window_nth_value'; SELECT nth_value_def (n := 2, val := ten) OVER (PARTITION BY four), ten, four FROM ( SELECT * FROM tenk1 WHERE unique2 < 10 ORDER BY four, ten) s; SELECT nth_value_def (ten) OVER (PARTITION BY four), ten, four FROM ( SELECT * FROM tenk1 WHERE unique2 < 10 ORDER BY four, ten) s; -- -- Test the basic moving-aggregate machinery -- -- create aggregates that record the series of transform calls (these are -- intentionally not true inverses) CREATE FUNCTION logging_sfunc_nonstrict (text, anyelement) RETURNS text AS $$ SELECT COALESCE($1, '') || '*' || quote_nullable($2) $$ LANGUAGE SQL IMMUTABLE; CREATE FUNCTION logging_msfunc_nonstrict (text, anyelement) RETURNS text AS $$ SELECT COALESCE($1, '') || '+' || quote_nullable($2) $$ LANGUAGE SQL IMMUTABLE; CREATE FUNCTION logging_minvfunc_nonstrict (text, anyelement) RETURNS text AS $$ SELECT $1 || '-' || quote_nullable($2) $$ LANGUAGE SQL IMMUTABLE; CREATE AGGREGATE logging_agg_nonstrict (anyelement) ( STYPE = text, SFUNC = logging_sfunc_nonstrict, MSTYPE = text, MSFUNC = logging_msfunc_nonstrict, MINVFUNC = logging_minvfunc_nonstrict ); CREATE AGGREGATE logging_agg_nonstrict_initcond (anyelement) ( STYPE = text, SFUNC = logging_sfunc_nonstrict, MSTYPE = text, MSFUNC = logging_msfunc_nonstrict, MINVFUNC = logging_minvfunc_nonstrict, INITCOND = 'I', MINITCOND = 'MI' ); CREATE FUNCTION logging_sfunc_strict (text, anyelement) RETURNS text AS $$ SELECT $1 || '*' || quote_nullable($2) $$ LANGUAGE SQL STRICT IMMUTABLE; CREATE FUNCTION logging_msfunc_strict (text, anyelement) RETURNS text AS $$ SELECT $1 || '+' || quote_nullable($2) $$ LANGUAGE SQL STRICT IMMUTABLE; CREATE FUNCTION logging_minvfunc_strict (text, anyelement) RETURNS text AS $$ SELECT $1 || '-' || quote_nullable($2) $$ LANGUAGE SQL STRICT IMMUTABLE; CREATE AGGREGATE logging_agg_strict (text) ( STYPE = text, SFUNC = logging_sfunc_strict, MSTYPE = text, MSFUNC = logging_msfunc_strict, MINVFUNC = logging_minvfunc_strict ); CREATE AGGREGATE logging_agg_strict_initcond (anyelement) ( STYPE = text, SFUNC = logging_sfunc_strict, MSTYPE = text, MSFUNC = logging_msfunc_strict, MINVFUNC = logging_minvfunc_strict, INITCOND = 'I', MINITCOND = 'MI' ); -- test strict and non-strict cases SELECT p::text || ',' || i::text || ':' || COALESCE(v::text, 'NULL') AS ROW, logging_agg_nonstrict (v) OVER wnd AS nstrict, logging_agg_nonstrict_initcond (v) OVER wnd AS nstrict_init, logging_agg_strict (v::text) OVER wnd AS STRICT, logging_agg_strict_initcond (v) OVER wnd AS strict_init FROM ( VALUES (1, 1, NULL), (1, 2, 'a'), (1, 3, 'b'), (1, 4, NULL), (1, 5, NULL), (1, 6, 'c'), (2, 1, NULL), (2, 2, 'x'), (3, 1, 'z')) AS t (p, i, v) WINDOW wnd AS (PARTITION BY P ORDER BY i ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) ORDER BY p, i; -- and again, but with filter SELECT p::text || ',' || i::text || ':' || CASE WHEN f THEN COALESCE(v::text, 'NULL') ELSE '-' END AS ROW, logging_agg_nonstrict (v) FILTER (WHERE f) OVER wnd AS nstrict_filt, logging_agg_nonstrict_initcond (v) FILTER (WHERE f) OVER wnd AS nstrict_init_filt, logging_agg_strict (v::text) FILTER (WHERE f) OVER wnd AS strict_filt, logging_agg_strict_initcond (v) FILTER (WHERE f) OVER wnd AS strict_init_filt FROM ( VALUES (1, 1, TRUE, NULL), (1, 2, FALSE, 'a'), (1, 3, TRUE, 'b'), (1, 4, FALSE, NULL), (1, 5, FALSE, NULL), (1, 6, FALSE, 'c'), (2, 1, FALSE, NULL), (2, 2, TRUE, 'x'), (3, 1, TRUE, 'z')) AS t (p, i, f, v) WINDOW wnd AS (PARTITION BY p ORDER BY i ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) ORDER BY p, i; -- test that volatile arguments disable moving-aggregate mode SELECT i::text || ':' || COALESCE(v::text, 'NULL') AS ROW, logging_agg_strict (v::text) OVER wnd AS inverse, logging_agg_strict (v::text || CASE WHEN random() < 0 THEN '?' ELSE '' END) OVER wnd AS noinverse FROM ( VALUES (1, 'a'), (2, 'b'), (3, 'c')) AS t (i, v) WINDOW wnd AS (ORDER BY i ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) ORDER BY i; SELECT i::text || ':' || COALESCE(v::text, 'NULL') AS ROW, logging_agg_strict (v::text) FILTER (WHERE TRUE) OVER wnd AS inverse, logging_agg_strict (v::text) FILTER (WHERE random() >= 0) OVER wnd AS noinverse FROM ( VALUES (1, 'a'), (2, 'b'), (3, 'c')) AS t (i, v) WINDOW wnd AS (ORDER BY i ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) ORDER BY i; -- test that non-overlapping windows don't use inverse transitions SELECT logging_agg_strict (v::text) OVER wnd FROM ( VALUES (1, 'a'), (2, 'b'), (3, 'c')) AS t (i, v) WINDOW wnd AS (ORDER BY i ROWS BETWEEN CURRENT ROW AND CURRENT ROW) ORDER BY i; -- test that returning NULL from the inverse transition functions -- restarts the aggregation from scratch. The second aggregate is supposed -- to test cases where only some aggregates restart, the third one checks -- that one aggregate restarting doesn't cause others to restart. CREATE FUNCTION sum_int_randrestart_minvfunc (int4, int4) RETURNS int4 AS $$ SELECT CASE WHEN random() < 0.2 THEN NULL ELSE $1 - $2 END $$ LANGUAGE SQL STRICT; CREATE AGGREGATE sum_int_randomrestart (int4) ( STYPE = int4, SFUNC = int4pl, MSTYPE = int4, MSFUNC = int4pl, MINVFUNC = sum_int_randrestart_minvfunc ); WITH vs AS ( SELECT i, (random() * 100)::int4 AS v FROM generate_series(1, 100) AS i ), sum_following AS ( SELECT i, SUM(v) OVER (ORDER BY i DESC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS s FROM vs ) SELECT DISTINCT sum_following.s = sum_int_randomrestart (v) OVER fwd AS eq1, - sum_following.s = sum_int_randomrestart (- v) OVER fwd AS eq2, 100 * 3 + (vs.i - 1) * 3 = length(logging_agg_nonstrict (''::text) OVER fwd) AS eq3 FROM vs JOIN sum_following ON sum_following.i = vs.i WINDOW fwd AS (ORDER BY vs.i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING); -- -- Test various built-in aggregates that have moving-aggregate support -- -- test inverse transition functions handle NULLs properly SELECT i, AVG(v::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 1), (2, 2), (3, NULL), (4, NULL)) t (i, v); SELECT i, AVG(v::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 1), (2, 2), (3, NULL), (4, NULL)) t (i, v); SELECT i, AVG(v::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 1), (2, 2), (3, NULL), (4, NULL)) t (i, v); SELECT i, AVG(v::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 1.5), (2, 2.5), (3, NULL), (4, NULL)) t (i, v); SELECT i, AVG(v::interval) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, '1 sec'), (2, '2 sec'), (3, NULL), (4, NULL)) t (i, v); SELECT i, SUM(v::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 1), (2, 2), (3, NULL), (4, NULL)) t (i, v); SELECT i, SUM(v::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 1), (2, 2), (3, NULL), (4, NULL)) t (i, v); SELECT i, SUM(v::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 1), (2, 2), (3, NULL), (4, NULL)) t (i, v); SELECT i, SUM(v::money) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, '1.10'), (2, '2.20'), (3, NULL), (4, NULL)) t (i, v); SELECT i, SUM(v::interval) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, '1 sec'), (2, '2 sec'), (3, NULL), (4, NULL)) t (i, v); SELECT i, SUM(v::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 1.1), (2, 2.2), (3, NULL), (4, NULL)) t (i, v); SELECT SUM(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 1.01), (2, 2), (3, 3)) v (i, n); SELECT i, COUNT(v) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 1), (2, 2), (3, NULL), (4, NULL)) t (i, v); SELECT i, COUNT(*) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 1), (2, 2), (3, NULL), (4, NULL)) t (i, v); SELECT VAR_POP(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 600), (2, 470), (3, 170), (4, 430), (5, 300)) r (i, n); SELECT VAR_POP(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 600), (2, 470), (3, 170), (4, 430), (5, 300)) r (i, n); SELECT VAR_POP(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 600), (2, 470), (3, 170), (4, 430), (5, 300)) r (i, n); SELECT VAR_POP(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 600), (2, 470), (3, 170), (4, 430), (5, 300)) r (i, n); SELECT VAR_SAMP(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 600), (2, 470), (3, 170), (4, 430), (5, 300)) r (i, n); SELECT VAR_SAMP(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 600), (2, 470), (3, 170), (4, 430), (5, 300)) r (i, n); SELECT VAR_SAMP(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 600), (2, 470), (3, 170), (4, 430), (5, 300)) r (i, n); SELECT VAR_SAMP(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 600), (2, 470), (3, 170), (4, 430), (5, 300)) r (i, n); SELECT VARIANCE(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 600), (2, 470), (3, 170), (4, 430), (5, 300)) r (i, n); SELECT VARIANCE(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 600), (2, 470), (3, 170), (4, 430), (5, 300)) r (i, n); SELECT VARIANCE(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 600), (2, 470), (3, 170), (4, 430), (5, 300)) r (i, n); SELECT VARIANCE(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, 600), (2, 470), (3, 170), (4, 430), (5, 300)) r (i, n); SELECT STDDEV_POP(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, NULL), (2, 600), (3, 470), (4, 170), (5, 430), (6, 300)) r (i, n); SELECT STDDEV_POP(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, NULL), (2, 600), (3, 470), (4, 170), (5, 430), (6, 300)) r (i, n); SELECT STDDEV_POP(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, NULL), (2, 600), (3, 470), (4, 170), (5, 430), (6, 300)) r (i, n); SELECT STDDEV_POP(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, NULL), (2, 600), (3, 470), (4, 170), (5, 430), (6, 300)) r (i, n); SELECT STDDEV_SAMP(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, NULL), (2, 600), (3, 470), (4, 170), (5, 430), (6, 300)) r (i, n); SELECT STDDEV_SAMP(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, NULL), (2, 600), (3, 470), (4, 170), (5, 430), (6, 300)) r (i, n); SELECT STDDEV_SAMP(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, NULL), (2, 600), (3, 470), (4, 170), (5, 430), (6, 300)) r (i, n); SELECT STDDEV_SAMP(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (1, NULL), (2, 600), (3, 470), (4, 170), (5, 430), (6, 300)) r (i, n); SELECT STDDEV(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (0, NULL), (1, 600), (2, 470), (3, 170), (4, 430), (5, 300)) r (i, n); SELECT STDDEV(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (0, NULL), (1, 600), (2, 470), (3, 170), (4, 430), (5, 300)) r (i, n); SELECT STDDEV(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (0, NULL), (1, 600), (2, 470), (3, 170), (4, 430), (5, 300)) r (i, n); SELECT STDDEV(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM ( VALUES (0, NULL), (1, 600), (2, 470), (3, 170), (4, 430), (5, 300)) r (i, n); -- test that inverse transition functions work with various frame options SELECT i, SUM(v::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND CURRENT ROW) FROM ( VALUES (1, 1), (2, 2), (3, NULL), (4, NULL)) t (i, v); SELECT i, SUM(v::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) FROM ( VALUES (1, 1), (2, 2), (3, NULL), (4, NULL)) t (i, v); SELECT i, SUM(v::int) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM ( VALUES (1, 1), (2, 2), (3, 3), (4, 4)) t (i, v); -- ensure aggregate over numeric properly recovers from NaN values SELECT a, b, SUM(b) OVER (ORDER BY A ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) FROM ( VALUES (1, 1::numeric), (2, 2), (3, 'NaN'), (4, 3), (5, 4)) t (a, b); -- It might be tempting for someone to add an inverse trans function for -- float and double precision. This should not be done as it can give incorrect -- results. This test should fail if anyone ever does this without thinking too -- hard about it. SELECT to_char(SUM(n::float8) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING), '999999999999999999999D9') FROM ( VALUES (1, 1e20), (2, 1)) n (i, n); SELECT i, b, bool_and(b) OVER w, bool_or(b) OVER w FROM ( VALUES (1, TRUE), (2, TRUE), (3, FALSE), (4, FALSE), (5, TRUE)) v (i, b) WINDOW w AS (ORDER BY i ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING); pgFormatter-4.2/t/pg-test-files/expected/with.sql000066400000000000000000001065221361326045100221030ustar00rootroot00000000000000-- -- Tests for common table expressions (WITH query, ... SELECT ...) -- -- Basic WITH WITH q1 ( x, y ) AS ( SELECT 1, 2 ) SELECT * FROM q1, q1 AS q2; -- Multiple uses are evaluated only once SELECT count(*) FROM ( WITH q1 ( x ) AS ( SELECT random() FROM generate_series(1, 5)) SELECT * FROM q1 UNION SELECT * FROM q1) ss; -- WITH RECURSIVE -- sum of 1..100 WITH RECURSIVE t ( n ) AS ( VALUES (1) UNION ALL SELECT n + 1 FROM t WHERE n < 100 ) SELECT sum(n) FROM t; WITH RECURSIVE t ( n ) AS ( SELECT ( VALUES (1)) UNION ALL SELECT n + 1 FROM t WHERE n < 5 ) SELECT * FROM t; -- recursive view CREATE RECURSIVE VIEW nums (n) AS VALUES (1) UNION ALL SELECT n + 1 FROM nums WHERE n < 5; SELECT * FROM nums; CREATE OR REPLACE RECURSIVE VIEW nums (n) AS VALUES (1) UNION ALL SELECT n + 1 FROM nums WHERE n < 6; SELECT * FROM nums; -- This is an infinite loop with UNION ALL, but not with UNION WITH RECURSIVE t ( n ) AS ( SELECT 1 UNION SELECT 10 - n FROM t ) SELECT * FROM t; -- This'd be an infinite loop, but outside query reads only as much as needed WITH RECURSIVE t ( n ) AS ( VALUES (1) UNION ALL SELECT n + 1 FROM t ) SELECT * FROM t LIMIT 10; -- UNION case should have same property WITH RECURSIVE t ( n ) AS ( SELECT 1 UNION SELECT n + 1 FROM t ) SELECT * FROM t LIMIT 10; -- Test behavior with an unknown-type literal in the WITH WITH q AS ( SELECT 'foo' AS x ) SELECT x, x IS OF (text) AS is_text FROM q; WITH RECURSIVE t ( n ) AS ( SELECT 'foo' UNION ALL SELECT n || ' bar' FROM t WHERE length(n) < 20 ) SELECT n, n IS OF (text) AS is_text FROM t; -- In a perfect world, this would work and resolve the literal as int ... -- but for now, we have to be content with resolving to text too soon. WITH RECURSIVE t ( n ) AS ( SELECT '7' UNION ALL SELECT n + 1 FROM t WHERE n < 10 ) SELECT n, n IS OF (int) AS is_int FROM t; -- -- Some examples with a tree -- -- department structure represented here is as follows: -- -- ROOT-+->A-+->B-+->C -- | | -- | +->D-+->F -- +->E-+->G CREATE TEMP TABLE department ( id integer PRIMARY KEY, -- department ID parent_department integer REFERENCES department, -- upper department ID name text -- department name ); INSERT INTO department VALUES (0, NULL, 'ROOT'); INSERT INTO department VALUES (1, 0, 'A'); INSERT INTO department VALUES (2, 1, 'B'); INSERT INTO department VALUES (3, 2, 'C'); INSERT INTO department VALUES (4, 2, 'D'); INSERT INTO department VALUES (5, 0, 'E'); INSERT INTO department VALUES (6, 4, 'F'); INSERT INTO department VALUES (7, 5, 'G'); -- extract all departments under 'A'. Result should be A, B, C, D and F WITH RECURSIVE subdepartment AS ( -- non recursive term SELECT name AS root_name, * FROM department WHERE name = 'A' UNION ALL -- recursive term SELECT sd.root_name, d.* FROM department AS d, subdepartment AS sd WHERE d.parent_department = sd.id ) SELECT * FROM subdepartment ORDER BY name; -- extract all departments under 'A' with "level" number WITH RECURSIVE subdepartment ( level, id, parent_department, name ) AS ( -- non recursive term SELECT 1, * FROM department WHERE name = 'A' UNION ALL -- recursive term SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd WHERE d.parent_department = sd.id ) SELECT * FROM subdepartment ORDER BY name; -- extract all departments under 'A' with "level" number. -- Only shows level 2 or more WITH RECURSIVE subdepartment ( level, id, parent_department, name ) AS ( -- non recursive term SELECT 1, * FROM department WHERE name = 'A' UNION ALL -- recursive term SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd WHERE d.parent_department = sd.id ) SELECT * FROM subdepartment WHERE level >= 2 ORDER BY name; -- "RECURSIVE" is ignored if the query has no self-reference WITH RECURSIVE subdepartment AS ( -- note lack of recursive UNION structure SELECT * FROM department WHERE name = 'A' ) SELECT * FROM subdepartment ORDER BY name; -- inside subqueries SELECT count(*) FROM ( WITH RECURSIVE t ( n ) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 500 ) SELECT * FROM t) AS t WHERE n < ( SELECT count(*) FROM ( WITH RECURSIVE t ( n ) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 100 ) SELECT * FROM t WHERE n < 50000) AS t WHERE n < 100); -- use same CTE twice at different subquery levels WITH q1 ( x, y ) AS ( SELECT hundred, sum(ten) FROM tenk1 GROUP BY hundred ) SELECT count(*) FROM q1 WHERE y > ( SELECT sum(y) / 100 FROM q1 qsub); -- via a VIEW CREATE TEMPORARY VIEW vsubdepartment AS WITH RECURSIVE subdepartment AS ( -- non recursive term SELECT * FROM department WHERE name = 'A' UNION ALL -- recursive term SELECT d.* FROM department AS d, subdepartment AS sd WHERE d.parent_department = sd.id ) SELECT * FROM subdepartment; SELECT * FROM vsubdepartment ORDER BY name; -- Check reverse listing SELECT pg_get_viewdef('vsubdepartment'::regclass); SELECT pg_get_viewdef('vsubdepartment'::regclass, TRUE); -- Another reverse-listing example CREATE VIEW sums_1_100 AS WITH RECURSIVE t ( n ) AS ( VALUES (1) UNION ALL SELECT n + 1 FROM t WHERE n < 100 ) SELECT sum(n) FROM t; \d+ sums_1_100 -- corner case in which sub-WITH gets initialized first WITH RECURSIVE q AS ( SELECT * FROM department UNION ALL ( WITH x AS ( SELECT * FROM q ) SELECT * FROM x)) SELECT * FROM q LIMIT 24; WITH RECURSIVE q AS ( SELECT * FROM department UNION ALL ( WITH RECURSIVE x AS ( SELECT * FROM department UNION ALL ( SELECT * FROM q UNION ALL SELECT * FROM x)) SELECT * FROM x)) SELECT * FROM q LIMIT 32; -- recursive term has sub-UNION WITH RECURSIVE t ( i, j ) AS ( VALUES (1, 2) UNION ALL SELECT t2.i, t.j + 1 FROM ( SELECT 2 AS i UNION ALL SELECT 3 AS i) AS t2 JOIN t ON (t2.i = t.i + 1)) SELECT * FROM t; -- -- different tree example -- CREATE TEMPORARY TABLE tree ( id integer PRIMARY KEY, parent_id integer REFERENCES tree (id) ); INSERT INTO tree VALUES (1, NULL), (2, 1), (3, 1), (4, 2), (5, 2), (6, 2), (7, 3), (8, 3), (9, 4), (10, 4), (11, 7), (12, 7), (13, 7), (14, 9), (15, 11), (16, 11); -- -- get all paths from "second level" nodes to leaf nodes -- WITH RECURSIVE t ( id, path ) AS ( VALUES (1, ARRAY[]::integer[]) UNION ALL SELECT tree.id, t.path || tree.id FROM tree JOIN t ON (tree.parent_id = t.id)) SELECT t1.*, t2.* FROM t AS t1 JOIN t AS t2 ON (t1.path[1] = t2.path[1] AND array_upper(t1.path, 1) = 1 AND array_upper(t2.path, 1) > 1) ORDER BY t1.id, t2.id; -- just count 'em WITH RECURSIVE t ( id, path ) AS ( VALUES (1, ARRAY[]::integer[]) UNION ALL SELECT tree.id, t.path || tree.id FROM tree JOIN t ON (tree.parent_id = t.id)) SELECT t1.id, count(t2.*) FROM t AS t1 JOIN t AS t2 ON (t1.path[1] = t2.path[1] AND array_upper(t1.path, 1) = 1 AND array_upper(t2.path, 1) > 1) GROUP BY t1.id ORDER BY t1.id; -- this variant tickled a whole-row-variable bug in 8.4devel WITH RECURSIVE t ( id, path ) AS ( VALUES (1, ARRAY[]::integer[]) UNION ALL SELECT tree.id, t.path || tree.id FROM tree JOIN t ON (tree.parent_id = t.id)) SELECT t1.id, t2.path, t2 FROM t AS t1 JOIN t AS t2 ON (t1.id = t2.id); -- -- test cycle detection -- CREATE temp TABLE graph ( f int, t int, label text ); INSERT INTO graph VALUES (1, 2, 'arc 1 -> 2'), (1, 3, 'arc 1 -> 3'), (2, 3, 'arc 2 -> 3'), (1, 4, 'arc 1 -> 4'), (4, 5, 'arc 4 -> 5'), (5, 1, 'arc 5 -> 1'); WITH RECURSIVE search_graph ( f, t, label, path, CYCLE ) AS ( SELECT *, ARRAY[ROW (g.f, g.t)], FALSE FROM graph g UNION ALL SELECT g.*, path || ROW (g.f, g.t), ROW (g.f, g.t) = ANY (path) FROM graph g, search_graph sg WHERE g.f = sg.t AND NOT CYCLE ) SELECT * FROM search_graph; -- ordering by the path column has same effect as SEARCH DEPTH FIRST WITH RECURSIVE search_graph ( f, t, label, path, CYCLE ) AS ( SELECT *, ARRAY[ROW (g.f, g.t)], FALSE FROM graph g UNION ALL SELECT g.*, path || ROW (g.f, g.t), ROW (g.f, g.t) = ANY (path) FROM graph g, search_graph sg WHERE g.f = sg.t AND NOT CYCLE ) SELECT * FROM search_graph ORDER BY path; -- -- test multiple WITH queries -- WITH RECURSIVE y ( id ) AS ( VALUES (1) ), x ( id ) AS ( SELECT * FROM y UNION ALL SELECT id + 1 FROM x WHERE id < 5 ) SELECT * FROM x; -- forward reference OK WITH RECURSIVE x ( id ) AS ( SELECT * FROM y UNION ALL SELECT id + 1 FROM x WHERE id < 5 ), y ( id ) AS ( VALUES (1)) SELECT * FROM x; WITH RECURSIVE x ( id ) AS ( VALUES (1) UNION ALL SELECT id + 1 FROM x WHERE id < 5 ), y ( id ) AS ( VALUES (1) UNION ALL SELECT id + 1 FROM y WHERE id < 10 ) SELECT y.*, x.* FROM y LEFT JOIN x USING (id); WITH RECURSIVE x ( id ) AS ( VALUES (1) UNION ALL SELECT id + 1 FROM x WHERE id < 5 ), y ( id ) AS ( VALUES (1) UNION ALL SELECT id + 1 FROM x WHERE id < 10 ) SELECT y.*, x.* FROM y LEFT JOIN x USING (id); WITH RECURSIVE x ( id ) AS ( SELECT 1 UNION ALL SELECT id + 1 FROM x WHERE id < 3 ), y ( id ) AS ( SELECT * FROM x UNION ALL SELECT * FROM x ), z ( id ) AS ( SELECT * FROM x UNION ALL SELECT id + 1 FROM z WHERE id < 10 ) SELECT * FROM z; WITH RECURSIVE x ( id ) AS ( SELECT 1 UNION ALL SELECT id + 1 FROM x WHERE id < 3 ), y ( id ) AS ( SELECT * FROM x UNION ALL SELECT * FROM x ), z ( id ) AS ( SELECT * FROM y UNION ALL SELECT id + 1 FROM z WHERE id < 10 ) SELECT * FROM z; -- -- Test WITH attached to a data-modifying statement -- CREATE TEMPORARY TABLE y ( a integer ); INSERT INTO y SELECT generate_series(1, 10); WITH t AS ( SELECT a FROM y) INSERT INTO y SELECT a + 20 FROM t RETURNING *; SELECT * FROM y; WITH t AS ( SELECT a FROM y) UPDATE y SET a = y.a - 10 FROM t WHERE y.a > 20 AND t.a = y.a RETURNING y.a; SELECT * FROM y; WITH RECURSIVE t ( a ) AS ( SELECT 11 UNION ALL SELECT a + 1 FROM t WHERE a < 50) DELETE FROM y USING t WHERE t.a = y.a RETURNING y.a; SELECT * FROM y; DROP TABLE y; -- -- error cases -- -- INTERSECT WITH RECURSIVE x ( n ) AS ( SELECT 1 INTERSECT SELECT n + 1 FROM x ) SELECT * FROM x; WITH RECURSIVE x ( n ) AS ( SELECT 1 INTERSECT ALL SELECT n + 1 FROM x ) SELECT * FROM x; -- EXCEPT WITH RECURSIVE x ( n ) AS ( SELECT 1 EXCEPT SELECT n + 1 FROM x ) SELECT * FROM x; WITH RECURSIVE x ( n ) AS ( SELECT 1 EXCEPT ALL SELECT n + 1 FROM x ) SELECT * FROM x; -- no non-recursive term WITH RECURSIVE x ( n ) AS ( SELECT n FROM x ) SELECT * FROM x; -- recursive term in the left hand side (strictly speaking, should allow this) WITH RECURSIVE x ( n ) AS ( SELECT n FROM x UNION ALL SELECT 1 ) SELECT * FROM x; CREATE TEMPORARY TABLE y ( a integer ); INSERT INTO y SELECT generate_series(1, 10); -- LEFT JOIN WITH RECURSIVE x ( n ) AS ( SELECT a FROM y WHERE a = 1 UNION ALL SELECT x.n + 1 FROM y LEFT JOIN x ON x.n = y.a WHERE n < 10 ) SELECT * FROM x; -- RIGHT JOIN WITH RECURSIVE x ( n ) AS ( SELECT a FROM y WHERE a = 1 UNION ALL SELECT x.n + 1 FROM x RIGHT JOIN y ON x.n = y.a WHERE n < 10 ) SELECT * FROM x; -- FULL JOIN WITH RECURSIVE x ( n ) AS ( SELECT a FROM y WHERE a = 1 UNION ALL SELECT x.n + 1 FROM x FULL JOIN y ON x.n = y.a WHERE n < 10 ) SELECT * FROM x; -- subquery WITH RECURSIVE x ( n ) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM x WHERE n IN ( SELECT * FROM x)) SELECT * FROM x; -- aggregate functions WITH RECURSIVE x ( n ) AS ( SELECT 1 UNION ALL SELECT count(*) FROM x ) SELECT * FROM x; WITH RECURSIVE x ( n ) AS ( SELECT 1 UNION ALL SELECT sum(n) FROM x ) SELECT * FROM x; -- ORDER BY WITH RECURSIVE x ( n ) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM x ORDER BY 1 ) SELECT * FROM x; -- LIMIT/OFFSET WITH RECURSIVE x ( n ) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM x LIMIT 10 OFFSET 1 ) SELECT * FROM x; -- FOR UPDATE WITH RECURSIVE x ( n ) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM x FOR UPDATE ) SELECT * FROM x; -- target list has a recursive query name WITH RECURSIVE x ( id ) AS ( VALUES (1) UNION ALL SELECT ( SELECT * FROM x) FROM x WHERE id < 5 ) SELECT * FROM x; -- mutual recursive query (not implemented) WITH RECURSIVE x ( id ) AS ( SELECT 1 UNION ALL SELECT id + 1 FROM y WHERE id < 5 ), y ( id ) AS ( SELECT 1 UNION ALL SELECT id + 1 FROM x WHERE id < 5 ) SELECT * FROM x; -- non-linear recursion is not allowed WITH RECURSIVE foo ( i ) AS ( VALUES (1) UNION ALL ( SELECT i + 1 FROM foo WHERE i < 10 UNION ALL SELECT i + 1 FROM foo WHERE i < 5)) SELECT * FROM foo; WITH RECURSIVE foo ( i ) AS ( VALUES (1) UNION ALL SELECT * FROM ( SELECT i + 1 FROM foo WHERE i < 10 UNION ALL SELECT i + 1 FROM foo WHERE i < 5) AS t ) SELECT * FROM foo; WITH RECURSIVE foo ( i ) AS ( VALUES (1) UNION ALL ( SELECT i + 1 FROM foo WHERE i < 10 EXCEPT SELECT i + 1 FROM foo WHERE i < 5)) SELECT * FROM foo; WITH RECURSIVE foo ( i ) AS ( VALUES (1) UNION ALL ( SELECT i + 1 FROM foo WHERE i < 10 INTERSECT SELECT i + 1 FROM foo WHERE i < 5)) SELECT * FROM foo; -- Wrong type induced from non-recursive term WITH RECURSIVE foo ( i ) AS ( SELECT i FROM ( VALUES (1), (2)) t (i) UNION ALL SELECT (i + 1)::numeric(10, 0) FROM foo WHERE i < 10 ) SELECT * FROM foo; -- rejects different typmod, too (should we allow this?) WITH RECURSIVE foo ( i ) AS ( SELECT i::numeric(3, 0) FROM ( VALUES (1), (2)) t (i) UNION ALL SELECT (i + 1)::numeric(10, 0) FROM foo WHERE i < 10 ) SELECT * FROM foo; -- disallow OLD/NEW reference in CTE CREATE TEMPORARY TABLE x ( n integer ); CREATE RULE r2 AS ON UPDATE TO x DO INSTEAD WITH t AS ( SELECT OLD.*) UPDATE y SET a = t.n FROM t; -- -- test for bug #4902 -- WITH cte ( foo ) AS ( VALUES (42)) VALUES (( SELECT foo FROM cte)); WITH cte ( foo ) AS ( SELECT 42 ) SELECT * FROM (( SELECT foo FROM cte)) q; -- test CTE referencing an outer-level variable (to see that changed-parameter -- signaling still works properly after fixing this bug) SELECT ( WITH cte ( foo ) AS ( VALUES (f1)) SELECT ( SELECT foo FROM cte)) FROM int4_tbl; SELECT ( WITH cte ( foo ) AS ( VALUES (f1)) VALUES (( SELECT foo FROM cte))) FROM int4_tbl; -- -- test for nested-recursive-WITH bug -- WITH RECURSIVE t ( j ) AS ( WITH RECURSIVE s ( i ) AS ( VALUES (1) UNION ALL SELECT i + 1 FROM s WHERE i < 10 ) SELECT i FROM s UNION ALL SELECT j + 1 FROM t WHERE j < 10 ) SELECT * FROM t; -- -- test WITH attached to intermediate-level set operation -- WITH outermost ( x ) AS ( SELECT 1 UNION ( WITH innermost AS ( SELECT 2 ) SELECT * FROM innermost UNION SELECT 3)) SELECT * FROM outermost ORDER BY 1; WITH outermost ( x ) AS ( SELECT 1 UNION ( WITH innermost AS ( SELECT 2 ) SELECT * FROM outermost -- fail UNION SELECT * FROM innermost)) SELECT * FROM outermost ORDER BY 1; WITH RECURSIVE outermost ( x ) AS ( SELECT 1 UNION ( WITH innermost AS ( SELECT 2 ) SELECT * FROM outermost UNION SELECT * FROM innermost)) SELECT * FROM outermost ORDER BY 1; WITH RECURSIVE outermost ( x ) AS ( WITH innermost AS ( SELECT 2 FROM outermost) -- fail SELECT * FROM innermost UNION SELECT * FROM outermost ) SELECT * FROM outermost ORDER BY 1; -- -- This test will fail with the old implementation of PARAM_EXEC parameter -- assignment, because the "q1" Var passed down to A's targetlist subselect -- looks exactly like the "A.id" Var passed down to C's subselect, causing -- the old code to give them the same runtime PARAM_EXEC slot. But the -- lifespans of the two parameters overlap, thanks to B also reading A. -- WITH A AS ( SELECT q2 AS id, ( SELECT q1) AS x FROM int8_tbl ), B AS ( SELECT id, row_number() OVER (PARTITION BY id) AS r FROM A ), C AS ( SELECT A.id, ARRAY ( SELECT B.id FROM B WHERE B.id = A.id) FROM A ) SELECT * FROM C; -- -- Test CTEs read in non-initialization orders -- WITH RECURSIVE tab ( id_key, link ) AS ( VALUES (1, 17), (2, 17), (3, 17), (4, 17), (6, 17), (5, 17) ), iter ( id_key, row_type, link ) AS ( SELECT 0, 'base', 17 UNION ALL ( WITH remaining ( id_key, row_type, link, min ) AS ( SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () FROM tab INNER JOIN iter USING (link) WHERE tab.id_key > iter.id_key), first_remaining AS ( SELECT id_key, row_type, link FROM remaining WHERE id_key = min), effect AS ( SELECT tab.id_key, 'new'::text, tab.link FROM first_remaining e INNER JOIN tab ON e.id_key = tab.id_key WHERE e.row_type = 'false' ) SELECT * FROM first_remaining UNION ALL SELECT * FROM effect)) SELECT * FROM iter; WITH RECURSIVE tab ( id_key, link ) AS ( VALUES (1, 17), (2, 17), (3, 17), (4, 17), (6, 17), (5, 17) ), iter ( id_key, row_type, link ) AS ( SELECT 0, 'base', 17 UNION ( WITH remaining ( id_key, row_type, link, min ) AS ( SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () FROM tab INNER JOIN iter USING (link) WHERE tab.id_key > iter.id_key), first_remaining AS ( SELECT id_key, row_type, link FROM remaining WHERE id_key = min), effect AS ( SELECT tab.id_key, 'new'::text, tab.link FROM first_remaining e INNER JOIN tab ON e.id_key = tab.id_key WHERE e.row_type = 'false' ) SELECT * FROM first_remaining UNION ALL SELECT * FROM effect)) SELECT * FROM iter; -- -- Data-modifying statements in WITH -- -- INSERT ... RETURNING WITH t AS ( INSERT INTO y VALUES (11), (12), (13), (14), (15), (16), (17), (18), (19), (20) RETURNING *) SELECT * FROM t; SELECT * FROM y; -- UPDATE ... RETURNING WITH t AS ( UPDATE y SET a = a + 1 RETURNING * ) SELECT * FROM t; SELECT * FROM y; -- DELETE ... RETURNING WITH t AS ( DELETE FROM y WHERE a <= 10 RETURNING * ) SELECT * FROM t; SELECT * FROM y; -- forward reference WITH RECURSIVE t AS ( INSERT INTO y SELECT a + 5 FROM t2 WHERE a > 5 RETURNING * ), t2 AS ( UPDATE y SET a = a - 11 RETURNING * ) SELECT * FROM t UNION ALL SELECT * FROM t2; SELECT * FROM y; -- unconditional DO INSTEAD rule CREATE RULE y_rule AS ON DELETE TO y DO INSTEAD INSERT INTO y VALUES (42) RETURNING *; WITH t AS ( DELETE FROM y RETURNING * ) SELECT * FROM t; SELECT * FROM y; DROP RULE y_rule ON y; -- check merging of outer CTE with CTE in a rule action CREATE TEMP TABLE bug6051 AS SELECT i FROM generate_series(1, 3) AS t (i); SELECT * FROM bug6051; WITH t1 AS ( DELETE FROM bug6051 RETURNING *) INSERT INTO bug6051 SELECT * FROM t1; SELECT * FROM bug6051; CREATE TEMP TABLE bug6051_2 ( i int ); CREATE RULE bug6051_ins AS ON INSERT TO bug6051 DO INSTEAD INSERT INTO bug6051_2 SELECT NEW.i; WITH t1 AS ( DELETE FROM bug6051 RETURNING *) INSERT INTO bug6051 SELECT * FROM t1; SELECT * FROM bug6051; SELECT * FROM bug6051_2; -- a truly recursive CTE in the same list WITH RECURSIVE t ( a ) AS ( SELECT 0 UNION ALL SELECT a + 1 FROM t WHERE a + 1 < 5 ), t2 AS ( INSERT INTO y SELECT * FROM t RETURNING * ) SELECT * FROM t2 JOIN y USING (a) ORDER BY a; SELECT * FROM y; -- data-modifying WITH in a modifying statement WITH t AS ( DELETE FROM y WHERE a <= 10 RETURNING *) INSERT INTO y SELECT - a FROM t RETURNING *; SELECT * FROM y; -- check that WITH query is run to completion even if outer query isn't WITH t AS ( UPDATE y SET a = a * 100 RETURNING * ) SELECT * FROM t LIMIT 10; SELECT * FROM y; -- data-modifying WITH containing INSERT...ON CONFLICT DO UPDATE CREATE TABLE withz AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i; ALTER TABLE withz ADD UNIQUE (k); WITH t AS ( INSERT INTO withz SELECT i, 'insert' FROM generate_series(0, 16) i ON CONFLICT (k) DO UPDATE SET v = withz.v || ', now update' RETURNING * ) SELECT * FROM t JOIN y ON t.k = y.a ORDER BY a, k; -- Test EXCLUDED.* reference within CTE WITH aa AS ( INSERT INTO withz VALUES (1, 5) ON CONFLICT (k) DO UPDATE SET v = EXCLUDED.v WHERE withz.k != EXCLUDED.k RETURNING *) SELECT * FROM aa; -- New query/snapshot demonstrates side-effects of previous query. SELECT * FROM withz ORDER BY k; -- -- Ensure subqueries within the update clause work, even if they -- reference outside values -- WITH aa AS ( SELECT 1 a, 2 b) INSERT INTO withz VALUES (1, 'insert') ON CONFLICT (k) DO UPDATE SET v = ( SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); WITH aa AS ( SELECT 1 a, 2 b) INSERT INTO withz VALUES (1, 'insert') ON CONFLICT (k) DO UPDATE SET v = ' update' WHERE withz.k = ( SELECT a FROM aa); WITH aa AS ( SELECT 1 a, 2 b) INSERT INTO withz VALUES (1, 'insert') ON CONFLICT (k) DO UPDATE SET v = ( SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); WITH aa AS ( SELECT 'a' a, 'b' b UNION ALL SELECT 'a' a, 'b' b) INSERT INTO withz VALUES (1, 'insert') ON CONFLICT (k) DO UPDATE SET v = ( SELECT b || ' update' FROM aa WHERE a = 'a' LIMIT 1); WITH aa AS ( SELECT 1 a, 2 b) INSERT INTO withz VALUES (1, ( SELECT b || ' insert' FROM aa WHERE a = 1)) ON CONFLICT (k) DO UPDATE SET v = ( SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); -- Update a row more than once, in different parts of a wCTE. That is -- an allowed, presumably very rare, edge case, but since it was -- broken in the past, having a test seems worthwhile. WITH simpletup AS ( SELECT 2 k, 'Green' v ), upsert_cte AS ( INSERT INTO withz VALUES (2, 'Blue') ON CONFLICT (k) DO UPDATE SET (k, v) = ( SELECT k, v FROM simpletup WHERE simpletup.k = withz.k) RETURNING k, v) INSERT INTO withz VALUES (2, 'Red') ON CONFLICT (k) DO UPDATE SET (k, v) = ( SELECT k, v FROM upsert_cte WHERE upsert_cte.k = withz.k) RETURNING k, v; DROP TABLE withz; -- check that run to completion happens in proper ordering TRUNCATE TABLE y; INSERT INTO y SELECT generate_series(1, 3); CREATE TEMPORARY TABLE yy ( a integer ); WITH RECURSIVE t1 AS ( INSERT INTO y SELECT * FROM y RETURNING * ), t2 AS ( INSERT INTO yy SELECT * FROM t1 RETURNING * ) SELECT 1; SELECT * FROM y; SELECT * FROM yy; WITH RECURSIVE t1 AS ( INSERT INTO yy SELECT * FROM t2 RETURNING * ), t2 AS ( INSERT INTO y SELECT * FROM y RETURNING * ) SELECT 1; SELECT * FROM y; SELECT * FROM yy; -- triggers TRUNCATE TABLE y; INSERT INTO y SELECT generate_series(1, 10); CREATE FUNCTION y_trigger () RETURNS TRIGGER AS $$ BEGIN RAISE notice 'y_trigger: a = %', new.a; RETURN new; END; $$ LANGUAGE plpgsql; CREATE TRIGGER y_trig BEFORE INSERT ON y FOR EACH ROW EXECUTE PROCEDURE y_trigger (); WITH t AS ( INSERT INTO y VALUES (21), (22), (23) RETURNING *) SELECT * FROM t; SELECT * FROM y; DROP TRIGGER y_trig ON y; CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH ROW EXECUTE PROCEDURE y_trigger (); WITH t AS ( INSERT INTO y VALUES (31), (32), (33) RETURNING *) SELECT * FROM t LIMIT 1; SELECT * FROM y; DROP TRIGGER y_trig ON y; CREATE OR REPLACE FUNCTION y_trigger () RETURNS TRIGGER AS $$ BEGIN RAISE notice 'y_trigger'; RETURN NULL; END; $$ LANGUAGE plpgsql; CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH STATEMENT EXECUTE PROCEDURE y_trigger (); WITH t AS ( INSERT INTO y VALUES (41), (42), (43) RETURNING *) SELECT * FROM t; SELECT * FROM y; DROP TRIGGER y_trig ON y; DROP FUNCTION y_trigger (); -- WITH attached to inherited UPDATE or DELETE CREATE TEMP TABLE parent ( id int, val text ); CREATE TEMP TABLE child1 () INHERITS ( parent ); CREATE TEMP TABLE child2 () INHERITS ( parent ); INSERT INTO parent VALUES (1, 'p1'); INSERT INTO child1 VALUES (11, 'c11'), (12, 'c12'); INSERT INTO child2 VALUES (23, 'c21'), (24, 'c22'); WITH rcte AS ( SELECT sum(id) AS totalid FROM parent) UPDATE parent SET id = id + totalid FROM rcte; SELECT * FROM parent; WITH wcte AS ( INSERT INTO child1 VALUES (42, 'new') RETURNING id AS newid) UPDATE parent SET id = id + newid FROM wcte; SELECT * FROM parent; WITH rcte AS ( SELECT max(id) AS maxid FROM parent) DELETE FROM parent USING rcte WHERE id = maxid; SELECT * FROM parent; WITH wcte AS ( INSERT INTO child2 VALUES (42, 'new2') RETURNING id AS newid) DELETE FROM parent USING wcte WHERE id = newid; SELECT * FROM parent; -- check EXPLAIN VERBOSE for a wCTE with RETURNING EXPLAIN ( VERBOSE, COSTS OFF ) WITH wcte AS ( INSERT INTO int8_tbl VALUES (42, 47) RETURNING q2) DELETE FROM a USING wcte WHERE aa = q2; -- error cases -- data-modifying WITH tries to use its own output WITH RECURSIVE t AS ( INSERT INTO y SELECT * FROM t) VALUES (FALSE); -- no RETURNING in a referenced data-modifying WITH WITH t AS ( INSERT INTO y VALUES (0)) SELECT * FROM t; -- data-modifying WITH allowed only at the top level SELECT * FROM ( WITH t AS ( UPDATE y SET a = a + 1 RETURNING * ) SELECT * FROM t) ss; -- most variants of rules aren't allowed CREATE RULE y_rule AS ON INSERT TO y WHERE a = 0 DO INSTEAD DELETE FROM y; WITH t AS ( INSERT INTO y VALUES (0)) VALUES (FALSE); DROP RULE y_rule ON y; -- check that parser lookahead for WITH doesn't cause any odd behavior CREATE TABLE foo ( WITH baz ); -- fail, WITH is a reserved word CREATE TABLE foo ( WITH ORDINALITY ); -- fail, WITH is a reserved word WITH ORDINALITY AS ( SELECT 1 AS x ) SELECT * FROM ORDINALITY; -- check sane response to attempt to modify CTE relation WITH test AS ( SELECT 42) INSERT INTO test VALUES (1); -- check response to attempt to modify table with same name as a CTE (perhaps -- surprisingly it works, because CTEs don't hide tables from data-modifying -- statements) CREATE temp TABLE test ( i int ); WITH test AS ( SELECT 42) INSERT INTO test SELECT * FROM test; SELECT * FROM test; DROP TABLE test; pgFormatter-4.2/t/pg-test-files/expected/write_parallel.sql000066400000000000000000000027371361326045100241410ustar00rootroot00000000000000-- -- PARALLEL -- -- Serializable isolation would disable parallel query, so explicitly use an -- arbitrary other level. BEGIN ISOLATION level REPEATABLE read; -- encourage use of parallel plans SET parallel_setup_cost = 0; SET parallel_tuple_cost = 0; SET min_parallel_table_scan_size = 0; SET max_parallel_workers_per_gather = 4; -- -- Test write operations that has an underlying query that is eligble -- for parallel plans -- EXPLAIN ( COSTS OFF ) CREATE TABLE parallel_write AS SELECT length(stringu1) FROM tenk1 GROUP BY length(stringu1); CREATE TABLE parallel_write AS SELECT length(stringu1) FROM tenk1 GROUP BY length(stringu1); DROP TABLE parallel_write; EXPLAIN ( COSTS OFF ) SELECT length(stringu1) INTO parallel_write FROM tenk1 GROUP BY length(stringu1); SELECT length(stringu1) INTO parallel_write FROM tenk1 GROUP BY length(stringu1); DROP TABLE parallel_write; EXPLAIN ( COSTS OFF ) CREATE MATERIALIZED VIEW parallel_mat_view AS SELECT length(stringu1) FROM tenk1 GROUP BY length(stringu1); CREATE MATERIALIZED VIEW parallel_mat_view AS SELECT length(stringu1) FROM tenk1 GROUP BY length(stringu1); DROP MATERIALIZED VIEW parallel_mat_view; PREPARE prep_stmt AS SELECT length(stringu1) FROM tenk1 GROUP BY length(stringu1); EXPLAIN ( COSTS OFF ) CREATE TABLE parallel_write AS EXECUTE prep_stmt; CREATE TABLE parallel_write AS EXECUTE prep_stmt; DROP TABLE parallel_write; ROLLBACK; pgFormatter-4.2/t/pg-test-files/expected/xml.sql000066400000000000000000000641211361326045100217260ustar00rootroot00000000000000CREATE TABLE xmltest ( id int, data xml ); INSERT INTO xmltest VALUES (1, 'one'); INSERT INTO xmltest VALUES (2, 'two'); INSERT INTO xmltest VALUES (3, '', NULL, ''); SELECT xmlconcat('', NULL, ''); SELECT xmlconcat(NULL); SELECT xmlconcat(NULL, NULL); SELECT xmlelement(name element, xmlattributes (1 AS one, 'deuce' AS two), 'content'); SELECT xmlelement(name element, xmlattributes ('unnamed and wrong')); SELECT xmlelement(name element, xmlelement(name nested, 'stuff')); SELECT xmlelement(name employee, xmlforest(name, age, salary AS pay)) FROM emp; SELECT xmlelement(name duplicate, xmlattributes (1 AS a, 2 AS b, 3 AS a)); SELECT xmlelement(name num, 37); SELECT xmlelement(name foo, text 'bar'); SELECT xmlelement(name foo, xml 'bar'); SELECT xmlelement(name foo, text 'br'); SELECT xmlelement(name foo, xml 'br'); SELECT xmlelement(name foo, ARRAY[1, 2, 3]); SET xmlbinary TO base64; SELECT xmlelement(name foo, bytea 'bar'); SET xmlbinary TO hex; SELECT xmlelement(name foo, bytea 'bar'); SELECT xmlelement(name foo, xmlattributes (TRUE AS bar)); SELECT xmlelement(name foo, xmlattributes ('2009-04-09 00:24:37'::timestamp AS bar)); SELECT xmlelement(name foo, xmlattributes ('infinity'::timestamp AS bar)); SELECT xmlelement(name foo, xmlattributes ('<>&"''' AS funny, xml 'br' AS funnier)); SELECT xmlparse (content ''); SELECT xmlparse (content ' '); SELECT xmlparse (content 'abc'); SELECT xmlparse (content 'x'); SELECT xmlparse (content '&'); SELECT xmlparse (content '&idontexist;'); SELECT xmlparse (content ''); SELECT xmlparse (content ''); SELECT xmlparse (content '&idontexist;'); SELECT xmlparse (content ''); SELECT xmlparse (document ' '); SELECT xmlparse (document 'abc'); SELECT xmlparse (document 'x'); SELECT xmlparse (document '&'); SELECT xmlparse (document '&idontexist;'); SELECT xmlparse (document ''); SELECT xmlparse (document ''); SELECT xmlparse (document '&idontexist;'); SELECT xmlparse (document ''); SELECT xmlpi(name foo); SELECT xmlpi(name xml); SELECT xmlpi(name xmlstuff); SELECT xmlpi(name foo, 'bar'); SELECT xmlpi(name foo, 'in?>valid'); SELECT xmlpi(name foo, NULL); SELECT xmlpi(name xml, NULL); SELECT xmlpi(name xmlstuff, NULL); SELECT xmlpi(name "xml-stylesheet", 'href="mystyle.css" type="text/css"'); SELECT xmlpi(name foo, ' bar'); SELECT xmlroot(xml '', version NO value, standalone NO value); SELECT xmlroot(xml '', version '2.0'); SELECT xmlroot(xml '', version NO value, standalone yes); SELECT xmlroot(xml '', version NO value, standalone yes); SELECT xmlroot(xmlroot(xml '', version '1.0'), version '1.1', standalone NO); SELECT xmlroot('', version NO value, standalone NO); SELECT xmlroot('', version NO value, standalone NO value); SELECT xmlroot('', version NO value); SELECT xmlroot(xmlelement(name gazonk, xmlattributes ('val' AS name, 1 + 1 AS num), xmlelement(NAME qux, 'foo')), version '1.0', standalone yes); SELECT xmlserialize(content data AS character varying(20)) FROM xmltest; SELECT xmlserialize(content 'good' AS char(10)); SELECT xmlserialize(document 'bad' AS text); SELECT xml 'bar' IS DOCUMENT; SELECT xml 'barfoo' IS DOCUMENT; SELECT xml '' IS NOT DOCUMENT; SELECT xml 'abc' IS NOT DOCUMENT; SELECT '<>' IS NOT DOCUMENT; SELECT xmlagg(data) FROM xmltest; SELECT xmlagg(data) FROM xmltest WHERE id > 10; SELECT xmlelement(name employees, xmlagg(xmlelement(name name, name))) FROM emp; -- Check mapping SQL identifier to XML name SELECT xmlpi(name ":::_xml_abc135.%-&_"); SELECT xmlpi(name "123"); PREPARE foo (xml) AS SELECT xmlconcat('', $1); SET XML OPTION DOCUMENT; EXECUTE foo (''); EXECUTE foo ('bad'); SELECT xml ''; SET XML OPTION CONTENT; EXECUTE foo (''); EXECUTE foo ('good'); SELECT xml ' '; SELECT xml ' '; SELECT xml ''; SELECT xml ' oops '; SELECT xml ' '; SELECT xml ''; -- Test backwards parsing CREATE VIEW xmlview1 AS SELECT xmlcomment('test'); CREATE VIEW xmlview2 AS SELECT xmlconcat('hello', 'you'); CREATE VIEW xmlview3 AS SELECT xmlelement(name element, xmlattributes (1 AS ":one:", 'deuce' AS two), 'content&'); CREATE VIEW xmlview4 AS SELECT xmlelement(name employee, xmlforest(name, age, salary AS pay)) FROM emp; CREATE VIEW xmlview5 AS SELECT xmlparse (content 'x'); CREATE VIEW xmlview6 AS SELECT xmlpi(name foo, 'bar'); CREATE VIEW xmlview7 AS SELECT xmlroot(xml '', version NO value, standalone yes); CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' AS char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' AS text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; -- Text XPath expressions evaluation SELECT xpath('/value', data) FROM xmltest; SELECT xpath(NULL, NULL) IS NULL FROM xmltest; SELECT xpath('', ''); SELECT xpath('//text()', 'number one'); SELECT xpath('//loc:piece/@id', 'number one', ARRAY[ARRAY['loc', 'http://127.0.0.1']]); SELECT xpath('//loc:piece', 'number one', ARRAY[ARRAY['loc', 'http://127.0.0.1']]); SELECT xpath('//loc:piece', 'number one', ARRAY[ARRAY['loc', 'http://127.0.0.1']]); SELECT xpath('//b', 'one two three etc'); SELECT xpath('//text()', '<'); SELECT xpath('//@value', ''); SELECT xpath('''<>''', ''); SELECT xpath('count(//*)', ''); SELECT xpath('count(//*)=0', ''); SELECT xpath('count(//*)=3', ''); SELECT xpath('name(/*)', ''); SELECT xpath('/nosuchtag', ''); SELECT xpath('root', ''); -- Round-trip non-ASCII data through xpath(). DO $$ DECLARE xml_declaration text := ''; degree_symbol text; res xml[]; BEGIN -- Per the documentation, except when the server encoding is UTF8, xpath() -- may not work on non-ASCII data. The untranslatable_character and -- undefined_function traps below, currently dead code, will become relevant -- if we remove this limitation. IF current_setting('server_encoding') <> 'UTF8' THEN RAISE LOG 'skip: encoding % unsupported for xpath', current_setting('server_encoding'); RETURN; END IF; degree_symbol := convert_from('\xc2b0', 'UTF8'); res := xpath('text()', (xml_declaration || '' || degree_symbol || '')::xml); IF degree_symbol <> res[1]::text THEN RAISE 'expected % (%), got % (%)', degree_symbol, convert_to(degree_symbol, 'UTF8'), res[1], convert_to(res[1]::text, 'UTF8'); END IF; EXCEPTION -- character with byte sequence 0xc2 0xb0 in encoding "UTF8" has no equivalent in encoding "LATIN8" WHEN untranslatable_character -- default conversion function for encoding "UTF8" to "MULE_INTERNAL" does not exist OR undefined_function -- unsupported XML feature OR feature_not_supported THEN RAISE LOG 'skip: %', SQLERRM; END $$; -- Test xmlexists and xpath_exists SELECT xmlexists ('//town[text() = ''Toronto'']' PASSING BY REF 'Bidford-on-AvonCwmbranBristol'); SELECT xmlexists ('//town[text() = ''Cwmbran'']' PASSING BY REF 'Bidford-on-AvonCwmbranBristol'); SELECT xmlexists ('count(/nosuchtag)' PASSING BY REF ''); SELECT xpath_exists('//town[text() = ''Toronto'']', 'Bidford-on-AvonCwmbranBristol'::xml); SELECT xpath_exists('//town[text() = ''Cwmbran'']', 'Bidford-on-AvonCwmbranBristol'::xml); SELECT xpath_exists('count(/nosuchtag)', ''::xml); INSERT INTO xmltest VALUES (4, 'BudvarfreeCarlinglots'::xml); INSERT INTO xmltest VALUES (5, 'MolsonfreeCarlinglots'::xml); INSERT INTO xmltest VALUES (6, 'BudvarfreeCarlinglots'::xml); INSERT INTO xmltest VALUES (7, 'MolsonfreeCarlinglots'::xml); SELECT COUNT(id) FROM xmltest WHERE xmlexists ('/menu/beer' PASSING data); SELECT COUNT(id) FROM xmltest WHERE xmlexists ('/menu/beer' PASSING BY REF data BY REF); SELECT COUNT(id) FROM xmltest WHERE xmlexists ('/menu/beers' PASSING BY REF data); SELECT COUNT(id) FROM xmltest WHERE xmlexists ('/menu/beers/name[text() = ''Molson'']' PASSING BY REF data); SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/menu/beer', data); SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/menu/beers', data); SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/menu/beers/name[text() = ''Molson'']', data); SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/myns:menu/myns:beer', data, ARRAY[ARRAY['myns', 'http://myns.com']]); SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/myns:menu/myns:beers', data, ARRAY[ARRAY['myns', 'http://myns.com']]); SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/myns:menu/myns:beers/myns:name[text() = ''Molson'']', data, ARRAY[ARRAY['myns', 'http://myns.com']]); CREATE TABLE query ( expr text ); INSERT INTO query VALUES ('/menu/beers/cost[text() = ''lots'']'); SELECT COUNT(id) FROM xmltest, query WHERE xmlexists (expr PASSING BY REF data); -- Test xml_is_well_formed and variants SELECT xml_is_well_formed_document('bar'); SELECT xml_is_well_formed_document('abc'); SELECT xml_is_well_formed_content('bar'); SELECT xml_is_well_formed_content('abc'); SET xmloption TO DOCUMENT; SELECT xml_is_well_formed('abc'); SELECT xml_is_well_formed('<>'); SELECT xml_is_well_formed(''); SELECT xml_is_well_formed('bar'); SELECT xml_is_well_formed('barbaz'); SELECT xml_is_well_formed('number one'); SELECT xml_is_well_formed('bar'); SELECT xml_is_well_formed('bar'); SELECT xml_is_well_formed('&'); SELECT xml_is_well_formed('&idontexist;'); SELECT xml_is_well_formed(''); SELECT xml_is_well_formed(''); SELECT xml_is_well_formed('&idontexist;'); SET xmloption TO CONTENT; SELECT xml_is_well_formed('abc'); -- Since xpath() deals with namespaces, it's a bit stricter about -- what's well-formed and what's not. If we don't obey these rules -- (i.e. ignore namespace-related errors from libxml), xpath() -- fails in subtle ways. The following would for example produce -- the xml value -- -- which is invalid because '<' may not appear un-escaped in -- attribute values. -- Since different libxml versions emit slightly different -- error messages, we suppress the DETAIL in this test. \set VERBOSITY terse SELECT xpath('/*', ''); \set VERBOSITY default -- Again, the XML isn't well-formed for namespace purposes SELECT xpath('/*', ''); -- XPath deprecates relative namespaces, but they're not supposed to -- throw an error, only a warning. SELECT xpath('/*', ''); -- External entity references should not leak filesystem information. SELECT XMLPARSE (DOCUMENT ']>&c;'); SELECT XMLPARSE (DOCUMENT ']>&c;'); -- This might or might not load the requested DTD, but it mustn't throw error. SELECT XMLPARSE (DOCUMENT ' '); -- XMLPATH tests CREATE TABLE xmldata ( data xml ); INSERT INTO xmldata VALUES (' AU Australia 3 CN China 3 HK HongKong 3 IN India 3 JP Japan 3Sinzo Abe SG Singapore 3791 '); -- XMLTABLE with columns SELECT xmltable.* FROM ( SELECT data FROM xmldata) x, LATERAL XMLTABLE('/ROWS/ROW' PASSING data COLUMNS id int PATH '@id', _id FOR ORDINALITY, country_name text PATH 'COUNTRY_NAME/text()' NOT NULL, country_id text PATH 'COUNTRY_ID', region_id int PATH 'REGION_ID', size float PATH 'SIZE', unit text PATH 'SIZE/@unit', premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'); CREATE VIEW xmltableview1 AS SELECT xmltable.* FROM ( SELECT data FROM xmldata) x, LATERAL XMLTABLE('/ROWS/ROW' PASSING data COLUMNS id int PATH '@id', _id FOR ORDINALITY, country_name text PATH 'COUNTRY_NAME/text()' NOT NULL, country_id text PATH 'COUNTRY_ID', region_id int PATH 'REGION_ID', size float PATH 'SIZE', unit text PATH 'SIZE/@unit', premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'); SELECT * FROM xmltableview1; \sv xmltableview1 EXPLAIN ( COSTS OFF ) SELECT * FROM xmltableview1; EXPLAIN ( COSTS OFF, VERBOSE ) SELECT * FROM xmltableview1; -- XMLNAMESPACES tests SELECT * FROM XMLTABLE(XMLNAMESPACES ('http://x.y' AS zz), '/zz:rows/zz:row' PASSING '10' COLUMNS a int PATH 'zz:a'); CREATE VIEW xmltableview2 AS SELECT * FROM XMLTABLE(XMLNAMESPACES ('http://x.y' AS zz), '/zz:rows/zz:row' PASSING '10' COLUMNS a int PATH 'zz:a'); SELECT * FROM xmltableview2; SELECT * FROM XMLTABLE(XMLNAMESPACES (DEFAULT 'http://x.y'), '/rows/row' PASSING '10' COLUMNS a int PATH 'a'); -- used in prepare statements PREPARE pp AS SELECT xmltable.* FROM ( SELECT data FROM xmldata) x, LATERAL XMLTABLE('/ROWS/ROW' PASSING data COLUMNS id int PATH '@id', _id FOR ORDINALITY, country_name text PATH 'COUNTRY_NAME' NOT NULL, country_id text PATH 'COUNTRY_ID', region_id int PATH 'REGION_ID', size float PATH 'SIZE', unit text PATH 'SIZE/@unit', premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'); EXECUTE pp; SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS "COUNTRY_NAME" text, "REGION_ID" int); SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS id FOR ORDINALITY, "COUNTRY_NAME" text, "REGION_ID" int); SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS id int PATH '@id', "COUNTRY_NAME" text, "REGION_ID" int); SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS id int PATH '@id'); SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS id FOR ORDINALITY); SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS id int PATH '@id', "COUNTRY_NAME" text, "REGION_ID" int, rawdata xml PATH '.'); SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS id int PATH '@id', "COUNTRY_NAME" text, "REGION_ID" int, rawdata xml PATH './*'); SELECT * FROM xmltable('/root' passing 'a1aa2a bbbbxxxcccc' COLUMNS element text); SELECT * FROM xmltable('/root' passing 'a1aa2a bbbbxxxcccc' COLUMNS element text PATH 'element/text()'); -- should fail -- CDATA test SELECT * FROM xmltable('d/r' passing ' &"<>!foo]]>2' columns c text); -- XML builtin entities SELECT * FROM xmltable('/x/a' PASSING ''"&<>' COLUMNS ent text); SELECT * FROM xmltable('/x/a' PASSING ''"&<>' COLUMNS ent xml); EXPLAIN ( VERBOSE, COSTS OFF ) SELECT xmltable.* FROM ( SELECT data FROM xmldata) x, LATERAL XMLTABLE('/ROWS/ROW' PASSING data COLUMNS id int PATH '@id', _id FOR ORDINALITY, country_name text PATH 'COUNTRY_NAME' NOT NULL, country_id text PATH 'COUNTRY_ID', region_id int PATH 'REGION_ID', size float PATH 'SIZE', unit text PATH 'SIZE/@unit', premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'); -- test qual SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS "COUNTRY_NAME" text, "REGION_ID" int) WHERE "COUNTRY_NAME" = 'Japan'; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS "COUNTRY_NAME" text, "REGION_ID" int) WHERE "COUNTRY_NAME" = 'Japan'; -- should to work with more data INSERT INTO xmldata VALUES (' CZ Czech Republic 2Milos Zeman DE Germany 2 FR France 2 '); INSERT INTO xmldata VALUES (' EG Egypt 1 SD Sudan 1 '); SELECT xmltable.* FROM ( SELECT data FROM xmldata) x, LATERAL XMLTABLE('/ROWS/ROW' PASSING data COLUMNS id int PATH '@id', _id FOR ORDINALITY, country_name text PATH 'COUNTRY_NAME' NOT NULL, country_id text PATH 'COUNTRY_ID', region_id int PATH 'REGION_ID', size float PATH 'SIZE', unit text PATH 'SIZE/@unit', premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'); SELECT xmltable.* FROM ( SELECT data FROM xmldata) x, LATERAL XMLTABLE('/ROWS/ROW' PASSING data COLUMNS id int PATH '@id', _id FOR ORDINALITY, country_name text PATH 'COUNTRY_NAME' NOT NULL, country_id text PATH 'COUNTRY_ID', region_id int PATH 'REGION_ID', size float PATH 'SIZE', unit text PATH 'SIZE/@unit', premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified') WHERE region_id = 2; EXPLAIN ( VERBOSE, COSTS OFF ) SELECT xmltable.* FROM ( SELECT data FROM xmldata) x, LATERAL XMLTABLE('/ROWS/ROW' PASSING data COLUMNS id int PATH '@id', _id FOR ORDINALITY, country_name text PATH 'COUNTRY_NAME' NOT NULL, country_id text PATH 'COUNTRY_ID', region_id int PATH 'REGION_ID', size float PATH 'SIZE', unit text PATH 'SIZE/@unit', premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified') WHERE region_id = 2; -- should fail, NULL value SELECT xmltable.* FROM ( SELECT data FROM xmldata) x, LATERAL XMLTABLE('/ROWS/ROW' PASSING data COLUMNS id int PATH '@id', _id FOR ORDINALITY, country_name text PATH 'COUNTRY_NAME' NOT NULL, country_id text PATH 'COUNTRY_ID', region_id int PATH 'REGION_ID', size float PATH 'SIZE' NOT NULL, unit text PATH 'SIZE/@unit', premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'); -- if all is ok, then result is empty -- one line xml test WITH x AS ( SELECT proname, proowner, procost::numeric, pronargs, array_to_string(proargnames, ',') AS proargnames, CASE WHEN proargtypes <> '' THEN array_to_string(proargtypes::oid[], ',') END AS proargtypes FROM pg_proc WHERE proname = 'f_leak' ), y AS ( SELECT xmlelement(name proc, xmlforest(proname, proowner, procost, pronargs, proargnames, proargtypes)) AS proc FROM x ), z AS ( SELECT xmltable.* FROM y, LATERAL xmltable('/proc' PASSING proc COLUMNS proname name, proowner oid, procost float, pronargs int, proargnames text, proargtypes text)) SELECT * FROM z EXCEPT SELECT * FROM x; -- multi line xml test, result should be empty too WITH x AS ( SELECT proname, proowner, procost::numeric, pronargs, array_to_string(proargnames, ',') AS proargnames, CASE WHEN proargtypes <> '' THEN array_to_string(proargtypes::oid[], ',') END AS proargtypes FROM pg_proc ), y AS ( SELECT xmlelement(name data, xmlagg(xmlelement(name proc, xmlforest(proname, proowner, procost, pronargs, proargnames, proargtypes)))) AS doc FROM x ), z AS ( SELECT xmltable.* FROM y, LATERAL xmltable('/data/proc' PASSING doc COLUMNS proname name, proowner oid, procost float, pronargs int, proargnames text, proargtypes text)) SELECT * FROM z EXCEPT SELECT * FROM x; CREATE TABLE xmltest2 ( x xml, _path text ); INSERT INTO xmltest2 VALUES ('1', 'A'); INSERT INTO xmltest2 VALUES ('2', 'B'); INSERT INTO xmltest2 VALUES ('3', 'C'); INSERT INTO xmltest2 VALUES ('2', 'D'); SELECT xmltable.* FROM xmltest2, LATERAL xmltable('/d/r' PASSING x COLUMNS a int PATH '' || lower(_path) || 'c'); SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c') PASSING x COLUMNS a int PATH '.'); SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c') PASSING x COLUMNS a int PATH 'x' DEFAULT ascii(_path) - 54); -- XPath result can be boolean or number too SELECT * FROM XMLTABLE('*' PASSING 'a' COLUMNS a xml PATH '.', b text PATH '.', c text PATH '"hi"', d boolean PATH '. = "a"', e integer PATH 'string-length(.)'); \x SELECT * FROM XMLTABLE('*' PASSING 'pre&deeppost' COLUMNS x xml PATH 'node()', y xml PATH '/'); \x SELECT * FROM XMLTABLE('.' PASSING XMLELEMENT(NAME a) columns a varchar(20) PATH '""', b xml PATH '""'); pgFormatter-4.2/t/pg-test-files/expected/xmlmap.sql000066400000000000000000000061041361326045100224210ustar00rootroot00000000000000CREATE SCHEMA testxmlschema; CREATE TABLE testxmlschema.test1 ( a int, b text ); INSERT INTO testxmlschema.test1 VALUES (1, 'one'), (2, 'two'), (- 1, NULL); CREATE DOMAIN testxmldomain AS varchar; CREATE TABLE testxmlschema.test2 ( z int, y varchar(500), x char(6), w numeric(9, 2), v smallint, u bigint, t real, s time, r timestamp, q date, p xml, o testxmldomain, n bool, m bytea, aaa text ); ALTER TABLE testxmlschema.test2 DROP COLUMN aaa; INSERT INTO testxmlschema.test2 VALUES (55, 'abc', 'def', 98.6, 2, 999, 0, '21:07', '2009-06-08 21:07:30', '2009-06-08', NULL, 'ABC', TRUE, 'XYZ'); SELECT table_to_xml('testxmlschema.test1', FALSE, FALSE, ''); SELECT table_to_xml('testxmlschema.test1', TRUE, FALSE, 'foo'); SELECT table_to_xml('testxmlschema.test1', FALSE, TRUE, ''); SELECT table_to_xml('testxmlschema.test1', TRUE, TRUE, ''); SELECT table_to_xml('testxmlschema.test2', FALSE, FALSE, ''); SELECT table_to_xmlschema('testxmlschema.test1', FALSE, FALSE, ''); SELECT table_to_xmlschema('testxmlschema.test1', TRUE, FALSE, ''); SELECT table_to_xmlschema('testxmlschema.test1', FALSE, TRUE, 'foo'); SELECT table_to_xmlschema('testxmlschema.test1', TRUE, TRUE, ''); SELECT table_to_xmlschema('testxmlschema.test2', FALSE, FALSE, ''); SELECT table_to_xml_and_xmlschema('testxmlschema.test1', FALSE, FALSE, ''); SELECT table_to_xml_and_xmlschema('testxmlschema.test1', TRUE, FALSE, ''); SELECT table_to_xml_and_xmlschema('testxmlschema.test1', FALSE, TRUE, ''); SELECT table_to_xml_and_xmlschema('testxmlschema.test1', TRUE, TRUE, 'foo'); SELECT query_to_xml('SELECT * FROM testxmlschema.test1', FALSE, FALSE, ''); SELECT query_to_xmlschema('SELECT * FROM testxmlschema.test1', FALSE, FALSE, ''); SELECT query_to_xml_and_xmlschema('SELECT * FROM testxmlschema.test1', TRUE, TRUE, ''); DECLARE xc CURSOR WITH HOLD FOR SELECT * FROM testxmlschema.test1 ORDER BY 1, 2; SELECT cursor_to_xml('xc'::refcursor, 5, FALSE, TRUE, ''); SELECT cursor_to_xmlschema('xc'::refcursor, FALSE, TRUE, ''); MOVE BACKWARD ALL IN xc; SELECT cursor_to_xml('xc'::refcursor, 5, TRUE, FALSE, ''); SELECT cursor_to_xmlschema('xc'::refcursor, TRUE, FALSE, ''); SELECT schema_to_xml('testxmlschema', FALSE, TRUE, ''); SELECT schema_to_xml('testxmlschema', TRUE, FALSE, ''); SELECT schema_to_xmlschema('testxmlschema', FALSE, TRUE, ''); SELECT schema_to_xmlschema('testxmlschema', TRUE, FALSE, ''); SELECT schema_to_xml_and_xmlschema('testxmlschema', TRUE, TRUE, 'foo'); -- test that domains are transformed like their base types CREATE DOMAIN testboolxmldomain AS bool; CREATE DOMAIN testdatexmldomain AS date; CREATE TABLE testxmlschema.test3 AS SELECT TRUE c1, TRUE::testboolxmldomain c2, '2013-02-21'::date c3, '2013-02-21'::testdatexmldomain c4; SELECT xmlforest(c1, c2, c3, c4) FROM testxmlschema.test3; SELECT table_to_xml('testxmlschema.test3', TRUE, TRUE, ''); pgFormatter-4.2/t/pg-test-files/sql/000077500000000000000000000000001361326045100173775ustar00rootroot00000000000000pgFormatter-4.2/t/pg-test-files/sql/advisory_lock.sql000066400000000000000000000100011361326045100227600ustar00rootroot00000000000000-- -- ADVISORY LOCKS -- BEGIN; SELECT pg_advisory_xact_lock(1), pg_advisory_xact_lock_shared(2), pg_advisory_xact_lock(1, 1), pg_advisory_xact_lock_shared(2, 2); SELECT locktype, classid, objid, objsubid, mode, granted FROM pg_locks WHERE locktype = 'advisory' ORDER BY classid, objid, objsubid; -- pg_advisory_unlock_all() shouldn't release xact locks SELECT pg_advisory_unlock_all(); SELECT count(*) FROM pg_locks WHERE locktype = 'advisory'; -- can't unlock xact locks SELECT pg_advisory_unlock(1), pg_advisory_unlock_shared(2), pg_advisory_unlock(1, 1), pg_advisory_unlock_shared(2, 2); -- automatically release xact locks at commit COMMIT; SELECT count(*) FROM pg_locks WHERE locktype = 'advisory'; BEGIN; -- holding both session and xact locks on the same objects, xact first SELECT pg_advisory_xact_lock(1), pg_advisory_xact_lock_shared(2), pg_advisory_xact_lock(1, 1), pg_advisory_xact_lock_shared(2, 2); SELECT locktype, classid, objid, objsubid, mode, granted FROM pg_locks WHERE locktype = 'advisory' ORDER BY classid, objid, objsubid; SELECT pg_advisory_lock(1), pg_advisory_lock_shared(2), pg_advisory_lock(1, 1), pg_advisory_lock_shared(2, 2); ROLLBACK; SELECT locktype, classid, objid, objsubid, mode, granted FROM pg_locks WHERE locktype = 'advisory' ORDER BY classid, objid, objsubid; -- unlocking session locks SELECT pg_advisory_unlock(1), pg_advisory_unlock(1), pg_advisory_unlock_shared(2), pg_advisory_unlock_shared(2), pg_advisory_unlock(1, 1), pg_advisory_unlock(1, 1), pg_advisory_unlock_shared(2, 2), pg_advisory_unlock_shared(2, 2); SELECT count(*) FROM pg_locks WHERE locktype = 'advisory'; BEGIN; -- holding both session and xact locks on the same objects, session first SELECT pg_advisory_lock(1), pg_advisory_lock_shared(2), pg_advisory_lock(1, 1), pg_advisory_lock_shared(2, 2); SELECT locktype, classid, objid, objsubid, mode, granted FROM pg_locks WHERE locktype = 'advisory' ORDER BY classid, objid, objsubid; SELECT pg_advisory_xact_lock(1), pg_advisory_xact_lock_shared(2), pg_advisory_xact_lock(1, 1), pg_advisory_xact_lock_shared(2, 2); ROLLBACK; SELECT locktype, classid, objid, objsubid, mode, granted FROM pg_locks WHERE locktype = 'advisory' ORDER BY classid, objid, objsubid; -- releasing all session locks SELECT pg_advisory_unlock_all(); SELECT count(*) FROM pg_locks WHERE locktype = 'advisory'; BEGIN; -- grabbing txn locks multiple times SELECT pg_advisory_xact_lock(1), pg_advisory_xact_lock(1), pg_advisory_xact_lock_shared(2), pg_advisory_xact_lock_shared(2), pg_advisory_xact_lock(1, 1), pg_advisory_xact_lock(1, 1), pg_advisory_xact_lock_shared(2, 2), pg_advisory_xact_lock_shared(2, 2); SELECT locktype, classid, objid, objsubid, mode, granted FROM pg_locks WHERE locktype = 'advisory' ORDER BY classid, objid, objsubid; COMMIT; SELECT count(*) FROM pg_locks WHERE locktype = 'advisory'; -- grabbing session locks multiple times SELECT pg_advisory_lock(1), pg_advisory_lock(1), pg_advisory_lock_shared(2), pg_advisory_lock_shared(2), pg_advisory_lock(1, 1), pg_advisory_lock(1, 1), pg_advisory_lock_shared(2, 2), pg_advisory_lock_shared(2, 2); SELECT locktype, classid, objid, objsubid, mode, granted FROM pg_locks WHERE locktype = 'advisory' ORDER BY classid, objid, objsubid; SELECT pg_advisory_unlock(1), pg_advisory_unlock(1), pg_advisory_unlock_shared(2), pg_advisory_unlock_shared(2), pg_advisory_unlock(1, 1), pg_advisory_unlock(1, 1), pg_advisory_unlock_shared(2, 2), pg_advisory_unlock_shared(2, 2); SELECT count(*) FROM pg_locks WHERE locktype = 'advisory'; -- .. and releasing them all at once SELECT pg_advisory_lock(1), pg_advisory_lock(1), pg_advisory_lock_shared(2), pg_advisory_lock_shared(2), pg_advisory_lock(1, 1), pg_advisory_lock(1, 1), pg_advisory_lock_shared(2, 2), pg_advisory_lock_shared(2, 2); SELECT locktype, classid, objid, objsubid, mode, granted FROM pg_locks WHERE locktype = 'advisory' ORDER BY classid, objid, objsubid; SELECT pg_advisory_unlock_all(); SELECT count(*) FROM pg_locks WHERE locktype = 'advisory'; pgFormatter-4.2/t/pg-test-files/sql/aggregates.sql000066400000000000000000000754051361326045100222440ustar00rootroot00000000000000-- -- AGGREGATES -- -- avoid bit-exact output here because operations may not be bit-exact. SET extra_float_digits = 0; SELECT avg(four) AS avg_1 FROM onek; SELECT avg(a) AS avg_32 FROM aggtest WHERE a < 100; -- In 7.1, avg(float4) is computed using float8 arithmetic. -- Round the result to 3 digits to avoid platform-specific results. SELECT avg(b)::numeric(10,3) AS avg_107_943 FROM aggtest; SELECT avg(gpa) AS avg_3_4 FROM ONLY student; SELECT sum(four) AS sum_1500 FROM onek; SELECT sum(a) AS sum_198 FROM aggtest; SELECT sum(b) AS avg_431_773 FROM aggtest; SELECT sum(gpa) AS avg_6_8 FROM ONLY student; SELECT max(four) AS max_3 FROM onek; SELECT max(a) AS max_100 FROM aggtest; SELECT max(aggtest.b) AS max_324_78 FROM aggtest; SELECT max(student.gpa) AS max_3_7 FROM student; SELECT stddev_pop(b) FROM aggtest; SELECT stddev_samp(b) FROM aggtest; SELECT var_pop(b) FROM aggtest; SELECT var_samp(b) FROM aggtest; SELECT stddev_pop(b::numeric) FROM aggtest; SELECT stddev_samp(b::numeric) FROM aggtest; SELECT var_pop(b::numeric) FROM aggtest; SELECT var_samp(b::numeric) FROM aggtest; -- population variance is defined for a single tuple, sample variance -- is not SELECT var_pop(1.0), var_samp(2.0); SELECT stddev_pop(3.0::numeric), stddev_samp(4.0::numeric); -- verify correct results for null and NaN inputs select sum(null::int4) from generate_series(1,3); select sum(null::int8) from generate_series(1,3); select sum(null::numeric) from generate_series(1,3); select sum(null::float8) from generate_series(1,3); select avg(null::int4) from generate_series(1,3); select avg(null::int8) from generate_series(1,3); select avg(null::numeric) from generate_series(1,3); select avg(null::float8) from generate_series(1,3); select sum('NaN'::numeric) from generate_series(1,3); select avg('NaN'::numeric) from generate_series(1,3); -- verify correct results for infinite inputs SELECT avg(x::float8), var_pop(x::float8) FROM (VALUES ('1'), ('infinity')) v(x); SELECT avg(x::float8), var_pop(x::float8) FROM (VALUES ('infinity'), ('1')) v(x); SELECT avg(x::float8), var_pop(x::float8) FROM (VALUES ('infinity'), ('infinity')) v(x); SELECT avg(x::float8), var_pop(x::float8) FROM (VALUES ('-infinity'), ('infinity')) v(x); -- test accuracy with a large input offset SELECT avg(x::float8), var_pop(x::float8) FROM (VALUES (100000003), (100000004), (100000006), (100000007)) v(x); SELECT avg(x::float8), var_pop(x::float8) FROM (VALUES (7000000000005), (7000000000007)) v(x); -- SQL2003 binary aggregates SELECT regr_count(b, a) FROM aggtest; SELECT regr_sxx(b, a) FROM aggtest; SELECT regr_syy(b, a) FROM aggtest; SELECT regr_sxy(b, a) FROM aggtest; SELECT regr_avgx(b, a), regr_avgy(b, a) FROM aggtest; SELECT regr_r2(b, a) FROM aggtest; SELECT regr_slope(b, a), regr_intercept(b, a) FROM aggtest; SELECT covar_pop(b, a), covar_samp(b, a) FROM aggtest; SELECT corr(b, a) FROM aggtest; -- test accum and combine functions directly CREATE TABLE regr_test (x float8, y float8); INSERT INTO regr_test VALUES (10,150),(20,250),(30,350),(80,540),(100,200); SELECT count(*), sum(x), regr_sxx(y,x), sum(y),regr_syy(y,x), regr_sxy(y,x) FROM regr_test WHERE x IN (10,20,30,80); SELECT count(*), sum(x), regr_sxx(y,x), sum(y),regr_syy(y,x), regr_sxy(y,x) FROM regr_test; SELECT float8_accum('{4,140,2900}'::float8[], 100); SELECT float8_regr_accum('{4,140,2900,1290,83075,15050}'::float8[], 200, 100); SELECT count(*), sum(x), regr_sxx(y,x), sum(y),regr_syy(y,x), regr_sxy(y,x) FROM regr_test WHERE x IN (10,20,30); SELECT count(*), sum(x), regr_sxx(y,x), sum(y),regr_syy(y,x), regr_sxy(y,x) FROM regr_test WHERE x IN (80,100); SELECT float8_combine('{3,60,200}'::float8[], '{0,0,0}'::float8[]); SELECT float8_combine('{0,0,0}'::float8[], '{2,180,200}'::float8[]); SELECT float8_combine('{3,60,200}'::float8[], '{2,180,200}'::float8[]); SELECT float8_regr_combine('{3,60,200,750,20000,2000}'::float8[], '{0,0,0,0,0,0}'::float8[]); SELECT float8_regr_combine('{0,0,0,0,0,0}'::float8[], '{2,180,200,740,57800,-3400}'::float8[]); SELECT float8_regr_combine('{3,60,200,750,20000,2000}'::float8[], '{2,180,200,740,57800,-3400}'::float8[]); DROP TABLE regr_test; -- test count, distinct SELECT count(four) AS cnt_1000 FROM onek; SELECT count(DISTINCT four) AS cnt_4 FROM onek; select ten, count(*), sum(four) from onek group by ten order by ten; select ten, count(four), sum(DISTINCT four) from onek group by ten order by ten; -- user-defined aggregates SELECT newavg(four) AS avg_1 FROM onek; SELECT newsum(four) AS sum_1500 FROM onek; SELECT newcnt(four) AS cnt_1000 FROM onek; SELECT newcnt(*) AS cnt_1000 FROM onek; SELECT oldcnt(*) AS cnt_1000 FROM onek; SELECT sum2(q1,q2) FROM int8_tbl; -- test for outer-level aggregates -- this should work select ten, sum(distinct four) from onek a group by ten having exists (select 1 from onek b where sum(distinct a.four) = b.four); -- this should fail because subquery has an agg of its own in WHERE select ten, sum(distinct four) from onek a group by ten having exists (select 1 from onek b where sum(distinct a.four + b.four) = b.four); -- Test handling of sublinks within outer-level aggregates. -- Per bug report from Daniel Grace. select (select max((select i.unique2 from tenk1 i where i.unique1 = o.unique1))) from tenk1 o; -- Test handling of Params within aggregate arguments in hashed aggregation. -- Per bug report from Jeevan Chalke. explain (verbose, costs off) select s1, s2, sm from generate_series(1, 3) s1, lateral (select s2, sum(s1 + s2) sm from generate_series(1, 3) s2 group by s2) ss order by 1, 2; select s1, s2, sm from generate_series(1, 3) s1, lateral (select s2, sum(s1 + s2) sm from generate_series(1, 3) s2 group by s2) ss order by 1, 2; explain (verbose, costs off) select array(select sum(x+y) s from generate_series(1,3) y group by y order by s) from generate_series(1,3) x; select array(select sum(x+y) s from generate_series(1,3) y group by y order by s) from generate_series(1,3) x; -- -- test for bitwise integer aggregates -- CREATE TEMPORARY TABLE bitwise_test( i2 INT2, i4 INT4, i8 INT8, i INTEGER, x INT2, y BIT(4) ); -- empty case SELECT BIT_AND(i2) AS "?", BIT_OR(i4) AS "?" FROM bitwise_test; SELECT BIT_AND(i2) AS "1", BIT_AND(i4) AS "1", BIT_AND(i8) AS "1", BIT_AND(i) AS "?", BIT_AND(x) AS "0", BIT_AND(y) AS "0100", BIT_OR(i2) AS "7", BIT_OR(i4) AS "7", BIT_OR(i8) AS "7", BIT_OR(i) AS "?", BIT_OR(x) AS "7", BIT_OR(y) AS "1101" FROM bitwise_test; -- -- test boolean aggregates -- -- first test all possible transition and final states SELECT -- boolean and transitions -- null because strict booland_statefunc(NULL, NULL) IS NULL AS "t", booland_statefunc(TRUE, NULL) IS NULL AS "t", booland_statefunc(FALSE, NULL) IS NULL AS "t", booland_statefunc(NULL, TRUE) IS NULL AS "t", booland_statefunc(NULL, FALSE) IS NULL AS "t", -- and actual computations booland_statefunc(TRUE, TRUE) AS "t", NOT booland_statefunc(TRUE, FALSE) AS "t", NOT booland_statefunc(FALSE, TRUE) AS "t", NOT booland_statefunc(FALSE, FALSE) AS "t"; SELECT -- boolean or transitions -- null because strict boolor_statefunc(NULL, NULL) IS NULL AS "t", boolor_statefunc(TRUE, NULL) IS NULL AS "t", boolor_statefunc(FALSE, NULL) IS NULL AS "t", boolor_statefunc(NULL, TRUE) IS NULL AS "t", boolor_statefunc(NULL, FALSE) IS NULL AS "t", -- actual computations boolor_statefunc(TRUE, TRUE) AS "t", boolor_statefunc(TRUE, FALSE) AS "t", boolor_statefunc(FALSE, TRUE) AS "t", NOT boolor_statefunc(FALSE, FALSE) AS "t"; CREATE TEMPORARY TABLE bool_test( b1 BOOL, b2 BOOL, b3 BOOL, b4 BOOL); -- empty case SELECT BOOL_AND(b1) AS "n", BOOL_OR(b3) AS "n" FROM bool_test; SELECT BOOL_AND(b1) AS "f", BOOL_AND(b2) AS "t", BOOL_AND(b3) AS "f", BOOL_AND(b4) AS "n", BOOL_AND(NOT b2) AS "f", BOOL_AND(NOT b3) AS "t" FROM bool_test; SELECT EVERY(b1) AS "f", EVERY(b2) AS "t", EVERY(b3) AS "f", EVERY(b4) AS "n", EVERY(NOT b2) AS "f", EVERY(NOT b3) AS "t" FROM bool_test; SELECT BOOL_OR(b1) AS "t", BOOL_OR(b2) AS "t", BOOL_OR(b3) AS "f", BOOL_OR(b4) AS "n", BOOL_OR(NOT b2) AS "f", BOOL_OR(NOT b3) AS "t" FROM bool_test; -- -- Test cases that should be optimized into indexscans instead of -- the generic aggregate implementation. -- -- Basic cases explain (costs off) select min(unique1) from tenk1; select min(unique1) from tenk1; explain (costs off) select max(unique1) from tenk1; select max(unique1) from tenk1; explain (costs off) select max(unique1) from tenk1 where unique1 < 42; select max(unique1) from tenk1 where unique1 < 42; explain (costs off) select max(unique1) from tenk1 where unique1 > 42; select max(unique1) from tenk1 where unique1 > 42; -- the planner may choose a generic aggregate here if parallel query is -- enabled, since that plan will be parallel safe and the "optimized" -- plan, which has almost identical cost, will not be. we want to test -- the optimized plan, so temporarily disable parallel query. begin; set local max_parallel_workers_per_gather = 0; explain (costs off) select max(unique1) from tenk1 where unique1 > 42000; select max(unique1) from tenk1 where unique1 > 42000; rollback; -- multi-column index (uses tenk1_thous_tenthous) explain (costs off) select max(tenthous) from tenk1 where thousand = 33; select max(tenthous) from tenk1 where thousand = 33; explain (costs off) select min(tenthous) from tenk1 where thousand = 33; select min(tenthous) from tenk1 where thousand = 33; -- check parameter propagation into an indexscan subquery explain (costs off) select f1, (select min(unique1) from tenk1 where unique1 > f1) AS gt from int4_tbl; select f1, (select min(unique1) from tenk1 where unique1 > f1) AS gt from int4_tbl; -- check some cases that were handled incorrectly in 8.3.0 explain (costs off) select distinct max(unique2) from tenk1; select distinct max(unique2) from tenk1; explain (costs off) select max(unique2) from tenk1 order by 1; select max(unique2) from tenk1 order by 1; explain (costs off) select max(unique2) from tenk1 order by max(unique2); select max(unique2) from tenk1 order by max(unique2); explain (costs off) select max(unique2) from tenk1 order by max(unique2)+1; select max(unique2) from tenk1 order by max(unique2)+1; explain (costs off) select max(unique2), generate_series(1,3) as g from tenk1 order by g desc; select max(unique2), generate_series(1,3) as g from tenk1 order by g desc; -- interesting corner case: constant gets optimized into a seqscan explain (costs off) select max(100) from tenk1; select max(100) from tenk1; -- try it on an inheritance tree create table minmaxtest(f1 int); create table minmaxtest1() inherits (minmaxtest); create table minmaxtest2() inherits (minmaxtest); create table minmaxtest3() inherits (minmaxtest); create index minmaxtesti on minmaxtest(f1); create index minmaxtest1i on minmaxtest1(f1); create index minmaxtest2i on minmaxtest2(f1 desc); create index minmaxtest3i on minmaxtest3(f1) where f1 is not null; insert into minmaxtest values(11), (12); insert into minmaxtest1 values(13), (14); insert into minmaxtest2 values(15), (16); insert into minmaxtest3 values(17), (18); explain (costs off) select min(f1), max(f1) from minmaxtest; select min(f1), max(f1) from minmaxtest; -- DISTINCT doesn't do anything useful here, but it shouldn't fail explain (costs off) select distinct min(f1), max(f1) from minmaxtest; select distinct min(f1), max(f1) from minmaxtest; drop table minmaxtest cascade; -- check for correct detection of nested-aggregate errors select max(min(unique1)) from tenk1; select (select max(min(unique1)) from int8_tbl) from tenk1; -- -- Test removal of redundant GROUP BY columns -- create temp table t1 (a int, b int, c int, d int, primary key (a, b)); create temp table t2 (x int, y int, z int, primary key (x, y)); create temp table t3 (a int, b int, c int, primary key(a, b) deferrable); -- Non-primary-key columns can be removed from GROUP BY explain (costs off) select * from t1 group by a,b,c,d; -- No removal can happen if the complete PK is not present in GROUP BY explain (costs off) select a,c from t1 group by a,c,d; -- Test removal across multiple relations explain (costs off) select * from t1 inner join t2 on t1.a = t2.x and t1.b = t2.y group by t1.a,t1.b,t1.c,t1.d,t2.x,t2.y,t2.z; -- Test case where t1 can be optimized but not t2 explain (costs off) select t1.*,t2.x,t2.z from t1 inner join t2 on t1.a = t2.x and t1.b = t2.y group by t1.a,t1.b,t1.c,t1.d,t2.x,t2.z; -- Cannot optimize when PK is deferrable explain (costs off) select * from t3 group by a,b,c; drop table t1; drop table t2; drop table t3; -- -- Test combinations of DISTINCT and/or ORDER BY -- select array_agg(a order by b) from (values (1,4),(2,3),(3,1),(4,2)) v(a,b); select array_agg(a order by a) from (values (1,4),(2,3),(3,1),(4,2)) v(a,b); select array_agg(a order by a desc) from (values (1,4),(2,3),(3,1),(4,2)) v(a,b); select array_agg(b order by a desc) from (values (1,4),(2,3),(3,1),(4,2)) v(a,b); select array_agg(distinct a) from (values (1),(2),(1),(3),(null),(2)) v(a); select array_agg(distinct a order by a) from (values (1),(2),(1),(3),(null),(2)) v(a); select array_agg(distinct a order by a desc) from (values (1),(2),(1),(3),(null),(2)) v(a); select array_agg(distinct a order by a desc nulls last) from (values (1),(2),(1),(3),(null),(2)) v(a); -- multi-arg aggs, strict/nonstrict, distinct/order by select aggfstr(a,b,c) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c); select aggfns(a,b,c) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c); select aggfstr(distinct a,b,c) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), generate_series(1,3) i; select aggfns(distinct a,b,c) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), generate_series(1,3) i; select aggfstr(distinct a,b,c order by b) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), generate_series(1,3) i; select aggfns(distinct a,b,c order by b) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), generate_series(1,3) i; -- test specific code paths select aggfns(distinct a,a,c order by c using ~<~,a) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), generate_series(1,2) i; select aggfns(distinct a,a,c order by c using ~<~) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), generate_series(1,2) i; select aggfns(distinct a,a,c order by a) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), generate_series(1,2) i; select aggfns(distinct a,b,c order by a,c using ~<~,b) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), generate_series(1,2) i; -- check node I/O via view creation and usage, also deparsing logic create view agg_view1 as select aggfns(a,b,c) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c); select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); create or replace view agg_view1 as select aggfns(distinct a,b,c) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), generate_series(1,3) i; select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); create or replace view agg_view1 as select aggfns(distinct a,b,c order by b) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), generate_series(1,3) i; select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); create or replace view agg_view1 as select aggfns(a,b,c order by b+1) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c); select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); create or replace view agg_view1 as select aggfns(a,a,c order by b) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c); select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); create or replace view agg_view1 as select aggfns(a,b,c order by c using ~<~) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c); select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); create or replace view agg_view1 as select aggfns(distinct a,b,c order by a,c using ~<~,b) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), generate_series(1,2) i; select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); drop view agg_view1; -- incorrect DISTINCT usage errors select aggfns(distinct a,b,c order by i) from (values (1,1,'foo')) v(a,b,c), generate_series(1,2) i; select aggfns(distinct a,b,c order by a,b+1) from (values (1,1,'foo')) v(a,b,c), generate_series(1,2) i; select aggfns(distinct a,b,c order by a,b,i,c) from (values (1,1,'foo')) v(a,b,c), generate_series(1,2) i; select aggfns(distinct a,a,c order by a,b) from (values (1,1,'foo')) v(a,b,c), generate_series(1,2) i; -- string_agg tests select string_agg(a,',') from (values('aaaa'),('bbbb'),('cccc')) g(a); select string_agg(a,',') from (values('aaaa'),(null),('bbbb'),('cccc')) g(a); select string_agg(a,'AB') from (values(null),(null),('bbbb'),('cccc')) g(a); select string_agg(a,',') from (values(null),(null)) g(a); -- check some implicit casting cases, as per bug #5564 select string_agg(distinct f1, ',' order by f1) from varchar_tbl; -- ok select string_agg(distinct f1::text, ',' order by f1) from varchar_tbl; -- not ok select string_agg(distinct f1, ',' order by f1::text) from varchar_tbl; -- not ok select string_agg(distinct f1::text, ',' order by f1::text) from varchar_tbl; -- ok -- string_agg bytea tests create table bytea_test_table(v bytea); select string_agg(v, '') from bytea_test_table; insert into bytea_test_table values(decode('ff','hex')); select string_agg(v, '') from bytea_test_table; insert into bytea_test_table values(decode('aa','hex')); select string_agg(v, '') from bytea_test_table; select string_agg(v, NULL) from bytea_test_table; select string_agg(v, decode('ee', 'hex')) from bytea_test_table; drop table bytea_test_table; -- FILTER tests select min(unique1) filter (where unique1 > 100) from tenk1; select sum(1/ten) filter (where ten > 0) from tenk1; select ten, sum(distinct four) filter (where four::text ~ '123') from onek a group by ten; select ten, sum(distinct four) filter (where four > 10) from onek a group by ten having exists (select 1 from onek b where sum(distinct a.four) = b.four); select max(foo COLLATE "C") filter (where (bar collate "POSIX") > '0') from (values ('a', 'b')) AS v(foo,bar); -- outer reference in FILTER (PostgreSQL extension) select (select count(*) from (values (1)) t0(inner_c)) from (values (2),(3)) t1(outer_c); -- inner query is aggregation query select (select count(*) filter (where outer_c <> 0) from (values (1)) t0(inner_c)) from (values (2),(3)) t1(outer_c); -- outer query is aggregation query select (select count(inner_c) filter (where outer_c <> 0) from (values (1)) t0(inner_c)) from (values (2),(3)) t1(outer_c); -- inner query is aggregation query select (select max((select i.unique2 from tenk1 i where i.unique1 = o.unique1)) filter (where o.unique1 < 10)) from tenk1 o; -- outer query is aggregation query -- subquery in FILTER clause (PostgreSQL extension) select sum(unique1) FILTER (WHERE unique1 IN (SELECT unique1 FROM onek where unique1 < 100)) FROM tenk1; -- exercise lots of aggregate parts with FILTER select aggfns(distinct a,b,c order by a,c using ~<~,b) filter (where a > 1) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), generate_series(1,2) i; -- ordered-set aggregates select p, percentile_cont(p) within group (order by x::float8) from generate_series(1,5) x, (values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) group by p order by p; select p, percentile_cont(p order by p) within group (order by x) -- error from generate_series(1,5) x, (values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) group by p order by p; select p, sum() within group (order by x::float8) -- error from generate_series(1,5) x, (values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) group by p order by p; select p, percentile_cont(p,p) -- error from generate_series(1,5) x, (values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) group by p order by p; select percentile_cont(0.5) within group (order by b) from aggtest; select percentile_cont(0.5) within group (order by b), sum(b) from aggtest; select percentile_cont(0.5) within group (order by thousand) from tenk1; select percentile_disc(0.5) within group (order by thousand) from tenk1; select rank(3) within group (order by x) from (values (1),(1),(2),(2),(3),(3),(4)) v(x); select cume_dist(3) within group (order by x) from (values (1),(1),(2),(2),(3),(3),(4)) v(x); select percent_rank(3) within group (order by x) from (values (1),(1),(2),(2),(3),(3),(4),(5)) v(x); select dense_rank(3) within group (order by x) from (values (1),(1),(2),(2),(3),(3),(4)) v(x); select percentile_disc(array[0,0.1,0.25,0.5,0.75,0.9,1]) within group (order by thousand) from tenk1; select percentile_cont(array[0,0.25,0.5,0.75,1]) within group (order by thousand) from tenk1; select percentile_disc(array[[null,1,0.5],[0.75,0.25,null]]) within group (order by thousand) from tenk1; select percentile_cont(array[0,1,0.25,0.75,0.5,1,0.3,0.32,0.35,0.38,0.4]) within group (order by x) from generate_series(1,6) x; select ten, mode() within group (order by string4) from tenk1 group by ten; select percentile_disc(array[0.25,0.5,0.75]) within group (order by x) from unnest('{fred,jim,fred,jack,jill,fred,jill,jim,jim,sheila,jim,sheila}'::text[]) u(x); -- check collation propagates up in suitable cases: select pg_collation_for(percentile_disc(1) within group (order by x collate "POSIX")) from (values ('fred'),('jim')) v(x); -- ordered-set aggs created with CREATE AGGREGATE select test_rank(3) within group (order by x) from (values (1),(1),(2),(2),(3),(3),(4)) v(x); select test_percentile_disc(0.5) within group (order by thousand) from tenk1; -- ordered-set aggs can't use ungrouped vars in direct args: select rank(x) within group (order by x) from generate_series(1,5) x; -- outer-level agg can't use a grouped arg of a lower level, either: select array(select percentile_disc(a) within group (order by x) from (values (0.3),(0.7)) v(a) group by a) from generate_series(1,5) g(x); -- agg in the direct args is a grouping violation, too: select rank(sum(x)) within group (order by x) from generate_series(1,5) x; -- hypothetical-set type unification and argument-count failures: select rank(3) within group (order by x) from (values ('fred'),('jim')) v(x); select rank(3) within group (order by stringu1,stringu2) from tenk1; select rank('fred') within group (order by x) from generate_series(1,5) x; select rank('adam'::text collate "C") within group (order by x collate "POSIX") from (values ('fred'),('jim')) v(x); -- hypothetical-set type unification successes: select rank('adam'::varchar) within group (order by x) from (values ('fred'),('jim')) v(x); select rank('3') within group (order by x) from generate_series(1,5) x; -- divide by zero check select percent_rank(0) within group (order by x) from generate_series(1,0) x; -- deparse and multiple features: create view aggordview1 as select ten, percentile_disc(0.5) within group (order by thousand) as p50, percentile_disc(0.5) within group (order by thousand) filter (where hundred=1) as px, rank(5,'AZZZZ',50) within group (order by hundred, string4 desc, hundred) from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); select * from aggordview1 order by ten; drop view aggordview1; -- variadic aggregates select least_agg(q1,q2) from int8_tbl; select least_agg(variadic array[q1,q2]) from int8_tbl; -- test aggregates with common transition functions share the same states begin work; create type avg_state as (total bigint, count bigint); create or replace function avg_transfn(state avg_state, n int) returns avg_state as $$ declare new_state avg_state; begin raise notice 'avg_transfn called with %', n; if state is null then if n is not null then new_state.total := n; new_state.count := 1; return new_state; end if; return null; elsif n is not null then state.total := state.total + n; state.count := state.count + 1; return state; end if; return null; end $$ language plpgsql; create function avg_finalfn(state avg_state) returns int4 as $$ begin if state is null then return NULL; else return state.total / state.count; end if; end $$ language plpgsql; create function sum_finalfn(state avg_state) returns int4 as $$ begin if state is null then return NULL; else return state.total; end if; end $$ language plpgsql; create aggregate my_avg(int4) ( stype = avg_state, sfunc = avg_transfn, finalfunc = avg_finalfn ); create aggregate my_sum(int4) ( stype = avg_state, sfunc = avg_transfn, finalfunc = sum_finalfn ); -- aggregate state should be shared as aggs are the same. select my_avg(one),my_avg(one) from (values(1),(3)) t(one); -- aggregate state should be shared as transfn is the same for both aggs. select my_avg(one),my_sum(one) from (values(1),(3)) t(one); -- same as previous one, but with DISTINCT, which requires sorting the input. select my_avg(distinct one),my_sum(distinct one) from (values(1),(3),(1)) t(one); -- shouldn't share states due to the distinctness not matching. select my_avg(distinct one),my_sum(one) from (values(1),(3)) t(one); -- shouldn't share states due to the filter clause not matching. select my_avg(one) filter (where one > 1),my_sum(one) from (values(1),(3)) t(one); -- this should not share the state due to different input columns. select my_avg(one),my_sum(two) from (values(1,2),(3,4)) t(one,two); -- exercise cases where OSAs share state select percentile_cont(0.5) within group (order by a), percentile_disc(0.5) within group (order by a) from (values(1::float8),(3),(5),(7)) t(a); select percentile_cont(0.25) within group (order by a), percentile_disc(0.5) within group (order by a) from (values(1::float8),(3),(5),(7)) t(a); -- these can't share state currently select rank(4) within group (order by a), dense_rank(4) within group (order by a) from (values(1),(3),(5),(7)) t(a); -- test that aggs with the same sfunc and initcond share the same agg state create aggregate my_sum_init(int4) ( stype = avg_state, sfunc = avg_transfn, finalfunc = sum_finalfn, initcond = '(10,0)' ); create aggregate my_avg_init(int4) ( stype = avg_state, sfunc = avg_transfn, finalfunc = avg_finalfn, initcond = '(10,0)' ); create aggregate my_avg_init2(int4) ( stype = avg_state, sfunc = avg_transfn, finalfunc = avg_finalfn, initcond = '(4,0)' ); -- state should be shared if INITCONDs are matching select my_sum_init(one),my_avg_init(one) from (values(1),(3)) t(one); -- Varying INITCONDs should cause the states not to be shared. select my_sum_init(one),my_avg_init2(one) from (values(1),(3)) t(one); rollback; -- test aggregate state sharing to ensure it works if one aggregate has a -- finalfn and the other one has none. begin work; create or replace function sum_transfn(state int4, n int4) returns int4 as $$ declare new_state int4; begin raise notice 'sum_transfn called with %', n; if state is null then if n is not null then new_state := n; return new_state; end if; return null; elsif n is not null then state := state + n; return state; end if; return null; end $$ language plpgsql; create function halfsum_finalfn(state int4) returns int4 as $$ begin if state is null then return NULL; else return state / 2; end if; end $$ language plpgsql; create aggregate my_sum(int4) ( stype = int4, sfunc = sum_transfn ); create aggregate my_half_sum(int4) ( stype = int4, sfunc = sum_transfn, finalfunc = halfsum_finalfn ); -- Agg state should be shared even though my_sum has no finalfn select my_sum(one),my_half_sum(one) from (values(1),(2),(3),(4)) t(one); rollback; -- test that the aggregate transition logic correctly handles -- transition / combine functions returning NULL -- First test the case of a normal transition function returning NULL BEGIN; CREATE FUNCTION balkifnull(int8, int4) RETURNS int8 STRICT LANGUAGE plpgsql AS $$ BEGIN IF $1 IS NULL THEN RAISE 'erroneously called with NULL argument'; END IF; RETURN NULL; END$$; CREATE AGGREGATE balk(int4) ( SFUNC = balkifnull(int8, int4), STYPE = int8, PARALLEL = SAFE, INITCOND = '0' ); SELECT balk(hundred) FROM tenk1; ROLLBACK; -- Secondly test the case of a parallel aggregate combiner function -- returning NULL. For that use normal transition function, but a -- combiner function returning NULL. BEGIN ISOLATION LEVEL REPEATABLE READ; CREATE FUNCTION balkifnull(int8, int8) RETURNS int8 PARALLEL SAFE STRICT LANGUAGE plpgsql AS $$ BEGIN IF $1 IS NULL THEN RAISE 'erroneously called with NULL argument'; END IF; RETURN NULL; END$$; CREATE AGGREGATE balk(int4) ( SFUNC = int4_sum(int8, int4), STYPE = int8, COMBINEFUNC = balkifnull(int8, int8), PARALLEL = SAFE, INITCOND = '0' ); -- force use of parallelism ALTER TABLE tenk1 set (parallel_workers = 4); SET LOCAL parallel_setup_cost=0; SET LOCAL max_parallel_workers_per_gather=4; EXPLAIN (COSTS OFF) SELECT balk(hundred) FROM tenk1; SELECT balk(hundred) FROM tenk1; ROLLBACK; -- test coverage for aggregate combine/serial/deserial functions BEGIN ISOLATION LEVEL REPEATABLE READ; SET parallel_setup_cost = 0; SET parallel_tuple_cost = 0; SET min_parallel_table_scan_size = 0; SET max_parallel_workers_per_gather = 4; SET enable_indexonlyscan = off; -- variance(int4) covers numeric_poly_combine -- sum(int8) covers int8_avg_combine EXPLAIN (COSTS OFF) SELECT variance(unique1::int4), sum(unique1::int8) FROM tenk1; SELECT variance(unique1::int4), sum(unique1::int8) FROM tenk1; ROLLBACK; -- test coverage for dense_rank SELECT dense_rank(x) WITHIN GROUP (ORDER BY x) FROM (VALUES (1),(1),(2),(2),(3),(3)) v(x) GROUP BY (x) ORDER BY 1; -- Ensure that the STRICT checks for aggregates does not take NULLness -- of ORDER BY columns into account. See bug report around -- 2a505161-2727-2473-7c46-591ed108ac52@email.cz SELECT min(x ORDER BY y) FROM (VALUES(1, NULL)) AS d(x,y); SELECT min(x ORDER BY y) FROM (VALUES(1, 2)) AS d(x,y); -- check collation-sensitive matching between grouping expressions select v||'a', case v||'a' when 'aa' then 1 else 0 end, count(*) from unnest(array['a','b']) u(v) group by v||'a' order by 1; select v||'a', case when v||'a' = 'aa' then 1 else 0 end, count(*) from unnest(array['a','b']) u(v) group by v||'a' order by 1; pgFormatter-4.2/t/pg-test-files/sql/alter_generic.sql000066400000000000000000000652161361326045100227350ustar00rootroot00000000000000-- -- Test for ALTER some_object {RENAME TO, OWNER TO, SET SCHEMA} -- -- Clean up in case a prior regression run failed SET client_min_messages TO 'warning'; DROP ROLE IF EXISTS regress_alter_generic_user1; DROP ROLE IF EXISTS regress_alter_generic_user2; DROP ROLE IF EXISTS regress_alter_generic_user3; RESET client_min_messages; CREATE USER regress_alter_generic_user3; CREATE USER regress_alter_generic_user2; CREATE USER regress_alter_generic_user1 IN ROLE regress_alter_generic_user3; CREATE SCHEMA alt_nsp1; CREATE SCHEMA alt_nsp2; GRANT ALL ON SCHEMA alt_nsp1, alt_nsp2 TO public; SET search_path = alt_nsp1, public; -- -- Function and Aggregate -- SET SESSION AUTHORIZATION regress_alter_generic_user1; CREATE FUNCTION alt_func1(int) RETURNS int LANGUAGE sql AS 'SELECT $1 + 1'; CREATE FUNCTION alt_func2(int) RETURNS int LANGUAGE sql AS 'SELECT $1 - 1'; CREATE AGGREGATE alt_agg1 ( sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond = 0 ); CREATE AGGREGATE alt_agg2 ( sfunc1 = int4mi, basetype = int4, stype1 = int4, initcond = 0 ); ALTER AGGREGATE alt_func1(int) RENAME TO alt_func3; -- failed (not aggregate) ALTER AGGREGATE alt_func1(int) OWNER TO regress_alter_generic_user3; -- failed (not aggregate) ALTER AGGREGATE alt_func1(int) SET SCHEMA alt_nsp2; -- failed (not aggregate) ALTER FUNCTION alt_func1(int) RENAME TO alt_func2; -- failed (name conflict) ALTER FUNCTION alt_func1(int) RENAME TO alt_func3; -- OK ALTER FUNCTION alt_func2(int) OWNER TO regress_alter_generic_user2; -- failed (no role membership) ALTER FUNCTION alt_func2(int) OWNER TO regress_alter_generic_user3; -- OK ALTER FUNCTION alt_func2(int) SET SCHEMA alt_nsp1; -- OK, already there ALTER FUNCTION alt_func2(int) SET SCHEMA alt_nsp2; -- OK ALTER AGGREGATE alt_agg1(int) RENAME TO alt_agg2; -- failed (name conflict) ALTER AGGREGATE alt_agg1(int) RENAME TO alt_agg3; -- OK ALTER AGGREGATE alt_agg2(int) OWNER TO regress_alter_generic_user2; -- failed (no role membership) ALTER AGGREGATE alt_agg2(int) OWNER TO regress_alter_generic_user3; -- OK ALTER AGGREGATE alt_agg2(int) SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_generic_user2; CREATE FUNCTION alt_func1(int) RETURNS int LANGUAGE sql AS 'SELECT $1 + 2'; CREATE FUNCTION alt_func2(int) RETURNS int LANGUAGE sql AS 'SELECT $1 - 2'; CREATE AGGREGATE alt_agg1 ( sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond = 100 ); CREATE AGGREGATE alt_agg2 ( sfunc1 = int4mi, basetype = int4, stype1 = int4, initcond = -100 ); ALTER FUNCTION alt_func3(int) RENAME TO alt_func4; -- failed (not owner) ALTER FUNCTION alt_func1(int) RENAME TO alt_func4; -- OK ALTER FUNCTION alt_func3(int) OWNER TO regress_alter_generic_user2; -- failed (not owner) ALTER FUNCTION alt_func2(int) OWNER TO regress_alter_generic_user3; -- failed (no role membership) ALTER FUNCTION alt_func3(int) SET SCHEMA alt_nsp2; -- failed (not owner) ALTER FUNCTION alt_func2(int) SET SCHEMA alt_nsp2; -- failed (name conflicts) ALTER AGGREGATE alt_agg3(int) RENAME TO alt_agg4; -- failed (not owner) ALTER AGGREGATE alt_agg1(int) RENAME TO alt_agg4; -- OK ALTER AGGREGATE alt_agg3(int) OWNER TO regress_alter_generic_user2; -- failed (not owner) ALTER AGGREGATE alt_agg2(int) OWNER TO regress_alter_generic_user3; -- failed (no role membership) ALTER AGGREGATE alt_agg3(int) SET SCHEMA alt_nsp2; -- failed (not owner) ALTER AGGREGATE alt_agg2(int) SET SCHEMA alt_nsp2; -- failed (name conflict) RESET SESSION AUTHORIZATION; SELECT n.nspname, proname, prorettype::regtype, prokind, a.rolname FROM pg_proc p, pg_namespace n, pg_authid a WHERE p.pronamespace = n.oid AND p.proowner = a.oid AND n.nspname IN ('alt_nsp1', 'alt_nsp2') ORDER BY nspname, proname; -- -- We would test collations here, but it's not possible because the error -- messages tend to be nonportable. -- -- -- Conversion -- SET SESSION AUTHORIZATION regress_alter_generic_user1; CREATE CONVERSION alt_conv1 FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; CREATE CONVERSION alt_conv2 FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; ALTER CONVERSION alt_conv1 RENAME TO alt_conv2; -- failed (name conflict) ALTER CONVERSION alt_conv1 RENAME TO alt_conv3; -- OK ALTER CONVERSION alt_conv2 OWNER TO regress_alter_generic_user2; -- failed (no role membership) ALTER CONVERSION alt_conv2 OWNER TO regress_alter_generic_user3; -- OK ALTER CONVERSION alt_conv2 SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_generic_user2; CREATE CONVERSION alt_conv1 FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; CREATE CONVERSION alt_conv2 FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; ALTER CONVERSION alt_conv3 RENAME TO alt_conv4; -- failed (not owner) ALTER CONVERSION alt_conv1 RENAME TO alt_conv4; -- OK ALTER CONVERSION alt_conv3 OWNER TO regress_alter_generic_user2; -- failed (not owner) ALTER CONVERSION alt_conv2 OWNER TO regress_alter_generic_user3; -- failed (no role membership) ALTER CONVERSION alt_conv3 SET SCHEMA alt_nsp2; -- failed (not owner) ALTER CONVERSION alt_conv2 SET SCHEMA alt_nsp2; -- failed (name conflict) RESET SESSION AUTHORIZATION; SELECT n.nspname, c.conname, a.rolname FROM pg_conversion c, pg_namespace n, pg_authid a WHERE c.connamespace = n.oid AND c.conowner = a.oid AND n.nspname IN ('alt_nsp1', 'alt_nsp2') ORDER BY nspname, conname; -- -- Foreign Data Wrapper and Foreign Server -- CREATE FOREIGN DATA WRAPPER alt_fdw1; CREATE FOREIGN DATA WRAPPER alt_fdw2; CREATE SERVER alt_fserv1 FOREIGN DATA WRAPPER alt_fdw1; CREATE SERVER alt_fserv2 FOREIGN DATA WRAPPER alt_fdw2; ALTER FOREIGN DATA WRAPPER alt_fdw1 RENAME TO alt_fdw2; -- failed (name conflict) ALTER FOREIGN DATA WRAPPER alt_fdw1 RENAME TO alt_fdw3; -- OK ALTER SERVER alt_fserv1 RENAME TO alt_fserv2; -- failed (name conflict) ALTER SERVER alt_fserv1 RENAME TO alt_fserv3; -- OK SELECT fdwname FROM pg_foreign_data_wrapper WHERE fdwname like 'alt_fdw%'; SELECT srvname FROM pg_foreign_server WHERE srvname like 'alt_fserv%'; -- -- Procedural Language -- CREATE LANGUAGE alt_lang1 HANDLER plpgsql_call_handler; CREATE LANGUAGE alt_lang2 HANDLER plpgsql_call_handler; ALTER LANGUAGE alt_lang1 OWNER TO regress_alter_generic_user1; -- OK ALTER LANGUAGE alt_lang2 OWNER TO regress_alter_generic_user2; -- OK SET SESSION AUTHORIZATION regress_alter_generic_user1; ALTER LANGUAGE alt_lang1 RENAME TO alt_lang2; -- failed (name conflict) ALTER LANGUAGE alt_lang2 RENAME TO alt_lang3; -- failed (not owner) ALTER LANGUAGE alt_lang1 RENAME TO alt_lang3; -- OK ALTER LANGUAGE alt_lang2 OWNER TO regress_alter_generic_user3; -- failed (not owner) ALTER LANGUAGE alt_lang3 OWNER TO regress_alter_generic_user2; -- failed (no role membership) ALTER LANGUAGE alt_lang3 OWNER TO regress_alter_generic_user3; -- OK RESET SESSION AUTHORIZATION; SELECT lanname, a.rolname FROM pg_language l, pg_authid a WHERE l.lanowner = a.oid AND l.lanname like 'alt_lang%' ORDER BY lanname; -- -- Operator -- SET SESSION AUTHORIZATION regress_alter_generic_user1; CREATE OPERATOR @-@ ( leftarg = int4, rightarg = int4, procedure = int4mi ); CREATE OPERATOR @+@ ( leftarg = int4, rightarg = int4, procedure = int4pl ); ALTER OPERATOR @+@(int4, int4) OWNER TO regress_alter_generic_user2; -- failed (no role membership) ALTER OPERATOR @+@(int4, int4) OWNER TO regress_alter_generic_user3; -- OK ALTER OPERATOR @-@(int4, int4) SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_generic_user2; CREATE OPERATOR @-@ ( leftarg = int4, rightarg = int4, procedure = int4mi ); ALTER OPERATOR @+@(int4, int4) OWNER TO regress_alter_generic_user2; -- failed (not owner) ALTER OPERATOR @-@(int4, int4) OWNER TO regress_alter_generic_user3; -- failed (no role membership) ALTER OPERATOR @+@(int4, int4) SET SCHEMA alt_nsp2; -- failed (not owner) -- can't test this: the error message includes the raw oid of namespace -- ALTER OPERATOR @-@(int4, int4) SET SCHEMA alt_nsp2; -- failed (name conflict) RESET SESSION AUTHORIZATION; SELECT n.nspname, oprname, a.rolname, oprleft::regtype, oprright::regtype, oprcode::regproc FROM pg_operator o, pg_namespace n, pg_authid a WHERE o.oprnamespace = n.oid AND o.oprowner = a.oid AND n.nspname IN ('alt_nsp1', 'alt_nsp2') ORDER BY nspname, oprname; -- -- OpFamily and OpClass -- CREATE OPERATOR FAMILY alt_opf1 USING hash; CREATE OPERATOR FAMILY alt_opf2 USING hash; ALTER OPERATOR FAMILY alt_opf1 USING hash OWNER TO regress_alter_generic_user1; ALTER OPERATOR FAMILY alt_opf2 USING hash OWNER TO regress_alter_generic_user1; CREATE OPERATOR CLASS alt_opc1 FOR TYPE uuid USING hash AS STORAGE uuid; CREATE OPERATOR CLASS alt_opc2 FOR TYPE uuid USING hash AS STORAGE uuid; ALTER OPERATOR CLASS alt_opc1 USING hash OWNER TO regress_alter_generic_user1; ALTER OPERATOR CLASS alt_opc2 USING hash OWNER TO regress_alter_generic_user1; SET SESSION AUTHORIZATION regress_alter_generic_user1; ALTER OPERATOR FAMILY alt_opf1 USING hash RENAME TO alt_opf2; -- failed (name conflict) ALTER OPERATOR FAMILY alt_opf1 USING hash RENAME TO alt_opf3; -- OK ALTER OPERATOR FAMILY alt_opf2 USING hash OWNER TO regress_alter_generic_user2; -- failed (no role membership) ALTER OPERATOR FAMILY alt_opf2 USING hash OWNER TO regress_alter_generic_user3; -- OK ALTER OPERATOR FAMILY alt_opf2 USING hash SET SCHEMA alt_nsp2; -- OK ALTER OPERATOR CLASS alt_opc1 USING hash RENAME TO alt_opc2; -- failed (name conflict) ALTER OPERATOR CLASS alt_opc1 USING hash RENAME TO alt_opc3; -- OK ALTER OPERATOR CLASS alt_opc2 USING hash OWNER TO regress_alter_generic_user2; -- failed (no role membership) ALTER OPERATOR CLASS alt_opc2 USING hash OWNER TO regress_alter_generic_user3; -- OK ALTER OPERATOR CLASS alt_opc2 USING hash SET SCHEMA alt_nsp2; -- OK RESET SESSION AUTHORIZATION; CREATE OPERATOR FAMILY alt_opf1 USING hash; CREATE OPERATOR FAMILY alt_opf2 USING hash; ALTER OPERATOR FAMILY alt_opf1 USING hash OWNER TO regress_alter_generic_user2; ALTER OPERATOR FAMILY alt_opf2 USING hash OWNER TO regress_alter_generic_user2; CREATE OPERATOR CLASS alt_opc1 FOR TYPE macaddr USING hash AS STORAGE macaddr; CREATE OPERATOR CLASS alt_opc2 FOR TYPE macaddr USING hash AS STORAGE macaddr; ALTER OPERATOR CLASS alt_opc1 USING hash OWNER TO regress_alter_generic_user2; ALTER OPERATOR CLASS alt_opc2 USING hash OWNER TO regress_alter_generic_user2; SET SESSION AUTHORIZATION regress_alter_generic_user2; ALTER OPERATOR FAMILY alt_opf3 USING hash RENAME TO alt_opf4; -- failed (not owner) ALTER OPERATOR FAMILY alt_opf1 USING hash RENAME TO alt_opf4; -- OK ALTER OPERATOR FAMILY alt_opf3 USING hash OWNER TO regress_alter_generic_user2; -- failed (not owner) ALTER OPERATOR FAMILY alt_opf2 USING hash OWNER TO regress_alter_generic_user3; -- failed (no role membership) ALTER OPERATOR FAMILY alt_opf3 USING hash SET SCHEMA alt_nsp2; -- failed (not owner) ALTER OPERATOR FAMILY alt_opf2 USING hash SET SCHEMA alt_nsp2; -- failed (name conflict) ALTER OPERATOR CLASS alt_opc3 USING hash RENAME TO alt_opc4; -- failed (not owner) ALTER OPERATOR CLASS alt_opc1 USING hash RENAME TO alt_opc4; -- OK ALTER OPERATOR CLASS alt_opc3 USING hash OWNER TO regress_alter_generic_user2; -- failed (not owner) ALTER OPERATOR CLASS alt_opc2 USING hash OWNER TO regress_alter_generic_user3; -- failed (no role membership) ALTER OPERATOR CLASS alt_opc3 USING hash SET SCHEMA alt_nsp2; -- failed (not owner) ALTER OPERATOR CLASS alt_opc2 USING hash SET SCHEMA alt_nsp2; -- failed (name conflict) RESET SESSION AUTHORIZATION; SELECT nspname, opfname, amname, rolname FROM pg_opfamily o, pg_am m, pg_namespace n, pg_authid a WHERE o.opfmethod = m.oid AND o.opfnamespace = n.oid AND o.opfowner = a.oid AND n.nspname IN ('alt_nsp1', 'alt_nsp2') AND NOT opfname LIKE 'alt_opc%' ORDER BY nspname, opfname; SELECT nspname, opcname, amname, rolname FROM pg_opclass o, pg_am m, pg_namespace n, pg_authid a WHERE o.opcmethod = m.oid AND o.opcnamespace = n.oid AND o.opcowner = a.oid AND n.nspname IN ('alt_nsp1', 'alt_nsp2') ORDER BY nspname, opcname; -- ALTER OPERATOR FAMILY ... ADD/DROP -- Should work. Textbook case of CREATE / ALTER ADD / ALTER DROP / DROP BEGIN TRANSACTION; CREATE OPERATOR FAMILY alt_opf4 USING btree; ALTER OPERATOR FAMILY alt_opf4 USING btree ADD -- int4 vs int2 OPERATOR 1 < (int4, int2) , OPERATOR 2 <= (int4, int2) , OPERATOR 3 = (int4, int2) , OPERATOR 4 >= (int4, int2) , OPERATOR 5 > (int4, int2) , FUNCTION 1 btint42cmp(int4, int2); ALTER OPERATOR FAMILY alt_opf4 USING btree DROP -- int4 vs int2 OPERATOR 1 (int4, int2) , OPERATOR 2 (int4, int2) , OPERATOR 3 (int4, int2) , OPERATOR 4 (int4, int2) , OPERATOR 5 (int4, int2) , FUNCTION 1 (int4, int2) ; DROP OPERATOR FAMILY alt_opf4 USING btree; ROLLBACK; -- Should fail. Invalid values for ALTER OPERATOR FAMILY .. ADD / DROP CREATE OPERATOR FAMILY alt_opf4 USING btree; ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int4, int2); -- invalid indexing_method ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5 ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5 ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5 ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5 ALTER OPERATOR FAMILY alt_opf4 USING btree ADD STORAGE invalid_storage; -- Ensure STORAGE is not a part of ALTER OPERATOR FAMILY DROP OPERATOR FAMILY alt_opf4 USING btree; -- Should fail. Need to be SUPERUSER to do ALTER OPERATOR FAMILY .. ADD / DROP BEGIN TRANSACTION; CREATE ROLE regress_alter_generic_user5 NOSUPERUSER; CREATE OPERATOR FAMILY alt_opf5 USING btree; SET ROLE regress_alter_generic_user5; ALTER OPERATOR FAMILY alt_opf5 USING btree ADD OPERATOR 1 < (int4, int2), FUNCTION 1 btint42cmp(int4, int2); RESET ROLE; DROP OPERATOR FAMILY alt_opf5 USING btree; ROLLBACK; -- Should fail. Need rights to namespace for ALTER OPERATOR FAMILY .. ADD / DROP BEGIN TRANSACTION; CREATE ROLE regress_alter_generic_user6; CREATE SCHEMA alt_nsp6; REVOKE ALL ON SCHEMA alt_nsp6 FROM regress_alter_generic_user6; CREATE OPERATOR FAMILY alt_nsp6.alt_opf6 USING btree; SET ROLE regress_alter_generic_user6; ALTER OPERATOR FAMILY alt_nsp6.alt_opf6 USING btree ADD OPERATOR 1 < (int4, int2); ROLLBACK; -- Should fail. Only two arguments required for ALTER OPERATOR FAMILY ... DROP OPERATOR CREATE OPERATOR FAMILY alt_opf7 USING btree; ALTER OPERATOR FAMILY alt_opf7 USING btree ADD OPERATOR 1 < (int4, int2); ALTER OPERATOR FAMILY alt_opf7 USING btree DROP OPERATOR 1 (int4, int2, int8); DROP OPERATOR FAMILY alt_opf7 USING btree; -- Should work. During ALTER OPERATOR FAMILY ... DROP OPERATOR -- when left type is the same as right type, a DROP with only one argument type should work CREATE OPERATOR FAMILY alt_opf8 USING btree; ALTER OPERATOR FAMILY alt_opf8 USING btree ADD OPERATOR 1 < (int4, int4); DROP OPERATOR FAMILY alt_opf8 USING btree; -- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY CREATE OPERATOR FAMILY alt_opf9 USING gist; ALTER OPERATOR FAMILY alt_opf9 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops; DROP OPERATOR FAMILY alt_opf9 USING gist; -- Should fail. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY CREATE OPERATOR FAMILY alt_opf10 USING btree; ALTER OPERATOR FAMILY alt_opf10 USING btree ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops; DROP OPERATOR FAMILY alt_opf10 USING btree; -- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY CREATE OPERATOR FAMILY alt_opf11 USING gist; ALTER OPERATOR FAMILY alt_opf11 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops; ALTER OPERATOR FAMILY alt_opf11 USING gist DROP OPERATOR 1 (int4, int4); DROP OPERATOR FAMILY alt_opf11 USING gist; -- Should fail. btree comparison functions should return INTEGER in ALTER OPERATOR FAMILY ... ADD FUNCTION BEGIN TRANSACTION; CREATE OPERATOR FAMILY alt_opf12 USING btree; CREATE FUNCTION fn_opf12 (int4, int2) RETURNS BIGINT AS 'SELECT NULL::BIGINT;' LANGUAGE SQL; ALTER OPERATOR FAMILY alt_opf12 USING btree ADD FUNCTION 1 fn_opf12(int4, int2); DROP OPERATOR FAMILY alt_opf12 USING btree; ROLLBACK; -- Should fail. hash comparison functions should return INTEGER in ALTER OPERATOR FAMILY ... ADD FUNCTION BEGIN TRANSACTION; CREATE OPERATOR FAMILY alt_opf13 USING hash; CREATE FUNCTION fn_opf13 (int4) RETURNS BIGINT AS 'SELECT NULL::BIGINT;' LANGUAGE SQL; ALTER OPERATOR FAMILY alt_opf13 USING hash ADD FUNCTION 1 fn_opf13(int4); DROP OPERATOR FAMILY alt_opf13 USING hash; ROLLBACK; -- Should fail. btree comparison functions should have two arguments in ALTER OPERATOR FAMILY ... ADD FUNCTION BEGIN TRANSACTION; CREATE OPERATOR FAMILY alt_opf14 USING btree; CREATE FUNCTION fn_opf14 (int4) RETURNS BIGINT AS 'SELECT NULL::BIGINT;' LANGUAGE SQL; ALTER OPERATOR FAMILY alt_opf14 USING btree ADD FUNCTION 1 fn_opf14(int4); DROP OPERATOR FAMILY alt_opf14 USING btree; ROLLBACK; -- Should fail. hash comparison functions should have one argument in ALTER OPERATOR FAMILY ... ADD FUNCTION BEGIN TRANSACTION; CREATE OPERATOR FAMILY alt_opf15 USING hash; CREATE FUNCTION fn_opf15 (int4, int2) RETURNS BIGINT AS 'SELECT NULL::BIGINT;' LANGUAGE SQL; ALTER OPERATOR FAMILY alt_opf15 USING hash ADD FUNCTION 1 fn_opf15(int4, int2); DROP OPERATOR FAMILY alt_opf15 USING hash; ROLLBACK; -- Should fail. In gist throw an error when giving different data types for function argument -- without defining left / right type in ALTER OPERATOR FAMILY ... ADD FUNCTION CREATE OPERATOR FAMILY alt_opf16 USING gist; ALTER OPERATOR FAMILY alt_opf16 USING gist ADD FUNCTION 1 btint42cmp(int4, int2); DROP OPERATOR FAMILY alt_opf16 USING gist; -- Should fail. duplicate operator number / function number in ALTER OPERATOR FAMILY ... ADD FUNCTION CREATE OPERATOR FAMILY alt_opf17 USING btree; ALTER OPERATOR FAMILY alt_opf17 USING btree ADD OPERATOR 1 < (int4, int4), OPERATOR 1 < (int4, int4); -- operator # appears twice in same statement ALTER OPERATOR FAMILY alt_opf17 USING btree ADD OPERATOR 1 < (int4, int4); -- operator 1 requested first-time ALTER OPERATOR FAMILY alt_opf17 USING btree ADD OPERATOR 1 < (int4, int4); -- operator 1 requested again in separate statement ALTER OPERATOR FAMILY alt_opf17 USING btree ADD OPERATOR 1 < (int4, int2) , OPERATOR 2 <= (int4, int2) , OPERATOR 3 = (int4, int2) , OPERATOR 4 >= (int4, int2) , OPERATOR 5 > (int4, int2) , FUNCTION 1 btint42cmp(int4, int2) , FUNCTION 1 btint42cmp(int4, int2); -- procedure 1 appears twice in same statement ALTER OPERATOR FAMILY alt_opf17 USING btree ADD OPERATOR 1 < (int4, int2) , OPERATOR 2 <= (int4, int2) , OPERATOR 3 = (int4, int2) , OPERATOR 4 >= (int4, int2) , OPERATOR 5 > (int4, int2) , FUNCTION 1 btint42cmp(int4, int2); -- procedure 1 appears first time ALTER OPERATOR FAMILY alt_opf17 USING btree ADD OPERATOR 1 < (int4, int2) , OPERATOR 2 <= (int4, int2) , OPERATOR 3 = (int4, int2) , OPERATOR 4 >= (int4, int2) , OPERATOR 5 > (int4, int2) , FUNCTION 1 btint42cmp(int4, int2); -- procedure 1 requested again in separate statement DROP OPERATOR FAMILY alt_opf17 USING btree; -- Should fail. Ensure that DROP requests for missing OPERATOR / FUNCTIONS -- return appropriate message in ALTER OPERATOR FAMILY ... DROP OPERATOR / FUNCTION CREATE OPERATOR FAMILY alt_opf18 USING btree; ALTER OPERATOR FAMILY alt_opf18 USING btree DROP OPERATOR 1 (int4, int4); ALTER OPERATOR FAMILY alt_opf18 USING btree ADD OPERATOR 1 < (int4, int2) , OPERATOR 2 <= (int4, int2) , OPERATOR 3 = (int4, int2) , OPERATOR 4 >= (int4, int2) , OPERATOR 5 > (int4, int2) , FUNCTION 1 btint42cmp(int4, int2); ALTER OPERATOR FAMILY alt_opf18 USING btree DROP FUNCTION 2 (int4, int4); DROP OPERATOR FAMILY alt_opf18 USING btree; -- -- Statistics -- SET SESSION AUTHORIZATION regress_alter_generic_user1; CREATE TABLE alt_regress_1 (a INTEGER, b INTEGER); CREATE STATISTICS alt_stat1 ON a, b FROM alt_regress_1; CREATE STATISTICS alt_stat2 ON a, b FROM alt_regress_1; ALTER STATISTICS alt_stat1 RENAME TO alt_stat2; -- failed (name conflict) ALTER STATISTICS alt_stat1 RENAME TO alt_stat3; -- failed (name conflict) ALTER STATISTICS alt_stat2 OWNER TO regress_alter_generic_user2; -- failed (no role membership) ALTER STATISTICS alt_stat2 OWNER TO regress_alter_generic_user3; -- OK ALTER STATISTICS alt_stat2 SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_generic_user2; CREATE TABLE alt_regress_2 (a INTEGER, b INTEGER); CREATE STATISTICS alt_stat1 ON a, b FROM alt_regress_2; CREATE STATISTICS alt_stat2 ON a, b FROM alt_regress_2; ALTER STATISTICS alt_stat3 RENAME TO alt_stat4; -- failed (not owner) ALTER STATISTICS alt_stat1 RENAME TO alt_stat4; -- OK ALTER STATISTICS alt_stat3 OWNER TO regress_alter_generic_user2; -- failed (not owner) ALTER STATISTICS alt_stat2 OWNER TO regress_alter_generic_user3; -- failed (no role membership) ALTER STATISTICS alt_stat3 SET SCHEMA alt_nsp2; -- failed (not owner) ALTER STATISTICS alt_stat2 SET SCHEMA alt_nsp2; -- failed (name conflict) RESET SESSION AUTHORIZATION; SELECT nspname, stxname, rolname FROM pg_statistic_ext s, pg_namespace n, pg_authid a WHERE s.stxnamespace = n.oid AND s.stxowner = a.oid AND n.nspname in ('alt_nsp1', 'alt_nsp2') ORDER BY nspname, stxname; -- -- Text Search Dictionary -- SET SESSION AUTHORIZATION regress_alter_generic_user1; CREATE TEXT SEARCH DICTIONARY alt_ts_dict1 (template=simple); CREATE TEXT SEARCH DICTIONARY alt_ts_dict2 (template=simple); ALTER TEXT SEARCH DICTIONARY alt_ts_dict1 RENAME TO alt_ts_dict2; -- failed (name conflict) ALTER TEXT SEARCH DICTIONARY alt_ts_dict1 RENAME TO alt_ts_dict3; -- OK ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 OWNER TO regress_alter_generic_user2; -- failed (no role membership) ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 OWNER TO regress_alter_generic_user3; -- OK ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_generic_user2; CREATE TEXT SEARCH DICTIONARY alt_ts_dict1 (template=simple); CREATE TEXT SEARCH DICTIONARY alt_ts_dict2 (template=simple); ALTER TEXT SEARCH DICTIONARY alt_ts_dict3 RENAME TO alt_ts_dict4; -- failed (not owner) ALTER TEXT SEARCH DICTIONARY alt_ts_dict1 RENAME TO alt_ts_dict4; -- OK ALTER TEXT SEARCH DICTIONARY alt_ts_dict3 OWNER TO regress_alter_generic_user2; -- failed (not owner) ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 OWNER TO regress_alter_generic_user3; -- failed (no role membership) ALTER TEXT SEARCH DICTIONARY alt_ts_dict3 SET SCHEMA alt_nsp2; -- failed (not owner) ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 SET SCHEMA alt_nsp2; -- failed (name conflict) RESET SESSION AUTHORIZATION; SELECT nspname, dictname, rolname FROM pg_ts_dict t, pg_namespace n, pg_authid a WHERE t.dictnamespace = n.oid AND t.dictowner = a.oid AND n.nspname in ('alt_nsp1', 'alt_nsp2') ORDER BY nspname, dictname; -- -- Text Search Configuration -- SET SESSION AUTHORIZATION regress_alter_generic_user1; CREATE TEXT SEARCH CONFIGURATION alt_ts_conf1 (copy=english); CREATE TEXT SEARCH CONFIGURATION alt_ts_conf2 (copy=english); ALTER TEXT SEARCH CONFIGURATION alt_ts_conf1 RENAME TO alt_ts_conf2; -- failed (name conflict) ALTER TEXT SEARCH CONFIGURATION alt_ts_conf1 RENAME TO alt_ts_conf3; -- OK ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 OWNER TO regress_alter_generic_user2; -- failed (no role membership) ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 OWNER TO regress_alter_generic_user3; -- OK ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_generic_user2; CREATE TEXT SEARCH CONFIGURATION alt_ts_conf1 (copy=english); CREATE TEXT SEARCH CONFIGURATION alt_ts_conf2 (copy=english); ALTER TEXT SEARCH CONFIGURATION alt_ts_conf3 RENAME TO alt_ts_conf4; -- failed (not owner) ALTER TEXT SEARCH CONFIGURATION alt_ts_conf1 RENAME TO alt_ts_conf4; -- OK ALTER TEXT SEARCH CONFIGURATION alt_ts_conf3 OWNER TO regress_alter_generic_user2; -- failed (not owner) ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 OWNER TO regress_alter_generic_user3; -- failed (no role membership) ALTER TEXT SEARCH CONFIGURATION alt_ts_conf3 SET SCHEMA alt_nsp2; -- failed (not owner) ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 SET SCHEMA alt_nsp2; -- failed (name conflict) RESET SESSION AUTHORIZATION; SELECT nspname, cfgname, rolname FROM pg_ts_config t, pg_namespace n, pg_authid a WHERE t.cfgnamespace = n.oid AND t.cfgowner = a.oid AND n.nspname in ('alt_nsp1', 'alt_nsp2') ORDER BY nspname, cfgname; -- -- Text Search Template -- CREATE TEXT SEARCH TEMPLATE alt_ts_temp1 (lexize=dsimple_lexize); CREATE TEXT SEARCH TEMPLATE alt_ts_temp2 (lexize=dsimple_lexize); ALTER TEXT SEARCH TEMPLATE alt_ts_temp1 RENAME TO alt_ts_temp2; -- failed (name conflict) ALTER TEXT SEARCH TEMPLATE alt_ts_temp1 RENAME TO alt_ts_temp3; -- OK ALTER TEXT SEARCH TEMPLATE alt_ts_temp2 SET SCHEMA alt_nsp2; -- OK CREATE TEXT SEARCH TEMPLATE alt_ts_temp2 (lexize=dsimple_lexize); ALTER TEXT SEARCH TEMPLATE alt_ts_temp2 SET SCHEMA alt_nsp2; -- failed (name conflict) -- invalid: non-lowercase quoted identifiers CREATE TEXT SEARCH TEMPLATE tstemp_case ("Init" = init_function); SELECT nspname, tmplname FROM pg_ts_template t, pg_namespace n WHERE t.tmplnamespace = n.oid AND nspname like 'alt_nsp%' ORDER BY nspname, tmplname; -- -- Text Search Parser -- CREATE TEXT SEARCH PARSER alt_ts_prs1 (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); CREATE TEXT SEARCH PARSER alt_ts_prs2 (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); ALTER TEXT SEARCH PARSER alt_ts_prs1 RENAME TO alt_ts_prs2; -- failed (name conflict) ALTER TEXT SEARCH PARSER alt_ts_prs1 RENAME TO alt_ts_prs3; -- OK ALTER TEXT SEARCH PARSER alt_ts_prs2 SET SCHEMA alt_nsp2; -- OK CREATE TEXT SEARCH PARSER alt_ts_prs2 (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); ALTER TEXT SEARCH PARSER alt_ts_prs2 SET SCHEMA alt_nsp2; -- failed (name conflict) -- invalid: non-lowercase quoted identifiers CREATE TEXT SEARCH PARSER tspars_case ("Start" = start_function); SELECT nspname, prsname FROM pg_ts_parser t, pg_namespace n WHERE t.prsnamespace = n.oid AND nspname like 'alt_nsp%' ORDER BY nspname, prsname; --- --- Cleanup resources --- DROP FOREIGN DATA WRAPPER alt_fdw2 CASCADE; DROP FOREIGN DATA WRAPPER alt_fdw3 CASCADE; DROP LANGUAGE alt_lang2 CASCADE; DROP LANGUAGE alt_lang3 CASCADE; DROP SCHEMA alt_nsp1 CASCADE; DROP SCHEMA alt_nsp2 CASCADE; DROP USER regress_alter_generic_user1; DROP USER regress_alter_generic_user2; DROP USER regress_alter_generic_user3; pgFormatter-4.2/t/pg-test-files/sql/alter_operator.sql000066400000000000000000000065051361326045100231500ustar00rootroot00000000000000CREATE FUNCTION alter_op_test_fn(boolean, boolean) RETURNS boolean AS $$ SELECT NULL::BOOLEAN; $$ LANGUAGE sql IMMUTABLE; CREATE FUNCTION customcontsel(internal, oid, internal, integer) RETURNS float8 AS 'contsel' LANGUAGE internal STABLE STRICT; CREATE OPERATOR === ( LEFTARG = boolean, RIGHTARG = boolean, PROCEDURE = alter_op_test_fn, COMMUTATOR = ===, NEGATOR = !==, RESTRICT = customcontsel, JOIN = contjoinsel, HASHES, MERGES ); SELECT pg_describe_object(refclassid,refobjid,refobjsubid) as ref, deptype FROM pg_depend WHERE classid = 'pg_operator'::regclass AND objid = '===(bool,bool)'::regoperator ORDER BY 1; -- -- Reset and set params -- ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NONE); ALTER OPERATOR === (boolean, boolean) SET (JOIN = NONE); SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '===' AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype; SELECT pg_describe_object(refclassid,refobjid,refobjsubid) as ref, deptype FROM pg_depend WHERE classid = 'pg_operator'::regclass AND objid = '===(bool,bool)'::regoperator ORDER BY 1; ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = contsel); ALTER OPERATOR === (boolean, boolean) SET (JOIN = contjoinsel); SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '===' AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype; SELECT pg_describe_object(refclassid,refobjid,refobjsubid) as ref, deptype FROM pg_depend WHERE classid = 'pg_operator'::regclass AND objid = '===(bool,bool)'::regoperator ORDER BY 1; ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NONE, JOIN = NONE); SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '===' AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype; SELECT pg_describe_object(refclassid,refobjid,refobjsubid) as ref, deptype FROM pg_depend WHERE classid = 'pg_operator'::regclass AND objid = '===(bool,bool)'::regoperator ORDER BY 1; ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = customcontsel, JOIN = contjoinsel); SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '===' AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype; SELECT pg_describe_object(refclassid,refobjid,refobjsubid) as ref, deptype FROM pg_depend WHERE classid = 'pg_operator'::regclass AND objid = '===(bool,bool)'::regoperator ORDER BY 1; -- -- Test invalid options. -- ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = ====); ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = ====); ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = non_existent_func); ALTER OPERATOR === (boolean, boolean) SET (JOIN = non_existent_func); ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = !==); ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = !==); -- invalid: non-lowercase quoted identifiers ALTER OPERATOR & (bit, bit) SET ("Restrict" = _int_contsel, "Join" = _int_contjoinsel); -- -- Test permission check. Must be owner to ALTER OPERATOR. -- CREATE USER regress_alter_op_user; SET SESSION AUTHORIZATION regress_alter_op_user; ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NONE); -- Clean up RESET SESSION AUTHORIZATION; DROP USER regress_alter_op_user; DROP OPERATOR === (boolean, boolean); DROP FUNCTION customcontsel(internal, oid, internal, integer); DROP FUNCTION alter_op_test_fn(boolean, boolean); pgFormatter-4.2/t/pg-test-files/sql/alter_table.sql000066400000000000000000003050421361326045100224020ustar00rootroot00000000000000-- -- ALTER_TABLE -- -- Clean up in case a prior regression run failed SET client_min_messages TO 'warning'; DROP ROLE IF EXISTS regress_alter_table_user1; RESET client_min_messages; CREATE USER regress_alter_table_user1; -- -- add attribute -- CREATE TABLE attmp (initial int4); COMMENT ON TABLE attmp_wrong IS 'table comment'; COMMENT ON TABLE attmp IS 'table comment'; COMMENT ON TABLE attmp IS NULL; ALTER TABLE attmp ADD COLUMN xmin integer; -- fails ALTER TABLE attmp ADD COLUMN a int4 default 3; ALTER TABLE attmp ADD COLUMN b name; ALTER TABLE attmp ADD COLUMN c text; ALTER TABLE attmp ADD COLUMN d float8; ALTER TABLE attmp ADD COLUMN e float4; ALTER TABLE attmp ADD COLUMN f int2; ALTER TABLE attmp ADD COLUMN g polygon; ALTER TABLE attmp ADD COLUMN i char; ALTER TABLE attmp ADD COLUMN k int4; ALTER TABLE attmp ADD COLUMN l tid; ALTER TABLE attmp ADD COLUMN m xid; ALTER TABLE attmp ADD COLUMN n oidvector; --ALTER TABLE attmp ADD COLUMN o lock; ALTER TABLE attmp ADD COLUMN p boolean; ALTER TABLE attmp ADD COLUMN q point; ALTER TABLE attmp ADD COLUMN r lseg; ALTER TABLE attmp ADD COLUMN s path; ALTER TABLE attmp ADD COLUMN t box; ALTER TABLE attmp ADD COLUMN v timestamp; ALTER TABLE attmp ADD COLUMN w interval; ALTER TABLE attmp ADD COLUMN x float8[]; ALTER TABLE attmp ADD COLUMN y float4[]; ALTER TABLE attmp ADD COLUMN z int2[]; INSERT INTO attmp (a, b, c, d, e, f, g, i, k, l, m, n, p, q, r, s, t, v, w, x, y, z) VALUES (4, 'name', 'text', 4.1, 4.1, 2, '(4.1,4.1,3.1,3.1)', 'c', 314159, '(1,1)', '512', '1 2 3 4 5 6 7 8', true, '(1.1,1.1)', '(4.1,4.1,3.1,3.1)', '(0,2,4.1,4.1,3.1,3.1)', '(4.1,4.1,3.1,3.1)', 'epoch', '01:00:10', '{1.0,2.0,3.0,4.0}', '{1.0,2.0,3.0,4.0}', '{1,2,3,4}'); SELECT * FROM attmp; DROP TABLE attmp; -- the wolf bug - schema mods caused inconsistent row descriptors CREATE TABLE attmp ( initial int4 ); ALTER TABLE attmp ADD COLUMN a int4; ALTER TABLE attmp ADD COLUMN b name; ALTER TABLE attmp ADD COLUMN c text; ALTER TABLE attmp ADD COLUMN d float8; ALTER TABLE attmp ADD COLUMN e float4; ALTER TABLE attmp ADD COLUMN f int2; ALTER TABLE attmp ADD COLUMN g polygon; ALTER TABLE attmp ADD COLUMN i char; ALTER TABLE attmp ADD COLUMN k int4; ALTER TABLE attmp ADD COLUMN l tid; ALTER TABLE attmp ADD COLUMN m xid; ALTER TABLE attmp ADD COLUMN n oidvector; --ALTER TABLE attmp ADD COLUMN o lock; ALTER TABLE attmp ADD COLUMN p boolean; ALTER TABLE attmp ADD COLUMN q point; ALTER TABLE attmp ADD COLUMN r lseg; ALTER TABLE attmp ADD COLUMN s path; ALTER TABLE attmp ADD COLUMN t box; ALTER TABLE attmp ADD COLUMN v timestamp; ALTER TABLE attmp ADD COLUMN w interval; ALTER TABLE attmp ADD COLUMN x float8[]; ALTER TABLE attmp ADD COLUMN y float4[]; ALTER TABLE attmp ADD COLUMN z int2[]; INSERT INTO attmp (a, b, c, d, e, f, g, i, k, l, m, n, p, q, r, s, t, v, w, x, y, z) VALUES (4, 'name', 'text', 4.1, 4.1, 2, '(4.1,4.1,3.1,3.1)', 'c', 314159, '(1,1)', '512', '1 2 3 4 5 6 7 8', true, '(1.1,1.1)', '(4.1,4.1,3.1,3.1)', '(0,2,4.1,4.1,3.1,3.1)', '(4.1,4.1,3.1,3.1)', 'epoch', '01:00:10', '{1.0,2.0,3.0,4.0}', '{1.0,2.0,3.0,4.0}', '{1,2,3,4}'); SELECT * FROM attmp; CREATE INDEX attmp_idx ON attmp (a, (d + e), b); ALTER INDEX attmp_idx ALTER COLUMN 0 SET STATISTICS 1000; ALTER INDEX attmp_idx ALTER COLUMN 1 SET STATISTICS 1000; ALTER INDEX attmp_idx ALTER COLUMN 2 SET STATISTICS 1000; \d+ attmp_idx ALTER INDEX attmp_idx ALTER COLUMN 3 SET STATISTICS 1000; ALTER INDEX attmp_idx ALTER COLUMN 4 SET STATISTICS 1000; ALTER INDEX attmp_idx ALTER COLUMN 2 SET STATISTICS -1; DROP TABLE attmp; -- -- rename - check on both non-temp and temp tables -- CREATE TABLE attmp (regtable int); CREATE TEMP TABLE attmp (attmptable int); ALTER TABLE attmp RENAME TO attmp_new; SELECT * FROM attmp; SELECT * FROM attmp_new; ALTER TABLE attmp RENAME TO attmp_new2; SELECT * FROM attmp; -- should fail SELECT * FROM attmp_new; SELECT * FROM attmp_new2; DROP TABLE attmp_new; DROP TABLE attmp_new2; -- check rename of partitioned tables and indexes also CREATE TABLE part_attmp (a int primary key) partition by range (a); CREATE TABLE part_attmp1 PARTITION OF part_attmp FOR VALUES FROM (0) TO (100); ALTER INDEX part_attmp_pkey RENAME TO part_attmp_index; ALTER INDEX part_attmp1_pkey RENAME TO part_attmp1_index; ALTER TABLE part_attmp RENAME TO part_at2tmp; ALTER TABLE part_attmp1 RENAME TO part_at2tmp1; SET ROLE regress_alter_table_user1; ALTER INDEX part_attmp_index RENAME TO fail; ALTER INDEX part_attmp1_index RENAME TO fail; ALTER TABLE part_at2tmp RENAME TO fail; ALTER TABLE part_at2tmp1 RENAME TO fail; RESET ROLE; DROP TABLE part_at2tmp; -- -- check renaming to a table's array type's autogenerated name -- (the array type's name should get out of the way) -- CREATE TABLE attmp_array (id int); CREATE TABLE attmp_array2 (id int); SELECT typname FROM pg_type WHERE oid = 'attmp_array[]'::regtype; SELECT typname FROM pg_type WHERE oid = 'attmp_array2[]'::regtype; ALTER TABLE attmp_array2 RENAME TO _attmp_array; SELECT typname FROM pg_type WHERE oid = 'attmp_array[]'::regtype; SELECT typname FROM pg_type WHERE oid = '_attmp_array[]'::regtype; DROP TABLE _attmp_array; DROP TABLE attmp_array; -- renaming to table's own array type's name is an interesting corner case CREATE TABLE attmp_array (id int); SELECT typname FROM pg_type WHERE oid = 'attmp_array[]'::regtype; ALTER TABLE attmp_array RENAME TO _attmp_array; SELECT typname FROM pg_type WHERE oid = '_attmp_array[]'::regtype; DROP TABLE _attmp_array; -- ALTER TABLE ... RENAME on non-table relations -- renaming indexes (FIXME: this should probably test the index's functionality) ALTER INDEX IF EXISTS __onek_unique1 RENAME TO attmp_onek_unique1; ALTER INDEX IF EXISTS __attmp_onek_unique1 RENAME TO onek_unique1; ALTER INDEX onek_unique1 RENAME TO attmp_onek_unique1; ALTER INDEX attmp_onek_unique1 RENAME TO onek_unique1; SET ROLE regress_alter_table_user1; ALTER INDEX onek_unique1 RENAME TO fail; -- permission denied RESET ROLE; -- renaming views CREATE VIEW attmp_view (unique1) AS SELECT unique1 FROM tenk1; ALTER TABLE attmp_view RENAME TO attmp_view_new; SET ROLE regress_alter_table_user1; ALTER VIEW attmp_view_new RENAME TO fail; -- permission denied RESET ROLE; -- hack to ensure we get an indexscan here set enable_seqscan to off; set enable_bitmapscan to off; -- 5 values, sorted SELECT unique1 FROM tenk1 WHERE unique1 < 5; reset enable_seqscan; reset enable_bitmapscan; DROP VIEW attmp_view_new; -- toast-like relation name alter table stud_emp rename to pg_toast_stud_emp; alter table pg_toast_stud_emp rename to stud_emp; -- renaming index should rename constraint as well ALTER TABLE onek ADD CONSTRAINT onek_unique1_constraint UNIQUE (unique1); ALTER INDEX onek_unique1_constraint RENAME TO onek_unique1_constraint_foo; ALTER TABLE onek DROP CONSTRAINT onek_unique1_constraint_foo; -- renaming constraint ALTER TABLE onek ADD CONSTRAINT onek_check_constraint CHECK (unique1 >= 0); ALTER TABLE onek RENAME CONSTRAINT onek_check_constraint TO onek_check_constraint_foo; ALTER TABLE onek DROP CONSTRAINT onek_check_constraint_foo; -- renaming constraint should rename index as well ALTER TABLE onek ADD CONSTRAINT onek_unique1_constraint UNIQUE (unique1); DROP INDEX onek_unique1_constraint; -- to see whether it's there ALTER TABLE onek RENAME CONSTRAINT onek_unique1_constraint TO onek_unique1_constraint_foo; DROP INDEX onek_unique1_constraint_foo; -- to see whether it's there ALTER TABLE onek DROP CONSTRAINT onek_unique1_constraint_foo; -- renaming constraints vs. inheritance CREATE TABLE constraint_rename_test (a int CONSTRAINT con1 CHECK (a > 0), b int, c int); \d constraint_rename_test CREATE TABLE constraint_rename_test2 (a int CONSTRAINT con1 CHECK (a > 0), d int) INHERITS (constraint_rename_test); \d constraint_rename_test2 ALTER TABLE constraint_rename_test2 RENAME CONSTRAINT con1 TO con1foo; -- fail ALTER TABLE ONLY constraint_rename_test RENAME CONSTRAINT con1 TO con1foo; -- fail ALTER TABLE constraint_rename_test RENAME CONSTRAINT con1 TO con1foo; -- ok \d constraint_rename_test \d constraint_rename_test2 ALTER TABLE constraint_rename_test ADD CONSTRAINT con2 CHECK (b > 0) NO INHERIT; ALTER TABLE ONLY constraint_rename_test RENAME CONSTRAINT con2 TO con2foo; -- ok ALTER TABLE constraint_rename_test RENAME CONSTRAINT con2foo TO con2bar; -- ok \d constraint_rename_test \d constraint_rename_test2 ALTER TABLE constraint_rename_test ADD CONSTRAINT con3 PRIMARY KEY (a); ALTER TABLE constraint_rename_test RENAME CONSTRAINT con3 TO con3foo; -- ok \d constraint_rename_test \d constraint_rename_test2 DROP TABLE constraint_rename_test2; DROP TABLE constraint_rename_test; ALTER TABLE IF EXISTS constraint_not_exist RENAME CONSTRAINT con3 TO con3foo; -- ok ALTER TABLE IF EXISTS constraint_rename_test ADD CONSTRAINT con4 UNIQUE (a); -- renaming constraints with cache reset of target relation CREATE TABLE constraint_rename_cache (a int, CONSTRAINT chk_a CHECK (a > 0), PRIMARY KEY (a)); ALTER TABLE constraint_rename_cache RENAME CONSTRAINT chk_a TO chk_a_new; ALTER TABLE constraint_rename_cache RENAME CONSTRAINT constraint_rename_cache_pkey TO constraint_rename_pkey_new; CREATE TABLE like_constraint_rename_cache (LIKE constraint_rename_cache INCLUDING ALL); \d like_constraint_rename_cache DROP TABLE constraint_rename_cache; DROP TABLE like_constraint_rename_cache; -- FOREIGN KEY CONSTRAINT adding TEST CREATE TABLE attmp2 (a int primary key); CREATE TABLE attmp3 (a int, b int); CREATE TABLE attmp4 (a int, b int, unique(a,b)); CREATE TABLE attmp5 (a int, b int); -- Insert rows into attmp2 (pktable) INSERT INTO attmp2 values (1); INSERT INTO attmp2 values (2); INSERT INTO attmp2 values (3); INSERT INTO attmp2 values (4); -- Insert rows into attmp3 INSERT INTO attmp3 values (1,10); INSERT INTO attmp3 values (1,20); INSERT INTO attmp3 values (5,50); -- Try (and fail) to add constraint due to invalid source columns ALTER TABLE attmp3 add constraint attmpconstr foreign key(c) references attmp2 match full; -- Try (and fail) to add constraint due to invalid destination columns explicitly given ALTER TABLE attmp3 add constraint attmpconstr foreign key(a) references attmp2(b) match full; -- Try (and fail) to add constraint due to invalid data ALTER TABLE attmp3 add constraint attmpconstr foreign key (a) references attmp2 match full; -- Delete failing row DELETE FROM attmp3 where a=5; -- Try (and succeed) ALTER TABLE attmp3 add constraint attmpconstr foreign key (a) references attmp2 match full; ALTER TABLE attmp3 drop constraint attmpconstr; INSERT INTO attmp3 values (5,50); -- Try NOT VALID and then VALIDATE CONSTRAINT, but fails. Delete failure then re-validate ALTER TABLE attmp3 add constraint attmpconstr foreign key (a) references attmp2 match full NOT VALID; ALTER TABLE attmp3 validate constraint attmpconstr; -- Delete failing row DELETE FROM attmp3 where a=5; -- Try (and succeed) and repeat to show it works on already valid constraint ALTER TABLE attmp3 validate constraint attmpconstr; ALTER TABLE attmp3 validate constraint attmpconstr; -- Try a non-verified CHECK constraint ALTER TABLE attmp3 ADD CONSTRAINT b_greater_than_ten CHECK (b > 10); -- fail ALTER TABLE attmp3 ADD CONSTRAINT b_greater_than_ten CHECK (b > 10) NOT VALID; -- succeeds ALTER TABLE attmp3 VALIDATE CONSTRAINT b_greater_than_ten; -- fails DELETE FROM attmp3 WHERE NOT b > 10; ALTER TABLE attmp3 VALIDATE CONSTRAINT b_greater_than_ten; -- succeeds ALTER TABLE attmp3 VALIDATE CONSTRAINT b_greater_than_ten; -- succeeds -- Test inherited NOT VALID CHECK constraints select * from attmp3; CREATE TABLE attmp6 () INHERITS (attmp3); CREATE TABLE attmp7 () INHERITS (attmp3); INSERT INTO attmp6 VALUES (6, 30), (7, 16); ALTER TABLE attmp3 ADD CONSTRAINT b_le_20 CHECK (b <= 20) NOT VALID; ALTER TABLE attmp3 VALIDATE CONSTRAINT b_le_20; -- fails DELETE FROM attmp6 WHERE b > 20; ALTER TABLE attmp3 VALIDATE CONSTRAINT b_le_20; -- succeeds -- An already validated constraint must not be revalidated CREATE FUNCTION boo(int) RETURNS int IMMUTABLE STRICT LANGUAGE plpgsql AS $$ BEGIN RAISE NOTICE 'boo: %', $1; RETURN $1; END; $$; INSERT INTO attmp7 VALUES (8, 18); ALTER TABLE attmp7 ADD CONSTRAINT identity CHECK (b = boo(b)); ALTER TABLE attmp3 ADD CONSTRAINT IDENTITY check (b = boo(b)) NOT VALID; ALTER TABLE attmp3 VALIDATE CONSTRAINT identity; -- A NO INHERIT constraint should not be looked for in children during VALIDATE CONSTRAINT create table parent_noinh_convalid (a int); create table child_noinh_convalid () inherits (parent_noinh_convalid); insert into parent_noinh_convalid values (1); insert into child_noinh_convalid values (1); alter table parent_noinh_convalid add constraint check_a_is_2 check (a = 2) no inherit not valid; -- fail, because of the row in parent alter table parent_noinh_convalid validate constraint check_a_is_2; delete from only parent_noinh_convalid; -- ok (parent itself contains no violating rows) alter table parent_noinh_convalid validate constraint check_a_is_2; select convalidated from pg_constraint where conrelid = 'parent_noinh_convalid'::regclass and conname = 'check_a_is_2'; -- cleanup drop table parent_noinh_convalid, child_noinh_convalid; -- Try (and fail) to create constraint from attmp5(a) to attmp4(a) - unique constraint on -- attmp4 is a,b ALTER TABLE attmp5 add constraint attmpconstr foreign key(a) references attmp4(a) match full; DROP TABLE attmp7; DROP TABLE attmp6; DROP TABLE attmp5; DROP TABLE attmp4; DROP TABLE attmp3; DROP TABLE attmp2; -- NOT VALID with plan invalidation -- ensure we don't use a constraint for -- exclusion until validated set constraint_exclusion TO 'partition'; create table nv_parent (d date, check (false) no inherit not valid); -- not valid constraint added at creation time should automatically become valid \d nv_parent create table nv_child_2010 () inherits (nv_parent); create table nv_child_2011 () inherits (nv_parent); alter table nv_child_2010 add check (d between '2010-01-01'::date and '2010-12-31'::date) not valid; alter table nv_child_2011 add check (d between '2011-01-01'::date and '2011-12-31'::date) not valid; explain (costs off) select * from nv_parent where d between '2011-08-01' and '2011-08-31'; create table nv_child_2009 (check (d between '2009-01-01'::date and '2009-12-31'::date)) inherits (nv_parent); explain (costs off) select * from nv_parent where d between '2011-08-01'::date and '2011-08-31'::date; explain (costs off) select * from nv_parent where d between '2009-08-01'::date and '2009-08-31'::date; -- after validation, the constraint should be used alter table nv_child_2011 VALIDATE CONSTRAINT nv_child_2011_d_check; explain (costs off) select * from nv_parent where d between '2009-08-01'::date and '2009-08-31'::date; -- add an inherited NOT VALID constraint alter table nv_parent add check (d between '2001-01-01'::date and '2099-12-31'::date) not valid; \d nv_child_2009 -- we leave nv_parent and children around to help test pg_dump logic -- Foreign key adding test with mixed types -- Note: these tables are TEMP to avoid name conflicts when this test -- is run in parallel with foreign_key.sql. CREATE TEMP TABLE PKTABLE (ptest1 int PRIMARY KEY); INSERT INTO PKTABLE VALUES(42); CREATE TEMP TABLE FKTABLE (ftest1 inet); -- This next should fail, because int=inet does not exist ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable; -- This should also fail for the same reason, but here we -- give the column name ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1); DROP TABLE FKTABLE; -- This should succeed, even though they are different types, -- because int=int8 exists and is a member of the integer opfamily CREATE TEMP TABLE FKTABLE (ftest1 int8); ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable; -- Check it actually works INSERT INTO FKTABLE VALUES(42); -- should succeed INSERT INTO FKTABLE VALUES(43); -- should fail DROP TABLE FKTABLE; -- This should fail, because we'd have to cast numeric to int which is -- not an implicit coercion (or use numeric=numeric, but that's not part -- of the integer opfamily) CREATE TEMP TABLE FKTABLE (ftest1 numeric); ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable; DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- On the other hand, this should work because int implicitly promotes to -- numeric, and we allow promotion on the FK side CREATE TEMP TABLE PKTABLE (ptest1 numeric PRIMARY KEY); INSERT INTO PKTABLE VALUES(42); CREATE TEMP TABLE FKTABLE (ftest1 int); ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable; -- Check it actually works INSERT INTO FKTABLE VALUES(42); -- should succeed INSERT INTO FKTABLE VALUES(43); -- should fail DROP TABLE FKTABLE; DROP TABLE PKTABLE; CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet, PRIMARY KEY(ptest1, ptest2)); -- This should fail, because we just chose really odd types CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp); ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable; DROP TABLE FKTABLE; -- Again, so should this... CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp); ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable(ptest1, ptest2); DROP TABLE FKTABLE; -- This fails because we mixed up the column ordering CREATE TEMP TABLE FKTABLE (ftest1 int, ftest2 inet); ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable(ptest2, ptest1); -- As does this... ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest2, ftest1) references pktable(ptest1, ptest2); DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- Test that ALTER CONSTRAINT updates trigger deferrability properly CREATE TEMP TABLE PKTABLE (ptest1 int primary key); CREATE TEMP TABLE FKTABLE (ftest1 int); ALTER TABLE FKTABLE ADD CONSTRAINT fknd FOREIGN KEY(ftest1) REFERENCES pktable ON DELETE CASCADE ON UPDATE NO ACTION NOT DEFERRABLE; ALTER TABLE FKTABLE ADD CONSTRAINT fkdd FOREIGN KEY(ftest1) REFERENCES pktable ON DELETE CASCADE ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED; ALTER TABLE FKTABLE ADD CONSTRAINT fkdi FOREIGN KEY(ftest1) REFERENCES pktable ON DELETE CASCADE ON UPDATE NO ACTION DEFERRABLE INITIALLY IMMEDIATE; ALTER TABLE FKTABLE ADD CONSTRAINT fknd2 FOREIGN KEY(ftest1) REFERENCES pktable ON DELETE CASCADE ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED; ALTER TABLE FKTABLE ALTER CONSTRAINT fknd2 NOT DEFERRABLE; ALTER TABLE FKTABLE ADD CONSTRAINT fkdd2 FOREIGN KEY(ftest1) REFERENCES pktable ON DELETE CASCADE ON UPDATE NO ACTION NOT DEFERRABLE; ALTER TABLE FKTABLE ALTER CONSTRAINT fkdd2 DEFERRABLE INITIALLY DEFERRED; ALTER TABLE FKTABLE ADD CONSTRAINT fkdi2 FOREIGN KEY(ftest1) REFERENCES pktable ON DELETE CASCADE ON UPDATE NO ACTION NOT DEFERRABLE; ALTER TABLE FKTABLE ALTER CONSTRAINT fkdi2 DEFERRABLE INITIALLY IMMEDIATE; SELECT conname, tgfoid::regproc, tgtype, tgdeferrable, tginitdeferred FROM pg_trigger JOIN pg_constraint con ON con.oid = tgconstraint WHERE tgrelid = 'pktable'::regclass ORDER BY 1,2,3; SELECT conname, tgfoid::regproc, tgtype, tgdeferrable, tginitdeferred FROM pg_trigger JOIN pg_constraint con ON con.oid = tgconstraint WHERE tgrelid = 'fktable'::regclass ORDER BY 1,2,3; -- temp tables should go away by themselves, need not drop them. -- test check constraint adding create table atacc1 ( test int ); -- add a check constraint alter table atacc1 add constraint atacc_test1 check (test>3); -- should fail insert into atacc1 (test) values (2); -- should succeed insert into atacc1 (test) values (4); drop table atacc1; -- let's do one where the check fails when added create table atacc1 ( test int ); -- insert a soon to be failing row insert into atacc1 (test) values (2); -- add a check constraint (fails) alter table atacc1 add constraint atacc_test1 check (test>3); insert into atacc1 (test) values (4); drop table atacc1; -- let's do one where the check fails because the column doesn't exist create table atacc1 ( test int ); -- add a check constraint (fails) alter table atacc1 add constraint atacc_test1 check (test1>3); drop table atacc1; -- something a little more complicated create table atacc1 ( test int, test2 int, test3 int); -- add a check constraint (fails) alter table atacc1 add constraint atacc_test1 check (test+test23), test2 int); alter table atacc1 add check (test2>test); -- should fail for $2 insert into atacc1 (test2, test) values (3, 4); drop table atacc1; -- inheritance related tests create table atacc1 (test int); create table atacc2 (test2 int); create table atacc3 (test3 int) inherits (atacc1, atacc2); alter table atacc2 add constraint foo check (test2>0); -- fail and then succeed on atacc2 insert into atacc2 (test2) values (-3); insert into atacc2 (test2) values (3); -- fail and then succeed on atacc3 insert into atacc3 (test2) values (-3); insert into atacc3 (test2) values (3); drop table atacc3; drop table atacc2; drop table atacc1; -- same things with one created with INHERIT create table atacc1 (test int); create table atacc2 (test2 int); create table atacc3 (test3 int) inherits (atacc1, atacc2); alter table atacc3 no inherit atacc2; -- fail alter table atacc3 no inherit atacc2; -- make sure it really isn't a child insert into atacc3 (test2) values (3); select test2 from atacc2; -- fail due to missing constraint alter table atacc2 add constraint foo check (test2>0); alter table atacc3 inherit atacc2; -- fail due to missing column alter table atacc3 rename test2 to testx; alter table atacc3 inherit atacc2; -- fail due to mismatched data type alter table atacc3 add test2 bool; alter table atacc3 inherit atacc2; alter table atacc3 drop test2; -- succeed alter table atacc3 add test2 int; update atacc3 set test2 = 4 where test2 is null; alter table atacc3 add constraint foo check (test2>0); alter table atacc3 inherit atacc2; -- fail due to duplicates and circular inheritance alter table atacc3 inherit atacc2; alter table atacc2 inherit atacc3; alter table atacc2 inherit atacc2; -- test that we really are a child now (should see 4 not 3 and cascade should go through) select test2 from atacc2; drop table atacc2 cascade; drop table atacc1; -- adding only to a parent is allowed as of 9.2 create table atacc1 (test int); create table atacc2 (test2 int) inherits (atacc1); -- ok: alter table atacc1 add constraint foo check (test>0) no inherit; -- check constraint is not there on child insert into atacc2 (test) values (-3); -- check constraint is there on parent insert into atacc1 (test) values (-3); insert into atacc1 (test) values (3); -- fail, violating row: alter table atacc2 add constraint foo check (test>0) no inherit; drop table atacc2; drop table atacc1; -- test unique constraint adding create table atacc1 ( test int ) ; -- add a unique constraint alter table atacc1 add constraint atacc_test1 unique (test); -- insert first value insert into atacc1 (test) values (2); -- should fail insert into atacc1 (test) values (2); -- should succeed insert into atacc1 (test) values (4); -- try to create duplicates via alter table using - should fail alter table atacc1 alter column test type integer using 0; drop table atacc1; -- let's do one where the unique constraint fails when added create table atacc1 ( test int ); -- insert soon to be failing rows insert into atacc1 (test) values (2); insert into atacc1 (test) values (2); -- add a unique constraint (fails) alter table atacc1 add constraint atacc_test1 unique (test); insert into atacc1 (test) values (3); drop table atacc1; -- let's do one where the unique constraint fails -- because the column doesn't exist create table atacc1 ( test int ); -- add a unique constraint (fails) alter table atacc1 add constraint atacc_test1 unique (test1); drop table atacc1; -- something a little more complicated create table atacc1 ( test int, test2 int); -- add a unique constraint alter table atacc1 add constraint atacc_test1 unique (test, test2); -- insert initial value insert into atacc1 (test,test2) values (4,4); -- should fail insert into atacc1 (test,test2) values (4,4); -- should all succeed insert into atacc1 (test,test2) values (4,5); insert into atacc1 (test,test2) values (5,4); insert into atacc1 (test,test2) values (5,5); drop table atacc1; -- lets do some naming tests create table atacc1 (test int, test2 int, unique(test)); alter table atacc1 add unique (test2); -- should fail for @@ second one @@ insert into atacc1 (test2, test) values (3, 3); insert into atacc1 (test2, test) values (2, 3); drop table atacc1; -- test primary key constraint adding create table atacc1 ( id serial, test int) ; -- add a primary key constraint alter table atacc1 add constraint atacc_test1 primary key (test); -- insert first value insert into atacc1 (test) values (2); -- should fail insert into atacc1 (test) values (2); -- should succeed insert into atacc1 (test) values (4); -- inserting NULL should fail insert into atacc1 (test) values(NULL); -- try adding a second primary key (should fail) alter table atacc1 add constraint atacc_oid1 primary key(id); -- drop first primary key constraint alter table atacc1 drop constraint atacc_test1 restrict; -- try adding a primary key on oid (should succeed) alter table atacc1 add constraint atacc_oid1 primary key(id); drop table atacc1; -- let's do one where the primary key constraint fails when added create table atacc1 ( test int ); -- insert soon to be failing rows insert into atacc1 (test) values (2); insert into atacc1 (test) values (2); -- add a primary key (fails) alter table atacc1 add constraint atacc_test1 primary key (test); insert into atacc1 (test) values (3); drop table atacc1; -- let's do another one where the primary key constraint fails when added create table atacc1 ( test int ); -- insert soon to be failing row insert into atacc1 (test) values (NULL); -- add a primary key (fails) alter table atacc1 add constraint atacc_test1 primary key (test); insert into atacc1 (test) values (3); drop table atacc1; -- let's do one where the primary key constraint fails -- because the column doesn't exist create table atacc1 ( test int ); -- add a primary key constraint (fails) alter table atacc1 add constraint atacc_test1 primary key (test1); drop table atacc1; -- adding a new column as primary key to a non-empty table. -- should fail unless the column has a non-null default value. create table atacc1 ( test int ); insert into atacc1 (test) values (0); -- add a primary key column without a default (fails). alter table atacc1 add column test2 int primary key; -- now add a primary key column with a default (succeeds). alter table atacc1 add column test2 int default 0 primary key; drop table atacc1; -- this combination used to have order-of-execution problems (bug #15580) create table atacc1 (a int); insert into atacc1 values(1); alter table atacc1 add column b float8 not null default random(), add primary key(a); drop table atacc1; -- something a little more complicated create table atacc1 ( test int, test2 int); -- add a primary key constraint alter table atacc1 add constraint atacc_test1 primary key (test, test2); -- try adding a second primary key - should fail alter table atacc1 add constraint atacc_test2 primary key (test); -- insert initial value insert into atacc1 (test,test2) values (4,4); -- should fail insert into atacc1 (test,test2) values (4,4); insert into atacc1 (test,test2) values (NULL,3); insert into atacc1 (test,test2) values (3, NULL); insert into atacc1 (test,test2) values (NULL,NULL); -- should all succeed insert into atacc1 (test,test2) values (4,5); insert into atacc1 (test,test2) values (5,4); insert into atacc1 (test,test2) values (5,5); drop table atacc1; -- lets do some naming tests create table atacc1 (test int, test2 int, primary key(test)); -- only first should succeed insert into atacc1 (test2, test) values (3, 3); insert into atacc1 (test2, test) values (2, 3); insert into atacc1 (test2, test) values (1, NULL); drop table atacc1; -- alter table / alter column [set/drop] not null tests -- try altering system catalogs, should fail alter table pg_class alter column relname drop not null; alter table pg_class alter relname set not null; -- try altering non-existent table, should fail alter table non_existent alter column bar set not null; alter table non_existent alter column bar drop not null; -- test setting columns to null and not null and vice versa -- test checking for null values and primary key create table atacc1 (test int not null); alter table atacc1 add constraint "atacc1_pkey" primary key (test); alter table atacc1 alter column test drop not null; alter table atacc1 drop constraint "atacc1_pkey"; alter table atacc1 alter column test drop not null; insert into atacc1 values (null); alter table atacc1 alter test set not null; delete from atacc1; alter table atacc1 alter test set not null; -- try altering a non-existent column, should fail alter table atacc1 alter bar set not null; alter table atacc1 alter bar drop not null; -- try creating a view and altering that, should fail create view myview as select * from atacc1; alter table myview alter column test drop not null; alter table myview alter column test set not null; drop view myview; drop table atacc1; -- set not null verified by constraints create table atacc1 (test_a int, test_b int); insert into atacc1 values (null, 1); -- constraint not cover all values, should fail alter table atacc1 add constraint atacc1_constr_or check(test_a is not null or test_b < 10); alter table atacc1 alter test_a set not null; alter table atacc1 drop constraint atacc1_constr_or; -- not valid constraint, should fail alter table atacc1 add constraint atacc1_constr_invalid check(test_a is not null) not valid; alter table atacc1 alter test_a set not null; alter table atacc1 drop constraint atacc1_constr_invalid; -- with valid constraint update atacc1 set test_a = 1; alter table atacc1 add constraint atacc1_constr_a_valid check(test_a is not null); alter table atacc1 alter test_a set not null; delete from atacc1; insert into atacc1 values (2, null); alter table atacc1 alter test_a drop not null; -- test multiple set not null at same time -- test_a checked by atacc1_constr_a_valid, test_b should fail by table scan alter table atacc1 alter test_a set not null, alter test_b set not null; -- commands order has no importance alter table atacc1 alter test_b set not null, alter test_a set not null; -- valid one by table scan, one by check constraints update atacc1 set test_b = 1; alter table atacc1 alter test_b set not null, alter test_a set not null; alter table atacc1 alter test_a drop not null, alter test_b drop not null; -- both column has check constraints alter table atacc1 add constraint atacc1_constr_b_valid check(test_b is not null); alter table atacc1 alter test_b set not null, alter test_a set not null; drop table atacc1; -- test inheritance create table parent (a int); create table child (b varchar(255)) inherits (parent); alter table parent alter a set not null; insert into parent values (NULL); insert into child (a, b) values (NULL, 'foo'); alter table parent alter a drop not null; insert into parent values (NULL); insert into child (a, b) values (NULL, 'foo'); alter table only parent alter a set not null; alter table child alter a set not null; delete from parent; alter table only parent alter a set not null; insert into parent values (NULL); alter table child alter a set not null; insert into child (a, b) values (NULL, 'foo'); delete from child; alter table child alter a set not null; insert into child (a, b) values (NULL, 'foo'); drop table child; drop table parent; -- test setting and removing default values create table def_test ( c1 int4 default 5, c2 text default 'initial_default' ); insert into def_test default values; alter table def_test alter column c1 drop default; insert into def_test default values; alter table def_test alter column c2 drop default; insert into def_test default values; alter table def_test alter column c1 set default 10; alter table def_test alter column c2 set default 'new_default'; insert into def_test default values; select * from def_test; -- set defaults to an incorrect type: this should fail alter table def_test alter column c1 set default 'wrong_datatype'; alter table def_test alter column c2 set default 20; -- set defaults on a non-existent column: this should fail alter table def_test alter column c3 set default 30; -- set defaults on views: we need to create a view, add a rule -- to allow insertions into it, and then alter the view to add -- a default create view def_view_test as select * from def_test; create rule def_view_test_ins as on insert to def_view_test do instead insert into def_test select new.*; insert into def_view_test default values; alter table def_view_test alter column c1 set default 45; insert into def_view_test default values; alter table def_view_test alter column c2 set default 'view_default'; insert into def_view_test default values; select * from def_view_test; drop rule def_view_test_ins on def_view_test; drop view def_view_test; drop table def_test; -- alter table / drop column tests -- try altering system catalogs, should fail alter table pg_class drop column relname; -- try altering non-existent table, should fail alter table nosuchtable drop column bar; -- test dropping columns create table atacc1 (a int4 not null, b int4, c int4 not null, d int4); insert into atacc1 values (1, 2, 3, 4); alter table atacc1 drop a; alter table atacc1 drop a; -- SELECTs select * from atacc1; select * from atacc1 order by a; select * from atacc1 order by "........pg.dropped.1........"; select * from atacc1 group by a; select * from atacc1 group by "........pg.dropped.1........"; select atacc1.* from atacc1; select a from atacc1; select atacc1.a from atacc1; select b,c,d from atacc1; select a,b,c,d from atacc1; select * from atacc1 where a = 1; select "........pg.dropped.1........" from atacc1; select atacc1."........pg.dropped.1........" from atacc1; select "........pg.dropped.1........",b,c,d from atacc1; select * from atacc1 where "........pg.dropped.1........" = 1; -- UPDATEs update atacc1 set a = 3; update atacc1 set b = 2 where a = 3; update atacc1 set "........pg.dropped.1........" = 3; update atacc1 set b = 2 where "........pg.dropped.1........" = 3; -- INSERTs insert into atacc1 values (10, 11, 12, 13); insert into atacc1 values (default, 11, 12, 13); insert into atacc1 values (11, 12, 13); insert into atacc1 (a) values (10); insert into atacc1 (a) values (default); insert into atacc1 (a,b,c,d) values (10,11,12,13); insert into atacc1 (a,b,c,d) values (default,11,12,13); insert into atacc1 (b,c,d) values (11,12,13); insert into atacc1 ("........pg.dropped.1........") values (10); insert into atacc1 ("........pg.dropped.1........") values (default); insert into atacc1 ("........pg.dropped.1........",b,c,d) values (10,11,12,13); insert into atacc1 ("........pg.dropped.1........",b,c,d) values (default,11,12,13); -- DELETEs delete from atacc1 where a = 3; delete from atacc1 where "........pg.dropped.1........" = 3; delete from atacc1; -- try dropping a non-existent column, should fail alter table atacc1 drop bar; -- try removing an oid column, should succeed (as it's nonexistant) alter table atacc1 SET WITHOUT OIDS; -- try adding an oid column, should fail (not supported) alter table atacc1 SET WITH OIDS; -- try dropping the xmin column, should fail alter table atacc1 drop xmin; -- try creating a view and altering that, should fail create view myview as select * from atacc1; select * from myview; alter table myview drop d; drop view myview; -- test some commands to make sure they fail on the dropped column analyze atacc1(a); analyze atacc1("........pg.dropped.1........"); vacuum analyze atacc1(a); vacuum analyze atacc1("........pg.dropped.1........"); comment on column atacc1.a is 'testing'; comment on column atacc1."........pg.dropped.1........" is 'testing'; alter table atacc1 alter a set storage plain; alter table atacc1 alter "........pg.dropped.1........" set storage plain; alter table atacc1 alter a set statistics 0; alter table atacc1 alter "........pg.dropped.1........" set statistics 0; alter table atacc1 alter a set default 3; alter table atacc1 alter "........pg.dropped.1........" set default 3; alter table atacc1 alter a drop default; alter table atacc1 alter "........pg.dropped.1........" drop default; alter table atacc1 alter a set not null; alter table atacc1 alter "........pg.dropped.1........" set not null; alter table atacc1 alter a drop not null; alter table atacc1 alter "........pg.dropped.1........" drop not null; alter table atacc1 rename a to x; alter table atacc1 rename "........pg.dropped.1........" to x; alter table atacc1 add primary key(a); alter table atacc1 add primary key("........pg.dropped.1........"); alter table atacc1 add unique(a); alter table atacc1 add unique("........pg.dropped.1........"); alter table atacc1 add check (a > 3); alter table atacc1 add check ("........pg.dropped.1........" > 3); create table atacc2 (id int4 unique); alter table atacc1 add foreign key (a) references atacc2(id); alter table atacc1 add foreign key ("........pg.dropped.1........") references atacc2(id); alter table atacc2 add foreign key (id) references atacc1(a); alter table atacc2 add foreign key (id) references atacc1("........pg.dropped.1........"); drop table atacc2; create index "testing_idx" on atacc1(a); create index "testing_idx" on atacc1("........pg.dropped.1........"); -- test create as and select into insert into atacc1 values (21, 22, 23); create table attest1 as select * from atacc1; select * from attest1; drop table attest1; select * into attest2 from atacc1; select * from attest2; drop table attest2; -- try dropping all columns alter table atacc1 drop c; alter table atacc1 drop d; alter table atacc1 drop b; select * from atacc1; drop table atacc1; -- test constraint error reporting in presence of dropped columns create table atacc1 (id serial primary key, value int check (value < 10)); insert into atacc1(value) values (100); alter table atacc1 drop column value; alter table atacc1 add column value int check (value < 10); insert into atacc1(value) values (100); insert into atacc1(id, value) values (null, 0); drop table atacc1; -- test inheritance create table parent (a int, b int, c int); insert into parent values (1, 2, 3); alter table parent drop a; create table child (d varchar(255)) inherits (parent); insert into child values (12, 13, 'testing'); select * from parent; select * from child; alter table parent drop c; select * from parent; select * from child; drop table child; drop table parent; -- check error cases for inheritance column merging create table parent (a float8, b numeric(10,4), c text collate "C"); create table child (a float4) inherits (parent); -- fail create table child (b decimal(10,7)) inherits (parent); -- fail create table child (c text collate "POSIX") inherits (parent); -- fail create table child (a double precision, b decimal(10,4)) inherits (parent); drop table child; drop table parent; -- test copy in/out create table attest (a int4, b int4, c int4); insert into attest values (1,2,3); alter table attest drop a; select * from attest; select * from attest; select * from attest; drop table attest; -- test inheritance create table dropColumn (a int, b int, e int); create table dropColumnChild (c int) inherits (dropColumn); create table dropColumnAnother (d int) inherits (dropColumnChild); -- these two should fail alter table dropColumnchild drop column a; alter table only dropColumnChild drop column b; -- these three should work alter table only dropColumn drop column e; alter table dropColumnChild drop column c; alter table dropColumn drop column a; create table renameColumn (a int); create table renameColumnChild (b int) inherits (renameColumn); create table renameColumnAnother (c int) inherits (renameColumnChild); -- these three should fail alter table renameColumnChild rename column a to d; alter table only renameColumnChild rename column a to d; alter table only renameColumn rename column a to d; -- these should work alter table renameColumn rename column a to d; alter table renameColumnChild rename column b to a; -- these should work alter table if exists doesnt_exist_tab rename column a to d; alter table if exists doesnt_exist_tab rename column b to a; -- this should work alter table renameColumn add column w int; -- this should fail alter table only renameColumn add column x int; -- Test corner cases in dropping of inherited columns create table p1 (f1 int, f2 int); create table c1 (f1 int not null) inherits(p1); -- should be rejected since c1.f1 is inherited alter table c1 drop column f1; -- should work alter table p1 drop column f1; -- c1.f1 is still there, but no longer inherited select f1 from c1; alter table c1 drop column f1; select f1 from c1; drop table p1 cascade; create table p1 (f1 int, f2 int); create table c1 () inherits(p1); -- should be rejected since c1.f1 is inherited alter table c1 drop column f1; alter table p1 drop column f1; -- c1.f1 is dropped now, since there is no local definition for it select f1 from c1; drop table p1 cascade; create table p1 (f1 int, f2 int); create table c1 () inherits(p1); -- should be rejected since c1.f1 is inherited alter table c1 drop column f1; alter table only p1 drop column f1; -- c1.f1 is NOT dropped, but must now be considered non-inherited alter table c1 drop column f1; drop table p1 cascade; create table p1 (f1 int, f2 int); create table c1 (f1 int not null) inherits(p1); -- should be rejected since c1.f1 is inherited alter table c1 drop column f1; alter table only p1 drop column f1; -- c1.f1 is still there, but no longer inherited alter table c1 drop column f1; drop table p1 cascade; create table p1(id int, name text); create table p2(id2 int, name text, height int); create table c1(age int) inherits(p1,p2); create table gc1() inherits (c1); select relname, attname, attinhcount, attislocal from pg_class join pg_attribute on (pg_class.oid = pg_attribute.attrelid) where relname in ('p1','p2','c1','gc1') and attnum > 0 and not attisdropped order by relname, attnum; -- should work alter table only p1 drop column name; -- should work. Now c1.name is local and inhcount is 0. alter table p2 drop column name; -- should be rejected since its inherited alter table gc1 drop column name; -- should work, and drop gc1.name along alter table c1 drop column name; -- should fail: column does not exist alter table gc1 drop column name; -- should work and drop the attribute in all tables alter table p2 drop column height; -- IF EXISTS test create table dropColumnExists (); alter table dropColumnExists drop column non_existing; --fail alter table dropColumnExists drop column if exists non_existing; --succeed select relname, attname, attinhcount, attislocal from pg_class join pg_attribute on (pg_class.oid = pg_attribute.attrelid) where relname in ('p1','p2','c1','gc1') and attnum > 0 and not attisdropped order by relname, attnum; drop table p1, p2 cascade; -- test attinhcount tracking with merged columns create table depth0(); create table depth1(c text) inherits (depth0); create table depth2() inherits (depth1); alter table depth0 add c text; select attrelid::regclass, attname, attinhcount, attislocal from pg_attribute where attnum > 0 and attrelid::regclass in ('depth0', 'depth1', 'depth2') order by attrelid::regclass::text, attnum; -- test renumbering of child-table columns in inherited operations create table p1 (f1 int); create table c1 (f2 text, f3 int) inherits (p1); alter table p1 add column a1 int check (a1 > 0); alter table p1 add column f2 text; insert into p1 values (1,2,'abc'); insert into c1 values(11,'xyz',33,0); -- should fail insert into c1 values(11,'xyz',33,22); select * from p1; update p1 set a1 = a1 + 1, f2 = upper(f2); select * from p1; drop table p1 cascade; -- test that operations with a dropped column do not try to reference -- its datatype create domain mytype as text; create temp table foo (f1 text, f2 mytype, f3 text); insert into foo values('bb','cc','dd'); select * from foo; drop domain mytype cascade; select * from foo; insert into foo values('qq','rr'); select * from foo; update foo set f3 = 'zz'; select * from foo; select f3,max(f1) from foo group by f3; -- Simple tests for alter table column type alter table foo alter f1 TYPE integer; -- fails alter table foo alter f1 TYPE varchar(10); create table anothertab (atcol1 serial8, atcol2 boolean, constraint anothertab_chk check (atcol1 <= 3)); insert into anothertab (atcol1, atcol2) values (default, true); insert into anothertab (atcol1, atcol2) values (default, false); select * from anothertab; alter table anothertab alter column atcol1 type boolean; -- fails alter table anothertab alter column atcol1 type boolean using atcol1::int; -- fails alter table anothertab alter column atcol1 type integer; select * from anothertab; insert into anothertab (atcol1, atcol2) values (45, null); -- fails insert into anothertab (atcol1, atcol2) values (default, null); select * from anothertab; alter table anothertab alter column atcol2 type text using case when atcol2 is true then 'IT WAS TRUE' when atcol2 is false then 'IT WAS FALSE' else 'IT WAS NULL!' end; select * from anothertab; alter table anothertab alter column atcol1 type boolean using case when atcol1 % 2 = 0 then true else false end; -- fails alter table anothertab alter column atcol1 drop default; alter table anothertab alter column atcol1 type boolean using case when atcol1 % 2 = 0 then true else false end; -- fails alter table anothertab drop constraint anothertab_chk; alter table anothertab drop constraint anothertab_chk; -- fails alter table anothertab drop constraint IF EXISTS anothertab_chk; -- succeeds alter table anothertab alter column atcol1 type boolean using case when atcol1 % 2 = 0 then true else false end; select * from anothertab; drop table anothertab; create table another (f1 int, f2 text); insert into another values(1, 'one'); insert into another values(2, 'two'); insert into another values(3, 'three'); select * from another; alter table another alter f1 type text using f2 || ' more', alter f2 type bigint using f1 * 10; select * from another; drop table another; -- table's row type create table tab1 (a int, b text); create table tab2 (x int, y tab1); alter table tab1 alter column b type varchar; -- fails -- Alter column type that's part of a partitioned index create table at_partitioned (a int, b text) partition by range (a); create table at_part_1 partition of at_partitioned for values from (0) to (1000); insert into at_partitioned values (512, '0.123'); create table at_part_2 (b text, a int); insert into at_part_2 values ('1.234', 1024); create index on at_partitioned (b); create index on at_partitioned (a); \d at_part_1 \d at_part_2 alter table at_partitioned attach partition at_part_2 for values from (1000) to (2000); \d at_part_2 alter table at_partitioned alter column b type numeric using b::numeric; \d at_part_1 \d at_part_2 drop table at_partitioned; -- Alter column type when no table rewrite is required -- Also check that comments are preserved create table at_partitioned(id int, name varchar(64), unique (id, name)) partition by hash(id); comment on constraint at_partitioned_id_name_key on at_partitioned is 'parent constraint'; comment on index at_partitioned_id_name_key is 'parent index'; create table at_partitioned_0 partition of at_partitioned for values with (modulus 2, remainder 0); comment on constraint at_partitioned_0_id_name_key on at_partitioned_0 is 'child 0 constraint'; comment on index at_partitioned_0_id_name_key is 'child 0 index'; create table at_partitioned_1 partition of at_partitioned for values with (modulus 2, remainder 1); comment on constraint at_partitioned_1_id_name_key on at_partitioned_1 is 'child 1 constraint'; comment on index at_partitioned_1_id_name_key is 'child 1 index'; insert into at_partitioned values(1, 'foo'); insert into at_partitioned values(3, 'bar'); create temp table old_oids as select relname, oid as oldoid, relfilenode as oldfilenode from pg_class where relname like 'at_partitioned%'; select relname, c.oid = oldoid as orig_oid, case relfilenode when 0 then 'none' when c.oid then 'own' when oldfilenode then 'orig' else 'OTHER' end as storage, obj_description(c.oid, 'pg_class') as desc from pg_class c left join old_oids using (relname) where relname like 'at_partitioned%' order by relname; select conname, obj_description(oid, 'pg_constraint') as desc from pg_constraint where conname like 'at_partitioned%' order by conname; alter table at_partitioned alter column name type varchar(127); -- Note: these tests currently show the wrong behavior for comments :-( select relname, c.oid = oldoid as orig_oid, case relfilenode when 0 then 'none' when c.oid then 'own' when oldfilenode then 'orig' else 'OTHER' end as storage, obj_description(c.oid, 'pg_class') as desc from pg_class c left join old_oids using (relname) where relname like 'at_partitioned%' order by relname; select conname, obj_description(oid, 'pg_constraint') as desc from pg_constraint where conname like 'at_partitioned%' order by conname; -- Don't remove this DROP, it exposes bug #15672 drop table at_partitioned; -- disallow recursive containment of row types create temp table recur1 (f1 int); alter table recur1 add column f2 recur1; -- fails alter table recur1 add column f2 recur1[]; -- fails create domain array_of_recur1 as recur1[]; alter table recur1 add column f2 array_of_recur1; -- fails create temp table recur2 (f1 int, f2 recur1); alter table recur1 add column f2 recur2; -- fails alter table recur1 add column f2 int; alter table recur1 alter column f2 type recur2; -- fails -- SET STORAGE may need to add a TOAST table create table test_storage (a text); alter table test_storage alter a set storage plain; alter table test_storage add b int default 0; -- rewrite table to remove its TOAST table alter table test_storage alter a set storage extended; -- re-add TOAST table select reltoastrelid <> 0 as has_toast_table from pg_class where oid = 'test_storage'::regclass; -- ALTER COLUMN TYPE with a check constraint and a child table (bug #13779) CREATE TABLE test_inh_check (a float check (a > 10.2), b float); CREATE TABLE test_inh_check_child() INHERITS(test_inh_check); \d test_inh_check \d test_inh_check_child select relname, conname, coninhcount, conislocal, connoinherit from pg_constraint c, pg_class r where relname like 'test_inh_check%' and c.conrelid = r.oid order by 1, 2; ALTER TABLE test_inh_check ALTER COLUMN a TYPE numeric; \d test_inh_check \d test_inh_check_child select relname, conname, coninhcount, conislocal, connoinherit from pg_constraint c, pg_class r where relname like 'test_inh_check%' and c.conrelid = r.oid order by 1, 2; -- also try noinherit, local, and local+inherited cases ALTER TABLE test_inh_check ADD CONSTRAINT bnoinherit CHECK (b > 100) NO INHERIT; ALTER TABLE test_inh_check_child ADD CONSTRAINT blocal CHECK (b < 1000); ALTER TABLE test_inh_check_child ADD CONSTRAINT bmerged CHECK (b > 1); ALTER TABLE test_inh_check ADD CONSTRAINT bmerged CHECK (b > 1); \d test_inh_check \d test_inh_check_child select relname, conname, coninhcount, conislocal, connoinherit from pg_constraint c, pg_class r where relname like 'test_inh_check%' and c.conrelid = r.oid order by 1, 2; ALTER TABLE test_inh_check ALTER COLUMN b TYPE numeric; \d test_inh_check \d test_inh_check_child select relname, conname, coninhcount, conislocal, connoinherit from pg_constraint c, pg_class r where relname like 'test_inh_check%' and c.conrelid = r.oid order by 1, 2; -- ALTER COLUMN TYPE with different schema in children -- Bug at https://postgr.es/m/20170102225618.GA10071@telsasoft.com CREATE TABLE test_type_diff (f1 int); CREATE TABLE test_type_diff_c (extra smallint) INHERITS (test_type_diff); ALTER TABLE test_type_diff ADD COLUMN f2 int; INSERT INTO test_type_diff_c VALUES (1, 2, 3); ALTER TABLE test_type_diff ALTER COLUMN f2 TYPE bigint USING f2::bigint; CREATE TABLE test_type_diff2 (int_two int2, int_four int4, int_eight int8); CREATE TABLE test_type_diff2_c1 (int_four int4, int_eight int8, int_two int2); CREATE TABLE test_type_diff2_c2 (int_eight int8, int_two int2, int_four int4); CREATE TABLE test_type_diff2_c3 (int_two int2, int_four int4, int_eight int8); ALTER TABLE test_type_diff2_c1 INHERIT test_type_diff2; ALTER TABLE test_type_diff2_c2 INHERIT test_type_diff2; ALTER TABLE test_type_diff2_c3 INHERIT test_type_diff2; INSERT INTO test_type_diff2_c1 VALUES (1, 2, 3); INSERT INTO test_type_diff2_c2 VALUES (4, 5, 6); INSERT INTO test_type_diff2_c3 VALUES (7, 8, 9); ALTER TABLE test_type_diff2 ALTER COLUMN int_four TYPE int8 USING int_four::int8; -- whole-row references are disallowed ALTER TABLE test_type_diff2 ALTER COLUMN int_four TYPE int4 USING (pg_column_size(test_type_diff2)); -- check for rollback of ANALYZE corrupting table property flags (bug #11638) CREATE TABLE check_fk_presence_1 (id int PRIMARY KEY, t text); CREATE TABLE check_fk_presence_2 (id int REFERENCES check_fk_presence_1, t text); BEGIN; ALTER TABLE check_fk_presence_2 DROP CONSTRAINT check_fk_presence_2_id_fkey; ANALYZE check_fk_presence_2; ROLLBACK; \d check_fk_presence_2 DROP TABLE check_fk_presence_1, check_fk_presence_2; -- check column addition within a view (bug #14876) create table at_base_table(id int, stuff text); insert into at_base_table values (23, 'skidoo'); create view at_view_1 as select * from at_base_table bt; create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; \d+ at_view_1 \d+ at_view_2 explain (verbose, costs off) select * from at_view_2; select * from at_view_2; create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; \d+ at_view_1 \d+ at_view_2 explain (verbose, costs off) select * from at_view_2; select * from at_view_2; drop view at_view_2; drop view at_view_1; drop table at_base_table; -- -- lock levels -- drop type lockmodes; create type lockmodes as enum ( 'SIReadLock' ,'AccessShareLock' ,'RowShareLock' ,'RowExclusiveLock' ,'ShareUpdateExclusiveLock' ,'ShareLock' ,'ShareRowExclusiveLock' ,'ExclusiveLock' ,'AccessExclusiveLock' ); drop view my_locks; create or replace view my_locks as select case when c.relname like 'pg_toast%' then 'pg_toast' else c.relname end, max(mode::lockmodes) as max_lockmode from pg_locks l join pg_class c on l.relation = c.oid where virtualtransaction = ( select virtualtransaction from pg_locks where transactionid = txid_current()::integer) and locktype = 'relation' and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog') and c.relname != 'my_locks' group by c.relname; create table alterlock (f1 int primary key, f2 text); insert into alterlock values (1, 'foo'); create table alterlock2 (f3 int primary key, f1 int); insert into alterlock2 values (1, 1); begin; alter table alterlock alter column f2 set statistics 150; select * from my_locks order by 1; rollback; begin; alter table alterlock cluster on alterlock_pkey; select * from my_locks order by 1; commit; begin; alter table alterlock set without cluster; select * from my_locks order by 1; commit; begin; alter table alterlock set (fillfactor = 100); select * from my_locks order by 1; commit; begin; alter table alterlock reset (fillfactor); select * from my_locks order by 1; commit; begin; alter table alterlock set (toast.autovacuum_enabled = off); select * from my_locks order by 1; commit; begin; alter table alterlock set (autovacuum_enabled = off); select * from my_locks order by 1; commit; begin; alter table alterlock alter column f2 set (n_distinct = 1); select * from my_locks order by 1; rollback; -- test that mixing options with different lock levels works as expected begin; alter table alterlock set (autovacuum_enabled = off, fillfactor = 80); select * from my_locks order by 1; commit; begin; alter table alterlock alter column f2 set storage extended; select * from my_locks order by 1; rollback; begin; alter table alterlock alter column f2 set default 'x'; select * from my_locks order by 1; rollback; begin; create trigger ttdummy before delete or update on alterlock for each row execute procedure ttdummy (1, 1); select * from my_locks order by 1; rollback; begin; select * from my_locks order by 1; alter table alterlock2 add foreign key (f1) references alterlock (f1); select * from my_locks order by 1; rollback; begin; alter table alterlock2 add constraint alterlock2nv foreign key (f1) references alterlock (f1) NOT VALID; select * from my_locks order by 1; commit; begin; alter table alterlock2 validate constraint alterlock2nv; select * from my_locks order by 1; rollback; create or replace view my_locks as select case when c.relname like 'pg_toast%' then 'pg_toast' else c.relname end, max(mode::lockmodes) as max_lockmode from pg_locks l join pg_class c on l.relation = c.oid where virtualtransaction = ( select virtualtransaction from pg_locks where transactionid = txid_current()::integer) and locktype = 'relation' and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog') and c.relname = 'my_locks' group by c.relname; -- raise exception alter table my_locks set (autovacuum_enabled = false); alter view my_locks set (autovacuum_enabled = false); alter table my_locks reset (autovacuum_enabled); alter view my_locks reset (autovacuum_enabled); begin; alter view my_locks set (security_barrier=off); select * from my_locks order by 1; alter view my_locks reset (security_barrier); rollback; -- this test intentionally applies the ALTER TABLE command against a view, but -- uses a view option so we expect this to succeed. This form of SQL is -- accepted for historical reasons, as shown in the docs for ALTER VIEW begin; alter table my_locks set (security_barrier=off); select * from my_locks order by 1; alter table my_locks reset (security_barrier); rollback; -- cleanup drop table alterlock2; drop table alterlock; drop view my_locks; drop type lockmodes; -- -- alter function -- create function test_strict(text) returns text as 'select coalesce($1, ''got passed a null'');' language sql returns null on null input; select test_strict(NULL); alter function test_strict(text) called on null input; select test_strict(NULL); create function non_strict(text) returns text as 'select coalesce($1, ''got passed a null'');' language sql called on null input; select non_strict(NULL); alter function non_strict(text) returns null on null input; select non_strict(NULL); -- -- alter object set schema -- create schema alter1; create schema alter2; create table alter1.t1(f1 serial primary key, f2 int check (f2 > 0)); create view alter1.v1 as select * from alter1.t1; create function alter1.plus1(int) returns int as 'select $1+1' language sql; create domain alter1.posint integer check (value > 0); create type alter1.ctype as (f1 int, f2 text); create function alter1.same(alter1.ctype, alter1.ctype) returns boolean language sql as 'select $1.f1 is not distinct from $2.f1 and $1.f2 is not distinct from $2.f2'; create operator alter1.=(procedure = alter1.same, leftarg = alter1.ctype, rightarg = alter1.ctype); create operator class alter1.ctype_hash_ops default for type alter1.ctype using hash as operator 1 alter1.=(alter1.ctype, alter1.ctype); create conversion alter1.ascii_to_utf8 for 'sql_ascii' to 'utf8' from ascii_to_utf8; create text search parser alter1.prs(start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); create text search configuration alter1.cfg(parser = alter1.prs); create text search template alter1.tmpl(init = dsimple_init, lexize = dsimple_lexize); create text search dictionary alter1.dict(template = alter1.tmpl); insert into alter1.t1(f2) values(11); insert into alter1.t1(f2) values(12); alter table alter1.t1 set schema alter1; -- no-op, same schema alter table alter1.t1 set schema alter2; alter table alter1.v1 set schema alter2; alter function alter1.plus1(int) set schema alter2; alter domain alter1.posint set schema alter2; alter operator class alter1.ctype_hash_ops using hash set schema alter2; alter operator family alter1.ctype_hash_ops using hash set schema alter2; alter operator alter1.=(alter1.ctype, alter1.ctype) set schema alter2; alter function alter1.same(alter1.ctype, alter1.ctype) set schema alter2; alter type alter1.ctype set schema alter1; -- no-op, same schema alter type alter1.ctype set schema alter2; alter conversion alter1.ascii_to_utf8 set schema alter2; alter text search parser alter1.prs set schema alter2; alter text search configuration alter1.cfg set schema alter2; alter text search template alter1.tmpl set schema alter2; alter text search dictionary alter1.dict set schema alter2; -- this should succeed because nothing is left in alter1 drop schema alter1; insert into alter2.t1(f2) values(13); insert into alter2.t1(f2) values(14); select * from alter2.t1; select * from alter2.v1; select alter2.plus1(41); -- clean up drop schema alter2 cascade; -- -- composite types -- CREATE TYPE test_type AS (a int); \d test_type ALTER TYPE nosuchtype ADD ATTRIBUTE b text; -- fails ALTER TYPE test_type ADD ATTRIBUTE b text; \d test_type ALTER TYPE test_type ADD ATTRIBUTE b text; -- fails ALTER TYPE test_type ALTER ATTRIBUTE b SET DATA TYPE varchar; \d test_type ALTER TYPE test_type ALTER ATTRIBUTE b SET DATA TYPE integer; \d test_type ALTER TYPE test_type DROP ATTRIBUTE b; \d test_type ALTER TYPE test_type DROP ATTRIBUTE c; -- fails ALTER TYPE test_type DROP ATTRIBUTE IF EXISTS c; ALTER TYPE test_type DROP ATTRIBUTE a, ADD ATTRIBUTE d boolean; \d test_type ALTER TYPE test_type RENAME ATTRIBUTE a TO aa; ALTER TYPE test_type RENAME ATTRIBUTE d TO dd; \d test_type DROP TYPE test_type; CREATE TYPE test_type1 AS (a int, b text); CREATE TABLE test_tbl1 (x int, y test_type1); ALTER TYPE test_type1 ALTER ATTRIBUTE b TYPE varchar; -- fails CREATE TYPE test_type2 AS (a int, b text); CREATE TABLE test_tbl2 OF test_type2; CREATE TABLE test_tbl2_subclass () INHERITS (test_tbl2); \d test_type2 \d test_tbl2 ALTER TYPE test_type2 ADD ATTRIBUTE c text; -- fails ALTER TYPE test_type2 ADD ATTRIBUTE c text CASCADE; \d test_type2 \d test_tbl2 ALTER TYPE test_type2 ALTER ATTRIBUTE b TYPE varchar; -- fails ALTER TYPE test_type2 ALTER ATTRIBUTE b TYPE varchar CASCADE; \d test_type2 \d test_tbl2 ALTER TYPE test_type2 DROP ATTRIBUTE b; -- fails ALTER TYPE test_type2 DROP ATTRIBUTE b CASCADE; \d test_type2 \d test_tbl2 ALTER TYPE test_type2 RENAME ATTRIBUTE a TO aa; -- fails ALTER TYPE test_type2 RENAME ATTRIBUTE a TO aa CASCADE; \d test_type2 \d test_tbl2 \d test_tbl2_subclass DROP TABLE test_tbl2_subclass; CREATE TYPE test_typex AS (a int, b text); CREATE TABLE test_tblx (x int, y test_typex check ((y).a > 0)); ALTER TYPE test_typex DROP ATTRIBUTE a; -- fails ALTER TYPE test_typex DROP ATTRIBUTE a CASCADE; \d test_tblx DROP TABLE test_tblx; DROP TYPE test_typex; -- This test isn't that interesting on its own, but the purpose is to leave -- behind a table to test pg_upgrade with. The table has a composite type -- column in it, and the composite type has a dropped attribute. CREATE TYPE test_type3 AS (a int); CREATE TABLE test_tbl3 (c) AS SELECT '(1)'::test_type3; ALTER TYPE test_type3 DROP ATTRIBUTE a, ADD ATTRIBUTE b int; CREATE TYPE test_type_empty AS (); DROP TYPE test_type_empty; -- -- typed tables: OF / NOT OF -- CREATE TYPE tt_t0 AS (z inet, x int, y numeric(8,2)); ALTER TYPE tt_t0 DROP ATTRIBUTE z; CREATE TABLE tt0 (x int NOT NULL, y numeric(8,2)); -- OK CREATE TABLE tt1 (x int, y bigint); -- wrong base type CREATE TABLE tt2 (x int, y numeric(9,2)); -- wrong typmod CREATE TABLE tt3 (y numeric(8,2), x int); -- wrong column order CREATE TABLE tt4 (x int); -- too few columns CREATE TABLE tt5 (x int, y numeric(8,2), z int); -- too few columns CREATE TABLE tt6 () INHERITS (tt0); -- can't have a parent CREATE TABLE tt7 (x int, q text, y numeric(8,2)); ALTER TABLE tt7 DROP q; -- OK ALTER TABLE tt0 OF tt_t0; ALTER TABLE tt1 OF tt_t0; ALTER TABLE tt2 OF tt_t0; ALTER TABLE tt3 OF tt_t0; ALTER TABLE tt4 OF tt_t0; ALTER TABLE tt5 OF tt_t0; ALTER TABLE tt6 OF tt_t0; ALTER TABLE tt7 OF tt_t0; CREATE TYPE tt_t1 AS (x int, y numeric(8,2)); ALTER TABLE tt7 OF tt_t1; -- reassign an already-typed table ALTER TABLE tt7 NOT OF; \d tt7 -- make sure we can drop a constraint on the parent but it remains on the child CREATE TABLE test_drop_constr_parent (c text CHECK (c IS NOT NULL)); CREATE TABLE test_drop_constr_child () INHERITS (test_drop_constr_parent); ALTER TABLE ONLY test_drop_constr_parent DROP CONSTRAINT "test_drop_constr_parent_c_check"; -- should fail INSERT INTO test_drop_constr_child (c) VALUES (NULL); DROP TABLE test_drop_constr_parent CASCADE; -- -- IF EXISTS test -- ALTER TABLE IF EXISTS tt8 ADD COLUMN f int; ALTER TABLE IF EXISTS tt8 ADD CONSTRAINT xxx PRIMARY KEY(f); ALTER TABLE IF EXISTS tt8 ADD CHECK (f BETWEEN 0 AND 10); ALTER TABLE IF EXISTS tt8 ALTER COLUMN f SET DEFAULT 0; ALTER TABLE IF EXISTS tt8 RENAME COLUMN f TO f1; ALTER TABLE IF EXISTS tt8 SET SCHEMA alter2; CREATE TABLE tt8(a int); CREATE SCHEMA alter2; ALTER TABLE IF EXISTS tt8 ADD COLUMN f int; ALTER TABLE IF EXISTS tt8 ADD CONSTRAINT xxx PRIMARY KEY(f); ALTER TABLE IF EXISTS tt8 ADD CHECK (f BETWEEN 0 AND 10); ALTER TABLE IF EXISTS tt8 ALTER COLUMN f SET DEFAULT 0; ALTER TABLE IF EXISTS tt8 RENAME COLUMN f TO f1; ALTER TABLE IF EXISTS tt8 SET SCHEMA alter2; \d alter2.tt8 DROP TABLE alter2.tt8; DROP SCHEMA alter2; -- -- Check conflicts between index and CHECK constraint names -- CREATE TABLE tt9(c integer); ALTER TABLE tt9 ADD CHECK(c > 1); ALTER TABLE tt9 ADD CHECK(c > 2); -- picks nonconflicting name ALTER TABLE tt9 ADD CONSTRAINT foo CHECK(c > 3); ALTER TABLE tt9 ADD CONSTRAINT foo CHECK(c > 4); -- fail, dup name ALTER TABLE tt9 ADD UNIQUE(c); ALTER TABLE tt9 ADD UNIQUE(c); -- picks nonconflicting name ALTER TABLE tt9 ADD CONSTRAINT tt9_c_key UNIQUE(c); -- fail, dup name ALTER TABLE tt9 ADD CONSTRAINT foo UNIQUE(c); -- fail, dup name ALTER TABLE tt9 ADD CONSTRAINT tt9_c_key CHECK(c > 5); -- fail, dup name ALTER TABLE tt9 ADD CONSTRAINT tt9_c_key2 CHECK(c > 6); ALTER TABLE tt9 ADD UNIQUE(c); -- picks nonconflicting name \d tt9 DROP TABLE tt9; -- Check that comments on constraints and indexes are not lost at ALTER TABLE. CREATE TABLE comment_test ( id int, positive_col int CHECK (positive_col > 0), indexed_col int, CONSTRAINT comment_test_pk PRIMARY KEY (id)); CREATE INDEX comment_test_index ON comment_test(indexed_col); COMMENT ON COLUMN comment_test.id IS 'Column ''id'' on comment_test'; COMMENT ON INDEX comment_test_index IS 'Simple index on comment_test'; COMMENT ON CONSTRAINT comment_test_positive_col_check ON comment_test IS 'CHECK constraint on comment_test.positive_col'; COMMENT ON CONSTRAINT comment_test_pk ON comment_test IS 'PRIMARY KEY constraint of comment_test'; COMMENT ON INDEX comment_test_pk IS 'Index backing the PRIMARY KEY of comment_test'; SELECT col_description('comment_test'::regclass, 1) as comment; SELECT indexrelid::regclass::text as index, obj_description(indexrelid, 'pg_class') as comment FROM pg_index where indrelid = 'comment_test'::regclass ORDER BY 1, 2; SELECT conname as constraint, obj_description(oid, 'pg_constraint') as comment FROM pg_constraint where conrelid = 'comment_test'::regclass ORDER BY 1, 2; -- Change the datatype of all the columns. ALTER TABLE is optimized to not -- rebuild an index if the new data type is binary compatible with the old -- one. Check do a dummy ALTER TABLE that doesn't change the datatype -- first, to test that no-op codepath, and another one that does. ALTER TABLE comment_test ALTER COLUMN indexed_col SET DATA TYPE int; ALTER TABLE comment_test ALTER COLUMN indexed_col SET DATA TYPE text; ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int; ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text; ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int; ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint; -- Check that the comments are intact. SELECT col_description('comment_test'::regclass, 1) as comment; SELECT indexrelid::regclass::text as index, obj_description(indexrelid, 'pg_class') as comment FROM pg_index where indrelid = 'comment_test'::regclass ORDER BY 1, 2; SELECT conname as constraint, obj_description(oid, 'pg_constraint') as comment FROM pg_constraint where conrelid = 'comment_test'::regclass ORDER BY 1, 2; -- Check compatibility for foreign keys and comments. This is done -- separately as rebuilding the column type of the parent leads -- to an error and would reduce the test scope. CREATE TABLE comment_test_child ( id text CONSTRAINT comment_test_child_fk REFERENCES comment_test); CREATE INDEX comment_test_child_fk ON comment_test_child(id); COMMENT ON COLUMN comment_test_child.id IS 'Column ''id'' on comment_test_child'; COMMENT ON INDEX comment_test_child_fk IS 'Index backing the FOREIGN KEY of comment_test_child'; COMMENT ON CONSTRAINT comment_test_child_fk ON comment_test_child IS 'FOREIGN KEY constraint of comment_test_child'; -- Change column type of parent ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text; ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int USING id::integer; -- Comments should be intact SELECT col_description('comment_test_child'::regclass, 1) as comment; SELECT indexrelid::regclass::text as index, obj_description(indexrelid, 'pg_class') as comment FROM pg_index where indrelid = 'comment_test_child'::regclass ORDER BY 1, 2; SELECT conname as constraint, obj_description(oid, 'pg_constraint') as comment FROM pg_constraint where conrelid = 'comment_test_child'::regclass ORDER BY 1, 2; -- Check that we map relation oids to filenodes and back correctly. Only -- display bad mappings so the test output doesn't change all the time. A -- filenode function call can return NULL for a relation dropped concurrently -- with the call's surrounding query, so ignore a NULL mapped_oid for -- relations that no longer exist after all calls finish. CREATE TEMP TABLE filenode_mapping AS SELECT oid, mapped_oid, reltablespace, relfilenode, relname FROM pg_class, pg_filenode_relation(reltablespace, pg_relation_filenode(oid)) AS mapped_oid WHERE relkind IN ('r', 'i', 'S', 't', 'm') AND mapped_oid IS DISTINCT FROM oid; SELECT m.* FROM filenode_mapping m LEFT JOIN pg_class c ON c.oid = m.oid WHERE c.oid IS NOT NULL OR m.mapped_oid IS NOT NULL; -- Checks on creating and manipulation of user defined relations in -- pg_catalog. -- -- XXX: It would be useful to add checks around trying to manipulate -- catalog tables, but that might have ugly consequences when run -- against an existing server with allow_system_table_mods = on. SHOW allow_system_table_mods; -- disallowed because of search_path issues with pg_dump CREATE TABLE pg_catalog.new_system_table(); -- instead create in public first, move to catalog CREATE TABLE new_system_table(id serial primary key, othercol text); ALTER TABLE new_system_table SET SCHEMA pg_catalog; ALTER TABLE new_system_table SET SCHEMA public; ALTER TABLE new_system_table SET SCHEMA pg_catalog; -- will be ignored -- already there: ALTER TABLE new_system_table SET SCHEMA pg_catalog; ALTER TABLE new_system_table RENAME TO old_system_table; CREATE INDEX old_system_table__othercol ON old_system_table (othercol); INSERT INTO old_system_table(othercol) VALUES ('somedata'), ('otherdata'); UPDATE old_system_table SET id = -id; DELETE FROM old_system_table WHERE othercol = 'somedata'; TRUNCATE old_system_table; ALTER TABLE old_system_table DROP CONSTRAINT new_system_table_pkey; ALTER TABLE old_system_table DROP COLUMN othercol; DROP TABLE old_system_table; -- set logged CREATE UNLOGGED TABLE unlogged1(f1 SERIAL PRIMARY KEY, f2 TEXT); -- check relpersistence of an unlogged table SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged1' UNION ALL SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged1' UNION ALL SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged1' ORDER BY relname; CREATE UNLOGGED TABLE unlogged2(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES unlogged1); -- foreign key CREATE UNLOGGED TABLE unlogged3(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES unlogged3); -- self-referencing foreign key ALTER TABLE unlogged3 SET LOGGED; -- skip self-referencing foreign key ALTER TABLE unlogged2 SET LOGGED; -- fails because a foreign key to an unlogged table exists ALTER TABLE unlogged1 SET LOGGED; -- check relpersistence of an unlogged table after changing to permanent SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged1' UNION ALL SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged1' UNION ALL SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged1' ORDER BY relname; ALTER TABLE unlogged1 SET LOGGED; -- silently do nothing DROP TABLE unlogged3; DROP TABLE unlogged2; DROP TABLE unlogged1; -- set unlogged CREATE TABLE logged1(f1 SERIAL PRIMARY KEY, f2 TEXT); -- check relpersistence of a permanent table SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged1' UNION ALL SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged1' UNION ALL SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged1' ORDER BY relname; CREATE TABLE logged2(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES logged1); -- foreign key CREATE TABLE logged3(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES logged3); -- self-referencing foreign key ALTER TABLE logged1 SET UNLOGGED; -- fails because a foreign key from a permanent table exists ALTER TABLE logged3 SET UNLOGGED; -- skip self-referencing foreign key ALTER TABLE logged2 SET UNLOGGED; ALTER TABLE logged1 SET UNLOGGED; -- check relpersistence of a permanent table after changing to unlogged SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged1' UNION ALL SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged1' UNION ALL SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged1' ORDER BY relname; ALTER TABLE logged1 SET UNLOGGED; -- silently do nothing DROP TABLE logged3; DROP TABLE logged2; DROP TABLE logged1; -- test ADD COLUMN IF NOT EXISTS CREATE TABLE test_add_column(c1 integer); \d test_add_column ALTER TABLE test_add_column ADD COLUMN c2 integer; \d test_add_column ALTER TABLE test_add_column ADD COLUMN c2 integer; -- fail because c2 already exists ALTER TABLE ONLY test_add_column ADD COLUMN c2 integer; -- fail because c2 already exists \d test_add_column ALTER TABLE test_add_column ADD COLUMN IF NOT EXISTS c2 integer; -- skipping because c2 already exists ALTER TABLE ONLY test_add_column ADD COLUMN IF NOT EXISTS c2 integer; -- skipping because c2 already exists \d test_add_column ALTER TABLE test_add_column ADD COLUMN c2 integer, -- fail because c2 already exists ADD COLUMN c3 integer; \d test_add_column ALTER TABLE test_add_column ADD COLUMN IF NOT EXISTS c2 integer, -- skipping because c2 already exists ADD COLUMN c3 integer; -- fail because c3 already exists \d test_add_column ALTER TABLE test_add_column ADD COLUMN IF NOT EXISTS c2 integer, -- skipping because c2 already exists ADD COLUMN IF NOT EXISTS c3 integer; -- skipping because c3 already exists \d test_add_column ALTER TABLE test_add_column ADD COLUMN IF NOT EXISTS c2 integer, -- skipping because c2 already exists ADD COLUMN IF NOT EXISTS c3 integer, -- skipping because c3 already exists ADD COLUMN c4 integer; \d test_add_column DROP TABLE test_add_column; -- unsupported constraint types for partitioned tables CREATE TABLE partitioned ( a int, b int ) PARTITION BY RANGE (a, (a+b+1)); ALTER TABLE partitioned ADD EXCLUDE USING gist (a WITH &&); -- cannot drop column that is part of the partition key ALTER TABLE partitioned DROP COLUMN a; ALTER TABLE partitioned ALTER COLUMN a TYPE char(5); ALTER TABLE partitioned DROP COLUMN b; ALTER TABLE partitioned ALTER COLUMN b TYPE char(5); -- partitioned table cannot participate in regular inheritance CREATE TABLE nonpartitioned ( a int, b int ); ALTER TABLE partitioned INHERIT nonpartitioned; ALTER TABLE nonpartitioned INHERIT partitioned; -- cannot add NO INHERIT constraint to partitioned tables ALTER TABLE partitioned ADD CONSTRAINT chk_a CHECK (a > 0) NO INHERIT; DROP TABLE partitioned, nonpartitioned; -- -- ATTACH PARTITION -- -- check that target table is partitioned CREATE TABLE unparted ( a int ); CREATE TABLE fail_part (like unparted); ALTER TABLE unparted ATTACH PARTITION fail_part FOR VALUES IN ('a'); DROP TABLE unparted, fail_part; -- check that partition bound is compatible CREATE TABLE list_parted ( a int NOT NULL, b char(2) COLLATE "C", CONSTRAINT check_a CHECK (a > 0) ) PARTITION BY LIST (a); CREATE TABLE fail_part (LIKE list_parted); ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES FROM (1) TO (10); DROP TABLE fail_part; -- check that the table being attached exists ALTER TABLE list_parted ATTACH PARTITION nonexistant FOR VALUES IN (1); -- check ownership of the source table CREATE ROLE regress_test_me; CREATE ROLE regress_test_not_me; CREATE TABLE not_owned_by_me (LIKE list_parted); ALTER TABLE not_owned_by_me OWNER TO regress_test_not_me; SET SESSION AUTHORIZATION regress_test_me; CREATE TABLE owned_by_me ( a int ) PARTITION BY LIST (a); ALTER TABLE owned_by_me ATTACH PARTITION not_owned_by_me FOR VALUES IN (1); RESET SESSION AUTHORIZATION; DROP TABLE owned_by_me, not_owned_by_me; DROP ROLE regress_test_not_me; DROP ROLE regress_test_me; -- check that the table being attached is not part of regular inheritance CREATE TABLE parent (LIKE list_parted); CREATE TABLE child () INHERITS (parent); ALTER TABLE list_parted ATTACH PARTITION child FOR VALUES IN (1); ALTER TABLE list_parted ATTACH PARTITION parent FOR VALUES IN (1); DROP TABLE parent CASCADE; -- check any TEMP-ness CREATE TEMP TABLE temp_parted (a int) PARTITION BY LIST (a); CREATE TABLE perm_part (a int); ALTER TABLE temp_parted ATTACH PARTITION perm_part FOR VALUES IN (1); DROP TABLE temp_parted, perm_part; -- check that the table being attached is not a typed table CREATE TYPE mytype AS (a int); CREATE TABLE fail_part OF mytype; ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); DROP TYPE mytype CASCADE; -- check that the table being attached has only columns present in the parent CREATE TABLE fail_part (like list_parted, c int); ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); DROP TABLE fail_part; -- check that the table being attached has every column of the parent CREATE TABLE fail_part (a int NOT NULL); ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); DROP TABLE fail_part; -- check that columns match in type, collation and NOT NULL status CREATE TABLE fail_part ( b char(3), a int NOT NULL ); ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); ALTER TABLE fail_part ALTER b TYPE char (2) COLLATE "POSIX"; ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); DROP TABLE fail_part; -- check that the table being attached has all constraints of the parent CREATE TABLE fail_part ( b char(2) COLLATE "C", a int NOT NULL ); ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); -- check that the constraint matches in definition with parent's constraint ALTER TABLE fail_part ADD CONSTRAINT check_a CHECK (a >= 0); ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); DROP TABLE fail_part; -- check the attributes and constraints after partition is attached CREATE TABLE part_1 ( a int NOT NULL, b char(2) COLLATE "C", CONSTRAINT check_a CHECK (a > 0) ); ALTER TABLE list_parted ATTACH PARTITION part_1 FOR VALUES IN (1); -- attislocal and conislocal are always false for merged attributes and constraints respectively. SELECT attislocal, attinhcount FROM pg_attribute WHERE attrelid = 'part_1'::regclass AND attnum > 0; SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_1'::regclass AND conname = 'check_a'; -- check that the new partition won't overlap with an existing partition CREATE TABLE fail_part (LIKE part_1 INCLUDING CONSTRAINTS); ALTER TABLE list_parted ATTACH PARTITION fail_part FOR VALUES IN (1); DROP TABLE fail_part; -- check that an existing table can be attached as a default partition CREATE TABLE def_part (LIKE list_parted INCLUDING CONSTRAINTS); ALTER TABLE list_parted ATTACH PARTITION def_part DEFAULT; -- check attaching default partition fails if a default partition already -- exists CREATE TABLE fail_def_part (LIKE part_1 INCLUDING CONSTRAINTS); ALTER TABLE list_parted ATTACH PARTITION fail_def_part DEFAULT; -- check validation when attaching list partitions CREATE TABLE list_parted2 ( a int, b char ) PARTITION BY LIST (a); -- check that violating rows are correctly reported CREATE TABLE part_2 (LIKE list_parted2); INSERT INTO part_2 VALUES (3, 'a'); ALTER TABLE list_parted2 ATTACH PARTITION part_2 FOR VALUES IN (2); -- should be ok after deleting the bad row DELETE FROM part_2; ALTER TABLE list_parted2 ATTACH PARTITION part_2 FOR VALUES IN (2); -- check partition cannot be attached if default has some row for its values CREATE TABLE list_parted2_def PARTITION OF list_parted2 DEFAULT; INSERT INTO list_parted2_def VALUES (11, 'z'); CREATE TABLE part_3 (LIKE list_parted2); ALTER TABLE list_parted2 ATTACH PARTITION part_3 FOR VALUES IN (11); -- should be ok after deleting the bad row DELETE FROM list_parted2_def WHERE a = 11; ALTER TABLE list_parted2 ATTACH PARTITION part_3 FOR VALUES IN (11); -- adding constraints that describe the desired partition constraint -- (or more restrictive) will help skip the validation scan CREATE TABLE part_3_4 ( LIKE list_parted2, CONSTRAINT check_a CHECK (a IN (3)) ); -- however, if a list partition does not accept nulls, there should be -- an explicit NOT NULL constraint on the partition key column for the -- validation scan to be skipped; ALTER TABLE list_parted2 ATTACH PARTITION part_3_4 FOR VALUES IN (3, 4); -- adding a NOT NULL constraint will cause the scan to be skipped ALTER TABLE list_parted2 DETACH PARTITION part_3_4; ALTER TABLE part_3_4 ALTER a SET NOT NULL; ALTER TABLE list_parted2 ATTACH PARTITION part_3_4 FOR VALUES IN (3, 4); -- check if default partition scan skipped ALTER TABLE list_parted2_def ADD CONSTRAINT check_a CHECK (a IN (5, 6)); CREATE TABLE part_55_66 PARTITION OF list_parted2 FOR VALUES IN (55, 66); -- check validation when attaching range partitions CREATE TABLE range_parted ( a int, b int ) PARTITION BY RANGE (a, b); -- check that violating rows are correctly reported CREATE TABLE part1 ( a int NOT NULL CHECK (a = 1), b int NOT NULL CHECK (b >= 1 AND b <= 10) ); INSERT INTO part1 VALUES (1, 10); -- Remember the TO bound is exclusive ALTER TABLE range_parted ATTACH PARTITION part1 FOR VALUES FROM (1, 1) TO (1, 10); -- should be ok after deleting the bad row DELETE FROM part1; ALTER TABLE range_parted ATTACH PARTITION part1 FOR VALUES FROM (1, 1) TO (1, 10); -- adding constraints that describe the desired partition constraint -- (or more restrictive) will help skip the validation scan CREATE TABLE part2 ( a int NOT NULL CHECK (a = 1), b int NOT NULL CHECK (b >= 10 AND b < 18) ); ALTER TABLE range_parted ATTACH PARTITION part2 FOR VALUES FROM (1, 10) TO (1, 20); -- Create default partition CREATE TABLE partr_def1 PARTITION OF range_parted DEFAULT; -- Only one default partition is allowed, hence, following should give error CREATE TABLE partr_def2 (LIKE part1 INCLUDING CONSTRAINTS); ALTER TABLE range_parted ATTACH PARTITION partr_def2 DEFAULT; -- Overlapping partitions cannot be attached, hence, following should give error INSERT INTO partr_def1 VALUES (2, 10); CREATE TABLE part3 (LIKE range_parted); ALTER TABLE range_parted ATTACH partition part3 FOR VALUES FROM (2, 10) TO (2, 20); -- Attaching partitions should be successful when there are no overlapping rows ALTER TABLE range_parted ATTACH partition part3 FOR VALUES FROM (3, 10) TO (3, 20); -- check that leaf partitions are scanned when attaching a partitioned -- table CREATE TABLE part_5 ( LIKE list_parted2 ) PARTITION BY LIST (b); -- check that violating rows are correctly reported CREATE TABLE part_5_a PARTITION OF part_5 FOR VALUES IN ('a'); INSERT INTO part_5_a (a, b) VALUES (6, 'a'); ALTER TABLE list_parted2 ATTACH PARTITION part_5 FOR VALUES IN (5); -- delete the faulting row and also add a constraint to skip the scan DELETE FROM part_5_a WHERE a NOT IN (3); ALTER TABLE part_5 ADD CONSTRAINT check_a CHECK (a IS NOT NULL AND a = 5); ALTER TABLE list_parted2 ATTACH PARTITION part_5 FOR VALUES IN (5); ALTER TABLE list_parted2 DETACH PARTITION part_5; ALTER TABLE part_5 DROP CONSTRAINT check_a; -- scan should again be skipped, even though NOT NULL is now a column property ALTER TABLE part_5 ADD CONSTRAINT check_a CHECK (a IN (5)), ALTER a SET NOT NULL; ALTER TABLE list_parted2 ATTACH PARTITION part_5 FOR VALUES IN (5); -- Check the case where attnos of the partitioning columns in the table being -- attached differs from the parent. It should not affect the constraint- -- checking logic that allows to skip the scan. CREATE TABLE part_6 ( c int, LIKE list_parted2, CONSTRAINT check_a CHECK (a IS NOT NULL AND a = 6) ); ALTER TABLE part_6 DROP c; ALTER TABLE list_parted2 ATTACH PARTITION part_6 FOR VALUES IN (6); -- Similar to above, but the table being attached is a partitioned table -- whose partition has still different attnos for the root partitioning -- columns. CREATE TABLE part_7 ( LIKE list_parted2, CONSTRAINT check_a CHECK (a IS NOT NULL AND a = 7) ) PARTITION BY LIST (b); CREATE TABLE part_7_a_null ( c int, d int, e int, LIKE list_parted2, -- 'a' will have attnum = 4 CONSTRAINT check_b CHECK (b IS NULL OR b = 'a'), CONSTRAINT check_a CHECK (a IS NOT NULL AND a = 7) ); ALTER TABLE part_7_a_null DROP c, DROP d, DROP e; ALTER TABLE part_7 ATTACH PARTITION part_7_a_null FOR VALUES IN ('a', null); ALTER TABLE list_parted2 ATTACH PARTITION part_7 FOR VALUES IN (7); -- Same example, but check this time that the constraint correctly detects -- violating rows ALTER TABLE list_parted2 DETACH PARTITION part_7; ALTER TABLE part_7 DROP CONSTRAINT check_a; -- thusly, scan won't be skipped INSERT INTO part_7 (a, b) VALUES (8, null), (9, 'a'); SELECT tableoid::regclass, a, b FROM part_7 order by a; ALTER TABLE list_parted2 ATTACH PARTITION part_7 FOR VALUES IN (7); -- check that leaf partitions of default partition are scanned when -- attaching a partitioned table. ALTER TABLE part_5 DROP CONSTRAINT check_a; CREATE TABLE part5_def PARTITION OF part_5 DEFAULT PARTITION BY LIST(a); CREATE TABLE part5_def_p1 PARTITION OF part5_def FOR VALUES IN (5); INSERT INTO part5_def_p1 VALUES (5, 'y'); CREATE TABLE part5_p1 (LIKE part_5); ALTER TABLE part_5 ATTACH PARTITION part5_p1 FOR VALUES IN ('y'); -- should be ok after deleting the bad row DELETE FROM part5_def_p1 WHERE b = 'y'; ALTER TABLE part_5 ATTACH PARTITION part5_p1 FOR VALUES IN ('y'); -- check that the table being attached is not already a partition ALTER TABLE list_parted2 ATTACH PARTITION part_2 FOR VALUES IN (2); -- check that circular inheritance is not allowed ALTER TABLE part_5 ATTACH PARTITION list_parted2 FOR VALUES IN ('b'); ALTER TABLE list_parted2 ATTACH PARTITION list_parted2 FOR VALUES IN (0); -- If a partitioned table being created or an existing table being attached -- as a partition does not have a constraint that would allow validation scan -- to be skipped, but an individual partition does, then the partition's -- validation scan is skipped. CREATE TABLE quuux (a int, b text) PARTITION BY LIST (a); CREATE TABLE quuux_default PARTITION OF quuux DEFAULT PARTITION BY LIST (b); CREATE TABLE quuux_default1 PARTITION OF quuux_default ( CONSTRAINT check_1 CHECK (a IS NOT NULL AND a = 1) ) FOR VALUES IN ('b'); CREATE TABLE quuux1 (a int, b text); ALTER TABLE quuux ATTACH PARTITION quuux1 FOR VALUES IN (1); -- validate! CREATE TABLE quuux2 (a int, b text); ALTER TABLE quuux ATTACH PARTITION quuux2 FOR VALUES IN (2); -- skip validation DROP TABLE quuux1, quuux2; -- should validate for quuux1, but not for quuux2 CREATE TABLE quuux1 PARTITION OF quuux FOR VALUES IN (1); CREATE TABLE quuux2 PARTITION OF quuux FOR VALUES IN (2); DROP TABLE quuux; -- check validation when attaching hash partitions -- Use hand-rolled hash functions and operator class to get predictable result -- on different matchines. part_test_int4_ops is defined in insert.sql. -- check that the new partition won't overlap with an existing partition CREATE TABLE hash_parted ( a int, b int ) PARTITION BY HASH (a part_test_int4_ops); CREATE TABLE hpart_1 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 4, REMAINDER 0); CREATE TABLE fail_part (LIKE hpart_1); ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 8, REMAINDER 4); ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 8, REMAINDER 0); DROP TABLE fail_part; -- check validation when attaching hash partitions -- check that violating rows are correctly reported CREATE TABLE hpart_2 (LIKE hash_parted); INSERT INTO hpart_2 VALUES (3, 0); ALTER TABLE hash_parted ATTACH PARTITION hpart_2 FOR VALUES WITH (MODULUS 4, REMAINDER 1); -- should be ok after deleting the bad row DELETE FROM hpart_2; ALTER TABLE hash_parted ATTACH PARTITION hpart_2 FOR VALUES WITH (MODULUS 4, REMAINDER 1); -- check that leaf partitions are scanned when attaching a partitioned -- table CREATE TABLE hpart_5 ( LIKE hash_parted ) PARTITION BY LIST (b); -- check that violating rows are correctly reported CREATE TABLE hpart_5_a PARTITION OF hpart_5 FOR VALUES IN ('1', '2', '3'); INSERT INTO hpart_5_a (a, b) VALUES (7, 1); ALTER TABLE hash_parted ATTACH PARTITION hpart_5 FOR VALUES WITH (MODULUS 4, REMAINDER 2); -- should be ok after deleting the bad row DELETE FROM hpart_5_a; ALTER TABLE hash_parted ATTACH PARTITION hpart_5 FOR VALUES WITH (MODULUS 4, REMAINDER 2); -- check that the table being attach is with valid modulus and remainder value CREATE TABLE fail_part(LIKE hash_parted); ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 0, REMAINDER 1); ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 8, REMAINDER 8); ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 3, REMAINDER 2); DROP TABLE fail_part; -- -- DETACH PARTITION -- -- check that the table is partitioned at all CREATE TABLE regular_table (a int); ALTER TABLE regular_table DETACH PARTITION any_name; DROP TABLE regular_table; -- check that the partition being detached exists at all ALTER TABLE list_parted2 DETACH PARTITION part_4; ALTER TABLE hash_parted DETACH PARTITION hpart_4; -- check that the partition being detached is actually a partition of the parent CREATE TABLE not_a_part (a int); ALTER TABLE list_parted2 DETACH PARTITION not_a_part; ALTER TABLE list_parted2 DETACH PARTITION part_1; ALTER TABLE hash_parted DETACH PARTITION not_a_part; DROP TABLE not_a_part; -- check that, after being detached, attinhcount/coninhcount is dropped to 0 and -- attislocal/conislocal is set to true ALTER TABLE list_parted2 DETACH PARTITION part_3_4; SELECT attinhcount, attislocal FROM pg_attribute WHERE attrelid = 'part_3_4'::regclass AND attnum > 0; SELECT coninhcount, conislocal FROM pg_constraint WHERE conrelid = 'part_3_4'::regclass AND conname = 'check_a'; DROP TABLE part_3_4; -- check that a detached partition is not dropped on dropping a partitioned table CREATE TABLE range_parted2 ( a int ) PARTITION BY RANGE(a); CREATE TABLE part_rp PARTITION OF range_parted2 FOR VALUES FROM (0) to (100); ALTER TABLE range_parted2 DETACH PARTITION part_rp; DROP TABLE range_parted2; SELECT * from part_rp; DROP TABLE part_rp; -- Check ALTER TABLE commands for partitioned tables and partitions -- cannot add/drop column to/from *only* the parent ALTER TABLE ONLY list_parted2 ADD COLUMN c int; ALTER TABLE ONLY list_parted2 DROP COLUMN b; -- cannot add a column to partition or drop an inherited one ALTER TABLE part_2 ADD COLUMN c text; ALTER TABLE part_2 DROP COLUMN b; -- Nor rename, alter type ALTER TABLE part_2 RENAME COLUMN b to c; ALTER TABLE part_2 ALTER COLUMN b TYPE text; -- cannot add/drop NOT NULL or check constraints to *only* the parent, when -- partitions exist ALTER TABLE ONLY list_parted2 ALTER b SET NOT NULL; ALTER TABLE ONLY list_parted2 ADD CONSTRAINT check_b CHECK (b <> 'zz'); ALTER TABLE list_parted2 ALTER b SET NOT NULL; ALTER TABLE ONLY list_parted2 ALTER b DROP NOT NULL; ALTER TABLE list_parted2 ADD CONSTRAINT check_b CHECK (b <> 'zz'); ALTER TABLE ONLY list_parted2 DROP CONSTRAINT check_b; -- It's alright though, if no partitions are yet created CREATE TABLE parted_no_parts (a int) PARTITION BY LIST (a); ALTER TABLE ONLY parted_no_parts ALTER a SET NOT NULL; ALTER TABLE ONLY parted_no_parts ADD CONSTRAINT check_a CHECK (a > 0); ALTER TABLE ONLY parted_no_parts ALTER a DROP NOT NULL; ALTER TABLE ONLY parted_no_parts DROP CONSTRAINT check_a; DROP TABLE parted_no_parts; -- cannot drop inherited NOT NULL or check constraints from partition ALTER TABLE list_parted2 ALTER b SET NOT NULL, ADD CONSTRAINT check_a2 CHECK (a > 0); ALTER TABLE part_2 ALTER b DROP NOT NULL; ALTER TABLE part_2 DROP CONSTRAINT check_a2; -- Doesn't make sense to add NO INHERIT constraints on partitioned tables ALTER TABLE list_parted2 add constraint check_b2 check (b <> 'zz') NO INHERIT; -- check that a partition cannot participate in regular inheritance CREATE TABLE inh_test () INHERITS (part_2); CREATE TABLE inh_test (LIKE part_2); ALTER TABLE inh_test INHERIT part_2; ALTER TABLE part_2 INHERIT inh_test; -- cannot drop or alter type of partition key columns of lower level -- partitioned tables; for example, part_5, which is list_parted2's -- partition, is partitioned on b; ALTER TABLE list_parted2 DROP COLUMN b; ALTER TABLE list_parted2 ALTER COLUMN b TYPE text; -- dropping non-partition key columns should be allowed on the parent table. ALTER TABLE list_parted DROP COLUMN b; SELECT * FROM list_parted; -- cleanup DROP TABLE list_parted, list_parted2, range_parted; DROP TABLE fail_def_part; DROP TABLE hash_parted; -- more tests for certain multi-level partitioning scenarios create table p (a int, b int) partition by range (a, b); create table p1 (b int, a int not null) partition by range (b); create table p11 (like p1); alter table p11 drop a; alter table p11 add a int; alter table p11 drop a; alter table p11 add a int not null; -- attnum for key attribute 'a' is different in p, p1, and p11 select attrelid::regclass, attname, attnum from pg_attribute where attname = 'a' and (attrelid = 'p'::regclass or attrelid = 'p1'::regclass or attrelid = 'p11'::regclass) order by attrelid::regclass::text; alter table p1 attach partition p11 for values from (2) to (5); insert into p1 (a, b) values (2, 3); -- check that partition validation scan correctly detects violating rows alter table p attach partition p1 for values from (1, 2) to (1, 10); -- cleanup drop table p; drop table p1; -- validate constraint on partitioned tables should only scan leaf partitions create table parted_validate_test (a int) partition by list (a); create table parted_validate_test_1 partition of parted_validate_test for values in (0, 1); alter table parted_validate_test add constraint parted_validate_test_chka check (a > 0) not valid; alter table parted_validate_test validate constraint parted_validate_test_chka; drop table parted_validate_test; -- test alter column options CREATE TABLE attmp(i integer); INSERT INTO attmp VALUES (1); ALTER TABLE attmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2); ALTER TABLE attmp ALTER COLUMN i RESET (n_distinct_inherited); ANALYZE attmp; DROP TABLE attmp; DROP USER regress_alter_table_user1; -- check that violating rows are correctly reported when attaching as the -- default partition create table defpart_attach_test (a int) partition by list (a); create table defpart_attach_test1 partition of defpart_attach_test for values in (1); create table defpart_attach_test_d (like defpart_attach_test); insert into defpart_attach_test_d values (1), (2); -- error because its constraint as the default partition would be violated -- by the row containing 1 alter table defpart_attach_test attach partition defpart_attach_test_d default; delete from defpart_attach_test_d where a = 1; alter table defpart_attach_test_d add check (a > 1); -- should be attached successfully and without needing to be scanned alter table defpart_attach_test attach partition defpart_attach_test_d default; drop table defpart_attach_test; -- check combinations of temporary and permanent relations when attaching -- partitions. create table perm_part_parent (a int) partition by list (a); create temp table temp_part_parent (a int) partition by list (a); create table perm_part_child (a int); create temp table temp_part_child (a int); alter table temp_part_parent attach partition perm_part_child default; -- error alter table perm_part_parent attach partition temp_part_child default; -- error alter table temp_part_parent attach partition temp_part_child default; -- ok drop table perm_part_parent cascade; drop table temp_part_parent cascade; -- check that attaching partitions to a table while it is being used is -- prevented create table tab_part_attach (a int) partition by list (a); create or replace function func_part_attach() returns trigger language plpgsql as $$ begin execute 'create table tab_part_attach_1 (a int)'; execute 'alter table tab_part_attach attach partition tab_part_attach_1 for values in (1)'; return null; end $$; create trigger trig_part_attach before insert on tab_part_attach for each statement execute procedure func_part_attach(); insert into tab_part_attach values (1); drop table tab_part_attach; drop function func_part_attach(); -- test case where the partitioning operator is a SQL function whose -- evaluation results in the table's relcache being rebuilt partway through -- the execution of an ATTACH PARTITION command create function at_test_sql_partop (int4, int4) returns int language sql as $$ select case when $1 = $2 then 0 when $1 > $2 then 1 else -1 end; $$; create operator class at_test_sql_partop for type int4 using btree as operator 1 < (int4, int4), operator 2 <= (int4, int4), operator 3 = (int4, int4), operator 4 >= (int4, int4), operator 5 > (int4, int4), function 1 at_test_sql_partop(int4, int4); create table at_test_sql_partop (a int) partition by range (a at_test_sql_partop); create table at_test_sql_partop_1 (a int); alter table at_test_sql_partop attach partition at_test_sql_partop_1 for values from (0) to (10); drop table at_test_sql_partop; drop operator class at_test_sql_partop using btree; drop function at_test_sql_partop; pgFormatter-4.2/t/pg-test-files/sql/amutils.sql000066400000000000000000000102451361326045100216000ustar00rootroot00000000000000-- -- Test index AM property-reporting functions -- select prop, pg_indexam_has_property(a.oid, prop) as "AM", pg_index_has_property('onek_hundred'::regclass, prop) as "Index", pg_index_column_has_property('onek_hundred'::regclass, 1, prop) as "Column" from pg_am a, unnest(array['asc', 'desc', 'nulls_first', 'nulls_last', 'orderable', 'distance_orderable', 'returnable', 'search_array', 'search_nulls', 'clusterable', 'index_scan', 'bitmap_scan', 'backward_scan', 'can_order', 'can_unique', 'can_multi_col', 'can_exclude', 'can_include', 'bogus']::text[]) with ordinality as u(prop,ord) where a.amname = 'btree' order by ord; select prop, pg_indexam_has_property(a.oid, prop) as "AM", pg_index_has_property('gcircleind'::regclass, prop) as "Index", pg_index_column_has_property('gcircleind'::regclass, 1, prop) as "Column" from pg_am a, unnest(array['asc', 'desc', 'nulls_first', 'nulls_last', 'orderable', 'distance_orderable', 'returnable', 'search_array', 'search_nulls', 'clusterable', 'index_scan', 'bitmap_scan', 'backward_scan', 'can_order', 'can_unique', 'can_multi_col', 'can_exclude', 'can_include', 'bogus']::text[]) with ordinality as u(prop,ord) where a.amname = 'gist' order by ord; select prop, pg_index_column_has_property('onek_hundred'::regclass, 1, prop) as btree, pg_index_column_has_property('hash_i4_index'::regclass, 1, prop) as hash, pg_index_column_has_property('gcircleind'::regclass, 1, prop) as gist, pg_index_column_has_property('sp_radix_ind'::regclass, 1, prop) as spgist_radix, pg_index_column_has_property('sp_quad_ind'::regclass, 1, prop) as spgist_quad, pg_index_column_has_property('botharrayidx'::regclass, 1, prop) as gin, pg_index_column_has_property('brinidx'::regclass, 1, prop) as brin from unnest(array['asc', 'desc', 'nulls_first', 'nulls_last', 'orderable', 'distance_orderable', 'returnable', 'search_array', 'search_nulls', 'bogus']::text[]) with ordinality as u(prop,ord) order by ord; select prop, pg_index_has_property('onek_hundred'::regclass, prop) as btree, pg_index_has_property('hash_i4_index'::regclass, prop) as hash, pg_index_has_property('gcircleind'::regclass, prop) as gist, pg_index_has_property('sp_radix_ind'::regclass, prop) as spgist, pg_index_has_property('botharrayidx'::regclass, prop) as gin, pg_index_has_property('brinidx'::regclass, prop) as brin from unnest(array['clusterable', 'index_scan', 'bitmap_scan', 'backward_scan', 'bogus']::text[]) with ordinality as u(prop,ord) order by ord; select amname, prop, pg_indexam_has_property(a.oid, prop) as p from pg_am a, unnest(array['can_order', 'can_unique', 'can_multi_col', 'can_exclude', 'can_include', 'bogus']::text[]) with ordinality as u(prop,ord) where amtype = 'i' order by amname, ord; -- -- additional checks for pg_index_column_has_property -- CREATE TEMP TABLE foo (f1 int, f2 int, f3 int, f4 int); CREATE INDEX fooindex ON foo (f1 desc, f2 asc, f3 nulls first, f4 nulls last); select col, prop, pg_index_column_has_property(o, col, prop) from (values ('fooindex'::regclass)) v1(o), (values (1,'orderable'),(2,'asc'),(3,'desc'), (4,'nulls_first'),(5,'nulls_last'), (6, 'bogus')) v2(idx,prop), generate_series(1,4) col order by col, idx; CREATE INDEX foocover ON foo (f1) INCLUDE (f2,f3); select col, prop, pg_index_column_has_property(o, col, prop) from (values ('foocover'::regclass)) v1(o), (values (1,'orderable'),(2,'asc'),(3,'desc'), (4,'nulls_first'),(5,'nulls_last'), (6,'distance_orderable'),(7,'returnable'), (8, 'bogus')) v2(idx,prop), generate_series(1,3) col order by col, idx; pgFormatter-4.2/t/pg-test-files/sql/arrays.sql000066400000000000000000000605711361326045100214320ustar00rootroot00000000000000-- -- ARRAYS -- CREATE TABLE arrtest ( a int2[], b int4[][][], c name[], d text[][], e float8[], f char(5)[], g varchar(5)[] ); -- -- only the 'e' array is 0-based, the others are 1-based. -- INSERT INTO arrtest (a[1:5], b[1:1][1:2][1:2], c, d, f, g) VALUES ('{1,2,3,4,5}', '{{{0,0},{1,2}}}', '{}', '{}', '{}', '{}'); UPDATE arrtest SET e[0] = '1.1'; UPDATE arrtest SET e[1] = '2.2'; INSERT INTO arrtest (f) VALUES ('{"too long"}'); INSERT INTO arrtest (a, b[1:2][1:2], c, d, e, f, g) VALUES ('{11,12,23}', '{{3,4},{4,5}}', '{"foobar"}', '{{"elt1", "elt2"}}', '{"3.4", "6.7"}', '{"abc","abcde"}', '{"abc","abcde"}'); INSERT INTO arrtest (a, b[1:2], c, d[1:2]) VALUES ('{}', '{3,4}', '{foo,bar}', '{bar,foo}'); SELECT * FROM arrtest; SELECT arrtest.a[1], arrtest.b[1][1][1], arrtest.c[1], arrtest.d[1][1], arrtest.e[0] FROM arrtest; SELECT a[1], b[1][1][1], c[1], d[1][1], e[0] FROM arrtest; SELECT a[1:3], b[1:1][1:2][1:2], c[1:2], d[1:1][1:2] FROM arrtest; SELECT array_ndims(a) AS a,array_ndims(b) AS b,array_ndims(c) AS c FROM arrtest; SELECT array_dims(a) AS a,array_dims(b) AS b,array_dims(c) AS c FROM arrtest; -- returns nothing SELECT * FROM arrtest WHERE a[1] < 5 and c = '{"foobar"}'::_name; UPDATE arrtest SET a[1:2] = '{16,25}' WHERE NOT a = '{}'::_int2; UPDATE arrtest SET b[1:1][1:1][1:2] = '{113, 117}', b[1:1][1:2][2:2] = '{142, 147}' WHERE array_dims(b) = '[1:1][1:2][1:2]'; UPDATE arrtest SET c[2:2] = '{"new_word"}' WHERE array_dims(c) is not null; SELECT a,b,c FROM arrtest; SELECT a[1:3], b[1:1][1:2][1:2], c[1:2], d[1:1][2:2] FROM arrtest; SELECT b[1:1][2][2], d[1:1][2] FROM arrtest; INSERT INTO arrtest(a) VALUES('{1,null,3}'); SELECT a FROM arrtest; UPDATE arrtest SET a[4] = NULL WHERE a[2] IS NULL; SELECT a FROM arrtest WHERE a[2] IS NULL; DELETE FROM arrtest WHERE a[2] IS NULL AND b IS NULL; SELECT a,b,c FROM arrtest; -- test mixed slice/scalar subscripting select '{{1,2,3},{4,5,6},{7,8,9}}'::int[]; select ('{{1,2,3},{4,5,6},{7,8,9}}'::int[])[1:2][2]; select '[0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}}'::int[]; select ('[0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}}'::int[])[1:2][2]; -- -- check subscription corner cases -- -- More subscripts than MAXDIMS(6) SELECT ('{}'::int[])[1][2][3][4][5][6][7]; -- NULL index yields NULL when selecting SELECT ('{{{1},{2},{3}},{{4},{5},{6}}}'::int[])[1][NULL][1]; SELECT ('{{{1},{2},{3}},{{4},{5},{6}}}'::int[])[1][NULL:1][1]; SELECT ('{{{1},{2},{3}},{{4},{5},{6}}}'::int[])[1][1:NULL][1]; -- NULL index in assignment is an error UPDATE arrtest SET c[NULL] = '{"can''t assign"}' WHERE array_dims(c) is not null; UPDATE arrtest SET c[NULL:1] = '{"can''t assign"}' WHERE array_dims(c) is not null; UPDATE arrtest SET c[1:NULL] = '{"can''t assign"}' WHERE array_dims(c) is not null; -- test slices with empty lower and/or upper index CREATE TEMP TABLE arrtest_s ( a int2[], b int2[][] ); INSERT INTO arrtest_s VALUES ('{1,2,3,4,5}', '{{1,2,3}, {4,5,6}, {7,8,9}}'); INSERT INTO arrtest_s VALUES ('[0:4]={1,2,3,4,5}', '[0:2][0:2]={{1,2,3}, {4,5,6}, {7,8,9}}'); SELECT * FROM arrtest_s; SELECT a[:3], b[:2][:2] FROM arrtest_s; SELECT a[2:], b[2:][2:] FROM arrtest_s; SELECT a[:], b[:] FROM arrtest_s; -- updates UPDATE arrtest_s SET a[:3] = '{11, 12, 13}', b[:2][:2] = '{{11,12}, {14,15}}' WHERE array_lower(a,1) = 1; SELECT * FROM arrtest_s; UPDATE arrtest_s SET a[3:] = '{23, 24, 25}', b[2:][2:] = '{{25,26}, {28,29}}'; SELECT * FROM arrtest_s; UPDATE arrtest_s SET a[:] = '{11, 12, 13, 14, 15}'; SELECT * FROM arrtest_s; UPDATE arrtest_s SET a[:] = '{23, 24, 25}'; -- fail, too small INSERT INTO arrtest_s VALUES(NULL, NULL); UPDATE arrtest_s SET a[:] = '{11, 12, 13, 14, 15}'; -- fail, no good with null -- check with fixed-length-array type, such as point SELECT f1[0:1] FROM POINT_TBL; SELECT f1[0:] FROM POINT_TBL; SELECT f1[:1] FROM POINT_TBL; SELECT f1[:] FROM POINT_TBL; -- subscript assignments to fixed-width result in NULL if previous value is NULL UPDATE point_tbl SET f1[0] = 10 WHERE f1 IS NULL RETURNING *; INSERT INTO point_tbl(f1[0]) VALUES(0) RETURNING *; -- NULL assignments get ignored UPDATE point_tbl SET f1[0] = NULL WHERE f1::text = '(10,10)'::point::text RETURNING *; -- but non-NULL subscript assignments work UPDATE point_tbl SET f1[0] = -10, f1[1] = -10 WHERE f1::text = '(10,10)'::point::text RETURNING *; -- but not to expand the range UPDATE point_tbl SET f1[3] = 10 WHERE f1::text = '(-10,-10)'::point::text RETURNING *; -- -- test array extension -- CREATE TEMP TABLE arrtest1 (i int[], t text[]); insert into arrtest1 values(array[1,2,null,4], array['one','two',null,'four']); select * from arrtest1; update arrtest1 set i[2] = 22, t[2] = 'twenty-two'; select * from arrtest1; update arrtest1 set i[5] = 5, t[5] = 'five'; select * from arrtest1; update arrtest1 set i[8] = 8, t[8] = 'eight'; select * from arrtest1; update arrtest1 set i[0] = 0, t[0] = 'zero'; select * from arrtest1; update arrtest1 set i[-3] = -3, t[-3] = 'minus-three'; select * from arrtest1; update arrtest1 set i[0:2] = array[10,11,12], t[0:2] = array['ten','eleven','twelve']; select * from arrtest1; update arrtest1 set i[8:10] = array[18,null,20], t[8:10] = array['p18',null,'p20']; select * from arrtest1; update arrtest1 set i[11:12] = array[null,22], t[11:12] = array[null,'p22']; select * from arrtest1; update arrtest1 set i[15:16] = array[null,26], t[15:16] = array[null,'p26']; select * from arrtest1; update arrtest1 set i[-5:-3] = array[-15,-14,-13], t[-5:-3] = array['m15','m14','m13']; select * from arrtest1; update arrtest1 set i[-7:-6] = array[-17,null], t[-7:-6] = array['m17',null]; select * from arrtest1; update arrtest1 set i[-12:-10] = array[-22,null,-20], t[-12:-10] = array['m22',null,'m20']; select * from arrtest1; delete from arrtest1; insert into arrtest1 values(array[1,2,null,4], array['one','two',null,'four']); select * from arrtest1; update arrtest1 set i[0:5] = array[0,1,2,null,4,5], t[0:5] = array['z','p1','p2',null,'p4','p5']; select * from arrtest1; -- -- array expressions and operators -- -- table creation and INSERTs CREATE TEMP TABLE arrtest2 (i integer ARRAY[4], f float8[], n numeric[], t text[], d timestamp[]); INSERT INTO arrtest2 VALUES( ARRAY[[[113,142],[1,147]]], ARRAY[1.1,1.2,1.3]::float8[], ARRAY[1.1,1.2,1.3], ARRAY[[['aaa','aab'],['aba','abb'],['aca','acb']],[['baa','bab'],['bba','bbb'],['bca','bcb']]], ARRAY['19620326','19931223','19970117']::timestamp[] ); -- some more test data CREATE TEMP TABLE arrtest_f (f0 int, f1 text, f2 float8); insert into arrtest_f values(1,'cat1',1.21); insert into arrtest_f values(2,'cat1',1.24); insert into arrtest_f values(3,'cat1',1.18); insert into arrtest_f values(4,'cat1',1.26); insert into arrtest_f values(5,'cat1',1.15); insert into arrtest_f values(6,'cat2',1.15); insert into arrtest_f values(7,'cat2',1.26); insert into arrtest_f values(8,'cat2',1.32); insert into arrtest_f values(9,'cat2',1.30); CREATE TEMP TABLE arrtest_i (f0 int, f1 text, f2 int); insert into arrtest_i values(1,'cat1',21); insert into arrtest_i values(2,'cat1',24); insert into arrtest_i values(3,'cat1',18); insert into arrtest_i values(4,'cat1',26); insert into arrtest_i values(5,'cat1',15); insert into arrtest_i values(6,'cat2',15); insert into arrtest_i values(7,'cat2',26); insert into arrtest_i values(8,'cat2',32); insert into arrtest_i values(9,'cat2',30); -- expressions SELECT t.f[1][3][1] AS "131", t.f[2][2][1] AS "221" FROM ( SELECT ARRAY[[[111,112],[121,122],[131,132]],[[211,212],[221,122],[231,232]]] AS f ) AS t; SELECT ARRAY[[[[[['hello'],['world']]]]]]; SELECT ARRAY[ARRAY['hello'],ARRAY['world']]; SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY"; -- with nulls SELECT '{1,null,3}'::int[]; SELECT ARRAY[1,NULL,3]; -- functions SELECT array_append(array[42], 6) AS "{42,6}"; SELECT array_prepend(6, array[42]) AS "{6,42}"; SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{1,2,3,4}"; SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}"; SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}"; SELECT array_position(ARRAY[1,2,3,4,5], 4); SELECT array_position(ARRAY[5,3,4,2,1], 4); SELECT array_position(ARRAY[[1,2],[3,4]], 3); SELECT array_position(ARRAY['sun','mon','tue','wed','thu','fri','sat'], 'mon'); SELECT array_position(ARRAY['sun','mon','tue','wed','thu','fri','sat'], 'sat'); SELECT array_position(ARRAY['sun','mon','tue','wed','thu','fri','sat'], NULL); SELECT array_position(ARRAY['sun','mon','tue','wed','thu',NULL,'fri','sat'], NULL); SELECT array_position(ARRAY['sun','mon','tue','wed','thu',NULL,'fri','sat'], 'sat'); SELECT array_positions(NULL, 10); SELECT array_positions(NULL, NULL::int); SELECT array_positions(ARRAY[1,2,3,4,5,6,1,2,3,4,5,6], 4); SELECT array_positions(ARRAY[[1,2],[3,4]], 4); SELECT array_positions(ARRAY[1,2,3,4,5,6,1,2,3,4,5,6], NULL); SELECT array_positions(ARRAY[1,2,3,NULL,5,6,1,2,3,NULL,5,6], NULL); SELECT array_length(array_positions(ARRAY(SELECT 'AAAAAAAAAAAAAAAAAAAAAAAAA'::text || i % 10 FROM generate_series(1,100) g(i)), 'AAAAAAAAAAAAAAAAAAAAAAAAA5'), 1); DO $$ DECLARE o int; a int[] := ARRAY[1,2,3,2,3,1,2]; BEGIN o := array_position(a, 2); WHILE o IS NOT NULL LOOP RAISE NOTICE '%', o; o := array_position(a, 2, o + 1); END LOOP; END $$ LANGUAGE plpgsql; SELECT array_position('[2:4]={1,2,3}'::int[], 1); SELECT array_positions('[2:4]={1,2,3}'::int[], 1); SELECT array_position(ids, (1, 1)), array_positions(ids, (1, 1)) FROM (VALUES (ARRAY[(0, 0), (1, 1)]), (ARRAY[(1, 1)]) ) AS f (ids); -- operators SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]]; SELECT NOT ARRAY[1.1,1.2,1.3] = ARRAY[1.1,1.2,1.3] AS "FALSE"; SELECT ARRAY[1,2] || 3 AS "{1,2,3}"; SELECT 0 || ARRAY[1,2] AS "{0,1,2}"; SELECT ARRAY[1,2] || ARRAY[3,4] AS "{1,2,3,4}"; SELECT ARRAY[[['hello','world']]] || ARRAY[[['happy','birthday']]] AS "ARRAY"; SELECT ARRAY[[1,2],[3,4]] || ARRAY[5,6] AS "{{1,2},{3,4},{5,6}}"; SELECT ARRAY[0,0] || ARRAY[1,1] || ARRAY[2,2] AS "{0,0,1,1,2,2}"; SELECT 0 || ARRAY[1,2] || 3 AS "{0,1,2,3}"; SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i @> '{32,17}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i && '{32,17}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i <@ '{38,34,32,89}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i = '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i @> '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i && '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i <@ '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t && '{AAAAAAAAAA646}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t <@ '{AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t = '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t @> '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t && '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t <@ '{}' ORDER BY seqno; -- array casts SELECT ARRAY[1,2,3]::text[]::int[]::float8[] AS "{1,2,3}"; SELECT ARRAY[1,2,3]::text[]::int[]::float8[] is of (float8[]) as "TRUE"; SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] AS "{{a,bc},{def,hijk}}"; SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] is of (varchar[]) as "TRUE"; SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}"; SELECT NULL::text[]::int[] AS "NULL"; -- scalar op any/all (array) select 33 = any ('{1,2,3}'); select 33 = any ('{1,2,33}'); select 33 = all ('{1,2,33}'); select 33 >= all ('{1,2,33}'); -- boundary cases select null::int >= all ('{1,2,33}'); select null::int >= all ('{}'); select null::int >= any ('{}'); -- cross-datatype select 33.4 = any (array[1,2,3]); select 33.4 > all (array[1,2,3]); -- errors select 33 * any ('{1,2,3}'); select 33 * any (44); -- nulls select 33 = any (null::int[]); select null::int = any ('{1,2,3}'); select 33 = any ('{1,null,3}'); select 33 = any ('{1,null,33}'); select 33 = all (null::int[]); select null::int = all ('{1,2,3}'); select 33 = all ('{1,null,3}'); select 33 = all ('{33,null,33}'); -- nulls later in the bitmap SELECT -1 != ALL(ARRAY(SELECT NULLIF(g.i, 900) FROM generate_series(1,1000) g(i))); -- test indexes on arrays create temp table arr_tbl (f1 int[] unique); insert into arr_tbl values ('{1,2,3}'); insert into arr_tbl values ('{1,2}'); -- failure expected: insert into arr_tbl values ('{1,2,3}'); insert into arr_tbl values ('{2,3,4}'); insert into arr_tbl values ('{1,5,3}'); insert into arr_tbl values ('{1,2,10}'); set enable_seqscan to off; set enable_bitmapscan to off; select * from arr_tbl where f1 > '{1,2,3}' and f1 <= '{1,5,3}'; select * from arr_tbl where f1 >= '{1,2,3}' and f1 < '{1,5,3}'; -- test ON CONFLICT DO UPDATE with arrays create temp table arr_pk_tbl (pk int4 primary key, f1 int[]); insert into arr_pk_tbl values (1, '{1,2,3}'); insert into arr_pk_tbl values (1, '{3,4,5}') on conflict (pk) do update set f1[1] = excluded.f1[1], f1[3] = excluded.f1[3] returning pk, f1; insert into arr_pk_tbl(pk, f1[1:2]) values (1, '{6,7,8}') on conflict (pk) do update set f1[1] = excluded.f1[1], f1[2] = excluded.f1[2], f1[3] = excluded.f1[3] returning pk, f1; -- note: if above selects don't produce the expected tuple order, -- then you didn't get an indexscan plan, and something is busted. reset enable_seqscan; reset enable_bitmapscan; -- test [not] (like|ilike) (any|all) (...) select 'foo' like any (array['%a', '%o']); -- t select 'foo' like any (array['%a', '%b']); -- f select 'foo' like all (array['f%', '%o']); -- t select 'foo' like all (array['f%', '%b']); -- f select 'foo' not like any (array['%a', '%b']); -- t select 'foo' not like all (array['%a', '%o']); -- f select 'foo' ilike any (array['%A', '%O']); -- t select 'foo' ilike all (array['F%', '%O']); -- t -- -- General array parser tests -- -- none of the following should be accepted select '{{1,{2}},{2,3}}'::text[]; select '{{},{}}'::text[]; select E'{{1,2},\\{2,3}}'::text[]; select '{{"1 2" x},{3}}'::text[]; select '{}}'::text[]; select '{ }}'::text[]; select array[]; -- none of the above should be accepted -- all of the following should be accepted select '{}'::text[]; select '{{{1,2,3,4},{2,3,4,5}},{{3,4,5,6},{4,5,6,7}}}'::text[]; select '{0 second ,0 second}'::interval[]; select '{ { "," } , { 3 } }'::text[]; select ' { { " 0 second " , 0 second } }'::text[]; select '{ 0 second, @ 1 hour @ 42 minutes @ 20 seconds }'::interval[]; select array[]::text[]; select '[0:1]={1.1,2.2}'::float8[]; -- all of the above should be accepted -- tests for array aggregates CREATE TEMP TABLE arraggtest ( f1 INT[], f2 TEXT[][], f3 FLOAT[]); INSERT INTO arraggtest (f1, f2, f3) VALUES ('{1,2,3,4}','{{grey,red},{blue,blue}}','{1.6, 0.0}'); INSERT INTO arraggtest (f1, f2, f3) VALUES ('{1,2,3}','{{grey,red},{grey,blue}}','{1.6}'); SELECT max(f1), min(f1), max(f2), min(f2), max(f3), min(f3) FROM arraggtest; INSERT INTO arraggtest (f1, f2, f3) VALUES ('{3,3,2,4,5,6}','{{white,yellow},{pink,orange}}','{2.1,3.3,1.8,1.7,1.6}'); SELECT max(f1), min(f1), max(f2), min(f2), max(f3), min(f3) FROM arraggtest; INSERT INTO arraggtest (f1, f2, f3) VALUES ('{2}','{{black,red},{green,orange}}','{1.6,2.2,2.6,0.4}'); SELECT max(f1), min(f1), max(f2), min(f2), max(f3), min(f3) FROM arraggtest; INSERT INTO arraggtest (f1, f2, f3) VALUES ('{4,2,6,7,8,1}','{{red},{black},{purple},{blue},{blue}}',NULL); SELECT max(f1), min(f1), max(f2), min(f2), max(f3), min(f3) FROM arraggtest; INSERT INTO arraggtest (f1, f2, f3) VALUES ('{}','{{pink,white,blue,red,grey,orange}}','{2.1,1.87,1.4,2.2}'); SELECT max(f1), min(f1), max(f2), min(f2), max(f3), min(f3) FROM arraggtest; -- A few simple tests for arrays of composite types create type comptype as (f1 int, f2 text); create table comptable (c1 comptype, c2 comptype[]); -- XXX would like to not have to specify row() construct types here ... insert into comptable values (row(1,'foo'), array[row(2,'bar')::comptype, row(3,'baz')::comptype]); -- check that implicitly named array type _comptype isn't a problem create type _comptype as enum('fooey'); select * from comptable; select c2[2].f2 from comptable; drop type _comptype; drop table comptable; drop type comptype; create or replace function unnest1(anyarray) returns setof anyelement as $$ select $1[s] from generate_subscripts($1,1) g(s); $$ language sql immutable; create or replace function unnest2(anyarray) returns setof anyelement as $$ select $1[s1][s2] from generate_subscripts($1,1) g1(s1), generate_subscripts($1,2) g2(s2); $$ language sql immutable; select * from unnest1(array[1,2,3]); select * from unnest2(array[[1,2,3],[4,5,6]]); drop function unnest1(anyarray); drop function unnest2(anyarray); select array_fill(null::integer, array[3,3],array[2,2]); select array_fill(null::integer, array[3,3]); select array_fill(null::text, array[3,3],array[2,2]); select array_fill(null::text, array[3,3]); select array_fill(7, array[3,3],array[2,2]); select array_fill(7, array[3,3]); select array_fill('juhu'::text, array[3,3],array[2,2]); select array_fill('juhu'::text, array[3,3]); select a, a = '{}' as is_eq, array_dims(a) from (select array_fill(42, array[0]) as a) ss; select a, a = '{}' as is_eq, array_dims(a) from (select array_fill(42, '{}') as a) ss; select a, a = '{}' as is_eq, array_dims(a) from (select array_fill(42, '{}', '{}') as a) ss; -- raise exception select array_fill(1, null, array[2,2]); select array_fill(1, array[2,2], null); select array_fill(1, array[2,2], '{}'); select array_fill(1, array[3,3], array[1,1,1]); select array_fill(1, array[1,2,null]); select array_fill(1, array[[1,2],[3,4]]); select string_to_array('1|2|3', '|'); select string_to_array('1|2|3|', '|'); select string_to_array('1||2|3||', '||'); select string_to_array('1|2|3', ''); select string_to_array('', '|'); select string_to_array('1|2|3', NULL); select string_to_array(NULL, '|') IS NULL; select string_to_array('abc', ''); select string_to_array('abc', '', 'abc'); select string_to_array('abc', ','); select string_to_array('abc', ',', 'abc'); select string_to_array('1,2,3,4,,6', ','); select string_to_array('1,2,3,4,,6', ',', ''); select string_to_array('1,2,3,4,*,6', ',', '*'); select array_to_string(NULL::int4[], ',') IS NULL; select array_to_string('{}'::int4[], ','); select array_to_string(array[1,2,3,4,NULL,6], ','); select array_to_string(array[1,2,3,4,NULL,6], ',', '*'); select array_to_string(array[1,2,3,4,NULL,6], NULL); select array_to_string(array[1,2,3,4,NULL,6], ',', NULL); select array_to_string(string_to_array('1|2|3', '|'), '|'); select array_length(array[1,2,3], 1); select array_length(array[[1,2,3], [4,5,6]], 0); select array_length(array[[1,2,3], [4,5,6]], 1); select array_length(array[[1,2,3], [4,5,6]], 2); select array_length(array[[1,2,3], [4,5,6]], 3); select cardinality(NULL::int[]); select cardinality('{}'::int[]); select cardinality(array[1,2,3]); select cardinality('[2:4]={5,6,7}'::int[]); select cardinality('{{1,2}}'::int[]); select cardinality('{{1,2},{3,4},{5,6}}'::int[]); select cardinality('{{{1,9},{5,6}},{{2,3},{3,4}}}'::int[]); -- array_agg(anynonarray) select array_agg(unique1) from (select unique1 from tenk1 where unique1 < 15 order by unique1) ss; select array_agg(ten) from (select ten from tenk1 where unique1 < 15 order by unique1) ss; select array_agg(nullif(ten, 4)) from (select ten from tenk1 where unique1 < 15 order by unique1) ss; select array_agg(unique1) from tenk1 where unique1 < -15; -- array_agg(anyarray) select array_agg(ar) from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar); select array_agg(distinct ar order by ar desc) from (select array[i / 2] from generate_series(1,10) a(i)) b(ar); select array_agg(ar) from (select array_agg(array[i, i+1, i-1]) from generate_series(1,2) a(i)) b(ar); select array_agg(array[i+1.2, i+1.3, i+1.4]) from generate_series(1,3) g(i); select array_agg(array['Hello', i::text]) from generate_series(9,11) g(i); select array_agg(array[i, nullif(i, 3), i+1]) from generate_series(1,4) g(i); -- errors select array_agg('{}'::int[]) from generate_series(1,2); select array_agg(null::int[]) from generate_series(1,2); select array_agg(ar) from (values ('{1,2}'::int[]), ('{3}'::int[])) v(ar); select unnest(array[1,2,3]); select * from unnest(array[1,2,3]); select unnest(array[1,2,3,4.5]::float8[]); select unnest(array[1,2,3,4.5]::numeric[]); select unnest(array[1,2,3,null,4,null,null,5,6]); select unnest(array[1,2,3,null,4,null,null,5,6]::text[]); select abs(unnest(array[1,2,null,-3])); select array_remove(array[1,2,2,3], 2); select array_remove(array[1,2,2,3], 5); select array_remove(array[1,NULL,NULL,3], NULL); select array_remove(array['A','CC','D','C','RR'], 'RR'); select array_remove('{{1,2,2},{1,4,3}}', 2); -- not allowed select array_remove(array['X','X','X'], 'X') = '{}'; select array_replace(array[1,2,5,4],5,3); select array_replace(array[1,2,5,4],5,NULL); select array_replace(array[1,2,NULL,4,NULL],NULL,5); select array_replace(array['A','B','DD','B'],'B','CC'); select array_replace(array[1,NULL,3],NULL,NULL); select array_replace(array['AB',NULL,'CDE'],NULL,'12'); -- array(select array-value ...) select array(select array[i,i/2] from generate_series(1,5) i); select array(select array['Hello', i::text] from generate_series(9,11) i); -- Insert/update on a column that is array of composite create temp table t1 (f1 int8_tbl[]); insert into t1 (f1[5].q1) values(42); select * from t1; update t1 set f1[5].q2 = 43; select * from t1; -- Check that arrays of composites are safely detoasted when needed create temp table src (f1 text); insert into src select string_agg(random()::text,'') from generate_series(1,10000); create type textandtext as (c1 text, c2 text); create temp table dest (f1 textandtext[]); insert into dest select array[row(f1,f1)::textandtext] from src; select length(md5((f1[1]).c2)) from dest; delete from src; select length(md5((f1[1]).c2)) from dest; truncate table src; drop table src; select length(md5((f1[1]).c2)) from dest; drop table dest; drop type textandtext; -- Tests for polymorphic-array form of width_bucket() -- this exercises the varwidth and float8 code paths SELECT op, width_bucket(op::numeric, ARRAY[1, 3, 5, 10.0]::numeric[]) AS wb_n1, width_bucket(op::numeric, ARRAY[0, 5.5, 9.99]::numeric[]) AS wb_n2, width_bucket(op::numeric, ARRAY[-6, -5, 2.0]::numeric[]) AS wb_n3, width_bucket(op::float8, ARRAY[1, 3, 5, 10.0]::float8[]) AS wb_f1, width_bucket(op::float8, ARRAY[0, 5.5, 9.99]::float8[]) AS wb_f2, width_bucket(op::float8, ARRAY[-6, -5, 2.0]::float8[]) AS wb_f3 FROM (VALUES (-5.2), (-0.0000000001), (0.000000000001), (1), (1.99999999999999), (2), (2.00000000000001), (3), (4), (4.5), (5), (5.5), (6), (7), (8), (9), (9.99999999999999), (10), (10.0000000000001) ) v(op); -- ensure float8 path handles NaN properly SELECT op, width_bucket(op, ARRAY[1, 3, 9, 'NaN', 'NaN']::float8[]) AS wb FROM (VALUES (-5.2::float8), (4::float8), (77::float8), ('NaN'::float8) ) v(op); -- these exercise the generic fixed-width code path SELECT op, width_bucket(op, ARRAY[1, 3, 5, 10]) AS wb_1 FROM generate_series(0,11) as op; SELECT width_bucket(now(), array['yesterday', 'today', 'tomorrow']::timestamptz[]); -- corner cases SELECT width_bucket(5, ARRAY[3]); SELECT width_bucket(5, '{}'); -- error cases SELECT width_bucket('5'::text, ARRAY[3, 4]::integer[]); SELECT width_bucket(5, ARRAY[3, 4, NULL]); SELECT width_bucket(5, ARRAY[ARRAY[1, 2], ARRAY[3, 4]]); pgFormatter-4.2/t/pg-test-files/sql/async.sql000066400000000000000000000013771361326045100212450ustar00rootroot00000000000000-- -- ASYNC -- --Should work. Send a valid message via a valid channel name SELECT pg_notify('notify_async1','sample message1'); SELECT pg_notify('notify_async1',''); SELECT pg_notify('notify_async1',NULL); -- Should fail. Send a valid message via an invalid channel name SELECT pg_notify('','sample message1'); SELECT pg_notify(NULL,'sample message1'); SELECT pg_notify('notify_async_channel_name_too_long______________________________','sample_message1'); --Should work. Valid NOTIFY/LISTEN/UNLISTEN commands NOTIFY notify_async2; LISTEN notify_async2; UNLISTEN notify_async2; UNLISTEN *; -- Should return zero while there are no pending notifications. -- src/test/isolation/specs/async-notify.spec tests for actual usage. SELECT pg_notification_queue_usage(); pgFormatter-4.2/t/pg-test-files/sql/bit.sql000066400000000000000000000144421361326045100207030ustar00rootroot00000000000000-- -- BIT types -- -- -- Build tables for testing -- CREATE TABLE BIT_TABLE(b BIT(11)); INSERT INTO BIT_TABLE VALUES (B'10'); -- too short INSERT INTO BIT_TABLE VALUES (B'00000000000'); INSERT INTO BIT_TABLE VALUES (B'11011000000'); INSERT INTO BIT_TABLE VALUES (B'01010101010'); INSERT INTO BIT_TABLE VALUES (B'101011111010'); -- too long --INSERT INTO BIT_TABLE VALUES ('X554'); --INSERT INTO BIT_TABLE VALUES ('X555'); SELECT * FROM BIT_TABLE; CREATE TABLE VARBIT_TABLE(v BIT VARYING(11)); INSERT INTO VARBIT_TABLE VALUES (B''); INSERT INTO VARBIT_TABLE VALUES (B'0'); INSERT INTO VARBIT_TABLE VALUES (B'010101'); INSERT INTO VARBIT_TABLE VALUES (B'01010101010'); INSERT INTO VARBIT_TABLE VALUES (B'101011111010'); -- too long --INSERT INTO VARBIT_TABLE VALUES ('X554'); --INSERT INTO VARBIT_TABLE VALUES ('X555'); SELECT * FROM VARBIT_TABLE; -- Concatenation SELECT v, b, (v || b) AS concat FROM BIT_TABLE, VARBIT_TABLE ORDER BY 3; -- Length SELECT b, length(b) AS lb FROM BIT_TABLE; SELECT v, length(v) AS lv FROM VARBIT_TABLE; -- Substring SELECT b, SUBSTRING(b FROM 2 FOR 4) AS sub_2_4, SUBSTRING(b FROM 7 FOR 13) AS sub_7_13, SUBSTRING(b FROM 6) AS sub_6 FROM BIT_TABLE; SELECT v, SUBSTRING(v FROM 2 FOR 4) AS sub_2_4, SUBSTRING(v FROM 7 FOR 13) AS sub_7_13, SUBSTRING(v FROM 6) AS sub_6 FROM VARBIT_TABLE; --- Bit operations DROP TABLE varbit_table; CREATE TABLE varbit_table (a BIT VARYING(16), b BIT VARYING(16)); SELECT a, b, ~a AS "~ a", a & b AS "a & b", a | b AS "a | b", a # b AS "a # b" FROM varbit_table; SELECT a,b,a=b AS "a>=b",a>b AS "a>b",a<>b AS "a<>b" FROM varbit_table; SELECT a,a<<4 AS "a<<4",b,b>>2 AS "b>>2" FROM varbit_table; DROP TABLE varbit_table; --- Bit operations DROP TABLE bit_table; CREATE TABLE bit_table (a BIT(16), b BIT(16)); SELECT a,b,~a AS "~ a",a & b AS "a & b", a|b AS "a | b", a # b AS "a # b" FROM bit_table; SELECT a,b,a=b AS "a>=b",a>b AS "a>b",a<>b AS "a<>b" FROM bit_table; SELECT a,a<<4 AS "a<<4",b,b>>2 AS "b>>2" FROM bit_table; DROP TABLE bit_table; -- The following should fail select B'001' & B'10'; select B'0111' | B'011'; select B'0010' # B'011101'; -- More position tests, checking all the boundary cases SELECT POSITION(B'1010' IN B'0000101'); -- 0 SELECT POSITION(B'1010' IN B'00001010'); -- 5 SELECT POSITION(B'1010' IN B'00000101'); -- 0 SELECT POSITION(B'1010' IN B'000001010'); -- 6 SELECT POSITION(B'' IN B'00001010'); -- 1 SELECT POSITION(B'0' IN B''); -- 0 SELECT POSITION(B'' IN B''); -- 0 SELECT POSITION(B'101101' IN B'001011011011011000'); -- 3 SELECT POSITION(B'10110110' IN B'001011011011010'); -- 3 SELECT POSITION(B'1011011011011' IN B'001011011011011'); -- 3 SELECT POSITION(B'1011011011011' IN B'00001011011011011'); -- 5 SELECT POSITION(B'11101011' IN B'11101011'); -- 1 SELECT POSITION(B'11101011' IN B'011101011'); -- 2 SELECT POSITION(B'11101011' IN B'00011101011'); -- 4 SELECT POSITION(B'11101011' IN B'0000011101011'); -- 6 SELECT POSITION(B'111010110' IN B'111010110'); -- 1 SELECT POSITION(B'111010110' IN B'0111010110'); -- 2 SELECT POSITION(B'111010110' IN B'000111010110'); -- 4 SELECT POSITION(B'111010110' IN B'00000111010110'); -- 6 SELECT POSITION(B'111010110' IN B'11101011'); -- 0 SELECT POSITION(B'111010110' IN B'011101011'); -- 0 SELECT POSITION(B'111010110' IN B'00011101011'); -- 0 SELECT POSITION(B'111010110' IN B'0000011101011'); -- 0 SELECT POSITION(B'111010110' IN B'111010110'); -- 1 SELECT POSITION(B'111010110' IN B'0111010110'); -- 2 SELECT POSITION(B'111010110' IN B'000111010110'); -- 4 SELECT POSITION(B'111010110' IN B'00000111010110'); -- 6 SELECT POSITION(B'111010110' IN B'000001110101111101011'); -- 0 SELECT POSITION(B'111010110' IN B'0000001110101111101011'); -- 0 SELECT POSITION(B'111010110' IN B'000000001110101111101011'); -- 0 SELECT POSITION(B'111010110' IN B'00000000001110101111101011'); -- 0 SELECT POSITION(B'111010110' IN B'0000011101011111010110'); -- 14 SELECT POSITION(B'111010110' IN B'00000011101011111010110'); -- 15 SELECT POSITION(B'111010110' IN B'0000000011101011111010110'); -- 17 SELECT POSITION(B'111010110' IN B'000000000011101011111010110'); -- 19 SELECT POSITION(B'000000000011101011111010110' IN B'000000000011101011111010110'); -- 1 SELECT POSITION(B'00000000011101011111010110' IN B'000000000011101011111010110'); -- 2 SELECT POSITION(B'0000000000011101011111010110' IN B'000000000011101011111010110'); -- 0 -- Shifting CREATE TABLE BIT_SHIFT_TABLE(b BIT(16)); INSERT INTO BIT_SHIFT_TABLE VALUES (B'1101100000000000'); INSERT INTO BIT_SHIFT_TABLE SELECT b>>1 FROM BIT_SHIFT_TABLE; INSERT INTO BIT_SHIFT_TABLE SELECT b>>2 FROM BIT_SHIFT_TABLE; INSERT INTO BIT_SHIFT_TABLE SELECT b>>4 FROM BIT_SHIFT_TABLE; INSERT INTO BIT_SHIFT_TABLE SELECT b>>8 FROM BIT_SHIFT_TABLE; SELECT POSITION(B'1101' IN b), POSITION(B'11011' IN b), b FROM BIT_SHIFT_TABLE ; CREATE TABLE VARBIT_SHIFT_TABLE(v BIT VARYING(20)); INSERT INTO VARBIT_SHIFT_TABLE VALUES (B'11011'); INSERT INTO VARBIT_SHIFT_TABLE SELECT CAST(v || B'0' AS BIT VARYING(6)) >>1 FROM VARBIT_SHIFT_TABLE; INSERT INTO VARBIT_SHIFT_TABLE SELECT CAST(v || B'00' AS BIT VARYING(8)) >>2 FROM VARBIT_SHIFT_TABLE; INSERT INTO VARBIT_SHIFT_TABLE SELECT CAST(v || B'0000' AS BIT VARYING(12)) >>4 FROM VARBIT_SHIFT_TABLE; INSERT INTO VARBIT_SHIFT_TABLE SELECT CAST(v || B'00000000' AS BIT VARYING(20)) >>8 FROM VARBIT_SHIFT_TABLE; SELECT POSITION(B'1101' IN v), POSITION(B'11011' IN v), v FROM VARBIT_SHIFT_TABLE ; DROP TABLE BIT_SHIFT_TABLE; DROP TABLE VARBIT_SHIFT_TABLE; -- Get/Set bit SELECT get_bit(B'0101011000100', 10); SELECT set_bit(B'0101011000100100', 15, 1); SELECT set_bit(B'0101011000100100', 16, 1); -- fail -- Overlay SELECT overlay(B'0101011100' placing '001' from 2 for 3); SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', b2 bit(4) DEFAULT B'0101', b3 bit varying(5) DEFAULT '1001', b4 bit varying(5) DEFAULT B'0101' ); \d bit_defaults INSERT INTO bit_defaults DEFAULT VALUES; TABLE bit_defaults; pgFormatter-4.2/t/pg-test-files/sql/bitmapops.sql000066400000000000000000000025251361326045100221220ustar00rootroot00000000000000-- Test bitmap AND and OR -- Generate enough data that we can test the lossy bitmaps. -- There's 55 tuples per page in the table. 53 is just -- below 55, so that an index scan with qual a = constant -- will return at least one hit per page. 59 is just above -- 55, so that an index scan with qual b = constant will return -- hits on most but not all pages. 53 and 59 are prime, so that -- there's a maximum number of a,b combinations in the table. -- That allows us to test all the different combinations of -- lossy and non-lossy pages with the minimum amount of data CREATE TABLE bmscantest (a int, b int, t text); INSERT INTO bmscantest SELECT (r%53), (r%59), 'foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo' FROM generate_series(1,70000) r; CREATE INDEX i_bmtest_a ON bmscantest(a); CREATE INDEX i_bmtest_b ON bmscantest(b); -- We want to use bitmapscans. With default settings, the planner currently -- chooses a bitmap scan for the queries below anyway, but let's make sure. set enable_indexscan=false; set enable_seqscan=false; -- Lower work_mem to trigger use of lossy bitmaps set work_mem = 64; -- Test bitmap-and. SELECT count(*) FROM bmscantest WHERE a = 1 AND b = 1; -- Test bitmap-or. SELECT count(*) FROM bmscantest WHERE a = 1 OR b = 1; -- clean up DROP TABLE bmscantest; pgFormatter-4.2/t/pg-test-files/sql/boolean.sql000066400000000000000000000131741361326045100215450ustar00rootroot00000000000000-- -- BOOLEAN -- -- -- sanity check - if this fails go insane! -- SELECT 1 AS one; -- ******************testing built-in type bool******************** -- check bool input syntax SELECT true AS true; SELECT false AS false; SELECT bool 't' AS true; SELECT bool ' f ' AS false; SELECT bool 'true' AS true; SELECT bool 'test' AS error; SELECT bool 'false' AS false; SELECT bool 'foo' AS error; SELECT bool 'y' AS true; SELECT bool 'yes' AS true; SELECT bool 'yeah' AS error; SELECT bool 'n' AS false; SELECT bool 'no' AS false; SELECT bool 'nay' AS error; SELECT bool 'on' AS true; SELECT bool 'off' AS false; SELECT bool 'of' AS false; SELECT bool 'o' AS error; SELECT bool 'on_' AS error; SELECT bool 'off_' AS error; SELECT bool '1' AS true; SELECT bool '11' AS error; SELECT bool '0' AS false; SELECT bool '000' AS error; SELECT bool '' AS error; -- and, or, not in qualifications SELECT bool 't' or bool 'f' AS true; SELECT bool 't' and bool 'f' AS false; SELECT not bool 'f' AS true; SELECT bool 't' = bool 'f' AS false; SELECT bool 't' <> bool 'f' AS true; SELECT bool 't' > bool 'f' AS true; SELECT bool 't' >= bool 'f' AS true; SELECT bool 'f' < bool 't' AS true; SELECT bool 'f' <= bool 't' AS true; -- explicit casts to/from text SELECT 'TrUe'::text::boolean AS true, 'fAlse'::text::boolean AS false; SELECT ' true '::text::boolean AS true, ' FALSE'::text::boolean AS false; SELECT true::boolean::text AS true, false::boolean::text AS false; SELECT ' tru e '::text::boolean AS invalid; -- error SELECT ''::text::boolean AS invalid; -- error CREATE TABLE BOOLTBL1 (f1 bool); INSERT INTO BOOLTBL1 (f1) VALUES (bool 't'); INSERT INTO BOOLTBL1 (f1) VALUES (bool 'True'); INSERT INTO BOOLTBL1 (f1) VALUES (bool 'true'); -- BOOLTBL1 should be full of true's at this point SELECT '' AS t_3, BOOLTBL1.* FROM BOOLTBL1; SELECT '' AS t_3, BOOLTBL1.* FROM BOOLTBL1 WHERE f1 = bool 'true'; SELECT '' AS t_3, BOOLTBL1.* FROM BOOLTBL1 WHERE f1 <> bool 'false'; SELECT '' AS zero, BOOLTBL1.* FROM BOOLTBL1 WHERE booleq(bool 'false', f1); INSERT INTO BOOLTBL1 (f1) VALUES (bool 'f'); SELECT '' AS f_1, BOOLTBL1.* FROM BOOLTBL1 WHERE f1 = bool 'false'; CREATE TABLE BOOLTBL2 (f1 bool); INSERT INTO BOOLTBL2 (f1) VALUES (bool 'f'); INSERT INTO BOOLTBL2 (f1) VALUES (bool 'false'); INSERT INTO BOOLTBL2 (f1) VALUES (bool 'False'); INSERT INTO BOOLTBL2 (f1) VALUES (bool 'FALSE'); -- This is now an invalid expression -- For pre-v6.3 this evaluated to false - thomas 1997-10-23 INSERT INTO BOOLTBL2 (f1) VALUES (bool 'XXX'); -- BOOLTBL2 should be full of false's at this point SELECT '' AS f_4, BOOLTBL2.* FROM BOOLTBL2; SELECT '' AS tf_12, BOOLTBL1.*, BOOLTBL2.* FROM BOOLTBL1, BOOLTBL2 WHERE BOOLTBL2.f1 <> BOOLTBL1.f1; SELECT '' AS tf_12, BOOLTBL1.*, BOOLTBL2.* FROM BOOLTBL1, BOOLTBL2 WHERE boolne(BOOLTBL2.f1,BOOLTBL1.f1); SELECT '' AS ff_4, BOOLTBL1.*, BOOLTBL2.* FROM BOOLTBL1, BOOLTBL2 WHERE BOOLTBL2.f1 = BOOLTBL1.f1 and BOOLTBL1.f1 = bool 'false'; SELECT '' AS tf_12_ff_4, BOOLTBL1.*, BOOLTBL2.* FROM BOOLTBL1, BOOLTBL2 WHERE BOOLTBL2.f1 = BOOLTBL1.f1 or BOOLTBL1.f1 = bool 'true' ORDER BY BOOLTBL1.f1, BOOLTBL2.f1; -- -- SQL syntax -- Try all combinations to ensure that we get nothing when we expect nothing -- - thomas 2000-01-04 -- SELECT '' AS "True", f1 FROM BOOLTBL1 WHERE f1 IS TRUE; SELECT '' AS "Not False", f1 FROM BOOLTBL1 WHERE f1 IS NOT FALSE; SELECT '' AS "False", f1 FROM BOOLTBL1 WHERE f1 IS FALSE; SELECT '' AS "Not True", f1 FROM BOOLTBL1 WHERE f1 IS NOT TRUE; SELECT '' AS "True", f1 FROM BOOLTBL2 WHERE f1 IS TRUE; SELECT '' AS "Not False", f1 FROM BOOLTBL2 WHERE f1 IS NOT FALSE; SELECT '' AS "False", f1 FROM BOOLTBL2 WHERE f1 IS FALSE; SELECT '' AS "Not True", f1 FROM BOOLTBL2 WHERE f1 IS NOT TRUE; -- -- Tests for BooleanTest -- CREATE TABLE BOOLTBL3 (d text, b bool, o int); INSERT INTO BOOLTBL3 (d, b, o) VALUES ('true', true, 1); INSERT INTO BOOLTBL3 (d, b, o) VALUES ('false', false, 2); INSERT INTO BOOLTBL3 (d, b, o) VALUES ('null', null, 3); SELECT d, b IS TRUE AS istrue, b IS NOT TRUE AS isnottrue, b IS FALSE AS isfalse, b IS NOT FALSE AS isnotfalse, b IS UNKNOWN AS isunknown, b IS NOT UNKNOWN AS isnotunknown FROM booltbl3 ORDER BY o; -- Test to make sure short-circuiting and NULL handling is -- correct. Use a table as source to prevent constant simplification -- to interfer. CREATE TABLE booltbl4(isfalse bool, istrue bool, isnul bool); INSERT INTO booltbl4 VALUES (false, true, null); \pset null '(null)' -- AND expression need to return null if there's any nulls and not all -- of the value are true SELECT istrue AND isnul AND istrue FROM booltbl4; SELECT istrue AND istrue AND isnul FROM booltbl4; SELECT isnul AND istrue AND istrue FROM booltbl4; SELECT isfalse AND isnul AND istrue FROM booltbl4; SELECT istrue AND isfalse AND isnul FROM booltbl4; SELECT isnul AND istrue AND isfalse FROM booltbl4; -- OR expression need to return null if there's any nulls and none -- of the value is true SELECT isfalse OR isnul OR isfalse FROM booltbl4; SELECT isfalse OR isfalse OR isnul FROM booltbl4; SELECT isnul OR isfalse OR isfalse FROM booltbl4; SELECT isfalse OR isnul OR istrue FROM booltbl4; SELECT istrue OR isfalse OR isnul FROM booltbl4; SELECT isnul OR istrue OR isfalse FROM booltbl4; -- -- Clean up -- Many tables are retained by the regression test, but these do not seem -- particularly useful so just get rid of them for now. -- - thomas 1997-11-30 -- DROP TABLE BOOLTBL1; DROP TABLE BOOLTBL2; DROP TABLE BOOLTBL3; DROP TABLE BOOLTBL4; pgFormatter-4.2/t/pg-test-files/sql/box.sql000066400000000000000000000145541361326045100207210ustar00rootroot00000000000000-- -- BOX -- -- -- box logic -- o -- 3 o--|X -- | o| -- 2 +-+-+ | -- | | | | -- 1 | o-+-o -- | | -- 0 +---+ -- -- 0 1 2 3 -- -- boxes are specified by two points, given by four floats x1,y1,x2,y2 CREATE TABLE BOX_TBL (f1 box); INSERT INTO BOX_TBL (f1) VALUES ('(2.0,2.0,0.0,0.0)'); INSERT INTO BOX_TBL (f1) VALUES ('(1.0,1.0,3.0,3.0)'); INSERT INTO BOX_TBL (f1) VALUES ('((-8, 2), (-2, -10))'); -- degenerate cases where the box is a line or a point -- note that lines and points boxes all have zero area INSERT INTO BOX_TBL (f1) VALUES ('(2.5, 2.5, 2.5,3.5)'); INSERT INTO BOX_TBL (f1) VALUES ('(3.0, 3.0,3.0,3.0)'); -- badly formatted box inputs INSERT INTO BOX_TBL (f1) VALUES ('(2.3, 4.5)'); INSERT INTO BOX_TBL (f1) VALUES ('[1, 2, 3, 4)'); INSERT INTO BOX_TBL (f1) VALUES ('(1, 2, 3, 4]'); INSERT INTO BOX_TBL (f1) VALUES ('(1, 2, 3, 4) x'); INSERT INTO BOX_TBL (f1) VALUES ('asdfasdf(ad'); SELECT '' AS four, * FROM BOX_TBL; SELECT '' AS four, b.*, area(b.f1) as barea FROM BOX_TBL b; -- overlap SELECT '' AS three, b.f1 FROM BOX_TBL b WHERE b.f1 && box '(2.5,2.5,1.0,1.0)'; -- left-or-overlap (x only) SELECT '' AS two, b1.* FROM BOX_TBL b1 WHERE b1.f1 &< box '(2.0,2.0,2.5,2.5)'; -- right-or-overlap (x only) SELECT '' AS two, b1.* FROM BOX_TBL b1 WHERE b1.f1 &> box '(2.0,2.0,2.5,2.5)'; -- left of SELECT '' AS two, b.f1 FROM BOX_TBL b WHERE b.f1 << box '(3.0,3.0,5.0,5.0)'; -- area <= SELECT '' AS four, b.f1 FROM BOX_TBL b WHERE b.f1 <= box '(3.0,3.0,5.0,5.0)'; -- area < SELECT '' AS two, b.f1 FROM BOX_TBL b WHERE b.f1 < box '(3.0,3.0,5.0,5.0)'; -- area = SELECT '' AS two, b.f1 FROM BOX_TBL b WHERE b.f1 = box '(3.0,3.0,5.0,5.0)'; -- area > SELECT '' AS two, b.f1 FROM BOX_TBL b -- zero area WHERE b.f1 > box '(3.5,3.0,4.5,3.0)'; -- area >= SELECT '' AS four, b.f1 FROM BOX_TBL b -- zero area WHERE b.f1 >= box '(3.5,3.0,4.5,3.0)'; -- right of SELECT '' AS two, b.f1 FROM BOX_TBL b WHERE box '(3.0,3.0,5.0,5.0)' >> b.f1; -- contained in SELECT '' AS three, b.f1 FROM BOX_TBL b WHERE b.f1 <@ box '(0,0,3,3)'; -- contains SELECT '' AS three, b.f1 FROM BOX_TBL b WHERE box '(0,0,3,3)' @> b.f1; -- box equality SELECT '' AS one, b.f1 FROM BOX_TBL b WHERE box '(1,1,3,3)' ~= b.f1; -- center of box, left unary operator SELECT '' AS four, @@(b1.f1) AS p FROM BOX_TBL b1; -- wholly-contained SELECT '' AS one, b1.*, b2.* FROM BOX_TBL b1, BOX_TBL b2 WHERE b1.f1 @> b2.f1 and not b1.f1 ~= b2.f1; SELECT '' AS four, height(f1), width(f1) FROM BOX_TBL; -- -- Test the SP-GiST index -- CREATE TEMPORARY TABLE box_temp (f1 box); INSERT INTO box_temp SELECT box(point(i, i), point(i * 2, i * 2)) FROM generate_series(1, 50) AS i; CREATE INDEX box_spgist ON box_temp USING spgist (f1); INSERT INTO box_temp VALUES (NULL), ('(0,0)(0,100)'), ('(-3,4.3333333333)(40,1)'), ('(0,100)(0,infinity)'), ('(-infinity,0)(0,infinity)'), ('(-infinity,-infinity)(infinity,infinity)'); SET enable_seqscan = false; SELECT * FROM box_temp WHERE f1 << '(10,20),(30,40)'; EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 << '(10,20),(30,40)'; SELECT * FROM box_temp WHERE f1 &< '(10,4.333334),(5,100)'; EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 &< '(10,4.333334),(5,100)'; SELECT * FROM box_temp WHERE f1 && '(15,20),(25,30)'; EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 && '(15,20),(25,30)'; SELECT * FROM box_temp WHERE f1 &> '(40,30),(45,50)'; EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 &> '(40,30),(45,50)'; SELECT * FROM box_temp WHERE f1 >> '(30,40),(40,30)'; EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 >> '(30,40),(40,30)'; SELECT * FROM box_temp WHERE f1 <<| '(10,4.33334),(5,100)'; EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 <<| '(10,4.33334),(5,100)'; SELECT * FROM box_temp WHERE f1 &<| '(10,4.3333334),(5,1)'; EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 &<| '(10,4.3333334),(5,1)'; SELECT * FROM box_temp WHERE f1 |&> '(49.99,49.99),(49.99,49.99)'; EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 |&> '(49.99,49.99),(49.99,49.99)'; SELECT * FROM box_temp WHERE f1 |>> '(37,38),(39,40)'; EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 |>> '(37,38),(39,40)'; SELECT * FROM box_temp WHERE f1 @> '(10,11),(15,16)'; EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 @> '(10,11),(15,15)'; SELECT * FROM box_temp WHERE f1 <@ '(10,15),(30,35)'; EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 <@ '(10,15),(30,35)'; SELECT * FROM box_temp WHERE f1 ~= '(20,20),(40,40)'; EXPLAIN (COSTS OFF) SELECT * FROM box_temp WHERE f1 ~= '(20,20),(40,40)'; RESET enable_seqscan; DROP INDEX box_spgist; -- -- Test the SP-GiST index on the larger volume of data -- CREATE TABLE quad_box_tbl (b box); INSERT INTO quad_box_tbl SELECT box(point(x * 10, y * 10), point(x * 10 + 5, y * 10 + 5)) FROM generate_series(1, 100) x, generate_series(1, 100) y; -- insert repeating data to test allTheSame INSERT INTO quad_box_tbl SELECT '((200, 300),(210, 310))' FROM generate_series(1, 1000); INSERT INTO quad_box_tbl VALUES (NULL), (NULL), ('((-infinity,-infinity),(infinity,infinity))'), ('((-infinity,100),(-infinity,500))'), ('((-infinity,-infinity),(700,infinity))'); CREATE INDEX quad_box_tbl_idx ON quad_box_tbl USING spgist(b); SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = ON; SELECT count(*) FROM quad_box_tbl WHERE b << box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b &< box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b && box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b &> box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b >> box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b >> box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b <<| box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b &<| box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b |&> box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b |>> box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b @> box '((201,301),(202,303))'; SELECT count(*) FROM quad_box_tbl WHERE b <@ box '((100,200),(300,500))'; SELECT count(*) FROM quad_box_tbl WHERE b ~= box '((200,300),(205,305))'; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; pgFormatter-4.2/t/pg-test-files/sql/brin.sql000066400000000000000000000371751361326045100210670ustar00rootroot00000000000000CREATE TABLE brintest (byteacol bytea, charcol "char", namecol name, int8col bigint, int2col smallint, int4col integer, textcol text, oidcol oid, tidcol tid, float4col real, float8col double precision, macaddrcol macaddr, inetcol inet, cidrcol cidr, bpcharcol character, datecol date, timecol time without time zone, timestampcol timestamp without time zone, timestamptzcol timestamp with time zone, intervalcol interval, timetzcol time with time zone, bitcol bit(10), varbitcol bit varying(16), numericcol numeric, uuidcol uuid, int4rangecol int4range, lsncol pg_lsn, boxcol box ) WITH (fillfactor=10); INSERT INTO brintest SELECT repeat(stringu1, 8)::bytea, substr(stringu1, 1, 1)::"char", stringu1::name, 142857 * tenthous, thousand, twothousand, repeat(stringu1, 8), unique1::oid, format('(%s,%s)', tenthous, twenty)::tid, (four + 1.0)/(hundred+1), odd::float8 / (tenthous + 1), format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, inet '10.2.3.4/24' + tenthous, cidr '10.2.3/24' + tenthous, substr(stringu1, 1, 1)::bpchar, date '1995-08-15' + tenthous, time '01:20:30' + thousand * interval '18.5 second', timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', justify_days(justify_hours(tenthous * interval '12 minutes')), timetz '01:30:20+02' + hundred * interval '15 seconds', thousand::bit(10), tenthous::bit(16)::varbit, tenthous::numeric(36,30) * fivethous * even / (hundred + 1), format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, int4range(thousand, twothousand), format('%s/%s%s', odd, even, tenthous)::pg_lsn, box(point(odd, even), point(thousand, twothousand)) FROM tenk1 ORDER BY unique2 LIMIT 100; -- throw in some NULL's and different values INSERT INTO brintest (inetcol, cidrcol, int4rangecol) SELECT inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous, 'empty'::int4range FROM tenk1 ORDER BY thousand, tenthous LIMIT 25; CREATE INDEX brinidx ON brintest USING brin ( byteacol, charcol, namecol, int8col, int2col, int4col, textcol, oidcol, tidcol, float4col, float8col, macaddrcol, inetcol inet_inclusion_ops, inetcol inet_minmax_ops, cidrcol inet_inclusion_ops, cidrcol inet_minmax_ops, bpcharcol, datecol, timecol, timestampcol, timestamptzcol, intervalcol, timetzcol, bitcol, varbitcol, numericcol, uuidcol, int4rangecol, lsncol, boxcol ) with (pages_per_range = 1); CREATE TABLE brinopers (colname name, typ text, op text[], value text[], matches int[], check (cardinality(op) = cardinality(value)), check (cardinality(op) = cardinality(matches))); INSERT INTO brinopers VALUES ('byteacol', 'bytea', '{>, >=, =, <=, <}', '{AAAAAA, AAAAAA, BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA, ZZZZZZ, ZZZZZZ}', '{100, 100, 1, 100, 100}'), ('charcol', '"char"', '{>, >=, =, <=, <}', '{A, A, M, Z, Z}', '{97, 100, 6, 100, 98}'), ('namecol', 'name', '{>, >=, =, <=, <}', '{AAAAAA, AAAAAA, MAAAAA, ZZAAAA, ZZAAAA}', '{100, 100, 2, 100, 100}'), ('int2col', 'int2', '{>, >=, =, <=, <}', '{0, 0, 800, 999, 999}', '{100, 100, 1, 100, 100}'), ('int2col', 'int4', '{>, >=, =, <=, <}', '{0, 0, 800, 999, 1999}', '{100, 100, 1, 100, 100}'), ('int2col', 'int8', '{>, >=, =, <=, <}', '{0, 0, 800, 999, 1428427143}', '{100, 100, 1, 100, 100}'), ('int4col', 'int2', '{>, >=, =, <=, <}', '{0, 0, 800, 1999, 1999}', '{100, 100, 1, 100, 100}'), ('int4col', 'int4', '{>, >=, =, <=, <}', '{0, 0, 800, 1999, 1999}', '{100, 100, 1, 100, 100}'), ('int4col', 'int8', '{>, >=, =, <=, <}', '{0, 0, 800, 1999, 1428427143}', '{100, 100, 1, 100, 100}'), ('int8col', 'int2', '{>, >=}', '{0, 0}', '{100, 100}'), ('int8col', 'int4', '{>, >=}', '{0, 0}', '{100, 100}'), ('int8col', 'int8', '{>, >=, =, <=, <}', '{0, 0, 1257141600, 1428427143, 1428427143}', '{100, 100, 1, 100, 100}'), ('textcol', 'text', '{>, >=, =, <=, <}', '{ABABAB, ABABAB, BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA, ZZAAAA, ZZAAAA}', '{100, 100, 1, 100, 100}'), ('oidcol', 'oid', '{>, >=, =, <=, <}', '{0, 0, 8800, 9999, 9999}', '{100, 100, 1, 100, 100}'), ('tidcol', 'tid', '{>, >=, =, <=, <}', '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}', '{100, 100, 1, 100, 100}'), ('float4col', 'float4', '{>, >=, =, <=, <}', '{0.0103093, 0.0103093, 1, 1, 1}', '{100, 100, 4, 100, 96}'), ('float4col', 'float8', '{>, >=, =, <=, <}', '{0.0103093, 0.0103093, 1, 1, 1}', '{100, 100, 4, 100, 96}'), ('float8col', 'float4', '{>, >=, =, <=, <}', '{0, 0, 0, 1.98, 1.98}', '{99, 100, 1, 100, 100}'), ('float8col', 'float8', '{>, >=, =, <=, <}', '{0, 0, 0, 1.98, 1.98}', '{99, 100, 1, 100, 100}'), ('macaddrcol', 'macaddr', '{>, >=, =, <=, <}', '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}', '{99, 100, 2, 100, 100}'), ('inetcol', 'inet', '{&&, =, <, <=, >, >=, >>=, >>, <<=, <<}', '{10/8, 10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0, 10.2.14.231/24, 10.2.14.231/25, 10.2.14.231/8, 0/0}', '{100, 1, 100, 100, 125, 125, 2, 2, 100, 100}'), ('inetcol', 'inet', '{&&, >>=, <<=, =}', '{fe80::6e40:8ff:fea9:a673/32, fe80::6e40:8ff:fea9:8c46, fe80::6e40:8ff:fea9:a673/32, fe80::6e40:8ff:fea9:8c46}', '{25, 1, 25, 1}'), ('inetcol', 'cidr', '{&&, <, <=, >, >=, >>=, >>, <<=, <<}', '{10/8, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0, 10.2.14/24, 10.2.14/25, 10/8, 0/0}', '{100, 100, 100, 125, 125, 2, 2, 100, 100}'), ('inetcol', 'cidr', '{&&, >>=, <<=, =}', '{fe80::/32, fe80::6e40:8ff:fea9:8c46, fe80::/32, fe80::6e40:8ff:fea9:8c46}', '{25, 1, 25, 1}'), ('cidrcol', 'inet', '{&&, =, <, <=, >, >=, >>=, >>, <<=, <<}', '{10/8, 10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0, 10.2.14.231/24, 10.2.14.231/25, 10.2.14.231/8, 0/0}', '{100, 2, 100, 100, 125, 125, 2, 2, 100, 100}'), ('cidrcol', 'inet', '{&&, >>=, <<=, =}', '{fe80::6e40:8ff:fea9:a673/32, fe80::6e40:8ff:fea9:8c46, fe80::6e40:8ff:fea9:a673/32, fe80::6e40:8ff:fea9:8c46}', '{25, 1, 25, 1}'), ('cidrcol', 'cidr', '{&&, =, <, <=, >, >=, >>=, >>, <<=, <<}', '{10/8, 10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0, 10.2.14/24, 10.2.14/25, 10/8, 0/0}', '{100, 2, 100, 100, 125, 125, 2, 2, 100, 100}'), ('cidrcol', 'cidr', '{&&, >>=, <<=, =}', '{fe80::/32, fe80::6e40:8ff:fea9:8c46, fe80::/32, fe80::6e40:8ff:fea9:8c46}', '{25, 1, 25, 1}'), ('bpcharcol', 'bpchar', '{>, >=, =, <=, <}', '{A, A, W, Z, Z}', '{97, 100, 6, 100, 98}'), ('datecol', 'date', '{>, >=, =, <=, <}', '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}', '{100, 100, 1, 100, 100}'), ('timecol', 'time', '{>, >=, =, <=, <}', '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}', '{100, 100, 1, 100, 100}'), ('timestampcol', 'timestamp', '{>, >=, =, <=, <}', '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', '{100, 100, 1, 100, 100}'), ('timestampcol', 'timestamptz', '{>, >=, =, <=, <}', '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}', '{100, 100, 1, 100, 100}'), ('timestamptzcol', 'timestamptz', '{>, >=, =, <=, <}', '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}', '{100, 100, 1, 100, 100}'), ('intervalcol', 'interval', '{>, >=, =, <=, <}', '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}', '{100, 100, 1, 100, 100}'), ('timetzcol', 'timetz', '{>, >=, =, <=, <}', '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}', '{99, 100, 2, 100, 100}'), ('bitcol', 'bit(10)', '{>, >=, =, <=, <}', '{0000000010, 0000000010, 0011011110, 1111111000, 1111111000}', '{100, 100, 1, 100, 100}'), ('varbitcol', 'varbit(16)', '{>, >=, =, <=, <}', '{0000000000000100, 0000000000000100, 0001010001100110, 1111111111111000, 1111111111111000}', '{100, 100, 1, 100, 100}'), ('numericcol', 'numeric', '{>, >=, =, <=, <}', '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}', '{100, 100, 1, 100, 100}'), ('uuidcol', 'uuid', '{>, >=, =, <=, <}', '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}', '{100, 100, 1, 100, 100}'), ('int4rangecol', 'int4range', '{<<, &<, &&, &>, >>, @>, <@, =, <, <=, >, >=}', '{"[10000,)","[10000,)","(,]","[3,4)","[36,44)","(1500,1501]","[3,4)","[222,1222)","[36,44)","[43,1043)","[367,4466)","[519,)"}', '{53, 53, 53, 53, 50, 22, 72, 1, 74, 75, 34, 21}'), ('int4rangecol', 'int4range', '{@>, <@, =, <=, >, >=}', '{empty, empty, empty, empty, empty, empty}', '{125, 72, 72, 72, 53, 125}'), ('int4rangecol', 'int4', '{@>}', '{1500}', '{22}'), ('lsncol', 'pg_lsn', '{>, >=, =, <=, <, IS, IS NOT}', '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}', '{100, 100, 1, 100, 100, 25, 100}'), ('boxcol', 'point', '{@>}', '{"(500,43)"}', '{11}'), ('boxcol', 'box', '{<<, &<, &&, &>, >>, <<|, &<|, |&>, |>>, @>, <@, ~=}', '{"((1000,2000),(3000,4000))","((1,2),(3000,4000))","((1,2),(3000,4000))","((1,2),(3000,4000))","((1,2),(3,4))","((1000,2000),(3000,4000))","((1,2000),(3,4000))","((1000,2),(3000,4))","((1,2),(3,4))","((1,2),(300,400))","((1,2),(3000,4000))","((222,1222),(44,45))"}', '{100, 100, 100, 99, 96, 100, 100, 99, 96, 1, 99, 1}'); DO $x$ DECLARE r record; r2 record; cond text; idx_ctids tid[]; ss_ctids tid[]; count int; plan_ok bool; plan_line text; BEGIN FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers, unnest(op) WITH ORDINALITY AS oper LOOP -- prepare the condition IF r.value IS NULL THEN cond := format('%I %s %L', r.colname, r.oper, r.value); ELSE cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ); END IF; -- run the query using the brin index SET enable_seqscan = 0; SET enable_bitmapscan = 1; plan_ok := false; FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest WHERE %s $y$, cond) LOOP IF plan_line LIKE '%Bitmap Heap Scan on brintest%' THEN plan_ok := true; END IF; END LOOP; IF NOT plan_ok THEN RAISE WARNING 'did not get bitmap indexscan plan for %', r; END IF; EXECUTE format($y$SELECT array_agg(ctid) FROM brintest WHERE %s $y$, cond) INTO idx_ctids; -- run the query using a seqscan SET enable_seqscan = 1; SET enable_bitmapscan = 0; plan_ok := false; FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest WHERE %s $y$, cond) LOOP IF plan_line LIKE '%Seq Scan on brintest%' THEN plan_ok := true; END IF; END LOOP; IF NOT plan_ok THEN RAISE WARNING 'did not get seqscan plan for %', r; END IF; EXECUTE format($y$SELECT array_agg(ctid) FROM brintest WHERE %s $y$, cond) INTO ss_ctids; -- make sure both return the same results count := array_length(idx_ctids, 1); IF NOT (count = array_length(ss_ctids, 1) AND idx_ctids @> ss_ctids AND idx_ctids <@ ss_ctids) THEN -- report the results of each scan to make the differences obvious RAISE WARNING 'something not right in %: count %', r, count; SET enable_seqscan = 1; SET enable_bitmapscan = 0; FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest WHERE ' || cond LOOP RAISE NOTICE 'seqscan: %', r2; END LOOP; SET enable_seqscan = 0; SET enable_bitmapscan = 1; FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest WHERE ' || cond LOOP RAISE NOTICE 'bitmapscan: %', r2; END LOOP; END IF; -- make sure we found expected number of matches IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF; END LOOP; END; $x$; RESET enable_seqscan; RESET enable_bitmapscan; INSERT INTO brintest SELECT repeat(stringu1, 42)::bytea, substr(stringu1, 1, 1)::"char", stringu1::name, 142857 * tenthous, thousand, twothousand, repeat(stringu1, 42), unique1::oid, format('(%s,%s)', tenthous, twenty)::tid, (four + 1.0)/(hundred+1), odd::float8 / (tenthous + 1), format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr, inet '10.2.3.4' + tenthous, cidr '10.2.3/24' + tenthous, substr(stringu1, 1, 1)::bpchar, date '1995-08-15' + tenthous, time '01:20:30' + thousand * interval '18.5 second', timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours', timestamptz '1972-10-10 03:00' + thousand * interval '1 hour', justify_days(justify_hours(tenthous * interval '12 minutes')), timetz '01:30:20' + hundred * interval '15 seconds', thousand::bit(10), tenthous::bit(16)::varbit, tenthous::numeric(36,30) * fivethous * even / (hundred + 1), format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, int4range(thousand, twothousand), format('%s/%s%s', odd, even, tenthous)::pg_lsn, box(point(odd, even), point(thousand, twothousand)) FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5; SELECT brin_desummarize_range('brinidx', 0); VACUUM brintest; -- force a summarization cycle in brinidx UPDATE brintest SET int8col = int8col * int4col; UPDATE brintest SET textcol = '' WHERE textcol IS NOT NULL; -- Tests for brin_summarize_new_values SELECT brin_summarize_new_values('brintest'); -- error, not an index SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index SELECT brin_summarize_new_values('brinidx'); -- ok, no change expected -- Tests for brin_desummarize_range SELECT brin_desummarize_range('brinidx', -1); -- error, invalid range SELECT brin_desummarize_range('brinidx', 0); SELECT brin_desummarize_range('brinidx', 0); SELECT brin_desummarize_range('brinidx', 100000000); -- Test brin_summarize_range CREATE TABLE brin_summarize ( value int ) WITH (fillfactor=10, autovacuum_enabled=false); CREATE INDEX brin_summarize_idx ON brin_summarize USING brin (value) WITH (pages_per_range=2); -- Fill a few pages DO $$ DECLARE curtid tid; BEGIN LOOP INSERT INTO brin_summarize VALUES (1) RETURNING ctid INTO curtid; EXIT WHEN curtid > tid '(2, 0)'; END LOOP; END; $$; -- summarize one range SELECT brin_summarize_range('brin_summarize_idx', 0); -- nothing: already summarized SELECT brin_summarize_range('brin_summarize_idx', 1); -- summarize one range SELECT brin_summarize_range('brin_summarize_idx', 2); -- nothing: page doesn't exist in table SELECT brin_summarize_range('brin_summarize_idx', 4294967295); -- invalid block number values SELECT brin_summarize_range('brin_summarize_idx', -1); SELECT brin_summarize_range('brin_summarize_idx', 4294967296); -- test brin cost estimates behave sanely based on correlation of values CREATE TABLE brin_test (a INT, b INT); INSERT INTO brin_test SELECT x/100,x%100 FROM generate_series(1,10000) x(x); CREATE INDEX brin_test_a_idx ON brin_test USING brin (a) WITH (pages_per_range = 2); CREATE INDEX brin_test_b_idx ON brin_test USING brin (b) WITH (pages_per_range = 2); VACUUM ANALYZE brin_test; -- Ensure brin index is used when columns are perfectly correlated EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE a = 1; -- Ensure brin index is not used when values are not correlated EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE b = 1; pgFormatter-4.2/t/pg-test-files/sql/btree_index.sql000066400000000000000000000107671361326045100224230ustar00rootroot00000000000000-- -- BTREE_INDEX -- test retrieval of min/max keys for each index -- SELECT b.* FROM bt_i4_heap b WHERE b.seqno < 1; SELECT b.* FROM bt_i4_heap b WHERE b.seqno >= 9999; SELECT b.* FROM bt_i4_heap b WHERE b.seqno = 4500; SELECT b.* FROM bt_name_heap b WHERE b.seqno < '1'::name; SELECT b.* FROM bt_name_heap b WHERE b.seqno >= '9999'::name; SELECT b.* FROM bt_name_heap b WHERE b.seqno = '4500'::name; SELECT b.* FROM bt_txt_heap b WHERE b.seqno < '1'::text; SELECT b.* FROM bt_txt_heap b WHERE b.seqno >= '9999'::text; SELECT b.* FROM bt_txt_heap b WHERE b.seqno = '4500'::text; SELECT b.* FROM bt_f8_heap b WHERE b.seqno < '1'::float8; SELECT b.* FROM bt_f8_heap b WHERE b.seqno >= '9999'::float8; SELECT b.* FROM bt_f8_heap b WHERE b.seqno = '4500'::float8; -- -- Check correct optimization of LIKE (special index operator support) -- for both indexscan and bitmapscan cases -- set enable_seqscan to false; set enable_indexscan to true; set enable_bitmapscan to false; explain (costs off) select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1; select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1; explain (costs off) select proname from pg_proc where proname ilike '00%foo' order by 1; select proname from pg_proc where proname ilike '00%foo' order by 1; explain (costs off) select proname from pg_proc where proname ilike 'ri%foo' order by 1; set enable_indexscan to false; set enable_bitmapscan to true; explain (costs off) select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1; select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1; explain (costs off) select proname from pg_proc where proname ilike '00%foo' order by 1; select proname from pg_proc where proname ilike '00%foo' order by 1; explain (costs off) select proname from pg_proc where proname ilike 'ri%foo' order by 1; reset enable_seqscan; reset enable_indexscan; reset enable_bitmapscan; -- -- Test B-tree fast path (cache rightmost leaf page) optimization. -- -- First create a tree that's at least three levels deep (i.e. has one level -- between the root and leaf levels). The text inserted is long. It won't be -- compressed because we use plain storage in the table. Only a few index -- tuples fit on each internal page, allowing us to get a tall tree with few -- pages. (A tall tree is required to trigger caching.) -- -- The text column must be the leading column in the index, since suffix -- truncation would otherwise truncate tuples on internal pages, leaving us -- with a short tree. create table btree_tall_tbl(id int4, t text); alter table btree_tall_tbl alter COLUMN t set storage plain; create index btree_tall_idx on btree_tall_tbl (t, id) with (fillfactor = 10); insert into btree_tall_tbl select g, repeat('x', 250) from generate_series(1, 130) g; -- -- Test vacuum_cleanup_index_scale_factor -- -- Simple create create table btree_test(a int); create index btree_idx1 on btree_test(a) with (vacuum_cleanup_index_scale_factor = 40.0); select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass; -- Fail while setting improper values create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_factor = -10.0); create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_factor = 100.0); create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_factor = 'string'); create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_factor = true); -- Simple ALTER INDEX alter index btree_idx1 set (vacuum_cleanup_index_scale_factor = 70.0); select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass; -- -- Test for multilevel page deletion -- CREATE TABLE delete_test_table (a bigint, b bigint, c bigint, d bigint); INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,80000) i; ALTER TABLE delete_test_table ADD PRIMARY KEY (a,b,c,d); -- Delete most entries, and vacuum, deleting internal pages and creating "fast -- root" DELETE FROM delete_test_table WHERE a < 79990; VACUUM delete_test_table; -- -- Test B-tree insertion with a metapage update (XLOG_BTREE_INSERT_META -- WAL record type). This happens when a "fast root" page is split. This -- also creates coverage for nbtree FSM page recycling. -- -- The vacuum above should've turned the leaf page into a fast root. We just -- need to insert some rows to cause the fast root page to split. INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,1000) i; pgFormatter-4.2/t/pg-test-files/sql/case.sql000066400000000000000000000136421361326045100210410ustar00rootroot00000000000000-- -- CASE -- Test the case statement -- CREATE TABLE CASE_TBL ( i integer, f double precision ); CREATE TABLE CASE2_TBL ( i integer, j integer ); INSERT INTO CASE_TBL VALUES (1, 10.1); INSERT INTO CASE_TBL VALUES (2, 20.2); INSERT INTO CASE_TBL VALUES (3, -30.3); INSERT INTO CASE_TBL VALUES (4, NULL); INSERT INTO CASE2_TBL VALUES (1, -1); INSERT INTO CASE2_TBL VALUES (2, -2); INSERT INTO CASE2_TBL VALUES (3, -3); INSERT INTO CASE2_TBL VALUES (2, -4); INSERT INTO CASE2_TBL VALUES (1, NULL); INSERT INTO CASE2_TBL VALUES (NULL, -6); -- -- Simplest examples without tables -- SELECT '3' AS "One", CASE WHEN 1 < 2 THEN 3 END AS "Simple WHEN"; SELECT '' AS "One", CASE WHEN 1 > 2 THEN 3 END AS "Simple default"; SELECT '3' AS "One", CASE WHEN 1 < 2 THEN 3 ELSE 4 END AS "Simple ELSE"; SELECT '4' AS "One", CASE WHEN 1 > 2 THEN 3 ELSE 4 END AS "ELSE default"; SELECT '6' AS "One", CASE WHEN 1 > 2 THEN 3 WHEN 4 < 5 THEN 6 ELSE 7 END AS "Two WHEN with default"; SELECT '7' AS "None", CASE WHEN random() < 0 THEN 1 END AS "NULL on no matches"; -- Constant-expression folding shouldn't evaluate unreachable subexpressions SELECT CASE WHEN 1=0 THEN 1/0 WHEN 1=1 THEN 1 ELSE 2/0 END; SELECT CASE 1 WHEN 0 THEN 1/0 WHEN 1 THEN 1 ELSE 2/0 END; -- However we do not currently suppress folding of potentially -- reachable subexpressions SELECT CASE WHEN i > 100 THEN 1/0 ELSE 0 END FROM case_tbl; -- Test for cases involving untyped literals in test expression SELECT CASE 'a' WHEN 'a' THEN 1 ELSE 2 END; -- -- Examples of targets involving tables -- SELECT '' AS "Five", CASE WHEN i >= 3 THEN i END AS ">= 3 or Null" FROM CASE_TBL; SELECT '' AS "Five", CASE WHEN i >= 3 THEN (i + i) ELSE i END AS "Simplest Math" FROM CASE_TBL; SELECT '' AS "Five", i AS "Value", CASE WHEN (i < 0) THEN 'small' WHEN (i = 0) THEN 'zero' WHEN (i = 1) THEN 'one' WHEN (i = 2) THEN 'two' ELSE 'big' END AS "Category" FROM CASE_TBL; SELECT '' AS "Five", CASE WHEN ((i < 0) or (i < 0)) THEN 'small' WHEN ((i = 0) or (i = 0)) THEN 'zero' WHEN ((i = 1) or (i = 1)) THEN 'one' WHEN ((i = 2) or (i = 2)) THEN 'two' ELSE 'big' END AS "Category" FROM CASE_TBL; -- -- Examples of qualifications involving tables -- -- -- NULLIF() and COALESCE() -- Shorthand forms for typical CASE constructs -- defined in the SQL standard. -- SELECT * FROM CASE_TBL WHERE COALESCE(f,i) = 4; SELECT * FROM CASE_TBL WHERE NULLIF(f,i) = 2; SELECT COALESCE(a.f, b.i, b.j) FROM CASE_TBL a, CASE2_TBL b; SELECT * FROM CASE_TBL a, CASE2_TBL b WHERE COALESCE(a.f, b.i, b.j) = 2; SELECT '' AS Five, NULLIF(a.i,b.i) AS "NULLIF(a.i,b.i)", NULLIF(b.i, 4) AS "NULLIF(b.i,4)" FROM CASE_TBL a, CASE2_TBL b; SELECT '' AS "Two", * FROM CASE_TBL a, CASE2_TBL b WHERE COALESCE(f,b.i) = 2; -- -- Examples of updates involving tables -- UPDATE CASE_TBL SET i = CASE WHEN i >= 3 THEN (- i) ELSE (2 * i) END; SELECT * FROM CASE_TBL; UPDATE CASE_TBL SET i = CASE WHEN i >= 2 THEN (2 * i) ELSE (3 * i) END; SELECT * FROM CASE_TBL; UPDATE CASE_TBL SET i = CASE WHEN b.i >= 2 THEN (2 * j) ELSE (3 * j) END FROM CASE2_TBL b WHERE j = -CASE_TBL.i; SELECT * FROM CASE_TBL; -- -- Nested CASE expressions -- -- This test exercises a bug caused by aliasing econtext->caseValue_isNull -- with the isNull argument of the inner CASE's CaseExpr evaluation. After -- evaluating the vol(null) expression in the inner CASE's second WHEN-clause, -- the isNull flag for the case test value incorrectly became true, causing -- the third WHEN-clause not to match. The volatile function calls are needed -- to prevent constant-folding in the planner, which would hide the bug. -- Wrap this in a single transaction so the transient '=' operator doesn't -- cause problems in concurrent sessions BEGIN; CREATE FUNCTION vol(text) returns text as 'begin return $1; end' language plpgsql volatile; SELECT CASE (CASE vol('bar') WHEN 'foo' THEN 'it was foo!' WHEN vol(null) THEN 'null input' WHEN 'bar' THEN 'it was bar!' END ) WHEN 'it was foo!' THEN 'foo recognized' WHEN 'it was bar!' THEN 'bar recognized' ELSE 'unrecognized' END; -- In this case, we can't inline the SQL function without confusing things. CREATE DOMAIN foodomain AS text; CREATE FUNCTION volfoo(text) returns foodomain as 'begin return $1::foodomain; end' language plpgsql volatile; CREATE FUNCTION inline_eq(foodomain, foodomain) returns boolean as 'SELECT CASE $2::text WHEN $1::text THEN true ELSE false END' language sql; CREATE OPERATOR = (procedure = inline_eq, leftarg = foodomain, rightarg = foodomain); SELECT CASE volfoo('bar') WHEN 'foo'::foodomain THEN 'is foo' ELSE 'is not foo' END; ROLLBACK; -- Test multiple evaluation of a CASE arg that is a read/write object (#14472) -- Wrap this in a single transaction so the transient '=' operator doesn't -- cause problems in concurrent sessions BEGIN; CREATE DOMAIN arrdomain AS int[]; CREATE FUNCTION make_ad(int,int) returns arrdomain as 'declare x arrdomain; begin x := array[$1,$2]; return x; end' language plpgsql volatile; CREATE FUNCTION ad_eq(arrdomain, arrdomain) returns boolean as 'begin return array_eq($1, $2); end' language plpgsql; CREATE OPERATOR = (procedure = ad_eq, leftarg = arrdomain, rightarg = arrdomain); SELECT CASE make_ad(1,2) WHEN array[2,4]::arrdomain THEN 'wrong' WHEN array[2,5]::arrdomain THEN 'still wrong' WHEN array[1,2]::arrdomain THEN 'right' END; ROLLBACK; -- Test interaction of CASE with ArrayCoerceExpr (bug #15471) BEGIN; CREATE TYPE casetestenum AS ENUM ('e', 'f', 'g'); SELECT CASE 'foo'::text WHEN 'foo' THEN ARRAY['a', 'b', 'c', 'd'] || enum_range(NULL::casetestenum)::text[] ELSE ARRAY['x', 'y'] END; ROLLBACK; -- -- Clean up -- DROP TABLE CASE_TBL; DROP TABLE CASE2_TBL; pgFormatter-4.2/t/pg-test-files/sql/char.sql000066400000000000000000000026161361326045100210420ustar00rootroot00000000000000-- -- CHAR -- -- fixed-length by value -- internally passed by value if <= 4 bytes in storage SELECT char 'c' = char 'c' AS true; -- -- Build a table for testing -- CREATE TABLE CHAR_TBL(f1 char); INSERT INTO CHAR_TBL (f1) VALUES ('a'); INSERT INTO CHAR_TBL (f1) VALUES ('A'); -- any of the following three input formats are acceptable INSERT INTO CHAR_TBL (f1) VALUES ('1'); INSERT INTO CHAR_TBL (f1) VALUES (2); INSERT INTO CHAR_TBL (f1) VALUES ('3'); -- zero-length char INSERT INTO CHAR_TBL (f1) VALUES (''); -- try char's of greater than 1 length INSERT INTO CHAR_TBL (f1) VALUES ('cd'); INSERT INTO CHAR_TBL (f1) VALUES ('c '); SELECT '' AS seven, * FROM CHAR_TBL; SELECT '' AS six, c.* FROM CHAR_TBL c WHERE c.f1 <> 'a'; SELECT '' AS one, c.* FROM CHAR_TBL c WHERE c.f1 = 'a'; SELECT '' AS five, c.* FROM CHAR_TBL c WHERE c.f1 < 'a'; SELECT '' AS six, c.* FROM CHAR_TBL c WHERE c.f1 <= 'a'; SELECT '' AS one, c.* FROM CHAR_TBL c WHERE c.f1 > 'a'; SELECT '' AS two, c.* FROM CHAR_TBL c WHERE c.f1 >= 'a'; DROP TABLE CHAR_TBL; -- -- Now test longer arrays of char -- CREATE TABLE CHAR_TBL(f1 char(4)); INSERT INTO CHAR_TBL (f1) VALUES ('a'); INSERT INTO CHAR_TBL (f1) VALUES ('ab'); INSERT INTO CHAR_TBL (f1) VALUES ('abcd'); INSERT INTO CHAR_TBL (f1) VALUES ('abcde'); INSERT INTO CHAR_TBL (f1) VALUES ('abcd '); SELECT '' AS four, * FROM CHAR_TBL; pgFormatter-4.2/t/pg-test-files/sql/circle.sql000066400000000000000000000025441361326045100213660ustar00rootroot00000000000000-- -- CIRCLE -- -- avoid bit-exact output here because operations may not be bit-exact. SET extra_float_digits = 0; CREATE TABLE CIRCLE_TBL (f1 circle); INSERT INTO CIRCLE_TBL VALUES ('<(5,1),3>'); INSERT INTO CIRCLE_TBL VALUES ('<(1,2),100>'); INSERT INTO CIRCLE_TBL VALUES ('1,3,5'); INSERT INTO CIRCLE_TBL VALUES ('((1,2),3)'); INSERT INTO CIRCLE_TBL VALUES ('<(100,200),10>'); INSERT INTO CIRCLE_TBL VALUES (' < ( 100 , 1 ) , 115 > '); INSERT INTO CIRCLE_TBL VALUES ('<(3,5),0>'); -- Zero radius INSERT INTO CIRCLE_TBL VALUES ('<(3,5),NaN>'); -- NaN radius -- bad values INSERT INTO CIRCLE_TBL VALUES ('<(-100,0),-100>'); INSERT INTO CIRCLE_TBL VALUES ('<(100,200),10'); INSERT INTO CIRCLE_TBL VALUES ('<(100,200),10> x'); INSERT INTO CIRCLE_TBL VALUES ('1abc,3,5'); INSERT INTO CIRCLE_TBL VALUES ('(3,(1,2),3)'); SELECT * FROM CIRCLE_TBL; SELECT '' AS six, center(f1) AS center FROM CIRCLE_TBL; SELECT '' AS six, radius(f1) AS radius FROM CIRCLE_TBL; SELECT '' AS six, diameter(f1) AS diameter FROM CIRCLE_TBL; SELECT '' AS two, f1 FROM CIRCLE_TBL WHERE radius(f1) < 5; SELECT '' AS four, f1 FROM CIRCLE_TBL WHERE diameter(f1) >= 10; SELECT '' as five, c1.f1 AS one, c2.f1 AS two, (c1.f1 <-> c2.f1) AS distance FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE (c1.f1 < c2.f1) AND ((c1.f1 <-> c2.f1) > 0) ORDER BY distance, area(c1.f1), area(c2.f1); pgFormatter-4.2/t/pg-test-files/sql/cluster.sql000066400000000000000000000175671361326045100216210ustar00rootroot00000000000000-- -- CLUSTER -- CREATE TABLE clstr_tst_s (rf_a SERIAL PRIMARY KEY, b INT); CREATE TABLE clstr_tst (a SERIAL PRIMARY KEY, b INT, c TEXT, d TEXT, CONSTRAINT clstr_tst_con FOREIGN KEY (b) REFERENCES clstr_tst_s); CREATE INDEX clstr_tst_b ON clstr_tst (b); CREATE INDEX clstr_tst_c ON clstr_tst (c); CREATE INDEX clstr_tst_c_b ON clstr_tst (c,b); CREATE INDEX clstr_tst_b_c ON clstr_tst (b,c); INSERT INTO clstr_tst_s (b) VALUES (0); INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; CREATE TABLE clstr_tst_inh () INHERITS (clstr_tst); INSERT INTO clstr_tst (b, c) VALUES (11, 'once'); INSERT INTO clstr_tst (b, c) VALUES (10, 'diez'); INSERT INTO clstr_tst (b, c) VALUES (31, 'treinta y uno'); INSERT INTO clstr_tst (b, c) VALUES (22, 'veintidos'); INSERT INTO clstr_tst (b, c) VALUES (3, 'tres'); INSERT INTO clstr_tst (b, c) VALUES (20, 'veinte'); INSERT INTO clstr_tst (b, c) VALUES (23, 'veintitres'); INSERT INTO clstr_tst (b, c) VALUES (21, 'veintiuno'); INSERT INTO clstr_tst (b, c) VALUES (4, 'cuatro'); INSERT INTO clstr_tst (b, c) VALUES (14, 'catorce'); INSERT INTO clstr_tst (b, c) VALUES (2, 'dos'); INSERT INTO clstr_tst (b, c) VALUES (18, 'dieciocho'); INSERT INTO clstr_tst (b, c) VALUES (27, 'veintisiete'); INSERT INTO clstr_tst (b, c) VALUES (25, 'veinticinco'); INSERT INTO clstr_tst (b, c) VALUES (13, 'trece'); INSERT INTO clstr_tst (b, c) VALUES (28, 'veintiocho'); INSERT INTO clstr_tst (b, c) VALUES (32, 'treinta y dos'); INSERT INTO clstr_tst (b, c) VALUES (5, 'cinco'); INSERT INTO clstr_tst (b, c) VALUES (29, 'veintinueve'); INSERT INTO clstr_tst (b, c) VALUES (1, 'uno'); INSERT INTO clstr_tst (b, c) VALUES (24, 'veinticuatro'); INSERT INTO clstr_tst (b, c) VALUES (30, 'treinta'); INSERT INTO clstr_tst (b, c) VALUES (12, 'doce'); INSERT INTO clstr_tst (b, c) VALUES (17, 'diecisiete'); INSERT INTO clstr_tst (b, c) VALUES (9, 'nueve'); INSERT INTO clstr_tst (b, c) VALUES (19, 'diecinueve'); INSERT INTO clstr_tst (b, c) VALUES (26, 'veintiseis'); INSERT INTO clstr_tst (b, c) VALUES (15, 'quince'); INSERT INTO clstr_tst (b, c) VALUES (7, 'siete'); INSERT INTO clstr_tst (b, c) VALUES (16, 'dieciseis'); INSERT INTO clstr_tst (b, c) VALUES (8, 'ocho'); -- This entry is needed to test that TOASTED values are copied correctly. INSERT INTO clstr_tst (b, c, d) VALUES (6, 'seis', repeat('xyzzy', 100000)); CLUSTER clstr_tst_c ON clstr_tst; SELECT a,b,c,substring(d for 30), length(d) from clstr_tst; SELECT a,b,c,substring(d for 30), length(d) from clstr_tst ORDER BY a; SELECT a,b,c,substring(d for 30), length(d) from clstr_tst ORDER BY b; SELECT a,b,c,substring(d for 30), length(d) from clstr_tst ORDER BY c; -- Verify that inheritance link still works INSERT INTO clstr_tst_inh VALUES (0, 100, 'in child table'); SELECT a,b,c,substring(d for 30), length(d) from clstr_tst; -- Verify that foreign key link still works INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should fail'); SELECT conname FROM pg_constraint WHERE conrelid = 'clstr_tst'::regclass ORDER BY 1; SELECT relname, relkind, EXISTS(SELECT 1 FROM pg_class WHERE oid = c.reltoastrelid) AS hastoast FROM pg_class c WHERE relname LIKE 'clstr_tst%' ORDER BY relname; -- Verify that indisclustered is correctly set SELECT pg_class.relname FROM pg_index, pg_class, pg_class AS pg_class_2 WHERE pg_class.oid=indexrelid AND indrelid=pg_class_2.oid AND pg_class_2.relname = 'clstr_tst' AND indisclustered; -- Try changing indisclustered ALTER TABLE clstr_tst CLUSTER ON clstr_tst_b_c; SELECT pg_class.relname FROM pg_index, pg_class, pg_class AS pg_class_2 WHERE pg_class.oid=indexrelid AND indrelid=pg_class_2.oid AND pg_class_2.relname = 'clstr_tst' AND indisclustered; -- Try turning off all clustering ALTER TABLE clstr_tst SET WITHOUT CLUSTER; SELECT pg_class.relname FROM pg_index, pg_class, pg_class AS pg_class_2 WHERE pg_class.oid=indexrelid AND indrelid=pg_class_2.oid AND pg_class_2.relname = 'clstr_tst' AND indisclustered; -- Verify that clustering all tables does in fact cluster the right ones CREATE USER regress_clstr_user; CREATE TABLE clstr_1 (a INT PRIMARY KEY); CREATE TABLE clstr_2 (a INT PRIMARY KEY); CREATE TABLE clstr_3 (a INT PRIMARY KEY); ALTER TABLE clstr_1 OWNER TO regress_clstr_user; ALTER TABLE clstr_3 OWNER TO regress_clstr_user; GRANT SELECT ON clstr_2 TO regress_clstr_user; INSERT INTO clstr_1 VALUES (2); INSERT INTO clstr_1 VALUES (1); INSERT INTO clstr_2 VALUES (2); INSERT INTO clstr_2 VALUES (1); INSERT INTO clstr_3 VALUES (2); INSERT INTO clstr_3 VALUES (1); -- "CLUSTER " on a table that hasn't been clustered CLUSTER clstr_2; CLUSTER clstr_1_pkey ON clstr_1; CLUSTER clstr_2 USING clstr_2_pkey; SELECT * FROM clstr_1 UNION ALL SELECT * FROM clstr_2 UNION ALL SELECT * FROM clstr_3; -- revert to the original state DELETE FROM clstr_1; DELETE FROM clstr_2; DELETE FROM clstr_3; INSERT INTO clstr_1 VALUES (2); INSERT INTO clstr_1 VALUES (1); INSERT INTO clstr_2 VALUES (2); INSERT INTO clstr_2 VALUES (1); INSERT INTO clstr_3 VALUES (2); INSERT INTO clstr_3 VALUES (1); -- this user can only cluster clstr_1 and clstr_3, but the latter -- has not been clustered SET SESSION AUTHORIZATION regress_clstr_user; CLUSTER; SELECT * FROM clstr_1 UNION ALL SELECT * FROM clstr_2 UNION ALL SELECT * FROM clstr_3; -- cluster a single table using the indisclustered bit previously set DELETE FROM clstr_1; INSERT INTO clstr_1 VALUES (2); INSERT INTO clstr_1 VALUES (1); CLUSTER clstr_1; SELECT * FROM clstr_1; -- Test MVCC-safety of cluster. There isn't much we can do to verify the -- results with a single backend... CREATE TABLE clustertest (key int PRIMARY KEY); INSERT INTO clustertest VALUES (10); INSERT INTO clustertest VALUES (20); INSERT INTO clustertest VALUES (30); INSERT INTO clustertest VALUES (40); INSERT INTO clustertest VALUES (50); -- Use a transaction so that updates are not committed when CLUSTER sees 'em BEGIN; -- Test update where the old row version is found first in the scan UPDATE clustertest SET key = 100 WHERE key = 10; -- Test update where the new row version is found first in the scan UPDATE clustertest SET key = 35 WHERE key = 40; -- Test longer update chain UPDATE clustertest SET key = 60 WHERE key = 50; UPDATE clustertest SET key = 70 WHERE key = 60; UPDATE clustertest SET key = 80 WHERE key = 70; SELECT * FROM clustertest; CLUSTER clustertest_pkey ON clustertest; SELECT * FROM clustertest; COMMIT; SELECT * FROM clustertest; -- check that temp tables can be clustered create temp table clstr_temp (col1 int primary key, col2 text); insert into clstr_temp values (2, 'two'), (1, 'one'); cluster clstr_temp using clstr_temp_pkey; select * from clstr_temp; drop table clstr_temp; RESET SESSION AUTHORIZATION; -- Check that partitioned tables cannot be clustered CREATE TABLE clstrpart (a int) PARTITION BY RANGE (a); CREATE INDEX clstrpart_idx ON clstrpart (a); ALTER TABLE clstrpart CLUSTER ON clstrpart_idx; CLUSTER clstrpart USING clstrpart_idx; DROP TABLE clstrpart; -- Test CLUSTER with external tuplesorting create table clstr_4 as select * from tenk1; create index cluster_sort on clstr_4 (hundred, thousand, tenthous); -- ensure we don't use the index in CLUSTER nor the checking SELECTs set enable_indexscan = off; -- Use external sort: set maintenance_work_mem = '1MB'; cluster clstr_4 using cluster_sort; select * from (select hundred, lag(hundred) over () as lhundred, thousand, lag(thousand) over () as lthousand, tenthous, lag(tenthous) over () as ltenthous from clstr_4) ss where row(hundred, thousand, tenthous) <= row(lhundred, lthousand, ltenthous); reset enable_indexscan; reset maintenance_work_mem; -- clean up DROP TABLE clustertest; DROP TABLE clstr_1; DROP TABLE clstr_2; DROP TABLE clstr_3; DROP TABLE clstr_4; DROP USER regress_clstr_user; pgFormatter-4.2/t/pg-test-files/sql/collate.icu.utf8.sql000066400000000000000000000665271361326045100232270ustar00rootroot00000000000000/* * This test is for ICU collations. */ SET client_encoding TO UTF8; CREATE SCHEMA collate_tests; SET search_path = collate_tests; CREATE TABLE collate_test1 ( a int, b text COLLATE "en-x-icu" NOT NULL ); \d collate_test1 CREATE TABLE collate_test_fail ( a int, b text COLLATE "ja_JP.eucjp-x-icu" ); CREATE TABLE collate_test_fail ( a int, b text COLLATE "foo-x-icu" ); CREATE TABLE collate_test_fail ( a int COLLATE "en-x-icu", b text ); CREATE TABLE collate_test_like ( LIKE collate_test1 ); \d collate_test_like CREATE TABLE collate_test2 ( a int, b text COLLATE "sv-x-icu" ); CREATE TABLE collate_test3 ( a int, b text COLLATE "C" ); INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); INSERT INTO collate_test2 SELECT * FROM collate_test1; INSERT INTO collate_test3 SELECT * FROM collate_test1; SELECT * FROM collate_test1 WHERE b >= 'bbc'; SELECT * FROM collate_test2 WHERE b >= 'bbc'; SELECT * FROM collate_test3 WHERE b >= 'bbc'; SELECT * FROM collate_test3 WHERE b >= 'BBC'; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en-x-icu"; CREATE DOMAIN testdomain_sv AS text COLLATE "sv-x-icu"; CREATE DOMAIN testdomain_i AS int COLLATE "sv-x-icu"; -- fails CREATE TABLE collate_test4 ( a int, b testdomain_sv ); INSERT INTO collate_test4 SELECT * FROM collate_test1; SELECT a, b FROM collate_test4 ORDER BY b; CREATE TABLE collate_test5 ( a int, b testdomain_sv COLLATE "en-x-icu" ); INSERT INTO collate_test5 SELECT * FROM collate_test1; SELECT a, b FROM collate_test5 ORDER BY b; SELECT a, b FROM collate_test1 ORDER BY b; SELECT a, b FROM collate_test2 ORDER BY b; SELECT a, b FROM collate_test3 ORDER BY b; SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; -- star expansion SELECT * FROM collate_test1 ORDER BY b; SELECT * FROM collate_test2 ORDER BY b; SELECT * FROM collate_test3 ORDER BY b; -- constant expression folding SELECT 'bbc' COLLATE "en-x-icu" > 'äbc' COLLATE "en-x-icu" AS "true"; SELECT 'bbc' COLLATE "sv-x-icu" > 'äbc' COLLATE "sv-x-icu" AS "false"; -- upper/lower CREATE TABLE collate_test10 ( a int, x text COLLATE "en-x-icu", y text COLLATE "tr-x-icu" ); INSERT INTO collate_test10 VALUES (1, 'hij', 'hij'), (2, 'HIJ', 'HIJ'); SELECT a, lower(x), lower(y), upper(x), upper(y), initcap(x), initcap(y) FROM collate_test10; SELECT a, lower(x COLLATE "C"), lower(y COLLATE "C") FROM collate_test10; SELECT a, x, y FROM collate_test10 ORDER BY lower(y), a; -- LIKE/ILIKE SELECT * FROM collate_test1 WHERE b LIKE 'abc'; SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; SELECT 'Türkiye' COLLATE "en-x-icu" ILIKE '%KI%' AS "true"; SELECT 'Türkiye' COLLATE "tr-x-icu" ILIKE '%KI%' AS "false"; SELECT 'bıt' ILIKE 'BIT' COLLATE "en-x-icu" AS "false"; SELECT 'bıt' ILIKE 'BIT' COLLATE "tr-x-icu" AS "true"; -- The following actually exercises the selectivity estimation for ILIKE. SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; -- regular expressions SELECT * FROM collate_test1 WHERE b ~ '^abc$'; SELECT * FROM collate_test1 WHERE b ~ '^abc'; SELECT * FROM collate_test1 WHERE b ~ 'bc'; SELECT * FROM collate_test1 WHERE b ~* '^abc$'; SELECT * FROM collate_test1 WHERE b ~* '^abc'; SELECT * FROM collate_test1 WHERE b ~* 'bc'; CREATE TABLE collate_test6 ( a int, b text COLLATE "en-x-icu" ); INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), (9, 'äbç'), (10, 'ÄBÇ'); SELECT b, b ~ '^[[:alpha:]]+$' AS is_alpha, b ~ '^[[:upper:]]+$' AS is_upper, b ~ '^[[:lower:]]+$' AS is_lower, b ~ '^[[:digit:]]+$' AS is_digit, b ~ '^[[:alnum:]]+$' AS is_alnum, b ~ '^[[:graph:]]+$' AS is_graph, b ~ '^[[:print:]]+$' AS is_print, b ~ '^[[:punct:]]+$' AS is_punct, b ~ '^[[:space:]]+$' AS is_space FROM collate_test6; SELECT 'Türkiye' COLLATE "en-x-icu" ~* 'KI' AS "true"; SELECT 'Türkiye' COLLATE "tr-x-icu" ~* 'KI' AS "true"; -- true with ICU SELECT 'bıt' ~* 'BIT' COLLATE "en-x-icu" AS "false"; SELECT 'bıt' ~* 'BIT' COLLATE "tr-x-icu" AS "false"; -- false with ICU -- The following actually exercises the selectivity estimation for ~*. SELECT relname FROM pg_class WHERE relname ~* '^abc'; /* not run by default because it requires tr_TR system locale -- to_char SET lc_time TO 'tr_TR'; SELECT to_char(date '2010-04-01', 'DD TMMON YYYY'); SELECT to_char(date '2010-04-01', 'DD TMMON YYYY' COLLATE "tr-x-icu"); */ -- backwards parsing CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; -- collation propagation in various expression types SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; SELECT a, lower(coalesce(x, 'foo')), lower(coalesce(y, 'foo')) FROM collate_test10; SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; SELECT a, x, y, lower(greatest(x, 'foo')), lower(greatest(y, 'foo')) FROM collate_test10; SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; SELECT a, lower(nullif(x, 'foo')), lower(nullif(y, 'foo')) FROM collate_test10; SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; CREATE DOMAIN testdomain AS text; SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; SELECT a, lower(x::testdomain), lower(y::testdomain) FROM collate_test10; SELECT min(b), max(b) FROM collate_test1; SELECT min(b), max(b) FROM collate_test2; SELECT min(b), max(b) FROM collate_test3; SELECT array_agg(b ORDER BY b) FROM collate_test1; SELECT array_agg(b ORDER BY b) FROM collate_test2; SELECT array_agg(b ORDER BY b) FROM collate_test3; SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail -- ideally this would be a parse-time error, but for now it must be run-time: select x < y from collate_test10; -- fail select x || y from collate_test10; -- ok, because || is not collation aware select x, y from collate_test10 order by x || y; -- not so ok -- collation mismatch between recursive and non-recursive term WITH RECURSIVE foo(x) AS (SELECT x FROM (VALUES('a' COLLATE "en-x-icu"),('b')) t(x) UNION ALL SELECT (x || 'c') COLLATE "de-x-icu" FROM foo WHERE length(x) < 10) SELECT * FROM foo; -- casting SELECT CAST('42' AS text COLLATE "C"); SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; -- propagation of collation in SQL functions (inlined and non-inlined cases) -- and plpgsql functions too CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql AS $$ select $1 < $2 $$; CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql AS $$ select $1 < $2 limit 1 $$; CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql AS $$ begin return $1 < $2; end $$; SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) FROM collate_test1 a, collate_test1 b ORDER BY a.b, b.b; SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), mylt_plpgsql(a.b, b.b COLLATE "C") FROM collate_test1 a, collate_test1 b ORDER BY a.b, b.b; -- collation override in plpgsql CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ declare xx text := x; yy text := y; begin return xx < yy; end $$; SELECT mylt2('a', 'B' collate "en-x-icu") as t, mylt2('a', 'B' collate "C") as f; CREATE OR REPLACE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ declare xx text COLLATE "POSIX" := x; yy text := y; begin return xx < yy; end $$; SELECT mylt2('a', 'B') as f; SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations SELECT mylt2('a', 'B' collate "POSIX") as f; -- polymorphism SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; CREATE FUNCTION dup (anyelement) RETURNS anyelement AS 'select $1' LANGUAGE sql; SELECT a, dup(b) FROM collate_test1 ORDER BY 2; SELECT a, dup(b) FROM collate_test2 ORDER BY 2; SELECT a, dup(b) FROM collate_test3 ORDER BY 2; -- indexes CREATE INDEX collate_test1_idx1 ON collate_test1 (b); CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; -- schema manipulation commands CREATE ROLE regress_test_role; CREATE SCHEMA test_schema; -- We need to do this this way to cope with varying names for encodings: do $$ BEGIN EXECUTE 'CREATE COLLATION test0 (provider = icu, locale = ' || quote_literal(current_setting('lc_collate')) || ');'; END $$; CREATE COLLATION test0 FROM "C"; -- fail, duplicate name do $$ BEGIN EXECUTE 'CREATE COLLATION test1 (provider = icu, lc_collate = ' || quote_literal(current_setting('lc_collate')) || ', lc_ctype = ' || quote_literal(current_setting('lc_ctype')) || ');'; END $$; CREATE COLLATION test3 (provider = icu, lc_collate = 'en_US.utf8'); -- fail, need lc_ctype CREATE COLLATION testx (provider = icu, locale = 'nonsense'); /* never fails with ICU */ DROP COLLATION testx; CREATE COLLATION test4 FROM nonsense; CREATE COLLATION test5 FROM test0; SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; ALTER COLLATION test1 RENAME TO test11; ALTER COLLATION test0 RENAME TO test11; -- fail ALTER COLLATION test1 RENAME TO test22; -- fail ALTER COLLATION test11 OWNER TO regress_test_role; ALTER COLLATION test11 OWNER TO nonsense; ALTER COLLATION test11 SET SCHEMA test_schema; COMMENT ON COLLATION test0 IS 'US English'; SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) WHERE collname LIKE 'test%' ORDER BY 1; DROP COLLATION test0, test_schema.test11, test5; DROP COLLATION test0; -- fail DROP COLLATION IF EXISTS test0; SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; DROP SCHEMA test_schema; DROP ROLE regress_test_role; -- ALTER ALTER COLLATION "en-x-icu" REFRESH VERSION; -- dependencies CREATE COLLATION test0 FROM "C"; CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; CREATE TABLE collate_dep_test4t (a int, b text); CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); DROP COLLATION test0 RESTRICT; -- fail DROP COLLATION test0 CASCADE; \d collate_dep_test1 \d collate_dep_test2 DROP TABLE collate_dep_test1, collate_dep_test4t; DROP TYPE collate_dep_test2; -- test range types and collations create type textrange_c as range(subtype=text, collation="C"); create type textrange_en_us as range(subtype=text, collation="en-x-icu"); select textrange_c('A','Z') @> 'b'::text; select textrange_en_us('A','Z') @> 'b'::text; drop type textrange_c; drop type textrange_en_us; -- test ICU collation customization -- test the attributes handled by icu_set_collation_attributes() CREATE COLLATION testcoll_ignore_accents (provider = icu, locale = '@colStrength=primary;colCaseLevel=yes'); SELECT 'aaá' > 'AAA' COLLATE "und-x-icu", 'aaá' < 'AAA' COLLATE testcoll_ignore_accents; CREATE COLLATION testcoll_backwards (provider = icu, locale = '@colBackwards=yes'); SELECT 'coté' < 'côte' COLLATE "und-x-icu", 'coté' > 'côte' COLLATE testcoll_backwards; CREATE COLLATION testcoll_lower_first (provider = icu, locale = '@colCaseFirst=lower'); CREATE COLLATION testcoll_upper_first (provider = icu, locale = '@colCaseFirst=upper'); SELECT 'aaa' < 'AAA' COLLATE testcoll_lower_first, 'aaa' > 'AAA' COLLATE testcoll_upper_first; CREATE COLLATION testcoll_shifted (provider = icu, locale = '@colAlternate=shifted'); SELECT 'de-luge' < 'deanza' COLLATE "und-x-icu", 'de-luge' > 'deanza' COLLATE testcoll_shifted; CREATE COLLATION testcoll_numeric (provider = icu, locale = '@colNumeric=yes'); SELECT 'A-21' > 'A-123' COLLATE "und-x-icu", 'A-21' < 'A-123' COLLATE testcoll_numeric; CREATE COLLATION testcoll_error1 (provider = icu, locale = '@colNumeric=lower'); -- test that attributes not handled by icu_set_collation_attributes() -- (handled by ucol_open() directly) also work CREATE COLLATION testcoll_de_phonebook (provider = icu, locale = 'de@collation=phonebook'); SELECT 'Goldmann' < 'Götz' COLLATE "de-x-icu", 'Goldmann' > 'Götz' COLLATE testcoll_de_phonebook; -- nondeterministic collations CREATE COLLATION ctest_det (provider = icu, locale = '', deterministic = true); CREATE COLLATION ctest_nondet (provider = icu, locale = '', deterministic = false); CREATE TABLE test6 (a int, b text); -- same string in different normal forms INSERT INTO test6 VALUES (1, U&'\00E4bc'); INSERT INTO test6 VALUES (2, U&'\0061\0308bc'); SELECT * FROM test6; SELECT * FROM test6 WHERE b = 'äbc' COLLATE ctest_det; SELECT * FROM test6 WHERE b = 'äbc' COLLATE ctest_nondet; CREATE COLLATION case_sensitive (provider = icu, locale = ''); CREATE COLLATION case_insensitive (provider = icu, locale = '@colStrength=secondary', deterministic = false); SELECT 'abc' <= 'ABC' COLLATE case_sensitive, 'abc' >= 'ABC' COLLATE case_sensitive; SELECT 'abc' <= 'ABC' COLLATE case_insensitive, 'abc' >= 'ABC' COLLATE case_insensitive; CREATE TABLE test1cs (x text COLLATE case_sensitive); CREATE TABLE test2cs (x text COLLATE case_sensitive); CREATE TABLE test3cs (x text COLLATE case_sensitive); INSERT INTO test1cs VALUES ('abc'), ('def'), ('ghi'); INSERT INTO test2cs VALUES ('ABC'), ('ghi'); INSERT INTO test3cs VALUES ('abc'), ('ABC'), ('def'), ('ghi'); SELECT x FROM test3cs WHERE x = 'abc'; SELECT x FROM test3cs WHERE x <> 'abc'; SELECT x FROM test3cs WHERE x LIKE 'a%'; SELECT x FROM test3cs WHERE x ILIKE 'a%'; SELECT x FROM test3cs WHERE x SIMILAR TO 'a%'; SELECT x FROM test3cs WHERE x ~ 'a'; SELECT x FROM test1cs UNION SELECT x FROM test2cs ORDER BY x; SELECT x FROM test2cs UNION SELECT x FROM test1cs ORDER BY x; SELECT x FROM test1cs INTERSECT SELECT x FROM test2cs; SELECT x FROM test2cs INTERSECT SELECT x FROM test1cs; SELECT x FROM test1cs EXCEPT SELECT x FROM test2cs; SELECT x FROM test2cs EXCEPT SELECT x FROM test1cs; SELECT DISTINCT x FROM test3cs ORDER BY x; SELECT count(DISTINCT x) FROM test3cs; SELECT x, count(*) FROM test3cs GROUP BY x ORDER BY x; SELECT x, row_number() OVER (ORDER BY x), rank() OVER (ORDER BY x) FROM test3cs ORDER BY x; CREATE UNIQUE INDEX ON test1cs (x); -- ok INSERT INTO test1cs VALUES ('ABC'); -- ok CREATE UNIQUE INDEX ON test3cs (x); -- ok SELECT string_to_array('ABC,DEF,GHI' COLLATE case_sensitive, ',', 'abc'); SELECT string_to_array('ABCDEFGHI' COLLATE case_sensitive, NULL, 'b'); CREATE TABLE test1ci (x text COLLATE case_insensitive); CREATE TABLE test2ci (x text COLLATE case_insensitive); CREATE TABLE test3ci (x text COLLATE case_insensitive); CREATE INDEX ON test3ci (x text_pattern_ops); -- error INSERT INTO test1ci VALUES ('abc'), ('def'), ('ghi'); INSERT INTO test2ci VALUES ('ABC'), ('ghi'); INSERT INTO test3ci VALUES ('abc'), ('ABC'), ('def'), ('ghi'); SELECT x FROM test3ci WHERE x = 'abc'; SELECT x FROM test3ci WHERE x <> 'abc'; SELECT x FROM test3ci WHERE x LIKE 'a%'; SELECT x FROM test3ci WHERE x ILIKE 'a%'; SELECT x FROM test3ci WHERE x SIMILAR TO 'a%'; SELECT x FROM test3ci WHERE x ~ 'a'; SELECT x FROM test1ci UNION SELECT x FROM test2ci ORDER BY x; SELECT x FROM test2ci UNION SELECT x FROM test1ci ORDER BY x; SELECT x FROM test1ci INTERSECT SELECT x FROM test2ci ORDER BY x; SELECT x FROM test2ci INTERSECT SELECT x FROM test1ci ORDER BY x; SELECT x FROM test1ci EXCEPT SELECT x FROM test2ci; SELECT x FROM test2ci EXCEPT SELECT x FROM test1ci; SELECT DISTINCT x FROM test3ci ORDER BY x; SELECT count(DISTINCT x) FROM test3ci; SELECT x, count(*) FROM test3ci GROUP BY x ORDER BY x; SELECT x, row_number() OVER (ORDER BY x), rank() OVER (ORDER BY x) FROM test3ci ORDER BY x; CREATE UNIQUE INDEX ON test1ci (x); -- ok INSERT INTO test1ci VALUES ('ABC'); -- error CREATE UNIQUE INDEX ON test3ci (x); -- error SELECT string_to_array('ABC,DEF,GHI' COLLATE case_insensitive, ',', 'abc'); SELECT string_to_array('ABCDEFGHI' COLLATE case_insensitive, NULL, 'b'); -- bpchar CREATE TABLE test1bpci (x char(3) COLLATE case_insensitive); CREATE TABLE test2bpci (x char(3) COLLATE case_insensitive); CREATE TABLE test3bpci (x char(3) COLLATE case_insensitive); CREATE INDEX ON test3bpci (x bpchar_pattern_ops); -- error INSERT INTO test1bpci VALUES ('abc'), ('def'), ('ghi'); INSERT INTO test2bpci VALUES ('ABC'), ('ghi'); INSERT INTO test3bpci VALUES ('abc'), ('ABC'), ('def'), ('ghi'); SELECT x FROM test3bpci WHERE x = 'abc'; SELECT x FROM test3bpci WHERE x <> 'abc'; SELECT x FROM test3bpci WHERE x LIKE 'a%'; SELECT x FROM test3bpci WHERE x ILIKE 'a%'; SELECT x FROM test3bpci WHERE x SIMILAR TO 'a%'; SELECT x FROM test3bpci WHERE x ~ 'a'; SELECT x FROM test1bpci UNION SELECT x FROM test2bpci ORDER BY x; SELECT x FROM test2bpci UNION SELECT x FROM test1bpci ORDER BY x; SELECT x FROM test1bpci INTERSECT SELECT x FROM test2bpci ORDER BY x; SELECT x FROM test2bpci INTERSECT SELECT x FROM test1bpci ORDER BY x; SELECT x FROM test1bpci EXCEPT SELECT x FROM test2bpci; SELECT x FROM test2bpci EXCEPT SELECT x FROM test1bpci; SELECT DISTINCT x FROM test3bpci ORDER BY x; SELECT count(DISTINCT x) FROM test3bpci; SELECT x, count(*) FROM test3bpci GROUP BY x ORDER BY x; SELECT x, row_number() OVER (ORDER BY x), rank() OVER (ORDER BY x) FROM test3bpci ORDER BY x; CREATE UNIQUE INDEX ON test1bpci (x); -- ok INSERT INTO test1bpci VALUES ('ABC'); -- error CREATE UNIQUE INDEX ON test3bpci (x); -- error SELECT string_to_array('ABC,DEF,GHI'::char(11) COLLATE case_insensitive, ',', 'abc'); SELECT string_to_array('ABCDEFGHI'::char(9) COLLATE case_insensitive, NULL, 'b'); -- This tests the issue described in match_pattern_prefix(). In the -- absence of that check, the case_insensitive tests below would -- return no rows where they should logically return one. CREATE TABLE test4c (x text COLLATE "C"); INSERT INTO test4c VALUES ('abc'); CREATE INDEX ON test4c (x); SET enable_seqscan = off; SELECT x FROM test4c WHERE x LIKE 'ABC' COLLATE case_sensitive; -- ok, no rows SELECT x FROM test4c WHERE x LIKE 'ABC%' COLLATE case_sensitive; -- ok, no rows SELECT x FROM test4c WHERE x LIKE 'ABC' COLLATE case_insensitive; -- error SELECT x FROM test4c WHERE x LIKE 'ABC%' COLLATE case_insensitive; -- error RESET enable_seqscan; -- Unicode special case: different variants of Greek lower case sigma. -- A naive implementation like citext that just does lower(x) = -- lower(y) will do the wrong thing here, because lower('Σ') is 'σ' -- but upper('ς') is 'Σ'. SELECT 'ὀδυσσεύς' = 'ὈΔΥΣΣΕΎΣ' COLLATE case_sensitive; SELECT 'ὀδυσσεύς' = 'ὈΔΥΣΣΕΎΣ' COLLATE case_insensitive; -- name vs. text comparison operators SELECT relname FROM pg_class WHERE relname = 'PG_CLASS'::text COLLATE case_insensitive; SELECT relname FROM pg_class WHERE 'PG_CLASS'::text = relname COLLATE case_insensitive; SELECT typname FROM pg_type WHERE typname LIKE 'int_' AND typname <> 'INT2'::text COLLATE case_insensitive; SELECT typname FROM pg_type WHERE typname LIKE 'int_' AND 'INT2'::text <> typname COLLATE case_insensitive;; -- test case adapted from subselect.sql CREATE TEMP TABLE outer_text (f1 text COLLATE case_insensitive, f2 text); INSERT INTO outer_text VALUES ('a', 'a'); INSERT INTO outer_text VALUES ('b', 'a'); INSERT INTO outer_text VALUES ('A', NULL); INSERT INTO outer_text VALUES ('B', NULL); CREATE TEMP TABLE inner_text (c1 text COLLATE case_insensitive, c2 text); INSERT INTO inner_text VALUES ('a', NULL); SELECT * FROM outer_text WHERE (f1, f2) NOT IN (SELECT * FROM inner_text); -- accents CREATE COLLATION ignore_accents (provider = icu, locale = '@colStrength=primary;colCaseLevel=yes', deterministic = false); CREATE TABLE test4 (a int, b text); INSERT INTO test4 VALUES (1, 'cote'), (2, 'côte'), (3, 'coté'), (4, 'côté'); SELECT * FROM test4 WHERE b = 'cote'; SELECT * FROM test4 WHERE b = 'cote' COLLATE ignore_accents; SELECT * FROM test4 WHERE b = 'Cote' COLLATE ignore_accents; -- still case-sensitive SELECT * FROM test4 WHERE b = 'Cote' COLLATE case_insensitive; -- foreign keys (should use collation of primary key) -- PK is case-sensitive, FK is case-insensitive CREATE TABLE test10pk (x text COLLATE case_sensitive PRIMARY KEY); INSERT INTO test10pk VALUES ('abc'), ('def'), ('ghi'); CREATE TABLE test10fk (x text COLLATE case_insensitive REFERENCES test10pk (x) ON UPDATE CASCADE ON DELETE CASCADE); INSERT INTO test10fk VALUES ('abc'); -- ok INSERT INTO test10fk VALUES ('ABC'); -- error INSERT INTO test10fk VALUES ('xyz'); -- error SELECT * FROM test10pk; SELECT * FROM test10fk; -- restrict update even though the values are "equal" in the FK table UPDATE test10fk SET x = 'ABC' WHERE x = 'abc'; -- error SELECT * FROM test10fk; DELETE FROM test10pk WHERE x = 'abc'; SELECT * FROM test10pk; SELECT * FROM test10fk; -- PK is case-insensitive, FK is case-sensitive CREATE TABLE test11pk (x text COLLATE case_insensitive PRIMARY KEY); INSERT INTO test11pk VALUES ('abc'), ('def'), ('ghi'); CREATE TABLE test11fk (x text COLLATE case_sensitive REFERENCES test11pk (x) ON UPDATE CASCADE ON DELETE CASCADE); INSERT INTO test11fk VALUES ('abc'); -- ok INSERT INTO test11fk VALUES ('ABC'); -- ok INSERT INTO test11fk VALUES ('xyz'); -- error SELECT * FROM test11pk; SELECT * FROM test11fk; -- cascade update even though the values are "equal" in the PK table UPDATE test11pk SET x = 'ABC' WHERE x = 'abc'; SELECT * FROM test11fk; DELETE FROM test11pk WHERE x = 'abc'; SELECT * FROM test11pk; SELECT * FROM test11fk; -- partitioning CREATE TABLE test20 (a int, b text COLLATE case_insensitive) PARTITION BY LIST (b); CREATE TABLE test20_1 PARTITION OF test20 FOR VALUES IN ('abc'); INSERT INTO test20 VALUES (1, 'abc'); INSERT INTO test20 VALUES (2, 'ABC'); SELECT * FROM test20_1; CREATE TABLE test21 (a int, b text COLLATE case_insensitive) PARTITION BY RANGE (b); CREATE TABLE test21_1 PARTITION OF test21 FOR VALUES FROM ('ABC') TO ('DEF'); INSERT INTO test21 VALUES (1, 'abc'); INSERT INTO test21 VALUES (2, 'ABC'); SELECT * FROM test21_1; CREATE TABLE test22 (a int, b text COLLATE case_sensitive) PARTITION BY HASH (b); CREATE TABLE test22_0 PARTITION OF test22 FOR VALUES WITH (MODULUS 2, REMAINDER 0); CREATE TABLE test22_1 PARTITION OF test22 FOR VALUES WITH (MODULUS 2, REMAINDER 1); INSERT INTO test22 VALUES (1, 'def'); INSERT INTO test22 VALUES (2, 'DEF'); -- they end up in different partitions SELECT (SELECT count(*) FROM test22_0) = (SELECT count(*) FROM test22_1); CREATE TABLE test23 (a int, b text COLLATE case_insensitive) PARTITION BY HASH (b); CREATE TABLE test23_0 PARTITION OF test23 FOR VALUES WITH (MODULUS 2, REMAINDER 0); CREATE TABLE test23_1 PARTITION OF test23 FOR VALUES WITH (MODULUS 2, REMAINDER 1); INSERT INTO test23 VALUES (1, 'def'); INSERT INTO test23 VALUES (2, 'DEF'); -- they end up in the same partition (but it's platform-dependent which one) SELECT (SELECT count(*) FROM test23_0) <> (SELECT count(*) FROM test23_1); CREATE TABLE test30 (a int, b char(3) COLLATE case_insensitive) PARTITION BY LIST (b); CREATE TABLE test30_1 PARTITION OF test30 FOR VALUES IN ('abc'); INSERT INTO test30 VALUES (1, 'abc'); INSERT INTO test30 VALUES (2, 'ABC'); SELECT * FROM test30_1; CREATE TABLE test31 (a int, b char(3) COLLATE case_insensitive) PARTITION BY RANGE (b); CREATE TABLE test31_1 PARTITION OF test31 FOR VALUES FROM ('ABC') TO ('DEF'); INSERT INTO test31 VALUES (1, 'abc'); INSERT INTO test31 VALUES (2, 'ABC'); SELECT * FROM test31_1; CREATE TABLE test32 (a int, b char(3) COLLATE case_sensitive) PARTITION BY HASH (b); CREATE TABLE test32_0 PARTITION OF test32 FOR VALUES WITH (MODULUS 2, REMAINDER 0); CREATE TABLE test32_1 PARTITION OF test32 FOR VALUES WITH (MODULUS 2, REMAINDER 1); INSERT INTO test32 VALUES (1, 'def'); INSERT INTO test32 VALUES (2, 'DEF'); -- they end up in different partitions SELECT (SELECT count(*) FROM test32_0) = (SELECT count(*) FROM test32_1); CREATE TABLE test33 (a int, b char(3) COLLATE case_insensitive) PARTITION BY HASH (b); CREATE TABLE test33_0 PARTITION OF test33 FOR VALUES WITH (MODULUS 2, REMAINDER 0); CREATE TABLE test33_1 PARTITION OF test33 FOR VALUES WITH (MODULUS 2, REMAINDER 1); INSERT INTO test33 VALUES (1, 'def'); INSERT INTO test33 VALUES (2, 'DEF'); -- they end up in the same partition (but it's platform-dependent which one) SELECT (SELECT count(*) FROM test33_0) <> (SELECT count(*) FROM test33_1); -- cleanup SET client_min_messages TO warning; DROP SCHEMA collate_tests CASCADE; RESET search_path; -- leave a collation for pg_upgrade test CREATE COLLATION coll_icu_upgrade FROM "und-x-icu"; pgFormatter-4.2/t/pg-test-files/sql/collate.linux.utf8.sql000066400000000000000000000344411361326045100235740ustar00rootroot00000000000000/* * This test is for Linux/glibc systems and assumes that a full set of * locales is installed. It must be run in a database with UTF-8 encoding, * because other encodings don't support all the characters used. */ SET client_encoding TO UTF8; CREATE SCHEMA collate_tests; SET search_path = collate_tests; CREATE TABLE collate_test1 ( a int, b text COLLATE "en_US" NOT NULL ); \d collate_test1 CREATE TABLE collate_test_fail ( a int, b text COLLATE "ja_JP.eucjp" ); CREATE TABLE collate_test_fail ( a int, b text COLLATE "foo" ); CREATE TABLE collate_test_fail ( a int COLLATE "en_US", b text ); CREATE TABLE collate_test_like ( LIKE collate_test1 ); \d collate_test_like CREATE TABLE collate_test2 ( a int, b text COLLATE "sv_SE" ); CREATE TABLE collate_test3 ( a int, b text COLLATE "C" ); INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); INSERT INTO collate_test2 SELECT * FROM collate_test1; INSERT INTO collate_test3 SELECT * FROM collate_test1; SELECT * FROM collate_test1 WHERE b >= 'bbc'; SELECT * FROM collate_test2 WHERE b >= 'bbc'; SELECT * FROM collate_test3 WHERE b >= 'bbc'; SELECT * FROM collate_test3 WHERE b >= 'BBC'; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails CREATE TABLE collate_test4 ( a int, b testdomain_sv ); INSERT INTO collate_test4 SELECT * FROM collate_test1; SELECT a, b FROM collate_test4 ORDER BY b; CREATE TABLE collate_test5 ( a int, b testdomain_sv COLLATE "en_US" ); INSERT INTO collate_test5 SELECT * FROM collate_test1; SELECT a, b FROM collate_test5 ORDER BY b; SELECT a, b FROM collate_test1 ORDER BY b; SELECT a, b FROM collate_test2 ORDER BY b; SELECT a, b FROM collate_test3 ORDER BY b; SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; -- star expansion SELECT * FROM collate_test1 ORDER BY b; SELECT * FROM collate_test2 ORDER BY b; SELECT * FROM collate_test3 ORDER BY b; -- constant expression folding SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; -- upper/lower CREATE TABLE collate_test10 ( a int, x text COLLATE "en_US", y text COLLATE "tr_TR" ); INSERT INTO collate_test10 VALUES (1, 'hij', 'hij'), (2, 'HIJ', 'HIJ'); SELECT a, lower(x), lower(y), upper(x), upper(y), initcap(x), initcap(y) FROM collate_test10; SELECT a, lower(x COLLATE "C"), lower(y COLLATE "C") FROM collate_test10; SELECT a, x, y FROM collate_test10 ORDER BY lower(y), a; -- LIKE/ILIKE SELECT * FROM collate_test1 WHERE b LIKE 'abc'; SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; SELECT 'Türkiye' COLLATE "en_US" ILIKE '%KI%' AS "true"; SELECT 'Türkiye' COLLATE "tr_TR" ILIKE '%KI%' AS "false"; SELECT 'bıt' ILIKE 'BIT' COLLATE "en_US" AS "false"; SELECT 'bıt' ILIKE 'BIT' COLLATE "tr_TR" AS "true"; -- The following actually exercises the selectivity estimation for ILIKE. SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; -- regular expressions SELECT * FROM collate_test1 WHERE b ~ '^abc$'; SELECT * FROM collate_test1 WHERE b ~ '^abc'; SELECT * FROM collate_test1 WHERE b ~ 'bc'; SELECT * FROM collate_test1 WHERE b ~* '^abc$'; SELECT * FROM collate_test1 WHERE b ~* '^abc'; SELECT * FROM collate_test1 WHERE b ~* 'bc'; CREATE TABLE collate_test6 ( a int, b text COLLATE "en_US" ); INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), (9, 'äbç'), (10, 'ÄBÇ'); SELECT b, b ~ '^[[:alpha:]]+$' AS is_alpha, b ~ '^[[:upper:]]+$' AS is_upper, b ~ '^[[:lower:]]+$' AS is_lower, b ~ '^[[:digit:]]+$' AS is_digit, b ~ '^[[:alnum:]]+$' AS is_alnum, b ~ '^[[:graph:]]+$' AS is_graph, b ~ '^[[:print:]]+$' AS is_print, b ~ '^[[:punct:]]+$' AS is_punct, b ~ '^[[:space:]]+$' AS is_space FROM collate_test6; SELECT 'Türkiye' COLLATE "en_US" ~* 'KI' AS "true"; SELECT 'Türkiye' COLLATE "tr_TR" ~* 'KI' AS "false"; SELECT 'bıt' ~* 'BIT' COLLATE "en_US" AS "false"; SELECT 'bıt' ~* 'BIT' COLLATE "tr_TR" AS "true"; -- The following actually exercises the selectivity estimation for ~*. SELECT relname FROM pg_class WHERE relname ~* '^abc'; -- to_char SET lc_time TO 'tr_TR'; SELECT to_char(date '2010-02-01', 'DD TMMON YYYY'); SELECT to_char(date '2010-02-01', 'DD TMMON YYYY' COLLATE "tr_TR"); SELECT to_char(date '2010-04-01', 'DD TMMON YYYY'); SELECT to_char(date '2010-04-01', 'DD TMMON YYYY' COLLATE "tr_TR"); -- backwards parsing CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; -- collation propagation in various expression types SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; SELECT a, lower(coalesce(x, 'foo')), lower(coalesce(y, 'foo')) FROM collate_test10; SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; SELECT a, x, y, lower(greatest(x, 'foo')), lower(greatest(y, 'foo')) FROM collate_test10; SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; SELECT a, lower(nullif(x, 'foo')), lower(nullif(y, 'foo')) FROM collate_test10; SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; CREATE DOMAIN testdomain AS text; SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; SELECT a, lower(x::testdomain), lower(y::testdomain) FROM collate_test10; SELECT min(b), max(b) FROM collate_test1; SELECT min(b), max(b) FROM collate_test2; SELECT min(b), max(b) FROM collate_test3; SELECT array_agg(b ORDER BY b) FROM collate_test1; SELECT array_agg(b ORDER BY b) FROM collate_test2; SELECT array_agg(b ORDER BY b) FROM collate_test3; SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail -- ideally this would be a parse-time error, but for now it must be run-time: select x < y from collate_test10; -- fail select x || y from collate_test10; -- ok, because || is not collation aware select x, y from collate_test10 order by x || y; -- not so ok -- collation mismatch between recursive and non-recursive term WITH RECURSIVE foo(x) AS (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) UNION ALL SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) SELECT * FROM foo; -- casting SELECT CAST('42' AS text COLLATE "C"); SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; -- propagation of collation in SQL functions (inlined and non-inlined cases) -- and plpgsql functions too CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql AS $$ select $1 < $2 $$; CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql AS $$ select $1 < $2 limit 1 $$; CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql AS $$ begin return $1 < $2; end $$; SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) FROM collate_test1 a, collate_test1 b ORDER BY a.b, b.b; SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), mylt_plpgsql(a.b, b.b COLLATE "C") FROM collate_test1 a, collate_test1 b ORDER BY a.b, b.b; -- collation override in plpgsql CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ declare xx text := x; yy text := y; begin return xx < yy; end $$; SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; CREATE OR REPLACE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ declare xx text COLLATE "POSIX" := x; yy text := y; begin return xx < yy; end $$; SELECT mylt2('a', 'B') as f; SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations SELECT mylt2('a', 'B' collate "POSIX") as f; -- polymorphism SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; CREATE FUNCTION dup (anyelement) RETURNS anyelement AS 'select $1' LANGUAGE sql; SELECT a, dup(b) FROM collate_test1 ORDER BY 2; SELECT a, dup(b) FROM collate_test2 ORDER BY 2; SELECT a, dup(b) FROM collate_test3 ORDER BY 2; -- indexes CREATE INDEX collate_test1_idx1 ON collate_test1 (b); CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; -- schema manipulation commands CREATE ROLE regress_test_role; CREATE SCHEMA test_schema; -- We need to do this this way to cope with varying names for encodings: do $$ BEGIN EXECUTE 'CREATE COLLATION test0 (locale = ' || quote_literal(current_setting('lc_collate')) || ');'; END $$; CREATE COLLATION test0 FROM "C"; -- fail, duplicate name CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped do $$ BEGIN EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || quote_literal(current_setting('lc_collate')) || ', lc_ctype = ' || quote_literal(current_setting('lc_ctype')) || ');'; END $$; CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype CREATE COLLATION testx (locale = 'nonsense'); -- fail CREATE COLLATION testy (locale = 'en_US.utf8', version = 'foo'); -- fail, no versions for libc CREATE COLLATION test4 FROM nonsense; CREATE COLLATION test5 FROM test0; SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; ALTER COLLATION test1 RENAME TO test11; ALTER COLLATION test0 RENAME TO test11; -- fail ALTER COLLATION test1 RENAME TO test22; -- fail ALTER COLLATION test11 OWNER TO regress_test_role; ALTER COLLATION test11 OWNER TO nonsense; ALTER COLLATION test11 SET SCHEMA test_schema; COMMENT ON COLLATION test0 IS 'US English'; SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) WHERE collname LIKE 'test%' ORDER BY 1; DROP COLLATION test0, test_schema.test11, test5; DROP COLLATION test0; -- fail DROP COLLATION IF EXISTS test0; SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; DROP SCHEMA test_schema; DROP ROLE regress_test_role; -- ALTER ALTER COLLATION "en_US" REFRESH VERSION; -- dependencies CREATE COLLATION test0 FROM "C"; CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; CREATE TABLE collate_dep_test4t (a int, b text); CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); DROP COLLATION test0 RESTRICT; -- fail DROP COLLATION test0 CASCADE; \d collate_dep_test1 \d collate_dep_test2 DROP TABLE collate_dep_test1, collate_dep_test4t; DROP TYPE collate_dep_test2; -- test range types and collations create type textrange_c as range(subtype=text, collation="C"); create type textrange_en_us as range(subtype=text, collation="en_US"); select textrange_c('A','Z') @> 'b'::text; select textrange_en_us('A','Z') @> 'b'::text; drop type textrange_c; drop type textrange_en_us; -- nondeterministic collations -- (not supported with libc provider) CREATE COLLATION ctest_det (locale = 'en_US.utf8', deterministic = true); CREATE COLLATION ctest_nondet (locale = 'en_US.utf8', deterministic = false); -- cleanup SET client_min_messages TO warning; DROP SCHEMA collate_tests CASCADE; pgFormatter-4.2/t/pg-test-files/sql/collate.sql000066400000000000000000000234611361326045100215510ustar00rootroot00000000000000/* * This test is intended to pass on all platforms supported by Postgres. * We can therefore only assume that the default, C, and POSIX collations * are available --- and since the regression tests are often run in a * C-locale database, these may well all have the same behavior. But * fortunately, the system doesn't know that and will treat them as * incompatible collations. It is therefore at least possible to test * parser behaviors such as collation conflict resolution. This test will, * however, be more revealing when run in a database with non-C locale, * since any departure from C sorting behavior will show as a failure. */ CREATE SCHEMA collate_tests; SET search_path = collate_tests; CREATE TABLE collate_test1 ( a int, b text COLLATE "C" NOT NULL ); \d collate_test1 CREATE TABLE collate_test_fail ( a int COLLATE "C", b text ); CREATE TABLE collate_test_like ( LIKE collate_test1 ); \d collate_test_like CREATE TABLE collate_test2 ( a int, b text COLLATE "POSIX" ); INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'Abc'), (3, 'bbc'), (4, 'ABD'); INSERT INTO collate_test2 SELECT * FROM collate_test1; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'abc'; SELECT * FROM collate_test1 WHERE b >= 'abc' COLLATE "C"; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'abc' COLLATE "C"; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "POSIX"; -- fail CREATE DOMAIN testdomain_p AS text COLLATE "POSIX"; CREATE DOMAIN testdomain_i AS int COLLATE "POSIX"; -- fail CREATE TABLE collate_test4 ( a int, b testdomain_p ); INSERT INTO collate_test4 SELECT * FROM collate_test1; SELECT a, b FROM collate_test4 ORDER BY b; CREATE TABLE collate_test5 ( a int, b testdomain_p COLLATE "C" ); INSERT INTO collate_test5 SELECT * FROM collate_test1; SELECT a, b FROM collate_test5 ORDER BY b; SELECT a, b FROM collate_test1 ORDER BY b; SELECT a, b FROM collate_test2 ORDER BY b; SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; -- star expansion SELECT * FROM collate_test1 ORDER BY b; SELECT * FROM collate_test2 ORDER BY b; -- constant expression folding SELECT 'bbc' COLLATE "C" > 'Abc' COLLATE "C" AS "true"; SELECT 'bbc' COLLATE "POSIX" < 'Abc' COLLATE "POSIX" AS "false"; -- upper/lower CREATE TABLE collate_test10 ( a int, x text COLLATE "C", y text COLLATE "POSIX" ); INSERT INTO collate_test10 VALUES (1, 'hij', 'hij'), (2, 'HIJ', 'HIJ'); SELECT a, lower(x), lower(y), upper(x), upper(y), initcap(x), initcap(y) FROM collate_test10; SELECT a, lower(x COLLATE "C"), lower(y COLLATE "C") FROM collate_test10; SELECT a, x, y FROM collate_test10 ORDER BY lower(y), a; -- backwards parsing CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; -- collation propagation in various expression types SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; SELECT a, lower(coalesce(x, 'foo')), lower(coalesce(y, 'foo')) FROM collate_test10; SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; SELECT a, x, y, lower(greatest(x, 'foo')), lower(greatest(y, 'foo')) FROM collate_test10; SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; SELECT a, lower(nullif(x, 'foo')), lower(nullif(y, 'foo')) FROM collate_test10; SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; CREATE DOMAIN testdomain AS text; SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; SELECT a, b::testdomain_p FROM collate_test2 ORDER BY 2; SELECT a, lower(x::testdomain), lower(y::testdomain) FROM collate_test10; SELECT min(b), max(b) FROM collate_test1; SELECT min(b), max(b) FROM collate_test2; SELECT array_agg(b ORDER BY b) FROM collate_test1; SELECT array_agg(b ORDER BY b) FROM collate_test2; -- In aggregates, ORDER BY expressions don't affect aggregate's collation SELECT string_agg(x COLLATE "C", y COLLATE "POSIX") FROM collate_test10; -- fail SELECT array_agg(x COLLATE "C" ORDER BY y COLLATE "POSIX") FROM collate_test10; SELECT array_agg(a ORDER BY x COLLATE "C", y COLLATE "POSIX") FROM collate_test10; SELECT array_agg(a ORDER BY x||y) FROM collate_test10; -- fail SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; SELECT a, b FROM collate_test2 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test2 WHERE a > 1 ORDER BY 2; SELECT a, b FROM collate_test2 EXCEPT SELECT a, b FROM collate_test2 WHERE a < 2 ORDER BY 2; SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test2 ORDER BY 2; -- fail SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test2; -- ok SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test2 ORDER BY 2; -- fail SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test2 ORDER BY 2; -- ok SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test2 ORDER BY 2; -- fail SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test2 ORDER BY 2; -- fail CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test2; -- fail -- ideally this would be a parse-time error, but for now it must be run-time: select x < y from collate_test10; -- fail select x || y from collate_test10; -- ok, because || is not collation aware select x, y from collate_test10 order by x || y; -- not so ok -- collation mismatch between recursive and non-recursive term WITH RECURSIVE foo(x) AS (SELECT x FROM (VALUES('a' COLLATE "C"),('b')) t(x) UNION ALL SELECT (x || 'c') COLLATE "POSIX" FROM foo WHERE length(x) < 10) SELECT * FROM foo; SELECT a, b, a < b as lt FROM (VALUES ('a', 'B'), ('A', 'b' COLLATE "C")) v(a,b); -- collation mismatch in subselects SELECT * FROM collate_test10 WHERE (x, y) NOT IN (SELECT y, x FROM collate_test10); -- now it works with overrides SELECT * FROM collate_test10 WHERE (x COLLATE "POSIX", y COLLATE "C") NOT IN (SELECT y, x FROM collate_test10); SELECT * FROM collate_test10 WHERE (x, y) NOT IN (SELECT y COLLATE "C", x COLLATE "POSIX" FROM collate_test10); -- casting SELECT CAST('42' AS text COLLATE "C"); SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; -- polymorphism SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; CREATE FUNCTION dup (anyelement) RETURNS anyelement AS 'select $1' LANGUAGE sql; SELECT a, dup(b) FROM collate_test1 ORDER BY 2; SELECT a, dup(b) FROM collate_test2 ORDER BY 2; -- indexes CREATE INDEX collate_test1_idx1 ON collate_test1 (b); CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "POSIX"); CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "POSIX")); -- this is different grammatically CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "POSIX"); -- fail CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "POSIX")); -- fail SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; -- foreign keys -- force indexes and mergejoins to be used for FK checking queries, -- else they might not exercise collation-dependent operators SET enable_seqscan TO 0; SET enable_hashjoin TO 0; SET enable_nestloop TO 0; CREATE TABLE collate_test20 (f1 text COLLATE "C" PRIMARY KEY); INSERT INTO collate_test20 VALUES ('foo'), ('bar'); CREATE TABLE collate_test21 (f2 text COLLATE "POSIX" REFERENCES collate_test20); INSERT INTO collate_test21 VALUES ('foo'), ('bar'); INSERT INTO collate_test21 VALUES ('baz'); -- fail CREATE TABLE collate_test22 (f2 text COLLATE "POSIX"); INSERT INTO collate_test22 VALUES ('foo'), ('bar'), ('baz'); ALTER TABLE collate_test22 ADD FOREIGN KEY (f2) REFERENCES collate_test20; -- fail DELETE FROM collate_test22 WHERE f2 = 'baz'; ALTER TABLE collate_test22 ADD FOREIGN KEY (f2) REFERENCES collate_test20; RESET enable_seqscan; RESET enable_hashjoin; RESET enable_nestloop; -- EXPLAIN EXPLAIN (COSTS OFF) SELECT * FROM collate_test10 ORDER BY x, y; EXPLAIN (COSTS OFF) SELECT * FROM collate_test10 ORDER BY x DESC, y COLLATE "C" ASC NULLS FIRST; -- CREATE/DROP COLLATION CREATE COLLATION mycoll1 FROM "C"; CREATE COLLATION mycoll2 ( LC_COLLATE = "POSIX", LC_CTYPE = "POSIX" ); CREATE COLLATION mycoll3 FROM "default"; -- intentionally unsupported DROP COLLATION mycoll1; CREATE TABLE collate_test23 (f1 text collate mycoll2); DROP COLLATION mycoll2; -- fail -- invalid: non-lowercase quoted identifiers CREATE COLLATION case_coll ("Lc_Collate" = "POSIX", "Lc_Ctype" = "POSIX"); -- 9.1 bug with useless COLLATE in an expression subject to length coercion CREATE TEMP TABLE vctable (f1 varchar(25)); INSERT INTO vctable VALUES ('foo' COLLATE "C"); SELECT collation for ('foo'); -- unknown type - null SELECT collation for ('foo'::text); SELECT collation for ((SELECT a FROM collate_test1 LIMIT 1)); -- non-collatable type - error SELECT collation for ((SELECT b FROM collate_test1 LIMIT 1)); -- -- Clean up. Many of these table names will be re-used if the user is -- trying to run any platform-specific collation tests later, so we -- must get rid of them. -- DROP SCHEMA collate_tests CASCADE; pgFormatter-4.2/t/pg-test-files/sql/combocid.sql000066400000000000000000000054161361326045100217050ustar00rootroot00000000000000-- -- Tests for some likely failure cases with combo cmin/cmax mechanism -- CREATE TEMP TABLE combocidtest (foobar int); BEGIN; -- a few dummy ops to push up the CommandId counter INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest VALUES (1); INSERT INTO combocidtest VALUES (2); SELECT ctid,cmin,* FROM combocidtest; SAVEPOINT s1; UPDATE combocidtest SET foobar = foobar + 10; -- here we should see only updated tuples SELECT ctid,cmin,* FROM combocidtest; ROLLBACK TO s1; -- now we should see old tuples, but with combo CIDs starting at 0 SELECT ctid,cmin,* FROM combocidtest; COMMIT; -- combo data is not there anymore, but should still see tuples SELECT ctid,cmin,* FROM combocidtest; -- Test combo cids with portals BEGIN; INSERT INTO combocidtest VALUES (333); DECLARE c CURSOR FOR SELECT ctid,cmin,* FROM combocidtest; DELETE FROM combocidtest; FETCH ALL FROM c; ROLLBACK; SELECT ctid,cmin,* FROM combocidtest; -- check behavior with locked tuples BEGIN; -- a few dummy ops to push up the CommandId counter INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest SELECT 1 LIMIT 0; INSERT INTO combocidtest VALUES (444); SELECT ctid,cmin,* FROM combocidtest; SAVEPOINT s1; -- this doesn't affect cmin SELECT ctid,cmin,* FROM combocidtest FOR UPDATE; SELECT ctid,cmin,* FROM combocidtest; -- but this does UPDATE combocidtest SET foobar = foobar + 10; SELECT ctid,cmin,* FROM combocidtest; ROLLBACK TO s1; SELECT ctid,cmin,* FROM combocidtest; COMMIT; SELECT ctid,cmin,* FROM combocidtest; -- test for bug reported in -- CABRT9RC81YUf1=jsmWopcKJEro=VoeG2ou6sPwyOUTx_qteRsg@mail.gmail.com CREATE TABLE IF NOT EXISTS testcase( id int PRIMARY KEY, balance numeric ); INSERT INTO testcase VALUES (1, 0); BEGIN; SELECT * FROM testcase WHERE testcase.id = 1 FOR UPDATE; UPDATE testcase SET balance = balance + 400 WHERE id=1; SAVEPOINT subxact; UPDATE testcase SET balance = balance - 100 WHERE id=1; ROLLBACK TO SAVEPOINT subxact; -- should return one tuple SELECT * FROM testcase WHERE id = 1 FOR UPDATE; ROLLBACK; DROP TABLE testcase; pgFormatter-4.2/t/pg-test-files/sql/comments.sql000066400000000000000000000020211361326045100217400ustar00rootroot00000000000000-- -- COMMENTS -- SELECT 'trailing' AS first; -- trailing single line SELECT /* embedded single line */ 'embedded' AS second; SELECT /* both embedded and trailing single line */ 'both' AS third; -- trailing single line SELECT 'before multi-line' AS fourth; /* This is an example of SQL which should not execute: * select 'multi-line'; */ SELECT 'after multi-line' AS fifth; -- -- Nested comments -- /* SELECT 'trailing' as x1; -- inside block comment */ /* This block comment surrounds a query which itself has a block comment... SELECT /* embedded single line */ 'embedded' AS x2; */ SELECT -- continued after the following block comments... /* Deeply nested comment. This includes a single apostrophe to make sure we aren't decoding this part as a string. SELECT 'deep nest' AS n1; /* Second level of nesting... SELECT 'deeper nest' as n2; /* Third level of nesting... SELECT 'deepest nest' as n3; */ Hoo boy. Still two deep... */ Now just one deep... */ 'deeply nested example' AS sixth; /* and this is the end of the file */ pgFormatter-4.2/t/pg-test-files/sql/conversion.sql000066400000000000000000000021141361326045100223030ustar00rootroot00000000000000-- -- create user defined conversion -- CREATE USER regress_conversion_user WITH NOCREATEDB NOCREATEROLE; SET SESSION AUTHORIZATION regress_conversion_user; CREATE CONVERSION myconv FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; -- -- cannot make same name conversion in same schema -- CREATE CONVERSION myconv FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; -- -- create default conversion with qualified name -- CREATE DEFAULT CONVERSION public.mydef FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; -- -- cannot make default conversion with same schema/for_encoding/to_encoding -- CREATE DEFAULT CONVERSION public.mydef2 FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; -- test comments COMMENT ON CONVERSION myconv_bad IS 'foo'; COMMENT ON CONVERSION myconv IS 'bar'; COMMENT ON CONVERSION myconv IS NULL; -- -- drop user defined conversion -- DROP CONVERSION myconv; DROP CONVERSION mydef; -- -- Note: the built-in conversions are exercised in opr_sanity.sql, -- so there's no need to do that here. -- -- -- return to the super user -- RESET SESSION AUTHORIZATION; DROP USER regress_conversion_user; pgFormatter-4.2/t/pg-test-files/sql/create_aggregate.sql000066400000000000000000000163441361326045100234010ustar00rootroot00000000000000-- -- CREATE_AGGREGATE -- -- all functions CREATEd CREATE AGGREGATE newavg ( sfunc = int4_avg_accum, basetype = int4, stype = _int8, finalfunc = int8_avg, initcond1 = '{0,0}' ); -- test comments COMMENT ON AGGREGATE newavg_wrong (int4) IS 'an agg comment'; COMMENT ON AGGREGATE newavg (int4) IS 'an agg comment'; COMMENT ON AGGREGATE newavg (int4) IS NULL; -- without finalfunc; test obsolete spellings 'sfunc1' etc CREATE AGGREGATE newsum ( sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0' ); -- zero-argument aggregate CREATE AGGREGATE newcnt (*) ( sfunc = int8inc, stype = int8, initcond = '0', parallel = safe ); -- old-style spelling of same (except without parallel-safe; that's too new) CREATE AGGREGATE oldcnt ( sfunc = int8inc, basetype = 'ANY', stype = int8, initcond = '0' ); -- aggregate that only cares about null/nonnull input CREATE AGGREGATE newcnt ("any") ( sfunc = int8inc_any, stype = int8, initcond = '0' ); COMMENT ON AGGREGATE nosuchagg (*) IS 'should fail'; COMMENT ON AGGREGATE newcnt (*) IS 'an agg(*) comment'; COMMENT ON AGGREGATE newcnt ("any") IS 'an agg(any) comment'; -- multi-argument aggregate create function sum3(int8,int8,int8) returns int8 as 'select $1 + $2 + $3' language sql strict immutable; create aggregate sum2(int8,int8) ( sfunc = sum3, stype = int8, initcond = '0' ); -- multi-argument aggregates sensitive to distinct/order, strict/nonstrict create type aggtype as (a integer, b integer, c text); create function aggf_trans(aggtype[],integer,integer,text) returns aggtype[] as 'select array_append($1,ROW($2,$3,$4)::aggtype)' language sql strict immutable; create function aggfns_trans(aggtype[],integer,integer,text) returns aggtype[] as 'select array_append($1,ROW($2,$3,$4)::aggtype)' language sql immutable; create aggregate aggfstr(integer,integer,text) ( sfunc = aggf_trans, stype = aggtype[], initcond = '{}' ); create aggregate aggfns(integer,integer,text) ( sfunc = aggfns_trans, stype = aggtype[], sspace = 10000, initcond = '{}' ); -- variadic aggregate create function least_accum(anyelement, variadic anyarray) returns anyelement language sql as 'select least($1, min($2[i])) from generate_subscripts($2,1) g(i)'; create aggregate least_agg(variadic items anyarray) ( stype = anyelement, sfunc = least_accum ); -- test ordered-set aggs using built-in support functions create aggregate my_percentile_disc(float8 ORDER BY anyelement) ( stype = internal, sfunc = ordered_set_transition, finalfunc = percentile_disc_final, finalfunc_extra = true, finalfunc_modify = read_write ); create aggregate my_rank(VARIADIC "any" ORDER BY VARIADIC "any") ( stype = internal, sfunc = ordered_set_transition_multi, finalfunc = rank_final, finalfunc_extra = true, hypothetical ); alter aggregate my_percentile_disc(float8 ORDER BY anyelement) rename to test_percentile_disc; alter aggregate my_rank(VARIADIC "any" ORDER BY VARIADIC "any") rename to test_rank; \da test_* -- moving-aggregate options CREATE AGGREGATE sumdouble (float8) ( stype = float8, sfunc = float8pl, mstype = float8, msfunc = float8pl, minvfunc = float8mi ); -- aggregate combine and serialization functions -- can't specify just one of serialfunc and deserialfunc CREATE AGGREGATE myavg (numeric) ( stype = internal, sfunc = numeric_avg_accum, serialfunc = numeric_avg_serialize ); -- serialfunc must have correct parameters CREATE AGGREGATE myavg (numeric) ( stype = internal, sfunc = numeric_avg_accum, serialfunc = numeric_avg_deserialize, deserialfunc = numeric_avg_deserialize ); -- deserialfunc must have correct parameters CREATE AGGREGATE myavg (numeric) ( stype = internal, sfunc = numeric_avg_accum, serialfunc = numeric_avg_serialize, deserialfunc = numeric_avg_serialize ); -- ensure combine function parameters are checked CREATE AGGREGATE myavg (numeric) ( stype = internal, sfunc = numeric_avg_accum, serialfunc = numeric_avg_serialize, deserialfunc = numeric_avg_deserialize, combinefunc = int4larger ); -- ensure create aggregate works. CREATE AGGREGATE myavg (numeric) ( stype = internal, sfunc = numeric_avg_accum, finalfunc = numeric_avg, serialfunc = numeric_avg_serialize, deserialfunc = numeric_avg_deserialize, combinefunc = numeric_avg_combine, finalfunc_modify = shareable -- just to test a non-default setting ); -- Ensure all these functions made it into the catalog SELECT aggfnoid, aggtransfn, aggcombinefn, aggtranstype::regtype, aggserialfn, aggdeserialfn, aggfinalmodify FROM pg_aggregate WHERE aggfnoid = 'myavg'::REGPROC; DROP AGGREGATE myavg (numeric); -- create or replace aggregate CREATE AGGREGATE myavg (numeric) ( stype = internal, sfunc = numeric_avg_accum, finalfunc = numeric_avg ); CREATE OR REPLACE AGGREGATE myavg (numeric) ( stype = internal, sfunc = numeric_avg_accum, finalfunc = numeric_avg, serialfunc = numeric_avg_serialize, deserialfunc = numeric_avg_deserialize, combinefunc = numeric_avg_combine, finalfunc_modify = shareable -- just to test a non-default setting ); -- Ensure all these functions made it into the catalog again SELECT aggfnoid, aggtransfn, aggcombinefn, aggtranstype::regtype, aggserialfn, aggdeserialfn, aggfinalmodify FROM pg_aggregate WHERE aggfnoid = 'myavg'::REGPROC; -- can change stype: CREATE OR REPLACE AGGREGATE myavg (numeric) ( stype = numeric, sfunc = numeric_add ); SELECT aggfnoid, aggtransfn, aggcombinefn, aggtranstype::regtype, aggserialfn, aggdeserialfn, aggfinalmodify FROM pg_aggregate WHERE aggfnoid = 'myavg'::REGPROC; -- can't change return type: CREATE OR REPLACE AGGREGATE myavg (numeric) ( stype = numeric, sfunc = numeric_add, finalfunc = numeric_out ); -- can't change to a different kind: CREATE OR REPLACE AGGREGATE myavg (order by numeric) ( stype = numeric, sfunc = numeric_add ); -- can't change plain function to aggregate: create function sum4(int8,int8,int8,int8) returns int8 as 'select $1 + $2 + $3 + $4' language sql strict immutable; CREATE OR REPLACE AGGREGATE sum3 (int8,int8,int8) ( stype = int8, sfunc = sum4 ); drop function sum4(int8,int8,int8,int8); DROP AGGREGATE myavg (numeric); -- invalid: bad parallel-safety marking CREATE AGGREGATE mysum (int) ( stype = int, sfunc = int4pl, parallel = pear ); -- invalid: nonstrict inverse with strict forward function CREATE FUNCTION float8mi_n(float8, float8) RETURNS float8 AS $$ SELECT $1 - $2; $$ LANGUAGE SQL; CREATE AGGREGATE invalidsumdouble (float8) ( stype = float8, sfunc = float8pl, mstype = float8, msfunc = float8pl, minvfunc = float8mi_n ); -- invalid: non-matching result types CREATE FUNCTION float8mi_int(float8, float8) RETURNS int AS $$ SELECT CAST($1 - $2 AS INT); $$ LANGUAGE SQL; CREATE AGGREGATE wrongreturntype (float8) ( stype = float8, sfunc = float8pl, mstype = float8, msfunc = float8pl, minvfunc = float8mi_int ); -- invalid: non-lowercase quoted identifiers CREATE AGGREGATE case_agg ( -- old syntax "Sfunc1" = int4pl, "Basetype" = int4, "Stype1" = int4, "Initcond1" = '0', "Parallel" = safe ); CREATE AGGREGATE case_agg(float8) ( "Stype" = internal, "Sfunc" = ordered_set_transition, "Finalfunc" = percentile_disc_final, "Finalfunc_extra" = true, "Finalfunc_modify" = read_write, "Parallel" = safe ); pgFormatter-4.2/t/pg-test-files/sql/create_am.sql000066400000000000000000000163231361326045100220450ustar00rootroot00000000000000-- -- Create access method tests -- -- Make gist2 over gisthandler. In fact, it would be a synonym to gist. CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler; -- Verify return type checks for handlers CREATE ACCESS METHOD bogus TYPE INDEX HANDLER int4in; CREATE ACCESS METHOD bogus TYPE INDEX HANDLER heap_tableam_handler; -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist CREATE INDEX grect2ind2 ON fast_emp4000 USING gist2 (home_base); -- Make operator class for boxes using gist2 CREATE OPERATOR CLASS box_ops DEFAULT FOR TYPE box USING gist2 AS OPERATOR 1 <<, OPERATOR 2 &<, OPERATOR 3 &&, OPERATOR 4 &>, OPERATOR 5 >>, OPERATOR 6 ~=, OPERATOR 7 @>, OPERATOR 8 <@, OPERATOR 9 &<|, OPERATOR 10 <<|, OPERATOR 11 |>>, OPERATOR 12 |&>, OPERATOR 13 ~, OPERATOR 14 @, FUNCTION 1 gist_box_consistent(internal, box, smallint, oid, internal), FUNCTION 2 gist_box_union(internal, internal), -- don't need compress, decompress, or fetch functions FUNCTION 5 gist_box_penalty(internal, internal, internal), FUNCTION 6 gist_box_picksplit(internal, internal), FUNCTION 7 gist_box_same(box, box, internal); -- Create gist2 index on fast_emp4000 CREATE INDEX grect2ind2 ON fast_emp4000 USING gist2 (home_base); -- Now check the results from plain indexscan; temporarily drop existing -- index grect2ind to ensure it doesn't capture the plan BEGIN; DROP INDEX grect2ind; SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = OFF; EXPLAIN (COSTS OFF) SELECT * FROM fast_emp4000 WHERE home_base @ '(200,200),(2000,1000)'::box ORDER BY (home_base[0])[0]; SELECT * FROM fast_emp4000 WHERE home_base @ '(200,200),(2000,1000)'::box ORDER BY (home_base[0])[0]; EXPLAIN (COSTS OFF) SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box; SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box; EXPLAIN (COSTS OFF) SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; ROLLBACK; -- Try to drop access method: fail because of dependent objects DROP ACCESS METHOD gist2; -- Drop access method cascade DROP ACCESS METHOD gist2 CASCADE; -- -- Test table access methods -- -- prevent empty values SET default_table_access_method = ''; -- prevent nonexistant values SET default_table_access_method = 'I do not exist AM'; -- prevent setting it to an index AM SET default_table_access_method = 'btree'; -- Create a heap2 table am handler with heapam handler CREATE ACCESS METHOD heap2 TYPE TABLE HANDLER heap_tableam_handler; -- Verify return type checks for handlers CREATE ACCESS METHOD bogus TYPE TABLE HANDLER int4in; CREATE ACCESS METHOD bogus TYPE TABLE HANDLER bthandler; SELECT amname, amhandler, amtype FROM pg_am where amtype = 't' ORDER BY 1, 2; -- First create tables employing the new AM using USING -- plain CREATE TABLE CREATE TABLE tableam_tbl_heap2(f1 int) USING heap2; INSERT INTO tableam_tbl_heap2 VALUES(1); SELECT f1 FROM tableam_tbl_heap2 ORDER BY f1; -- CREATE TABLE AS CREATE TABLE tableam_tblas_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2; SELECT f1 FROM tableam_tbl_heap2 ORDER BY f1; -- SELECT INTO doesn't support USING SELECT INTO tableam_tblselectinto_heap2 USING heap2 FROM tableam_tbl_heap2; -- CREATE VIEW doesn't support USING CREATE VIEW tableam_view_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2; -- CREATE SEQUENCE doesn't support USING CREATE SEQUENCE tableam_seq_heap2 USING heap2; -- CREATE MATERIALIZED VIEW does support USING CREATE MATERIALIZED VIEW tableam_tblmv_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2; SELECT f1 FROM tableam_tblmv_heap2 ORDER BY f1; -- CREATE TABLE .. PARTITION BY doesn't not support USING CREATE TABLE tableam_parted_heap2 (a text, b int) PARTITION BY list (a) USING heap2; CREATE TABLE tableam_parted_heap2 (a text, b int) PARTITION BY list (a); -- new partitions will inherit from the current default, rather the partition root SET default_table_access_method = 'heap'; CREATE TABLE tableam_parted_a_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('a'); SET default_table_access_method = 'heap2'; CREATE TABLE tableam_parted_b_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('b'); RESET default_table_access_method; -- but the method can be explicitly specified CREATE TABLE tableam_parted_c_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('c') USING heap; CREATE TABLE tableam_parted_d_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('d') USING heap2; -- List all objects in AM SELECT pc.relkind, pa.amname, CASE WHEN relkind = 't' THEN (SELECT 'toast for ' || relname::regclass FROM pg_class pcm WHERE pcm.reltoastrelid = pc.oid) ELSE relname::regclass::text END COLLATE "C" AS relname FROM pg_class AS pc, pg_am AS pa WHERE pa.oid = pc.relam AND pa.amname = 'heap2' ORDER BY 3, 1, 2; -- Show dependencies onto AM - there shouldn't be any for toast SELECT pg_describe_object(classid,objid,objsubid) AS obj FROM pg_depend, pg_am WHERE pg_depend.refclassid = 'pg_am'::regclass AND pg_am.oid = pg_depend.refobjid AND pg_am.amname = 'heap2' ORDER BY classid, objid, objsubid; -- Second, create objects in the new AM by changing the default AM BEGIN; SET LOCAL default_table_access_method = 'heap2'; -- following tests should all respect the default AM CREATE TABLE tableam_tbl_heapx(f1 int); CREATE TABLE tableam_tblas_heapx AS SELECT * FROM tableam_tbl_heapx; SELECT INTO tableam_tblselectinto_heapx FROM tableam_tbl_heapx; CREATE MATERIALIZED VIEW tableam_tblmv_heapx USING heap2 AS SELECT * FROM tableam_tbl_heapx; CREATE TABLE tableam_parted_heapx (a text, b int) PARTITION BY list (a); CREATE TABLE tableam_parted_1_heapx PARTITION OF tableam_parted_heapx FOR VALUES IN ('a', 'b'); -- but an explicitly set AM overrides it CREATE TABLE tableam_parted_2_heapx PARTITION OF tableam_parted_heapx FOR VALUES IN ('c', 'd') USING heap; -- sequences, views and foreign servers shouldn't have an AM CREATE VIEW tableam_view_heapx AS SELECT * FROM tableam_tbl_heapx; CREATE SEQUENCE tableam_seq_heapx; CREATE FOREIGN DATA WRAPPER fdw_heap2 VALIDATOR postgresql_fdw_validator; CREATE SERVER fs_heap2 FOREIGN DATA WRAPPER fdw_heap2 ; CREATE FOREIGN table tableam_fdw_heapx () SERVER fs_heap2; -- Verify that new AM was used for tables, matviews, but not for sequences, views and fdws SELECT pc.relkind, pa.amname, CASE WHEN relkind = 't' THEN (SELECT 'toast for ' || relname::regclass FROM pg_class pcm WHERE pcm.reltoastrelid = pc.oid) ELSE relname::regclass::text END COLLATE "C" AS relname FROM pg_class AS pc LEFT JOIN pg_am AS pa ON (pa.oid = pc.relam) WHERE pc.relname LIKE 'tableam_%_heapx' ORDER BY 3, 1, 2; -- don't want to keep those tables, nor the default ROLLBACK; -- Third, check that we can neither create a table using a nonexistant -- AM, nor using an index AM CREATE TABLE i_am_a_failure() USING ""; CREATE TABLE i_am_a_failure() USING i_do_not_exist_am; CREATE TABLE i_am_a_failure() USING "I do not exist AM"; CREATE TABLE i_am_a_failure() USING "btree"; -- Drop table access method, which fails as objects depends on it DROP ACCESS METHOD heap2; -- we intentionally leave the objects created above alive, to verify pg_dump support pgFormatter-4.2/t/pg-test-files/sql/create_cast.sql000066400000000000000000000030741361326045100224010ustar00rootroot00000000000000-- -- CREATE_CAST -- -- Create some types to test with CREATE TYPE casttesttype; CREATE FUNCTION casttesttype_in(cstring) RETURNS casttesttype AS 'textin' LANGUAGE internal STRICT IMMUTABLE; CREATE FUNCTION casttesttype_out(casttesttype) RETURNS cstring AS 'textout' LANGUAGE internal STRICT IMMUTABLE; CREATE TYPE casttesttype ( internallength = variable, input = casttesttype_in, output = casttesttype_out, alignment = int4 ); -- a dummy function to test with CREATE FUNCTION casttestfunc(casttesttype) RETURNS int4 LANGUAGE SQL AS $$ SELECT 1; $$; SELECT casttestfunc('foo'::text); -- fails, as there's no cast -- Try binary coercion cast CREATE CAST (text AS casttesttype) WITHOUT FUNCTION; SELECT casttestfunc('foo'::text); -- doesn't work, as the cast is explicit SELECT casttestfunc('foo'::text::casttesttype); -- should work DROP CAST (text AS casttesttype); -- cleanup -- Try IMPLICIT binary coercion cast CREATE CAST (text AS casttesttype) WITHOUT FUNCTION AS IMPLICIT; SELECT casttestfunc('foo'::text); -- Should work now -- Try I/O conversion cast. SELECT 1234::int4::casttesttype; -- No cast yet, should fail CREATE CAST (int4 AS casttesttype) WITH INOUT; SELECT 1234::int4::casttesttype; -- Should work now DROP CAST (int4 AS casttesttype); -- Try cast with a function CREATE FUNCTION int4_casttesttype(int4) RETURNS casttesttype LANGUAGE SQL AS $$ SELECT ('foo'::text || $1::text)::casttesttype; $$; CREATE CAST (int4 AS casttesttype) WITH FUNCTION int4_casttesttype(int4) AS IMPLICIT; SELECT 1234::int4::casttesttype; -- Should work now pgFormatter-4.2/t/pg-test-files/sql/create_function_3.sql000066400000000000000000000173041361326045100235170ustar00rootroot00000000000000-- -- CREATE FUNCTION -- -- Assorted tests using SQL-language functions -- -- All objects made in this test are in temp_func_test schema CREATE USER regress_unpriv_user; CREATE SCHEMA temp_func_test; GRANT ALL ON SCHEMA temp_func_test TO public; SET search_path TO temp_func_test, public; -- -- Make sanity checks on the pg_proc entries created by CREATE FUNCTION -- -- -- ARGUMENT and RETURN TYPES -- CREATE FUNCTION functest_A_1(text, date) RETURNS bool LANGUAGE 'sql' AS 'SELECT $1 = ''abcd'' AND $2 > ''2001-01-01'''; CREATE FUNCTION functest_A_2(text[]) RETURNS int LANGUAGE 'sql' AS 'SELECT $1[0]::int'; CREATE FUNCTION functest_A_3() RETURNS bool LANGUAGE 'sql' AS 'SELECT false'; SELECT proname, prorettype::regtype, proargtypes::regtype[] FROM pg_proc WHERE oid in ('functest_A_1'::regproc, 'functest_A_2'::regproc, 'functest_A_3'::regproc) ORDER BY proname; -- -- IMMUTABLE | STABLE | VOLATILE -- CREATE FUNCTION functest_B_1(int) RETURNS bool LANGUAGE 'sql' AS 'SELECT $1 > 0'; CREATE FUNCTION functest_B_2(int) RETURNS bool LANGUAGE 'sql' IMMUTABLE AS 'SELECT $1 > 0'; CREATE FUNCTION functest_B_3(int) RETURNS bool LANGUAGE 'sql' STABLE AS 'SELECT $1 = 0'; CREATE FUNCTION functest_B_4(int) RETURNS bool LANGUAGE 'sql' VOLATILE AS 'SELECT $1 < 0'; SELECT proname, provolatile FROM pg_proc WHERE oid in ('functest_B_1'::regproc, 'functest_B_2'::regproc, 'functest_B_3'::regproc, 'functest_B_4'::regproc) ORDER BY proname; ALTER FUNCTION functest_B_2(int) VOLATILE; ALTER FUNCTION functest_B_3(int) COST 100; -- unrelated change, no effect SELECT proname, provolatile FROM pg_proc WHERE oid in ('functest_B_1'::regproc, 'functest_B_2'::regproc, 'functest_B_3'::regproc, 'functest_B_4'::regproc) ORDER BY proname; -- -- SECURITY DEFINER | INVOKER -- CREATE FUNCTION functest_C_1(int) RETURNS bool LANGUAGE 'sql' AS 'SELECT $1 > 0'; CREATE FUNCTION functest_C_2(int) RETURNS bool LANGUAGE 'sql' SECURITY DEFINER AS 'SELECT $1 = 0'; CREATE FUNCTION functest_C_3(int) RETURNS bool LANGUAGE 'sql' SECURITY INVOKER AS 'SELECT $1 < 0'; SELECT proname, prosecdef FROM pg_proc WHERE oid in ('functest_C_1'::regproc, 'functest_C_2'::regproc, 'functest_C_3'::regproc) ORDER BY proname; ALTER FUNCTION functest_C_1(int) IMMUTABLE; -- unrelated change, no effect ALTER FUNCTION functest_C_2(int) SECURITY INVOKER; ALTER FUNCTION functest_C_3(int) SECURITY DEFINER; SELECT proname, prosecdef FROM pg_proc WHERE oid in ('functest_C_1'::regproc, 'functest_C_2'::regproc, 'functest_C_3'::regproc) ORDER BY proname; -- -- LEAKPROOF -- CREATE FUNCTION functest_E_1(int) RETURNS bool LANGUAGE 'sql' AS 'SELECT $1 > 100'; CREATE FUNCTION functest_E_2(int) RETURNS bool LANGUAGE 'sql' LEAKPROOF AS 'SELECT $1 > 100'; SELECT proname, proleakproof FROM pg_proc WHERE oid in ('functest_E_1'::regproc, 'functest_E_2'::regproc) ORDER BY proname; ALTER FUNCTION functest_E_1(int) LEAKPROOF; ALTER FUNCTION functest_E_2(int) STABLE; -- unrelated change, no effect SELECT proname, proleakproof FROM pg_proc WHERE oid in ('functest_E_1'::regproc, 'functest_E_2'::regproc) ORDER BY proname; ALTER FUNCTION functest_E_2(int) NOT LEAKPROOF; -- remove leakproof attribute SELECT proname, proleakproof FROM pg_proc WHERE oid in ('functest_E_1'::regproc, 'functest_E_2'::regproc) ORDER BY proname; -- it takes superuser privilege to turn on leakproof, but not to turn off ALTER FUNCTION functest_E_1(int) OWNER TO regress_unpriv_user; ALTER FUNCTION functest_E_2(int) OWNER TO regress_unpriv_user; SET SESSION AUTHORIZATION regress_unpriv_user; SET search_path TO temp_func_test, public; ALTER FUNCTION functest_E_1(int) NOT LEAKPROOF; ALTER FUNCTION functest_E_2(int) LEAKPROOF; CREATE FUNCTION functest_E_3(int) RETURNS bool LANGUAGE 'sql' LEAKPROOF AS 'SELECT $1 < 200'; -- fail RESET SESSION AUTHORIZATION; -- -- CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT -- CREATE FUNCTION functest_F_1(int) RETURNS bool LANGUAGE 'sql' AS 'SELECT $1 > 50'; CREATE FUNCTION functest_F_2(int) RETURNS bool LANGUAGE 'sql' CALLED ON NULL INPUT AS 'SELECT $1 = 50'; CREATE FUNCTION functest_F_3(int) RETURNS bool LANGUAGE 'sql' RETURNS NULL ON NULL INPUT AS 'SELECT $1 < 50'; CREATE FUNCTION functest_F_4(int) RETURNS bool LANGUAGE 'sql' STRICT AS 'SELECT $1 = 50'; SELECT proname, proisstrict FROM pg_proc WHERE oid in ('functest_F_1'::regproc, 'functest_F_2'::regproc, 'functest_F_3'::regproc, 'functest_F_4'::regproc) ORDER BY proname; ALTER FUNCTION functest_F_1(int) IMMUTABLE; -- unrelated change, no effect ALTER FUNCTION functest_F_2(int) STRICT; ALTER FUNCTION functest_F_3(int) CALLED ON NULL INPUT; SELECT proname, proisstrict FROM pg_proc WHERE oid in ('functest_F_1'::regproc, 'functest_F_2'::regproc, 'functest_F_3'::regproc, 'functest_F_4'::regproc) ORDER BY proname; -- pg_get_functiondef tests SELECT pg_get_functiondef('functest_A_1'::regproc); SELECT pg_get_functiondef('functest_B_3'::regproc); SELECT pg_get_functiondef('functest_C_3'::regproc); SELECT pg_get_functiondef('functest_F_2'::regproc); -- information_schema tests CREATE FUNCTION functest_IS_1(a int, b int default 1, c text default 'foo') RETURNS int LANGUAGE SQL AS 'SELECT $1 + $2'; CREATE FUNCTION functest_IS_2(out a int, b int default 1) RETURNS int LANGUAGE SQL AS 'SELECT $1'; CREATE FUNCTION functest_IS_3(a int default 1, out b int) RETURNS int LANGUAGE SQL AS 'SELECT $1'; SELECT routine_name, ordinal_position, parameter_name, parameter_default FROM information_schema.parameters JOIN information_schema.routines USING (specific_schema, specific_name) WHERE routine_schema = 'temp_func_test' AND routine_name ~ '^functest_is_' ORDER BY 1, 2; DROP FUNCTION functest_IS_1(int, int, text), functest_IS_2(int), functest_IS_3(int); -- overload CREATE FUNCTION functest_B_2(bigint) RETURNS bool LANGUAGE 'sql' IMMUTABLE AS 'SELECT $1 > 0'; DROP FUNCTION functest_b_1; DROP FUNCTION functest_b_1; -- error, not found DROP FUNCTION functest_b_2; -- error, ambiguous -- CREATE OR REPLACE tests CREATE FUNCTION functest1(a int) RETURNS int LANGUAGE SQL AS 'SELECT $1'; CREATE OR REPLACE FUNCTION functest1(a int) RETURNS int LANGUAGE SQL WINDOW AS 'SELECT $1'; CREATE OR REPLACE PROCEDURE functest1(a int) LANGUAGE SQL AS 'SELECT $1'; DROP FUNCTION functest1(a int); -- Check behavior of VOID-returning SQL functions CREATE FUNCTION voidtest1(a int) RETURNS VOID LANGUAGE SQL AS $$ SELECT a + 1 $$; SELECT voidtest1(42); CREATE FUNCTION voidtest2(a int, b int) RETURNS VOID LANGUAGE SQL AS $$ SELECT voidtest1(a + b) $$; SELECT voidtest2(11,22); -- currently, we can inline voidtest2 but not voidtest1 EXPLAIN (verbose, costs off) SELECT voidtest2(11,22); CREATE TEMP TABLE sometable(f1 int); CREATE FUNCTION voidtest3(a int) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO sometable VALUES(a + 1) $$; SELECT voidtest3(17); CREATE FUNCTION voidtest4(a int) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO sometable VALUES(a - 1) RETURNING f1 $$; SELECT voidtest4(39); TABLE sometable; CREATE FUNCTION voidtest5(a int) RETURNS SETOF VOID LANGUAGE SQL AS $$ SELECT generate_series(1, a) $$ STABLE; SELECT * FROM voidtest5(3); -- Cleanup DROP SCHEMA temp_func_test CASCADE; DROP USER regress_unpriv_user; RESET search_path; pgFormatter-4.2/t/pg-test-files/sql/create_index.sql000066400000000000000000001017471361326045100225640ustar00rootroot00000000000000-- -- CREATE_INDEX -- Create ancillary data structures (i.e. indices) -- -- -- BTREE -- CREATE INDEX onek_unique1 ON onek USING btree(unique1 int4_ops); CREATE INDEX IF NOT EXISTS onek_unique1 ON onek USING btree(unique1 int4_ops); CREATE INDEX IF NOT EXISTS ON onek USING btree(unique1 int4_ops); CREATE INDEX onek_unique2 ON onek USING btree(unique2 int4_ops); CREATE INDEX onek_hundred ON onek USING btree(hundred int4_ops); CREATE INDEX onek_stringu1 ON onek USING btree(stringu1 name_ops); CREATE INDEX tenk1_unique1 ON tenk1 USING btree(unique1 int4_ops); CREATE INDEX tenk1_unique2 ON tenk1 USING btree(unique2 int4_ops); CREATE INDEX tenk1_hundred ON tenk1 USING btree(hundred int4_ops); CREATE INDEX tenk1_thous_tenthous ON tenk1 (thousand, tenthous); CREATE INDEX tenk2_unique1 ON tenk2 USING btree(unique1 int4_ops); CREATE INDEX tenk2_unique2 ON tenk2 USING btree(unique2 int4_ops); CREATE INDEX tenk2_hundred ON tenk2 USING btree(hundred int4_ops); CREATE INDEX rix ON road USING btree (name text_ops); CREATE INDEX iix ON ihighway USING btree (name text_ops); CREATE INDEX six ON shighway USING btree (name text_ops); -- test comments COMMENT ON INDEX six_wrong IS 'bad index'; COMMENT ON INDEX six IS 'good index'; COMMENT ON INDEX six IS NULL; -- -- BTREE ascending/descending cases -- -- we load int4/text from pure descending data (each key is a new -- low key) and name/f8 from pure ascending data (each key is a new -- high key). we had a bug where new low keys would sometimes be -- "lost". -- CREATE INDEX bt_i4_index ON bt_i4_heap USING btree (seqno int4_ops); CREATE INDEX bt_name_index ON bt_name_heap USING btree (seqno name_ops); CREATE INDEX bt_txt_index ON bt_txt_heap USING btree (seqno text_ops); CREATE INDEX bt_f8_index ON bt_f8_heap USING btree (seqno float8_ops); -- -- BTREE partial indices -- CREATE INDEX onek2_u1_prtl ON onek2 USING btree(unique1 int4_ops) where unique1 < 20 or unique1 > 980; CREATE INDEX onek2_u2_prtl ON onek2 USING btree(unique2 int4_ops) where stringu1 < 'B'; CREATE INDEX onek2_stu1_prtl ON onek2 USING btree(stringu1 name_ops) where onek2.stringu1 >= 'J' and onek2.stringu1 < 'K'; -- -- GiST (rtree-equivalent opclasses only) -- CREATE INDEX grect2ind ON fast_emp4000 USING gist (home_base); CREATE INDEX gpolygonind ON polygon_tbl USING gist (f1); CREATE INDEX gcircleind ON circle_tbl USING gist (f1); INSERT INTO POINT_TBL(f1) VALUES (NULL); CREATE INDEX gpointind ON point_tbl USING gist (f1); CREATE TEMP TABLE gpolygon_tbl AS SELECT polygon(home_base) AS f1 FROM slow_emp4000; INSERT INTO gpolygon_tbl VALUES ( '(1000,0,0,1000)' ); INSERT INTO gpolygon_tbl VALUES ( '(0,1000,1000,1000)' ); CREATE TEMP TABLE gcircle_tbl AS SELECT circle(home_base) AS f1 FROM slow_emp4000; CREATE INDEX ggpolygonind ON gpolygon_tbl USING gist (f1); CREATE INDEX ggcircleind ON gcircle_tbl USING gist (f1); -- -- Test GiST indexes -- -- get non-indexed results for comparison purposes SET enable_seqscan = ON; SET enable_indexscan = OFF; SET enable_bitmapscan = OFF; SELECT * FROM fast_emp4000 WHERE home_base @ '(200,200),(2000,1000)'::box ORDER BY (home_base[0])[0]; SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box; SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon ORDER BY (poly_center(f1))[0]; SELECT * FROM circle_tbl WHERE f1 && circle(point(1,-2), 1) ORDER BY area(f1); SELECT count(*) FROM gpolygon_tbl WHERE f1 && '(1000,1000,0,0)'::polygon; SELECT count(*) FROM gcircle_tbl WHERE f1 && '<(500,500),500>'::circle; SELECT count(*) FROM point_tbl WHERE f1 <@ box '(0,0,100,100)'; SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1; SELECT count(*) FROM point_tbl WHERE f1 <@ polygon '(0,0),(0,100),(100,100),(50,50),(100,0),(0,0)'; SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>'; SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)'; SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)'; SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)'; SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)'; SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)'; SELECT * FROM point_tbl ORDER BY f1 <-> '0,1'; SELECT * FROM point_tbl WHERE f1 IS NULL; SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1'; SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1'; SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10; SELECT circle_center(f1), round(radius(f1)) as radius FROM gcircle_tbl ORDER BY f1 <-> '(200,300)'::point LIMIT 10; -- Now check the results from plain indexscan SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = OFF; EXPLAIN (COSTS OFF) SELECT * FROM fast_emp4000 WHERE home_base @ '(200,200),(2000,1000)'::box ORDER BY (home_base[0])[0]; SELECT * FROM fast_emp4000 WHERE home_base @ '(200,200),(2000,1000)'::box ORDER BY (home_base[0])[0]; EXPLAIN (COSTS OFF) SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box; SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box; EXPLAIN (COSTS OFF) SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; EXPLAIN (COSTS OFF) SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon ORDER BY (poly_center(f1))[0]; SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon ORDER BY (poly_center(f1))[0]; EXPLAIN (COSTS OFF) SELECT * FROM circle_tbl WHERE f1 && circle(point(1,-2), 1) ORDER BY area(f1); SELECT * FROM circle_tbl WHERE f1 && circle(point(1,-2), 1) ORDER BY area(f1); EXPLAIN (COSTS OFF) SELECT count(*) FROM gpolygon_tbl WHERE f1 && '(1000,1000,0,0)'::polygon; SELECT count(*) FROM gpolygon_tbl WHERE f1 && '(1000,1000,0,0)'::polygon; EXPLAIN (COSTS OFF) SELECT count(*) FROM gcircle_tbl WHERE f1 && '<(500,500),500>'::circle; SELECT count(*) FROM gcircle_tbl WHERE f1 && '<(500,500),500>'::circle; EXPLAIN (COSTS OFF) SELECT count(*) FROM point_tbl WHERE f1 <@ box '(0,0,100,100)'; SELECT count(*) FROM point_tbl WHERE f1 <@ box '(0,0,100,100)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1; SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1; EXPLAIN (COSTS OFF) SELECT count(*) FROM point_tbl WHERE f1 <@ polygon '(0,0),(0,100),(100,100),(50,50),(100,0),(0,0)'; SELECT count(*) FROM point_tbl WHERE f1 <@ polygon '(0,0),(0,100),(100,100),(50,50),(100,0),(0,0)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>'; SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>'; EXPLAIN (COSTS OFF) SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)'; SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)'; SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)'; SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)'; SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)'; SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)'; EXPLAIN (COSTS OFF) SELECT * FROM point_tbl ORDER BY f1 <-> '0,1'; SELECT * FROM point_tbl ORDER BY f1 <-> '0,1'; EXPLAIN (COSTS OFF) SELECT * FROM point_tbl WHERE f1 IS NULL; SELECT * FROM point_tbl WHERE f1 IS NULL; EXPLAIN (COSTS OFF) SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1'; SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1'; EXPLAIN (COSTS OFF) SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1'; SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1'; EXPLAIN (COSTS OFF) SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10; SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10; EXPLAIN (COSTS OFF) SELECT circle_center(f1), round(radius(f1)) as radius FROM gcircle_tbl ORDER BY f1 <-> '(200,300)'::point LIMIT 10; SELECT circle_center(f1), round(radius(f1)) as radius FROM gcircle_tbl ORDER BY f1 <-> '(200,300)'::point LIMIT 10; -- Now check the results from bitmap indexscan SET enable_seqscan = OFF; SET enable_indexscan = OFF; SET enable_bitmapscan = ON; EXPLAIN (COSTS OFF) SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1'; SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1'; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; -- -- GIN over int[] and text[] -- -- Note: GIN currently supports only bitmap scans, not plain indexscans -- SET enable_seqscan = OFF; SET enable_indexscan = OFF; SET enable_bitmapscan = ON; CREATE INDEX intarrayidx ON array_index_op_test USING gin (i); explain (costs off) SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i && '{32}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i @> '{17}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i && '{17}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i @> '{32,17}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i && '{32,17}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i <@ '{38,34,32,89}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i = '{47,77}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i = '{}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i @> '{}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i && '{}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i <@ '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno; CREATE INDEX textarrayidx ON array_index_op_test USING gin (t); explain (costs off) SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t && '{AAAAAAAAAA646}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t && '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t <@ '{AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t = '{AAAAAAAAAA646,A87088}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t = '{}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t @> '{}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t && '{}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t <@ '{}' ORDER BY seqno; -- And try it with a multicolumn GIN index DROP INDEX intarrayidx, textarrayidx; CREATE INDEX botharrayidx ON array_index_op_test USING gin (i, t); SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i && '{32}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAA80240}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t && '{AAAAAAA80240}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i @> '{32}' AND t && '{AAAAAAA80240}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE i && '{32}' AND t @> '{AAAAAAA80240}' ORDER BY seqno; SELECT * FROM array_index_op_test WHERE t = '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno; SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; -- -- Try a GIN index with a lot of items with same key. (GIN creates a posting -- tree when there are enough duplicates) -- CREATE TABLE array_gin_test (a int[]); INSERT INTO array_gin_test SELECT ARRAY[1, g%5, g] FROM generate_series(1, 10000) g; CREATE INDEX array_gin_test_idx ON array_gin_test USING gin (a); SELECT COUNT(*) FROM array_gin_test WHERE a @> '{2}'; DROP TABLE array_gin_test; -- -- Test GIN index's reloptions -- CREATE INDEX gin_relopts_test ON array_index_op_test USING gin (i) WITH (FASTUPDATE=on, GIN_PENDING_LIST_LIMIT=128); \d+ gin_relopts_test -- -- HASH -- CREATE INDEX hash_i4_index ON hash_i4_heap USING hash (random int4_ops); CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops); CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops); CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60); CREATE UNLOGGED TABLE unlogged_hash_table (id int4); CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops); DROP TABLE unlogged_hash_table; -- CREATE INDEX hash_ovfl_index ON hash_ovfl_heap USING hash (x int4_ops); -- Test hash index build tuplesorting. Force hash tuplesort using low -- maintenance_work_mem setting and fillfactor: SET maintenance_work_mem = '1MB'; CREATE INDEX hash_tuplesort_idx ON tenk1 USING hash (stringu1 name_ops) WITH (fillfactor = 10); EXPLAIN (COSTS OFF) SELECT count(*) FROM tenk1 WHERE stringu1 = 'TVAAAA'; SELECT count(*) FROM tenk1 WHERE stringu1 = 'TVAAAA'; DROP INDEX hash_tuplesort_idx; RESET maintenance_work_mem; -- -- Test functional index -- CREATE TABLE func_index_heap (f1 text, f2 text); CREATE UNIQUE INDEX func_index_index on func_index_heap (textcat(f1,f2)); INSERT INTO func_index_heap VALUES('ABC','DEF'); INSERT INTO func_index_heap VALUES('AB','CDEFG'); INSERT INTO func_index_heap VALUES('QWE','RTY'); -- this should fail because of unique index: INSERT INTO func_index_heap VALUES('ABCD', 'EF'); -- but this shouldn't: INSERT INTO func_index_heap VALUES('QWERTY'); -- -- Same test, expressional index -- DROP TABLE func_index_heap; CREATE TABLE func_index_heap (f1 text, f2 text); CREATE UNIQUE INDEX func_index_index on func_index_heap ((f1 || f2) text_ops); INSERT INTO func_index_heap VALUES('ABC','DEF'); INSERT INTO func_index_heap VALUES('AB','CDEFG'); INSERT INTO func_index_heap VALUES('QWE','RTY'); -- this should fail because of unique index: INSERT INTO func_index_heap VALUES('ABCD', 'EF'); -- but this shouldn't: INSERT INTO func_index_heap VALUES('QWERTY'); -- -- Test unique index with included columns -- CREATE TABLE covering_index_heap (f1 int, f2 int, f3 text); CREATE UNIQUE INDEX covering_index_index on covering_index_heap (f1,f2) INCLUDE(f3); INSERT INTO covering_index_heap VALUES(1,1,'AAA'); INSERT INTO covering_index_heap VALUES(1,2,'AAA'); -- this should fail because of unique index on f1,f2: INSERT INTO covering_index_heap VALUES(1,2,'BBB'); -- and this shouldn't: INSERT INTO covering_index_heap VALUES(1,4,'AAA'); -- Try to build index on table that already contains data CREATE UNIQUE INDEX covering_pkey on covering_index_heap (f1,f2) INCLUDE(f3); -- Try to use existing covering index as primary key ALTER TABLE covering_index_heap ADD CONSTRAINT covering_pkey PRIMARY KEY USING INDEX covering_pkey; DROP TABLE covering_index_heap; -- -- Also try building functional, expressional, and partial indexes on -- tables that already contain data. -- create unique index hash_f8_index_1 on hash_f8_heap(abs(random)); create unique index hash_f8_index_2 on hash_f8_heap((seqno + 1), random); create unique index hash_f8_index_3 on hash_f8_heap(random) where seqno > 1000; -- -- Try some concurrent index builds -- -- Unfortunately this only tests about half the code paths because there are -- no concurrent updates happening to the table at the same time. CREATE TABLE concur_heap (f1 text, f2 text); -- empty table CREATE INDEX CONCURRENTLY concur_index1 ON concur_heap(f2,f1); CREATE INDEX CONCURRENTLY IF NOT EXISTS concur_index1 ON concur_heap(f2,f1); INSERT INTO concur_heap VALUES ('a','b'); INSERT INTO concur_heap VALUES ('b','b'); -- unique index CREATE UNIQUE INDEX CONCURRENTLY concur_index2 ON concur_heap(f1); CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS concur_index2 ON concur_heap(f1); -- check if constraint is set up properly to be enforced INSERT INTO concur_heap VALUES ('b','x'); -- check if constraint is enforced properly at build time CREATE UNIQUE INDEX CONCURRENTLY concur_index3 ON concur_heap(f2); -- test that expression indexes and partial indexes work concurrently CREATE INDEX CONCURRENTLY concur_index4 on concur_heap(f2) WHERE f1='a'; CREATE INDEX CONCURRENTLY concur_index5 on concur_heap(f2) WHERE f1='x'; -- here we also check that you can default the index name CREATE INDEX CONCURRENTLY on concur_heap((f2||f1)); -- You can't do a concurrent index build in a transaction BEGIN; CREATE INDEX CONCURRENTLY concur_index7 ON concur_heap(f1); COMMIT; -- But you can do a regular index build in a transaction BEGIN; CREATE INDEX std_index on concur_heap(f2); COMMIT; -- Failed builds are left invalid by VACUUM FULL, fixed by REINDEX VACUUM FULL concur_heap; REINDEX TABLE concur_heap; DELETE FROM concur_heap WHERE f1 = 'b'; VACUUM FULL concur_heap; \d concur_heap REINDEX TABLE concur_heap; \d concur_heap -- -- Try some concurrent index drops -- DROP INDEX CONCURRENTLY "concur_index2"; -- works DROP INDEX CONCURRENTLY IF EXISTS "concur_index2"; -- notice -- failures DROP INDEX CONCURRENTLY "concur_index2", "concur_index3"; BEGIN; DROP INDEX CONCURRENTLY "concur_index5"; ROLLBACK; -- successes DROP INDEX CONCURRENTLY IF EXISTS "concur_index3"; DROP INDEX CONCURRENTLY "concur_index4"; DROP INDEX CONCURRENTLY "concur_index5"; DROP INDEX CONCURRENTLY "concur_index1"; DROP INDEX CONCURRENTLY "concur_heap_expr_idx"; \d concur_heap DROP TABLE concur_heap; -- -- Test ADD CONSTRAINT USING INDEX -- CREATE TABLE cwi_test( a int , b varchar(10), c char); -- add some data so that all tests have something to work with. INSERT INTO cwi_test VALUES(1, 2), (3, 4), (5, 6); CREATE UNIQUE INDEX cwi_uniq_idx ON cwi_test(a , b); ALTER TABLE cwi_test ADD primary key USING INDEX cwi_uniq_idx; \d cwi_test \d cwi_uniq_idx CREATE UNIQUE INDEX cwi_uniq2_idx ON cwi_test(b , a); ALTER TABLE cwi_test DROP CONSTRAINT cwi_uniq_idx, ADD CONSTRAINT cwi_replaced_pkey PRIMARY KEY USING INDEX cwi_uniq2_idx; \d cwi_test \d cwi_replaced_pkey DROP INDEX cwi_replaced_pkey; -- Should fail; a constraint depends on it DROP TABLE cwi_test; -- ADD CONSTRAINT USING INDEX is forbidden on partitioned tables CREATE TABLE cwi_test(a int) PARTITION BY hash (a); create unique index on cwi_test (a); alter table cwi_test add primary key using index cwi_test_a_idx ; DROP TABLE cwi_test; -- -- Check handling of indexes on system columns -- CREATE TABLE syscol_table (a INT); -- System columns cannot be indexed CREATE INDEX ON syscolcol_table (ctid); -- nor used in expressions CREATE INDEX ON syscol_table ((ctid >= '(1000,0)')); -- nor used in predicates CREATE INDEX ON syscol_table (a) WHERE ctid >= '(1000,0)'; DROP TABLE syscol_table; -- -- Tests for IS NULL/IS NOT NULL with b-tree indexes -- SELECT unique1, unique2 INTO onek_with_null FROM onek; INSERT INTO onek_with_null (unique1,unique2) VALUES (NULL, -1), (NULL, NULL); CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2,unique1); SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = ON; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NOT NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL AND unique1 > 500; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique1 > 500; DROP INDEX onek_nulltest; CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2 desc,unique1); SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NOT NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL AND unique1 > 500; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique1 > 500; DROP INDEX onek_nulltest; CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2 desc nulls last,unique1); SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NOT NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL AND unique1 > 500; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique1 > 500; DROP INDEX onek_nulltest; CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2 nulls first,unique1); SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NOT NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL AND unique1 > 500; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique1 > 500; DROP INDEX onek_nulltest; -- Check initial-positioning logic too CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2); SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = OFF; SELECT unique1, unique2 FROM onek_with_null ORDER BY unique2 LIMIT 2; SELECT unique1, unique2 FROM onek_with_null WHERE unique2 >= -1 ORDER BY unique2 LIMIT 2; SELECT unique1, unique2 FROM onek_with_null WHERE unique2 >= 0 ORDER BY unique2 LIMIT 2; SELECT unique1, unique2 FROM onek_with_null ORDER BY unique2 DESC LIMIT 2; SELECT unique1, unique2 FROM onek_with_null WHERE unique2 >= -1 ORDER BY unique2 DESC LIMIT 2; SELECT unique1, unique2 FROM onek_with_null WHERE unique2 < 999 ORDER BY unique2 DESC LIMIT 2; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; DROP TABLE onek_with_null; -- -- Check bitmap index path planning -- EXPLAIN (COSTS OFF) SELECT * FROM tenk1 WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42); SELECT * FROM tenk1 WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42); EXPLAIN (COSTS OFF) SELECT count(*) FROM tenk1 WHERE hundred = 42 AND (thousand = 42 OR thousand = 99); SELECT count(*) FROM tenk1 WHERE hundred = 42 AND (thousand = 42 OR thousand = 99); -- -- Check behavior with duplicate index column contents -- CREATE TABLE dupindexcols AS SELECT unique1 as id, stringu2::text as f1 FROM tenk1; CREATE INDEX dupindexcols_i ON dupindexcols (f1, id, f1 text_pattern_ops); ANALYZE dupindexcols; EXPLAIN (COSTS OFF) SELECT count(*) FROM dupindexcols WHERE f1 BETWEEN 'WA' AND 'ZZZ' and id < 1000 and f1 ~<~ 'YX'; SELECT count(*) FROM dupindexcols WHERE f1 BETWEEN 'WA' AND 'ZZZ' and id < 1000 and f1 ~<~ 'YX'; -- -- Check ordering of =ANY indexqual results (bug in 9.2.0) -- vacuum tenk1; -- ensure we get consistent plans here explain (costs off) SELECT unique1 FROM tenk1 WHERE unique1 IN (1,42,7) ORDER BY unique1; SELECT unique1 FROM tenk1 WHERE unique1 IN (1,42,7) ORDER BY unique1; explain (costs off) SELECT thousand, tenthous FROM tenk1 WHERE thousand < 2 AND tenthous IN (1001,3000) ORDER BY thousand; SELECT thousand, tenthous FROM tenk1 WHERE thousand < 2 AND tenthous IN (1001,3000) ORDER BY thousand; SET enable_indexonlyscan = OFF; explain (costs off) SELECT thousand, tenthous FROM tenk1 WHERE thousand < 2 AND tenthous IN (1001,3000) ORDER BY thousand; SELECT thousand, tenthous FROM tenk1 WHERE thousand < 2 AND tenthous IN (1001,3000) ORDER BY thousand; RESET enable_indexonlyscan; -- -- Check elimination of constant-NULL subexpressions -- explain (costs off) select * from tenk1 where (thousand, tenthous) in ((1,1001), (null,null)); -- -- Check matching of boolean index columns to WHERE conditions and sort keys -- create temp table boolindex (b bool, i int, unique(b, i), junk float); explain (costs off) select * from boolindex order by b, i limit 10; explain (costs off) select * from boolindex where b order by i limit 10; explain (costs off) select * from boolindex where b = true order by i desc limit 10; explain (costs off) select * from boolindex where not b order by i limit 10; explain (costs off) select * from boolindex where b is true order by i desc limit 10; explain (costs off) select * from boolindex where b is false order by i desc limit 10; -- -- REINDEX (VERBOSE) -- CREATE TABLE reindex_verbose(id integer primary key); \set VERBOSITY terse \\ -- suppress machine-dependent details REINDEX (VERBOSE) TABLE reindex_verbose; \set VERBOSITY default DROP TABLE reindex_verbose; -- -- REINDEX CONCURRENTLY -- CREATE TABLE concur_reindex_tab (c1 int); -- REINDEX REINDEX TABLE concur_reindex_tab; -- notice REINDEX TABLE CONCURRENTLY concur_reindex_tab; -- notice ALTER TABLE concur_reindex_tab ADD COLUMN c2 text; -- add toast index -- Normal index with integer column CREATE UNIQUE INDEX concur_reindex_ind1 ON concur_reindex_tab(c1); -- Normal index with text column CREATE INDEX concur_reindex_ind2 ON concur_reindex_tab(c2); -- UNIQUE index with expression CREATE UNIQUE INDEX concur_reindex_ind3 ON concur_reindex_tab(abs(c1)); -- Duplicate column names CREATE INDEX concur_reindex_ind4 ON concur_reindex_tab(c1, c1, c2); -- Create table for check on foreign key dependence switch with indexes swapped ALTER TABLE concur_reindex_tab ADD PRIMARY KEY USING INDEX concur_reindex_ind1; CREATE TABLE concur_reindex_tab2 (c1 int REFERENCES concur_reindex_tab); INSERT INTO concur_reindex_tab VALUES (1, 'a'); INSERT INTO concur_reindex_tab VALUES (2, 'a'); -- Reindex concurrently of exclusion constraint currently not supported CREATE TABLE concur_reindex_tab3 (c1 int, c2 int4range, EXCLUDE USING gist (c2 WITH &&)); INSERT INTO concur_reindex_tab3 VALUES (3, '[1,2]'); REINDEX INDEX CONCURRENTLY concur_reindex_tab3_c2_excl; -- error REINDEX TABLE CONCURRENTLY concur_reindex_tab3; -- succeeds with warning INSERT INTO concur_reindex_tab3 VALUES (4, '[2,4]'); -- Check materialized views CREATE MATERIALIZED VIEW concur_reindex_matview AS SELECT * FROM concur_reindex_tab; REINDEX INDEX CONCURRENTLY concur_reindex_ind1; REINDEX TABLE CONCURRENTLY concur_reindex_tab; REINDEX TABLE CONCURRENTLY concur_reindex_matview; -- Check that comments are preserved CREATE TABLE testcomment (i int); CREATE INDEX testcomment_idx1 ON testcomment (i); COMMENT ON INDEX testcomment_idx1 IS 'test comment'; SELECT obj_description('testcomment_idx1'::regclass, 'pg_class'); REINDEX TABLE testcomment; SELECT obj_description('testcomment_idx1'::regclass, 'pg_class'); REINDEX TABLE CONCURRENTLY testcomment ; SELECT obj_description('testcomment_idx1'::regclass, 'pg_class'); DROP TABLE testcomment; -- Partitions -- Create some partitioned tables CREATE TABLE concur_reindex_part (c1 int, c2 int) PARTITION BY RANGE (c1); CREATE TABLE concur_reindex_part_0 PARTITION OF concur_reindex_part FOR VALUES FROM (0) TO (10) PARTITION BY list (c2); CREATE TABLE concur_reindex_part_0_1 PARTITION OF concur_reindex_part_0 FOR VALUES IN (1); CREATE TABLE concur_reindex_part_0_2 PARTITION OF concur_reindex_part_0 FOR VALUES IN (2); -- This partitioned table will have no partitions. CREATE TABLE concur_reindex_part_10 PARTITION OF concur_reindex_part FOR VALUES FROM (10) TO (20) PARTITION BY list (c2); -- Create some partitioned indexes CREATE INDEX concur_reindex_part_index ON ONLY concur_reindex_part (c1); CREATE INDEX concur_reindex_part_index_0 ON ONLY concur_reindex_part_0 (c1); ALTER INDEX concur_reindex_part_index ATTACH PARTITION concur_reindex_part_index_0; -- This partitioned index will have no partitions. CREATE INDEX concur_reindex_part_index_10 ON ONLY concur_reindex_part_10 (c1); ALTER INDEX concur_reindex_part_index ATTACH PARTITION concur_reindex_part_index_10; CREATE INDEX concur_reindex_part_index_0_1 ON ONLY concur_reindex_part_0_1 (c1); ALTER INDEX concur_reindex_part_index_0 ATTACH PARTITION concur_reindex_part_index_0_1; CREATE INDEX concur_reindex_part_index_0_2 ON ONLY concur_reindex_part_0_2 (c1); ALTER INDEX concur_reindex_part_index_0 ATTACH PARTITION concur_reindex_part_index_0_2; SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index') ORDER BY relid, level; -- REINDEX fails for partitioned indexes REINDEX INDEX concur_reindex_part_index_10; REINDEX INDEX CONCURRENTLY concur_reindex_part_index_10; -- REINDEX is a no-op for partitioned tables REINDEX TABLE concur_reindex_part_10; REINDEX TABLE CONCURRENTLY concur_reindex_part_10; SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index') ORDER BY relid, level; -- REINDEX should preserve dependencies of partition tree. REINDEX INDEX CONCURRENTLY concur_reindex_part_index_0_1; REINDEX INDEX CONCURRENTLY concur_reindex_part_index_0_2; SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index') ORDER BY relid, level; REINDEX TABLE CONCURRENTLY concur_reindex_part_0_1; REINDEX TABLE CONCURRENTLY concur_reindex_part_0_2; SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index') ORDER BY relid, level; DROP TABLE concur_reindex_part; -- Check errors -- Cannot run inside a transaction block BEGIN; REINDEX TABLE CONCURRENTLY concur_reindex_tab; COMMIT; REINDEX TABLE CONCURRENTLY pg_database; -- no shared relation REINDEX TABLE CONCURRENTLY pg_class; -- no catalog relations REINDEX SYSTEM CONCURRENTLY postgres; -- not allowed for SYSTEM -- Warns about catalog relations REINDEX SCHEMA CONCURRENTLY pg_catalog; -- Check the relation status, there should not be invalid indexes \d concur_reindex_tab DROP MATERIALIZED VIEW concur_reindex_matview; DROP TABLE concur_reindex_tab, concur_reindex_tab2, concur_reindex_tab3; -- Check handling of invalid indexes CREATE TABLE concur_reindex_tab4 (c1 int); INSERT INTO concur_reindex_tab4 VALUES (1), (1), (2); -- This trick creates an invalid index. CREATE UNIQUE INDEX CONCURRENTLY concur_reindex_ind5 ON concur_reindex_tab4 (c1); -- Reindexing concurrently this index fails with the same failure. -- The extra index created is itself invalid, and can be dropped. REINDEX INDEX CONCURRENTLY concur_reindex_ind5; \d concur_reindex_tab4 DROP INDEX concur_reindex_ind5_ccnew; -- This makes the previous failure go away, so the index can become valid. DELETE FROM concur_reindex_tab4 WHERE c1 = 1; -- The invalid index is not processed when running REINDEX TABLE. REINDEX TABLE CONCURRENTLY concur_reindex_tab4; \d concur_reindex_tab4 -- But it is fixed with REINDEX INDEX. REINDEX INDEX CONCURRENTLY concur_reindex_ind5; \d concur_reindex_tab4 DROP TABLE concur_reindex_tab4; -- -- REINDEX SCHEMA -- REINDEX SCHEMA schema_to_reindex; -- failure, schema does not exist CREATE SCHEMA schema_to_reindex; SET search_path = 'schema_to_reindex'; CREATE TABLE table1(col1 SERIAL PRIMARY KEY); INSERT INTO table1 SELECT generate_series(1,400); CREATE TABLE table2(col1 SERIAL PRIMARY KEY, col2 TEXT NOT NULL); INSERT INTO table2 SELECT generate_series(1,400), 'abc'; CREATE INDEX ON table2(col2); CREATE MATERIALIZED VIEW matview AS SELECT col1 FROM table2; CREATE INDEX ON matview(col1); CREATE VIEW view AS SELECT col2 FROM table2; CREATE TABLE reindex_before AS SELECT oid, relname, relfilenode, relkind, reltoastrelid FROM pg_class where relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'schema_to_reindex'); INSERT INTO reindex_before SELECT oid, 'pg_toast_TABLE', relfilenode, relkind, reltoastrelid FROM pg_class WHERE oid IN (SELECT reltoastrelid FROM reindex_before WHERE reltoastrelid > 0); INSERT INTO reindex_before SELECT oid, 'pg_toast_TABLE_index', relfilenode, relkind, reltoastrelid FROM pg_class where oid in (select indexrelid from pg_index where indrelid in (select reltoastrelid from reindex_before where reltoastrelid > 0)); REINDEX SCHEMA schema_to_reindex; CREATE TABLE reindex_after AS SELECT oid, relname, relfilenode, relkind FROM pg_class where relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'schema_to_reindex'); SELECT b.relname, b.relkind, CASE WHEN a.relfilenode = b.relfilenode THEN 'relfilenode is unchanged' ELSE 'relfilenode has changed' END FROM reindex_before b JOIN pg_class a ON b.oid = a.oid ORDER BY 1; REINDEX SCHEMA schema_to_reindex; BEGIN; REINDEX SCHEMA schema_to_reindex; -- failure, cannot run in a transaction END; -- concurrently REINDEX SCHEMA CONCURRENTLY schema_to_reindex; -- Failure for unauthorized user CREATE ROLE regress_reindexuser NOLOGIN; SET SESSION ROLE regress_reindexuser; REINDEX SCHEMA schema_to_reindex; -- Clean up RESET ROLE; DROP ROLE regress_reindexuser; DROP SCHEMA schema_to_reindex CASCADE; pgFormatter-4.2/t/pg-test-files/sql/create_index_spgist.sql000066400000000000000000000373771361326045100241640ustar00rootroot00000000000000-- -- SP-GiST index tests -- CREATE TABLE quad_point_tbl AS SELECT point(unique1,unique2) AS p FROM tenk1; INSERT INTO quad_point_tbl SELECT '(333.0,400.0)'::point FROM generate_series(1,1000); INSERT INTO quad_point_tbl VALUES (NULL), (NULL), (NULL); CREATE INDEX sp_quad_ind ON quad_point_tbl USING spgist (p); CREATE TABLE kd_point_tbl AS SELECT * FROM quad_point_tbl; CREATE INDEX sp_kd_ind ON kd_point_tbl USING spgist (p kd_point_ops); CREATE TABLE radix_text_tbl AS SELECT name AS t FROM road WHERE name !~ '^[0-9]'; INSERT INTO radix_text_tbl SELECT 'P0123456789abcdef' FROM generate_series(1,1000); INSERT INTO radix_text_tbl VALUES ('P0123456789abcde'); INSERT INTO radix_text_tbl VALUES ('P0123456789abcdefF'); CREATE INDEX sp_radix_ind ON radix_text_tbl USING spgist (t); -- get non-indexed results for comparison purposes SET enable_seqscan = ON; SET enable_indexscan = OFF; SET enable_bitmapscan = OFF; SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; SELECT count(*) FROM quad_point_tbl; SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; CREATE TEMP TABLE quad_point_tbl_ord_seq1 AS SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM quad_point_tbl; CREATE TEMP TABLE quad_point_tbl_ord_seq2 AS SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; CREATE TEMP TABLE quad_point_tbl_ord_seq3 AS SELECT row_number() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p FROM quad_point_tbl WHERE p IS NOT NULL; SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; -- Now check the results from plain indexscan SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = OFF; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl; SELECT count(*) FROM quad_point_tbl; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; EXPLAIN (COSTS OFF) SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM quad_point_tbl; CREATE TEMP TABLE quad_point_tbl_ord_idx1 AS SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM quad_point_tbl; SELECT * FROM quad_point_tbl_ord_seq1 seq FULL JOIN quad_point_tbl_ord_idx1 idx ON seq.n = idx.n WHERE seq.dist IS DISTINCT FROM idx.dist; EXPLAIN (COSTS OFF) SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; CREATE TEMP TABLE quad_point_tbl_ord_idx2 AS SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; SELECT * FROM quad_point_tbl_ord_seq2 seq FULL JOIN quad_point_tbl_ord_idx2 idx ON seq.n = idx.n WHERE seq.dist IS DISTINCT FROM idx.dist; EXPLAIN (COSTS OFF) SELECT row_number() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p FROM quad_point_tbl WHERE p IS NOT NULL; CREATE TEMP TABLE quad_point_tbl_ord_idx3 AS SELECT row_number() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p FROM quad_point_tbl WHERE p IS NOT NULL; SELECT * FROM quad_point_tbl_ord_seq3 seq FULL JOIN quad_point_tbl_ord_idx3 idx ON seq.n = idx.n WHERE seq.dist IS DISTINCT FROM idx.dist; EXPLAIN (COSTS OFF) SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; EXPLAIN (COSTS OFF) SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; EXPLAIN (COSTS OFF) SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM kd_point_tbl; CREATE TEMP TABLE kd_point_tbl_ord_idx1 AS SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM kd_point_tbl; SELECT * FROM quad_point_tbl_ord_seq1 seq FULL JOIN kd_point_tbl_ord_idx1 idx ON seq.n = idx.n WHERE seq.dist IS DISTINCT FROM idx.dist; EXPLAIN (COSTS OFF) SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; CREATE TEMP TABLE kd_point_tbl_ord_idx2 AS SELECT row_number() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; SELECT * FROM quad_point_tbl_ord_seq2 seq FULL JOIN kd_point_tbl_ord_idx2 idx ON seq.n = idx.n WHERE seq.dist IS DISTINCT FROM idx.dist; EXPLAIN (COSTS OFF) SELECT row_number() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p FROM kd_point_tbl WHERE p IS NOT NULL; CREATE TEMP TABLE kd_point_tbl_ord_idx3 AS SELECT row_number() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p FROM kd_point_tbl WHERE p IS NOT NULL; SELECT * FROM quad_point_tbl_ord_seq3 seq FULL JOIN kd_point_tbl_ord_idx3 idx ON seq.n = idx.n WHERE seq.dist IS DISTINCT FROM idx.dist; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; -- Now check the results from bitmap indexscan SET enable_seqscan = OFF; SET enable_indexscan = OFF; SET enable_bitmapscan = ON; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl; SELECT count(*) FROM quad_point_tbl; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; EXPLAIN (COSTS OFF) SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; EXPLAIN (COSTS OFF) SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; pgFormatter-4.2/t/pg-test-files/sql/create_misc.sql000066400000000000000000000123701361326045100224010ustar00rootroot00000000000000-- -- CREATE_MISC -- -- CLASS POPULATION -- (any resemblance to real life is purely coincidental) -- INSERT INTO tenk2 SELECT * FROM tenk1; SELECT * INTO TABLE onek2 FROM onek; INSERT INTO fast_emp4000 SELECT * FROM slow_emp4000; SELECT * INTO TABLE Bprime FROM tenk1 WHERE unique2 < 1000; INSERT INTO hobbies_r (name, person) SELECT 'posthacking', p.name FROM person* p WHERE p.name = 'mike' or p.name = 'jeff'; INSERT INTO hobbies_r (name, person) SELECT 'basketball', p.name FROM person p WHERE p.name = 'joe' or p.name = 'sally'; INSERT INTO hobbies_r (name) VALUES ('skywalking'); INSERT INTO equipment_r (name, hobby) VALUES ('advil', 'posthacking'); INSERT INTO equipment_r (name, hobby) VALUES ('peet''s coffee', 'posthacking'); INSERT INTO equipment_r (name, hobby) VALUES ('hightops', 'basketball'); INSERT INTO equipment_r (name, hobby) VALUES ('guts', 'skywalking'); INSERT INTO city VALUES ('Podunk', '(1,2),(3,4)', '100,127,1000'), ('Gotham', '(1000,34),(1100,334)', '123456,127,-1000,6789'); TABLE city; SELECT * INTO TABLE ramp FROM road WHERE name ~ '.*Ramp'; INSERT INTO ihighway SELECT * FROM road WHERE name ~ 'I- .*'; INSERT INTO shighway SELECT * FROM road WHERE name ~ 'State Hwy.*'; UPDATE shighway SET surface = 'asphalt'; INSERT INTO a_star (class, a) VALUES ('a', 1); INSERT INTO a_star (class, a) VALUES ('a', 2); INSERT INTO a_star (class) VALUES ('a'); INSERT INTO b_star (class, a, b) VALUES ('b', 3, 'mumble'::text); INSERT INTO b_star (class, a) VALUES ('b', 4); INSERT INTO b_star (class, b) VALUES ('b', 'bumble'::text); INSERT INTO b_star (class) VALUES ('b'); INSERT INTO c_star (class, a, c) VALUES ('c', 5, 'hi mom'::name); INSERT INTO c_star (class, a) VALUES ('c', 6); INSERT INTO c_star (class, c) VALUES ('c', 'hi paul'::name); INSERT INTO c_star (class) VALUES ('c'); INSERT INTO d_star (class, a, b, c, d) VALUES ('d', 7, 'grumble'::text, 'hi sunita'::name, '0.0'::float8); INSERT INTO d_star (class, a, b, c) VALUES ('d', 8, 'stumble'::text, 'hi koko'::name); INSERT INTO d_star (class, a, b, d) VALUES ('d', 9, 'rumble'::text, '1.1'::float8); INSERT INTO d_star (class, a, c, d) VALUES ('d', 10, 'hi kristin'::name, '10.01'::float8); INSERT INTO d_star (class, b, c, d) VALUES ('d', 'crumble'::text, 'hi boris'::name, '100.001'::float8); INSERT INTO d_star (class, a, b) VALUES ('d', 11, 'fumble'::text); INSERT INTO d_star (class, a, c) VALUES ('d', 12, 'hi avi'::name); INSERT INTO d_star (class, a, d) VALUES ('d', 13, '1000.0001'::float8); INSERT INTO d_star (class, b, c) VALUES ('d', 'tumble'::text, 'hi andrew'::name); INSERT INTO d_star (class, b, d) VALUES ('d', 'humble'::text, '10000.00001'::float8); INSERT INTO d_star (class, c, d) VALUES ('d', 'hi ginger'::name, '100000.000001'::float8); INSERT INTO d_star (class, a) VALUES ('d', 14); INSERT INTO d_star (class, b) VALUES ('d', 'jumble'::text); INSERT INTO d_star (class, c) VALUES ('d', 'hi jolly'::name); INSERT INTO d_star (class, d) VALUES ('d', '1000000.0000001'::float8); INSERT INTO d_star (class) VALUES ('d'); INSERT INTO e_star (class, a, c, e) VALUES ('e', 15, 'hi carol'::name, '-1'::int2); INSERT INTO e_star (class, a, c) VALUES ('e', 16, 'hi bob'::name); INSERT INTO e_star (class, a, e) VALUES ('e', 17, '-2'::int2); INSERT INTO e_star (class, c, e) VALUES ('e', 'hi michelle'::name, '-3'::int2); INSERT INTO e_star (class, a) VALUES ('e', 18); INSERT INTO e_star (class, c) VALUES ('e', 'hi elisa'::name); INSERT INTO e_star (class, e) VALUES ('e', '-4'::int2); INSERT INTO f_star (class, a, c, e, f) VALUES ('f', 19, 'hi claire'::name, '-5'::int2, '(1,3),(2,4)'::polygon); INSERT INTO f_star (class, a, c, e) VALUES ('f', 20, 'hi mike'::name, '-6'::int2); INSERT INTO f_star (class, a, c, f) VALUES ('f', 21, 'hi marcel'::name, '(11,44),(22,55),(33,66)'::polygon); INSERT INTO f_star (class, a, e, f) VALUES ('f', 22, '-7'::int2, '(111,555),(222,666),(333,777),(444,888)'::polygon); INSERT INTO f_star (class, c, e, f) VALUES ('f', 'hi keith'::name, '-8'::int2, '(1111,3333),(2222,4444)'::polygon); INSERT INTO f_star (class, a, c) VALUES ('f', 24, 'hi marc'::name); INSERT INTO f_star (class, a, e) VALUES ('f', 25, '-9'::int2); INSERT INTO f_star (class, a, f) VALUES ('f', 26, '(11111,33333),(22222,44444)'::polygon); INSERT INTO f_star (class, c, e) VALUES ('f', 'hi allison'::name, '-10'::int2); INSERT INTO f_star (class, c, f) VALUES ('f', 'hi jeff'::name, '(111111,333333),(222222,444444)'::polygon); INSERT INTO f_star (class, e, f) VALUES ('f', '-11'::int2, '(1111111,3333333),(2222222,4444444)'::polygon); INSERT INTO f_star (class, a) VALUES ('f', 27); INSERT INTO f_star (class, c) VALUES ('f', 'hi carl'::name); INSERT INTO f_star (class, e) VALUES ('f', '-12'::int2); INSERT INTO f_star (class, f) VALUES ('f', '(11111111,33333333),(22222222,44444444)'::polygon); INSERT INTO f_star (class) VALUES ('f'); -- -- for internal portal (cursor) tests -- CREATE TABLE iportaltest ( i int4, d float4, p polygon ); INSERT INTO iportaltest (i, d, p) VALUES (1, 3.567, '(3.0,1.0),(4.0,2.0)'::polygon); INSERT INTO iportaltest (i, d, p) VALUES (2, 89.05, '(4.0,2.0),(3.0,1.0)'::polygon); pgFormatter-4.2/t/pg-test-files/sql/create_operator.sql000066400000000000000000000134471361326045100233070ustar00rootroot00000000000000-- -- CREATE_OPERATOR -- CREATE OPERATOR ## ( leftarg = path, rightarg = path, function = path_inter, commutator = ## ); CREATE OPERATOR <% ( leftarg = point, rightarg = widget, procedure = pt_in_widget, commutator = >% , negator = >=% ); CREATE OPERATOR @#@ ( rightarg = int8, -- left unary procedure = numeric_fac ); CREATE OPERATOR #@# ( leftarg = int8, -- right unary procedure = numeric_fac ); CREATE OPERATOR #%# ( leftarg = int8, -- right unary procedure = numeric_fac ); -- Test operator created above SELECT point '(1,2)' <% widget '(0,0,3)' AS t, point '(1,2)' <% widget '(0,0,1)' AS f; -- Test comments COMMENT ON OPERATOR ###### (int4, NONE) IS 'bad right unary'; -- => is disallowed now CREATE OPERATOR => ( leftarg = int8, -- right unary procedure = numeric_fac ); -- lexing of <=, >=, <>, != has a number of edge cases -- (=> is tested elsewhere) -- this is legal because ! is not allowed in sql ops CREATE OPERATOR !=- ( leftarg = int8, -- right unary procedure = numeric_fac ); SELECT 2 !=-; -- make sure lexer returns != as <> even in edge cases SELECT 2 !=/**/ 1, 2 !=/**/ 2; SELECT 2 !=-- comment to be removed by psql 1; DO $$ -- use DO to protect -- from psql declare r boolean; begin execute $e$ select 2 !=-- comment 1 $e$ into r; raise info 'r = %', r; end; $$; -- check that <= etc. followed by more operator characters are returned -- as the correct token with correct precedence SELECT true<>-1 BETWEEN 1 AND 1; -- BETWEEN has prec. above <> but below Op SELECT false<>/**/1 BETWEEN 1 AND 1; SELECT false<=-1 BETWEEN 1 AND 1; SELECT false>=-1 BETWEEN 1 AND 1; SELECT 2<=/**/3, 3>=/**/2, 2<>/**/3; SELECT 3<=/**/2, 2>=/**/3, 2<>/**/2; -- Should fail. CREATE OPERATOR requires USAGE on SCHEMA BEGIN TRANSACTION; CREATE ROLE regress_rol_op1; CREATE SCHEMA schema_op1; GRANT USAGE ON SCHEMA schema_op1 TO PUBLIC; REVOKE USAGE ON SCHEMA schema_op1 FROM regress_rol_op1; SET ROLE regress_rol_op1; CREATE OPERATOR schema_op1.#*# ( leftarg = int8, -- right unary procedure = numeric_fac ); ROLLBACK; -- Should fail. SETOF type functions not allowed as argument (testing leftarg) BEGIN TRANSACTION; CREATE OPERATOR #*# ( leftarg = SETOF int8, procedure = numeric_fac ); ROLLBACK; -- Should fail. SETOF type functions not allowed as argument (testing rightarg) BEGIN TRANSACTION; CREATE OPERATOR #*# ( rightarg = SETOF int8, procedure = numeric_fac ); ROLLBACK; -- Should work. Sample text-book case BEGIN TRANSACTION; CREATE OR REPLACE FUNCTION fn_op2(boolean, boolean) RETURNS boolean AS $$ SELECT NULL::BOOLEAN; $$ LANGUAGE sql IMMUTABLE; CREATE OPERATOR === ( LEFTARG = boolean, RIGHTARG = boolean, PROCEDURE = fn_op2, COMMUTATOR = ===, NEGATOR = !==, RESTRICT = contsel, JOIN = contjoinsel, SORT1, SORT2, LTCMP, GTCMP, HASHES, MERGES ); ROLLBACK; -- Should fail. Invalid attribute CREATE OPERATOR #@%# ( leftarg = int8, -- right unary procedure = numeric_fac, invalid_att = int8 ); -- Should fail. At least leftarg or rightarg should be mandatorily specified CREATE OPERATOR #@%# ( procedure = numeric_fac ); -- Should fail. Procedure should be mandatorily specified CREATE OPERATOR #@%# ( leftarg = int8 ); -- Should fail. CREATE OPERATOR requires USAGE on TYPE BEGIN TRANSACTION; CREATE ROLE regress_rol_op3; CREATE TYPE type_op3 AS ENUM ('new', 'open', 'closed'); CREATE FUNCTION fn_op3(type_op3, int8) RETURNS int8 AS $$ SELECT NULL::int8; $$ LANGUAGE sql IMMUTABLE; REVOKE USAGE ON TYPE type_op3 FROM regress_rol_op3; REVOKE USAGE ON TYPE type_op3 FROM PUBLIC; -- Need to do this so that regress_rol_op3 is not allowed USAGE via PUBLIC SET ROLE regress_rol_op3; CREATE OPERATOR #*# ( leftarg = type_op3, rightarg = int8, procedure = fn_op3 ); ROLLBACK; -- Should fail. CREATE OPERATOR requires USAGE on TYPE (need to check separately for rightarg) BEGIN TRANSACTION; CREATE ROLE regress_rol_op4; CREATE TYPE type_op4 AS ENUM ('new', 'open', 'closed'); CREATE FUNCTION fn_op4(int8, type_op4) RETURNS int8 AS $$ SELECT NULL::int8; $$ LANGUAGE sql IMMUTABLE; REVOKE USAGE ON TYPE type_op4 FROM regress_rol_op4; REVOKE USAGE ON TYPE type_op4 FROM PUBLIC; -- Need to do this so that regress_rol_op3 is not allowed USAGE via PUBLIC SET ROLE regress_rol_op4; CREATE OPERATOR #*# ( leftarg = int8, rightarg = type_op4, procedure = fn_op4 ); ROLLBACK; -- Should fail. CREATE OPERATOR requires EXECUTE on function BEGIN TRANSACTION; CREATE ROLE regress_rol_op5; CREATE TYPE type_op5 AS ENUM ('new', 'open', 'closed'); CREATE FUNCTION fn_op5(int8, int8) RETURNS int8 AS $$ SELECT NULL::int8; $$ LANGUAGE sql IMMUTABLE; REVOKE EXECUTE ON FUNCTION fn_op5(int8, int8) FROM regress_rol_op5; REVOKE EXECUTE ON FUNCTION fn_op5(int8, int8) FROM PUBLIC;-- Need to do this so that regress_rol_op3 is not allowed EXECUTE via PUBLIC SET ROLE regress_rol_op5; CREATE OPERATOR #*# ( leftarg = int8, rightarg = int8, procedure = fn_op5 ); ROLLBACK; -- Should fail. CREATE OPERATOR requires USAGE on return TYPE BEGIN TRANSACTION; CREATE ROLE regress_rol_op6; CREATE TYPE type_op6 AS ENUM ('new', 'open', 'closed'); CREATE FUNCTION fn_op6(int8, int8) RETURNS type_op6 AS $$ SELECT NULL::type_op6; $$ LANGUAGE sql IMMUTABLE; REVOKE USAGE ON TYPE type_op6 FROM regress_rol_op6; REVOKE USAGE ON TYPE type_op6 FROM PUBLIC; -- Need to do this so that regress_rol_op3 is not allowed USAGE via PUBLIC SET ROLE regress_rol_op6; CREATE OPERATOR #*# ( leftarg = int8, rightarg = int8, procedure = fn_op6 ); ROLLBACK; -- invalid: non-lowercase quoted identifiers CREATE OPERATOR === ( "Leftarg" = box, "Rightarg" = box, "Procedure" = area_equal_function, "Commutator" = ===, "Negator" = !==, "Restrict" = area_restriction_function, "Join" = area_join_function, "Hashes", "Merges" ); pgFormatter-4.2/t/pg-test-files/sql/create_procedure.sql000066400000000000000000000063011361326045100234330ustar00rootroot00000000000000CALL nonexistent(); -- error CALL random(); -- error CREATE FUNCTION cp_testfunc1(a int) RETURNS int LANGUAGE SQL AS $$ SELECT a $$; CREATE TABLE cp_test (a int, b text); CREATE PROCEDURE ptest1(x text) LANGUAGE SQL AS $$ INSERT INTO cp_test VALUES (1, x); $$; \df ptest1 SELECT pg_get_functiondef('ptest1'::regproc); -- show only normal functions \dfn public.*test*1 -- show only procedures \dfp public.*test*1 SELECT ptest1('x'); -- error CALL ptest1('a'); -- ok CALL ptest1('xy' || 'zzy'); -- ok, constant-folded arg CALL ptest1(substring(random()::numeric(20,15)::text, 1, 1)); -- ok, volatile arg SELECT * FROM cp_test ORDER BY b COLLATE "C"; CREATE PROCEDURE ptest2() LANGUAGE SQL AS $$ SELECT 5; $$; CALL ptest2(); -- nested CALL TRUNCATE cp_test; CREATE PROCEDURE ptest3(y text) LANGUAGE SQL AS $$ CALL ptest1(y); CALL ptest1($1); $$; CALL ptest3('b'); SELECT * FROM cp_test; -- output arguments CREATE PROCEDURE ptest4a(INOUT a int, INOUT b int) LANGUAGE SQL AS $$ SELECT 1, 2; $$; CALL ptest4a(NULL, NULL); CREATE PROCEDURE ptest4b(INOUT b int, INOUT a int) LANGUAGE SQL AS $$ CALL ptest4a(a, b); -- error, not supported $$; DROP PROCEDURE ptest4a; -- named and default parameters CREATE OR REPLACE PROCEDURE ptest5(a int, b text, c int default 100) LANGUAGE SQL AS $$ INSERT INTO cp_test VALUES(a, b); INSERT INTO cp_test VALUES(c, b); $$; TRUNCATE cp_test; CALL ptest5(10, 'Hello', 20); CALL ptest5(10, 'Hello'); CALL ptest5(10, b => 'Hello'); CALL ptest5(b => 'Hello', a => 10); SELECT * FROM cp_test; -- polymorphic types CREATE PROCEDURE ptest6(a int, b anyelement) LANGUAGE SQL AS $$ SELECT NULL::int; $$; CALL ptest6(1, 2); -- collation assignment CREATE PROCEDURE ptest7(a text, b text) LANGUAGE SQL AS $$ SELECT a = b; $$; CALL ptest7(least('a', 'b'), 'a'); -- various error cases CALL version(); -- error: not a procedure CALL sum(1); -- error: not a procedure CREATE PROCEDURE ptestx() LANGUAGE SQL WINDOW AS $$ INSERT INTO cp_test VALUES (1, 'a') $$; CREATE PROCEDURE ptestx() LANGUAGE SQL STRICT AS $$ INSERT INTO cp_test VALUES (1, 'a') $$; CREATE PROCEDURE ptestx(OUT a int) LANGUAGE SQL AS $$ INSERT INTO cp_test VALUES (1, 'a') $$; ALTER PROCEDURE ptest1(text) STRICT; ALTER FUNCTION ptest1(text) VOLATILE; -- error: not a function ALTER PROCEDURE cp_testfunc1(int) VOLATILE; -- error: not a procedure ALTER PROCEDURE nonexistent() VOLATILE; DROP FUNCTION ptest1(text); -- error: not a function DROP PROCEDURE cp_testfunc1(int); -- error: not a procedure DROP PROCEDURE nonexistent(); -- privileges CREATE USER regress_cp_user1; GRANT INSERT ON cp_test TO regress_cp_user1; REVOKE EXECUTE ON PROCEDURE ptest1(text) FROM PUBLIC; SET ROLE regress_cp_user1; CALL ptest1('a'); -- error RESET ROLE; GRANT EXECUTE ON PROCEDURE ptest1(text) TO regress_cp_user1; SET ROLE regress_cp_user1; CALL ptest1('a'); -- ok RESET ROLE; -- ROUTINE syntax ALTER ROUTINE cp_testfunc1(int) RENAME TO cp_testfunc1a; ALTER ROUTINE cp_testfunc1a RENAME TO cp_testfunc1; ALTER ROUTINE ptest1(text) RENAME TO ptest1a; ALTER ROUTINE ptest1a RENAME TO ptest1; DROP ROUTINE cp_testfunc1(int); -- cleanup DROP PROCEDURE ptest1; DROP PROCEDURE ptest2; DROP TABLE cp_test; DROP USER regress_cp_user1; pgFormatter-4.2/t/pg-test-files/sql/create_table.sql000066400000000000000000000702541361326045100225420ustar00rootroot00000000000000-- -- CREATE_TABLE -- -- -- CLASS DEFINITIONS -- CREATE TABLE hobbies_r ( name text, person text ); CREATE TABLE equipment_r ( name text, hobby text ); CREATE TABLE onek ( unique1 int4, unique2 int4, two int4, four int4, ten int4, twenty int4, hundred int4, thousand int4, twothousand int4, fivethous int4, tenthous int4, odd int4, even int4, stringu1 name, stringu2 name, string4 name ); CREATE TABLE tenk1 ( unique1 int4, unique2 int4, two int4, four int4, ten int4, twenty int4, hundred int4, thousand int4, twothousand int4, fivethous int4, tenthous int4, odd int4, even int4, stringu1 name, stringu2 name, string4 name ); CREATE TABLE tenk2 ( unique1 int4, unique2 int4, two int4, four int4, ten int4, twenty int4, hundred int4, thousand int4, twothousand int4, fivethous int4, tenthous int4, odd int4, even int4, stringu1 name, stringu2 name, string4 name ); CREATE TABLE person ( name text, age int4, location point ); CREATE TABLE emp ( salary int4, manager name ) INHERITS (person); CREATE TABLE student ( gpa float8 ) INHERITS (person); CREATE TABLE stud_emp ( percent int4 ) INHERITS (emp, student); CREATE TABLE city ( name name, location box, budget city_budget ); CREATE TABLE dept ( dname name, mgrname text ); CREATE TABLE slow_emp4000 ( home_base box ); CREATE TABLE fast_emp4000 ( home_base box ); CREATE TABLE road ( name text, thepath path ); CREATE TABLE ihighway () INHERITS (road); CREATE TABLE shighway ( surface text ) INHERITS (road); CREATE TABLE real_city ( pop int4, cname text, outline path ); -- -- test the "star" operators a bit more thoroughly -- this time, -- throw in lots of NULL fields... -- -- a is the type root -- b and c inherit from a (one-level single inheritance) -- d inherits from b and c (two-level multiple inheritance) -- e inherits from c (two-level single inheritance) -- f inherits from e (three-level single inheritance) -- CREATE TABLE a_star ( class char, a int4 ); CREATE TABLE b_star ( b text ) INHERITS (a_star); CREATE TABLE c_star ( c name ) INHERITS (a_star); CREATE TABLE d_star ( d float8 ) INHERITS (b_star, c_star); CREATE TABLE e_star ( e int2 ) INHERITS (c_star); CREATE TABLE f_star ( f polygon ) INHERITS (e_star); CREATE TABLE aggtest ( a int2, b float4 ); CREATE TABLE hash_i4_heap ( seqno int4, random int4 ); CREATE TABLE hash_name_heap ( seqno int4, random name ); CREATE TABLE hash_txt_heap ( seqno int4, random text ); CREATE TABLE hash_f8_heap ( seqno int4, random float8 ); -- don't include the hash_ovfl_heap stuff in the distribution -- the data set is too large for what it's worth -- -- CREATE TABLE hash_ovfl_heap ( -- x int4, -- y int4 -- ); CREATE TABLE bt_i4_heap ( seqno int4, random int4 ); CREATE TABLE bt_name_heap ( seqno name, random int4 ); CREATE TABLE bt_txt_heap ( seqno text, random int4 ); CREATE TABLE bt_f8_heap ( seqno float8, random int4 ); CREATE TABLE array_op_test ( seqno int4, i int4[], t text[] ); CREATE TABLE array_index_op_test ( seqno int4, i int4[], t text[] ); CREATE TABLE testjsonb ( j jsonb ); CREATE TABLE unknowntab ( u unknown -- fail ); CREATE TYPE unknown_comptype AS ( u unknown -- fail ); CREATE TABLE IF NOT EXISTS test_tsvector( t text, a tsvector ); CREATE TABLE IF NOT EXISTS test_tsvector( t text ); -- invalid: non-lowercase quoted reloptions identifiers CREATE TABLE tas_case WITH ("Fillfactor" = 10) AS SELECT 1 a; CREATE UNLOGGED TABLE unlogged1 (a int primary key); -- OK CREATE TEMPORARY TABLE unlogged2 (a int primary key); -- OK SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged\d' ORDER BY relname; REINDEX INDEX unlogged1_pkey; REINDEX INDEX unlogged2_pkey; SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged\d' ORDER BY relname; DROP TABLE unlogged2; INSERT INTO unlogged1 VALUES (42); CREATE UNLOGGED TABLE public.unlogged2 (a int primary key); -- also OK CREATE UNLOGGED TABLE pg_temp.unlogged3 (a int primary key); -- not OK CREATE TABLE pg_temp.implicitly_temp (a int primary key); -- OK CREATE TEMP TABLE explicitly_temp (a int primary key); -- also OK CREATE TEMP TABLE pg_temp.doubly_temp (a int primary key); -- also OK CREATE TEMP TABLE public.temp_to_perm (a int primary key); -- not OK DROP TABLE unlogged1, public.unlogged2; CREATE TABLE as_select1 AS SELECT * FROM pg_class WHERE relkind = 'r'; CREATE TABLE as_select1 AS SELECT * FROM pg_class WHERE relkind = 'r'; CREATE TABLE IF NOT EXISTS as_select1 AS SELECT * FROM pg_class WHERE relkind = 'r'; DROP TABLE as_select1; PREPARE select1 AS SELECT 1 as a; CREATE TABLE as_select1 AS EXECUTE select1; CREATE TABLE as_select1 AS EXECUTE select1; SELECT * FROM as_select1; CREATE TABLE IF NOT EXISTS as_select1 AS EXECUTE select1; DROP TABLE as_select1; DEALLOCATE select1; -- create an extra wide table to test for issues related to that -- (temporarily hide query, to avoid the long CREATE TABLE stmt) \set ECHO none SELECT 'CREATE TABLE extra_wide_table(firstc text, '|| array_to_string(array_agg('c'||i||' bool'),',')||', lastc text);' FROM generate_series(1, 1100) g(i) \gexec \set ECHO all INSERT INTO extra_wide_table(firstc, lastc) VALUES('first col', 'last col'); SELECT firstc, lastc FROM extra_wide_table; -- check that tables with oids cannot be created anymore CREATE TABLE withoid() WITH OIDS; CREATE TABLE withoid() WITH (oids); CREATE TABLE withoid() WITH (oids = true); -- but explicitly not adding oids is still supported CREATE TEMP TABLE withoutoid() WITHOUT OIDS; DROP TABLE withoutoid; CREATE TEMP TABLE withoutoid() WITH (oids = false); DROP TABLE withoutoid; -- check restriction with default expressions -- invalid use of column reference in default expressions CREATE TABLE default_expr_column (id int DEFAULT (id)); CREATE TABLE default_expr_column (id int DEFAULT (bar.id)); CREATE TABLE default_expr_agg_column (id int DEFAULT (avg(id))); -- invalid column definition CREATE TABLE default_expr_non_column (a int DEFAULT (avg(non_existent))); -- invalid use of aggregate CREATE TABLE default_expr_agg (a int DEFAULT (avg(1))); -- invalid use of subquery CREATE TABLE default_expr_agg (a int DEFAULT (select 1)); -- invalid use of set-returning function CREATE TABLE default_expr_agg (a int DEFAULT (generate_series(1,3))); -- -- Partitioned tables -- -- cannot combine INHERITS and PARTITION BY (although grammar allows) CREATE TABLE partitioned ( a int ) INHERITS (some_table) PARTITION BY LIST (a); -- cannot use more than 1 column as partition key for list partitioned table CREATE TABLE partitioned ( a1 int, a2 int ) PARTITION BY LIST (a1, a2); -- fail -- unsupported constraint type for partitioned tables CREATE TABLE partitioned ( a int, EXCLUDE USING gist (a WITH &&) ) PARTITION BY RANGE (a); -- prevent using prohibited expressions in the key CREATE FUNCTION retset (a int) RETURNS SETOF int AS $$ SELECT 1; $$ LANGUAGE SQL IMMUTABLE; CREATE TABLE partitioned ( a int ) PARTITION BY RANGE (retset(a)); DROP FUNCTION retset(int); CREATE TABLE partitioned ( a int ) PARTITION BY RANGE ((avg(a))); CREATE TABLE partitioned ( a int, b int ) PARTITION BY RANGE ((avg(a) OVER (PARTITION BY b))); CREATE TABLE partitioned ( a int ) PARTITION BY LIST ((a LIKE (SELECT 1))); CREATE TABLE partitioned ( a int ) PARTITION BY RANGE (('a')); CREATE FUNCTION const_func () RETURNS int AS $$ SELECT 1; $$ LANGUAGE SQL IMMUTABLE; CREATE TABLE partitioned ( a int ) PARTITION BY RANGE (const_func()); DROP FUNCTION const_func(); -- only accept valid partitioning strategy CREATE TABLE partitioned ( a int ) PARTITION BY MAGIC (a); -- specified column must be present in the table CREATE TABLE partitioned ( a int ) PARTITION BY RANGE (b); -- cannot use system columns in partition key CREATE TABLE partitioned ( a int ) PARTITION BY RANGE (xmin); -- functions in key must be immutable CREATE FUNCTION immut_func (a int) RETURNS int AS $$ SELECT a + random()::int; $$ LANGUAGE SQL; CREATE TABLE partitioned ( a int ) PARTITION BY RANGE (immut_func(a)); DROP FUNCTION immut_func(int); -- cannot contain whole-row references CREATE TABLE partitioned ( a int ) PARTITION BY RANGE ((partitioned)); -- prevent using columns of unsupported types in key (type must have a btree operator class) CREATE TABLE partitioned ( a point ) PARTITION BY LIST (a); CREATE TABLE partitioned ( a point ) PARTITION BY LIST (a point_ops); CREATE TABLE partitioned ( a point ) PARTITION BY RANGE (a); CREATE TABLE partitioned ( a point ) PARTITION BY RANGE (a point_ops); -- cannot add NO INHERIT constraints to partitioned tables CREATE TABLE partitioned ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); -- some checks after successful creation of a partitioned table CREATE FUNCTION plusone(a int) RETURNS INT AS $$ SELECT a+1; $$ LANGUAGE SQL; CREATE TABLE partitioned ( a int, b int, c text, d text ) PARTITION BY RANGE (a oid_ops, plusone(b), c collate "default", d collate "C"); -- check relkind SELECT relkind FROM pg_class WHERE relname = 'partitioned'; -- prevent a function referenced in partition key from being dropped DROP FUNCTION plusone(int); -- partitioned table cannot participate in regular inheritance CREATE TABLE partitioned2 ( a int, b text ) PARTITION BY RANGE ((a+1), substr(b, 1, 5)); CREATE TABLE fail () INHERITS (partitioned2); -- Partition key in describe output \d partitioned \d+ partitioned2 INSERT INTO partitioned2 VALUES (1, 'hello'); CREATE TABLE part2_1 PARTITION OF partitioned2 FOR VALUES FROM (-1, 'aaaaa') TO (100, 'ccccc'); \d+ part2_1 DROP TABLE partitioned, partitioned2; -- -- Partitions -- -- check partition bound syntax CREATE TABLE list_parted ( a int ) PARTITION BY LIST (a); CREATE TABLE part_p1 PARTITION OF list_parted FOR VALUES IN ('1'); CREATE TABLE part_p2 PARTITION OF list_parted FOR VALUES IN (2); CREATE TABLE part_p3 PARTITION OF list_parted FOR VALUES IN ((2+1)); CREATE TABLE part_null PARTITION OF list_parted FOR VALUES IN (null); \d+ list_parted -- forbidden expressions for partition bound with list partitioned table CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (somename); CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (somename.somename); CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (a); CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (sum(a)); CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (sum(somename)); CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (sum(1)); CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN ((select 1)); CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (generate_series(4, 6)); -- syntax does not allow empty list of values for list partitions CREATE TABLE fail_part PARTITION OF list_parted FOR VALUES IN (); -- trying to specify range for list partitioned table CREATE TABLE fail_part PARTITION OF list_parted FOR VALUES FROM (1) TO (2); -- trying to specify modulus and remainder for list partitioned table CREATE TABLE fail_part PARTITION OF list_parted FOR VALUES WITH (MODULUS 10, REMAINDER 1); -- check default partition cannot be created more than once CREATE TABLE part_default PARTITION OF list_parted DEFAULT; CREATE TABLE fail_default_part PARTITION OF list_parted DEFAULT; -- specified literal can't be cast to the partition column data type CREATE TABLE bools ( a bool ) PARTITION BY LIST (a); CREATE TABLE bools_true PARTITION OF bools FOR VALUES IN (1); DROP TABLE bools; -- specified literal can be cast, and the cast might not be immutable CREATE TABLE moneyp ( a money ) PARTITION BY LIST (a); CREATE TABLE moneyp_10 PARTITION OF moneyp FOR VALUES IN (10); CREATE TABLE moneyp_11 PARTITION OF moneyp FOR VALUES IN ('11'); CREATE TABLE moneyp_12 PARTITION OF moneyp FOR VALUES IN (to_char(12, '99')::int); DROP TABLE moneyp; -- cast is immutable CREATE TABLE bigintp ( a bigint ) PARTITION BY LIST (a); CREATE TABLE bigintp_10 PARTITION OF bigintp FOR VALUES IN (10); -- fails due to overlap: CREATE TABLE bigintp_10_2 PARTITION OF bigintp FOR VALUES IN ('10'); DROP TABLE bigintp; CREATE TABLE range_parted ( a date ) PARTITION BY RANGE (a); -- forbidden expressions for partition bounds with range partitioned table CREATE TABLE part_bogus_expr_fail PARTITION OF range_parted FOR VALUES FROM (somename) TO ('2019-01-01'); CREATE TABLE part_bogus_expr_fail PARTITION OF range_parted FOR VALUES FROM (somename.somename) TO ('2019-01-01'); CREATE TABLE part_bogus_expr_fail PARTITION OF range_parted FOR VALUES FROM (a) TO ('2019-01-01'); CREATE TABLE part_bogus_expr_fail PARTITION OF range_parted FOR VALUES FROM (max(a)) TO ('2019-01-01'); CREATE TABLE part_bogus_expr_fail PARTITION OF range_parted FOR VALUES FROM (max(somename)) TO ('2019-01-01'); CREATE TABLE part_bogus_expr_fail PARTITION OF range_parted FOR VALUES FROM (max('2019-02-01'::date)) TO ('2019-01-01'); CREATE TABLE part_bogus_expr_fail PARTITION OF range_parted FOR VALUES FROM ((select 1)) TO ('2019-01-01'); CREATE TABLE part_bogus_expr_fail PARTITION OF range_parted FOR VALUES FROM (generate_series(1, 3)) TO ('2019-01-01'); -- trying to specify list for range partitioned table CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES IN ('a'); -- trying to specify modulus and remainder for range partitioned table CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES WITH (MODULUS 10, REMAINDER 1); -- each of start and end bounds must have same number of values as the -- length of the partition key CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM ('a', 1) TO ('z'); CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM ('a') TO ('z', 1); -- cannot specify null values in range bounds CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM (null) TO (maxvalue); -- trying to specify modulus and remainder for range partitioned table CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES WITH (MODULUS 10, REMAINDER 1); -- check partition bound syntax for the hash partition CREATE TABLE hash_parted ( a int ) PARTITION BY HASH (a); CREATE TABLE hpart_1 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 10, REMAINDER 0); CREATE TABLE hpart_2 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 50, REMAINDER 1); CREATE TABLE hpart_3 PARTITION OF hash_parted FOR VALUES WITH (MODULUS 200, REMAINDER 2); -- modulus 25 is factor of modulus of 50 but 10 is not factor of 25. CREATE TABLE fail_part PARTITION OF hash_parted FOR VALUES WITH (MODULUS 25, REMAINDER 3); -- previous modulus 50 is factor of 150 but this modulus is not factor of next modulus 200. CREATE TABLE fail_part PARTITION OF hash_parted FOR VALUES WITH (MODULUS 150, REMAINDER 3); -- trying to specify range for the hash partitioned table CREATE TABLE fail_part PARTITION OF hash_parted FOR VALUES FROM ('a', 1) TO ('z'); -- trying to specify list value for the hash partitioned table CREATE TABLE fail_part PARTITION OF hash_parted FOR VALUES IN (1000); -- trying to create default partition for the hash partitioned table CREATE TABLE fail_default_part PARTITION OF hash_parted DEFAULT; -- check if compatible with the specified parent -- cannot create as partition of a non-partitioned table CREATE TABLE unparted ( a int ); CREATE TABLE fail_part PARTITION OF unparted FOR VALUES IN ('a'); CREATE TABLE fail_part PARTITION OF unparted FOR VALUES WITH (MODULUS 2, REMAINDER 1); DROP TABLE unparted; -- cannot create a permanent rel as partition of a temp rel CREATE TEMP TABLE temp_parted ( a int ) PARTITION BY LIST (a); CREATE TABLE fail_part PARTITION OF temp_parted FOR VALUES IN ('a'); DROP TABLE temp_parted; -- check for partition bound overlap and other invalid specifications CREATE TABLE list_parted2 ( a varchar ) PARTITION BY LIST (a); CREATE TABLE part_null_z PARTITION OF list_parted2 FOR VALUES IN (null, 'z'); CREATE TABLE part_ab PARTITION OF list_parted2 FOR VALUES IN ('a', 'b'); CREATE TABLE list_parted2_def PARTITION OF list_parted2 DEFAULT; CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN (null); CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN ('b', 'c'); -- check default partition overlap INSERT INTO list_parted2 VALUES('X'); CREATE TABLE fail_part PARTITION OF list_parted2 FOR VALUES IN ('W', 'X', 'Y'); CREATE TABLE range_parted2 ( a int ) PARTITION BY RANGE (a); -- trying to create range partition with empty range CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (0); -- note that the range '[1, 1)' has no elements CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (1); CREATE TABLE part0 PARTITION OF range_parted2 FOR VALUES FROM (minvalue) TO (1); CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (minvalue) TO (2); CREATE TABLE part1 PARTITION OF range_parted2 FOR VALUES FROM (1) TO (10); CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (9) TO (maxvalue); CREATE TABLE part2 PARTITION OF range_parted2 FOR VALUES FROM (20) TO (30); CREATE TABLE part3 PARTITION OF range_parted2 FOR VALUES FROM (30) TO (40); CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (30); CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (50); -- Create a default partition for range partitioned table CREATE TABLE range2_default PARTITION OF range_parted2 DEFAULT; -- More than one default partition is not allowed, so this should give error CREATE TABLE fail_default_part PARTITION OF range_parted2 DEFAULT; -- Check if the range for default partitions overlap INSERT INTO range_parted2 VALUES (85); CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (80) TO (90); CREATE TABLE part4 PARTITION OF range_parted2 FOR VALUES FROM (90) TO (100); -- now check for multi-column range partition key CREATE TABLE range_parted3 ( a int, b int ) PARTITION BY RANGE (a, (b+1)); CREATE TABLE part00 PARTITION OF range_parted3 FOR VALUES FROM (0, minvalue) TO (0, maxvalue); CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, minvalue) TO (0, 1); CREATE TABLE part10 PARTITION OF range_parted3 FOR VALUES FROM (1, minvalue) TO (1, 1); CREATE TABLE part11 PARTITION OF range_parted3 FOR VALUES FROM (1, 1) TO (1, 10); CREATE TABLE part12 PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, maxvalue); CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, 20); CREATE TABLE range3_default PARTITION OF range_parted3 DEFAULT; -- cannot create a partition that says column b is allowed to range -- from -infinity to +infinity, while there exist partitions that have -- more specific ranges CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, minvalue) TO (1, maxvalue); -- check for partition bound overlap and other invalid specifications for the hash partition CREATE TABLE hash_parted2 ( a varchar ) PARTITION BY HASH (a); CREATE TABLE h2part_1 PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 4, REMAINDER 2); CREATE TABLE h2part_2 PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 8, REMAINDER 0); CREATE TABLE h2part_3 PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 8, REMAINDER 4); CREATE TABLE h2part_4 PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 8, REMAINDER 5); -- overlap with part_4 CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 2, REMAINDER 1); -- modulus must be greater than zero CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 0, REMAINDER 1); -- remainder must be greater than or equal to zero and less than modulus CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 8, REMAINDER 8); -- check schema propagation from parent CREATE TABLE parted ( a text, b int NOT NULL DEFAULT 0, CONSTRAINT check_a CHECK (length(a) > 0) ) PARTITION BY LIST (a); CREATE TABLE part_a PARTITION OF parted FOR VALUES IN ('a'); -- only inherited attributes (never local ones) SELECT attname, attislocal, attinhcount FROM pg_attribute WHERE attrelid = 'part_a'::regclass and attnum > 0 ORDER BY attnum; -- able to specify column default, column constraint, and table constraint -- first check the "column specified more than once" error CREATE TABLE part_b PARTITION OF parted ( b NOT NULL, b DEFAULT 1, b CHECK (b >= 0), CONSTRAINT check_a CHECK (length(a) > 0) ) FOR VALUES IN ('b'); CREATE TABLE part_b PARTITION OF parted ( b NOT NULL DEFAULT 1, CONSTRAINT check_a CHECK (length(a) > 0), CONSTRAINT check_b CHECK (b >= 0) ) FOR VALUES IN ('b'); -- conislocal should be false for any merged constraints, true otherwise SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_b'::regclass ORDER BY conislocal, coninhcount; -- Once check_b is added to the parent, it should be made non-local for part_b ALTER TABLE parted ADD CONSTRAINT check_b CHECK (b >= 0); SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_b'::regclass; -- Neither check_a nor check_b are droppable from part_b ALTER TABLE part_b DROP CONSTRAINT check_a; ALTER TABLE part_b DROP CONSTRAINT check_b; -- And dropping it from parted should leave no trace of them on part_b, unlike -- traditional inheritance where they will be left behind, because they would -- be local constraints. ALTER TABLE parted DROP CONSTRAINT check_a, DROP CONSTRAINT check_b; SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_b'::regclass; -- specify PARTITION BY for a partition CREATE TABLE fail_part_col_not_found PARTITION OF parted FOR VALUES IN ('c') PARTITION BY RANGE (c); CREATE TABLE part_c PARTITION OF parted (b WITH OPTIONS NOT NULL DEFAULT 0) FOR VALUES IN ('c') PARTITION BY RANGE ((b)); -- create a level-2 partition CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10); -- check that NOT NULL and default value are inherited correctly create table parted_notnull_inh_test (a int default 1, b int not null default 0) partition by list (a); create table parted_notnull_inh_test1 partition of parted_notnull_inh_test (a not null, b default 1) for values in (1); insert into parted_notnull_inh_test (b) values (null); -- note that while b's default is overriden, a's default is preserved \d parted_notnull_inh_test1 drop table parted_notnull_inh_test; -- check for a conflicting COLLATE clause create table parted_collate_must_match (a text collate "C", b text collate "C") partition by range (a); -- on the partition key create table parted_collate_must_match1 partition of parted_collate_must_match (a collate "POSIX") for values from ('a') to ('m'); -- on another column create table parted_collate_must_match2 partition of parted_collate_must_match (b collate "POSIX") for values from ('m') to ('z'); drop table parted_collate_must_match; -- check that specifying incompatible collations for partition bound -- expressions fails promptly create table test_part_coll_posix (a text) partition by range (a collate "POSIX"); -- fail create table test_part_coll partition of test_part_coll_posix for values from ('a' collate "C") to ('g'); -- ok create table test_part_coll partition of test_part_coll_posix for values from ('a' collate "POSIX") to ('g'); -- ok create table test_part_coll2 partition of test_part_coll_posix for values from ('g') to ('m'); -- using a cast expression uses the target type's default collation -- fail create table test_part_coll_cast partition of test_part_coll_posix for values from (name 'm' collate "C") to ('s'); -- ok create table test_part_coll_cast partition of test_part_coll_posix for values from (name 'm' collate "POSIX") to ('s'); -- ok; partition collation silently overrides the default collation of type 'name' create table test_part_coll_cast2 partition of test_part_coll_posix for values from (name 's') to ('z'); drop table test_part_coll_posix; -- Partition bound in describe output \d+ part_b -- Both partition bound and partition key in describe output \d+ part_c -- a level-2 partition's constraint will include the parent's expressions \d+ part_c_1_10 -- Show partition count in the parent's describe output -- Tempted to include \d+ output listing partitions with bound info but -- output could vary depending on the order in which partition oids are -- returned. \d parted \d hash_parted -- check that we get the expected partition constraints CREATE TABLE range_parted4 (a int, b int, c int) PARTITION BY RANGE (abs(a), abs(b), c); CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (MAXVALUE, MAXVALUE, MAXVALUE); \d+ unbounded_range_part DROP TABLE unbounded_range_part; CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, MAXVALUE, MAXVALUE); \d+ range_parted4_1 CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE); \d+ range_parted4_2 CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MAXVALUE); \d+ range_parted4_3 DROP TABLE range_parted4; -- user-defined operator class in partition key CREATE FUNCTION my_int4_sort(int4,int4) RETURNS int LANGUAGE sql AS $$ SELECT CASE WHEN $1 = $2 THEN 0 WHEN $1 > $2 THEN 1 ELSE -1 END; $$; CREATE OPERATOR CLASS test_int4_ops FOR TYPE int4 USING btree AS OPERATOR 1 < (int4,int4), OPERATOR 2 <= (int4,int4), OPERATOR 3 = (int4,int4), OPERATOR 4 >= (int4,int4), OPERATOR 5 > (int4,int4), FUNCTION 1 my_int4_sort(int4,int4); CREATE TABLE partkey_t (a int4) PARTITION BY RANGE (a test_int4_ops); CREATE TABLE partkey_t_1 PARTITION OF partkey_t FOR VALUES FROM (0) TO (1000); INSERT INTO partkey_t VALUES (100); INSERT INTO partkey_t VALUES (200); -- cleanup DROP TABLE parted, list_parted, range_parted, list_parted2, range_parted2, range_parted3; DROP TABLE partkey_t, hash_parted, hash_parted2; DROP OPERATOR CLASS test_int4_ops USING btree; DROP FUNCTION my_int4_sort(int4,int4); -- comments on partitioned tables columns CREATE TABLE parted_col_comment (a int, b text) PARTITION BY LIST (a); COMMENT ON TABLE parted_col_comment IS 'Am partitioned table'; COMMENT ON COLUMN parted_col_comment.a IS 'Partition key'; SELECT obj_description('parted_col_comment'::regclass); \d+ parted_col_comment DROP TABLE parted_col_comment; -- list partitioning on array type column CREATE TABLE arrlp (a int[]) PARTITION BY LIST (a); CREATE TABLE arrlp12 PARTITION OF arrlp FOR VALUES IN ('{1}', '{2}'); \d+ arrlp12 DROP TABLE arrlp; -- partition on boolean column create table boolspart (a bool) partition by list (a); create table boolspart_t partition of boolspart for values in (true); create table boolspart_f partition of boolspart for values in (false); \d+ boolspart drop table boolspart; -- partitions mixing temporary and permanent relations create table perm_parted (a int) partition by list (a); create temporary table temp_parted (a int) partition by list (a); create table perm_part partition of temp_parted default; -- error create temp table temp_part partition of perm_parted default; -- error create temp table temp_part partition of temp_parted default; -- ok drop table perm_parted cascade; drop table temp_parted cascade; -- check that adding partitions to a table while it is being used is prevented create table tab_part_create (a int) partition by list (a); create or replace function func_part_create() returns trigger language plpgsql as $$ begin execute 'create table tab_part_create_1 partition of tab_part_create for values in (1)'; return null; end $$; create trigger trig_part_create before insert on tab_part_create for each statement execute procedure func_part_create(); insert into tab_part_create values (1); drop table tab_part_create; drop function func_part_create(); -- test using a volatile expression as partition bound create table volatile_partbound_test (partkey timestamp) partition by range (partkey); create table volatile_partbound_test1 partition of volatile_partbound_test for values from (minvalue) to (current_timestamp); create table volatile_partbound_test2 partition of volatile_partbound_test for values from (current_timestamp) to (maxvalue); -- this should go into the partition volatile_partbound_test2 insert into volatile_partbound_test values (current_timestamp); select tableoid::regclass from volatile_partbound_test; drop table volatile_partbound_test; pgFormatter-4.2/t/pg-test-files/sql/create_table_like.sql000066400000000000000000000147061361326045100235460ustar00rootroot00000000000000/* Test inheritance of structure (LIKE) */ CREATE TABLE inhx (xx text DEFAULT 'text'); /* * Test double inheritance * * Ensure that defaults are NOT included unless * INCLUDING DEFAULTS is specified */ CREATE TABLE ctla (aa TEXT); CREATE TABLE ctlb (bb TEXT) INHERITS (ctla); CREATE TABLE foo (LIKE nonexistent); CREATE TABLE inhe (ee text, LIKE inhx) inherits (ctlb); INSERT INTO inhe VALUES ('ee-col1', 'ee-col2', DEFAULT, 'ee-col4'); SELECT * FROM inhe; /* Columns aa, bb, xx value NULL, ee */ SELECT * FROM inhx; /* Empty set since LIKE inherits structure only */ SELECT * FROM ctlb; /* Has ee entry */ SELECT * FROM ctla; /* Has ee entry */ CREATE TABLE inhf (LIKE inhx, LIKE inhx); /* Throw error */ CREATE TABLE inhf (LIKE inhx INCLUDING DEFAULTS INCLUDING CONSTRAINTS); INSERT INTO inhf DEFAULT VALUES; SELECT * FROM inhf; /* Single entry with value 'text' */ ALTER TABLE inhx add constraint foo CHECK (xx = 'text'); ALTER TABLE inhx ADD PRIMARY KEY (xx); CREATE TABLE inhg (LIKE inhx); /* Doesn't copy constraint */ INSERT INTO inhg VALUES ('foo'); DROP TABLE inhg; CREATE TABLE inhg (x text, LIKE inhx INCLUDING CONSTRAINTS, y text); /* Copies constraints */ INSERT INTO inhg VALUES ('x', 'text', 'y'); /* Succeeds */ INSERT INTO inhg VALUES ('x', 'text', 'y'); /* Succeeds -- Unique constraints not copied */ INSERT INTO inhg VALUES ('x', 'foo', 'y'); /* fails due to constraint */ SELECT * FROM inhg; /* Two records with three columns in order x=x, xx=text, y=y */ DROP TABLE inhg; CREATE TABLE test_like_id_1 (a bigint GENERATED ALWAYS AS IDENTITY, b text); \d test_like_id_1 INSERT INTO test_like_id_1 (b) VALUES ('b1'); SELECT * FROM test_like_id_1; CREATE TABLE test_like_id_2 (LIKE test_like_id_1); \d test_like_id_2 INSERT INTO test_like_id_2 (b) VALUES ('b2'); SELECT * FROM test_like_id_2; -- identity was not copied CREATE TABLE test_like_id_3 (LIKE test_like_id_1 INCLUDING IDENTITY); \d test_like_id_3 INSERT INTO test_like_id_3 (b) VALUES ('b3'); SELECT * FROM test_like_id_3; -- identity was copied and applied DROP TABLE test_like_id_1, test_like_id_2, test_like_id_3; CREATE TABLE test_like_gen_1 (a int, b int GENERATED ALWAYS AS (a * 2) STORED); \d test_like_gen_1 INSERT INTO test_like_gen_1 (a) VALUES (1); SELECT * FROM test_like_gen_1; CREATE TABLE test_like_gen_2 (LIKE test_like_gen_1); \d test_like_gen_2 INSERT INTO test_like_gen_2 (a) VALUES (1); SELECT * FROM test_like_gen_2; CREATE TABLE test_like_gen_3 (LIKE test_like_gen_1 INCLUDING GENERATED); \d test_like_gen_3 INSERT INTO test_like_gen_3 (a) VALUES (1); SELECT * FROM test_like_gen_3; DROP TABLE test_like_gen_1, test_like_gen_2, test_like_gen_3; CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, y text); /* copies indexes */ INSERT INTO inhg VALUES (5, 10); INSERT INTO inhg VALUES (20, 10); -- should fail DROP TABLE inhg; /* Multiple primary keys creation should fail */ CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, PRIMARY KEY(x)); /* fails */ CREATE TABLE inhz (xx text DEFAULT 'text', yy int UNIQUE); CREATE UNIQUE INDEX inhz_xx_idx on inhz (xx) WHERE xx <> 'test'; /* Ok to create multiple unique indexes */ CREATE TABLE inhg (x text UNIQUE, LIKE inhz INCLUDING INDEXES); INSERT INTO inhg (xx, yy, x) VALUES ('test', 5, 10); INSERT INTO inhg (xx, yy, x) VALUES ('test', 10, 15); INSERT INTO inhg (xx, yy, x) VALUES ('foo', 10, 15); -- should fail DROP TABLE inhg; DROP TABLE inhz; -- including storage and comments CREATE TABLE ctlt1 (a text CHECK (length(a) > 2) PRIMARY KEY, b text); CREATE INDEX ctlt1_b_key ON ctlt1 (b); CREATE INDEX ctlt1_fnidx ON ctlt1 ((a || b)); CREATE STATISTICS ctlt1_a_b_stat ON a,b FROM ctlt1; COMMENT ON STATISTICS ctlt1_a_b_stat IS 'ab stats'; COMMENT ON COLUMN ctlt1.a IS 'A'; COMMENT ON COLUMN ctlt1.b IS 'B'; COMMENT ON CONSTRAINT ctlt1_a_check ON ctlt1 IS 't1_a_check'; COMMENT ON INDEX ctlt1_pkey IS 'index pkey'; COMMENT ON INDEX ctlt1_b_key IS 'index b_key'; ALTER TABLE ctlt1 ALTER COLUMN a SET STORAGE MAIN; CREATE TABLE ctlt2 (c text); ALTER TABLE ctlt2 ALTER COLUMN c SET STORAGE EXTERNAL; COMMENT ON COLUMN ctlt2.c IS 'C'; CREATE TABLE ctlt3 (a text CHECK (length(a) < 5), c text); ALTER TABLE ctlt3 ALTER COLUMN c SET STORAGE EXTERNAL; ALTER TABLE ctlt3 ALTER COLUMN a SET STORAGE MAIN; COMMENT ON COLUMN ctlt3.a IS 'A3'; COMMENT ON COLUMN ctlt3.c IS 'C'; COMMENT ON CONSTRAINT ctlt3_a_check ON ctlt3 IS 't3_a_check'; CREATE TABLE ctlt4 (a text, c text); ALTER TABLE ctlt4 ALTER COLUMN c SET STORAGE EXTERNAL; CREATE TABLE ctlt12_storage (LIKE ctlt1 INCLUDING STORAGE, LIKE ctlt2 INCLUDING STORAGE); \d+ ctlt12_storage CREATE TABLE ctlt12_comments (LIKE ctlt1 INCLUDING COMMENTS, LIKE ctlt2 INCLUDING COMMENTS); \d+ ctlt12_comments CREATE TABLE ctlt1_inh (LIKE ctlt1 INCLUDING CONSTRAINTS INCLUDING COMMENTS) INHERITS (ctlt1); \d+ ctlt1_inh SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 'ctlt1_inh'::regclass; CREATE TABLE ctlt13_inh () INHERITS (ctlt1, ctlt3); \d+ ctlt13_inh CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1); \d+ ctlt13_like SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 'ctlt13_like'::regclass; CREATE TABLE ctlt_all (LIKE ctlt1 INCLUDING ALL); \d+ ctlt_all SELECT c.relname, objsubid, description FROM pg_description, pg_index i, pg_class c WHERE classoid = 'pg_class'::regclass AND objoid = i.indexrelid AND c.oid = i.indexrelid AND i.indrelid = 'ctlt_all'::regclass ORDER BY c.relname, objsubid; SELECT s.stxname, objsubid, description FROM pg_description, pg_statistic_ext s WHERE classoid = 'pg_statistic_ext'::regclass AND objoid = s.oid AND s.stxrelid = 'ctlt_all'::regclass ORDER BY s.stxname, objsubid; CREATE TABLE inh_error1 () INHERITS (ctlt1, ctlt4); CREATE TABLE inh_error2 (LIKE ctlt4 INCLUDING STORAGE) INHERITS (ctlt1); DROP TABLE ctlt1, ctlt2, ctlt3, ctlt4, ctlt12_storage, ctlt12_comments, ctlt1_inh, ctlt13_inh, ctlt13_like, ctlt_all, ctla, ctlb CASCADE; /* LIKE with other relation kinds */ CREATE TABLE ctlt4 (a int, b text); CREATE SEQUENCE ctlseq1; CREATE TABLE ctlt10 (LIKE ctlseq1); -- fail CREATE VIEW ctlv1 AS SELECT * FROM ctlt4; CREATE TABLE ctlt11 (LIKE ctlv1); CREATE TABLE ctlt11a (LIKE ctlv1 INCLUDING ALL); CREATE TYPE ctlty1 AS (a int, b text); CREATE TABLE ctlt12 (LIKE ctlty1); DROP SEQUENCE ctlseq1; DROP TYPE ctlty1; DROP VIEW ctlv1; DROP TABLE IF EXISTS ctlt4, ctlt10, ctlt11, ctlt11a, ctlt12; pgFormatter-4.2/t/pg-test-files/sql/create_type.sql000066400000000000000000000104141361326045100224240ustar00rootroot00000000000000-- -- CREATE_TYPE -- -- -- Note: widget_in/out were created in create_function_1, without any -- prior shell-type creation. These commands therefore complete a test -- of the "old style" approach of making the functions first. -- CREATE TYPE widget ( internallength = 24, input = widget_in, output = widget_out, typmod_in = numerictypmodin, typmod_out = numerictypmodout, alignment = double ); CREATE TYPE city_budget ( internallength = 16, input = int44in, output = int44out, element = int4, category = 'x', -- just to verify the system will take it preferred = true -- ditto ); -- Test creation and destruction of shell types CREATE TYPE shell; CREATE TYPE shell; -- fail, type already present DROP TYPE shell; DROP TYPE shell; -- fail, type not exist -- also, let's leave one around for purposes of pg_dump testing CREATE TYPE myshell; -- -- Test type-related default values (broken in releases before PG 7.2) -- -- This part of the test also exercises the "new style" approach of making -- a shell type and then filling it in. -- CREATE TYPE int42; CREATE TYPE text_w_default; -- Make dummy I/O routines using the existing internal support for int4, text CREATE FUNCTION int42_in(cstring) RETURNS int42 AS 'int4in' LANGUAGE internal STRICT IMMUTABLE; CREATE FUNCTION int42_out(int42) RETURNS cstring AS 'int4out' LANGUAGE internal STRICT IMMUTABLE; CREATE FUNCTION text_w_default_in(cstring) RETURNS text_w_default AS 'textin' LANGUAGE internal STRICT IMMUTABLE; CREATE FUNCTION text_w_default_out(text_w_default) RETURNS cstring AS 'textout' LANGUAGE internal STRICT IMMUTABLE; CREATE TYPE int42 ( internallength = 4, input = int42_in, output = int42_out, alignment = int4, default = 42, passedbyvalue ); CREATE TYPE text_w_default ( internallength = variable, input = text_w_default_in, output = text_w_default_out, alignment = int4, default = 'zippo' ); CREATE TABLE default_test (f1 text_w_default, f2 int42); INSERT INTO default_test DEFAULT VALUES; SELECT * FROM default_test; -- invalid: non-lowercase quoted identifiers CREATE TYPE case_int42 ( "Internallength" = 4, "Input" = int42_in, "Output" = int42_out, "Alignment" = int4, "Default" = 42, "Passedbyvalue" ); -- Test stand-alone composite type CREATE TYPE default_test_row AS (f1 text_w_default, f2 int42); CREATE FUNCTION get_default_test() RETURNS SETOF default_test_row AS ' SELECT * FROM default_test; ' LANGUAGE SQL; SELECT * FROM get_default_test(); -- Test comments COMMENT ON TYPE bad IS 'bad comment'; COMMENT ON TYPE default_test_row IS 'good comment'; COMMENT ON TYPE default_test_row IS NULL; COMMENT ON COLUMN default_test_row.nope IS 'bad comment'; COMMENT ON COLUMN default_test_row.f1 IS 'good comment'; COMMENT ON COLUMN default_test_row.f1 IS NULL; -- Check shell type create for existing types CREATE TYPE text_w_default; -- should fail DROP TYPE default_test_row CASCADE; DROP TABLE default_test; -- Check type create with input/output incompatibility CREATE TYPE not_existing_type (INPUT = array_in, OUTPUT = array_out, ELEMENT = int, INTERNALLENGTH = 32); -- Check dependency transfer of opaque functions when creating a new type CREATE FUNCTION base_fn_in(cstring) RETURNS opaque AS 'boolin' LANGUAGE internal IMMUTABLE STRICT; CREATE FUNCTION base_fn_out(opaque) RETURNS opaque AS 'boolout' LANGUAGE internal IMMUTABLE STRICT; CREATE TYPE base_type(INPUT = base_fn_in, OUTPUT = base_fn_out); DROP FUNCTION base_fn_in(cstring); -- error DROP FUNCTION base_fn_out(opaque); -- error DROP TYPE base_type; -- error DROP TYPE base_type CASCADE; -- Check usage of typmod with a user-defined type -- (we have borrowed numeric's typmod functions) CREATE TEMP TABLE mytab (foo widget(42,13,7)); -- should fail CREATE TEMP TABLE mytab (foo widget(42,13)); SELECT format_type(atttypid,atttypmod) FROM pg_attribute WHERE attrelid = 'mytab'::regclass AND attnum > 0; -- might as well exercise the widget type while we're here INSERT INTO mytab VALUES ('(1,2,3)'), ('(-44,5.5,12)'); TABLE mytab; -- and test format_type() a bit more, too select format_type('varchar'::regtype, 42); select format_type('bpchar'::regtype, null); -- this behavior difference is intentional select format_type('bpchar'::regtype, -1); pgFormatter-4.2/t/pg-test-files/sql/create_view.sql000066400000000000000000000433171361326045100224250ustar00rootroot00000000000000-- -- CREATE_VIEW -- Virtual class definitions -- (this also tests the query rewrite system) -- CREATE VIEW street AS SELECT r.name, r.thepath, c.cname AS cname FROM ONLY road r, real_city c WHERE c.outline ## r.thepath; CREATE VIEW iexit AS SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE ih.thepath ## r.thepath; CREATE VIEW toyemp AS SELECT name, age, location, 12*salary AS annualsal FROM emp; -- Test comments COMMENT ON VIEW noview IS 'no view'; COMMENT ON VIEW toyemp IS 'is a view'; COMMENT ON VIEW toyemp IS NULL; -- These views are left around mainly to exercise special cases in pg_dump. CREATE TABLE view_base_table (key int PRIMARY KEY, data varchar(20)); CREATE VIEW key_dependent_view AS SELECT * FROM view_base_table GROUP BY key; ALTER TABLE view_base_table DROP CONSTRAINT view_base_table_pkey; -- fails CREATE VIEW key_dependent_view_no_cols AS SELECT FROM view_base_table GROUP BY key HAVING length(data) > 0; -- -- CREATE OR REPLACE VIEW -- CREATE OR REPLACE VIEW viewtest AS SELECT * FROM viewtest_tbl; CREATE OR REPLACE VIEW viewtest AS SELECT * FROM viewtest_tbl WHERE a > 10; SELECT * FROM viewtest; CREATE OR REPLACE VIEW viewtest AS SELECT a, b FROM viewtest_tbl WHERE a > 5 ORDER BY b DESC; SELECT * FROM viewtest; -- should fail CREATE OR REPLACE VIEW viewtest AS SELECT a FROM viewtest_tbl WHERE a <> 20; -- should fail CREATE OR REPLACE VIEW viewtest AS SELECT 1, * FROM viewtest_tbl; -- should fail CREATE OR REPLACE VIEW viewtest AS SELECT a, b::numeric FROM viewtest_tbl; -- should work CREATE OR REPLACE VIEW viewtest AS SELECT a, b, 0 AS c FROM viewtest_tbl; DROP VIEW viewtest; DROP TABLE viewtest_tbl; -- tests for temporary views CREATE SCHEMA temp_view_test CREATE TABLE base_table (a int, id int) CREATE TABLE base_table2 (a int, id int); SET search_path TO temp_view_test, public; CREATE TEMPORARY TABLE temp_table (a int, id int); -- should be created in temp_view_test schema CREATE VIEW v1 AS SELECT * FROM base_table; -- should be created in temp object schema CREATE VIEW v1_temp AS SELECT * FROM temp_table; -- should be created in temp object schema CREATE TEMP VIEW v2_temp AS SELECT * FROM base_table; -- should be created in temp_views schema CREATE VIEW temp_view_test.v2 AS SELECT * FROM base_table; -- should fail CREATE VIEW temp_view_test.v3_temp AS SELECT * FROM temp_table; -- should fail CREATE SCHEMA test_view_schema CREATE TEMP VIEW testview AS SELECT 1; -- joins: if any of the join relations are temporary, the view -- should also be temporary -- should be non-temp CREATE VIEW v3 AS SELECT t1.a AS t1_a, t2.a AS t2_a FROM base_table t1, base_table2 t2 WHERE t1.id = t2.id; -- should be temp (one join rel is temp) CREATE VIEW v4_temp AS SELECT t1.a AS t1_a, t2.a AS t2_a FROM base_table t1, temp_table t2 WHERE t1.id = t2.id; -- should be temp CREATE VIEW v5_temp AS SELECT t1.a AS t1_a, t2.a AS t2_a, t3.a AS t3_a FROM base_table t1, base_table2 t2, temp_table t3 WHERE t1.id = t2.id and t2.id = t3.id; -- subqueries CREATE VIEW v4 AS SELECT * FROM base_table WHERE id IN (SELECT id FROM base_table2); CREATE VIEW v5 AS SELECT t1.id, t2.a FROM base_table t1, (SELECT * FROM base_table2) t2; CREATE VIEW v6 AS SELECT * FROM base_table WHERE EXISTS (SELECT 1 FROM base_table2); CREATE VIEW v7 AS SELECT * FROM base_table WHERE NOT EXISTS (SELECT 1 FROM base_table2); CREATE VIEW v8 AS SELECT * FROM base_table WHERE EXISTS (SELECT 1); CREATE VIEW v6_temp AS SELECT * FROM base_table WHERE id IN (SELECT id FROM temp_table); CREATE VIEW v7_temp AS SELECT t1.id, t2.a FROM base_table t1, (SELECT * FROM temp_table) t2; CREATE VIEW v8_temp AS SELECT * FROM base_table WHERE EXISTS (SELECT 1 FROM temp_table); CREATE VIEW v9_temp AS SELECT * FROM base_table WHERE NOT EXISTS (SELECT 1 FROM temp_table); -- a view should also be temporary if it references a temporary view CREATE VIEW v10_temp AS SELECT * FROM v7_temp; CREATE VIEW v11_temp AS SELECT t1.id, t2.a FROM base_table t1, v10_temp t2; CREATE VIEW v12_temp AS SELECT true FROM v11_temp; -- a view should also be temporary if it references a temporary sequence CREATE SEQUENCE seq1; CREATE TEMPORARY SEQUENCE seq1_temp; CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1; CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp; SELECT relname FROM pg_class WHERE relname LIKE 'v_' AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'temp_view_test') ORDER BY relname; SELECT relname FROM pg_class WHERE relname LIKE 'v%' AND relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname LIKE 'pg_temp%') ORDER BY relname; CREATE SCHEMA testviewschm2; SET search_path TO testviewschm2, public; CREATE TABLE t1 (num int, name text); CREATE TABLE t2 (num2 int, value text); CREATE TEMP TABLE tt (num2 int, value text); CREATE VIEW nontemp1 AS SELECT * FROM t1 CROSS JOIN t2; CREATE VIEW temporal1 AS SELECT * FROM t1 CROSS JOIN tt; CREATE VIEW nontemp2 AS SELECT * FROM t1 INNER JOIN t2 ON t1.num = t2.num2; CREATE VIEW temporal2 AS SELECT * FROM t1 INNER JOIN tt ON t1.num = tt.num2; CREATE VIEW nontemp3 AS SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num2; CREATE VIEW temporal3 AS SELECT * FROM t1 LEFT JOIN tt ON t1.num = tt.num2; CREATE VIEW nontemp4 AS SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num2 AND t2.value = 'xxx'; CREATE VIEW temporal4 AS SELECT * FROM t1 LEFT JOIN tt ON t1.num = tt.num2 AND tt.value = 'xxx'; SELECT relname FROM pg_class WHERE relname LIKE 'nontemp%' AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'testviewschm2') ORDER BY relname; SELECT relname FROM pg_class WHERE relname LIKE 'temporal%' AND relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname LIKE 'pg_temp%') ORDER BY relname; CREATE TABLE tbl1 ( a int, b int); CREATE TABLE tbl2 (c int, d int); CREATE TABLE tbl3 (e int, f int); CREATE TABLE tbl4 (g int, h int); CREATE TEMP TABLE tmptbl (i int, j int); --Should be in testviewschm2 CREATE VIEW pubview AS SELECT * FROM tbl1 WHERE tbl1.a BETWEEN (SELECT d FROM tbl2 WHERE c = 1) AND (SELECT e FROM tbl3 WHERE f = 2) AND EXISTS (SELECT g FROM tbl4 LEFT JOIN tbl3 ON tbl4.h = tbl3.f); SELECT count(*) FROM pg_class where relname = 'pubview' AND relnamespace IN (SELECT OID FROM pg_namespace WHERE nspname = 'testviewschm2'); --Should be in temp object schema CREATE VIEW mytempview AS SELECT * FROM tbl1 WHERE tbl1.a BETWEEN (SELECT d FROM tbl2 WHERE c = 1) AND (SELECT e FROM tbl3 WHERE f = 2) AND EXISTS (SELECT g FROM tbl4 LEFT JOIN tbl3 ON tbl4.h = tbl3.f) AND NOT EXISTS (SELECT g FROM tbl4 LEFT JOIN tmptbl ON tbl4.h = tmptbl.j); SELECT count(*) FROM pg_class where relname LIKE 'mytempview' And relnamespace IN (SELECT OID FROM pg_namespace WHERE nspname LIKE 'pg_temp%'); -- -- CREATE VIEW and WITH(...) clause -- CREATE VIEW mysecview1 AS SELECT * FROM tbl1 WHERE a = 0; CREATE VIEW mysecview2 WITH (security_barrier=true) AS SELECT * FROM tbl1 WHERE a > 0; CREATE VIEW mysecview3 WITH (security_barrier=false) AS SELECT * FROM tbl1 WHERE a < 0; CREATE VIEW mysecview4 WITH (security_barrier) AS SELECT * FROM tbl1 WHERE a <> 0; CREATE VIEW mysecview5 WITH (security_barrier=100) -- Error AS SELECT * FROM tbl1 WHERE a > 100; CREATE VIEW mysecview6 WITH (invalid_option) -- Error AS SELECT * FROM tbl1 WHERE a < 100; SELECT relname, relkind, reloptions FROM pg_class WHERE oid in ('mysecview1'::regclass, 'mysecview2'::regclass, 'mysecview3'::regclass, 'mysecview4'::regclass) ORDER BY relname; CREATE OR REPLACE VIEW mysecview1 AS SELECT * FROM tbl1 WHERE a = 256; CREATE OR REPLACE VIEW mysecview2 AS SELECT * FROM tbl1 WHERE a > 256; CREATE OR REPLACE VIEW mysecview3 WITH (security_barrier=true) AS SELECT * FROM tbl1 WHERE a < 256; CREATE OR REPLACE VIEW mysecview4 WITH (security_barrier=false) AS SELECT * FROM tbl1 WHERE a <> 256; SELECT relname, relkind, reloptions FROM pg_class WHERE oid in ('mysecview1'::regclass, 'mysecview2'::regclass, 'mysecview3'::regclass, 'mysecview4'::regclass) ORDER BY relname; -- Check that unknown literals are converted to "text" in CREATE VIEW, -- so that we don't end up with unknown-type columns. CREATE VIEW unspecified_types AS SELECT 42 as i, 42.5 as num, 'foo' as u, 'foo'::unknown as u2, null as n; \d+ unspecified_types SELECT * FROM unspecified_types; -- This test checks that proper typmods are assigned in a multi-row VALUES CREATE VIEW tt1 AS SELECT * FROM ( VALUES ('abc'::varchar(3), '0123456789', 42, 'abcd'::varchar(4)), ('0123456789', 'abc'::varchar(3), 42.12, 'abc'::varchar(4)) ) vv(a,b,c,d); \d+ tt1 SELECT * FROM tt1; SELECT a::varchar(3) FROM tt1; DROP VIEW tt1; -- Test view decompilation in the face of relation renaming conflicts CREATE TABLE tt1 (f1 int, f2 int, f3 text); CREATE TABLE tx1 (x1 int, x2 int, x3 text); CREATE TABLE temp_view_test.tt1 (y1 int, f2 int, f3 text); CREATE VIEW aliased_view_1 AS select * from tt1 where exists (select 1 from tx1 where tt1.f1 = tx1.x1); CREATE VIEW aliased_view_2 AS select * from tt1 a1 where exists (select 1 from tx1 where a1.f1 = tx1.x1); CREATE VIEW aliased_view_3 AS select * from tt1 where exists (select 1 from tx1 a2 where tt1.f1 = a2.x1); CREATE VIEW aliased_view_4 AS select * from temp_view_test.tt1 where exists (select 1 from tt1 where temp_view_test.tt1.y1 = tt1.f1); \d+ aliased_view_1 \d+ aliased_view_2 \d+ aliased_view_3 \d+ aliased_view_4 ALTER TABLE tx1 RENAME TO a1; \d+ aliased_view_1 \d+ aliased_view_2 \d+ aliased_view_3 \d+ aliased_view_4 ALTER TABLE tt1 RENAME TO a2; \d+ aliased_view_1 \d+ aliased_view_2 \d+ aliased_view_3 \d+ aliased_view_4 ALTER TABLE a1 RENAME TO tt1; \d+ aliased_view_1 \d+ aliased_view_2 \d+ aliased_view_3 \d+ aliased_view_4 ALTER TABLE a2 RENAME TO tx1; ALTER TABLE tx1 SET SCHEMA temp_view_test; \d+ aliased_view_1 \d+ aliased_view_2 \d+ aliased_view_3 \d+ aliased_view_4 ALTER TABLE temp_view_test.tt1 RENAME TO tmp1; ALTER TABLE temp_view_test.tmp1 SET SCHEMA testviewschm2; ALTER TABLE tmp1 RENAME TO tx1; \d+ aliased_view_1 \d+ aliased_view_2 \d+ aliased_view_3 \d+ aliased_view_4 -- Test view decompilation in the face of column addition/deletion/renaming create table tt2 (a int, b int, c int); create table tt3 (ax int8, b int2, c numeric); create table tt4 (ay int, b int, q int); create view v1 as select * from tt2 natural join tt3; create view v1a as select * from (tt2 natural join tt3) j; create view v2 as select * from tt2 join tt3 using (b,c) join tt4 using (b); create view v2a as select * from (tt2 join tt3 using (b,c) join tt4 using (b)) j; create view v3 as select * from tt2 join tt3 using (b,c) full join tt4 using (b); select pg_get_viewdef('v1', true); select pg_get_viewdef('v1a', true); select pg_get_viewdef('v2', true); select pg_get_viewdef('v2a', true); select pg_get_viewdef('v3', true); alter table tt2 add column d int; alter table tt2 add column e int; select pg_get_viewdef('v1', true); select pg_get_viewdef('v1a', true); select pg_get_viewdef('v2', true); select pg_get_viewdef('v2a', true); select pg_get_viewdef('v3', true); alter table tt3 rename c to d; select pg_get_viewdef('v1', true); select pg_get_viewdef('v1a', true); select pg_get_viewdef('v2', true); select pg_get_viewdef('v2a', true); select pg_get_viewdef('v3', true); alter table tt3 add column c int; alter table tt3 add column e int; select pg_get_viewdef('v1', true); select pg_get_viewdef('v1a', true); select pg_get_viewdef('v2', true); select pg_get_viewdef('v2a', true); select pg_get_viewdef('v3', true); alter table tt2 drop column d; select pg_get_viewdef('v1', true); select pg_get_viewdef('v1a', true); select pg_get_viewdef('v2', true); select pg_get_viewdef('v2a', true); select pg_get_viewdef('v3', true); create table tt5 (a int, b int); create table tt6 (c int, d int); create view vv1 as select * from (tt5 cross join tt6) j(aa,bb,cc,dd); select pg_get_viewdef('vv1', true); alter table tt5 add column c int; select pg_get_viewdef('vv1', true); alter table tt5 add column cc int; select pg_get_viewdef('vv1', true); alter table tt5 drop column c; select pg_get_viewdef('vv1', true); -- Unnamed FULL JOIN USING is lots of fun too create table tt7 (x int, xx int, y int); alter table tt7 drop column xx; create table tt8 (x int, z int); create view vv2 as select * from (values(1,2,3,4,5)) v(a,b,c,d,e) union all select * from tt7 full join tt8 using (x), tt8 tt8x; select pg_get_viewdef('vv2', true); create view vv3 as select * from (values(1,2,3,4,5,6)) v(a,b,c,x,e,f) union all select * from tt7 full join tt8 using (x), tt7 tt7x full join tt8 tt8x using (x); select pg_get_viewdef('vv3', true); create view vv4 as select * from (values(1,2,3,4,5,6,7)) v(a,b,c,x,e,f,g) union all select * from tt7 full join tt8 using (x), tt7 tt7x full join tt8 tt8x using (x) full join tt8 tt8y using (x); select pg_get_viewdef('vv4', true); alter table tt7 add column zz int; alter table tt7 add column z int; alter table tt7 drop column zz; alter table tt8 add column z2 int; select pg_get_viewdef('vv2', true); select pg_get_viewdef('vv3', true); select pg_get_viewdef('vv4', true); -- Implicit coercions in a JOIN USING create issues similar to FULL JOIN create table tt7a (x date, xx int, y int); alter table tt7a drop column xx; create table tt8a (x timestamptz, z int); create view vv2a as select * from (values(now(),2,3,now(),5)) v(a,b,c,d,e) union all select * from tt7a left join tt8a using (x), tt8a tt8ax; select pg_get_viewdef('vv2a', true); -- -- Also check dropping a column that existed when the view was made -- create table tt9 (x int, xx int, y int); create table tt10 (x int, z int); create view vv5 as select x,y,z from tt9 join tt10 using(x); select pg_get_viewdef('vv5', true); alter table tt9 drop column xx; select pg_get_viewdef('vv5', true); -- -- Another corner case is that we might add a column to a table below a -- JOIN USING, and thereby make the USING column name ambiguous -- create table tt11 (x int, y int); create table tt12 (x int, z int); create table tt13 (z int, q int); create view vv6 as select x,y,z,q from (tt11 join tt12 using(x)) join tt13 using(z); select pg_get_viewdef('vv6', true); alter table tt11 add column z int; select pg_get_viewdef('vv6', true); -- -- Check cases involving dropped/altered columns in a function's rowtype result -- create table tt14t (f1 text, f2 text, f3 text, f4 text); insert into tt14t values('foo', 'bar', 'baz', '42'); alter table tt14t drop column f2; create function tt14f() returns setof tt14t as $$ declare rec1 record; begin for rec1 in select * from tt14t loop return next rec1; end loop; end; $$ language plpgsql; create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); select * from tt14v; begin; -- this perhaps should be rejected, but it isn't: alter table tt14t drop column f3; -- f3 is still in the view ... select pg_get_viewdef('tt14v', true); -- but will fail at execution select f1, f4 from tt14v; select * from tt14v; rollback; begin; -- this perhaps should be rejected, but it isn't: alter table tt14t alter column f4 type integer using f4::integer; -- f4 is still in the view ... select pg_get_viewdef('tt14v', true); -- but will fail at execution select f1, f3 from tt14v; select * from tt14v; rollback; -- check display of whole-row variables in some corner cases create type nestedcomposite as (x int8_tbl); create view tt15v as select row(i)::nestedcomposite from int8_tbl i; select * from tt15v; select pg_get_viewdef('tt15v', true); select row(i.*::int8_tbl)::nestedcomposite from int8_tbl i; create view tt16v as select * from int8_tbl i, lateral(values(i)) ss; select * from tt16v; select pg_get_viewdef('tt16v', true); select * from int8_tbl i, lateral(values(i.*::int8_tbl)) ss; create view tt17v as select * from int8_tbl i where i in (values(i)); select * from tt17v; select pg_get_viewdef('tt17v', true); select * from int8_tbl i where i.* in (values(i.*::int8_tbl)); -- check unique-ification of overlength names create view tt18v as select * from int8_tbl xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxy union all select * from int8_tbl xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxz; select pg_get_viewdef('tt18v', true); explain (costs off) select * from tt18v; -- check display of ScalarArrayOp with a sub-select select 'foo'::text = any(array['abc','def','foo']::text[]); select 'foo'::text = any((select array['abc','def','foo']::text[])); -- fail select 'foo'::text = any((select array['abc','def','foo']::text[])::text[]); create view tt19v as select 'foo'::text = any(array['abc','def','foo']::text[]) c1, 'foo'::text = any((select array['abc','def','foo']::text[])::text[]) c2; select pg_get_viewdef('tt19v', true); -- check display of assorted RTE_FUNCTION expressions create view tt20v as select * from coalesce(1,2) as c, collation for ('x'::text) col, current_date as d, localtimestamp(3) as t, cast(1+2 as int4) as i4, cast(1+2 as int8) as i8; select pg_get_viewdef('tt20v', true); -- corner cases with empty join conditions create view tt21v as select * from tt5 natural inner join tt6; select pg_get_viewdef('tt21v', true); create view tt22v as select * from tt5 natural left join tt6; select pg_get_viewdef('tt22v', true); -- check handling of views with immediately-renamed columns create view tt23v (col_a, col_b) as select q1 as other_name1, q2 as other_name2 from int8_tbl union select 42, 43; select pg_get_viewdef('tt23v', true); select pg_get_ruledef(oid, true) from pg_rewrite where ev_class = 'tt23v'::regclass and ev_type = '1'; -- clean up all the random objects we made above DROP SCHEMA temp_view_test CASCADE; DROP SCHEMA testviewschm2 CASCADE; pgFormatter-4.2/t/pg-test-files/sql/date.sql000066400000000000000000000256641361326045100210520ustar00rootroot00000000000000-- -- DATE -- CREATE TABLE DATE_TBL (f1 date); INSERT INTO DATE_TBL VALUES ('1957-04-09'); INSERT INTO DATE_TBL VALUES ('1957-06-13'); INSERT INTO DATE_TBL VALUES ('1996-02-28'); INSERT INTO DATE_TBL VALUES ('1996-02-29'); INSERT INTO DATE_TBL VALUES ('1996-03-01'); INSERT INTO DATE_TBL VALUES ('1996-03-02'); INSERT INTO DATE_TBL VALUES ('1997-02-28'); INSERT INTO DATE_TBL VALUES ('1997-02-29'); INSERT INTO DATE_TBL VALUES ('1997-03-01'); INSERT INTO DATE_TBL VALUES ('1997-03-02'); INSERT INTO DATE_TBL VALUES ('2000-04-01'); INSERT INTO DATE_TBL VALUES ('2000-04-02'); INSERT INTO DATE_TBL VALUES ('2000-04-03'); INSERT INTO DATE_TBL VALUES ('2038-04-08'); INSERT INTO DATE_TBL VALUES ('2039-04-09'); INSERT INTO DATE_TBL VALUES ('2040-04-10'); SELECT f1 AS "Fifteen" FROM DATE_TBL; SELECT f1 AS "Nine" FROM DATE_TBL WHERE f1 < '2000-01-01'; SELECT f1 AS "Three" FROM DATE_TBL WHERE f1 BETWEEN '2000-01-01' AND '2001-01-01'; -- -- Check all the documented input formats -- SET datestyle TO iso; -- display results in ISO SET datestyle TO ymd; SELECT date 'January 8, 1999'; SELECT date '1999-01-08'; SELECT date '1999-01-18'; SELECT date '1/8/1999'; SELECT date '1/18/1999'; SELECT date '18/1/1999'; SELECT date '01/02/03'; SELECT date '19990108'; SELECT date '990108'; SELECT date '1999.008'; SELECT date 'J2451187'; SELECT date 'January 8, 99 BC'; SELECT date '99-Jan-08'; SELECT date '1999-Jan-08'; SELECT date '08-Jan-99'; SELECT date '08-Jan-1999'; SELECT date 'Jan-08-99'; SELECT date 'Jan-08-1999'; SELECT date '99-08-Jan'; SELECT date '1999-08-Jan'; SELECT date '99 Jan 08'; SELECT date '1999 Jan 08'; SELECT date '08 Jan 99'; SELECT date '08 Jan 1999'; SELECT date 'Jan 08 99'; SELECT date 'Jan 08 1999'; SELECT date '99 08 Jan'; SELECT date '1999 08 Jan'; SELECT date '99-01-08'; SELECT date '1999-01-08'; SELECT date '08-01-99'; SELECT date '08-01-1999'; SELECT date '01-08-99'; SELECT date '01-08-1999'; SELECT date '99-08-01'; SELECT date '1999-08-01'; SELECT date '99 01 08'; SELECT date '1999 01 08'; SELECT date '08 01 99'; SELECT date '08 01 1999'; SELECT date '01 08 99'; SELECT date '01 08 1999'; SELECT date '99 08 01'; SELECT date '1999 08 01'; SET datestyle TO dmy; SELECT date 'January 8, 1999'; SELECT date '1999-01-08'; SELECT date '1999-01-18'; SELECT date '1/8/1999'; SELECT date '1/18/1999'; SELECT date '18/1/1999'; SELECT date '01/02/03'; SELECT date '19990108'; SELECT date '990108'; SELECT date '1999.008'; SELECT date 'J2451187'; SELECT date 'January 8, 99 BC'; SELECT date '99-Jan-08'; SELECT date '1999-Jan-08'; SELECT date '08-Jan-99'; SELECT date '08-Jan-1999'; SELECT date 'Jan-08-99'; SELECT date 'Jan-08-1999'; SELECT date '99-08-Jan'; SELECT date '1999-08-Jan'; SELECT date '99 Jan 08'; SELECT date '1999 Jan 08'; SELECT date '08 Jan 99'; SELECT date '08 Jan 1999'; SELECT date 'Jan 08 99'; SELECT date 'Jan 08 1999'; SELECT date '99 08 Jan'; SELECT date '1999 08 Jan'; SELECT date '99-01-08'; SELECT date '1999-01-08'; SELECT date '08-01-99'; SELECT date '08-01-1999'; SELECT date '01-08-99'; SELECT date '01-08-1999'; SELECT date '99-08-01'; SELECT date '1999-08-01'; SELECT date '99 01 08'; SELECT date '1999 01 08'; SELECT date '08 01 99'; SELECT date '08 01 1999'; SELECT date '01 08 99'; SELECT date '01 08 1999'; SELECT date '99 08 01'; SELECT date '1999 08 01'; SET datestyle TO mdy; SELECT date 'January 8, 1999'; SELECT date '1999-01-08'; SELECT date '1999-01-18'; SELECT date '1/8/1999'; SELECT date '1/18/1999'; SELECT date '18/1/1999'; SELECT date '01/02/03'; SELECT date '19990108'; SELECT date '990108'; SELECT date '1999.008'; SELECT date 'J2451187'; SELECT date 'January 8, 99 BC'; SELECT date '99-Jan-08'; SELECT date '1999-Jan-08'; SELECT date '08-Jan-99'; SELECT date '08-Jan-1999'; SELECT date 'Jan-08-99'; SELECT date 'Jan-08-1999'; SELECT date '99-08-Jan'; SELECT date '1999-08-Jan'; SELECT date '99 Jan 08'; SELECT date '1999 Jan 08'; SELECT date '08 Jan 99'; SELECT date '08 Jan 1999'; SELECT date 'Jan 08 99'; SELECT date 'Jan 08 1999'; SELECT date '99 08 Jan'; SELECT date '1999 08 Jan'; SELECT date '99-01-08'; SELECT date '1999-01-08'; SELECT date '08-01-99'; SELECT date '08-01-1999'; SELECT date '01-08-99'; SELECT date '01-08-1999'; SELECT date '99-08-01'; SELECT date '1999-08-01'; SELECT date '99 01 08'; SELECT date '1999 01 08'; SELECT date '08 01 99'; SELECT date '08 01 1999'; SELECT date '01 08 99'; SELECT date '01 08 1999'; SELECT date '99 08 01'; SELECT date '1999 08 01'; -- Check upper and lower limits of date range SELECT date '4714-11-24 BC'; SELECT date '4714-11-23 BC'; -- out of range SELECT date '5874897-12-31'; SELECT date '5874898-01-01'; -- out of range RESET datestyle; -- -- Simple math -- Leave most of it for the horology tests -- SELECT f1 - date '2000-01-01' AS "Days From 2K" FROM DATE_TBL; SELECT f1 - date 'epoch' AS "Days From Epoch" FROM DATE_TBL; SELECT date 'yesterday' - date 'today' AS "One day"; SELECT date 'today' - date 'tomorrow' AS "One day"; SELECT date 'yesterday' - date 'tomorrow' AS "Two days"; SELECT date 'tomorrow' - date 'today' AS "One day"; SELECT date 'today' - date 'yesterday' AS "One day"; SELECT date 'tomorrow' - date 'yesterday' AS "Two days"; -- -- test extract! -- -- epoch -- SELECT EXTRACT(EPOCH FROM DATE '1970-01-01'); -- 0 SELECT EXTRACT(EPOCH FROM TIMESTAMP '1970-01-01'); -- 0 SELECT EXTRACT(EPOCH FROM TIMESTAMPTZ '1970-01-01+00'); -- 0 -- -- century -- SELECT EXTRACT(CENTURY FROM DATE '0101-12-31 BC'); -- -2 SELECT EXTRACT(CENTURY FROM DATE '0100-12-31 BC'); -- -1 SELECT EXTRACT(CENTURY FROM DATE '0001-12-31 BC'); -- -1 SELECT EXTRACT(CENTURY FROM DATE '0001-01-01'); -- 1 SELECT EXTRACT(CENTURY FROM DATE '0001-01-01 AD'); -- 1 SELECT EXTRACT(CENTURY FROM DATE '1900-12-31'); -- 19 SELECT EXTRACT(CENTURY FROM DATE '1901-01-01'); -- 20 SELECT EXTRACT(CENTURY FROM DATE '2000-12-31'); -- 20 SELECT EXTRACT(CENTURY FROM DATE '2001-01-01'); -- 21 SELECT EXTRACT(CENTURY FROM CURRENT_DATE)>=21 AS True; -- true -- -- millennium -- SELECT EXTRACT(MILLENNIUM FROM DATE '0001-12-31 BC'); -- -1 SELECT EXTRACT(MILLENNIUM FROM DATE '0001-01-01 AD'); -- 1 SELECT EXTRACT(MILLENNIUM FROM DATE '1000-12-31'); -- 1 SELECT EXTRACT(MILLENNIUM FROM DATE '1001-01-01'); -- 2 SELECT EXTRACT(MILLENNIUM FROM DATE '2000-12-31'); -- 2 SELECT EXTRACT(MILLENNIUM FROM DATE '2001-01-01'); -- 3 -- next test to be fixed on the turn of the next millennium;-) SELECT EXTRACT(MILLENNIUM FROM CURRENT_DATE); -- 3 -- -- decade -- SELECT EXTRACT(DECADE FROM DATE '1994-12-25'); -- 199 SELECT EXTRACT(DECADE FROM DATE '0010-01-01'); -- 1 SELECT EXTRACT(DECADE FROM DATE '0009-12-31'); -- 0 SELECT EXTRACT(DECADE FROM DATE '0001-01-01 BC'); -- 0 SELECT EXTRACT(DECADE FROM DATE '0002-12-31 BC'); -- -1 SELECT EXTRACT(DECADE FROM DATE '0011-01-01 BC'); -- -1 SELECT EXTRACT(DECADE FROM DATE '0012-12-31 BC'); -- -2 -- -- some other types: -- -- on a timestamp. SELECT EXTRACT(CENTURY FROM NOW())>=21 AS True; -- true SELECT EXTRACT(CENTURY FROM TIMESTAMP '1970-03-20 04:30:00.00000'); -- 20 -- on an interval SELECT EXTRACT(CENTURY FROM INTERVAL '100 y'); -- 1 SELECT EXTRACT(CENTURY FROM INTERVAL '99 y'); -- 0 SELECT EXTRACT(CENTURY FROM INTERVAL '-99 y'); -- 0 SELECT EXTRACT(CENTURY FROM INTERVAL '-100 y'); -- -1 -- -- test trunc function! -- SELECT DATE_TRUNC('MILLENNIUM', TIMESTAMP '1970-03-20 04:30:00.00000'); -- 1001 SELECT DATE_TRUNC('MILLENNIUM', DATE '1970-03-20'); -- 1001-01-01 SELECT DATE_TRUNC('CENTURY', TIMESTAMP '1970-03-20 04:30:00.00000'); -- 1901 SELECT DATE_TRUNC('CENTURY', DATE '1970-03-20'); -- 1901 SELECT DATE_TRUNC('CENTURY', DATE '2004-08-10'); -- 2001-01-01 SELECT DATE_TRUNC('CENTURY', DATE '0002-02-04'); -- 0001-01-01 SELECT DATE_TRUNC('CENTURY', DATE '0055-08-10 BC'); -- 0100-01-01 BC SELECT DATE_TRUNC('DECADE', DATE '1993-12-25'); -- 1990-01-01 SELECT DATE_TRUNC('DECADE', DATE '0004-12-25'); -- 0001-01-01 BC SELECT DATE_TRUNC('DECADE', DATE '0002-12-31 BC'); -- 0011-01-01 BC -- -- test infinity -- select 'infinity'::date, '-infinity'::date; select 'infinity'::date > 'today'::date as t; select '-infinity'::date < 'today'::date as t; select isfinite('infinity'::date), isfinite('-infinity'::date), isfinite('today'::date); -- -- oscillating fields from non-finite date/timestamptz: -- SELECT EXTRACT(HOUR FROM DATE 'infinity'); -- NULL SELECT EXTRACT(HOUR FROM DATE '-infinity'); -- NULL SELECT EXTRACT(HOUR FROM TIMESTAMP 'infinity'); -- NULL SELECT EXTRACT(HOUR FROM TIMESTAMP '-infinity'); -- NULL SELECT EXTRACT(HOUR FROM TIMESTAMPTZ 'infinity'); -- NULL SELECT EXTRACT(HOUR FROM TIMESTAMPTZ '-infinity'); -- NULL -- all possible fields SELECT EXTRACT(MICROSECONDS FROM DATE 'infinity'); -- NULL SELECT EXTRACT(MILLISECONDS FROM DATE 'infinity'); -- NULL SELECT EXTRACT(SECOND FROM DATE 'infinity'); -- NULL SELECT EXTRACT(MINUTE FROM DATE 'infinity'); -- NULL SELECT EXTRACT(HOUR FROM DATE 'infinity'); -- NULL SELECT EXTRACT(DAY FROM DATE 'infinity'); -- NULL SELECT EXTRACT(MONTH FROM DATE 'infinity'); -- NULL SELECT EXTRACT(QUARTER FROM DATE 'infinity'); -- NULL SELECT EXTRACT(WEEK FROM DATE 'infinity'); -- NULL SELECT EXTRACT(DOW FROM DATE 'infinity'); -- NULL SELECT EXTRACT(ISODOW FROM DATE 'infinity'); -- NULL SELECT EXTRACT(DOY FROM DATE 'infinity'); -- NULL SELECT EXTRACT(TIMEZONE FROM DATE 'infinity'); -- NULL SELECT EXTRACT(TIMEZONE_M FROM DATE 'infinity'); -- NULL SELECT EXTRACT(TIMEZONE_H FROM DATE 'infinity'); -- NULL -- -- monotonic fields from non-finite date/timestamptz: -- SELECT EXTRACT(EPOCH FROM DATE 'infinity'); -- Infinity SELECT EXTRACT(EPOCH FROM DATE '-infinity'); -- -Infinity SELECT EXTRACT(EPOCH FROM TIMESTAMP 'infinity'); -- Infinity SELECT EXTRACT(EPOCH FROM TIMESTAMP '-infinity'); -- -Infinity SELECT EXTRACT(EPOCH FROM TIMESTAMPTZ 'infinity'); -- Infinity SELECT EXTRACT(EPOCH FROM TIMESTAMPTZ '-infinity'); -- -Infinity -- all possible fields SELECT EXTRACT(YEAR FROM DATE 'infinity'); -- Infinity SELECT EXTRACT(DECADE FROM DATE 'infinity'); -- Infinity SELECT EXTRACT(CENTURY FROM DATE 'infinity'); -- Infinity SELECT EXTRACT(MILLENNIUM FROM DATE 'infinity'); -- Infinity SELECT EXTRACT(JULIAN FROM DATE 'infinity'); -- Infinity SELECT EXTRACT(ISOYEAR FROM DATE 'infinity'); -- Infinity SELECT EXTRACT(EPOCH FROM DATE 'infinity'); -- Infinity -- -- wrong fields from non-finite date: -- SELECT EXTRACT(MICROSEC FROM DATE 'infinity'); -- ERROR: timestamp units "microsec" not recognized SELECT EXTRACT(UNDEFINED FROM DATE 'infinity'); -- ERROR: timestamp units "undefined" not supported -- test constructors select make_date(2013, 7, 15); select make_date(-44, 3, 15); select make_time(8, 20, 0.0); -- should fail select make_date(2013, 2, 30); select make_date(2013, 13, 1); select make_date(2013, 11, -1); select make_time(10, 55, 100.1); select make_time(24, 0, 2.1); pgFormatter-4.2/t/pg-test-files/sql/dbsize.sql000066400000000000000000000037631361326045100214110ustar00rootroot00000000000000SELECT size, pg_size_pretty(size), pg_size_pretty(-1 * size) FROM (VALUES (10::bigint), (1000::bigint), (1000000::bigint), (1000000000::bigint), (1000000000000::bigint), (1000000000000000::bigint)) x(size); SELECT size, pg_size_pretty(size), pg_size_pretty(-1 * size) FROM (VALUES (10::numeric), (1000::numeric), (1000000::numeric), (1000000000::numeric), (1000000000000::numeric), (1000000000000000::numeric), (10.5::numeric), (1000.5::numeric), (1000000.5::numeric), (1000000000.5::numeric), (1000000000000.5::numeric), (1000000000000000.5::numeric)) x(size); SELECT size, pg_size_bytes(size) FROM (VALUES ('1'), ('123bytes'), ('1kB'), ('1MB'), (' 1 GB'), ('1.5 GB '), ('1TB'), ('3000 TB'), ('1e6 MB')) x(size); -- case-insensitive units are supported SELECT size, pg_size_bytes(size) FROM (VALUES ('1'), ('123bYteS'), ('1kb'), ('1mb'), (' 1 Gb'), ('1.5 gB '), ('1tb'), ('3000 tb'), ('1e6 mb')) x(size); -- negative numbers are supported SELECT size, pg_size_bytes(size) FROM (VALUES ('-1'), ('-123bytes'), ('-1kb'), ('-1mb'), (' -1 Gb'), ('-1.5 gB '), ('-1tb'), ('-3000 TB'), ('-10e-1 MB')) x(size); -- different cases with allowed points SELECT size, pg_size_bytes(size) FROM (VALUES ('-1.'), ('-1.kb'), ('-1. kb'), ('-0. gb'), ('-.1'), ('-.1kb'), ('-.1 kb'), ('-.0 gb')) x(size); -- invalid inputs SELECT pg_size_bytes('1 AB'); SELECT pg_size_bytes('1 AB A'); SELECT pg_size_bytes('1 AB A '); SELECT pg_size_bytes('9223372036854775807.9'); SELECT pg_size_bytes('1e100'); SELECT pg_size_bytes('1e1000000000000000000'); SELECT pg_size_bytes('1 byte'); -- the singular "byte" is not supported SELECT pg_size_bytes(''); SELECT pg_size_bytes('kb'); SELECT pg_size_bytes('..'); SELECT pg_size_bytes('-.'); SELECT pg_size_bytes('-.kb'); SELECT pg_size_bytes('-. kb'); SELECT pg_size_bytes('.+912'); SELECT pg_size_bytes('+912+ kB'); SELECT pg_size_bytes('++123 kB'); pgFormatter-4.2/t/pg-test-files/sql/delete.sql000066400000000000000000000012371361326045100213650ustar00rootroot00000000000000CREATE TABLE delete_test ( id SERIAL PRIMARY KEY, a INT, b text ); INSERT INTO delete_test (a) VALUES (10); INSERT INTO delete_test (a, b) VALUES (50, repeat('x', 10000)); INSERT INTO delete_test (a) VALUES (100); -- allow an alias to be specified for DELETE's target table DELETE FROM delete_test AS dt WHERE dt.a > 75; -- if an alias is specified, don't allow the original table name -- to be referenced DELETE FROM delete_test dt WHERE delete_test.a > 25; SELECT id, a, char_length(b) FROM delete_test; -- delete a row with a TOASTed value DELETE FROM delete_test WHERE a > 25; SELECT id, a, char_length(b) FROM delete_test; DROP TABLE delete_test; pgFormatter-4.2/t/pg-test-files/sql/dependency.sql000066400000000000000000000070561361326045100222460ustar00rootroot00000000000000-- -- DEPENDENCIES -- CREATE USER regress_dep_user; CREATE USER regress_dep_user2; CREATE USER regress_dep_user3; CREATE GROUP regress_dep_group; CREATE TABLE deptest (f1 serial primary key, f2 text); GRANT SELECT ON TABLE deptest TO GROUP regress_dep_group; GRANT ALL ON TABLE deptest TO regress_dep_user, regress_dep_user2; -- can't drop neither because they have privileges somewhere DROP USER regress_dep_user; DROP GROUP regress_dep_group; -- if we revoke the privileges we can drop the group REVOKE SELECT ON deptest FROM GROUP regress_dep_group; DROP GROUP regress_dep_group; -- can't drop the user if we revoke the privileges partially REVOKE SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES ON deptest FROM regress_dep_user; DROP USER regress_dep_user; -- now we are OK to drop him REVOKE TRIGGER ON deptest FROM regress_dep_user; DROP USER regress_dep_user; -- we are OK too if we drop the privileges all at once REVOKE ALL ON deptest FROM regress_dep_user2; DROP USER regress_dep_user2; -- can't drop the owner of an object -- the error message detail here would include a pg_toast_nnn name that -- is not constant, so suppress it \set VERBOSITY terse ALTER TABLE deptest OWNER TO regress_dep_user3; DROP USER regress_dep_user3; \set VERBOSITY default -- if we drop the object, we can drop the user too DROP TABLE deptest; DROP USER regress_dep_user3; -- Test DROP OWNED CREATE USER regress_dep_user0; CREATE USER regress_dep_user1; CREATE USER regress_dep_user2; SET SESSION AUTHORIZATION regress_dep_user0; -- permission denied DROP OWNED BY regress_dep_user1; DROP OWNED BY regress_dep_user0, regress_dep_user2; REASSIGN OWNED BY regress_dep_user0 TO regress_dep_user1; REASSIGN OWNED BY regress_dep_user1 TO regress_dep_user0; -- this one is allowed DROP OWNED BY regress_dep_user0; CREATE TABLE deptest1 (f1 int unique); GRANT ALL ON deptest1 TO regress_dep_user1 WITH GRANT OPTION; SET SESSION AUTHORIZATION regress_dep_user1; CREATE TABLE deptest (a serial primary key, b text); GRANT ALL ON deptest1 TO regress_dep_user2; RESET SESSION AUTHORIZATION; \z deptest1 DROP OWNED BY regress_dep_user1; -- all grants revoked \z deptest1 -- table was dropped \d deptest -- Test REASSIGN OWNED GRANT ALL ON deptest1 TO regress_dep_user1; GRANT CREATE ON DATABASE regression TO regress_dep_user1; SET SESSION AUTHORIZATION regress_dep_user1; CREATE SCHEMA deptest; CREATE TABLE deptest (a serial primary key, b text); ALTER DEFAULT PRIVILEGES FOR ROLE regress_dep_user1 IN SCHEMA deptest GRANT ALL ON TABLES TO regress_dep_user2; CREATE FUNCTION deptest_func() RETURNS void LANGUAGE plpgsql AS $$ BEGIN END; $$; CREATE TYPE deptest_enum AS ENUM ('red'); CREATE TYPE deptest_range AS RANGE (SUBTYPE = int4); CREATE TABLE deptest2 (f1 int); -- make a serial column the hard way CREATE SEQUENCE ss1; ALTER TABLE deptest2 ALTER f1 SET DEFAULT nextval('ss1'); ALTER SEQUENCE ss1 OWNED BY deptest2.f1; -- When reassigning ownership of a composite type, its pg_class entry -- should match CREATE TYPE deptest_t AS (a int); SELECT typowner = relowner FROM pg_type JOIN pg_class c ON typrelid = c.oid WHERE typname = 'deptest_t'; RESET SESSION AUTHORIZATION; REASSIGN OWNED BY regress_dep_user1 TO regress_dep_user2; \dt deptest SELECT typowner = relowner FROM pg_type JOIN pg_class c ON typrelid = c.oid WHERE typname = 'deptest_t'; -- doesn't work: grant still exists DROP USER regress_dep_user1; DROP OWNED BY regress_dep_user1; DROP USER regress_dep_user1; DROP USER regress_dep_user2; DROP OWNED BY regress_dep_user2, regress_dep_user0; DROP USER regress_dep_user2; DROP USER regress_dep_user0; pgFormatter-4.2/t/pg-test-files/sql/domain.sql000066400000000000000000000524521361326045100213770ustar00rootroot00000000000000-- -- Test domains. -- -- Test Comment / Drop create domain domaindroptest int4; comment on domain domaindroptest is 'About to drop this..'; create domain dependenttypetest domaindroptest; -- fail because of dependent type drop domain domaindroptest; drop domain domaindroptest cascade; -- this should fail because already gone drop domain domaindroptest cascade; -- Test domain input. -- Note: the point of checking both INSERT and COPY FROM is that INSERT -- exercises CoerceToDomain while COPY exercises domain_in. create domain domainvarchar varchar(5); create domain domainnumeric numeric(8,2); create domain domainint4 int4; create domain domaintext text; -- Test explicit coercions --- these should succeed (and truncate) SELECT cast('123456' as domainvarchar); SELECT cast('12345' as domainvarchar); -- Test tables using domains create table basictest ( testint4 domainint4 , testtext domaintext , testvarchar domainvarchar , testnumeric domainnumeric ); INSERT INTO basictest values ('88', 'haha', 'short', '123.12'); -- Good INSERT INTO basictest values ('88', 'haha', 'short text', '123.12'); -- Bad varchar INSERT INTO basictest values ('88', 'haha', 'short', '123.1212'); -- Truncate numeric select * from basictest; -- check that domains inherit operations from base types select testtext || testvarchar as concat, testnumeric + 42 as sum from basictest; -- check that union/case/coalesce type resolution handles domains properly select coalesce(4::domainint4, 7) is of (int4) as t; select coalesce(4::domainint4, 7) is of (domainint4) as f; select coalesce(4::domainint4, 7::domainint4) is of (domainint4) as t; drop table basictest; drop domain domainvarchar restrict; drop domain domainnumeric restrict; drop domain domainint4 restrict; drop domain domaintext; -- Test domains over array types create domain domainint4arr int4[1]; create domain domainchar4arr varchar(4)[2][3]; create table domarrtest ( testint4arr domainint4arr , testchar4arr domainchar4arr ); INSERT INTO domarrtest values ('{2,2}', '{{"a","b"},{"c","d"}}'); INSERT INTO domarrtest values ('{{2,2},{2,2}}', '{{"a","b"}}'); INSERT INTO domarrtest values ('{2,2}', '{{"a","b"},{"c","d"},{"e","f"}}'); INSERT INTO domarrtest values ('{2,2}', '{{"a"},{"c"}}'); INSERT INTO domarrtest values (NULL, '{{"a","b","c"},{"d","e","f"}}'); INSERT INTO domarrtest values (NULL, '{{"toolong","b","c"},{"d","e","f"}}'); INSERT INTO domarrtest (testint4arr[1], testint4arr[3]) values (11,22); select * from domarrtest; select testint4arr[1], testchar4arr[2:2] from domarrtest; select array_dims(testint4arr), array_dims(testchar4arr) from domarrtest; select * from domarrtest; update domarrtest set testint4arr[1] = testint4arr[1] + 1, testint4arr[3] = testint4arr[3] - 1 where testchar4arr is null; select * from domarrtest where testchar4arr is null; drop table domarrtest; drop domain domainint4arr restrict; drop domain domainchar4arr restrict; create domain dia as int[]; select '{1,2,3}'::dia; select array_dims('{1,2,3}'::dia); select pg_typeof('{1,2,3}'::dia); select pg_typeof('{1,2,3}'::dia || 42); -- should be int[] not dia drop domain dia; -- Test domains over composites create type comptype as (r float8, i float8); create domain dcomptype as comptype; create table dcomptable (d1 dcomptype unique); insert into dcomptable values (row(1,2)::dcomptype); insert into dcomptable values (row(3,4)::comptype); insert into dcomptable values (row(1,2)::dcomptype); -- fail on uniqueness insert into dcomptable (d1.r) values(11); select * from dcomptable; select (d1).r, (d1).i, (d1).* from dcomptable; update dcomptable set d1.r = (d1).r + 1 where (d1).i > 0; select * from dcomptable; alter domain dcomptype add constraint c1 check ((value).r <= (value).i); alter domain dcomptype add constraint c2 check ((value).r > (value).i); -- fail select row(2,1)::dcomptype; -- fail insert into dcomptable values (row(1,2)::comptype); insert into dcomptable values (row(2,1)::comptype); -- fail insert into dcomptable (d1.r) values(99); insert into dcomptable (d1.r, d1.i) values(99, 100); insert into dcomptable (d1.r, d1.i) values(100, 99); -- fail update dcomptable set d1.r = (d1).r + 1 where (d1).i > 0; -- fail update dcomptable set d1.r = (d1).r - 1, d1.i = (d1).i + 1 where (d1).i > 0; select * from dcomptable; explain (verbose, costs off) update dcomptable set d1.r = (d1).r - 1, d1.i = (d1).i + 1 where (d1).i > 0; create rule silly as on delete to dcomptable do instead update dcomptable set d1.r = (d1).r - 1, d1.i = (d1).i + 1 where (d1).i > 0; \d+ dcomptable drop table dcomptable; drop type comptype cascade; -- check altering and dropping columns used by domain constraints create type comptype as (r float8, i float8); create domain dcomptype as comptype; alter domain dcomptype add constraint c1 check ((value).r > 0); comment on constraint c1 on domain dcomptype is 'random commentary'; select row(0,1)::dcomptype; -- fail alter type comptype alter attribute r type varchar; -- fail alter type comptype alter attribute r type bigint; alter type comptype drop attribute r; -- fail alter type comptype drop attribute i; select conname, obj_description(oid, 'pg_constraint') from pg_constraint where contypid = 'dcomptype'::regtype; -- check comment is still there drop type comptype cascade; -- Test domains over arrays of composite create type comptype as (r float8, i float8); create domain dcomptypea as comptype[]; create table dcomptable (d1 dcomptypea unique); insert into dcomptable values (array[row(1,2)]::dcomptypea); insert into dcomptable values (array[row(3,4), row(5,6)]::comptype[]); insert into dcomptable values (array[row(7,8)::comptype, row(9,10)::comptype]); insert into dcomptable values (array[row(1,2)]::dcomptypea); -- fail on uniqueness insert into dcomptable (d1[1]) values(row(9,10)); insert into dcomptable (d1[1].r) values(11); select * from dcomptable; select d1[2], d1[1].r, d1[1].i from dcomptable; update dcomptable set d1[2] = row(d1[2].i, d1[2].r); select * from dcomptable; update dcomptable set d1[1].r = d1[1].r + 1 where d1[1].i > 0; select * from dcomptable; alter domain dcomptypea add constraint c1 check (value[1].r <= value[1].i); alter domain dcomptypea add constraint c2 check (value[1].r > value[1].i); -- fail select array[row(2,1)]::dcomptypea; -- fail insert into dcomptable values (array[row(1,2)]::comptype[]); insert into dcomptable values (array[row(2,1)]::comptype[]); -- fail insert into dcomptable (d1[1].r) values(99); insert into dcomptable (d1[1].r, d1[1].i) values(99, 100); insert into dcomptable (d1[1].r, d1[1].i) values(100, 99); -- fail update dcomptable set d1[1].r = d1[1].r + 1 where d1[1].i > 0; -- fail update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1 where d1[1].i > 0; select * from dcomptable; explain (verbose, costs off) update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1 where d1[1].i > 0; create rule silly as on delete to dcomptable do instead update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1 where d1[1].i > 0; \d+ dcomptable drop table dcomptable; drop type comptype cascade; -- Test arrays over domains create domain posint as int check (value > 0); create table pitable (f1 posint[]); insert into pitable values(array[42]); insert into pitable values(array[-1]); -- fail insert into pitable values('{0}'); -- fail update pitable set f1[1] = f1[1] + 1; update pitable set f1[1] = 0; -- fail select * from pitable; drop table pitable; create domain vc4 as varchar(4); create table vc4table (f1 vc4[]); insert into vc4table values(array['too long']); -- fail insert into vc4table values(array['too long']::vc4[]); -- cast truncates select * from vc4table; drop table vc4table; drop type vc4; -- You can sort of fake arrays-of-arrays by putting a domain in between create domain dposinta as posint[]; create table dposintatable (f1 dposinta[]); insert into dposintatable values(array[array[42]]); -- fail insert into dposintatable values(array[array[42]::posint[]]); -- still fail insert into dposintatable values(array[array[42]::dposinta]); -- but this works select f1, f1[1], (f1[1])[1] from dposintatable; select pg_typeof(f1) from dposintatable; select pg_typeof(f1[1]) from dposintatable; select pg_typeof(f1[1][1]) from dposintatable; select pg_typeof((f1[1])[1]) from dposintatable; update dposintatable set f1[2] = array[99]; select f1, f1[1], (f1[2])[1] from dposintatable; -- it'd be nice if you could do something like this, but for now you can't: update dposintatable set f1[2][1] = array[97]; -- maybe someday we can make this syntax work: update dposintatable set (f1[2])[1] = array[98]; drop table dposintatable; drop domain posint cascade; -- Test not-null restrictions create domain dnotnull varchar(15) NOT NULL; create domain dnull varchar(15); create domain dcheck varchar(15) NOT NULL CHECK (VALUE = 'a' OR VALUE = 'c' OR VALUE = 'd'); create table nulltest ( col1 dnotnull , col2 dnotnull NULL -- NOT NULL in the domain cannot be overridden , col3 dnull NOT NULL , col4 dnull , col5 dcheck CHECK (col5 IN ('c', 'd')) ); INSERT INTO nulltest DEFAULT VALUES; INSERT INTO nulltest values ('a', 'b', 'c', 'd', 'c'); -- Good insert into nulltest values ('a', 'b', 'c', 'd', NULL); insert into nulltest values ('a', 'b', 'c', 'd', 'a'); INSERT INTO nulltest values (NULL, 'b', 'c', 'd', 'd'); INSERT INTO nulltest values ('a', NULL, 'c', 'd', 'c'); INSERT INTO nulltest values ('a', 'b', NULL, 'd', 'c'); INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good select * from nulltest; -- Test out coerced (casted) constraints SELECT cast('1' as dnotnull); SELECT cast(NULL as dnotnull); -- fail SELECT cast(cast(NULL as dnull) as dnotnull); -- fail SELECT cast(col4 as dnotnull) from nulltest; -- fail -- cleanup drop table nulltest; drop domain dnotnull restrict; drop domain dnull restrict; drop domain dcheck restrict; create domain ddef1 int4 DEFAULT 3; create domain ddef2 oid DEFAULT '12'; -- Type mixing, function returns int8 create domain ddef3 text DEFAULT 5; create sequence ddef4_seq; create domain ddef4 int4 DEFAULT nextval('ddef4_seq'); create domain ddef5 numeric(8,2) NOT NULL DEFAULT '12.12'; create table defaulttest ( col1 ddef1 , col2 ddef2 , col3 ddef3 , col4 ddef4 PRIMARY KEY , col5 ddef1 NOT NULL DEFAULT NULL , col6 ddef2 DEFAULT '88' , col7 ddef4 DEFAULT 8000 , col8 ddef5 ); insert into defaulttest(col4) values(0); -- fails, col5 defaults to null alter table defaulttest alter column col5 drop default; insert into defaulttest default values; -- succeeds, inserts domain default -- We used to treat SET DEFAULT NULL as equivalent to DROP DEFAULT; wrong alter table defaulttest alter column col5 set default null; insert into defaulttest(col4) values(0); -- fails alter table defaulttest alter column col5 drop default; insert into defaulttest default values; insert into defaulttest default values; select * from defaulttest; drop table defaulttest cascade; -- Test ALTER DOMAIN .. NOT NULL create domain dnotnulltest integer; create table domnotnull ( col1 dnotnulltest , col2 dnotnulltest ); insert into domnotnull default values; alter domain dnotnulltest set not null; -- fails update domnotnull set col1 = 5; alter domain dnotnulltest set not null; -- fails update domnotnull set col2 = 6; alter domain dnotnulltest set not null; update domnotnull set col1 = null; -- fails alter domain dnotnulltest drop not null; update domnotnull set col1 = null; drop domain dnotnulltest cascade; -- Test ALTER DOMAIN .. DEFAULT .. create table domdeftest (col1 ddef1); insert into domdeftest default values; select * from domdeftest; alter domain ddef1 set default '42'; insert into domdeftest default values; select * from domdeftest; alter domain ddef1 drop default; insert into domdeftest default values; select * from domdeftest; drop table domdeftest; -- Test ALTER DOMAIN .. CONSTRAINT .. create domain con as integer; create table domcontest (col1 con); insert into domcontest values (1); insert into domcontest values (2); alter domain con add constraint t check (VALUE < 1); -- fails alter domain con add constraint t check (VALUE < 34); alter domain con add check (VALUE > 0); insert into domcontest values (-5); -- fails insert into domcontest values (42); -- fails insert into domcontest values (5); alter domain con drop constraint t; insert into domcontest values (-5); --fails insert into domcontest values (42); alter domain con drop constraint nonexistent; alter domain con drop constraint if exists nonexistent; -- Test ALTER DOMAIN .. CONSTRAINT .. NOT VALID create domain things AS INT; CREATE TABLE thethings (stuff things); INSERT INTO thethings (stuff) VALUES (55); ALTER DOMAIN things ADD CONSTRAINT meow CHECK (VALUE < 11); ALTER DOMAIN things ADD CONSTRAINT meow CHECK (VALUE < 11) NOT VALID; ALTER DOMAIN things VALIDATE CONSTRAINT meow; UPDATE thethings SET stuff = 10; ALTER DOMAIN things VALIDATE CONSTRAINT meow; -- Confirm ALTER DOMAIN with RULES. create table domtab (col1 integer); create domain dom as integer; create view domview as select cast(col1 as dom) from domtab; insert into domtab (col1) values (null); insert into domtab (col1) values (5); select * from domview; alter domain dom set not null; select * from domview; -- fail alter domain dom drop not null; select * from domview; alter domain dom add constraint domchkgt6 check(value > 6); select * from domview; --fail alter domain dom drop constraint domchkgt6 restrict; select * from domview; -- cleanup drop domain ddef1 restrict; drop domain ddef2 restrict; drop domain ddef3 restrict; drop domain ddef4 restrict; drop domain ddef5 restrict; drop sequence ddef4_seq; -- Test domains over domains create domain vchar4 varchar(4); create domain dinter vchar4 check (substring(VALUE, 1, 1) = 'x'); create domain dtop dinter check (substring(VALUE, 2, 1) = '1'); select 'x123'::dtop; select 'x1234'::dtop; -- explicit coercion should truncate select 'y1234'::dtop; -- fail select 'y123'::dtop; -- fail select 'yz23'::dtop; -- fail select 'xz23'::dtop; -- fail create temp table dtest(f1 dtop); insert into dtest values('x123'); insert into dtest values('x1234'); -- fail, implicit coercion insert into dtest values('y1234'); -- fail, implicit coercion insert into dtest values('y123'); -- fail insert into dtest values('yz23'); -- fail insert into dtest values('xz23'); -- fail drop table dtest; drop domain vchar4 cascade; -- Make sure that constraints of newly-added domain columns are -- enforced correctly, even if there's no default value for the new -- column. Per bug #1433 create domain str_domain as text not null; create table domain_test (a int, b int); insert into domain_test values (1, 2); insert into domain_test values (1, 2); -- should fail alter table domain_test add column c str_domain; create domain str_domain2 as text check (value <> 'foo') default 'foo'; -- should fail alter table domain_test add column d str_domain2; -- Check that domain constraints on prepared statement parameters of -- unknown type are enforced correctly. create domain pos_int as int4 check (value > 0) not null; prepare s1 as select $1::pos_int = 10 as "is_ten"; execute s1(10); execute s1(0); -- should fail execute s1(NULL); -- should fail -- Check that domain constraints on plpgsql function parameters, results, -- and local variables are enforced correctly. create function doubledecrement(p1 pos_int) returns pos_int as $$ declare v pos_int; begin return p1; end$$ language plpgsql; select doubledecrement(3); -- fail because of implicit null assignment create or replace function doubledecrement(p1 pos_int) returns pos_int as $$ declare v pos_int := 0; begin return p1; end$$ language plpgsql; select doubledecrement(3); -- fail at initialization assignment create or replace function doubledecrement(p1 pos_int) returns pos_int as $$ declare v pos_int := 1; begin v := p1 - 1; return v - 1; end$$ language plpgsql; select doubledecrement(null); -- fail before call select doubledecrement(0); -- fail before call select doubledecrement(1); -- fail at assignment to v select doubledecrement(2); -- fail at return select doubledecrement(3); -- good -- Check that ALTER DOMAIN tests columns of derived types create domain posint as int4; -- Currently, this doesn't work for composite types, but verify it complains create type ddtest1 as (f1 posint); create table ddtest2(f1 ddtest1); insert into ddtest2 values(row(-1)); alter domain posint add constraint c1 check(value >= 0); drop table ddtest2; -- Likewise for domains within arrays of composite create table ddtest2(f1 ddtest1[]); insert into ddtest2 values('{(-1)}'); alter domain posint add constraint c1 check(value >= 0); drop table ddtest2; -- Likewise for domains within domains over composite create domain ddtest1d as ddtest1; create table ddtest2(f1 ddtest1d); insert into ddtest2 values('(-1)'); alter domain posint add constraint c1 check(value >= 0); drop table ddtest2; drop domain ddtest1d; -- Likewise for domains within domains over array of composite create domain ddtest1d as ddtest1[]; create table ddtest2(f1 ddtest1d); insert into ddtest2 values('{(-1)}'); alter domain posint add constraint c1 check(value >= 0); drop table ddtest2; drop domain ddtest1d; -- Doesn't work for ranges, either create type rposint as range (subtype = posint); create table ddtest2(f1 rposint); insert into ddtest2 values('(-1,3]'); alter domain posint add constraint c1 check(value >= 0); drop table ddtest2; drop type rposint; alter domain posint add constraint c1 check(value >= 0); create domain posint2 as posint check (value % 2 = 0); create table ddtest2(f1 posint2); insert into ddtest2 values(11); -- fail insert into ddtest2 values(-2); -- fail insert into ddtest2 values(2); alter domain posint add constraint c2 check(value >= 10); -- fail alter domain posint add constraint c2 check(value > 0); -- OK drop table ddtest2; drop type ddtest1; drop domain posint cascade; -- -- Check enforcement of domain-related typmod in plpgsql (bug #5717) -- create or replace function array_elem_check(numeric) returns numeric as $$ declare x numeric(4,2)[1]; begin x[1] := $1; return x[1]; end$$ language plpgsql; select array_elem_check(121.00); select array_elem_check(1.23456); create domain mynums as numeric(4,2)[1]; create or replace function array_elem_check(numeric) returns numeric as $$ declare x mynums; begin x[1] := $1; return x[1]; end$$ language plpgsql; select array_elem_check(121.00); select array_elem_check(1.23456); create domain mynums2 as mynums; create or replace function array_elem_check(numeric) returns numeric as $$ declare x mynums2; begin x[1] := $1; return x[1]; end$$ language plpgsql; select array_elem_check(121.00); select array_elem_check(1.23456); drop function array_elem_check(numeric); -- -- Check enforcement of array-level domain constraints -- create domain orderedpair as int[2] check (value[1] < value[2]); select array[1,2]::orderedpair; select array[2,1]::orderedpair; -- fail create temp table op (f1 orderedpair); insert into op values (array[1,2]); insert into op values (array[2,1]); -- fail update op set f1[2] = 3; update op set f1[2] = 0; -- fail select * from op; create or replace function array_elem_check(int) returns int as $$ declare x orderedpair := '{1,2}'; begin x[2] := $1; return x[2]; end$$ language plpgsql; select array_elem_check(3); select array_elem_check(-1); drop function array_elem_check(int); -- -- Check enforcement of changing constraints in plpgsql -- create domain di as int; create function dom_check(int) returns di as $$ declare d di; begin d := $1::di; return d; end $$ language plpgsql immutable; select dom_check(0); alter domain di add constraint pos check (value > 0); select dom_check(0); -- fail alter domain di drop constraint pos; select dom_check(0); -- implicit cast during assignment is a separate code path, test that too create or replace function dom_check(int) returns di as $$ declare d di; begin d := $1; return d; end $$ language plpgsql immutable; select dom_check(0); alter domain di add constraint pos check (value > 0); select dom_check(0); -- fail alter domain di drop constraint pos; select dom_check(0); drop function dom_check(int); drop domain di; -- -- Check use of a (non-inline-able) SQL function in a domain constraint; -- this has caused issues in the past -- create function sql_is_distinct_from(anyelement, anyelement) returns boolean language sql as 'select $1 is distinct from $2 limit 1'; create domain inotnull int check (sql_is_distinct_from(value, null)); select 1::inotnull; select null::inotnull; create table dom_table (x inotnull); insert into dom_table values ('1'); insert into dom_table values (1); insert into dom_table values (null); drop table dom_table; drop domain inotnull; drop function sql_is_distinct_from(anyelement, anyelement); -- -- Renaming -- create domain testdomain1 as int; alter domain testdomain1 rename to testdomain2; alter type testdomain2 rename to testdomain3; -- alter type also works drop domain testdomain3; -- -- Renaming domain constraints -- create domain testdomain1 as int constraint unsigned check (value > 0); alter domain testdomain1 rename constraint unsigned to unsigned_foo; alter domain testdomain1 drop constraint unsigned_foo; drop domain testdomain1; pgFormatter-4.2/t/pg-test-files/sql/drop_if_exists.sql000066400000000000000000000206521361326045100231460ustar00rootroot00000000000000-- -- IF EXISTS tests -- -- table (will be really dropped at the end) DROP TABLE test_exists; DROP TABLE IF EXISTS test_exists; CREATE TABLE test_exists (a int, b text); -- view DROP VIEW test_view_exists; DROP VIEW IF EXISTS test_view_exists; CREATE VIEW test_view_exists AS select * from test_exists; DROP VIEW IF EXISTS test_view_exists; DROP VIEW test_view_exists; -- index DROP INDEX test_index_exists; DROP INDEX IF EXISTS test_index_exists; CREATE INDEX test_index_exists on test_exists(a); DROP INDEX IF EXISTS test_index_exists; DROP INDEX test_index_exists; -- sequence DROP SEQUENCE test_sequence_exists; DROP SEQUENCE IF EXISTS test_sequence_exists; CREATE SEQUENCE test_sequence_exists; DROP SEQUENCE IF EXISTS test_sequence_exists; DROP SEQUENCE test_sequence_exists; -- schema DROP SCHEMA test_schema_exists; DROP SCHEMA IF EXISTS test_schema_exists; CREATE SCHEMA test_schema_exists; DROP SCHEMA IF EXISTS test_schema_exists; DROP SCHEMA test_schema_exists; -- type DROP TYPE test_type_exists; DROP TYPE IF EXISTS test_type_exists; CREATE type test_type_exists as (a int, b text); DROP TYPE IF EXISTS test_type_exists; DROP TYPE test_type_exists; -- domain DROP DOMAIN test_domain_exists; DROP DOMAIN IF EXISTS test_domain_exists; CREATE domain test_domain_exists as int not null check (value > 0); DROP DOMAIN IF EXISTS test_domain_exists; DROP DOMAIN test_domain_exists; --- --- role/user/group --- CREATE USER regress_test_u1; CREATE ROLE regress_test_r1; CREATE GROUP regress_test_g1; DROP USER regress_test_u2; DROP USER IF EXISTS regress_test_u1, regress_test_u2; DROP USER regress_test_u1; DROP ROLE regress_test_r2; DROP ROLE IF EXISTS regress_test_r1, regress_test_r2; DROP ROLE regress_test_r1; DROP GROUP regress_test_g2; DROP GROUP IF EXISTS regress_test_g1, regress_test_g2; DROP GROUP regress_test_g1; -- collation DROP COLLATION IF EXISTS test_collation_exists; -- conversion DROP CONVERSION test_conversion_exists; DROP CONVERSION IF EXISTS test_conversion_exists; CREATE CONVERSION test_conversion_exists FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; DROP CONVERSION test_conversion_exists; -- text search parser DROP TEXT SEARCH PARSER test_tsparser_exists; DROP TEXT SEARCH PARSER IF EXISTS test_tsparser_exists; -- text search dictionary DROP TEXT SEARCH DICTIONARY test_tsdict_exists; DROP TEXT SEARCH DICTIONARY IF EXISTS test_tsdict_exists; CREATE TEXT SEARCH DICTIONARY test_tsdict_exists ( Template=ispell, DictFile=ispell_sample, AffFile=ispell_sample ); DROP TEXT SEARCH DICTIONARY test_tsdict_exists; -- test search template DROP TEXT SEARCH TEMPLATE test_tstemplate_exists; DROP TEXT SEARCH TEMPLATE IF EXISTS test_tstemplate_exists; -- text search configuration DROP TEXT SEARCH CONFIGURATION test_tsconfig_exists; DROP TEXT SEARCH CONFIGURATION IF EXISTS test_tsconfig_exists; CREATE TEXT SEARCH CONFIGURATION test_tsconfig_exists (COPY=english); DROP TEXT SEARCH CONFIGURATION test_tsconfig_exists; -- extension DROP EXTENSION test_extension_exists; DROP EXTENSION IF EXISTS test_extension_exists; -- functions DROP FUNCTION test_function_exists(); DROP FUNCTION IF EXISTS test_function_exists(); DROP FUNCTION test_function_exists(int, text, int[]); DROP FUNCTION IF EXISTS test_function_exists(int, text, int[]); -- aggregate DROP AGGREGATE test_aggregate_exists(*); DROP AGGREGATE IF EXISTS test_aggregate_exists(*); DROP AGGREGATE test_aggregate_exists(int); DROP AGGREGATE IF EXISTS test_aggregate_exists(int); -- operator DROP OPERATOR @#@ (int, int); DROP OPERATOR IF EXISTS @#@ (int, int); CREATE OPERATOR @#@ (leftarg = int8, rightarg = int8, procedure = int8xor); DROP OPERATOR @#@ (int8, int8); -- language DROP LANGUAGE test_language_exists; DROP LANGUAGE IF EXISTS test_language_exists; -- cast DROP CAST (text AS text); DROP CAST IF EXISTS (text AS text); -- trigger DROP TRIGGER test_trigger_exists ON test_exists; DROP TRIGGER IF EXISTS test_trigger_exists ON test_exists; DROP TRIGGER test_trigger_exists ON no_such_table; DROP TRIGGER IF EXISTS test_trigger_exists ON no_such_table; DROP TRIGGER test_trigger_exists ON no_such_schema.no_such_table; DROP TRIGGER IF EXISTS test_trigger_exists ON no_such_schema.no_such_table; CREATE TRIGGER test_trigger_exists BEFORE UPDATE ON test_exists FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger(); DROP TRIGGER test_trigger_exists ON test_exists; -- rule DROP RULE test_rule_exists ON test_exists; DROP RULE IF EXISTS test_rule_exists ON test_exists; DROP RULE test_rule_exists ON no_such_table; DROP RULE IF EXISTS test_rule_exists ON no_such_table; DROP RULE test_rule_exists ON no_such_schema.no_such_table; DROP RULE IF EXISTS test_rule_exists ON no_such_schema.no_such_table; CREATE RULE test_rule_exists AS ON INSERT TO test_exists DO INSTEAD INSERT INTO test_exists VALUES (NEW.a, NEW.b || NEW.a::text); DROP RULE test_rule_exists ON test_exists; -- foreign data wrapper DROP FOREIGN DATA WRAPPER test_fdw_exists; DROP FOREIGN DATA WRAPPER IF EXISTS test_fdw_exists; -- foreign server DROP SERVER test_server_exists; DROP SERVER IF EXISTS test_server_exists; -- operator class DROP OPERATOR CLASS test_operator_class USING btree; DROP OPERATOR CLASS IF EXISTS test_operator_class USING btree; DROP OPERATOR CLASS test_operator_class USING no_such_am; DROP OPERATOR CLASS IF EXISTS test_operator_class USING no_such_am; -- operator family DROP OPERATOR FAMILY test_operator_family USING btree; DROP OPERATOR FAMILY IF EXISTS test_operator_family USING btree; DROP OPERATOR FAMILY test_operator_family USING no_such_am; DROP OPERATOR FAMILY IF EXISTS test_operator_family USING no_such_am; -- access method DROP ACCESS METHOD no_such_am; DROP ACCESS METHOD IF EXISTS no_such_am; -- drop the table DROP TABLE IF EXISTS test_exists; DROP TABLE test_exists; -- be tolerant with missing schemas, types, etc DROP AGGREGATE IF EXISTS no_such_schema.foo(int); DROP AGGREGATE IF EXISTS foo(no_such_type); DROP AGGREGATE IF EXISTS foo(no_such_schema.no_such_type); DROP CAST IF EXISTS (INTEGER AS no_such_type2); DROP CAST IF EXISTS (no_such_type1 AS INTEGER); DROP CAST IF EXISTS (INTEGER AS no_such_schema.bar); DROP CAST IF EXISTS (no_such_schema.foo AS INTEGER); DROP COLLATION IF EXISTS no_such_schema.foo; DROP CONVERSION IF EXISTS no_such_schema.foo; DROP DOMAIN IF EXISTS no_such_schema.foo; DROP FOREIGN TABLE IF EXISTS no_such_schema.foo; DROP FUNCTION IF EXISTS no_such_schema.foo(); DROP FUNCTION IF EXISTS foo(no_such_type); DROP FUNCTION IF EXISTS foo(no_such_schema.no_such_type); DROP INDEX IF EXISTS no_such_schema.foo; DROP MATERIALIZED VIEW IF EXISTS no_such_schema.foo; DROP OPERATOR IF EXISTS no_such_schema.+ (int, int); DROP OPERATOR IF EXISTS + (no_such_type, no_such_type); DROP OPERATOR IF EXISTS + (no_such_schema.no_such_type, no_such_schema.no_such_type); DROP OPERATOR IF EXISTS # (NONE, no_such_schema.no_such_type); DROP OPERATOR CLASS IF EXISTS no_such_schema.widget_ops USING btree; DROP OPERATOR FAMILY IF EXISTS no_such_schema.float_ops USING btree; DROP RULE IF EXISTS foo ON no_such_schema.bar; DROP SEQUENCE IF EXISTS no_such_schema.foo; DROP TABLE IF EXISTS no_such_schema.foo; DROP TEXT SEARCH CONFIGURATION IF EXISTS no_such_schema.foo; DROP TEXT SEARCH DICTIONARY IF EXISTS no_such_schema.foo; DROP TEXT SEARCH PARSER IF EXISTS no_such_schema.foo; DROP TEXT SEARCH TEMPLATE IF EXISTS no_such_schema.foo; DROP TRIGGER IF EXISTS foo ON no_such_schema.bar; DROP TYPE IF EXISTS no_such_schema.foo; DROP VIEW IF EXISTS no_such_schema.foo; -- Check we receive an ambiguous function error when there are -- multiple matching functions. CREATE FUNCTION test_ambiguous_funcname(int) returns int as $$ select $1; $$ language sql; CREATE FUNCTION test_ambiguous_funcname(text) returns text as $$ select $1; $$ language sql; DROP FUNCTION test_ambiguous_funcname; DROP FUNCTION IF EXISTS test_ambiguous_funcname; -- cleanup DROP FUNCTION test_ambiguous_funcname(int); DROP FUNCTION test_ambiguous_funcname(text); -- Likewise for procedures. CREATE PROCEDURE test_ambiguous_procname(int) as $$ begin end; $$ language plpgsql; CREATE PROCEDURE test_ambiguous_procname(text) as $$ begin end; $$ language plpgsql; DROP PROCEDURE test_ambiguous_procname; DROP PROCEDURE IF EXISTS test_ambiguous_procname; -- Check we get a similar error if we use ROUTINE instead of PROCEDURE. DROP ROUTINE IF EXISTS test_ambiguous_procname; -- cleanup DROP PROCEDURE test_ambiguous_procname(int); DROP PROCEDURE test_ambiguous_procname(text); pgFormatter-4.2/t/pg-test-files/sql/drop_operator.sql000066400000000000000000000025271361326045100230050ustar00rootroot00000000000000CREATE OPERATOR === ( PROCEDURE = int8eq, LEFTARG = bigint, RIGHTARG = bigint, COMMUTATOR = === ); CREATE OPERATOR !== ( PROCEDURE = int8ne, LEFTARG = bigint, RIGHTARG = bigint, NEGATOR = ===, COMMUTATOR = !== ); DROP OPERATOR !==(bigint, bigint); SELECT ctid, oprcom FROM pg_catalog.pg_operator fk WHERE oprcom != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.oprcom); SELECT ctid, oprnegate FROM pg_catalog.pg_operator fk WHERE oprnegate != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.oprnegate); DROP OPERATOR ===(bigint, bigint); CREATE OPERATOR <| ( PROCEDURE = int8lt, LEFTARG = bigint, RIGHTARG = bigint ); CREATE OPERATOR |> ( PROCEDURE = int8gt, LEFTARG = bigint, RIGHTARG = bigint, NEGATOR = <|, COMMUTATOR = <| ); DROP OPERATOR |>(bigint, bigint); SELECT ctid, oprcom FROM pg_catalog.pg_operator fk WHERE oprcom != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.oprcom); SELECT ctid, oprnegate FROM pg_catalog.pg_operator fk WHERE oprnegate != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.oprnegate); DROP OPERATOR <|(bigint, bigint); pgFormatter-4.2/t/pg-test-files/sql/enum.sql000066400000000000000000000224011361326045100210630ustar00rootroot00000000000000-- -- Enum tests -- CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple'); -- -- Did it create the right number of rows? -- SELECT COUNT(*) FROM pg_enum WHERE enumtypid = 'rainbow'::regtype; -- -- I/O functions -- SELECT 'red'::rainbow; SELECT 'mauve'::rainbow; -- -- adding new values -- CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' ); SELECT enumlabel, enumsortorder FROM pg_enum WHERE enumtypid = 'planets'::regtype ORDER BY 2; ALTER TYPE planets ADD VALUE 'uranus'; SELECT enumlabel, enumsortorder FROM pg_enum WHERE enumtypid = 'planets'::regtype ORDER BY 2; ALTER TYPE planets ADD VALUE 'mercury' BEFORE 'venus'; ALTER TYPE planets ADD VALUE 'saturn' BEFORE 'uranus'; ALTER TYPE planets ADD VALUE 'jupiter' AFTER 'mars'; ALTER TYPE planets ADD VALUE 'neptune' AFTER 'uranus'; SELECT enumlabel, enumsortorder FROM pg_enum WHERE enumtypid = 'planets'::regtype ORDER BY 2; SELECT enumlabel, enumsortorder FROM pg_enum WHERE enumtypid = 'planets'::regtype ORDER BY enumlabel::planets; -- errors for adding labels ALTER TYPE planets ADD VALUE 'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto'; ALTER TYPE planets ADD VALUE 'pluto' AFTER 'zeus'; -- if not exists tests -- existing value gives error ALTER TYPE planets ADD VALUE 'mercury'; -- unless IF NOT EXISTS is specified ALTER TYPE planets ADD VALUE IF NOT EXISTS 'mercury'; -- should be neptune, not mercury SELECT enum_last(NULL::planets); ALTER TYPE planets ADD VALUE IF NOT EXISTS 'pluto'; -- should be pluto, i.e. the new value SELECT enum_last(NULL::planets); -- -- Test inserting so many values that we have to renumber -- create type insenum as enum ('L1', 'L2'); alter type insenum add value 'i1' before 'L2'; alter type insenum add value 'i2' before 'L2'; alter type insenum add value 'i3' before 'L2'; alter type insenum add value 'i4' before 'L2'; alter type insenum add value 'i5' before 'L2'; alter type insenum add value 'i6' before 'L2'; alter type insenum add value 'i7' before 'L2'; alter type insenum add value 'i8' before 'L2'; alter type insenum add value 'i9' before 'L2'; alter type insenum add value 'i10' before 'L2'; alter type insenum add value 'i11' before 'L2'; alter type insenum add value 'i12' before 'L2'; alter type insenum add value 'i13' before 'L2'; alter type insenum add value 'i14' before 'L2'; alter type insenum add value 'i15' before 'L2'; alter type insenum add value 'i16' before 'L2'; alter type insenum add value 'i17' before 'L2'; alter type insenum add value 'i18' before 'L2'; alter type insenum add value 'i19' before 'L2'; alter type insenum add value 'i20' before 'L2'; alter type insenum add value 'i21' before 'L2'; alter type insenum add value 'i22' before 'L2'; alter type insenum add value 'i23' before 'L2'; alter type insenum add value 'i24' before 'L2'; alter type insenum add value 'i25' before 'L2'; alter type insenum add value 'i26' before 'L2'; alter type insenum add value 'i27' before 'L2'; alter type insenum add value 'i28' before 'L2'; alter type insenum add value 'i29' before 'L2'; alter type insenum add value 'i30' before 'L2'; -- The exact values of enumsortorder will now depend on the local properties -- of float4, but in any reasonable implementation we should get at least -- 20 splits before having to renumber; so only hide values > 20. SELECT enumlabel, case when enumsortorder > 20 then null else enumsortorder end as so FROM pg_enum WHERE enumtypid = 'insenum'::regtype ORDER BY enumsortorder; -- -- Basic table creation, row selection -- CREATE TABLE enumtest (col rainbow); INSERT INTO enumtest values ('red'), ('orange'), ('yellow'), ('green'); SELECT * FROM enumtest; -- -- Operators, no index -- SELECT * FROM enumtest WHERE col = 'orange'; SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col; SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col; SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col; SELECT * FROM enumtest WHERE col < 'green' ORDER BY col; SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col; -- -- Cast to/from text -- SELECT 'red'::rainbow::text || 'hithere'; SELECT 'red'::text::rainbow = 'red'::rainbow; -- -- Aggregates -- SELECT min(col) FROM enumtest; SELECT max(col) FROM enumtest; SELECT max(col) FROM enumtest WHERE col < 'green'; -- -- Index tests, force use of index -- SET enable_seqscan = off; SET enable_bitmapscan = off; -- -- Btree index / opclass with the various operators -- CREATE UNIQUE INDEX enumtest_btree ON enumtest USING btree (col); SELECT * FROM enumtest WHERE col = 'orange'; SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col; SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col; SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col; SELECT * FROM enumtest WHERE col < 'green' ORDER BY col; SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col; SELECT min(col) FROM enumtest; SELECT max(col) FROM enumtest; SELECT max(col) FROM enumtest WHERE col < 'green'; DROP INDEX enumtest_btree; -- -- Hash index / opclass with the = operator -- CREATE INDEX enumtest_hash ON enumtest USING hash (col); SELECT * FROM enumtest WHERE col = 'orange'; DROP INDEX enumtest_hash; -- -- End index tests -- RESET enable_seqscan; RESET enable_bitmapscan; -- -- Domains over enums -- CREATE DOMAIN rgb AS rainbow CHECK (VALUE IN ('red', 'green', 'blue')); SELECT 'red'::rgb; SELECT 'purple'::rgb; SELECT 'purple'::rainbow::rgb; DROP DOMAIN rgb; -- -- Arrays -- SELECT '{red,green,blue}'::rainbow[]; SELECT ('{red,green,blue}'::rainbow[])[2]; SELECT 'red' = ANY ('{red,green,blue}'::rainbow[]); SELECT 'yellow' = ANY ('{red,green,blue}'::rainbow[]); SELECT 'red' = ALL ('{red,green,blue}'::rainbow[]); SELECT 'red' = ALL ('{red,red}'::rainbow[]); -- -- Support functions -- SELECT enum_first(NULL::rainbow); SELECT enum_last('green'::rainbow); SELECT enum_range(NULL::rainbow); SELECT enum_range('orange'::rainbow, 'green'::rainbow); SELECT enum_range(NULL, 'green'::rainbow); SELECT enum_range('orange'::rainbow, NULL); SELECT enum_range(NULL::rainbow, NULL); -- -- User functions, can't test perl/python etc here since may not be compiled. -- CREATE FUNCTION echo_me(anyenum) RETURNS text AS $$ BEGIN RETURN $1::text || 'omg'; END $$ LANGUAGE plpgsql; SELECT echo_me('red'::rainbow); -- -- Concrete function should override generic one -- CREATE FUNCTION echo_me(rainbow) RETURNS text AS $$ BEGIN RETURN $1::text || 'wtf'; END $$ LANGUAGE plpgsql; SELECT echo_me('red'::rainbow); -- -- If we drop the original generic one, we don't have to qualify the type -- anymore, since there's only one match -- DROP FUNCTION echo_me(anyenum); SELECT echo_me('red'); DROP FUNCTION echo_me(rainbow); -- -- RI triggers on enum types -- CREATE TABLE enumtest_parent (id rainbow PRIMARY KEY); CREATE TABLE enumtest_child (parent rainbow REFERENCES enumtest_parent); INSERT INTO enumtest_parent VALUES ('red'); INSERT INTO enumtest_child VALUES ('red'); INSERT INTO enumtest_child VALUES ('blue'); -- fail DELETE FROM enumtest_parent; -- fail -- -- cross-type RI should fail -- CREATE TYPE bogus AS ENUM('good', 'bad', 'ugly'); CREATE TABLE enumtest_bogus_child(parent bogus REFERENCES enumtest_parent); DROP TYPE bogus; -- check renaming a value ALTER TYPE rainbow RENAME VALUE 'red' TO 'crimson'; SELECT enumlabel, enumsortorder FROM pg_enum WHERE enumtypid = 'rainbow'::regtype ORDER BY 2; -- check that renaming a non-existent value fails ALTER TYPE rainbow RENAME VALUE 'red' TO 'crimson'; -- check that renaming to an existent value fails ALTER TYPE rainbow RENAME VALUE 'blue' TO 'green'; -- -- check transactional behaviour of ALTER TYPE ... ADD VALUE -- CREATE TYPE bogus AS ENUM('good'); -- check that we can add new values to existing enums in a transaction -- but we can't use them BEGIN; ALTER TYPE bogus ADD VALUE 'new'; SAVEPOINT x; SELECT 'new'::bogus; -- unsafe ROLLBACK TO x; SELECT enum_first(null::bogus); -- safe SELECT enum_last(null::bogus); -- unsafe ROLLBACK TO x; SELECT enum_range(null::bogus); -- unsafe ROLLBACK TO x; COMMIT; SELECT 'new'::bogus; -- now safe SELECT enumlabel, enumsortorder FROM pg_enum WHERE enumtypid = 'bogus'::regtype ORDER BY 2; -- check that we recognize the case where the enum already existed but was -- modified in the current txn; this should not be considered safe BEGIN; ALTER TYPE bogus RENAME TO bogon; ALTER TYPE bogon ADD VALUE 'bad'; SELECT 'bad'::bogon; ROLLBACK; -- but a renamed value is safe to use later in same transaction BEGIN; ALTER TYPE bogus RENAME VALUE 'good' to 'bad'; SELECT 'bad'::bogus; ROLLBACK; DROP TYPE bogus; -- check that values created during CREATE TYPE can be used in any case BEGIN; CREATE TYPE bogus AS ENUM('good','bad','ugly'); ALTER TYPE bogus RENAME TO bogon; select enum_range(null::bogon); ROLLBACK; -- ideally, we'd allow this usage; but it requires keeping track of whether -- the enum type was created in the current transaction, which is expensive BEGIN; CREATE TYPE bogus AS ENUM('good'); ALTER TYPE bogus RENAME TO bogon; ALTER TYPE bogon ADD VALUE 'bad'; ALTER TYPE bogon ADD VALUE 'ugly'; select enum_range(null::bogon); -- fails ROLLBACK; -- -- Cleanup -- DROP TABLE enumtest_child; DROP TABLE enumtest_parent; DROP TABLE enumtest; DROP TYPE rainbow; -- -- Verify properly cleaned up -- SELECT COUNT(*) FROM pg_type WHERE typname = 'rainbow'; SELECT * FROM pg_enum WHERE NOT EXISTS (SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid); pgFormatter-4.2/t/pg-test-files/sql/equivclass.sql000066400000000000000000000205511361326045100223020ustar00rootroot00000000000000-- -- Tests for the planner's "equivalence class" mechanism -- -- One thing that's not tested well during normal querying is the logic -- for handling "broken" ECs. This is because an EC can only become broken -- if its underlying btree operator family doesn't include a complete set -- of cross-type equality operators. There are not (and should not be) -- any such families built into Postgres; so we have to hack things up -- to create one. We do this by making two alias types that are really -- int8 (so we need no new C code) and adding only some operators for them -- into the standard integer_ops opfamily. create type int8alias1; create function int8alias1in(cstring) returns int8alias1 strict immutable language internal as 'int8in'; create function int8alias1out(int8alias1) returns cstring strict immutable language internal as 'int8out'; create type int8alias1 ( input = int8alias1in, output = int8alias1out, like = int8 ); create type int8alias2; create function int8alias2in(cstring) returns int8alias2 strict immutable language internal as 'int8in'; create function int8alias2out(int8alias2) returns cstring strict immutable language internal as 'int8out'; create type int8alias2 ( input = int8alias2in, output = int8alias2out, like = int8 ); create cast (int8 as int8alias1) without function; create cast (int8 as int8alias2) without function; create cast (int8alias1 as int8) without function; create cast (int8alias2 as int8) without function; create function int8alias1eq(int8alias1, int8alias1) returns bool strict immutable language internal as 'int8eq'; create operator = ( procedure = int8alias1eq, leftarg = int8alias1, rightarg = int8alias1, commutator = =, restrict = eqsel, join = eqjoinsel, merges ); alter operator family integer_ops using btree add operator 3 = (int8alias1, int8alias1); create function int8alias2eq(int8alias2, int8alias2) returns bool strict immutable language internal as 'int8eq'; create operator = ( procedure = int8alias2eq, leftarg = int8alias2, rightarg = int8alias2, commutator = =, restrict = eqsel, join = eqjoinsel, merges ); alter operator family integer_ops using btree add operator 3 = (int8alias2, int8alias2); create function int8alias1eq(int8, int8alias1) returns bool strict immutable language internal as 'int8eq'; create operator = ( procedure = int8alias1eq, leftarg = int8, rightarg = int8alias1, restrict = eqsel, join = eqjoinsel, merges ); alter operator family integer_ops using btree add operator 3 = (int8, int8alias1); create function int8alias1eq(int8alias1, int8alias2) returns bool strict immutable language internal as 'int8eq'; create operator = ( procedure = int8alias1eq, leftarg = int8alias1, rightarg = int8alias2, restrict = eqsel, join = eqjoinsel, merges ); alter operator family integer_ops using btree add operator 3 = (int8alias1, int8alias2); create function int8alias1lt(int8alias1, int8alias1) returns bool strict immutable language internal as 'int8lt'; create operator < ( procedure = int8alias1lt, leftarg = int8alias1, rightarg = int8alias1 ); alter operator family integer_ops using btree add operator 1 < (int8alias1, int8alias1); create function int8alias1cmp(int8, int8alias1) returns int strict immutable language internal as 'btint8cmp'; alter operator family integer_ops using btree add function 1 int8alias1cmp (int8, int8alias1); create table ec0 (ff int8 primary key, f1 int8, f2 int8); create table ec1 (ff int8 primary key, f1 int8alias1, f2 int8alias2); create table ec2 (xf int8 primary key, x1 int8alias1, x2 int8alias2); -- for the moment we only want to look at nestloop plans set enable_hashjoin = off; set enable_mergejoin = off; -- -- Note that for cases where there's a missing operator, we don't care so -- much whether the plan is ideal as that we don't fail or generate an -- outright incorrect plan. -- explain (costs off) select * from ec0 where ff = f1 and f1 = '42'::int8; explain (costs off) select * from ec0 where ff = f1 and f1 = '42'::int8alias1; explain (costs off) select * from ec1 where ff = f1 and f1 = '42'::int8alias1; explain (costs off) select * from ec1 where ff = f1 and f1 = '42'::int8alias2; explain (costs off) select * from ec1, ec2 where ff = x1 and ff = '42'::int8; explain (costs off) select * from ec1, ec2 where ff = x1 and ff = '42'::int8alias1; explain (costs off) select * from ec1, ec2 where ff = x1 and '42'::int8 = x1; explain (costs off) select * from ec1, ec2 where ff = x1 and x1 = '42'::int8alias1; explain (costs off) select * from ec1, ec2 where ff = x1 and x1 = '42'::int8alias2; create unique index ec1_expr1 on ec1((ff + 1)); create unique index ec1_expr2 on ec1((ff + 2 + 1)); create unique index ec1_expr3 on ec1((ff + 3 + 1)); create unique index ec1_expr4 on ec1((ff + 4)); explain (costs off) select * from ec1, (select ff + 1 as x from (select ff + 2 as ff from ec1 union all select ff + 3 as ff from ec1) ss0 union all select ff + 4 as x from ec1) as ss1 where ss1.x = ec1.f1 and ec1.ff = 42::int8; explain (costs off) select * from ec1, (select ff + 1 as x from (select ff + 2 as ff from ec1 union all select ff + 3 as ff from ec1) ss0 union all select ff + 4 as x from ec1) as ss1 where ss1.x = ec1.f1 and ec1.ff = 42::int8 and ec1.ff = ec1.f1; explain (costs off) select * from ec1, (select ff + 1 as x from (select ff + 2 as ff from ec1 union all select ff + 3 as ff from ec1) ss0 union all select ff + 4 as x from ec1) as ss1, (select ff + 1 as x from (select ff + 2 as ff from ec1 union all select ff + 3 as ff from ec1) ss0 union all select ff + 4 as x from ec1) as ss2 where ss1.x = ec1.f1 and ss1.x = ss2.x and ec1.ff = 42::int8; -- let's try that as a mergejoin set enable_mergejoin = on; set enable_nestloop = off; explain (costs off) select * from ec1, (select ff + 1 as x from (select ff + 2 as ff from ec1 union all select ff + 3 as ff from ec1) ss0 union all select ff + 4 as x from ec1) as ss1, (select ff + 1 as x from (select ff + 2 as ff from ec1 union all select ff + 3 as ff from ec1) ss0 union all select ff + 4 as x from ec1) as ss2 where ss1.x = ec1.f1 and ss1.x = ss2.x and ec1.ff = 42::int8; -- check partially indexed scan set enable_nestloop = on; set enable_mergejoin = off; drop index ec1_expr3; explain (costs off) select * from ec1, (select ff + 1 as x from (select ff + 2 as ff from ec1 union all select ff + 3 as ff from ec1) ss0 union all select ff + 4 as x from ec1) as ss1 where ss1.x = ec1.f1 and ec1.ff = 42::int8; -- let's try that as a mergejoin set enable_mergejoin = on; set enable_nestloop = off; explain (costs off) select * from ec1, (select ff + 1 as x from (select ff + 2 as ff from ec1 union all select ff + 3 as ff from ec1) ss0 union all select ff + 4 as x from ec1) as ss1 where ss1.x = ec1.f1 and ec1.ff = 42::int8; -- check effects of row-level security set enable_nestloop = on; set enable_mergejoin = off; alter table ec1 enable row level security; create policy p1 on ec1 using (f1 < '5'::int8alias1); create user regress_user_ectest; grant select on ec0 to regress_user_ectest; grant select on ec1 to regress_user_ectest; -- without any RLS, we'll treat {a.ff, b.ff, 43} as an EquivalenceClass explain (costs off) select * from ec0 a, ec1 b where a.ff = b.ff and a.ff = 43::bigint::int8alias1; set session authorization regress_user_ectest; -- with RLS active, the non-leakproof a.ff = 43 clause is not treated -- as a suitable source for an EquivalenceClass; currently, this is true -- even though the RLS clause has nothing to do directly with the EC explain (costs off) select * from ec0 a, ec1 b where a.ff = b.ff and a.ff = 43::bigint::int8alias1; reset session authorization; revoke select on ec0 from regress_user_ectest; revoke select on ec1 from regress_user_ectest; drop user regress_user_ectest; -- check that X=X is converted to X IS NOT NULL when appropriate explain (costs off) select * from tenk1 where unique1 = unique1 and unique2 = unique2; -- this could be converted, but isn't at present explain (costs off) select * from tenk1 where unique1 = unique1 or unique2 = unique2; pgFormatter-4.2/t/pg-test-files/sql/errors.sql000066400000000000000000000140471361326045100214420ustar00rootroot00000000000000-- -- ERRORS -- -- bad in postquel, but ok in PostgreSQL select 1; -- -- UNSUPPORTED STUFF -- doesn't work -- notify pg_class -- -- -- SELECT -- this used to be a syntax error, but now we allow an empty target list select; -- no such relation select * from nonesuch; -- bad name in target list select nonesuch from pg_database; -- empty distinct list isn't OK select distinct from pg_database; -- bad attribute name on lhs of operator select * from pg_database where nonesuch = pg_database.datname; -- bad attribute name on rhs of operator select * from pg_database where pg_database.datname = nonesuch; -- bad attribute name in select distinct on select distinct on (foobar) * from pg_database; -- -- DELETE -- missing relation name (this had better not wildcard!) delete from; -- no such relation delete from nonesuch; -- -- DROP -- missing relation name (this had better not wildcard!) drop table; -- no such relation drop table nonesuch; -- -- ALTER TABLE -- relation renaming -- missing relation name alter table rename; -- no such relation alter table nonesuch rename to newnonesuch; -- no such relation alter table nonesuch rename to stud_emp; -- conflict alter table stud_emp rename to aggtest; -- self-conflict alter table stud_emp rename to stud_emp; -- attribute renaming -- no such relation alter table nonesuchrel rename column nonesuchatt to newnonesuchatt; -- no such attribute alter table emp rename column nonesuchatt to newnonesuchatt; -- conflict alter table emp rename column salary to manager; -- conflict alter table emp rename column salary to ctid; -- -- TRANSACTION STUFF -- not in a xact abort; -- not in a xact end; -- -- CREATE AGGREGATE -- sfunc/finalfunc type disagreement create aggregate newavg2 (sfunc = int4pl, basetype = int4, stype = int4, finalfunc = int2um, initcond = '0'); -- left out basetype create aggregate newcnt1 (sfunc = int4inc, stype = int4, initcond = '0'); -- -- DROP INDEX -- missing index name drop index; -- bad index name drop index 314159; -- no such index drop index nonesuch; -- -- DROP AGGREGATE -- missing aggregate name drop aggregate; -- missing aggregate type drop aggregate newcnt1; -- bad aggregate name drop aggregate 314159 (int); -- bad aggregate type drop aggregate newcnt (nonesuch); -- no such aggregate drop aggregate nonesuch (int4); -- no such aggregate for type drop aggregate newcnt (float4); -- -- DROP FUNCTION -- missing function name drop function (); -- bad function name drop function 314159(); -- no such function drop function nonesuch(); -- -- DROP TYPE -- missing type name drop type; -- bad type name drop type 314159; -- no such type drop type nonesuch; -- -- DROP OPERATOR -- missing everything drop operator; -- bad operator name drop operator equals; -- missing type list drop operator ===; -- missing parentheses drop operator int4, int4; -- missing operator name drop operator (int4, int4); -- missing type list contents drop operator === (); -- no such operator drop operator === (int4); -- no such operator by that name drop operator === (int4, int4); -- no such type1 drop operator = (nonesuch); -- no such type1 drop operator = ( , int4); -- no such type1 drop operator = (nonesuch, int4); -- no such type2 drop operator = (int4, nonesuch); -- no such type2 drop operator = (int4, ); -- -- DROP RULE -- missing rule name drop rule; -- bad rule name drop rule 314159; -- no such rule drop rule nonesuch on noplace; -- these postquel variants are no longer supported drop tuple rule nonesuch; drop instance rule nonesuch on noplace; drop rewrite rule nonesuch; -- -- Check that division-by-zero is properly caught. -- select 1/0; select 1::int8/0; select 1/0::int8; select 1::int2/0; select 1/0::int2; select 1::numeric/0; select 1/0::numeric; select 1::float8/0; select 1/0::float8; select 1::float4/0; select 1/0::float4; -- -- Test psql's reporting of syntax error location -- xxx; CREATE foo; CREATE TABLE ; CREATE TABLE \g INSERT INTO foo VALUES(123) foo; INSERT INTO 123 VALUES(123); INSERT INTO foo VALUES(123) 123 ; -- with a tab CREATE TABLE foo (id INT4 UNIQUE NOT NULL, id2 TEXT NOT NULL PRIMARY KEY, id3 INTEGER NOT NUL, id4 INT4 UNIQUE NOT NULL, id5 TEXT UNIQUE NOT NULL); -- long line to be truncated on the left CREATE TABLE foo(id INT4 UNIQUE NOT NULL, id2 TEXT NOT NULL PRIMARY KEY, id3 INTEGER NOT NUL, id4 INT4 UNIQUE NOT NULL, id5 TEXT UNIQUE NOT NULL); -- long line to be truncated on the right CREATE TABLE foo( id3 INTEGER NOT NUL, id4 INT4 UNIQUE NOT NULL, id5 TEXT UNIQUE NOT NULL, id INT4 UNIQUE NOT NULL, id2 TEXT NOT NULL PRIMARY KEY); -- long line to be truncated both ways CREATE TABLE foo(id INT4 UNIQUE NOT NULL, id2 TEXT NOT NULL PRIMARY KEY, id3 INTEGER NOT NUL, id4 INT4 UNIQUE NOT NULL, id5 TEXT UNIQUE NOT NULL); -- long line to be truncated on the left, many lines CREATE TEMPORARY TABLE foo(id INT4 UNIQUE NOT NULL, id2 TEXT NOT NULL PRIMARY KEY, id3 INTEGER NOT NUL, id4 INT4 UNIQUE NOT NULL, id5 TEXT UNIQUE NOT NULL) ; -- long line to be truncated on the right, many lines CREATE TEMPORARY TABLE foo( id3 INTEGER NOT NUL, id4 INT4 UNIQUE NOT NULL, id5 TEXT UNIQUE NOT NULL, id INT4 UNIQUE NOT NULL, id2 TEXT NOT NULL PRIMARY KEY) ; -- long line to be truncated both ways, many lines CREATE TEMPORARY TABLE foo (id INT4 UNIQUE NOT NULL, idx INT4 UNIQUE NOT NULL, idy INT4 UNIQUE NOT NULL, id2 TEXT NOT NULL PRIMARY KEY, id3 INTEGER NOT NUL, id4 INT4 UNIQUE NOT NULL, id5 TEXT UNIQUE NOT NULL, idz INT4 UNIQUE NOT NULL, idv INT4 UNIQUE NOT NULL); -- more than 10 lines... CREATE TEMPORARY TABLE foo (id INT4 UNIQUE NOT NULL , idm INT4 UNIQUE NOT NULL, idx INT4 UNIQUE NOT NULL, idy INT4 UNIQUE NOT NULL, id2 TEXT NOT NULL PRIMARY KEY, id3 INTEGER NOT NUL, id4 INT4 UNIQUE NOT NULL, id5 TEXT UNIQUE NOT NULL, idz INT4 UNIQUE NOT NULL, idv INT4 UNIQUE NOT NULL); -- Check that stack depth detection mechanism works and -- max_stack_depth is not set too high create function infinite_recurse() returns int as 'select infinite_recurse()' language sql; \set VERBOSITY terse select infinite_recurse(); pgFormatter-4.2/t/pg-test-files/sql/event_trigger.sql000066400000000000000000000343251361326045100227730ustar00rootroot00000000000000-- should fail, return type mismatch create event trigger regress_event_trigger on ddl_command_start execute procedure pg_backend_pid(); -- OK create function test_event_trigger() returns event_trigger as $$ BEGIN RAISE NOTICE 'test_event_trigger: % %', tg_event, tg_tag; END $$ language plpgsql; -- should fail, event triggers cannot have declared arguments create function test_event_trigger_arg(name text) returns event_trigger as $$ BEGIN RETURN 1; END $$ language plpgsql; -- should fail, SQL functions cannot be event triggers create function test_event_trigger_sql() returns event_trigger as $$ SELECT 1 $$ language sql; -- should fail, no elephant_bootstrap entry point create event trigger regress_event_trigger on elephant_bootstrap execute procedure test_event_trigger(); -- OK create event trigger regress_event_trigger on ddl_command_start execute procedure test_event_trigger(); -- OK create event trigger regress_event_trigger_end on ddl_command_end execute function test_event_trigger(); -- should fail, food is not a valid filter variable create event trigger regress_event_trigger2 on ddl_command_start when food in ('sandwich') execute procedure test_event_trigger(); -- should fail, sandwich is not a valid command tag create event trigger regress_event_trigger2 on ddl_command_start when tag in ('sandwich') execute procedure test_event_trigger(); -- should fail, create skunkcabbage is not a valid command tag create event trigger regress_event_trigger2 on ddl_command_start when tag in ('create table', 'create skunkcabbage') execute procedure test_event_trigger(); -- should fail, can't have event triggers on event triggers create event trigger regress_event_trigger2 on ddl_command_start when tag in ('DROP EVENT TRIGGER') execute procedure test_event_trigger(); -- should fail, can't have event triggers on global objects create event trigger regress_event_trigger2 on ddl_command_start when tag in ('CREATE ROLE') execute procedure test_event_trigger(); -- should fail, can't have event triggers on global objects create event trigger regress_event_trigger2 on ddl_command_start when tag in ('CREATE DATABASE') execute procedure test_event_trigger(); -- should fail, can't have event triggers on global objects create event trigger regress_event_trigger2 on ddl_command_start when tag in ('CREATE TABLESPACE') execute procedure test_event_trigger(); -- should fail, can't have same filter variable twice create event trigger regress_event_trigger2 on ddl_command_start when tag in ('create table') and tag in ('CREATE FUNCTION') execute procedure test_event_trigger(); -- should fail, can't have arguments create event trigger regress_event_trigger2 on ddl_command_start execute procedure test_event_trigger('argument not allowed'); -- OK create event trigger regress_event_trigger2 on ddl_command_start when tag in ('create table', 'CREATE FUNCTION') execute procedure test_event_trigger(); -- OK comment on event trigger regress_event_trigger is 'test comment'; -- drop as non-superuser should fail create role regress_evt_user; set role regress_evt_user; create event trigger regress_event_trigger_noperms on ddl_command_start execute procedure test_event_trigger(); reset role; -- test enabling and disabling alter event trigger regress_event_trigger disable; -- fires _trigger2 and _trigger_end should fire, but not _trigger create table event_trigger_fire1 (a int); alter event trigger regress_event_trigger enable; set session_replication_role = replica; -- fires nothing create table event_trigger_fire2 (a int); alter event trigger regress_event_trigger enable replica; -- fires only _trigger create table event_trigger_fire3 (a int); alter event trigger regress_event_trigger enable always; -- fires only _trigger create table event_trigger_fire4 (a int); reset session_replication_role; -- fires all three create table event_trigger_fire5 (a int); -- non-top-level command create function f1() returns int language plpgsql as $$ begin create table event_trigger_fire6 (a int); return 0; end $$; select f1(); -- non-top-level command create procedure p1() language plpgsql as $$ begin create table event_trigger_fire7 (a int); end $$; call p1(); -- clean up alter event trigger regress_event_trigger disable; drop table event_trigger_fire2, event_trigger_fire3, event_trigger_fire4, event_trigger_fire5, event_trigger_fire6, event_trigger_fire7; drop routine f1(), p1(); -- regress_event_trigger_end should fire on these commands grant all on table event_trigger_fire1 to public; comment on table event_trigger_fire1 is 'here is a comment'; revoke all on table event_trigger_fire1 from public; drop table event_trigger_fire1; create foreign data wrapper useless; create server useless_server foreign data wrapper useless; create user mapping for regress_evt_user server useless_server; alter default privileges for role regress_evt_user revoke delete on tables from regress_evt_user; -- alter owner to non-superuser should fail alter event trigger regress_event_trigger owner to regress_evt_user; -- alter owner to superuser should work alter role regress_evt_user superuser; alter event trigger regress_event_trigger owner to regress_evt_user; -- should fail, name collision alter event trigger regress_event_trigger rename to regress_event_trigger2; -- OK alter event trigger regress_event_trigger rename to regress_event_trigger3; -- should fail, doesn't exist any more drop event trigger regress_event_trigger; -- should fail, regress_evt_user owns some objects drop role regress_evt_user; -- cleanup before next test -- these are all OK; the second one should emit a NOTICE drop event trigger if exists regress_event_trigger2; drop event trigger if exists regress_event_trigger2; drop event trigger regress_event_trigger3; drop event trigger regress_event_trigger_end; -- test support for dropped objects CREATE SCHEMA schema_one authorization regress_evt_user; CREATE SCHEMA schema_two authorization regress_evt_user; CREATE SCHEMA audit_tbls authorization regress_evt_user; CREATE TEMP TABLE a_temp_tbl (); SET SESSION AUTHORIZATION regress_evt_user; CREATE TABLE schema_one.table_one(a int); CREATE TABLE schema_one."table two"(a int); CREATE TABLE schema_one.table_three(a int); CREATE TABLE audit_tbls.schema_one_table_two(the_value text); CREATE TABLE schema_two.table_two(a int); CREATE TABLE schema_two.table_three(a int, b text); CREATE TABLE audit_tbls.schema_two_table_three(the_value text); CREATE OR REPLACE FUNCTION schema_two.add(int, int) RETURNS int LANGUAGE plpgsql CALLED ON NULL INPUT AS $$ BEGIN RETURN coalesce($1,0) + coalesce($2,0); END; $$; CREATE AGGREGATE schema_two.newton (BASETYPE = int, SFUNC = schema_two.add, STYPE = int); RESET SESSION AUTHORIZATION; CREATE TABLE undroppable_objs ( object_type text, object_identity text ); INSERT INTO undroppable_objs VALUES ('table', 'schema_one.table_three'), ('table', 'audit_tbls.schema_two_table_three'); CREATE TABLE dropped_objects ( type text, schema text, object text ); -- This tests errors raised within event triggers; the one in audit_tbls -- uses 2nd-level recursive invocation via test_evtrig_dropped_objects(). CREATE OR REPLACE FUNCTION undroppable() RETURNS event_trigger LANGUAGE plpgsql AS $$ DECLARE obj record; BEGIN PERFORM 1 FROM pg_tables WHERE tablename = 'undroppable_objs'; IF NOT FOUND THEN RAISE NOTICE 'table undroppable_objs not found, skipping'; RETURN; END IF; FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() JOIN undroppable_objs USING (object_type, object_identity) LOOP RAISE EXCEPTION 'object % of type % cannot be dropped', obj.object_identity, obj.object_type; END LOOP; END; $$; CREATE EVENT TRIGGER undroppable ON sql_drop EXECUTE PROCEDURE undroppable(); CREATE OR REPLACE FUNCTION test_evtrig_dropped_objects() RETURNS event_trigger LANGUAGE plpgsql AS $$ DECLARE obj record; BEGIN FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() LOOP IF obj.object_type = 'table' THEN EXECUTE format('DROP TABLE IF EXISTS audit_tbls.%I', format('%s_%s', obj.schema_name, obj.object_name)); END IF; INSERT INTO dropped_objects (type, schema, object) VALUES (obj.object_type, obj.schema_name, obj.object_identity); END LOOP; END $$; CREATE EVENT TRIGGER regress_event_trigger_drop_objects ON sql_drop WHEN TAG IN ('drop table', 'drop function', 'drop view', 'drop owned', 'drop schema', 'alter table') EXECUTE PROCEDURE test_evtrig_dropped_objects(); ALTER TABLE schema_one.table_one DROP COLUMN a; DROP SCHEMA schema_one, schema_two CASCADE; DELETE FROM undroppable_objs WHERE object_identity = 'audit_tbls.schema_two_table_three'; DROP SCHEMA schema_one, schema_two CASCADE; DELETE FROM undroppable_objs WHERE object_identity = 'schema_one.table_three'; DROP SCHEMA schema_one, schema_two CASCADE; SELECT * FROM dropped_objects WHERE schema IS NULL OR schema <> 'pg_toast'; DROP OWNED BY regress_evt_user; SELECT * FROM dropped_objects WHERE type = 'schema'; DROP ROLE regress_evt_user; DROP EVENT TRIGGER regress_event_trigger_drop_objects; DROP EVENT TRIGGER undroppable; CREATE OR REPLACE FUNCTION event_trigger_report_dropped() RETURNS event_trigger LANGUAGE plpgsql AS $$ DECLARE r record; BEGIN FOR r IN SELECT * from pg_event_trigger_dropped_objects() LOOP IF NOT r.normal AND NOT r.original THEN CONTINUE; END IF; RAISE NOTICE 'NORMAL: orig=% normal=% istemp=% type=% identity=% name=% args=%', r.original, r.normal, r.is_temporary, r.object_type, r.object_identity, r.address_names, r.address_args; END LOOP; END; $$; CREATE EVENT TRIGGER regress_event_trigger_report_dropped ON sql_drop EXECUTE PROCEDURE event_trigger_report_dropped(); CREATE SCHEMA evttrig CREATE TABLE one (col_a SERIAL PRIMARY KEY, col_b text DEFAULT 'forty two') CREATE INDEX one_idx ON one (col_b) CREATE TABLE two (col_c INTEGER CHECK (col_c > 0) REFERENCES one DEFAULT 42); -- Partitioned tables with a partitioned index CREATE TABLE evttrig.parted ( id int PRIMARY KEY) PARTITION BY RANGE (id); CREATE TABLE evttrig.part_1_10 PARTITION OF evttrig.parted (id) FOR VALUES FROM (1) TO (10); CREATE TABLE evttrig.part_10_20 PARTITION OF evttrig.parted (id) FOR VALUES FROM (10) TO (20) PARTITION BY RANGE (id); CREATE TABLE evttrig.part_10_15 PARTITION OF evttrig.part_10_20 (id) FOR VALUES FROM (10) TO (15); CREATE TABLE evttrig.part_15_20 PARTITION OF evttrig.part_10_20 (id) FOR VALUES FROM (15) TO (20); ALTER TABLE evttrig.two DROP COLUMN col_c; ALTER TABLE evttrig.one ALTER COLUMN col_b DROP DEFAULT; ALTER TABLE evttrig.one DROP CONSTRAINT one_pkey; DROP INDEX evttrig.one_idx; DROP SCHEMA evttrig CASCADE; DROP TABLE a_temp_tbl; DROP EVENT TRIGGER regress_event_trigger_report_dropped; -- only allowed from within an event trigger function, should fail select pg_event_trigger_table_rewrite_oid(); -- test Table Rewrite Event Trigger CREATE OR REPLACE FUNCTION test_evtrig_no_rewrite() RETURNS event_trigger LANGUAGE plpgsql AS $$ BEGIN RAISE EXCEPTION 'rewrites not allowed'; END; $$; create event trigger no_rewrite_allowed on table_rewrite execute procedure test_evtrig_no_rewrite(); create table rewriteme (id serial primary key, foo float, bar timestamptz); insert into rewriteme select x * 1.001 from generate_series(1, 500) as t(x); alter table rewriteme alter column foo type numeric; alter table rewriteme add column baz int default 0; -- test with more than one reason to rewrite a single table CREATE OR REPLACE FUNCTION test_evtrig_no_rewrite() RETURNS event_trigger LANGUAGE plpgsql AS $$ BEGIN RAISE NOTICE 'Table ''%'' is being rewritten (reason = %)', pg_event_trigger_table_rewrite_oid()::regclass, pg_event_trigger_table_rewrite_reason(); END; $$; alter table rewriteme add column onemore int default 0, add column another int default -1, alter column foo type numeric(10,4); -- shouldn't trigger a table_rewrite event alter table rewriteme alter column foo type numeric(12,4); begin; set timezone to 'UTC'; alter table rewriteme alter column bar type timestamp; set timezone to '0'; alter table rewriteme alter column bar type timestamptz; set timezone to 'Europe/London'; alter table rewriteme alter column bar type timestamp; -- does rewrite rollback; -- typed tables are rewritten when their type changes. Don't emit table -- name, because firing order is not stable. CREATE OR REPLACE FUNCTION test_evtrig_no_rewrite() RETURNS event_trigger LANGUAGE plpgsql AS $$ BEGIN RAISE NOTICE 'Table is being rewritten (reason = %)', pg_event_trigger_table_rewrite_reason(); END; $$; create type rewritetype as (a int); create table rewritemetoo1 of rewritetype; create table rewritemetoo2 of rewritetype; alter type rewritetype alter attribute a type text cascade; -- but this doesn't work create table rewritemetoo3 (a rewritetype); alter type rewritetype alter attribute a type varchar cascade; drop table rewriteme; drop event trigger no_rewrite_allowed; drop function test_evtrig_no_rewrite(); -- test Row Security Event Trigger RESET SESSION AUTHORIZATION; CREATE TABLE event_trigger_test (a integer, b text); CREATE OR REPLACE FUNCTION start_command() RETURNS event_trigger AS $$ BEGIN RAISE NOTICE '% - ddl_command_start', tg_tag; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION end_command() RETURNS event_trigger AS $$ BEGIN RAISE NOTICE '% - ddl_command_end', tg_tag; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION drop_sql_command() RETURNS event_trigger AS $$ BEGIN RAISE NOTICE '% - sql_drop', tg_tag; END; $$ LANGUAGE plpgsql; CREATE EVENT TRIGGER start_rls_command ON ddl_command_start WHEN TAG IN ('CREATE POLICY', 'ALTER POLICY', 'DROP POLICY') EXECUTE PROCEDURE start_command(); CREATE EVENT TRIGGER end_rls_command ON ddl_command_end WHEN TAG IN ('CREATE POLICY', 'ALTER POLICY', 'DROP POLICY') EXECUTE PROCEDURE end_command(); CREATE EVENT TRIGGER sql_drop_command ON sql_drop WHEN TAG IN ('DROP POLICY') EXECUTE PROCEDURE drop_sql_command(); CREATE POLICY p1 ON event_trigger_test USING (FALSE); ALTER POLICY p1 ON event_trigger_test USING (TRUE); ALTER POLICY p1 ON event_trigger_test RENAME TO p2; DROP POLICY p2 ON event_trigger_test; DROP EVENT TRIGGER start_rls_command; DROP EVENT TRIGGER end_rls_command; DROP EVENT TRIGGER sql_drop_command; pgFormatter-4.2/t/pg-test-files/sql/expressions.sql000066400000000000000000000016521361326045100225060ustar00rootroot00000000000000-- -- expression evaluated tests that don't fit into a more specific file -- -- -- Tests for SQLVAlueFunction -- -- current_date (always matches because of transactional behaviour) SELECT date(now())::text = current_date::text; -- current_time / localtime SELECT now()::timetz::text = current_time::text; SELECT now()::time::text = localtime::text; -- current_timestamp / localtimestamp (always matches because of transactional behaviour) SELECT current_timestamp = NOW(); -- precision SELECT length(current_timestamp::text) >= length(current_timestamp(0)::text); -- localtimestamp SELECT now()::timestamp::text = localtimestamp::text; -- current_role/user/user is tested in rolnames.sql -- current database / catalog SELECT current_catalog = current_database(); -- current_schema SELECT current_schema; SET search_path = 'notme'; SELECT current_schema; SET search_path = 'pg_catalog'; SELECT current_schema; RESET search_path; pgFormatter-4.2/t/pg-test-files/sql/fast_default.sql000066400000000000000000000367771361326045100226050ustar00rootroot00000000000000-- -- ALTER TABLE ADD COLUMN DEFAULT test -- SET search_path = fast_default; CREATE SCHEMA fast_default; CREATE TABLE m(id OID); INSERT INTO m VALUES (NULL::OID); CREATE FUNCTION set(tabname name) RETURNS VOID AS $$ BEGIN UPDATE m SET id = (SELECT c.relfilenode FROM pg_class AS c, pg_namespace AS s WHERE c.relname = tabname AND c.relnamespace = s.oid AND s.nspname = 'fast_default'); END; $$ LANGUAGE 'plpgsql'; CREATE FUNCTION comp() RETURNS TEXT AS $$ BEGIN RETURN (SELECT CASE WHEN m.id = c.relfilenode THEN 'Unchanged' ELSE 'Rewritten' END FROM m, pg_class AS c, pg_namespace AS s WHERE c.relname = 't' AND c.relnamespace = s.oid AND s.nspname = 'fast_default'); END; $$ LANGUAGE 'plpgsql'; CREATE FUNCTION log_rewrite() RETURNS event_trigger LANGUAGE plpgsql as $func$ declare this_schema text; begin select into this_schema relnamespace::regnamespace::text from pg_class where oid = pg_event_trigger_table_rewrite_oid(); if this_schema = 'fast_default' then RAISE NOTICE 'rewriting table % for reason %', pg_event_trigger_table_rewrite_oid()::regclass, pg_event_trigger_table_rewrite_reason(); end if; end; $func$; CREATE TABLE has_volatile AS SELECT * FROM generate_series(1,10) id; CREATE EVENT TRIGGER has_volatile_rewrite ON table_rewrite EXECUTE PROCEDURE log_rewrite(); -- only the last of these should trigger a rewrite ALTER TABLE has_volatile ADD col1 int; ALTER TABLE has_volatile ADD col2 int DEFAULT 1; ALTER TABLE has_volatile ADD col3 timestamptz DEFAULT current_timestamp; ALTER TABLE has_volatile ADD col4 int DEFAULT (random() * 10000)::int; -- Test a large sample of different datatypes CREATE TABLE T(pk INT NOT NULL PRIMARY KEY, c_int INT DEFAULT 1); SELECT set('t'); INSERT INTO T VALUES (1), (2); ALTER TABLE T ADD COLUMN c_bpchar BPCHAR(5) DEFAULT 'hello', ALTER COLUMN c_int SET DEFAULT 2; INSERT INTO T VALUES (3), (4); ALTER TABLE T ADD COLUMN c_text TEXT DEFAULT 'world', ALTER COLUMN c_bpchar SET DEFAULT 'dog'; INSERT INTO T VALUES (5), (6); ALTER TABLE T ADD COLUMN c_date DATE DEFAULT '2016-06-02', ALTER COLUMN c_text SET DEFAULT 'cat'; INSERT INTO T VALUES (7), (8); ALTER TABLE T ADD COLUMN c_timestamp TIMESTAMP DEFAULT '2016-09-01 12:00:00', ADD COLUMN c_timestamp_null TIMESTAMP, ALTER COLUMN c_date SET DEFAULT '2010-01-01'; INSERT INTO T VALUES (9), (10); ALTER TABLE T ADD COLUMN c_array TEXT[] DEFAULT '{"This", "is", "the", "real", "world"}', ALTER COLUMN c_timestamp SET DEFAULT '1970-12-31 11:12:13', ALTER COLUMN c_timestamp_null SET DEFAULT '2016-09-29 12:00:00'; INSERT INTO T VALUES (11), (12); ALTER TABLE T ADD COLUMN c_small SMALLINT DEFAULT -5, ADD COLUMN c_small_null SMALLINT, ALTER COLUMN c_array SET DEFAULT '{"This", "is", "no", "fantasy"}'; INSERT INTO T VALUES (13), (14); ALTER TABLE T ADD COLUMN c_big BIGINT DEFAULT 180000000000018, ALTER COLUMN c_small SET DEFAULT 9, ALTER COLUMN c_small_null SET DEFAULT 13; INSERT INTO T VALUES (15), (16); ALTER TABLE T ADD COLUMN c_num NUMERIC DEFAULT 1.00000000001, ALTER COLUMN c_big SET DEFAULT -9999999999999999; INSERT INTO T VALUES (17), (18); ALTER TABLE T ADD COLUMN c_time TIME DEFAULT '12:00:00', ALTER COLUMN c_num SET DEFAULT 2.000000000000002; INSERT INTO T VALUES (19), (20); ALTER TABLE T ADD COLUMN c_interval INTERVAL DEFAULT '1 day', ALTER COLUMN c_time SET DEFAULT '23:59:59'; INSERT INTO T VALUES (21), (22); ALTER TABLE T ADD COLUMN c_hugetext TEXT DEFAULT repeat('abcdefg',1000), ALTER COLUMN c_interval SET DEFAULT '3 hours'; INSERT INTO T VALUES (23), (24); ALTER TABLE T ALTER COLUMN c_interval DROP DEFAULT, ALTER COLUMN c_hugetext SET DEFAULT repeat('poiuyt', 1000); INSERT INTO T VALUES (25), (26); ALTER TABLE T ALTER COLUMN c_bpchar DROP DEFAULT, ALTER COLUMN c_date DROP DEFAULT, ALTER COLUMN c_text DROP DEFAULT, ALTER COLUMN c_timestamp DROP DEFAULT, ALTER COLUMN c_array DROP DEFAULT, ALTER COLUMN c_small DROP DEFAULT, ALTER COLUMN c_big DROP DEFAULT, ALTER COLUMN c_num DROP DEFAULT, ALTER COLUMN c_time DROP DEFAULT, ALTER COLUMN c_hugetext DROP DEFAULT; INSERT INTO T VALUES (27), (28); SELECT pk, c_int, c_bpchar, c_text, c_date, c_timestamp, c_timestamp_null, c_array, c_small, c_small_null, c_big, c_num, c_time, c_interval, c_hugetext = repeat('abcdefg',1000) as c_hugetext_origdef, c_hugetext = repeat('poiuyt', 1000) as c_hugetext_newdef FROM T ORDER BY pk; SELECT comp(); DROP TABLE T; -- Test expressions in the defaults CREATE OR REPLACE FUNCTION foo(a INT) RETURNS TEXT AS $$ DECLARE res TEXT := ''; i INT; BEGIN i := 0; WHILE (i < a) LOOP res := res || chr(ascii('a') + i); i := i + 1; END LOOP; RETURN res; END; $$ LANGUAGE PLPGSQL STABLE; CREATE TABLE T(pk INT NOT NULL PRIMARY KEY, c_int INT DEFAULT LENGTH(foo(6))); SELECT set('t'); INSERT INTO T VALUES (1), (2); ALTER TABLE T ADD COLUMN c_bpchar BPCHAR(5) DEFAULT foo(4), ALTER COLUMN c_int SET DEFAULT LENGTH(foo(8)); INSERT INTO T VALUES (3), (4); ALTER TABLE T ADD COLUMN c_text TEXT DEFAULT foo(6), ALTER COLUMN c_bpchar SET DEFAULT foo(3); INSERT INTO T VALUES (5), (6); ALTER TABLE T ADD COLUMN c_date DATE DEFAULT '2016-06-02'::DATE + LENGTH(foo(10)), ALTER COLUMN c_text SET DEFAULT foo(12); INSERT INTO T VALUES (7), (8); ALTER TABLE T ADD COLUMN c_timestamp TIMESTAMP DEFAULT '2016-09-01'::DATE + LENGTH(foo(10)), ALTER COLUMN c_date SET DEFAULT '2010-01-01'::DATE - LENGTH(foo(4)); INSERT INTO T VALUES (9), (10); ALTER TABLE T ADD COLUMN c_array TEXT[] DEFAULT ('{"This", "is", "' || foo(4) || '","the", "real", "world"}')::TEXT[], ALTER COLUMN c_timestamp SET DEFAULT '1970-12-31'::DATE + LENGTH(foo(30)); INSERT INTO T VALUES (11), (12); ALTER TABLE T ALTER COLUMN c_int DROP DEFAULT, ALTER COLUMN c_array SET DEFAULT ('{"This", "is", "' || foo(1) || '", "fantasy"}')::text[]; INSERT INTO T VALUES (13), (14); ALTER TABLE T ALTER COLUMN c_bpchar DROP DEFAULT, ALTER COLUMN c_date DROP DEFAULT, ALTER COLUMN c_text DROP DEFAULT, ALTER COLUMN c_timestamp DROP DEFAULT, ALTER COLUMN c_array DROP DEFAULT; INSERT INTO T VALUES (15), (16); SELECT * FROM T; SELECT comp(); DROP TABLE T; DROP FUNCTION foo(INT); -- Fall back to full rewrite for volatile expressions CREATE TABLE T(pk INT NOT NULL PRIMARY KEY); INSERT INTO T VALUES (1); SELECT set('t'); -- now() is stable, because it returns the transaction timestamp ALTER TABLE T ADD COLUMN c1 TIMESTAMP DEFAULT now(); SELECT comp(); -- clock_timestamp() is volatile ALTER TABLE T ADD COLUMN c2 TIMESTAMP DEFAULT clock_timestamp(); SELECT comp(); DROP TABLE T; -- Simple querie CREATE TABLE T (pk INT NOT NULL PRIMARY KEY); SELECT set('t'); INSERT INTO T SELECT * FROM generate_series(1, 10) a; ALTER TABLE T ADD COLUMN c_bigint BIGINT NOT NULL DEFAULT -1; INSERT INTO T SELECT b, b - 10 FROM generate_series(11, 20) a(b); ALTER TABLE T ADD COLUMN c_text TEXT DEFAULT 'hello'; INSERT INTO T SELECT b, b - 10, (b + 10)::text FROM generate_series(21, 30) a(b); -- WHERE clause SELECT c_bigint, c_text FROM T WHERE c_bigint = -1 LIMIT 1; EXPLAIN (VERBOSE TRUE, COSTS FALSE) SELECT c_bigint, c_text FROM T WHERE c_bigint = -1 LIMIT 1; SELECT c_bigint, c_text FROM T WHERE c_text = 'hello' LIMIT 1; EXPLAIN (VERBOSE TRUE, COSTS FALSE) SELECT c_bigint, c_text FROM T WHERE c_text = 'hello' LIMIT 1; -- COALESCE SELECT COALESCE(c_bigint, pk), COALESCE(c_text, pk::text) FROM T ORDER BY pk LIMIT 10; -- Aggregate function SELECT SUM(c_bigint), MAX(c_text COLLATE "C" ), MIN(c_text COLLATE "C") FROM T; -- ORDER BY SELECT * FROM T ORDER BY c_bigint, c_text, pk LIMIT 10; EXPLAIN (VERBOSE TRUE, COSTS FALSE) SELECT * FROM T ORDER BY c_bigint, c_text, pk LIMIT 10; -- LIMIT SELECT * FROM T WHERE c_bigint > -1 ORDER BY c_bigint, c_text, pk LIMIT 10; EXPLAIN (VERBOSE TRUE, COSTS FALSE) SELECT * FROM T WHERE c_bigint > -1 ORDER BY c_bigint, c_text, pk LIMIT 10; -- DELETE with RETURNING DELETE FROM T WHERE pk BETWEEN 10 AND 20 RETURNING *; EXPLAIN (VERBOSE TRUE, COSTS FALSE) DELETE FROM T WHERE pk BETWEEN 10 AND 20 RETURNING *; -- UPDATE UPDATE T SET c_text = '"' || c_text || '"' WHERE pk < 10; SELECT * FROM T WHERE c_text LIKE '"%"' ORDER BY PK; SELECT comp(); DROP TABLE T; -- Combine with other DDL CREATE TABLE T(pk INT NOT NULL PRIMARY KEY); SELECT set('t'); INSERT INTO T VALUES (1), (2); ALTER TABLE T ADD COLUMN c_int INT NOT NULL DEFAULT -1; INSERT INTO T VALUES (3), (4); ALTER TABLE T ADD COLUMN c_text TEXT DEFAULT 'Hello'; INSERT INTO T VALUES (5), (6); ALTER TABLE T ALTER COLUMN c_text SET DEFAULT 'world', ALTER COLUMN c_int SET DEFAULT 1; INSERT INTO T VALUES (7), (8); SELECT * FROM T ORDER BY pk; -- Add an index CREATE INDEX i ON T(c_int, c_text); SELECT c_text FROM T WHERE c_int = -1; SELECT comp(); -- query to exercise expand_tuple function CREATE TABLE t1 AS SELECT 1::int AS a , 2::int AS b FROM generate_series(1,20) q; ALTER TABLE t1 ADD COLUMN c text; SELECT a, stddev(cast((SELECT sum(1) FROM generate_series(1,20) x) AS float4)) OVER (PARTITION BY a,b,c ORDER BY b) AS z FROM t1; DROP TABLE T; -- test that we account for missing columns without defaults correctly -- in expand_tuple, and that rows are correctly expanded for triggers CREATE FUNCTION test_trigger() RETURNS trigger LANGUAGE plpgsql AS $$ begin raise notice 'old tuple: %', to_json(OLD)::text; if TG_OP = 'DELETE' then return OLD; else return NEW; end if; end; $$; -- 2 new columns, both have defaults CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int); INSERT INTO t (a,b,c) VALUES (1,2,3); ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4; ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5; CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger(); SELECT * FROM t; UPDATE t SET y = 2; SELECT * FROM t; DROP TABLE t; -- 2 new columns, first has default CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int); INSERT INTO t (a,b,c) VALUES (1,2,3); ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4; ALTER TABLE t ADD COLUMN y int; CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger(); SELECT * FROM t; UPDATE t SET y = 2; SELECT * FROM t; DROP TABLE t; -- 2 new columns, second has default CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int); INSERT INTO t (a,b,c) VALUES (1,2,3); ALTER TABLE t ADD COLUMN x int; ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5; CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger(); SELECT * FROM t; UPDATE t SET y = 2; SELECT * FROM t; DROP TABLE t; -- 2 new columns, neither has default CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int); INSERT INTO t (a,b,c) VALUES (1,2,3); ALTER TABLE t ADD COLUMN x int; ALTER TABLE t ADD COLUMN y int; CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger(); SELECT * FROM t; UPDATE t SET y = 2; SELECT * FROM t; DROP TABLE t; -- same as last 4 tests but here the last original column has a NULL value -- 2 new columns, both have defaults CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int); INSERT INTO t (a,b,c) VALUES (1,2,NULL); ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4; ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5; CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger(); SELECT * FROM t; UPDATE t SET y = 2; SELECT * FROM t; DROP TABLE t; -- 2 new columns, first has default CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int); INSERT INTO t (a,b,c) VALUES (1,2,NULL); ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4; ALTER TABLE t ADD COLUMN y int; CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger(); SELECT * FROM t; UPDATE t SET y = 2; SELECT * FROM t; DROP TABLE t; -- 2 new columns, second has default CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int); INSERT INTO t (a,b,c) VALUES (1,2,NULL); ALTER TABLE t ADD COLUMN x int; ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5; CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger(); SELECT * FROM t; UPDATE t SET y = 2; SELECT * FROM t; DROP TABLE t; -- 2 new columns, neither has default CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int); INSERT INTO t (a,b,c) VALUES (1,2,NULL); ALTER TABLE t ADD COLUMN x int; ALTER TABLE t ADD COLUMN y int; CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger(); SELECT * FROM t; UPDATE t SET y = 2; SELECT * FROM t; DROP TABLE t; -- make sure expanded tuple has correct self pointer -- it will be required by the RI trigger doing the cascading delete CREATE TABLE leader (a int PRIMARY KEY, b int); CREATE TABLE follower (a int REFERENCES leader ON DELETE CASCADE, b int); INSERT INTO leader VALUES (1, 1), (2, 2); ALTER TABLE leader ADD c int; ALTER TABLE leader DROP c; DELETE FROM leader; -- check that ALTER TABLE ... ALTER TYPE does the right thing CREATE TABLE vtype( a integer); INSERT INTO vtype VALUES (1); ALTER TABLE vtype ADD COLUMN b DOUBLE PRECISION DEFAULT 0.2; ALTER TABLE vtype ADD COLUMN c BOOLEAN DEFAULT true; SELECT * FROM vtype; ALTER TABLE vtype ALTER b TYPE text USING b::text, ALTER c TYPE text USING c::text; SELECT * FROM vtype; -- also check the case that doesn't rewrite the table CREATE TABLE vtype2 (a int); INSERT INTO vtype2 VALUES (1); ALTER TABLE vtype2 ADD COLUMN b varchar(10) DEFAULT 'xxx'; ALTER TABLE vtype2 ALTER COLUMN b SET DEFAULT 'yyy'; INSERT INTO vtype2 VALUES (2); ALTER TABLE vtype2 ALTER COLUMN b TYPE varchar(20) USING b::varchar(20); SELECT * FROM vtype2; -- Ensure that defaults are checked when evaluating whether HOT update -- is possible, this was broken for a while: -- https://postgr.es/m/20190202133521.ylauh3ckqa7colzj%40alap3.anarazel.de BEGIN; CREATE TABLE t(); INSERT INTO t DEFAULT VALUES; ALTER TABLE t ADD COLUMN a int DEFAULT 1; CREATE INDEX ON t(a); -- set column with a default 1 to NULL, due to a bug that wasn't -- noticed has heap_getattr buggily returned NULL for default columns UPDATE t SET a = NULL; -- verify that index and non-index scans show the same result SET LOCAL enable_seqscan = true; SELECT * FROM t WHERE a IS NULL; SET LOCAL enable_seqscan = false; SELECT * FROM t WHERE a IS NULL; ROLLBACK; -- cleanup DROP TABLE vtype; DROP TABLE vtype2; DROP TABLE follower; DROP TABLE leader; DROP FUNCTION test_trigger(); DROP TABLE t1; DROP FUNCTION set(name); DROP FUNCTION comp(); DROP TABLE m; DROP TABLE has_volatile; DROP EVENT TRIGGER has_volatile_rewrite; DROP FUNCTION log_rewrite; DROP SCHEMA fast_default; -- Leave a table with an active fast default in place, for pg_upgrade testing set search_path = public; create table has_fast_default(f1 int); insert into has_fast_default values(1); alter table has_fast_default add column f2 int default 42; table has_fast_default; pgFormatter-4.2/t/pg-test-files/sql/float4.sql000066400000000000000000000257461361326045100213270ustar00rootroot00000000000000-- -- FLOAT4 -- CREATE TABLE FLOAT4_TBL (f1 float4); INSERT INTO FLOAT4_TBL(f1) VALUES (' 0.0'); INSERT INTO FLOAT4_TBL(f1) VALUES ('1004.30 '); INSERT INTO FLOAT4_TBL(f1) VALUES (' -34.84 '); INSERT INTO FLOAT4_TBL(f1) VALUES ('1.2345678901234e+20'); INSERT INTO FLOAT4_TBL(f1) VALUES ('1.2345678901234e-20'); -- test for over and under flow INSERT INTO FLOAT4_TBL(f1) VALUES ('10e70'); INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e70'); INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-70'); INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-70'); INSERT INTO FLOAT4_TBL(f1) VALUES ('10e400'); INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e400'); INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-400'); INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-400'); -- bad input INSERT INTO FLOAT4_TBL(f1) VALUES (''); INSERT INTO FLOAT4_TBL(f1) VALUES (' '); INSERT INTO FLOAT4_TBL(f1) VALUES ('xyz'); INSERT INTO FLOAT4_TBL(f1) VALUES ('5.0.0'); INSERT INTO FLOAT4_TBL(f1) VALUES ('5 . 0'); INSERT INTO FLOAT4_TBL(f1) VALUES ('5. 0'); INSERT INTO FLOAT4_TBL(f1) VALUES (' - 3.0'); INSERT INTO FLOAT4_TBL(f1) VALUES ('123 5'); -- special inputs SELECT 'NaN'::float4; SELECT 'nan'::float4; SELECT ' NAN '::float4; SELECT 'infinity'::float4; SELECT ' -INFINiTY '::float4; -- bad special inputs SELECT 'N A N'::float4; SELECT 'NaN x'::float4; SELECT ' INFINITY x'::float4; SELECT 'Infinity'::float4 + 100.0; SELECT 'Infinity'::float4 / 'Infinity'::float4; SELECT 'nan'::float4 / 'nan'::float4; SELECT 'nan'::numeric::float4; SELECT '' AS five, * FROM FLOAT4_TBL; SELECT '' AS four, f.* FROM FLOAT4_TBL f WHERE f.f1 <> '1004.3'; SELECT '' AS one, f.* FROM FLOAT4_TBL f WHERE f.f1 = '1004.3'; SELECT '' AS three, f.* FROM FLOAT4_TBL f WHERE '1004.3' > f.f1; SELECT '' AS three, f.* FROM FLOAT4_TBL f WHERE f.f1 < '1004.3'; SELECT '' AS four, f.* FROM FLOAT4_TBL f WHERE '1004.3' >= f.f1; SELECT '' AS four, f.* FROM FLOAT4_TBL f WHERE f.f1 <= '1004.3'; SELECT '' AS three, f.f1, f.f1 * '-10' AS x FROM FLOAT4_TBL f WHERE f.f1 > '0.0'; SELECT '' AS three, f.f1, f.f1 + '-10' AS x FROM FLOAT4_TBL f WHERE f.f1 > '0.0'; SELECT '' AS three, f.f1, f.f1 / '-10' AS x FROM FLOAT4_TBL f WHERE f.f1 > '0.0'; SELECT '' AS three, f.f1, f.f1 - '-10' AS x FROM FLOAT4_TBL f WHERE f.f1 > '0.0'; -- test divide by zero SELECT '' AS bad, f.f1 / '0.0' from FLOAT4_TBL f; SELECT '' AS five, * FROM FLOAT4_TBL; -- test the unary float4abs operator SELECT '' AS five, f.f1, @f.f1 AS abs_f1 FROM FLOAT4_TBL f; UPDATE FLOAT4_TBL SET f1 = FLOAT4_TBL.f1 * '-1' WHERE FLOAT4_TBL.f1 > '0.0'; SELECT '' AS five, * FROM FLOAT4_TBL; -- test edge-case coercions to integer SELECT '32767.4'::float4::int2; SELECT '32767.6'::float4::int2; SELECT '-32768.4'::float4::int2; SELECT '-32768.6'::float4::int2; SELECT '2147483520'::float4::int4; SELECT '2147483647'::float4::int4; SELECT '-2147483648.5'::float4::int4; SELECT '-2147483900'::float4::int4; SELECT '9223369837831520256'::float4::int8; SELECT '9223372036854775807'::float4::int8; SELECT '-9223372036854775808.5'::float4::int8; SELECT '-9223380000000000000'::float4::int8; -- Test for correct input rounding in edge cases. -- These lists are from Paxson 1991, excluding subnormals and -- inputs of over 9 sig. digits. SELECT float4send('5e-20'::float4); SELECT float4send('67e14'::float4); SELECT float4send('985e15'::float4); SELECT float4send('55895e-16'::float4); SELECT float4send('7038531e-32'::float4); SELECT float4send('702990899e-20'::float4); SELECT float4send('3e-23'::float4); SELECT float4send('57e18'::float4); SELECT float4send('789e-35'::float4); SELECT float4send('2539e-18'::float4); SELECT float4send('76173e28'::float4); SELECT float4send('887745e-11'::float4); SELECT float4send('5382571e-37'::float4); SELECT float4send('82381273e-35'::float4); SELECT float4send('750486563e-38'::float4); -- Test that the smallest possible normalized input value inputs -- correctly, either in 9-significant-digit or shortest-decimal -- format. -- -- exact val is 1.1754943508... -- shortest val is 1.1754944000 -- midpoint to next val is 1.1754944208... SELECT float4send('1.17549435e-38'::float4); SELECT float4send('1.1754944e-38'::float4); -- test output (and round-trip safety) of various values. -- To ensure we're testing what we think we're testing, start with -- float values specified by bit patterns (as a useful side effect, -- this means we'll fail on non-IEEE platforms). create type xfloat4; create function xfloat4in(cstring) returns xfloat4 immutable strict language internal as 'int4in'; create function xfloat4out(xfloat4) returns cstring immutable strict language internal as 'int4out'; create type xfloat4 (input = xfloat4in, output = xfloat4out, like = float4); create cast (xfloat4 as float4) without function; create cast (float4 as xfloat4) without function; create cast (xfloat4 as integer) without function; create cast (integer as xfloat4) without function; -- float4: seeeeeee emmmmmmm mmmmmmmm mmmmmmmm -- we don't care to assume the platform's strtod() handles subnormals -- correctly; those are "use at your own risk". However we do test -- subnormal outputs, since those are under our control. with testdata(bits) as (values -- small subnormals (x'00000001'), (x'00000002'), (x'00000003'), (x'00000010'), (x'00000011'), (x'00000100'), (x'00000101'), (x'00004000'), (x'00004001'), (x'00080000'), (x'00080001'), -- stress values (x'0053c4f4'), -- 7693e-42 (x'006c85c4'), -- 996622e-44 (x'0041ca76'), -- 60419369e-46 (x'004b7678'), -- 6930161142e-48 -- taken from upstream testsuite (x'00000007'), (x'00424fe2'), -- borderline between subnormal and normal (x'007ffff0'), (x'007ffff1'), (x'007ffffe'), (x'007fffff')) select float4send(flt) as ibits, flt from (select bits::integer::xfloat4::float4 as flt from testdata offset 0) s; with testdata(bits) as (values (x'00000000'), -- smallest normal values (x'00800000'), (x'00800001'), (x'00800004'), (x'00800005'), (x'00800006'), -- small normal values chosen for short vs. long output (x'008002f1'), (x'008002f2'), (x'008002f3'), (x'00800e17'), (x'00800e18'), (x'00800e19'), -- assorted values (random mantissae) (x'01000001'), (x'01102843'), (x'01a52c98'), (x'0219c229'), (x'02e4464d'), (x'037343c1'), (x'03a91b36'), (x'047ada65'), (x'0496fe87'), (x'0550844f'), (x'05999da3'), (x'060ea5e2'), (x'06e63c45'), (x'07f1e548'), (x'0fc5282b'), (x'1f850283'), (x'2874a9d6'), -- values around 5e-08 (x'3356bf94'), (x'3356bf95'), (x'3356bf96'), -- around 1e-07 (x'33d6bf94'), (x'33d6bf95'), (x'33d6bf96'), -- around 3e-07 .. 1e-04 (x'34a10faf'), (x'34a10fb0'), (x'34a10fb1'), (x'350637bc'), (x'350637bd'), (x'350637be'), (x'35719786'), (x'35719787'), (x'35719788'), (x'358637bc'), (x'358637bd'), (x'358637be'), (x'36a7c5ab'), (x'36a7c5ac'), (x'36a7c5ad'), (x'3727c5ab'), (x'3727c5ac'), (x'3727c5ad'), -- format crossover at 1e-04 (x'38d1b714'), (x'38d1b715'), (x'38d1b716'), (x'38d1b717'), (x'38d1b718'), (x'38d1b719'), (x'38d1b71a'), (x'38d1b71b'), (x'38d1b71c'), (x'38d1b71d'), -- (x'38dffffe'), (x'38dfffff'), (x'38e00000'), (x'38efffff'), (x'38f00000'), (x'38f00001'), (x'3a83126e'), (x'3a83126f'), (x'3a831270'), (x'3c23d709'), (x'3c23d70a'), (x'3c23d70b'), (x'3dcccccc'), (x'3dcccccd'), (x'3dccccce'), -- chosen to need 9 digits for 3dcccd70 (x'3dcccd6f'), (x'3dcccd70'), (x'3dcccd71'), -- (x'3effffff'), (x'3f000000'), (x'3f000001'), (x'3f333332'), (x'3f333333'), (x'3f333334'), -- approach 1.0 with increasing numbers of 9s (x'3f666665'), (x'3f666666'), (x'3f666667'), (x'3f7d70a3'), (x'3f7d70a4'), (x'3f7d70a5'), (x'3f7fbe76'), (x'3f7fbe77'), (x'3f7fbe78'), (x'3f7ff971'), (x'3f7ff972'), (x'3f7ff973'), (x'3f7fff57'), (x'3f7fff58'), (x'3f7fff59'), (x'3f7fffee'), (x'3f7fffef'), -- values very close to 1 (x'3f7ffff0'), (x'3f7ffff1'), (x'3f7ffff2'), (x'3f7ffff3'), (x'3f7ffff4'), (x'3f7ffff5'), (x'3f7ffff6'), (x'3f7ffff7'), (x'3f7ffff8'), (x'3f7ffff9'), (x'3f7ffffa'), (x'3f7ffffb'), (x'3f7ffffc'), (x'3f7ffffd'), (x'3f7ffffe'), (x'3f7fffff'), (x'3f800000'), (x'3f800001'), (x'3f800002'), (x'3f800003'), (x'3f800004'), (x'3f800005'), (x'3f800006'), (x'3f800007'), (x'3f800008'), (x'3f800009'), -- values 1 to 1.1 (x'3f80000f'), (x'3f800010'), (x'3f800011'), (x'3f800012'), (x'3f800013'), (x'3f800014'), (x'3f800017'), (x'3f800018'), (x'3f800019'), (x'3f80001a'), (x'3f80001b'), (x'3f80001c'), (x'3f800029'), (x'3f80002a'), (x'3f80002b'), (x'3f800053'), (x'3f800054'), (x'3f800055'), (x'3f800346'), (x'3f800347'), (x'3f800348'), (x'3f8020c4'), (x'3f8020c5'), (x'3f8020c6'), (x'3f8147ad'), (x'3f8147ae'), (x'3f8147af'), (x'3f8ccccc'), (x'3f8ccccd'), (x'3f8cccce'), -- (x'3fc90fdb'), -- pi/2 (x'402df854'), -- e (x'40490fdb'), -- pi -- (x'409fffff'), (x'40a00000'), (x'40a00001'), (x'40afffff'), (x'40b00000'), (x'40b00001'), (x'411fffff'), (x'41200000'), (x'41200001'), (x'42c7ffff'), (x'42c80000'), (x'42c80001'), (x'4479ffff'), (x'447a0000'), (x'447a0001'), (x'461c3fff'), (x'461c4000'), (x'461c4001'), (x'47c34fff'), (x'47c35000'), (x'47c35001'), (x'497423ff'), (x'49742400'), (x'49742401'), (x'4b18967f'), (x'4b189680'), (x'4b189681'), (x'4cbebc1f'), (x'4cbebc20'), (x'4cbebc21'), (x'4e6e6b27'), (x'4e6e6b28'), (x'4e6e6b29'), (x'501502f8'), (x'501502f9'), (x'501502fa'), (x'51ba43b6'), (x'51ba43b7'), (x'51ba43b8'), -- stress values (x'1f6c1e4a'), -- 5e-20 (x'59be6cea'), -- 67e14 (x'5d5ab6c4'), -- 985e15 (x'2cc4a9bd'), -- 55895e-16 (x'15ae43fd'), -- 7038531e-32 (x'2cf757ca'), -- 702990899e-20 (x'665ba998'), -- 25933168707e13 (x'743c3324'), -- 596428896559e20 -- exercise fixed-point memmoves (x'47f1205a'), (x'4640e6ae'), (x'449a5225'), (x'42f6e9d5'), (x'414587dd'), (x'3f9e064b'), -- these cases come from the upstream's testsuite -- BoundaryRoundEven (x'4c000004'), (x'50061c46'), (x'510006a8'), -- ExactValueRoundEven (x'48951f84'), (x'45fd1840'), -- LotsOfTrailingZeros (x'39800000'), (x'3b200000'), (x'3b900000'), (x'3bd00000'), -- Regression (x'63800000'), (x'4b000000'), (x'4b800000'), (x'4c000001'), (x'4c800b0d'), (x'00d24584'), (x'00d90b88'), (x'45803f34'), (x'4f9f24f7'), (x'3a8722c3'), (x'5c800041'), (x'15ae43fd'), (x'5d4cccfb'), (x'4c800001'), (x'57800ed8'), (x'5f000000'), (x'700000f0'), (x'5f23e9ac'), (x'5e9502f9'), (x'5e8012b1'), (x'3c000028'), (x'60cde861'), (x'03aa2a50'), (x'43480000'), (x'4c000000'), -- LooksLikePow5 (x'5D1502F9'), (x'5D9502F9'), (x'5E1502F9'), -- OutputLength (x'3f99999a'), (x'3f9d70a4'), (x'3f9df3b6'), (x'3f9e0419'), (x'3f9e0610'), (x'3f9e064b'), (x'3f9e0651'), (x'03d20cfe') ) select float4send(flt) as ibits, flt, flt::text::float4 as r_flt, float4send(flt::text::float4) as obits, float4send(flt::text::float4) = float4send(flt) as correct from (select bits::integer::xfloat4::float4 as flt from testdata offset 0) s; -- clean up, lest opr_sanity complain drop type xfloat4 cascade; pgFormatter-4.2/t/pg-test-files/sql/float8.sql000066400000000000000000000353001361326045100213160ustar00rootroot00000000000000-- -- FLOAT8 -- CREATE TABLE FLOAT8_TBL(f1 float8); INSERT INTO FLOAT8_TBL(f1) VALUES (' 0.0 '); INSERT INTO FLOAT8_TBL(f1) VALUES ('1004.30 '); INSERT INTO FLOAT8_TBL(f1) VALUES (' -34.84'); INSERT INTO FLOAT8_TBL(f1) VALUES ('1.2345678901234e+200'); INSERT INTO FLOAT8_TBL(f1) VALUES ('1.2345678901234e-200'); -- test for underflow and overflow handling SELECT '10e400'::float8; SELECT '-10e400'::float8; SELECT '10e-400'::float8; SELECT '-10e-400'::float8; -- test smallest normalized input SELECT float8send('2.2250738585072014E-308'::float8); -- bad input INSERT INTO FLOAT8_TBL(f1) VALUES (''); INSERT INTO FLOAT8_TBL(f1) VALUES (' '); INSERT INTO FLOAT8_TBL(f1) VALUES ('xyz'); INSERT INTO FLOAT8_TBL(f1) VALUES ('5.0.0'); INSERT INTO FLOAT8_TBL(f1) VALUES ('5 . 0'); INSERT INTO FLOAT8_TBL(f1) VALUES ('5. 0'); INSERT INTO FLOAT8_TBL(f1) VALUES (' - 3'); INSERT INTO FLOAT8_TBL(f1) VALUES ('123 5'); -- special inputs SELECT 'NaN'::float8; SELECT 'nan'::float8; SELECT ' NAN '::float8; SELECT 'infinity'::float8; SELECT ' -INFINiTY '::float8; -- bad special inputs SELECT 'N A N'::float8; SELECT 'NaN x'::float8; SELECT ' INFINITY x'::float8; SELECT 'Infinity'::float8 + 100.0; SELECT 'Infinity'::float8 / 'Infinity'::float8; SELECT 'nan'::float8 / 'nan'::float8; SELECT 'nan'::numeric::float8; SELECT '' AS five, * FROM FLOAT8_TBL; SELECT '' AS four, f.* FROM FLOAT8_TBL f WHERE f.f1 <> '1004.3'; SELECT '' AS one, f.* FROM FLOAT8_TBL f WHERE f.f1 = '1004.3'; SELECT '' AS three, f.* FROM FLOAT8_TBL f WHERE '1004.3' > f.f1; SELECT '' AS three, f.* FROM FLOAT8_TBL f WHERE f.f1 < '1004.3'; SELECT '' AS four, f.* FROM FLOAT8_TBL f WHERE '1004.3' >= f.f1; SELECT '' AS four, f.* FROM FLOAT8_TBL f WHERE f.f1 <= '1004.3'; SELECT '' AS three, f.f1, f.f1 * '-10' AS x FROM FLOAT8_TBL f WHERE f.f1 > '0.0'; SELECT '' AS three, f.f1, f.f1 + '-10' AS x FROM FLOAT8_TBL f WHERE f.f1 > '0.0'; SELECT '' AS three, f.f1, f.f1 / '-10' AS x FROM FLOAT8_TBL f WHERE f.f1 > '0.0'; SELECT '' AS three, f.f1, f.f1 - '-10' AS x FROM FLOAT8_TBL f WHERE f.f1 > '0.0'; SELECT '' AS one, f.f1 ^ '2.0' AS square_f1 FROM FLOAT8_TBL f where f.f1 = '1004.3'; -- absolute value SELECT '' AS five, f.f1, @f.f1 AS abs_f1 FROM FLOAT8_TBL f; -- truncate SELECT '' AS five, f.f1, trunc(f.f1) AS trunc_f1 FROM FLOAT8_TBL f; -- round SELECT '' AS five, f.f1, round(f.f1) AS round_f1 FROM FLOAT8_TBL f; -- ceil / ceiling select ceil(f1) as ceil_f1 from float8_tbl f; select ceiling(f1) as ceiling_f1 from float8_tbl f; -- floor select floor(f1) as floor_f1 from float8_tbl f; -- sign select sign(f1) as sign_f1 from float8_tbl f; -- avoid bit-exact output here because operations may not be bit-exact. SET extra_float_digits = 0; -- square root SELECT sqrt(float8 '64') AS eight; SELECT |/ float8 '64' AS eight; SELECT '' AS three, f.f1, |/f.f1 AS sqrt_f1 FROM FLOAT8_TBL f WHERE f.f1 > '0.0'; -- power SELECT power(float8 '144', float8 '0.5'); SELECT power(float8 'NaN', float8 '0.5'); SELECT power(float8 '144', float8 'NaN'); SELECT power(float8 'NaN', float8 'NaN'); SELECT power(float8 '-1', float8 'NaN'); SELECT power(float8 '1', float8 'NaN'); SELECT power(float8 'NaN', float8 '0'); -- take exp of ln(f.f1) SELECT '' AS three, f.f1, exp(ln(f.f1)) AS exp_ln_f1 FROM FLOAT8_TBL f WHERE f.f1 > '0.0'; -- cube root SELECT ||/ float8 '27' AS three; SELECT '' AS five, f.f1, ||/f.f1 AS cbrt_f1 FROM FLOAT8_TBL f; SELECT '' AS five, * FROM FLOAT8_TBL; UPDATE FLOAT8_TBL SET f1 = FLOAT8_TBL.f1 * '-1' WHERE FLOAT8_TBL.f1 > '0.0'; SELECT '' AS bad, f.f1 * '1e200' from FLOAT8_TBL f; SELECT '' AS bad, f.f1 ^ '1e200' from FLOAT8_TBL f; SELECT 0 ^ 0 + 0 ^ 1 + 0 ^ 0.0 + 0 ^ 0.5; SELECT '' AS bad, ln(f.f1) from FLOAT8_TBL f where f.f1 = '0.0' ; SELECT '' AS bad, ln(f.f1) from FLOAT8_TBL f where f.f1 < '0.0' ; SELECT '' AS bad, exp(f.f1) from FLOAT8_TBL f; SELECT '' AS bad, f.f1 / '0.0' from FLOAT8_TBL f; SELECT '' AS five, * FROM FLOAT8_TBL; -- hyperbolic functions -- we run these with extra_float_digits = 0 too, since different platforms -- tend to produce results that vary in the last place. SELECT sinh(float8 '1'); SELECT cosh(float8 '1'); SELECT tanh(float8 '1'); SELECT asinh(float8 '1'); SELECT acosh(float8 '2'); SELECT atanh(float8 '0.5'); -- test Inf/NaN cases for hyperbolic functions SELECT sinh(float8 'infinity'); SELECT sinh(float8 '-infinity'); SELECT sinh(float8 'nan'); SELECT cosh(float8 'infinity'); SELECT cosh(float8 '-infinity'); SELECT cosh(float8 'nan'); SELECT tanh(float8 'infinity'); SELECT tanh(float8 '-infinity'); SELECT tanh(float8 'nan'); SELECT asinh(float8 'infinity'); SELECT asinh(float8 '-infinity'); SELECT asinh(float8 'nan'); -- acosh(Inf) should be Inf, but some mingw versions produce NaN, so skip test -- SELECT acosh(float8 'infinity'); SELECT acosh(float8 '-infinity'); SELECT acosh(float8 'nan'); SELECT atanh(float8 'infinity'); SELECT atanh(float8 '-infinity'); SELECT atanh(float8 'nan'); RESET extra_float_digits; -- test for over- and underflow INSERT INTO FLOAT8_TBL(f1) VALUES ('10e400'); INSERT INTO FLOAT8_TBL(f1) VALUES ('-10e400'); INSERT INTO FLOAT8_TBL(f1) VALUES ('10e-400'); INSERT INTO FLOAT8_TBL(f1) VALUES ('-10e-400'); -- maintain external table consistency across platforms -- delete all values and reinsert well-behaved ones DELETE FROM FLOAT8_TBL; INSERT INTO FLOAT8_TBL(f1) VALUES ('0.0'); INSERT INTO FLOAT8_TBL(f1) VALUES ('-34.84'); INSERT INTO FLOAT8_TBL(f1) VALUES ('-1004.30'); INSERT INTO FLOAT8_TBL(f1) VALUES ('-1.2345678901234e+200'); INSERT INTO FLOAT8_TBL(f1) VALUES ('-1.2345678901234e-200'); SELECT '' AS five, * FROM FLOAT8_TBL; -- test edge-case coercions to integer SELECT '32767.4'::float8::int2; SELECT '32767.6'::float8::int2; SELECT '-32768.4'::float8::int2; SELECT '-32768.6'::float8::int2; SELECT '2147483647.4'::float8::int4; SELECT '2147483647.6'::float8::int4; SELECT '-2147483648.4'::float8::int4; SELECT '-2147483648.6'::float8::int4; SELECT '9223372036854773760'::float8::int8; SELECT '9223372036854775807'::float8::int8; SELECT '-9223372036854775808.5'::float8::int8; SELECT '-9223372036854780000'::float8::int8; -- test exact cases for trigonometric functions in degrees SELECT x, sind(x), sind(x) IN (-1,-0.5,0,0.5,1) AS sind_exact FROM (VALUES (0), (30), (90), (150), (180), (210), (270), (330), (360)) AS t(x); SELECT x, cosd(x), cosd(x) IN (-1,-0.5,0,0.5,1) AS cosd_exact FROM (VALUES (0), (60), (90), (120), (180), (240), (270), (300), (360)) AS t(x); SELECT x, tand(x), tand(x) IN ('-Infinity'::float8,-1,0, 1,'Infinity'::float8) AS tand_exact, cotd(x), cotd(x) IN ('-Infinity'::float8,-1,0, 1,'Infinity'::float8) AS cotd_exact FROM (VALUES (0), (45), (90), (135), (180), (225), (270), (315), (360)) AS t(x); SELECT x, asind(x), asind(x) IN (-90,-30,0,30,90) AS asind_exact, acosd(x), acosd(x) IN (0,60,90,120,180) AS acosd_exact FROM (VALUES (-1), (-0.5), (0), (0.5), (1)) AS t(x); SELECT x, atand(x), atand(x) IN (-90,-45,0,45,90) AS atand_exact FROM (VALUES ('-Infinity'::float8), (-1), (0), (1), ('Infinity'::float8)) AS t(x); SELECT x, y, atan2d(y, x), atan2d(y, x) IN (-90,0,90,180) AS atan2d_exact FROM (SELECT 10*cosd(a), 10*sind(a) FROM generate_series(0, 360, 90) AS t(a)) AS t(x,y); -- -- test output (and round-trip safety) of various values. -- To ensure we're testing what we think we're testing, start with -- float values specified by bit patterns (as a useful side effect, -- this means we'll fail on non-IEEE platforms). create type xfloat8; create function xfloat8in(cstring) returns xfloat8 immutable strict language internal as 'int8in'; create function xfloat8out(xfloat8) returns cstring immutable strict language internal as 'int8out'; create type xfloat8 (input = xfloat8in, output = xfloat8out, like = float8); create cast (xfloat8 as float8) without function; create cast (float8 as xfloat8) without function; create cast (xfloat8 as bigint) without function; create cast (bigint as xfloat8) without function; -- float8: seeeeeee eeeeeeee eeeeeeee mmmmmmmm mmmmmmmm(x4) -- we don't care to assume the platform's strtod() handles subnormals -- correctly; those are "use at your own risk". However we do test -- subnormal outputs, since those are under our control. with testdata(bits) as (values -- small subnormals (x'0000000000000001'), (x'0000000000000002'), (x'0000000000000003'), (x'0000000000001000'), (x'0000000100000000'), (x'0000010000000000'), (x'0000010100000000'), (x'0000400000000000'), (x'0000400100000000'), (x'0000800000000000'), (x'0000800000000001'), -- these values taken from upstream testsuite (x'00000000000f4240'), (x'00000000016e3600'), (x'0000008cdcdea440'), -- borderline between subnormal and normal (x'000ffffffffffff0'), (x'000ffffffffffff1'), (x'000ffffffffffffe'), (x'000fffffffffffff')) select float8send(flt) as ibits, flt from (select bits::bigint::xfloat8::float8 as flt from testdata offset 0) s; -- round-trip tests with testdata(bits) as (values (x'0000000000000000'), -- smallest normal values (x'0010000000000000'), (x'0010000000000001'), (x'0010000000000002'), (x'0018000000000000'), -- (x'3ddb7cdfd9d7bdba'), (x'3ddb7cdfd9d7bdbb'), (x'3ddb7cdfd9d7bdbc'), (x'3e112e0be826d694'), (x'3e112e0be826d695'), (x'3e112e0be826d696'), (x'3e45798ee2308c39'), (x'3e45798ee2308c3a'), (x'3e45798ee2308c3b'), (x'3e7ad7f29abcaf47'), (x'3e7ad7f29abcaf48'), (x'3e7ad7f29abcaf49'), (x'3eb0c6f7a0b5ed8c'), (x'3eb0c6f7a0b5ed8d'), (x'3eb0c6f7a0b5ed8e'), (x'3ee4f8b588e368ef'), (x'3ee4f8b588e368f0'), (x'3ee4f8b588e368f1'), (x'3f1a36e2eb1c432c'), (x'3f1a36e2eb1c432d'), (x'3f1a36e2eb1c432e'), (x'3f50624dd2f1a9fb'), (x'3f50624dd2f1a9fc'), (x'3f50624dd2f1a9fd'), (x'3f847ae147ae147a'), (x'3f847ae147ae147b'), (x'3f847ae147ae147c'), (x'3fb9999999999999'), (x'3fb999999999999a'), (x'3fb999999999999b'), -- values very close to 1 (x'3feffffffffffff0'), (x'3feffffffffffff1'), (x'3feffffffffffff2'), (x'3feffffffffffff3'), (x'3feffffffffffff4'), (x'3feffffffffffff5'), (x'3feffffffffffff6'), (x'3feffffffffffff7'), (x'3feffffffffffff8'), (x'3feffffffffffff9'), (x'3feffffffffffffa'), (x'3feffffffffffffb'), (x'3feffffffffffffc'), (x'3feffffffffffffd'), (x'3feffffffffffffe'), (x'3fefffffffffffff'), (x'3ff0000000000000'), (x'3ff0000000000001'), (x'3ff0000000000002'), (x'3ff0000000000003'), (x'3ff0000000000004'), (x'3ff0000000000005'), (x'3ff0000000000006'), (x'3ff0000000000007'), (x'3ff0000000000008'), (x'3ff0000000000009'), -- (x'3ff921fb54442d18'), (x'4005bf0a8b14576a'), (x'400921fb54442d18'), -- (x'4023ffffffffffff'), (x'4024000000000000'), (x'4024000000000001'), (x'4058ffffffffffff'), (x'4059000000000000'), (x'4059000000000001'), (x'408f3fffffffffff'), (x'408f400000000000'), (x'408f400000000001'), (x'40c387ffffffffff'), (x'40c3880000000000'), (x'40c3880000000001'), (x'40f869ffffffffff'), (x'40f86a0000000000'), (x'40f86a0000000001'), (x'412e847fffffffff'), (x'412e848000000000'), (x'412e848000000001'), (x'416312cfffffffff'), (x'416312d000000000'), (x'416312d000000001'), (x'4197d783ffffffff'), (x'4197d78400000000'), (x'4197d78400000001'), (x'41cdcd64ffffffff'), (x'41cdcd6500000000'), (x'41cdcd6500000001'), (x'4202a05f1fffffff'), (x'4202a05f20000000'), (x'4202a05f20000001'), (x'42374876e7ffffff'), (x'42374876e8000000'), (x'42374876e8000001'), (x'426d1a94a1ffffff'), (x'426d1a94a2000000'), (x'426d1a94a2000001'), (x'42a2309ce53fffff'), (x'42a2309ce5400000'), (x'42a2309ce5400001'), (x'42d6bcc41e8fffff'), (x'42d6bcc41e900000'), (x'42d6bcc41e900001'), (x'430c6bf52633ffff'), (x'430c6bf526340000'), (x'430c6bf526340001'), (x'4341c37937e07fff'), (x'4341c37937e08000'), (x'4341c37937e08001'), (x'4376345785d89fff'), (x'4376345785d8a000'), (x'4376345785d8a001'), (x'43abc16d674ec7ff'), (x'43abc16d674ec800'), (x'43abc16d674ec801'), (x'43e158e460913cff'), (x'43e158e460913d00'), (x'43e158e460913d01'), (x'4415af1d78b58c3f'), (x'4415af1d78b58c40'), (x'4415af1d78b58c41'), (x'444b1ae4d6e2ef4f'), (x'444b1ae4d6e2ef50'), (x'444b1ae4d6e2ef51'), (x'4480f0cf064dd591'), (x'4480f0cf064dd592'), (x'4480f0cf064dd593'), (x'44b52d02c7e14af5'), (x'44b52d02c7e14af6'), (x'44b52d02c7e14af7'), (x'44ea784379d99db3'), (x'44ea784379d99db4'), (x'44ea784379d99db5'), (x'45208b2a2c280290'), (x'45208b2a2c280291'), (x'45208b2a2c280292'), -- (x'7feffffffffffffe'), (x'7fefffffffffffff'), -- round to even tests (+ve) (x'4350000000000002'), (x'4350000000002e06'), (x'4352000000000003'), (x'4352000000000004'), (x'4358000000000003'), (x'4358000000000004'), (x'435f000000000020'), -- round to even tests (-ve) (x'c350000000000002'), (x'c350000000002e06'), (x'c352000000000003'), (x'c352000000000004'), (x'c358000000000003'), (x'c358000000000004'), (x'c35f000000000020'), -- exercise fixed-point memmoves (x'42dc12218377de66'), (x'42a674e79c5fe51f'), (x'4271f71fb04cb74c'), (x'423cbe991a145879'), (x'4206fee0e1a9e061'), (x'41d26580b487e6b4'), (x'419d6f34540ca453'), (x'41678c29dcd6e9dc'), (x'4132d687e3df217d'), (x'40fe240c9fcb68c8'), (x'40c81cd6e63c53d3'), (x'40934a4584fd0fdc'), (x'405edd3c07fb4c93'), (x'4028b0fcd32f7076'), (x'3ff3c0ca428c59f8'), -- these cases come from the upstream's testsuite -- LotsOfTrailingZeros) (x'3e60000000000000'), -- Regression (x'c352bd2668e077c4'), (x'434018601510c000'), (x'43d055dc36f24000'), (x'43e052961c6f8000'), (x'3ff3c0ca2a5b1d5d'), -- LooksLikePow5 (x'4830f0cf064dd592'), (x'4840f0cf064dd592'), (x'4850f0cf064dd592'), -- OutputLength (x'3ff3333333333333'), (x'3ff3ae147ae147ae'), (x'3ff3be76c8b43958'), (x'3ff3c083126e978d'), (x'3ff3c0c1fc8f3238'), (x'3ff3c0c9539b8887'), (x'3ff3c0ca2a5b1d5d'), (x'3ff3c0ca4283de1b'), (x'3ff3c0ca43db770a'), (x'3ff3c0ca428abd53'), (x'3ff3c0ca428c1d2b'), (x'3ff3c0ca428c51f2'), (x'3ff3c0ca428c58fc'), (x'3ff3c0ca428c59dd'), (x'3ff3c0ca428c59f8'), (x'3ff3c0ca428c59fb'), -- 32-bit chunking (x'40112e0be8047a7d'), (x'40112e0be815a889'), (x'40112e0be826d695'), (x'40112e0be83804a1'), (x'40112e0be84932ad'), -- MinMaxShift (x'0040000000000000'), (x'007fffffffffffff'), (x'0290000000000000'), (x'029fffffffffffff'), (x'4350000000000000'), (x'435fffffffffffff'), (x'1330000000000000'), (x'133fffffffffffff'), (x'3a6fa7161a4d6e0c') ) select float8send(flt) as ibits, flt, flt::text::float8 as r_flt, float8send(flt::text::float8) as obits, float8send(flt::text::float8) = float8send(flt) as correct from (select bits::bigint::xfloat8::float8 as flt from testdata offset 0) s; -- clean up, lest opr_sanity complain drop type xfloat8 cascade; pgFormatter-4.2/t/pg-test-files/sql/foreign_data.sql000066400000000000000000001016431361326045100225470ustar00rootroot00000000000000-- -- Test foreign-data wrapper and server management. -- -- Clean up in case a prior regression run failed -- Suppress NOTICE messages when roles don't exist SET client_min_messages TO 'warning'; DROP ROLE IF EXISTS regress_foreign_data_user, regress_test_role, regress_test_role2, regress_test_role_super, regress_test_indirect, regress_unprivileged_role; RESET client_min_messages; CREATE ROLE regress_foreign_data_user LOGIN SUPERUSER; SET SESSION AUTHORIZATION 'regress_foreign_data_user'; CREATE ROLE regress_test_role; CREATE ROLE regress_test_role2; CREATE ROLE regress_test_role_super SUPERUSER; CREATE ROLE regress_test_indirect; CREATE ROLE regress_unprivileged_role; CREATE FOREIGN DATA WRAPPER dummy; COMMENT ON FOREIGN DATA WRAPPER dummy IS 'useless'; CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator; -- At this point we should have 2 built-in wrappers and no servers. SELECT fdwname, fdwhandler::regproc, fdwvalidator::regproc, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3; SELECT srvname, srvoptions FROM pg_foreign_server; SELECT * FROM pg_user_mapping; -- CREATE FOREIGN DATA WRAPPER CREATE FOREIGN DATA WRAPPER foo VALIDATOR bar; -- ERROR CREATE FOREIGN DATA WRAPPER foo; \dew CREATE FOREIGN DATA WRAPPER foo; -- duplicate DROP FOREIGN DATA WRAPPER foo; CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1'); \dew+ DROP FOREIGN DATA WRAPPER foo; CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1', testing '2'); -- ERROR CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1', another '2'); \dew+ DROP FOREIGN DATA WRAPPER foo; SET ROLE regress_test_role; CREATE FOREIGN DATA WRAPPER foo; -- ERROR RESET ROLE; CREATE FOREIGN DATA WRAPPER foo VALIDATOR postgresql_fdw_validator; \dew+ -- HANDLER related checks CREATE FUNCTION invalid_fdw_handler() RETURNS int LANGUAGE SQL AS 'SELECT 1;'; CREATE FOREIGN DATA WRAPPER test_fdw HANDLER invalid_fdw_handler; -- ERROR CREATE FOREIGN DATA WRAPPER test_fdw HANDLER test_fdw_handler HANDLER invalid_fdw_handler; -- ERROR CREATE FOREIGN DATA WRAPPER test_fdw HANDLER test_fdw_handler; DROP FOREIGN DATA WRAPPER test_fdw; -- ALTER FOREIGN DATA WRAPPER ALTER FOREIGN DATA WRAPPER foo; -- ERROR ALTER FOREIGN DATA WRAPPER foo VALIDATOR bar; -- ERROR ALTER FOREIGN DATA WRAPPER foo NO VALIDATOR; \dew+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '1', b '2'); ALTER FOREIGN DATA WRAPPER foo OPTIONS (SET c '4'); -- ERROR ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP c); -- ERROR ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD x '1', DROP x); \dew+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4'); \dew+ ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '2'); ALTER FOREIGN DATA WRAPPER foo OPTIONS (b '4'); -- ERROR \dew+ SET ROLE regress_test_role; ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5'); -- ERROR SET ROLE regress_test_role_super; ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5'); \dew+ ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_role; -- ERROR ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_role_super; ALTER ROLE regress_test_role_super NOSUPERUSER; SET ROLE regress_test_role_super; ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD e '6'); -- ERROR RESET ROLE; \dew+ ALTER FOREIGN DATA WRAPPER foo RENAME TO foo1; \dew+ ALTER FOREIGN DATA WRAPPER foo1 RENAME TO foo; -- HANDLER related checks ALTER FOREIGN DATA WRAPPER foo HANDLER invalid_fdw_handler; -- ERROR ALTER FOREIGN DATA WRAPPER foo HANDLER test_fdw_handler HANDLER anything; -- ERROR ALTER FOREIGN DATA WRAPPER foo HANDLER test_fdw_handler; DROP FUNCTION invalid_fdw_handler(); -- DROP FOREIGN DATA WRAPPER DROP FOREIGN DATA WRAPPER nonexistent; -- ERROR DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent; \dew+ DROP ROLE regress_test_role_super; -- ERROR SET ROLE regress_test_role_super; DROP FOREIGN DATA WRAPPER foo; RESET ROLE; DROP ROLE regress_test_role_super; \dew+ CREATE FOREIGN DATA WRAPPER foo; CREATE SERVER s1 FOREIGN DATA WRAPPER foo; COMMENT ON SERVER s1 IS 'foreign server'; CREATE USER MAPPING FOR current_user SERVER s1; CREATE USER MAPPING FOR current_user SERVER s1; -- ERROR CREATE USER MAPPING IF NOT EXISTS FOR current_user SERVER s1; -- NOTICE \dew+ \des+ \deu+ DROP FOREIGN DATA WRAPPER foo; -- ERROR SET ROLE regress_test_role; DROP FOREIGN DATA WRAPPER foo CASCADE; -- ERROR RESET ROLE; DROP FOREIGN DATA WRAPPER foo CASCADE; \dew+ \des+ \deu+ -- exercise CREATE SERVER CREATE SERVER s1 FOREIGN DATA WRAPPER foo; -- ERROR CREATE FOREIGN DATA WRAPPER foo OPTIONS ("test wrapper" 'true'); CREATE SERVER s1 FOREIGN DATA WRAPPER foo; CREATE SERVER s1 FOREIGN DATA WRAPPER foo; -- ERROR CREATE SERVER IF NOT EXISTS s1 FOREIGN DATA WRAPPER foo; -- No ERROR, just NOTICE CREATE SERVER s2 FOREIGN DATA WRAPPER foo OPTIONS (host 'a', dbname 'b'); CREATE SERVER s3 TYPE 'oracle' FOREIGN DATA WRAPPER foo; CREATE SERVER s4 TYPE 'oracle' FOREIGN DATA WRAPPER foo OPTIONS (host 'a', dbname 'b'); CREATE SERVER s5 VERSION '15.0' FOREIGN DATA WRAPPER foo; CREATE SERVER s6 VERSION '16.0' FOREIGN DATA WRAPPER foo OPTIONS (host 'a', dbname 'b'); CREATE SERVER s7 TYPE 'oracle' VERSION '17.0' FOREIGN DATA WRAPPER foo OPTIONS (host 'a', dbname 'b'); CREATE SERVER s8 FOREIGN DATA WRAPPER postgresql OPTIONS (foo '1'); -- ERROR CREATE SERVER s8 FOREIGN DATA WRAPPER postgresql OPTIONS (host 'localhost', dbname 's8db'); \des+ SET ROLE regress_test_role; CREATE SERVER t1 FOREIGN DATA WRAPPER foo; -- ERROR: no usage on FDW RESET ROLE; GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role; SET ROLE regress_test_role; CREATE SERVER t1 FOREIGN DATA WRAPPER foo; RESET ROLE; \des+ REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_test_role; GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_indirect; SET ROLE regress_test_role; CREATE SERVER t2 FOREIGN DATA WRAPPER foo; -- ERROR RESET ROLE; GRANT regress_test_indirect TO regress_test_role; SET ROLE regress_test_role; CREATE SERVER t2 FOREIGN DATA WRAPPER foo; \des+ RESET ROLE; REVOKE regress_test_indirect FROM regress_test_role; -- ALTER SERVER ALTER SERVER s0; -- ERROR ALTER SERVER s0 OPTIONS (a '1'); -- ERROR ALTER SERVER s1 VERSION '1.0' OPTIONS (servername 's1'); ALTER SERVER s2 VERSION '1.1'; ALTER SERVER s3 OPTIONS ("tns name" 'orcl', port '1521'); GRANT USAGE ON FOREIGN SERVER s1 TO regress_test_role; GRANT USAGE ON FOREIGN SERVER s6 TO regress_test_role2 WITH GRANT OPTION; \des+ SET ROLE regress_test_role; ALTER SERVER s1 VERSION '1.1'; -- ERROR ALTER SERVER s1 OWNER TO regress_test_role; -- ERROR RESET ROLE; ALTER SERVER s1 OWNER TO regress_test_role; GRANT regress_test_role2 TO regress_test_role; SET ROLE regress_test_role; ALTER SERVER s1 VERSION '1.1'; ALTER SERVER s1 OWNER TO regress_test_role2; -- ERROR RESET ROLE; ALTER SERVER s8 OPTIONS (foo '1'); -- ERROR option validation ALTER SERVER s8 OPTIONS (connect_timeout '30', SET dbname 'db1', DROP host); SET ROLE regress_test_role; ALTER SERVER s1 OWNER TO regress_test_indirect; -- ERROR RESET ROLE; GRANT regress_test_indirect TO regress_test_role; SET ROLE regress_test_role; ALTER SERVER s1 OWNER TO regress_test_indirect; RESET ROLE; GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_indirect; SET ROLE regress_test_role; ALTER SERVER s1 OWNER TO regress_test_indirect; RESET ROLE; DROP ROLE regress_test_indirect; -- ERROR \des+ ALTER SERVER s8 RENAME to s8new; \des+ ALTER SERVER s8new RENAME to s8; -- DROP SERVER DROP SERVER nonexistent; -- ERROR DROP SERVER IF EXISTS nonexistent; \des SET ROLE regress_test_role; DROP SERVER s2; -- ERROR DROP SERVER s1; RESET ROLE; \des ALTER SERVER s2 OWNER TO regress_test_role; SET ROLE regress_test_role; DROP SERVER s2; RESET ROLE; \des CREATE USER MAPPING FOR current_user SERVER s3; \deu DROP SERVER s3; -- ERROR DROP SERVER s3 CASCADE; \des \deu -- CREATE USER MAPPING CREATE USER MAPPING FOR regress_test_missing_role SERVER s1; -- ERROR CREATE USER MAPPING FOR current_user SERVER s1; -- ERROR CREATE USER MAPPING FOR current_user SERVER s4; CREATE USER MAPPING FOR user SERVER s4; -- ERROR duplicate CREATE USER MAPPING FOR public SERVER s4 OPTIONS ("this mapping" 'is public'); CREATE USER MAPPING FOR user SERVER s8 OPTIONS (username 'test', password 'secret'); -- ERROR CREATE USER MAPPING FOR user SERVER s8 OPTIONS (user 'test', password 'secret'); ALTER SERVER s5 OWNER TO regress_test_role; ALTER SERVER s6 OWNER TO regress_test_indirect; SET ROLE regress_test_role; CREATE USER MAPPING FOR current_user SERVER s5; CREATE USER MAPPING FOR current_user SERVER s6 OPTIONS (username 'test'); CREATE USER MAPPING FOR current_user SERVER s7; -- ERROR CREATE USER MAPPING FOR public SERVER s8; -- ERROR RESET ROLE; ALTER SERVER t1 OWNER TO regress_test_indirect; SET ROLE regress_test_role; CREATE USER MAPPING FOR current_user SERVER t1 OPTIONS (username 'bob', password 'boo'); CREATE USER MAPPING FOR public SERVER t1; RESET ROLE; \deu -- ALTER USER MAPPING ALTER USER MAPPING FOR regress_test_missing_role SERVER s4 OPTIONS (gotcha 'true'); -- ERROR ALTER USER MAPPING FOR user SERVER ss4 OPTIONS (gotcha 'true'); -- ERROR ALTER USER MAPPING FOR public SERVER s5 OPTIONS (gotcha 'true'); -- ERROR ALTER USER MAPPING FOR current_user SERVER s8 OPTIONS (username 'test'); -- ERROR ALTER USER MAPPING FOR current_user SERVER s8 OPTIONS (DROP user, SET password 'public'); SET ROLE regress_test_role; ALTER USER MAPPING FOR current_user SERVER s5 OPTIONS (ADD modified '1'); ALTER USER MAPPING FOR public SERVER s4 OPTIONS (ADD modified '1'); -- ERROR ALTER USER MAPPING FOR public SERVER t1 OPTIONS (ADD modified '1'); RESET ROLE; \deu+ -- DROP USER MAPPING DROP USER MAPPING FOR regress_test_missing_role SERVER s4; -- ERROR DROP USER MAPPING FOR user SERVER ss4; DROP USER MAPPING FOR public SERVER s7; -- ERROR DROP USER MAPPING IF EXISTS FOR regress_test_missing_role SERVER s4; DROP USER MAPPING IF EXISTS FOR user SERVER ss4; DROP USER MAPPING IF EXISTS FOR public SERVER s7; CREATE USER MAPPING FOR public SERVER s8; SET ROLE regress_test_role; DROP USER MAPPING FOR public SERVER s8; -- ERROR RESET ROLE; DROP SERVER s7; \deu -- CREATE FOREIGN TABLE CREATE SCHEMA foreign_schema; CREATE SERVER s0 FOREIGN DATA WRAPPER dummy; CREATE FOREIGN TABLE ft1 (); -- ERROR CREATE FOREIGN TABLE ft1 () SERVER no_server; -- ERROR CREATE FOREIGN TABLE ft1 ( c1 integer OPTIONS ("param 1" 'val1') PRIMARY KEY, c2 text OPTIONS (param2 'val2', param3 'val3'), c3 date ) SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value'); -- ERROR CREATE TABLE ref_table (id integer PRIMARY KEY); CREATE FOREIGN TABLE ft1 ( c1 integer OPTIONS ("param 1" 'val1') REFERENCES ref_table (id), c2 text OPTIONS (param2 'val2', param3 'val3'), c3 date ) SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value'); -- ERROR DROP TABLE ref_table; CREATE FOREIGN TABLE ft1 ( c1 integer OPTIONS ("param 1" 'val1') NOT NULL, c2 text OPTIONS (param2 'val2', param3 'val3'), c3 date, UNIQUE (c3) ) SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value'); -- ERROR CREATE FOREIGN TABLE ft1 ( c1 integer OPTIONS ("param 1" 'val1') NOT NULL, c2 text OPTIONS (param2 'val2', param3 'val3') CHECK (c2 <> ''), c3 date, CHECK (c3 BETWEEN '1994-01-01'::date AND '1994-01-31'::date) ) SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value'); COMMENT ON FOREIGN TABLE ft1 IS 'ft1'; COMMENT ON COLUMN ft1.c1 IS 'ft1.c1'; \d+ ft1 \det+ CREATE INDEX id_ft1_c2 ON ft1 (c2); -- ERROR SELECT * FROM ft1; -- ERROR EXPLAIN SELECT * FROM ft1; -- ERROR CREATE TABLE lt1 (a INT) PARTITION BY RANGE (a); CREATE FOREIGN TABLE ft_part1 PARTITION OF lt1 FOR VALUES FROM (0) TO (1000) SERVER s0; CREATE INDEX ON lt1 (a); -- ERROR DROP TABLE lt1; -- ALTER FOREIGN TABLE COMMENT ON FOREIGN TABLE ft1 IS 'foreign table'; COMMENT ON FOREIGN TABLE ft1 IS NULL; COMMENT ON COLUMN ft1.c1 IS 'foreign column'; COMMENT ON COLUMN ft1.c1 IS NULL; ALTER FOREIGN TABLE ft1 ADD COLUMN c4 integer; ALTER FOREIGN TABLE ft1 ADD COLUMN c5 integer DEFAULT 0; ALTER FOREIGN TABLE ft1 ADD COLUMN c6 integer; ALTER FOREIGN TABLE ft1 ADD COLUMN c7 integer NOT NULL; ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer; ALTER FOREIGN TABLE ft1 ADD COLUMN c9 integer; ALTER FOREIGN TABLE ft1 ADD COLUMN c10 integer OPTIONS (p1 'v1'); ALTER FOREIGN TABLE ft1 ALTER COLUMN c4 SET DEFAULT 0; ALTER FOREIGN TABLE ft1 ALTER COLUMN c5 DROP DEFAULT; ALTER FOREIGN TABLE ft1 ALTER COLUMN c6 SET NOT NULL; ALTER FOREIGN TABLE ft1 ALTER COLUMN c7 DROP NOT NULL; ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10) USING '0'; -- ERROR ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10); ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE text; ALTER FOREIGN TABLE ft1 ALTER COLUMN xmin OPTIONS (ADD p1 'v1'); -- ERROR ALTER FOREIGN TABLE ft1 ALTER COLUMN c7 OPTIONS (ADD p1 'v1', ADD p2 'v2'), ALTER COLUMN c8 OPTIONS (ADD p1 'v1', ADD p2 'v2'); ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 OPTIONS (SET p2 'V2', DROP p1); ALTER FOREIGN TABLE ft1 ALTER COLUMN c1 SET STATISTICS 10000; ALTER FOREIGN TABLE ft1 ALTER COLUMN c1 SET (n_distinct = 100); ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET STATISTICS -1; ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET STORAGE PLAIN; \d+ ft1 -- can't change the column type if it's used elsewhere CREATE TABLE use_ft1_column_type (x ft1); ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE integer; -- ERROR DROP TABLE use_ft1_column_type; ALTER FOREIGN TABLE ft1 ADD PRIMARY KEY (c7); -- ERROR ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0) NOT VALID; ALTER FOREIGN TABLE ft1 ALTER CONSTRAINT ft1_c9_check DEFERRABLE; -- ERROR ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c9_check; ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const; -- ERROR ALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const; ALTER FOREIGN TABLE ft1 OWNER TO regress_test_role; ALTER FOREIGN TABLE ft1 OPTIONS (DROP delimiter, SET quote '~', ADD escape '@'); ALTER FOREIGN TABLE ft1 DROP COLUMN no_column; -- ERROR ALTER FOREIGN TABLE ft1 DROP COLUMN IF EXISTS no_column; ALTER FOREIGN TABLE ft1 DROP COLUMN c9; ALTER FOREIGN TABLE ft1 SET SCHEMA foreign_schema; ALTER FOREIGN TABLE ft1 SET TABLESPACE ts; -- ERROR ALTER FOREIGN TABLE foreign_schema.ft1 RENAME c1 TO foreign_column_1; ALTER FOREIGN TABLE foreign_schema.ft1 RENAME TO foreign_table_1; \d foreign_schema.foreign_table_1 -- alter noexisting table ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c4 integer; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c6 integer; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c7 integer NOT NULL; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c8 integer; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c9 integer; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c10 integer OPTIONS (p1 'v1'); ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ALTER COLUMN c6 SET NOT NULL; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ALTER COLUMN c7 DROP NOT NULL; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ALTER COLUMN c8 TYPE char(10); ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ALTER COLUMN c8 SET DATA TYPE text; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ALTER COLUMN c7 OPTIONS (ADD p1 'v1', ADD p2 'v2'), ALTER COLUMN c8 OPTIONS (ADD p1 'v1', ADD p2 'v2'); ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ALTER COLUMN c8 OPTIONS (SET p2 'V2', DROP p1); ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP CONSTRAINT IF EXISTS no_const; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP CONSTRAINT ft1_c1_check; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 OWNER TO regress_test_role; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 OPTIONS (DROP delimiter, SET quote '~', ADD escape '@'); ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP COLUMN IF EXISTS no_column; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP COLUMN c9; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 SET SCHEMA foreign_schema; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 RENAME c1 TO foreign_column_1; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 RENAME TO foreign_table_1; -- Information schema SELECT * FROM information_schema.foreign_data_wrappers ORDER BY 1, 2; SELECT * FROM information_schema.foreign_data_wrapper_options ORDER BY 1, 2, 3; SELECT * FROM information_schema.foreign_servers ORDER BY 1, 2; SELECT * FROM information_schema.foreign_server_options ORDER BY 1, 2, 3; SELECT * FROM information_schema.user_mappings ORDER BY lower(authorization_identifier), 2, 3; SELECT * FROM information_schema.user_mapping_options ORDER BY lower(authorization_identifier), 2, 3, 4; SELECT * FROM information_schema.usage_privileges WHERE object_type LIKE 'FOREIGN%' AND object_name IN ('s6', 'foo') ORDER BY 1, 2, 3, 4, 5; SELECT * FROM information_schema.role_usage_grants WHERE object_type LIKE 'FOREIGN%' AND object_name IN ('s6', 'foo') ORDER BY 1, 2, 3, 4, 5; SELECT * FROM information_schema.foreign_tables ORDER BY 1, 2, 3; SELECT * FROM information_schema.foreign_table_options ORDER BY 1, 2, 3, 4; SET ROLE regress_test_role; SELECT * FROM information_schema.user_mapping_options ORDER BY 1, 2, 3, 4; SELECT * FROM information_schema.usage_privileges WHERE object_type LIKE 'FOREIGN%' AND object_name IN ('s6', 'foo') ORDER BY 1, 2, 3, 4, 5; SELECT * FROM information_schema.role_usage_grants WHERE object_type LIKE 'FOREIGN%' AND object_name IN ('s6', 'foo') ORDER BY 1, 2, 3, 4, 5; DROP USER MAPPING FOR current_user SERVER t1; SET ROLE regress_test_role2; SELECT * FROM information_schema.user_mapping_options ORDER BY 1, 2, 3, 4; RESET ROLE; -- has_foreign_data_wrapper_privilege SELECT has_foreign_data_wrapper_privilege('regress_test_role', (SELECT oid FROM pg_foreign_data_wrapper WHERE fdwname='foo'), 'USAGE'); SELECT has_foreign_data_wrapper_privilege('regress_test_role', 'foo', 'USAGE'); SELECT has_foreign_data_wrapper_privilege( (SELECT oid FROM pg_roles WHERE rolname='regress_test_role'), (SELECT oid FROM pg_foreign_data_wrapper WHERE fdwname='foo'), 'USAGE'); SELECT has_foreign_data_wrapper_privilege( (SELECT oid FROM pg_foreign_data_wrapper WHERE fdwname='foo'), 'USAGE'); SELECT has_foreign_data_wrapper_privilege( (SELECT oid FROM pg_roles WHERE rolname='regress_test_role'), 'foo', 'USAGE'); SELECT has_foreign_data_wrapper_privilege('foo', 'USAGE'); GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role; SELECT has_foreign_data_wrapper_privilege('regress_test_role', 'foo', 'USAGE'); -- has_server_privilege SELECT has_server_privilege('regress_test_role', (SELECT oid FROM pg_foreign_server WHERE srvname='s8'), 'USAGE'); SELECT has_server_privilege('regress_test_role', 's8', 'USAGE'); SELECT has_server_privilege( (SELECT oid FROM pg_roles WHERE rolname='regress_test_role'), (SELECT oid FROM pg_foreign_server WHERE srvname='s8'), 'USAGE'); SELECT has_server_privilege( (SELECT oid FROM pg_foreign_server WHERE srvname='s8'), 'USAGE'); SELECT has_server_privilege( (SELECT oid FROM pg_roles WHERE rolname='regress_test_role'), 's8', 'USAGE'); SELECT has_server_privilege('s8', 'USAGE'); GRANT USAGE ON FOREIGN SERVER s8 TO regress_test_role; SELECT has_server_privilege('regress_test_role', 's8', 'USAGE'); REVOKE USAGE ON FOREIGN SERVER s8 FROM regress_test_role; GRANT USAGE ON FOREIGN SERVER s4 TO regress_test_role; DROP USER MAPPING FOR public SERVER s4; ALTER SERVER s6 OPTIONS (DROP host, DROP dbname); ALTER USER MAPPING FOR regress_test_role SERVER s6 OPTIONS (DROP username); ALTER FOREIGN DATA WRAPPER foo VALIDATOR postgresql_fdw_validator; -- Privileges SET ROLE regress_unprivileged_role; CREATE FOREIGN DATA WRAPPER foobar; -- ERROR ALTER FOREIGN DATA WRAPPER foo OPTIONS (gotcha 'true'); -- ERROR ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_unprivileged_role; -- ERROR DROP FOREIGN DATA WRAPPER foo; -- ERROR GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role; -- ERROR CREATE SERVER s9 FOREIGN DATA WRAPPER foo; -- ERROR ALTER SERVER s4 VERSION '0.5'; -- ERROR ALTER SERVER s4 OWNER TO regress_unprivileged_role; -- ERROR DROP SERVER s4; -- ERROR GRANT USAGE ON FOREIGN SERVER s4 TO regress_test_role; -- ERROR CREATE USER MAPPING FOR public SERVER s4; -- ERROR ALTER USER MAPPING FOR regress_test_role SERVER s6 OPTIONS (gotcha 'true'); -- ERROR DROP USER MAPPING FOR regress_test_role SERVER s6; -- ERROR RESET ROLE; GRANT USAGE ON FOREIGN DATA WRAPPER postgresql TO regress_unprivileged_role; GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_unprivileged_role WITH GRANT OPTION; SET ROLE regress_unprivileged_role; CREATE FOREIGN DATA WRAPPER foobar; -- ERROR ALTER FOREIGN DATA WRAPPER foo OPTIONS (gotcha 'true'); -- ERROR DROP FOREIGN DATA WRAPPER foo; -- ERROR GRANT USAGE ON FOREIGN DATA WRAPPER postgresql TO regress_test_role; -- WARNING GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role; CREATE SERVER s9 FOREIGN DATA WRAPPER postgresql; ALTER SERVER s6 VERSION '0.5'; -- ERROR DROP SERVER s6; -- ERROR GRANT USAGE ON FOREIGN SERVER s6 TO regress_test_role; -- ERROR GRANT USAGE ON FOREIGN SERVER s9 TO regress_test_role; CREATE USER MAPPING FOR public SERVER s6; -- ERROR CREATE USER MAPPING FOR public SERVER s9; ALTER USER MAPPING FOR regress_test_role SERVER s6 OPTIONS (gotcha 'true'); -- ERROR DROP USER MAPPING FOR regress_test_role SERVER s6; -- ERROR RESET ROLE; REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_unprivileged_role; -- ERROR REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_unprivileged_role CASCADE; SET ROLE regress_unprivileged_role; GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role; -- ERROR CREATE SERVER s10 FOREIGN DATA WRAPPER foo; -- ERROR ALTER SERVER s9 VERSION '1.1'; GRANT USAGE ON FOREIGN SERVER s9 TO regress_test_role; CREATE USER MAPPING FOR current_user SERVER s9; DROP SERVER s9 CASCADE; RESET ROLE; CREATE SERVER s9 FOREIGN DATA WRAPPER foo; GRANT USAGE ON FOREIGN SERVER s9 TO regress_unprivileged_role; SET ROLE regress_unprivileged_role; ALTER SERVER s9 VERSION '1.2'; -- ERROR GRANT USAGE ON FOREIGN SERVER s9 TO regress_test_role; -- WARNING CREATE USER MAPPING FOR current_user SERVER s9; DROP SERVER s9 CASCADE; -- ERROR -- Check visibility of user mapping data SET ROLE regress_test_role; CREATE SERVER s10 FOREIGN DATA WRAPPER foo; CREATE USER MAPPING FOR public SERVER s10 OPTIONS (user 'secret'); CREATE USER MAPPING FOR regress_unprivileged_role SERVER s10 OPTIONS (user 'secret'); -- owner of server can see some option fields \deu+ RESET ROLE; -- superuser can see all option fields \deu+ -- unprivileged user cannot see any option field SET ROLE regress_unprivileged_role; \deu+ RESET ROLE; DROP SERVER s10 CASCADE; -- Triggers CREATE FUNCTION dummy_trigger() RETURNS TRIGGER AS $$ BEGIN RETURN NULL; END $$ language plpgsql; CREATE TRIGGER trigtest_before_stmt BEFORE INSERT OR UPDATE OR DELETE ON foreign_schema.foreign_table_1 FOR EACH STATEMENT EXECUTE PROCEDURE dummy_trigger(); CREATE TRIGGER trigtest_after_stmt AFTER INSERT OR UPDATE OR DELETE ON foreign_schema.foreign_table_1 FOR EACH STATEMENT EXECUTE PROCEDURE dummy_trigger(); CREATE TRIGGER trigtest_after_stmt_tt AFTER INSERT OR UPDATE OR DELETE -- ERROR ON foreign_schema.foreign_table_1 REFERENCING NEW TABLE AS new_table FOR EACH STATEMENT EXECUTE PROCEDURE dummy_trigger(); CREATE TRIGGER trigtest_before_row BEFORE INSERT OR UPDATE OR DELETE ON foreign_schema.foreign_table_1 FOR EACH ROW EXECUTE PROCEDURE dummy_trigger(); CREATE TRIGGER trigtest_after_row AFTER INSERT OR UPDATE OR DELETE ON foreign_schema.foreign_table_1 FOR EACH ROW EXECUTE PROCEDURE dummy_trigger(); CREATE CONSTRAINT TRIGGER trigtest_constraint AFTER INSERT OR UPDATE OR DELETE ON foreign_schema.foreign_table_1 FOR EACH ROW EXECUTE PROCEDURE dummy_trigger(); ALTER FOREIGN TABLE foreign_schema.foreign_table_1 DISABLE TRIGGER trigtest_before_stmt; ALTER FOREIGN TABLE foreign_schema.foreign_table_1 ENABLE TRIGGER trigtest_before_stmt; DROP TRIGGER trigtest_before_stmt ON foreign_schema.foreign_table_1; DROP TRIGGER trigtest_before_row ON foreign_schema.foreign_table_1; DROP TRIGGER trigtest_after_stmt ON foreign_schema.foreign_table_1; DROP TRIGGER trigtest_after_row ON foreign_schema.foreign_table_1; DROP FUNCTION dummy_trigger(); -- Table inheritance CREATE TABLE fd_pt1 ( c1 integer NOT NULL, c2 text, c3 date ); CREATE FOREIGN TABLE ft2 () INHERITS (fd_pt1) SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value'); \d+ fd_pt1 \d+ ft2 DROP FOREIGN TABLE ft2; \d+ fd_pt1 CREATE FOREIGN TABLE ft2 ( c1 integer NOT NULL, c2 text, c3 date ) SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value'); \d+ ft2 ALTER FOREIGN TABLE ft2 INHERIT fd_pt1; \d+ fd_pt1 \d+ ft2 CREATE TABLE ct3() INHERITS(ft2); CREATE FOREIGN TABLE ft3 ( c1 integer NOT NULL, c2 text, c3 date ) INHERITS(ft2) SERVER s0; \d+ ft2 \d+ ct3 \d+ ft3 -- add attributes recursively ALTER TABLE fd_pt1 ADD COLUMN c4 integer; ALTER TABLE fd_pt1 ADD COLUMN c5 integer DEFAULT 0; ALTER TABLE fd_pt1 ADD COLUMN c6 integer; ALTER TABLE fd_pt1 ADD COLUMN c7 integer NOT NULL; ALTER TABLE fd_pt1 ADD COLUMN c8 integer; \d+ fd_pt1 \d+ ft2 \d+ ct3 \d+ ft3 -- alter attributes recursively ALTER TABLE fd_pt1 ALTER COLUMN c4 SET DEFAULT 0; ALTER TABLE fd_pt1 ALTER COLUMN c5 DROP DEFAULT; ALTER TABLE fd_pt1 ALTER COLUMN c6 SET NOT NULL; ALTER TABLE fd_pt1 ALTER COLUMN c7 DROP NOT NULL; ALTER TABLE fd_pt1 ALTER COLUMN c8 TYPE char(10) USING '0'; -- ERROR ALTER TABLE fd_pt1 ALTER COLUMN c8 TYPE char(10); ALTER TABLE fd_pt1 ALTER COLUMN c8 SET DATA TYPE text; ALTER TABLE fd_pt1 ALTER COLUMN c1 SET STATISTICS 10000; ALTER TABLE fd_pt1 ALTER COLUMN c1 SET (n_distinct = 100); ALTER TABLE fd_pt1 ALTER COLUMN c8 SET STATISTICS -1; ALTER TABLE fd_pt1 ALTER COLUMN c8 SET STORAGE EXTERNAL; \d+ fd_pt1 \d+ ft2 -- drop attributes recursively ALTER TABLE fd_pt1 DROP COLUMN c4; ALTER TABLE fd_pt1 DROP COLUMN c5; ALTER TABLE fd_pt1 DROP COLUMN c6; ALTER TABLE fd_pt1 DROP COLUMN c7; ALTER TABLE fd_pt1 DROP COLUMN c8; \d+ fd_pt1 \d+ ft2 -- add constraints recursively ALTER TABLE fd_pt1 ADD CONSTRAINT fd_pt1chk1 CHECK (c1 > 0) NO INHERIT; ALTER TABLE fd_pt1 ADD CONSTRAINT fd_pt1chk2 CHECK (c2 <> ''); -- connoinherit should be true for NO INHERIT constraint SELECT relname, conname, contype, conislocal, coninhcount, connoinherit FROM pg_class AS pc JOIN pg_constraint AS pgc ON (conrelid = pc.oid) WHERE pc.relname = 'fd_pt1' ORDER BY 1,2; -- child does not inherit NO INHERIT constraints \d+ fd_pt1 \d+ ft2 DROP FOREIGN TABLE ft2; -- ERROR DROP FOREIGN TABLE ft2 CASCADE; CREATE FOREIGN TABLE ft2 ( c1 integer NOT NULL, c2 text, c3 date ) SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value'); -- child must have parent's INHERIT constraints ALTER FOREIGN TABLE ft2 INHERIT fd_pt1; -- ERROR ALTER FOREIGN TABLE ft2 ADD CONSTRAINT fd_pt1chk2 CHECK (c2 <> ''); ALTER FOREIGN TABLE ft2 INHERIT fd_pt1; -- child does not inherit NO INHERIT constraints \d+ fd_pt1 \d+ ft2 -- drop constraints recursively ALTER TABLE fd_pt1 DROP CONSTRAINT fd_pt1chk1 CASCADE; ALTER TABLE fd_pt1 DROP CONSTRAINT fd_pt1chk2 CASCADE; -- NOT VALID case INSERT INTO fd_pt1 VALUES (1, 'fd_pt1'::text, '1994-01-01'::date); ALTER TABLE fd_pt1 ADD CONSTRAINT fd_pt1chk3 CHECK (c2 <> '') NOT VALID; \d+ fd_pt1 \d+ ft2 -- VALIDATE CONSTRAINT need do nothing on foreign tables ALTER TABLE fd_pt1 VALIDATE CONSTRAINT fd_pt1chk3; \d+ fd_pt1 \d+ ft2 -- changes name of an attribute recursively ALTER TABLE fd_pt1 RENAME COLUMN c1 TO f1; ALTER TABLE fd_pt1 RENAME COLUMN c2 TO f2; ALTER TABLE fd_pt1 RENAME COLUMN c3 TO f3; -- changes name of a constraint recursively ALTER TABLE fd_pt1 RENAME CONSTRAINT fd_pt1chk3 TO f2_check; \d+ fd_pt1 \d+ ft2 -- TRUNCATE doesn't work on foreign tables, either directly or recursively TRUNCATE ft2; -- ERROR TRUNCATE fd_pt1; -- ERROR DROP TABLE fd_pt1 CASCADE; -- IMPORT FOREIGN SCHEMA IMPORT FOREIGN SCHEMA s1 FROM SERVER s9 INTO public; -- ERROR IMPORT FOREIGN SCHEMA s1 LIMIT TO (t1) FROM SERVER s9 INTO public; --ERROR IMPORT FOREIGN SCHEMA s1 EXCEPT (t1) FROM SERVER s9 INTO public; -- ERROR IMPORT FOREIGN SCHEMA s1 EXCEPT (t1, t2) FROM SERVER s9 INTO public OPTIONS (option1 'value1', option2 'value2'); -- ERROR -- DROP FOREIGN TABLE DROP FOREIGN TABLE no_table; -- ERROR DROP FOREIGN TABLE IF EXISTS no_table; DROP FOREIGN TABLE foreign_schema.foreign_table_1; -- REASSIGN OWNED/DROP OWNED of foreign objects REASSIGN OWNED BY regress_test_role TO regress_test_role2; DROP OWNED BY regress_test_role2; DROP OWNED BY regress_test_role2 CASCADE; -- Foreign partition DDL stuff CREATE TABLE fd_pt2 ( c1 integer NOT NULL, c2 text, c3 date ) PARTITION BY LIST (c1); CREATE FOREIGN TABLE fd_pt2_1 PARTITION OF fd_pt2 FOR VALUES IN (1) SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value'); \d+ fd_pt2 \d+ fd_pt2_1 -- partition cannot have additional columns DROP FOREIGN TABLE fd_pt2_1; CREATE FOREIGN TABLE fd_pt2_1 ( c1 integer NOT NULL, c2 text, c3 date, c4 char ) SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value'); \d+ fd_pt2_1 ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); -- ERROR DROP FOREIGN TABLE fd_pt2_1; \d+ fd_pt2 CREATE FOREIGN TABLE fd_pt2_1 ( c1 integer NOT NULL, c2 text, c3 date ) SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value'); \d+ fd_pt2_1 -- no attach partition validation occurs for foreign tables ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); \d+ fd_pt2 \d+ fd_pt2_1 -- cannot add column to a partition ALTER TABLE fd_pt2_1 ADD c4 char; -- ok to have a partition's own constraints though ALTER TABLE fd_pt2_1 ALTER c3 SET NOT NULL; ALTER TABLE fd_pt2_1 ADD CONSTRAINT p21chk CHECK (c2 <> ''); \d+ fd_pt2 \d+ fd_pt2_1 -- cannot drop inherited NOT NULL constraint from a partition ALTER TABLE fd_pt2_1 ALTER c1 DROP NOT NULL; -- partition must have parent's constraints ALTER TABLE fd_pt2 DETACH PARTITION fd_pt2_1; ALTER TABLE fd_pt2 ALTER c2 SET NOT NULL; \d+ fd_pt2 \d+ fd_pt2_1 ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); -- ERROR ALTER FOREIGN TABLE fd_pt2_1 ALTER c2 SET NOT NULL; ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); ALTER TABLE fd_pt2 DETACH PARTITION fd_pt2_1; ALTER TABLE fd_pt2 ADD CONSTRAINT fd_pt2chk1 CHECK (c1 > 0); \d+ fd_pt2 \d+ fd_pt2_1 ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); -- ERROR ALTER FOREIGN TABLE fd_pt2_1 ADD CONSTRAINT fd_pt2chk1 CHECK (c1 > 0); ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1); -- TRUNCATE doesn't work on foreign tables, either directly or recursively TRUNCATE fd_pt2_1; -- ERROR TRUNCATE fd_pt2; -- ERROR DROP FOREIGN TABLE fd_pt2_1; DROP TABLE fd_pt2; -- foreign table cannot be part of partition tree made of temporary -- relations. CREATE TEMP TABLE temp_parted (a int) PARTITION BY LIST (a); CREATE FOREIGN TABLE foreign_part PARTITION OF temp_parted DEFAULT SERVER s0; -- ERROR CREATE FOREIGN TABLE foreign_part (a int) SERVER s0; ALTER TABLE temp_parted ATTACH PARTITION foreign_part DEFAULT; -- ERROR DROP FOREIGN TABLE foreign_part; DROP TABLE temp_parted; -- Cleanup DROP SCHEMA foreign_schema CASCADE; DROP ROLE regress_test_role; -- ERROR DROP SERVER t1 CASCADE; DROP USER MAPPING FOR regress_test_role SERVER s6; DROP FOREIGN DATA WRAPPER foo CASCADE; DROP SERVER s8 CASCADE; DROP ROLE regress_test_indirect; DROP ROLE regress_test_role; DROP ROLE regress_unprivileged_role; -- ERROR REVOKE ALL ON FOREIGN DATA WRAPPER postgresql FROM regress_unprivileged_role; DROP ROLE regress_unprivileged_role; DROP ROLE regress_test_role2; DROP FOREIGN DATA WRAPPER postgresql CASCADE; DROP FOREIGN DATA WRAPPER dummy CASCADE; \c DROP ROLE regress_foreign_data_user; -- At this point we should have no wrappers, no servers, and no mappings. SELECT fdwname, fdwhandler, fdwvalidator, fdwoptions FROM pg_foreign_data_wrapper; SELECT srvname, srvoptions FROM pg_foreign_server; SELECT * FROM pg_user_mapping; pgFormatter-4.2/t/pg-test-files/sql/foreign_key.sql000066400000000000000000001656571361326045100224450ustar00rootroot00000000000000-- -- FOREIGN KEY -- -- MATCH FULL -- -- First test, check and cascade -- CREATE TABLE PKTABLE ( ptest1 int PRIMARY KEY, ptest2 text ); CREATE TABLE FKTABLE ( ftest1 int REFERENCES PKTABLE MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int ); -- Insert test data into PKTABLE INSERT INTO PKTABLE VALUES (1, 'Test1'); INSERT INTO PKTABLE VALUES (2, 'Test2'); INSERT INTO PKTABLE VALUES (3, 'Test3'); INSERT INTO PKTABLE VALUES (4, 'Test4'); INSERT INTO PKTABLE VALUES (5, 'Test5'); -- Insert successful rows into FK TABLE INSERT INTO FKTABLE VALUES (1, 2); INSERT INTO FKTABLE VALUES (2, 3); INSERT INTO FKTABLE VALUES (3, 4); INSERT INTO FKTABLE VALUES (NULL, 1); -- Insert a failed row into FK TABLE INSERT INTO FKTABLE VALUES (100, 2); -- Check FKTABLE SELECT * FROM FKTABLE; -- Delete a row from PK TABLE DELETE FROM PKTABLE WHERE ptest1=1; -- Check FKTABLE for removal of matched row SELECT * FROM FKTABLE; -- Update a row from PK TABLE UPDATE PKTABLE SET ptest1=1 WHERE ptest1=2; -- Check FKTABLE for update of matched row SELECT * FROM FKTABLE; DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- -- check set NULL and table constraint on multiple columns -- CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 text, PRIMARY KEY(ptest1, ptest2) ); CREATE TABLE FKTABLE ( ftest1 int, ftest2 int, ftest3 int, CONSTRAINT constrname FOREIGN KEY(ftest1, ftest2) REFERENCES PKTABLE MATCH FULL ON DELETE SET NULL ON UPDATE SET NULL); -- Test comments COMMENT ON CONSTRAINT constrname_wrong ON FKTABLE IS 'fk constraint comment'; COMMENT ON CONSTRAINT constrname ON FKTABLE IS 'fk constraint comment'; COMMENT ON CONSTRAINT constrname ON FKTABLE IS NULL; -- Insert test data into PKTABLE INSERT INTO PKTABLE VALUES (1, 2, 'Test1'); INSERT INTO PKTABLE VALUES (1, 3, 'Test1-2'); INSERT INTO PKTABLE VALUES (2, 4, 'Test2'); INSERT INTO PKTABLE VALUES (3, 6, 'Test3'); INSERT INTO PKTABLE VALUES (4, 8, 'Test4'); INSERT INTO PKTABLE VALUES (5, 10, 'Test5'); -- Insert successful rows into FK TABLE INSERT INTO FKTABLE VALUES (1, 2, 4); INSERT INTO FKTABLE VALUES (1, 3, 5); INSERT INTO FKTABLE VALUES (2, 4, 8); INSERT INTO FKTABLE VALUES (3, 6, 12); INSERT INTO FKTABLE VALUES (NULL, NULL, 0); -- Insert failed rows into FK TABLE INSERT INTO FKTABLE VALUES (100, 2, 4); INSERT INTO FKTABLE VALUES (2, 2, 4); INSERT INTO FKTABLE VALUES (NULL, 2, 4); INSERT INTO FKTABLE VALUES (1, NULL, 4); -- Check FKTABLE SELECT * FROM FKTABLE; -- Delete a row from PK TABLE DELETE FROM PKTABLE WHERE ptest1=1 and ptest2=2; -- Check FKTABLE for removal of matched row SELECT * FROM FKTABLE; -- Delete another row from PK TABLE DELETE FROM PKTABLE WHERE ptest1=5 and ptest2=10; -- Check FKTABLE (should be no change) SELECT * FROM FKTABLE; -- Update a row from PK TABLE UPDATE PKTABLE SET ptest1=1 WHERE ptest1=2; -- Check FKTABLE for update of matched row SELECT * FROM FKTABLE; -- Check update with part of key null UPDATE FKTABLE SET ftest1 = NULL WHERE ftest1 = 1; -- Check update with old and new key values equal UPDATE FKTABLE SET ftest1 = 1 WHERE ftest1 = 1; -- Try altering the column type where foreign keys are involved ALTER TABLE PKTABLE ALTER COLUMN ptest1 TYPE bigint; ALTER TABLE FKTABLE ALTER COLUMN ftest1 TYPE bigint; SELECT * FROM PKTABLE; SELECT * FROM FKTABLE; DROP TABLE PKTABLE CASCADE; DROP TABLE FKTABLE; -- -- check set default and table constraint on multiple columns -- CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 text, PRIMARY KEY(ptest1, ptest2) ); CREATE TABLE FKTABLE ( ftest1 int DEFAULT -1, ftest2 int DEFAULT -2, ftest3 int, CONSTRAINT constrname2 FOREIGN KEY(ftest1, ftest2) REFERENCES PKTABLE MATCH FULL ON DELETE SET DEFAULT ON UPDATE SET DEFAULT); -- Insert a value in PKTABLE for default INSERT INTO PKTABLE VALUES (-1, -2, 'The Default!'); -- Insert test data into PKTABLE INSERT INTO PKTABLE VALUES (1, 2, 'Test1'); INSERT INTO PKTABLE VALUES (1, 3, 'Test1-2'); INSERT INTO PKTABLE VALUES (2, 4, 'Test2'); INSERT INTO PKTABLE VALUES (3, 6, 'Test3'); INSERT INTO PKTABLE VALUES (4, 8, 'Test4'); INSERT INTO PKTABLE VALUES (5, 10, 'Test5'); -- Insert successful rows into FK TABLE INSERT INTO FKTABLE VALUES (1, 2, 4); INSERT INTO FKTABLE VALUES (1, 3, 5); INSERT INTO FKTABLE VALUES (2, 4, 8); INSERT INTO FKTABLE VALUES (3, 6, 12); INSERT INTO FKTABLE VALUES (NULL, NULL, 0); -- Insert failed rows into FK TABLE INSERT INTO FKTABLE VALUES (100, 2, 4); INSERT INTO FKTABLE VALUES (2, 2, 4); INSERT INTO FKTABLE VALUES (NULL, 2, 4); INSERT INTO FKTABLE VALUES (1, NULL, 4); -- Check FKTABLE SELECT * FROM FKTABLE; -- Delete a row from PK TABLE DELETE FROM PKTABLE WHERE ptest1=1 and ptest2=2; -- Check FKTABLE to check for removal SELECT * FROM FKTABLE; -- Delete another row from PK TABLE DELETE FROM PKTABLE WHERE ptest1=5 and ptest2=10; -- Check FKTABLE (should be no change) SELECT * FROM FKTABLE; -- Update a row from PK TABLE UPDATE PKTABLE SET ptest1=1 WHERE ptest1=2; -- Check FKTABLE for update of matched row SELECT * FROM FKTABLE; -- this should fail for lack of CASCADE DROP TABLE PKTABLE; DROP TABLE PKTABLE CASCADE; DROP TABLE FKTABLE; -- -- First test, check with no on delete or on update -- CREATE TABLE PKTABLE ( ptest1 int PRIMARY KEY, ptest2 text ); CREATE TABLE FKTABLE ( ftest1 int REFERENCES PKTABLE MATCH FULL, ftest2 int ); -- Insert test data into PKTABLE INSERT INTO PKTABLE VALUES (1, 'Test1'); INSERT INTO PKTABLE VALUES (2, 'Test2'); INSERT INTO PKTABLE VALUES (3, 'Test3'); INSERT INTO PKTABLE VALUES (4, 'Test4'); INSERT INTO PKTABLE VALUES (5, 'Test5'); -- Insert successful rows into FK TABLE INSERT INTO FKTABLE VALUES (1, 2); INSERT INTO FKTABLE VALUES (2, 3); INSERT INTO FKTABLE VALUES (3, 4); INSERT INTO FKTABLE VALUES (NULL, 1); -- Insert a failed row into FK TABLE INSERT INTO FKTABLE VALUES (100, 2); -- Check FKTABLE SELECT * FROM FKTABLE; -- Check PKTABLE SELECT * FROM PKTABLE; -- Delete a row from PK TABLE (should fail) DELETE FROM PKTABLE WHERE ptest1=1; -- Delete a row from PK TABLE (should succeed) DELETE FROM PKTABLE WHERE ptest1=5; -- Check PKTABLE for deletes SELECT * FROM PKTABLE; -- Update a row from PK TABLE (should fail) UPDATE PKTABLE SET ptest1=0 WHERE ptest1=2; -- Update a row from PK TABLE (should succeed) UPDATE PKTABLE SET ptest1=0 WHERE ptest1=4; -- Check PKTABLE for updates SELECT * FROM PKTABLE; DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- -- Check initial check upon ALTER TABLE -- CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, PRIMARY KEY(ptest1, ptest2) ); CREATE TABLE FKTABLE ( ftest1 int, ftest2 int ); INSERT INTO PKTABLE VALUES (1, 2); INSERT INTO FKTABLE VALUES (1, NULL); ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) REFERENCES PKTABLE MATCH FULL; DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- MATCH SIMPLE -- Base test restricting update/delete CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) ); CREATE TABLE FKTABLE ( ftest1 int, ftest2 int, ftest3 int, ftest4 int, CONSTRAINT constrname3 FOREIGN KEY(ftest1, ftest2, ftest3) REFERENCES PKTABLE); -- Insert Primary Key values INSERT INTO PKTABLE VALUES (1, 2, 3, 'test1'); INSERT INTO PKTABLE VALUES (1, 3, 3, 'test2'); INSERT INTO PKTABLE VALUES (2, 3, 4, 'test3'); INSERT INTO PKTABLE VALUES (2, 4, 5, 'test4'); -- Insert Foreign Key values INSERT INTO FKTABLE VALUES (1, 2, 3, 1); INSERT INTO FKTABLE VALUES (NULL, 2, 3, 2); INSERT INTO FKTABLE VALUES (2, NULL, 3, 3); INSERT INTO FKTABLE VALUES (NULL, 2, 7, 4); INSERT INTO FKTABLE VALUES (NULL, 3, 4, 5); -- Insert a failed values INSERT INTO FKTABLE VALUES (1, 2, 7, 6); -- Show FKTABLE SELECT * from FKTABLE; -- Try to update something that should fail UPDATE PKTABLE set ptest2=5 where ptest2=2; -- Try to update something that should succeed UPDATE PKTABLE set ptest1=1 WHERE ptest2=3; -- Try to delete something that should fail DELETE FROM PKTABLE where ptest1=1 and ptest2=2 and ptest3=3; -- Try to delete something that should work DELETE FROM PKTABLE where ptest1=2; -- Show PKTABLE and FKTABLE SELECT * from PKTABLE; SELECT * from FKTABLE; DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- restrict with null values CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, UNIQUE(ptest1, ptest2, ptest3) ); CREATE TABLE FKTABLE ( ftest1 int, ftest2 int, ftest3 int, ftest4 int, CONSTRAINT constrname3 FOREIGN KEY(ftest1, ftest2, ftest3) REFERENCES PKTABLE (ptest1, ptest2, ptest3)); INSERT INTO PKTABLE VALUES (1, 2, 3, 'test1'); INSERT INTO PKTABLE VALUES (1, 3, NULL, 'test2'); INSERT INTO PKTABLE VALUES (2, NULL, 4, 'test3'); INSERT INTO FKTABLE VALUES (1, 2, 3, 1); DELETE FROM PKTABLE WHERE ptest1 = 2; SELECT * FROM PKTABLE; SELECT * FROM FKTABLE; DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- cascade update/delete CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) ); CREATE TABLE FKTABLE ( ftest1 int, ftest2 int, ftest3 int, ftest4 int, CONSTRAINT constrname3 FOREIGN KEY(ftest1, ftest2, ftest3) REFERENCES PKTABLE ON DELETE CASCADE ON UPDATE CASCADE); -- Insert Primary Key values INSERT INTO PKTABLE VALUES (1, 2, 3, 'test1'); INSERT INTO PKTABLE VALUES (1, 3, 3, 'test2'); INSERT INTO PKTABLE VALUES (2, 3, 4, 'test3'); INSERT INTO PKTABLE VALUES (2, 4, 5, 'test4'); -- Insert Foreign Key values INSERT INTO FKTABLE VALUES (1, 2, 3, 1); INSERT INTO FKTABLE VALUES (NULL, 2, 3, 2); INSERT INTO FKTABLE VALUES (2, NULL, 3, 3); INSERT INTO FKTABLE VALUES (NULL, 2, 7, 4); INSERT INTO FKTABLE VALUES (NULL, 3, 4, 5); -- Insert a failed values INSERT INTO FKTABLE VALUES (1, 2, 7, 6); -- Show FKTABLE SELECT * from FKTABLE; -- Try to update something that will cascade UPDATE PKTABLE set ptest2=5 where ptest2=2; -- Try to update something that should not cascade UPDATE PKTABLE set ptest1=1 WHERE ptest2=3; -- Show PKTABLE and FKTABLE SELECT * from PKTABLE; SELECT * from FKTABLE; -- Try to delete something that should cascade DELETE FROM PKTABLE where ptest1=1 and ptest2=5 and ptest3=3; -- Show PKTABLE and FKTABLE SELECT * from PKTABLE; SELECT * from FKTABLE; -- Try to delete something that should not have a cascade DELETE FROM PKTABLE where ptest1=2; -- Show PKTABLE and FKTABLE SELECT * from PKTABLE; SELECT * from FKTABLE; DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- set null update / set default delete CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) ); CREATE TABLE FKTABLE ( ftest1 int DEFAULT 0, ftest2 int, ftest3 int, ftest4 int, CONSTRAINT constrname3 FOREIGN KEY(ftest1, ftest2, ftest3) REFERENCES PKTABLE ON DELETE SET DEFAULT ON UPDATE SET NULL); -- Insert Primary Key values INSERT INTO PKTABLE VALUES (1, 2, 3, 'test1'); INSERT INTO PKTABLE VALUES (1, 3, 3, 'test2'); INSERT INTO PKTABLE VALUES (2, 3, 4, 'test3'); INSERT INTO PKTABLE VALUES (2, 4, 5, 'test4'); -- Insert Foreign Key values INSERT INTO FKTABLE VALUES (1, 2, 3, 1); INSERT INTO FKTABLE VALUES (2, 3, 4, 1); INSERT INTO FKTABLE VALUES (NULL, 2, 3, 2); INSERT INTO FKTABLE VALUES (2, NULL, 3, 3); INSERT INTO FKTABLE VALUES (NULL, 2, 7, 4); INSERT INTO FKTABLE VALUES (NULL, 3, 4, 5); -- Insert a failed values INSERT INTO FKTABLE VALUES (1, 2, 7, 6); -- Show FKTABLE SELECT * from FKTABLE; -- Try to update something that will set null UPDATE PKTABLE set ptest2=5 where ptest2=2; -- Try to update something that should not set null UPDATE PKTABLE set ptest2=2 WHERE ptest2=3 and ptest1=1; -- Show PKTABLE and FKTABLE SELECT * from PKTABLE; SELECT * from FKTABLE; -- Try to delete something that should set default DELETE FROM PKTABLE where ptest1=2 and ptest2=3 and ptest3=4; -- Show PKTABLE and FKTABLE SELECT * from PKTABLE; SELECT * from FKTABLE; -- Try to delete something that should not set default DELETE FROM PKTABLE where ptest2=5; -- Show PKTABLE and FKTABLE SELECT * from PKTABLE; SELECT * from FKTABLE; DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- set default update / set null delete CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) ); CREATE TABLE FKTABLE ( ftest1 int DEFAULT 0, ftest2 int DEFAULT -1, ftest3 int DEFAULT -2, ftest4 int, CONSTRAINT constrname3 FOREIGN KEY(ftest1, ftest2, ftest3) REFERENCES PKTABLE ON DELETE SET NULL ON UPDATE SET DEFAULT); -- Insert Primary Key values INSERT INTO PKTABLE VALUES (1, 2, 3, 'test1'); INSERT INTO PKTABLE VALUES (1, 3, 3, 'test2'); INSERT INTO PKTABLE VALUES (2, 3, 4, 'test3'); INSERT INTO PKTABLE VALUES (2, 4, 5, 'test4'); INSERT INTO PKTABLE VALUES (2, -1, 5, 'test5'); -- Insert Foreign Key values INSERT INTO FKTABLE VALUES (1, 2, 3, 1); INSERT INTO FKTABLE VALUES (2, 3, 4, 1); INSERT INTO FKTABLE VALUES (2, 4, 5, 1); INSERT INTO FKTABLE VALUES (NULL, 2, 3, 2); INSERT INTO FKTABLE VALUES (2, NULL, 3, 3); INSERT INTO FKTABLE VALUES (NULL, 2, 7, 4); INSERT INTO FKTABLE VALUES (NULL, 3, 4, 5); -- Insert a failed values INSERT INTO FKTABLE VALUES (1, 2, 7, 6); -- Show FKTABLE SELECT * from FKTABLE; -- Try to update something that will fail UPDATE PKTABLE set ptest2=5 where ptest2=2; -- Try to update something that will set default UPDATE PKTABLE set ptest1=0, ptest2=-1, ptest3=-2 where ptest2=2; UPDATE PKTABLE set ptest2=10 where ptest2=4; -- Try to update something that should not set default UPDATE PKTABLE set ptest2=2 WHERE ptest2=3 and ptest1=1; -- Show PKTABLE and FKTABLE SELECT * from PKTABLE; SELECT * from FKTABLE; -- Try to delete something that should set null DELETE FROM PKTABLE where ptest1=2 and ptest2=3 and ptest3=4; -- Show PKTABLE and FKTABLE SELECT * from PKTABLE; SELECT * from FKTABLE; -- Try to delete something that should not set null DELETE FROM PKTABLE where ptest2=-1 and ptest3=5; -- Show PKTABLE and FKTABLE SELECT * from PKTABLE; SELECT * from FKTABLE; DROP TABLE FKTABLE; DROP TABLE PKTABLE; CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY); CREATE TABLE FKTABLE_FAIL1 ( ftest1 int, CONSTRAINT fkfail1 FOREIGN KEY (ftest2) REFERENCES PKTABLE); CREATE TABLE FKTABLE_FAIL2 ( ftest1 int, CONSTRAINT fkfail1 FOREIGN KEY (ftest1) REFERENCES PKTABLE(ptest2)); DROP TABLE FKTABLE_FAIL1; DROP TABLE FKTABLE_FAIL2; DROP TABLE PKTABLE; -- Test for referencing column number smaller than referenced constraint CREATE TABLE PKTABLE (ptest1 int, ptest2 int, UNIQUE(ptest1, ptest2)); CREATE TABLE FKTABLE_FAIL1 (ftest1 int REFERENCES pktable(ptest1)); DROP TABLE FKTABLE_FAIL1; DROP TABLE PKTABLE; -- -- Tests for mismatched types -- -- Basic one column, two table setup CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY); INSERT INTO PKTABLE VALUES(42); -- This next should fail, because int=inet does not exist CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable); -- This should also fail for the same reason, but here we -- give the column name CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable(ptest1)); -- This should succeed, even though they are different types, -- because int=int8 exists and is a member of the integer opfamily CREATE TABLE FKTABLE (ftest1 int8 REFERENCES pktable); -- Check it actually works INSERT INTO FKTABLE VALUES(42); -- should succeed INSERT INTO FKTABLE VALUES(43); -- should fail UPDATE FKTABLE SET ftest1 = ftest1; -- should succeed UPDATE FKTABLE SET ftest1 = ftest1 + 1; -- should fail DROP TABLE FKTABLE; -- This should fail, because we'd have to cast numeric to int which is -- not an implicit coercion (or use numeric=numeric, but that's not part -- of the integer opfamily) CREATE TABLE FKTABLE (ftest1 numeric REFERENCES pktable); DROP TABLE PKTABLE; -- On the other hand, this should work because int implicitly promotes to -- numeric, and we allow promotion on the FK side CREATE TABLE PKTABLE (ptest1 numeric PRIMARY KEY); INSERT INTO PKTABLE VALUES(42); CREATE TABLE FKTABLE (ftest1 int REFERENCES pktable); -- Check it actually works INSERT INTO FKTABLE VALUES(42); -- should succeed INSERT INTO FKTABLE VALUES(43); -- should fail UPDATE FKTABLE SET ftest1 = ftest1; -- should succeed UPDATE FKTABLE SET ftest1 = ftest1 + 1; -- should fail DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- Two columns, two tables CREATE TABLE PKTABLE (ptest1 int, ptest2 inet, PRIMARY KEY(ptest1, ptest2)); -- This should fail, because we just chose really odd types CREATE TABLE FKTABLE (ftest1 cidr, ftest2 timestamp, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable); -- Again, so should this... CREATE TABLE FKTABLE (ftest1 cidr, ftest2 timestamp, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable(ptest1, ptest2)); -- This fails because we mixed up the column ordering CREATE TABLE FKTABLE (ftest1 int, ftest2 inet, FOREIGN KEY(ftest2, ftest1) REFERENCES pktable); -- As does this... CREATE TABLE FKTABLE (ftest1 int, ftest2 inet, FOREIGN KEY(ftest2, ftest1) REFERENCES pktable(ptest1, ptest2)); -- And again.. CREATE TABLE FKTABLE (ftest1 int, ftest2 inet, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable(ptest2, ptest1)); -- This works... CREATE TABLE FKTABLE (ftest1 int, ftest2 inet, FOREIGN KEY(ftest2, ftest1) REFERENCES pktable(ptest2, ptest1)); DROP TABLE FKTABLE; -- As does this CREATE TABLE FKTABLE (ftest1 int, ftest2 inet, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable(ptest1, ptest2)); DROP TABLE FKTABLE; DROP TABLE PKTABLE; -- Two columns, same table -- Make sure this still works... CREATE TABLE PKTABLE (ptest1 int, ptest2 inet, ptest3 int, ptest4 inet, PRIMARY KEY(ptest1, ptest2), FOREIGN KEY(ptest3, ptest4) REFERENCES pktable(ptest1, ptest2)); DROP TABLE PKTABLE; -- And this, CREATE TABLE PKTABLE (ptest1 int, ptest2 inet, ptest3 int, ptest4 inet, PRIMARY KEY(ptest1, ptest2), FOREIGN KEY(ptest3, ptest4) REFERENCES pktable); DROP TABLE PKTABLE; -- This shouldn't (mixed up columns) CREATE TABLE PKTABLE (ptest1 int, ptest2 inet, ptest3 int, ptest4 inet, PRIMARY KEY(ptest1, ptest2), FOREIGN KEY(ptest3, ptest4) REFERENCES pktable(ptest2, ptest1)); -- Nor should this... (same reason, we have 4,3 referencing 1,2 which mismatches types CREATE TABLE PKTABLE (ptest1 int, ptest2 inet, ptest3 int, ptest4 inet, PRIMARY KEY(ptest1, ptest2), FOREIGN KEY(ptest4, ptest3) REFERENCES pktable(ptest1, ptest2)); -- Not this one either... Same as the last one except we didn't defined the columns being referenced. CREATE TABLE PKTABLE (ptest1 int, ptest2 inet, ptest3 int, ptest4 inet, PRIMARY KEY(ptest1, ptest2), FOREIGN KEY(ptest4, ptest3) REFERENCES pktable); -- -- Now some cases with inheritance -- Basic 2 table case: 1 column of matching types. create table pktable_base (base1 int not null); create table pktable (ptest1 int, primary key(base1), unique(base1, ptest1)) inherits (pktable_base); create table fktable (ftest1 int references pktable(base1)); -- now some ins, upd, del insert into pktable(base1) values (1); insert into pktable(base1) values (2); -- let's insert a non-existent fktable value insert into fktable(ftest1) values (3); -- let's make a valid row for that insert into pktable(base1) values (3); insert into fktable(ftest1) values (3); -- let's try removing a row that should fail from pktable delete from pktable where base1>2; -- okay, let's try updating all of the base1 values to *4 -- which should fail. update pktable set base1=base1*4; -- okay, let's try an update that should work. update pktable set base1=base1*4 where base1<3; -- and a delete that should work delete from pktable where base1>3; -- cleanup drop table fktable; delete from pktable; -- Now 2 columns 2 tables, matching types create table fktable (ftest1 int, ftest2 int, foreign key(ftest1, ftest2) references pktable(base1, ptest1)); -- now some ins, upd, del insert into pktable(base1, ptest1) values (1, 1); insert into pktable(base1, ptest1) values (2, 2); -- let's insert a non-existent fktable value insert into fktable(ftest1, ftest2) values (3, 1); -- let's make a valid row for that insert into pktable(base1,ptest1) values (3, 1); insert into fktable(ftest1, ftest2) values (3, 1); -- let's try removing a row that should fail from pktable delete from pktable where base1>2; -- okay, let's try updating all of the base1 values to *4 -- which should fail. update pktable set base1=base1*4; -- okay, let's try an update that should work. update pktable set base1=base1*4 where base1<3; -- and a delete that should work delete from pktable where base1>3; -- cleanup drop table fktable; drop table pktable; drop table pktable_base; -- Now we'll do one all in 1 table with 2 columns of matching types create table pktable_base(base1 int not null, base2 int); create table pktable(ptest1 int, ptest2 int, primary key(base1, ptest1), foreign key(base2, ptest2) references pktable(base1, ptest1)) inherits (pktable_base); insert into pktable (base1, ptest1, base2, ptest2) values (1, 1, 1, 1); insert into pktable (base1, ptest1, base2, ptest2) values (2, 1, 1, 1); insert into pktable (base1, ptest1, base2, ptest2) values (2, 2, 2, 1); insert into pktable (base1, ptest1, base2, ptest2) values (1, 3, 2, 2); -- fails (3,2) isn't in base1, ptest1 insert into pktable (base1, ptest1, base2, ptest2) values (2, 3, 3, 2); -- fails (2,2) is being referenced delete from pktable where base1=2; -- fails (1,1) is being referenced (twice) update pktable set base1=3 where base1=1; -- this sequence of two deletes will work, since after the first there will be no (2,*) references delete from pktable where base2=2; delete from pktable where base1=2; drop table pktable; drop table pktable_base; -- 2 columns (2 tables), mismatched types create table pktable_base(base1 int not null); create table pktable(ptest1 inet, primary key(base1, ptest1)) inherits (pktable_base); -- just generally bad types (with and without column references on the referenced table) create table fktable(ftest1 cidr, ftest2 int[], foreign key (ftest1, ftest2) references pktable); create table fktable(ftest1 cidr, ftest2 int[], foreign key (ftest1, ftest2) references pktable(base1, ptest1)); -- let's mix up which columns reference which create table fktable(ftest1 int, ftest2 inet, foreign key(ftest2, ftest1) references pktable); create table fktable(ftest1 int, ftest2 inet, foreign key(ftest2, ftest1) references pktable(base1, ptest1)); create table fktable(ftest1 int, ftest2 inet, foreign key(ftest1, ftest2) references pktable(ptest1, base1)); drop table pktable; drop table pktable_base; -- 2 columns (1 table), mismatched types create table pktable_base(base1 int not null, base2 int); create table pktable(ptest1 inet, ptest2 inet[], primary key(base1, ptest1), foreign key(base2, ptest2) references pktable(base1, ptest1)) inherits (pktable_base); create table pktable(ptest1 inet, ptest2 inet, primary key(base1, ptest1), foreign key(base2, ptest2) references pktable(ptest1, base1)) inherits (pktable_base); create table pktable(ptest1 inet, ptest2 inet, primary key(base1, ptest1), foreign key(ptest2, base2) references pktable(base1, ptest1)) inherits (pktable_base); create table pktable(ptest1 inet, ptest2 inet, primary key(base1, ptest1), foreign key(ptest2, base2) references pktable(base1, ptest1)) inherits (pktable_base); drop table pktable; drop table pktable_base; -- -- Deferrable constraints -- -- deferrable, explicitly deferred CREATE TABLE pktable ( id INT4 PRIMARY KEY, other INT4 ); CREATE TABLE fktable ( id INT4 PRIMARY KEY, fk INT4 REFERENCES pktable DEFERRABLE ); -- default to immediate: should fail INSERT INTO fktable VALUES (5, 10); -- explicitly defer the constraint BEGIN; SET CONSTRAINTS ALL DEFERRED; INSERT INTO fktable VALUES (10, 15); INSERT INTO pktable VALUES (15, 0); -- make the FK insert valid COMMIT; DROP TABLE fktable, pktable; -- deferrable, initially deferred CREATE TABLE pktable ( id INT4 PRIMARY KEY, other INT4 ); CREATE TABLE fktable ( id INT4 PRIMARY KEY, fk INT4 REFERENCES pktable DEFERRABLE INITIALLY DEFERRED ); -- default to deferred, should succeed BEGIN; INSERT INTO fktable VALUES (100, 200); INSERT INTO pktable VALUES (200, 500); -- make the FK insert valid COMMIT; -- default to deferred, explicitly make immediate BEGIN; SET CONSTRAINTS ALL IMMEDIATE; -- should fail INSERT INTO fktable VALUES (500, 1000); COMMIT; DROP TABLE fktable, pktable; -- tricky behavior: according to SQL99, if a deferred constraint is set -- to 'immediate' mode, it should be checked for validity *immediately*, -- not when the current transaction commits (i.e. the mode change applies -- retroactively) CREATE TABLE pktable ( id INT4 PRIMARY KEY, other INT4 ); CREATE TABLE fktable ( id INT4 PRIMARY KEY, fk INT4 REFERENCES pktable DEFERRABLE ); BEGIN; SET CONSTRAINTS ALL DEFERRED; -- should succeed, for now INSERT INTO fktable VALUES (1000, 2000); -- should cause transaction abort, due to preceding error SET CONSTRAINTS ALL IMMEDIATE; INSERT INTO pktable VALUES (2000, 3); -- too late COMMIT; DROP TABLE fktable, pktable; -- deferrable, initially deferred CREATE TABLE pktable ( id INT4 PRIMARY KEY, other INT4 ); CREATE TABLE fktable ( id INT4 PRIMARY KEY, fk INT4 REFERENCES pktable DEFERRABLE INITIALLY DEFERRED ); BEGIN; -- no error here INSERT INTO fktable VALUES (100, 200); -- error here on commit COMMIT; DROP TABLE pktable, fktable; -- test notice about expensive referential integrity checks, -- where the index cannot be used because of type incompatibilities. CREATE TEMP TABLE pktable ( id1 INT4 PRIMARY KEY, id2 VARCHAR(4) UNIQUE, id3 REAL UNIQUE, UNIQUE(id1, id2, id3) ); CREATE TEMP TABLE fktable ( x1 INT4 REFERENCES pktable(id1), x2 VARCHAR(4) REFERENCES pktable(id2), x3 REAL REFERENCES pktable(id3), x4 TEXT, x5 INT2 ); -- check individual constraints with alter table. -- should fail -- varchar does not promote to real ALTER TABLE fktable ADD CONSTRAINT fk_2_3 FOREIGN KEY (x2) REFERENCES pktable(id3); -- nor to int4 ALTER TABLE fktable ADD CONSTRAINT fk_2_1 FOREIGN KEY (x2) REFERENCES pktable(id1); -- real does not promote to int4 ALTER TABLE fktable ADD CONSTRAINT fk_3_1 FOREIGN KEY (x3) REFERENCES pktable(id1); -- int4 does not promote to text ALTER TABLE fktable ADD CONSTRAINT fk_1_2 FOREIGN KEY (x1) REFERENCES pktable(id2); -- should succeed -- int4 promotes to real ALTER TABLE fktable ADD CONSTRAINT fk_1_3 FOREIGN KEY (x1) REFERENCES pktable(id3); -- text is compatible with varchar ALTER TABLE fktable ADD CONSTRAINT fk_4_2 FOREIGN KEY (x4) REFERENCES pktable(id2); -- int2 is part of integer opfamily as of 8.0 ALTER TABLE fktable ADD CONSTRAINT fk_5_1 FOREIGN KEY (x5) REFERENCES pktable(id1); -- check multikey cases, especially out-of-order column lists -- these should work ALTER TABLE fktable ADD CONSTRAINT fk_123_123 FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id1,id2,id3); ALTER TABLE fktable ADD CONSTRAINT fk_213_213 FOREIGN KEY (x2,x1,x3) REFERENCES pktable(id2,id1,id3); ALTER TABLE fktable ADD CONSTRAINT fk_253_213 FOREIGN KEY (x2,x5,x3) REFERENCES pktable(id2,id1,id3); -- these should fail ALTER TABLE fktable ADD CONSTRAINT fk_123_231 FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id2,id3,id1); ALTER TABLE fktable ADD CONSTRAINT fk_241_132 FOREIGN KEY (x2,x4,x1) REFERENCES pktable(id1,id3,id2); DROP TABLE pktable, fktable; -- test a tricky case: we can elide firing the FK check trigger during -- an UPDATE if the UPDATE did not change the foreign key -- field. However, we can't do this if our transaction was the one that -- created the updated row and the trigger is deferred, since our UPDATE -- will have invalidated the original newly-inserted tuple, and therefore -- cause the on-INSERT RI trigger not to be fired. CREATE TEMP TABLE pktable ( id int primary key, other int ); CREATE TEMP TABLE fktable ( id int primary key, fk int references pktable deferrable initially deferred ); INSERT INTO pktable VALUES (5, 10); BEGIN; -- doesn't match PK, but no error yet INSERT INTO fktable VALUES (0, 20); -- don't change FK UPDATE fktable SET id = id + 1; -- should catch error from initial INSERT COMMIT; -- check same case when insert is in a different subtransaction than update BEGIN; -- doesn't match PK, but no error yet INSERT INTO fktable VALUES (0, 20); -- UPDATE will be in a subxact SAVEPOINT savept1; -- don't change FK UPDATE fktable SET id = id + 1; -- should catch error from initial INSERT COMMIT; BEGIN; -- INSERT will be in a subxact SAVEPOINT savept1; -- doesn't match PK, but no error yet INSERT INTO fktable VALUES (0, 20); RELEASE SAVEPOINT savept1; -- don't change FK UPDATE fktable SET id = id + 1; -- should catch error from initial INSERT COMMIT; BEGIN; -- doesn't match PK, but no error yet INSERT INTO fktable VALUES (0, 20); -- UPDATE will be in a subxact SAVEPOINT savept1; -- don't change FK UPDATE fktable SET id = id + 1; -- Roll back the UPDATE ROLLBACK TO savept1; -- should catch error from initial INSERT COMMIT; -- -- check ALTER CONSTRAINT -- INSERT INTO fktable VALUES (1, 5); ALTER TABLE fktable ALTER CONSTRAINT fktable_fk_fkey DEFERRABLE INITIALLY IMMEDIATE; BEGIN; -- doesn't match FK, should throw error now UPDATE pktable SET id = 10 WHERE id = 5; COMMIT; BEGIN; -- doesn't match PK, should throw error now INSERT INTO fktable VALUES (0, 20); COMMIT; -- try additional syntax ALTER TABLE fktable ALTER CONSTRAINT fktable_fk_fkey NOT DEFERRABLE; -- illegal option ALTER TABLE fktable ALTER CONSTRAINT fktable_fk_fkey NOT DEFERRABLE INITIALLY DEFERRED; -- test order of firing of FK triggers when several RI-induced changes need to -- be made to the same row. This was broken by subtransaction-related -- changes in 8.0. CREATE TEMP TABLE users ( id INT PRIMARY KEY, name VARCHAR NOT NULL ); INSERT INTO users VALUES (1, 'Jozko'); INSERT INTO users VALUES (2, 'Ferko'); INSERT INTO users VALUES (3, 'Samko'); CREATE TEMP TABLE tasks ( id INT PRIMARY KEY, owner INT REFERENCES users ON UPDATE CASCADE ON DELETE SET NULL, worker INT REFERENCES users ON UPDATE CASCADE ON DELETE SET NULL, checked_by INT REFERENCES users ON UPDATE CASCADE ON DELETE SET NULL ); INSERT INTO tasks VALUES (1,1,NULL,NULL); INSERT INTO tasks VALUES (2,2,2,NULL); INSERT INTO tasks VALUES (3,3,3,3); SELECT * FROM tasks; UPDATE users SET id = 4 WHERE id = 3; SELECT * FROM tasks; DELETE FROM users WHERE id = 4; SELECT * FROM tasks; -- could fail with only 2 changes to make, if row was already updated BEGIN; UPDATE tasks set id=id WHERE id=2; SELECT * FROM tasks; DELETE FROM users WHERE id = 2; SELECT * FROM tasks; COMMIT; -- -- Test self-referential FK with CASCADE (bug #6268) -- create temp table selfref ( a int primary key, b int, foreign key (b) references selfref (a) on update cascade on delete cascade ); insert into selfref (a, b) values (0, 0), (1, 1); begin; update selfref set a = 123 where a = 0; select a, b from selfref; update selfref set a = 456 where a = 123; select a, b from selfref; commit; -- -- Test that SET DEFAULT actions recognize updates to default values -- create temp table defp (f1 int primary key); create temp table defc (f1 int default 0 references defp on delete set default); insert into defp values (0), (1), (2); insert into defc values (2); select * from defc; delete from defp where f1 = 2; select * from defc; delete from defp where f1 = 0; -- fail alter table defc alter column f1 set default 1; delete from defp where f1 = 0; select * from defc; delete from defp where f1 = 1; -- fail -- -- Test the difference between NO ACTION and RESTRICT -- create temp table pp (f1 int primary key); create temp table cc (f1 int references pp on update no action on delete no action); insert into pp values(12); insert into pp values(11); update pp set f1=f1+1; insert into cc values(13); update pp set f1=f1+1; update pp set f1=f1+1; -- fail delete from pp where f1 = 13; -- fail drop table pp, cc; create temp table pp (f1 int primary key); create temp table cc (f1 int references pp on update restrict on delete restrict); insert into pp values(12); insert into pp values(11); update pp set f1=f1+1; insert into cc values(13); update pp set f1=f1+1; -- fail delete from pp where f1 = 13; -- fail drop table pp, cc; -- -- Test interaction of foreign-key optimization with rules (bug #14219) -- create temp table t1 (a integer primary key, b text); create temp table t2 (a integer primary key, b integer references t1); create rule r1 as on delete to t1 do delete from t2 where t2.b = old.a; explain (costs off) delete from t1 where a = 1; delete from t1 where a = 1; -- Test a primary key with attributes located in later attnum positions -- compared to the fk attributes. create table pktable2 (a int, b int, c int, d int, e int, primary key (d, e)); create table fktable2 (d int, e int, foreign key (d, e) references pktable2); insert into pktable2 values (1, 2, 3, 4, 5); insert into fktable2 values (4, 5); delete from pktable2; update pktable2 set d = 5; drop table pktable2, fktable2; -- Test truncation of long foreign key names create table pktable1 (a int primary key); create table pktable2 (a int, b int, primary key (a, b)); create table fktable2 ( a int, b int, very_very_long_column_name_to_exceed_63_characters int, foreign key (very_very_long_column_name_to_exceed_63_characters) references pktable1, foreign key (a, very_very_long_column_name_to_exceed_63_characters) references pktable2, foreign key (a, very_very_long_column_name_to_exceed_63_characters) references pktable2 ); select conname from pg_constraint where conrelid = 'fktable2'::regclass order by conname; drop table pktable1, pktable2, fktable2; -- -- Test deferred FK check on a tuple deleted by a rolled-back subtransaction -- create table pktable2(f1 int primary key); create table fktable2(f1 int references pktable2 deferrable initially deferred); insert into pktable2 values(1); begin; insert into fktable2 values(1); savepoint x; delete from fktable2; rollback to x; commit; begin; insert into fktable2 values(2); savepoint x; delete from fktable2; rollback to x; commit; -- fail -- -- Test that we prevent dropping FK constraint with pending trigger events -- begin; insert into fktable2 values(2); alter table fktable2 drop constraint fktable2_f1_fkey; commit; begin; delete from pktable2 where f1 = 1; alter table fktable2 drop constraint fktable2_f1_fkey; commit; drop table pktable2, fktable2; -- -- Test keys that "look" different but compare as equal -- create table pktable2 (a float8, b float8, primary key (a, b)); create table fktable2 (x float8, y float8, foreign key (x, y) references pktable2 (a, b) on update cascade); insert into pktable2 values ('-0', '-0'); insert into fktable2 values ('-0', '-0'); select * from pktable2; select * from fktable2; update pktable2 set a = '0' where a = '-0'; select * from pktable2; -- should have updated fktable2.x select * from fktable2; drop table pktable2, fktable2; -- -- Foreign keys and partitioned tables -- -- Creation of a partitioned hierarchy with irregular definitions CREATE TABLE fk_notpartitioned_pk (fdrop1 int, a int, fdrop2 int, b int, PRIMARY KEY (a, b)); ALTER TABLE fk_notpartitioned_pk DROP COLUMN fdrop1, DROP COLUMN fdrop2; CREATE TABLE fk_partitioned_fk (b int, fdrop1 int, a int) PARTITION BY RANGE (a, b); ALTER TABLE fk_partitioned_fk DROP COLUMN fdrop1; CREATE TABLE fk_partitioned_fk_1 (fdrop1 int, fdrop2 int, a int, fdrop3 int, b int); ALTER TABLE fk_partitioned_fk_1 DROP COLUMN fdrop1, DROP COLUMN fdrop2, DROP COLUMN fdrop3; ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_1 FOR VALUES FROM (0,0) TO (1000,1000); ALTER TABLE fk_partitioned_fk ADD FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk; CREATE TABLE fk_partitioned_fk_2 (b int, fdrop1 int, fdrop2 int, a int); ALTER TABLE fk_partitioned_fk_2 DROP COLUMN fdrop1, DROP COLUMN fdrop2; ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_2 FOR VALUES FROM (1000,1000) TO (2000,2000); CREATE TABLE fk_partitioned_fk_3 (fdrop1 int, fdrop2 int, fdrop3 int, fdrop4 int, b int, a int) PARTITION BY HASH (a); ALTER TABLE fk_partitioned_fk_3 DROP COLUMN fdrop1, DROP COLUMN fdrop2, DROP COLUMN fdrop3, DROP COLUMN fdrop4; CREATE TABLE fk_partitioned_fk_3_0 PARTITION OF fk_partitioned_fk_3 FOR VALUES WITH (MODULUS 5, REMAINDER 0); CREATE TABLE fk_partitioned_fk_3_1 PARTITION OF fk_partitioned_fk_3 FOR VALUES WITH (MODULUS 5, REMAINDER 1); ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_3 FOR VALUES FROM (2000,2000) TO (3000,3000); -- Creating a foreign key with ONLY on a partitioned table referencing -- a non-partitioned table fails. ALTER TABLE ONLY fk_partitioned_fk ADD FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk; -- Adding a NOT VALID foreign key on a partitioned table referencing -- a non-partitioned table fails. ALTER TABLE fk_partitioned_fk ADD FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk NOT VALID; -- these inserts, targeting both the partition directly as well as the -- partitioned table, should all fail INSERT INTO fk_partitioned_fk (a,b) VALUES (500, 501); INSERT INTO fk_partitioned_fk_1 (a,b) VALUES (500, 501); INSERT INTO fk_partitioned_fk (a,b) VALUES (1500, 1501); INSERT INTO fk_partitioned_fk_2 (a,b) VALUES (1500, 1501); INSERT INTO fk_partitioned_fk (a,b) VALUES (2500, 2502); INSERT INTO fk_partitioned_fk_3 (a,b) VALUES (2500, 2502); INSERT INTO fk_partitioned_fk (a,b) VALUES (2501, 2503); INSERT INTO fk_partitioned_fk_3 (a,b) VALUES (2501, 2503); -- but if we insert the values that make them valid, then they work INSERT INTO fk_notpartitioned_pk VALUES (500, 501), (1500, 1501), (2500, 2502), (2501, 2503); INSERT INTO fk_partitioned_fk (a,b) VALUES (500, 501); INSERT INTO fk_partitioned_fk (a,b) VALUES (1500, 1501); INSERT INTO fk_partitioned_fk (a,b) VALUES (2500, 2502); INSERT INTO fk_partitioned_fk (a,b) VALUES (2501, 2503); -- this update fails because there is no referenced row UPDATE fk_partitioned_fk SET a = a + 1 WHERE a = 2501; -- but we can fix it thusly: INSERT INTO fk_notpartitioned_pk (a,b) VALUES (2502, 2503); UPDATE fk_partitioned_fk SET a = a + 1 WHERE a = 2501; -- these updates would leave lingering rows in the referencing table; disallow UPDATE fk_notpartitioned_pk SET b = 502 WHERE a = 500; UPDATE fk_notpartitioned_pk SET b = 1502 WHERE a = 1500; UPDATE fk_notpartitioned_pk SET b = 2504 WHERE a = 2500; -- check psql behavior \d fk_notpartitioned_pk ALTER TABLE fk_partitioned_fk DROP CONSTRAINT fk_partitioned_fk_a_b_fkey; -- done. DROP TABLE fk_notpartitioned_pk, fk_partitioned_fk; -- Altering a type referenced by a foreign key needs to drop/recreate the FK. -- Ensure that works. CREATE TABLE fk_notpartitioned_pk (a INT, PRIMARY KEY(a), CHECK (a > 0)); CREATE TABLE fk_partitioned_fk (a INT REFERENCES fk_notpartitioned_pk(a) PRIMARY KEY) PARTITION BY RANGE(a); CREATE TABLE fk_partitioned_fk_1 PARTITION OF fk_partitioned_fk FOR VALUES FROM (MINVALUE) TO (MAXVALUE); INSERT INTO fk_notpartitioned_pk VALUES (1); INSERT INTO fk_partitioned_fk VALUES (1); ALTER TABLE fk_notpartitioned_pk ALTER COLUMN a TYPE bigint; DELETE FROM fk_notpartitioned_pk WHERE a = 1; DROP TABLE fk_notpartitioned_pk, fk_partitioned_fk; -- Test some other exotic foreign key features: MATCH SIMPLE, ON UPDATE/DELETE -- actions CREATE TABLE fk_notpartitioned_pk (a int, b int, primary key (a, b)); CREATE TABLE fk_partitioned_fk (a int default 2501, b int default 142857) PARTITION BY LIST (a); CREATE TABLE fk_partitioned_fk_1 PARTITION OF fk_partitioned_fk FOR VALUES IN (NULL,500,501,502); ALTER TABLE fk_partitioned_fk ADD FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk MATCH SIMPLE ON DELETE SET NULL ON UPDATE SET NULL; CREATE TABLE fk_partitioned_fk_2 PARTITION OF fk_partitioned_fk FOR VALUES IN (1500,1502); CREATE TABLE fk_partitioned_fk_3 (a int, b int); ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_3 FOR VALUES IN (2500,2501,2502,2503); -- this insert fails INSERT INTO fk_partitioned_fk (a, b) VALUES (2502, 2503); INSERT INTO fk_partitioned_fk_3 (a, b) VALUES (2502, 2503); -- but since the FK is MATCH SIMPLE, this one doesn't INSERT INTO fk_partitioned_fk_3 (a, b) VALUES (2502, NULL); -- now create the referenced row ... INSERT INTO fk_notpartitioned_pk VALUES (2502, 2503); --- and now the same insert work INSERT INTO fk_partitioned_fk_3 (a, b) VALUES (2502, 2503); -- this always works INSERT INTO fk_partitioned_fk (a,b) VALUES (NULL, NULL); -- MATCH FULL INSERT INTO fk_notpartitioned_pk VALUES (1, 2); CREATE TABLE fk_partitioned_fk_full (x int, y int) PARTITION BY RANGE (x); CREATE TABLE fk_partitioned_fk_full_1 PARTITION OF fk_partitioned_fk_full DEFAULT; INSERT INTO fk_partitioned_fk_full VALUES (1, NULL); ALTER TABLE fk_partitioned_fk_full ADD FOREIGN KEY (x, y) REFERENCES fk_notpartitioned_pk MATCH FULL; -- fails TRUNCATE fk_partitioned_fk_full; ALTER TABLE fk_partitioned_fk_full ADD FOREIGN KEY (x, y) REFERENCES fk_notpartitioned_pk MATCH FULL; INSERT INTO fk_partitioned_fk_full VALUES (1, NULL); -- fails DROP TABLE fk_partitioned_fk_full; -- ON UPDATE SET NULL SELECT tableoid::regclass, a, b FROM fk_partitioned_fk WHERE b IS NULL ORDER BY a; UPDATE fk_notpartitioned_pk SET a = a + 1 WHERE a = 2502; SELECT tableoid::regclass, a, b FROM fk_partitioned_fk WHERE b IS NULL ORDER BY a; -- ON DELETE SET NULL INSERT INTO fk_partitioned_fk VALUES (2503, 2503); SELECT count(*) FROM fk_partitioned_fk WHERE a IS NULL; DELETE FROM fk_notpartitioned_pk; SELECT count(*) FROM fk_partitioned_fk WHERE a IS NULL; -- ON UPDATE/DELETE SET DEFAULT ALTER TABLE fk_partitioned_fk DROP CONSTRAINT fk_partitioned_fk_a_b_fkey; ALTER TABLE fk_partitioned_fk ADD FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk ON DELETE SET DEFAULT ON UPDATE SET DEFAULT; INSERT INTO fk_notpartitioned_pk VALUES (2502, 2503); INSERT INTO fk_partitioned_fk_3 (a, b) VALUES (2502, 2503); -- this fails, because the defaults for the referencing table are not present -- in the referenced table: UPDATE fk_notpartitioned_pk SET a = 1500 WHERE a = 2502; -- but inserting the row we can make it work: INSERT INTO fk_notpartitioned_pk VALUES (2501, 142857); UPDATE fk_notpartitioned_pk SET a = 1500 WHERE a = 2502; SELECT * FROM fk_partitioned_fk WHERE b = 142857; -- ON UPDATE/DELETE CASCADE ALTER TABLE fk_partitioned_fk DROP CONSTRAINT fk_partitioned_fk_a_b_fkey; ALTER TABLE fk_partitioned_fk ADD FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk ON DELETE CASCADE ON UPDATE CASCADE; UPDATE fk_notpartitioned_pk SET a = 2502 WHERE a = 2501; SELECT * FROM fk_partitioned_fk WHERE b = 142857; -- Now you see it ... SELECT * FROM fk_partitioned_fk WHERE b = 142857; DELETE FROM fk_notpartitioned_pk WHERE b = 142857; -- now you don't. SELECT * FROM fk_partitioned_fk WHERE a = 142857; -- verify that DROP works DROP TABLE fk_partitioned_fk_2; -- Test behavior of the constraint together with attaching and detaching -- partitions. CREATE TABLE fk_partitioned_fk_2 PARTITION OF fk_partitioned_fk FOR VALUES IN (1500,1502); ALTER TABLE fk_partitioned_fk DETACH PARTITION fk_partitioned_fk_2; BEGIN; DROP TABLE fk_partitioned_fk; -- constraint should still be there \d fk_partitioned_fk_2; ROLLBACK; ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_2 FOR VALUES IN (1500,1502); DROP TABLE fk_partitioned_fk_2; CREATE TABLE fk_partitioned_fk_2 (b int, c text, a int, FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk ON UPDATE CASCADE ON DELETE CASCADE); ALTER TABLE fk_partitioned_fk_2 DROP COLUMN c; ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_2 FOR VALUES IN (1500,1502); -- should have only one constraint \d fk_partitioned_fk_2 DROP TABLE fk_partitioned_fk_2; CREATE TABLE fk_partitioned_fk_4 (a int, b int, FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk(a, b) ON UPDATE CASCADE ON DELETE CASCADE) PARTITION BY RANGE (b, a); CREATE TABLE fk_partitioned_fk_4_1 PARTITION OF fk_partitioned_fk_4 FOR VALUES FROM (1,1) TO (100,100); CREATE TABLE fk_partitioned_fk_4_2 (a int, b int, FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk(a, b) ON UPDATE SET NULL); ALTER TABLE fk_partitioned_fk_4 ATTACH PARTITION fk_partitioned_fk_4_2 FOR VALUES FROM (100,100) TO (1000,1000); ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_4 FOR VALUES IN (3500,3502); ALTER TABLE fk_partitioned_fk DETACH PARTITION fk_partitioned_fk_4; ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_4 FOR VALUES IN (3500,3502); -- should only have one constraint \d fk_partitioned_fk_4 \d fk_partitioned_fk_4_1 -- this one has an FK with mismatched properties \d fk_partitioned_fk_4_2 CREATE TABLE fk_partitioned_fk_5 (a int, b int, FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk(a, b) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE, FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk(a, b) MATCH FULL ON UPDATE CASCADE ON DELETE CASCADE) PARTITION BY RANGE (a); CREATE TABLE fk_partitioned_fk_5_1 (a int, b int, FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk); ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_5 FOR VALUES IN (4500); ALTER TABLE fk_partitioned_fk_5 ATTACH PARTITION fk_partitioned_fk_5_1 FOR VALUES FROM (0) TO (10); ALTER TABLE fk_partitioned_fk DETACH PARTITION fk_partitioned_fk_5; ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_5 FOR VALUES IN (4500); -- this one has two constraints, similar but not quite the one in the parent, -- so it gets a new one \d fk_partitioned_fk_5 -- verify that it works to reattaching a child with multiple candidate -- constraints ALTER TABLE fk_partitioned_fk_5 DETACH PARTITION fk_partitioned_fk_5_1; ALTER TABLE fk_partitioned_fk_5 ATTACH PARTITION fk_partitioned_fk_5_1 FOR VALUES FROM (0) TO (10); \d fk_partitioned_fk_5_1 -- verify that attaching a table checks that the existing data satisfies the -- constraint CREATE TABLE fk_partitioned_fk_2 (a int, b int) PARTITION BY RANGE (b); CREATE TABLE fk_partitioned_fk_2_1 PARTITION OF fk_partitioned_fk_2 FOR VALUES FROM (0) TO (1000); CREATE TABLE fk_partitioned_fk_2_2 PARTITION OF fk_partitioned_fk_2 FOR VALUES FROM (1000) TO (2000); INSERT INTO fk_partitioned_fk_2 VALUES (1600, 601), (1600, 1601); ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_2 FOR VALUES IN (1600); INSERT INTO fk_notpartitioned_pk VALUES (1600, 601), (1600, 1601); ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_2 FOR VALUES IN (1600); -- leave these tables around intentionally -- test the case when the referenced table is owned by a different user create role regress_other_partitioned_fk_owner; grant references on fk_notpartitioned_pk to regress_other_partitioned_fk_owner; set role regress_other_partitioned_fk_owner; create table other_partitioned_fk(a int, b int) partition by list (a); create table other_partitioned_fk_1 partition of other_partitioned_fk for values in (2048); insert into other_partitioned_fk select 2048, x from generate_series(1,10) x; -- this should fail alter table other_partitioned_fk add foreign key (a, b) references fk_notpartitioned_pk(a, b); -- add the missing keys and retry reset role; insert into fk_notpartitioned_pk (a, b) select 2048, x from generate_series(1,10) x; set role regress_other_partitioned_fk_owner; alter table other_partitioned_fk add foreign key (a, b) references fk_notpartitioned_pk(a, b); -- clean up drop table other_partitioned_fk; reset role; revoke all on fk_notpartitioned_pk from regress_other_partitioned_fk_owner; drop role regress_other_partitioned_fk_owner; -- Test creating a constraint at the parent that already exists in partitions. -- There should be no duplicated constraints, and attempts to drop the -- constraint in partitions should raise appropriate errors. create schema fkpart0 create table pkey (a int primary key) create table fk_part (a int) partition by list (a) create table fk_part_1 partition of fk_part (foreign key (a) references fkpart0.pkey) for values in (1) create table fk_part_23 partition of fk_part (foreign key (a) references fkpart0.pkey) for values in (2, 3) partition by list (a) create table fk_part_23_2 partition of fk_part_23 for values in (2); alter table fkpart0.fk_part add foreign key (a) references fkpart0.pkey; \d fkpart0.fk_part_1 \\ -- should have only one FK alter table fkpart0.fk_part_1 drop constraint fk_part_1_a_fkey; \d fkpart0.fk_part_23 \\ -- should have only one FK \d fkpart0.fk_part_23_2 \\ -- should have only one FK alter table fkpart0.fk_part_23 drop constraint fk_part_23_a_fkey; alter table fkpart0.fk_part_23_2 drop constraint fk_part_23_a_fkey; create table fkpart0.fk_part_4 partition of fkpart0.fk_part for values in (4); \d fkpart0.fk_part_4 alter table fkpart0.fk_part_4 drop constraint fk_part_a_fkey; create table fkpart0.fk_part_56 partition of fkpart0.fk_part for values in (5,6) partition by list (a); create table fkpart0.fk_part_56_5 partition of fkpart0.fk_part_56 for values in (5); \d fkpart0.fk_part_56 alter table fkpart0.fk_part_56 drop constraint fk_part_a_fkey; alter table fkpart0.fk_part_56_5 drop constraint fk_part_a_fkey; -- verify that attaching and detaching partitions maintains the right set of -- triggers create schema fkpart1 create table pkey (a int primary key) create table fk_part (a int) partition by list (a) create table fk_part_1 partition of fk_part for values in (1) partition by list (a) create table fk_part_1_1 partition of fk_part_1 for values in (1); alter table fkpart1.fk_part add foreign key (a) references fkpart1.pkey; insert into fkpart1.fk_part values (1); -- should fail insert into fkpart1.pkey values (1); insert into fkpart1.fk_part values (1); delete from fkpart1.pkey where a = 1; -- should fail alter table fkpart1.fk_part detach partition fkpart1.fk_part_1; create table fkpart1.fk_part_1_2 partition of fkpart1.fk_part_1 for values in (2); insert into fkpart1.fk_part_1 values (2); -- should fail delete from fkpart1.pkey where a = 1; -- verify that attaching and detaching partitions manipulates the inheritance -- properties of their FK constraints correctly create schema fkpart2 create table pkey (a int primary key) create table fk_part (a int, constraint fkey foreign key (a) references fkpart2.pkey) partition by list (a) create table fk_part_1 partition of fkpart2.fk_part for values in (1) partition by list (a) create table fk_part_1_1 (a int, constraint my_fkey foreign key (a) references fkpart2.pkey); alter table fkpart2.fk_part_1 attach partition fkpart2.fk_part_1_1 for values in (1); alter table fkpart2.fk_part_1 drop constraint fkey; -- should fail alter table fkpart2.fk_part_1_1 drop constraint my_fkey; -- should fail alter table fkpart2.fk_part detach partition fkpart2.fk_part_1; alter table fkpart2.fk_part_1 drop constraint fkey; -- ok alter table fkpart2.fk_part_1_1 drop constraint my_fkey; -- doesn't exist drop schema fkpart0, fkpart1, fkpart2 cascade; -- Test a partitioned table as referenced table. -- Verify basic functionality with a regular partition creation and a partition -- with a different column layout, as well as partitions added (created and -- attached) after creating the foreign key. CREATE SCHEMA fkpart3; SET search_path TO fkpart3; CREATE TABLE pk (a int PRIMARY KEY) PARTITION BY RANGE (a); CREATE TABLE pk1 PARTITION OF pk FOR VALUES FROM (0) TO (1000); CREATE TABLE pk2 (b int, a int); ALTER TABLE pk2 DROP COLUMN b; ALTER TABLE pk2 ALTER a SET NOT NULL; ALTER TABLE pk ATTACH PARTITION pk2 FOR VALUES FROM (1000) TO (2000); CREATE TABLE fk (a int) PARTITION BY RANGE (a); CREATE TABLE fk1 PARTITION OF fk FOR VALUES FROM (0) TO (750); ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk; CREATE TABLE fk2 (b int, a int) ; ALTER TABLE fk2 DROP COLUMN b; ALTER TABLE fk ATTACH PARTITION fk2 FOR VALUES FROM (750) TO (3500); CREATE TABLE pk3 PARTITION OF pk FOR VALUES FROM (2000) TO (3000); CREATE TABLE pk4 (LIKE pk); ALTER TABLE pk ATTACH PARTITION pk4 FOR VALUES FROM (3000) TO (4000); CREATE TABLE pk5 (c int, b int, a int NOT NULL) PARTITION BY RANGE (a); ALTER TABLE pk5 DROP COLUMN b, DROP COLUMN c; CREATE TABLE pk51 PARTITION OF pk5 FOR VALUES FROM (4000) TO (4500); CREATE TABLE pk52 PARTITION OF pk5 FOR VALUES FROM (4500) TO (5000); ALTER TABLE pk ATTACH PARTITION pk5 FOR VALUES FROM (4000) TO (5000); CREATE TABLE fk3 PARTITION OF fk FOR VALUES FROM (3500) TO (5000); -- these should fail: referenced value not present INSERT into fk VALUES (1); INSERT into fk VALUES (1000); INSERT into fk VALUES (2000); INSERT into fk VALUES (3000); INSERT into fk VALUES (4000); INSERT into fk VALUES (4500); -- insert into the referenced table, now they should work INSERT into pk VALUES (1), (1000), (2000), (3000), (4000), (4500); INSERT into fk VALUES (1), (1000), (2000), (3000), (4000), (4500); -- should fail: referencing value present DELETE FROM pk WHERE a = 1; DELETE FROM pk WHERE a = 1000; DELETE FROM pk WHERE a = 2000; DELETE FROM pk WHERE a = 3000; DELETE FROM pk WHERE a = 4000; DELETE FROM pk WHERE a = 4500; UPDATE pk SET a = 2 WHERE a = 1; UPDATE pk SET a = 1002 WHERE a = 1000; UPDATE pk SET a = 2002 WHERE a = 2000; UPDATE pk SET a = 3002 WHERE a = 3000; UPDATE pk SET a = 4002 WHERE a = 4000; UPDATE pk SET a = 4502 WHERE a = 4500; -- now they should work DELETE FROM fk; UPDATE pk SET a = 2 WHERE a = 1; DELETE FROM pk WHERE a = 2; UPDATE pk SET a = 1002 WHERE a = 1000; DELETE FROM pk WHERE a = 1002; UPDATE pk SET a = 2002 WHERE a = 2000; DELETE FROM pk WHERE a = 2002; UPDATE pk SET a = 3002 WHERE a = 3000; DELETE FROM pk WHERE a = 3002; UPDATE pk SET a = 4002 WHERE a = 4000; DELETE FROM pk WHERE a = 4002; UPDATE pk SET a = 4502 WHERE a = 4500; DELETE FROM pk WHERE a = 4502; CREATE SCHEMA fkpart4; SET search_path TO fkpart4; -- dropping/detaching PARTITIONs is prevented if that would break -- a foreign key's existing data CREATE TABLE droppk (a int PRIMARY KEY) PARTITION BY RANGE (a); CREATE TABLE droppk1 PARTITION OF droppk FOR VALUES FROM (0) TO (1000); CREATE TABLE droppk_d PARTITION OF droppk DEFAULT; CREATE TABLE droppk2 PARTITION OF droppk FOR VALUES FROM (1000) TO (2000) PARTITION BY RANGE (a); CREATE TABLE droppk21 PARTITION OF droppk2 FOR VALUES FROM (1000) TO (1400); CREATE TABLE droppk2_d PARTITION OF droppk2 DEFAULT; INSERT into droppk VALUES (1), (1000), (1500), (2000); CREATE TABLE dropfk (a int REFERENCES droppk); INSERT into dropfk VALUES (1), (1000), (1500), (2000); -- these should all fail ALTER TABLE droppk DETACH PARTITION droppk_d; ALTER TABLE droppk2 DETACH PARTITION droppk2_d; ALTER TABLE droppk DETACH PARTITION droppk1; ALTER TABLE droppk DETACH PARTITION droppk2; ALTER TABLE droppk2 DETACH PARTITION droppk21; -- dropping partitions is disallowed DROP TABLE droppk_d; DROP TABLE droppk2_d; DROP TABLE droppk1; DROP TABLE droppk2; DROP TABLE droppk21; DELETE FROM dropfk; -- dropping partitions is disallowed, even when no referencing values DROP TABLE droppk_d; DROP TABLE droppk2_d; DROP TABLE droppk1; -- but DETACH is allowed, and DROP afterwards works ALTER TABLE droppk2 DETACH PARTITION droppk21; DROP TABLE droppk2; -- Verify that initial constraint creation and cloning behave correctly CREATE SCHEMA fkpart5; SET search_path TO fkpart5; CREATE TABLE pk (a int PRIMARY KEY) PARTITION BY LIST (a); CREATE TABLE pk1 PARTITION OF pk FOR VALUES IN (1) PARTITION BY LIST (a); CREATE TABLE pk11 PARTITION OF pk1 FOR VALUES IN (1); CREATE TABLE fk (a int) PARTITION BY LIST (a); CREATE TABLE fk1 PARTITION OF fk FOR VALUES IN (1) PARTITION BY LIST (a); CREATE TABLE fk11 PARTITION OF fk1 FOR VALUES IN (1); ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk; CREATE TABLE pk2 PARTITION OF pk FOR VALUES IN (2); CREATE TABLE pk3 (a int NOT NULL) PARTITION BY LIST (a); CREATE TABLE pk31 PARTITION OF pk3 FOR VALUES IN (31); CREATE TABLE pk32 (b int, a int NOT NULL); ALTER TABLE pk32 DROP COLUMN b; ALTER TABLE pk3 ATTACH PARTITION pk32 FOR VALUES IN (32); ALTER TABLE pk ATTACH PARTITION pk3 FOR VALUES IN (31, 32); CREATE TABLE fk2 PARTITION OF fk FOR VALUES IN (2); CREATE TABLE fk3 (b int, a int); ALTER TABLE fk3 DROP COLUMN b; ALTER TABLE fk ATTACH PARTITION fk3 FOR VALUES IN (3); SELECT pg_describe_object('pg_constraint'::regclass, oid, 0), confrelid::regclass, CASE WHEN conparentid <> 0 THEN pg_describe_object('pg_constraint'::regclass, conparentid, 0) ELSE 'TOP' END FROM pg_catalog.pg_constraint WHERE conrelid IN (SELECT relid FROM pg_partition_tree('fk')) ORDER BY conrelid::regclass::text, conname; CREATE TABLE fk4 (LIKE fk); INSERT INTO fk4 VALUES (50); ALTER TABLE fk ATTACH PARTITION fk4 FOR VALUES IN (50); -- Verify ON UPDATE/DELETE behavior CREATE SCHEMA fkpart6; SET search_path TO fkpart6; CREATE TABLE pk (a int PRIMARY KEY) PARTITION BY RANGE (a); CREATE TABLE pk1 PARTITION OF pk FOR VALUES FROM (1) TO (100) PARTITION BY RANGE (a); CREATE TABLE pk11 PARTITION OF pk1 FOR VALUES FROM (1) TO (50); CREATE TABLE pk12 PARTITION OF pk1 FOR VALUES FROM (50) TO (100); CREATE TABLE fk (a int) PARTITION BY RANGE (a); CREATE TABLE fk1 PARTITION OF fk FOR VALUES FROM (1) TO (100) PARTITION BY RANGE (a); CREATE TABLE fk11 PARTITION OF fk1 FOR VALUES FROM (1) TO (10); CREATE TABLE fk12 PARTITION OF fk1 FOR VALUES FROM (10) TO (100); ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk ON UPDATE CASCADE ON DELETE CASCADE; CREATE TABLE fk_d PARTITION OF fk DEFAULT; INSERT INTO pk VALUES (1); INSERT INTO fk VALUES (1); UPDATE pk SET a = 20; SELECT tableoid::regclass, * FROM fk; DELETE FROM pk WHERE a = 20; SELECT tableoid::regclass, * FROM fk; DROP TABLE fk; TRUNCATE TABLE pk; INSERT INTO pk VALUES (20), (50); CREATE TABLE fk (a int) PARTITION BY RANGE (a); CREATE TABLE fk1 PARTITION OF fk FOR VALUES FROM (1) TO (100) PARTITION BY RANGE (a); CREATE TABLE fk11 PARTITION OF fk1 FOR VALUES FROM (1) TO (10); CREATE TABLE fk12 PARTITION OF fk1 FOR VALUES FROM (10) TO (100); ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk ON UPDATE SET NULL ON DELETE SET NULL; CREATE TABLE fk_d PARTITION OF fk DEFAULT; INSERT INTO fk VALUES (20), (50); UPDATE pk SET a = 21 WHERE a = 20; DELETE FROM pk WHERE a = 50; SELECT tableoid::regclass, * FROM fk; DROP TABLE fk; TRUNCATE TABLE pk; INSERT INTO pk VALUES (20), (30), (50); CREATE TABLE fk (id int, a int DEFAULT 50) PARTITION BY RANGE (a); CREATE TABLE fk1 PARTITION OF fk FOR VALUES FROM (1) TO (100) PARTITION BY RANGE (a); CREATE TABLE fk11 PARTITION OF fk1 FOR VALUES FROM (1) TO (10); CREATE TABLE fk12 PARTITION OF fk1 FOR VALUES FROM (10) TO (100); ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk ON UPDATE SET DEFAULT ON DELETE SET DEFAULT; CREATE TABLE fk_d PARTITION OF fk DEFAULT; INSERT INTO fk VALUES (1, 20), (2, 30); DELETE FROM pk WHERE a = 20 RETURNING *; UPDATE pk SET a = 90 WHERE a = 30 RETURNING *; SELECT tableoid::regclass, * FROM fk; DROP TABLE fk; TRUNCATE TABLE pk; INSERT INTO pk VALUES (20), (30); CREATE TABLE fk (a int DEFAULT 50) PARTITION BY RANGE (a); CREATE TABLE fk1 PARTITION OF fk FOR VALUES FROM (1) TO (100) PARTITION BY RANGE (a); CREATE TABLE fk11 PARTITION OF fk1 FOR VALUES FROM (1) TO (10); CREATE TABLE fk12 PARTITION OF fk1 FOR VALUES FROM (10) TO (100); ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk ON UPDATE RESTRICT ON DELETE RESTRICT; CREATE TABLE fk_d PARTITION OF fk DEFAULT; INSERT INTO fk VALUES (20), (30); DELETE FROM pk WHERE a = 20; UPDATE pk SET a = 90 WHERE a = 30; SELECT tableoid::regclass, * FROM fk; DROP TABLE fk; -- test for reported bug: relispartition not set -- https://postgr.es/m/CA+HiwqHMsRtRYRWYTWavKJ8x14AFsv7bmAV46mYwnfD3vy8goQ@mail.gmail.com CREATE SCHEMA fkpart7 CREATE TABLE pkpart (a int) PARTITION BY LIST (a) CREATE TABLE pkpart1 PARTITION OF pkpart FOR VALUES IN (1); ALTER TABLE fkpart7.pkpart1 ADD PRIMARY KEY (a); ALTER TABLE fkpart7.pkpart ADD PRIMARY KEY (a); CREATE TABLE fkpart7.fk (a int REFERENCES fkpart7.pkpart); DROP SCHEMA fkpart7 CASCADE; pgFormatter-4.2/t/pg-test-files/sql/fsm.sql000066400000000000000000000042511361326045100207070ustar00rootroot00000000000000-- -- Free Space Map test -- SELECT current_setting('block_size')::integer AS blocksize, current_setting('block_size')::integer / 8 AS strsize \gset CREATE TABLE fsm_check_size (num int, str text); -- Fill 3 blocks with one record each ALTER TABLE fsm_check_size SET (fillfactor=15); INSERT INTO fsm_check_size SELECT i, rpad('', :strsize, 'a') FROM generate_series(1,3) i; -- There should be no FSM VACUUM fsm_check_size; SELECT pg_relation_size('fsm_check_size', 'main') / :blocksize AS heap_nblocks, pg_relation_size('fsm_check_size', 'fsm') / :blocksize AS fsm_nblocks; -- The following operations are for testing the functionality of the local -- in-memory map. In particular, we want to be able to insert into some -- other block than the one at the end of the heap, without using a FSM. -- Fill most of the last block ALTER TABLE fsm_check_size SET (fillfactor=100); INSERT INTO fsm_check_size SELECT i, rpad('', :strsize, 'a') FROM generate_series(101,105) i; -- Make sure records can go into any block but the last one ALTER TABLE fsm_check_size SET (fillfactor=30); -- Insert large record and make sure it does not cause the relation to extend INSERT INTO fsm_check_size VALUES (111, rpad('', :strsize, 'a')); VACUUM fsm_check_size; SELECT pg_relation_size('fsm_check_size', 'main') / :blocksize AS heap_nblocks, pg_relation_size('fsm_check_size', 'fsm') / :blocksize AS fsm_nblocks; -- Extend table with enough blocks to exceed the FSM threshold DO $$ DECLARE curtid tid; num int; BEGIN num = 11; LOOP INSERT INTO fsm_check_size VALUES (num, 'b') RETURNING ctid INTO curtid; EXIT WHEN curtid >= tid '(4, 0)'; num = num + 1; END LOOP; END; $$; VACUUM fsm_check_size; SELECT pg_relation_size('fsm_check_size', 'fsm') / :blocksize AS fsm_nblocks; -- Add long random string to extend TOAST table to 1 block INSERT INTO fsm_check_size VALUES(0, (SELECT string_agg(md5(chr(i)), '') FROM generate_series(1, :blocksize / 100) i)); VACUUM fsm_check_size; SELECT pg_relation_size(reltoastrelid, 'main') / :blocksize AS toast_nblocks, pg_relation_size(reltoastrelid, 'fsm') / :blocksize AS toast_fsm_nblocks FROM pg_class WHERE relname = 'fsm_check_size'; DROP TABLE fsm_check_size; pgFormatter-4.2/t/pg-test-files/sql/functional_deps.sql000066400000000000000000000124151361326045100233000ustar00rootroot00000000000000-- from http://www.depesz.com/index.php/2010/04/19/getting-unique-elements/ CREATE TEMP TABLE articles ( id int CONSTRAINT articles_pkey PRIMARY KEY, keywords text, title text UNIQUE NOT NULL, body text UNIQUE, created date ); CREATE TEMP TABLE articles_in_category ( article_id int, category_id int, changed date, PRIMARY KEY (article_id, category_id) ); -- test functional dependencies based on primary keys/unique constraints -- base tables -- group by primary key (OK) SELECT id, keywords, title, body, created FROM articles GROUP BY id; -- group by unique not null (fail/todo) SELECT id, keywords, title, body, created FROM articles GROUP BY title; -- group by unique nullable (fail) SELECT id, keywords, title, body, created FROM articles GROUP BY body; -- group by something else (fail) SELECT id, keywords, title, body, created FROM articles GROUP BY keywords; -- multiple tables -- group by primary key (OK) SELECT a.id, a.keywords, a.title, a.body, a.created FROM articles AS a, articles_in_category AS aic WHERE a.id = aic.article_id AND aic.category_id in (14,62,70,53,138) GROUP BY a.id; -- group by something else (fail) SELECT a.id, a.keywords, a.title, a.body, a.created FROM articles AS a, articles_in_category AS aic WHERE a.id = aic.article_id AND aic.category_id in (14,62,70,53,138) GROUP BY aic.article_id, aic.category_id; -- JOIN syntax -- group by left table's primary key (OK) SELECT a.id, a.keywords, a.title, a.body, a.created FROM articles AS a JOIN articles_in_category AS aic ON a.id = aic.article_id WHERE aic.category_id in (14,62,70,53,138) GROUP BY a.id; -- group by something else (fail) SELECT a.id, a.keywords, a.title, a.body, a.created FROM articles AS a JOIN articles_in_category AS aic ON a.id = aic.article_id WHERE aic.category_id in (14,62,70,53,138) GROUP BY aic.article_id, aic.category_id; -- group by right table's (composite) primary key (OK) SELECT aic.changed FROM articles AS a JOIN articles_in_category AS aic ON a.id = aic.article_id WHERE aic.category_id in (14,62,70,53,138) GROUP BY aic.category_id, aic.article_id; -- group by right table's partial primary key (fail) SELECT aic.changed FROM articles AS a JOIN articles_in_category AS aic ON a.id = aic.article_id WHERE aic.category_id in (14,62,70,53,138) GROUP BY aic.article_id; -- example from documentation CREATE TEMP TABLE products (product_id int, name text, price numeric); CREATE TEMP TABLE sales (product_id int, units int); -- OK SELECT product_id, p.name, (sum(s.units) * p.price) AS sales FROM products p LEFT JOIN sales s USING (product_id) GROUP BY product_id, p.name, p.price; -- fail SELECT product_id, p.name, (sum(s.units) * p.price) AS sales FROM products p LEFT JOIN sales s USING (product_id) GROUP BY product_id; ALTER TABLE products ADD PRIMARY KEY (product_id); -- OK now SELECT product_id, p.name, (sum(s.units) * p.price) AS sales FROM products p LEFT JOIN sales s USING (product_id) GROUP BY product_id; -- Drupal example, http://drupal.org/node/555530 CREATE TEMP TABLE node ( nid SERIAL, vid integer NOT NULL default '0', type varchar(32) NOT NULL default '', title varchar(128) NOT NULL default '', uid integer NOT NULL default '0', status integer NOT NULL default '1', created integer NOT NULL default '0', -- snip PRIMARY KEY (nid, vid) ); CREATE TEMP TABLE users ( uid integer NOT NULL default '0', name varchar(60) NOT NULL default '', pass varchar(32) NOT NULL default '', -- snip PRIMARY KEY (uid), UNIQUE (name) ); -- OK SELECT u.uid, u.name FROM node n INNER JOIN users u ON u.uid = n.uid WHERE n.type = 'blog' AND n.status = 1 GROUP BY u.uid, u.name; -- OK SELECT u.uid, u.name FROM node n INNER JOIN users u ON u.uid = n.uid WHERE n.type = 'blog' AND n.status = 1 GROUP BY u.uid; -- Check views and dependencies -- fail CREATE TEMP VIEW fdv1 AS SELECT id, keywords, title, body, created FROM articles GROUP BY body; -- OK CREATE TEMP VIEW fdv1 AS SELECT id, keywords, title, body, created FROM articles GROUP BY id; -- fail ALTER TABLE articles DROP CONSTRAINT articles_pkey RESTRICT; DROP VIEW fdv1; -- multiple dependencies CREATE TEMP VIEW fdv2 AS SELECT a.id, a.keywords, a.title, aic.category_id, aic.changed FROM articles AS a JOIN articles_in_category AS aic ON a.id = aic.article_id WHERE aic.category_id in (14,62,70,53,138) GROUP BY a.id, aic.category_id, aic.article_id; ALTER TABLE articles DROP CONSTRAINT articles_pkey RESTRICT; -- fail ALTER TABLE articles_in_category DROP CONSTRAINT articles_in_category_pkey RESTRICT; --fail DROP VIEW fdv2; -- nested queries CREATE TEMP VIEW fdv3 AS SELECT id, keywords, title, body, created FROM articles GROUP BY id UNION SELECT id, keywords, title, body, created FROM articles GROUP BY id; ALTER TABLE articles DROP CONSTRAINT articles_pkey RESTRICT; -- fail DROP VIEW fdv3; CREATE TEMP VIEW fdv4 AS SELECT * FROM articles WHERE title IN (SELECT title FROM articles GROUP BY id); ALTER TABLE articles DROP CONSTRAINT articles_pkey RESTRICT; -- fail DROP VIEW fdv4; -- prepared query plans: this results in failure on reuse PREPARE foo AS SELECT id, keywords, title, body, created FROM articles GROUP BY id; EXECUTE foo; ALTER TABLE articles DROP CONSTRAINT articles_pkey RESTRICT; EXECUTE foo; -- fail pgFormatter-4.2/t/pg-test-files/sql/generated.sql000066400000000000000000000344131361326045100220630ustar00rootroot00000000000000-- sanity check of system catalog SELECT attrelid, attname, attgenerated FROM pg_attribute WHERE attgenerated NOT IN ('', 's'); CREATE TABLE gtest0 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (55) STORED); CREATE TABLE gtest1 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED); SELECT table_name, column_name, column_default, is_nullable, is_generated, generation_expression FROM information_schema.columns WHERE table_name LIKE 'gtest_' ORDER BY 1, 2; SELECT table_name, column_name, dependent_column FROM information_schema.column_column_usage ORDER BY 1, 2, 3; \d gtest1 -- duplicate generated CREATE TABLE gtest_err_1 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED GENERATED ALWAYS AS (a * 3) STORED); -- references to other generated columns, including self-references CREATE TABLE gtest_err_2a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (b * 2) STORED); CREATE TABLE gtest_err_2b (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED, c int GENERATED ALWAYS AS (b * 3) STORED); -- invalid reference CREATE TABLE gtest_err_3 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (c * 2) STORED); -- generation expression must be immutable CREATE TABLE gtest_err_4 (a int PRIMARY KEY, b double precision GENERATED ALWAYS AS (random()) STORED); -- cannot have default/identity and generated CREATE TABLE gtest_err_5a (a int PRIMARY KEY, b int DEFAULT 5 GENERATED ALWAYS AS (a * 2) STORED); CREATE TABLE gtest_err_5b (a int PRIMARY KEY, b int GENERATED ALWAYS AS identity GENERATED ALWAYS AS (a * 2) STORED); -- reference to system column not allowed in generated column CREATE TABLE gtest_err_6a (a int PRIMARY KEY, b bool GENERATED ALWAYS AS (xmin <> 37) STORED); -- various prohibited constructs CREATE TABLE gtest_err_7a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (avg(a)) STORED); CREATE TABLE gtest_err_7b (a int PRIMARY KEY, b int GENERATED ALWAYS AS (row_number() OVER (ORDER BY a)) STORED); CREATE TABLE gtest_err_7c (a int PRIMARY KEY, b int GENERATED ALWAYS AS ((SELECT a)) STORED); CREATE TABLE gtest_err_7d (a int PRIMARY KEY, b int GENERATED ALWAYS AS (generate_series(1, a)) STORED); -- GENERATED BY DEFAULT not allowed CREATE TABLE gtest_err_8 (a int PRIMARY KEY, b int GENERATED BY DEFAULT AS (a * 2) STORED); INSERT INTO gtest1 VALUES (1); INSERT INTO gtest1 VALUES (2, DEFAULT); INSERT INTO gtest1 VALUES (3, 33); -- error SELECT * FROM gtest1 ORDER BY a; UPDATE gtest1 SET b = DEFAULT WHERE a = 1; UPDATE gtest1 SET b = 11 WHERE a = 1; -- error SELECT * FROM gtest1 ORDER BY a; SELECT a, b, b * 2 AS b2 FROM gtest1 ORDER BY a; SELECT a, b FROM gtest1 WHERE b = 4 ORDER BY a; -- test that overflow error happens on write INSERT INTO gtest1 VALUES (2000000000); SELECT * FROM gtest1; DELETE FROM gtest1 WHERE a = 2000000000; -- test with joins CREATE TABLE gtestx (x int, y int); INSERT INTO gtestx VALUES (11, 1), (22, 2), (33, 3); SELECT * FROM gtestx, gtest1 WHERE gtestx.y = gtest1.a; DROP TABLE gtestx; -- test UPDATE/DELETE quals SELECT * FROM gtest1 ORDER BY a; UPDATE gtest1 SET a = 3 WHERE b = 4; SELECT * FROM gtest1 ORDER BY a; DELETE FROM gtest1 WHERE b = 2; SELECT * FROM gtest1 ORDER BY a; -- views CREATE VIEW gtest1v AS SELECT * FROM gtest1; SELECT * FROM gtest1v; INSERT INTO gtest1v VALUES (4, 8); -- fails DROP VIEW gtest1v; -- CTEs WITH foo AS (SELECT * FROM gtest1) SELECT * FROM foo; -- inheritance CREATE TABLE gtest1_1 () INHERITS (gtest1); SELECT * FROM gtest1_1; \d gtest1_1 INSERT INTO gtest1_1 VALUES (4); SELECT * FROM gtest1_1; SELECT * FROM gtest1; -- test inheritance mismatch CREATE TABLE gtesty (x int, b int); CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error DROP TABLE gtesty; -- test stored update CREATE TABLE gtest3 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 3) STORED); INSERT INTO gtest3 (a) VALUES (1), (2), (3); SELECT * FROM gtest3 ORDER BY a; UPDATE gtest3 SET a = 22 WHERE a = 2; SELECT * FROM gtest3 ORDER BY a; -- COPY TRUNCATE gtest1; INSERT INTO gtest1 (a) VALUES (1), (2); SELECT * FROM gtest1 ORDER BY a; TRUNCATE gtest3; INSERT INTO gtest3 (a) VALUES (1), (2); SELECT * FROM gtest3 ORDER BY a; -- null values CREATE TABLE gtest2 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (NULL) STORED); INSERT INTO gtest2 VALUES (1); SELECT * FROM gtest2; -- composite types CREATE TYPE double_int as (a int, b int); CREATE TABLE gtest4 ( a int, b double_int GENERATED ALWAYS AS ((a * 2, a * 3)) STORED ); INSERT INTO gtest4 VALUES (1), (6); SELECT * FROM gtest4; DROP TABLE gtest4; DROP TYPE double_int; -- using tableoid is allowed CREATE TABLE gtest_tableoid ( a int PRIMARY KEY, b bool GENERATED ALWAYS AS (tableoid <> 0) STORED ); INSERT INTO gtest_tableoid VALUES (1), (2); SELECT * FROM gtest_tableoid; -- drop column behavior CREATE TABLE gtest10 (a int PRIMARY KEY, b int, c int GENERATED ALWAYS AS (b * 2) STORED); ALTER TABLE gtest10 DROP COLUMN b; \d gtest10 CREATE TABLE gtest10a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED); ALTER TABLE gtest10a DROP COLUMN b; INSERT INTO gtest10a (a) VALUES (1); -- privileges CREATE USER regress_user11; CREATE TABLE gtest11s (a int PRIMARY KEY, b int, c int GENERATED ALWAYS AS (b * 2) STORED); INSERT INTO gtest11s VALUES (1, 10), (2, 20); GRANT SELECT (a, c) ON gtest11s TO regress_user11; CREATE FUNCTION gf1(a int) RETURNS int AS $$ SELECT a * 3 $$ IMMUTABLE LANGUAGE SQL; REVOKE ALL ON FUNCTION gf1(int) FROM PUBLIC; CREATE TABLE gtest12s (a int PRIMARY KEY, b int, c int GENERATED ALWAYS AS (gf1(b)) STORED); INSERT INTO gtest12s VALUES (1, 10), (2, 20); GRANT SELECT (a, c) ON gtest12s TO regress_user11; SET ROLE regress_user11; SELECT a, b FROM gtest11s; -- not allowed SELECT a, c FROM gtest11s; -- allowed SELECT gf1(10); -- not allowed SELECT a, c FROM gtest12s; -- allowed RESET ROLE; DROP TABLE gtest11s, gtest12s; DROP FUNCTION gf1(int); DROP USER regress_user11; -- check constraints CREATE TABLE gtest20 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED CHECK (b < 50)); INSERT INTO gtest20 (a) VALUES (10); -- ok INSERT INTO gtest20 (a) VALUES (30); -- violates constraint CREATE TABLE gtest20a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED); INSERT INTO gtest20a (a) VALUES (10); INSERT INTO gtest20a (a) VALUES (30); ALTER TABLE gtest20a ADD CHECK (b < 50); -- fails on existing row CREATE TABLE gtest20b (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED); INSERT INTO gtest20b (a) VALUES (10); INSERT INTO gtest20b (a) VALUES (30); ALTER TABLE gtest20b ADD CONSTRAINT chk CHECK (b < 50) NOT VALID; ALTER TABLE gtest20b VALIDATE CONSTRAINT chk; -- fails on existing row -- not-null constraints CREATE TABLE gtest21a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (nullif(a, 0)) STORED NOT NULL); INSERT INTO gtest21a (a) VALUES (1); -- ok INSERT INTO gtest21a (a) VALUES (0); -- violates constraint CREATE TABLE gtest21b (a int PRIMARY KEY, b int GENERATED ALWAYS AS (nullif(a, 0)) STORED); ALTER TABLE gtest21b ALTER COLUMN b SET NOT NULL; INSERT INTO gtest21b (a) VALUES (1); -- ok INSERT INTO gtest21b (a) VALUES (0); -- violates constraint ALTER TABLE gtest21b ALTER COLUMN b DROP NOT NULL; INSERT INTO gtest21b (a) VALUES (0); -- ok now -- index constraints CREATE TABLE gtest22a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a / 2) STORED UNIQUE); INSERT INTO gtest22a VALUES (2); INSERT INTO gtest22a VALUES (3); INSERT INTO gtest22a VALUES (4); CREATE TABLE gtest22b (a int, b int GENERATED ALWAYS AS (a / 2) STORED, PRIMARY KEY (a, b)); INSERT INTO gtest22b VALUES (2); INSERT INTO gtest22b VALUES (2); -- indexes CREATE TABLE gtest22c (a int, b int GENERATED ALWAYS AS (a * 2) STORED); CREATE INDEX gtest22c_b_idx ON gtest22c (b); CREATE INDEX gtest22c_expr_idx ON gtest22c ((b * 3)); CREATE INDEX gtest22c_pred_idx ON gtest22c (a) WHERE b > 0; \d gtest22c INSERT INTO gtest22c VALUES (1), (2), (3); SET enable_seqscan TO off; SET enable_bitmapscan TO off; EXPLAIN (COSTS OFF) SELECT * FROM gtest22c WHERE b = 4; SELECT * FROM gtest22c WHERE b = 4; EXPLAIN (COSTS OFF) SELECT * FROM gtest22c WHERE b * 3 = 6; SELECT * FROM gtest22c WHERE b * 3 = 6; EXPLAIN (COSTS OFF) SELECT * FROM gtest22c WHERE a = 1 AND b > 0; SELECT * FROM gtest22c WHERE a = 1 AND b > 0; RESET enable_seqscan; RESET enable_bitmapscan; -- foreign keys CREATE TABLE gtest23a (x int PRIMARY KEY, y int); INSERT INTO gtest23a VALUES (1, 11), (2, 22), (3, 33); CREATE TABLE gtest23x (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED REFERENCES gtest23a (x) ON UPDATE CASCADE); -- error CREATE TABLE gtest23x (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED REFERENCES gtest23a (x) ON DELETE SET NULL); -- error CREATE TABLE gtest23b (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED REFERENCES gtest23a (x)); \d gtest23b INSERT INTO gtest23b VALUES (1); -- ok INSERT INTO gtest23b VALUES (5); -- error DROP TABLE gtest23b; DROP TABLE gtest23a; CREATE TABLE gtest23p (x int, y int GENERATED ALWAYS AS (x * 2) STORED, PRIMARY KEY (y)); INSERT INTO gtest23p VALUES (1), (2), (3); CREATE TABLE gtest23q (a int PRIMARY KEY, b int REFERENCES gtest23p (y)); INSERT INTO gtest23q VALUES (1, 2); -- ok INSERT INTO gtest23q VALUES (2, 5); -- error -- domains CREATE DOMAIN gtestdomain1 AS int CHECK (VALUE < 10); CREATE TABLE gtest24 (a int PRIMARY KEY, b gtestdomain1 GENERATED ALWAYS AS (a * 2) STORED); INSERT INTO gtest24 (a) VALUES (4); -- ok INSERT INTO gtest24 (a) VALUES (6); -- error -- typed tables (currently not supported) CREATE TYPE gtest_type AS (f1 integer, f2 text, f3 bigint); CREATE TABLE gtest28 OF gtest_type (f1 WITH OPTIONS GENERATED ALWAYS AS (f2 *2) STORED); DROP TYPE gtest_type CASCADE; -- table partitions (currently not supported) CREATE TABLE gtest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1); CREATE TABLE gtest_child PARTITION OF gtest_parent ( f3 WITH OPTIONS GENERATED ALWAYS AS (f2 * 2) STORED ) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error DROP TABLE gtest_parent; -- partitioned table CREATE TABLE gtest_parent (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE (f1); CREATE TABLE gtest_child PARTITION OF gtest_parent FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); INSERT INTO gtest_parent (f1, f2) VALUES ('2016-07-15', 1); SELECT * FROM gtest_parent; SELECT * FROM gtest_child; DROP TABLE gtest_parent; -- generated columns in partition key (not allowed) CREATE TABLE gtest_parent (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE (f3); CREATE TABLE gtest_parent (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3 * 3)); -- ALTER TABLE ... ADD COLUMN CREATE TABLE gtest25 (a int PRIMARY KEY); INSERT INTO gtest25 VALUES (3), (4); ALTER TABLE gtest25 ADD COLUMN b int GENERATED ALWAYS AS (a * 3) STORED; SELECT * FROM gtest25 ORDER BY a; ALTER TABLE gtest25 ADD COLUMN x int GENERATED ALWAYS AS (b * 4) STORED; -- error ALTER TABLE gtest25 ADD COLUMN x int GENERATED ALWAYS AS (z * 4) STORED; -- error -- ALTER TABLE ... ALTER COLUMN CREATE TABLE gtest27 ( a int, b int GENERATED ALWAYS AS (a * 2) STORED ); INSERT INTO gtest27 (a) VALUES (3), (4); ALTER TABLE gtest27 ALTER COLUMN a TYPE text; -- error ALTER TABLE gtest27 ALTER COLUMN b TYPE numeric; \d gtest27 SELECT * FROM gtest27; ALTER TABLE gtest27 ALTER COLUMN b TYPE boolean USING b <> 0; -- error ALTER TABLE gtest27 ALTER COLUMN b DROP DEFAULT; -- error \d gtest27 -- triggers CREATE TABLE gtest26 ( a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED ); CREATE FUNCTION gtest_trigger_func() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN IF tg_op IN ('DELETE', 'UPDATE') THEN RAISE INFO '%: %: old = %', TG_NAME, TG_WHEN, OLD; END IF; IF tg_op IN ('INSERT', 'UPDATE') THEN RAISE INFO '%: %: new = %', TG_NAME, TG_WHEN, NEW; END IF; IF tg_op = 'DELETE' THEN RETURN OLD; ELSE RETURN NEW; END IF; END $$; CREATE TRIGGER gtest1 BEFORE DELETE OR UPDATE ON gtest26 FOR EACH ROW WHEN (OLD.b < 0) -- ok EXECUTE PROCEDURE gtest_trigger_func(); CREATE TRIGGER gtest2a BEFORE INSERT OR UPDATE ON gtest26 FOR EACH ROW WHEN (NEW.b < 0) -- error EXECUTE PROCEDURE gtest_trigger_func(); CREATE TRIGGER gtest2b BEFORE INSERT OR UPDATE ON gtest26 FOR EACH ROW WHEN (NEW.* IS NOT NULL) -- error EXECUTE PROCEDURE gtest_trigger_func(); CREATE TRIGGER gtest2 BEFORE INSERT ON gtest26 FOR EACH ROW WHEN (NEW.a < 0) EXECUTE PROCEDURE gtest_trigger_func(); CREATE TRIGGER gtest3 AFTER DELETE OR UPDATE ON gtest26 FOR EACH ROW WHEN (OLD.b < 0) -- ok EXECUTE PROCEDURE gtest_trigger_func(); CREATE TRIGGER gtest4 AFTER INSERT OR UPDATE ON gtest26 FOR EACH ROW WHEN (NEW.b < 0) -- ok EXECUTE PROCEDURE gtest_trigger_func(); INSERT INTO gtest26 (a) VALUES (-2), (0), (3); SELECT * FROM gtest26 ORDER BY a; UPDATE gtest26 SET a = a * -2; SELECT * FROM gtest26 ORDER BY a; DELETE FROM gtest26 WHERE a = -6; SELECT * FROM gtest26 ORDER BY a; DROP TRIGGER gtest1 ON gtest26; DROP TRIGGER gtest2 ON gtest26; DROP TRIGGER gtest3 ON gtest26; -- Check that an UPDATE of "a" fires the trigger for UPDATE OF b, per -- SQL standard. CREATE FUNCTION gtest_trigger_func3() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN RAISE NOTICE 'OK'; RETURN NEW; END $$; CREATE TRIGGER gtest11 BEFORE UPDATE OF b ON gtest26 FOR EACH ROW EXECUTE PROCEDURE gtest_trigger_func3(); UPDATE gtest26 SET a = 1 WHERE a = 0; DROP TRIGGER gtest11 ON gtest26; TRUNCATE gtest26; -- check that modifications of stored generated columns in triggers do -- not get propagated CREATE FUNCTION gtest_trigger_func4() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NEW.a = 10; NEW.b = 300; RETURN NEW; END; $$; CREATE TRIGGER gtest12_01 BEFORE UPDATE ON gtest26 FOR EACH ROW EXECUTE PROCEDURE gtest_trigger_func(); CREATE TRIGGER gtest12_02 BEFORE UPDATE ON gtest26 FOR EACH ROW EXECUTE PROCEDURE gtest_trigger_func4(); CREATE TRIGGER gtest12_03 BEFORE UPDATE ON gtest26 FOR EACH ROW EXECUTE PROCEDURE gtest_trigger_func(); INSERT INTO gtest26 (a) VALUES (1); UPDATE gtest26 SET a = 11 WHERE a = 1; SELECT * FROM gtest26 ORDER BY a; -- LIKE INCLUDING GENERATED and dropped column handling CREATE TABLE gtest28a ( a int, b int, c int, x int GENERATED ALWAYS AS (b * 2) STORED ); ALTER TABLE gtest28a DROP COLUMN a; CREATE TABLE gtest28b (LIKE gtest28a INCLUDING GENERATED); \d gtest28* pgFormatter-4.2/t/pg-test-files/sql/geometry.sql000066400000000000000000000332331361326045100217570ustar00rootroot00000000000000-- -- GEOMETRY -- -- Back off displayed precision a little bit to reduce platform-to-platform -- variation in results. SET extra_float_digits TO -3; -- -- Points -- SELECT '' AS four, center(f1) AS center FROM BOX_TBL; SELECT '' AS four, (@@ f1) AS center FROM BOX_TBL; SELECT '' AS six, point(f1) AS center FROM CIRCLE_TBL; SELECT '' AS six, (@@ f1) AS center FROM CIRCLE_TBL; SELECT '' AS two, (@@ f1) AS center FROM POLYGON_TBL WHERE (# f1) > 2; -- "is horizontal" function SELECT '' AS two, p1.f1 FROM POINT_TBL p1 WHERE ishorizontal(p1.f1, point '(0,0)'); -- "is horizontal" operator SELECT '' AS two, p1.f1 FROM POINT_TBL p1 WHERE p1.f1 ?- point '(0,0)'; -- "is vertical" function SELECT '' AS one, p1.f1 FROM POINT_TBL p1 WHERE isvertical(p1.f1, point '(5.1,34.5)'); -- "is vertical" operator SELECT '' AS one, p1.f1 FROM POINT_TBL p1 WHERE p1.f1 ?| point '(5.1,34.5)'; -- Slope SELECT p1.f1, p2.f1, slope(p1.f1, p2.f1) FROM POINT_TBL p1, POINT_TBL p2; -- Add point SELECT p1.f1, p2.f1, p1.f1 + p2.f1 FROM POINT_TBL p1, POINT_TBL p2; -- Subtract point SELECT p1.f1, p2.f1, p1.f1 - p2.f1 FROM POINT_TBL p1, POINT_TBL p2; -- Multiply with point SELECT p1.f1, p2.f1, p1.f1 * p2.f1 FROM POINT_TBL p1, POINT_TBL p2 WHERE p1.f1[0] BETWEEN 1 AND 1000; -- Underflow error SELECT p1.f1, p2.f1, p1.f1 * p2.f1 FROM POINT_TBL p1, POINT_TBL p2 WHERE p1.f1[0] < 1; -- Divide by point SELECT p1.f1, p2.f1, p1.f1 / p2.f1 FROM POINT_TBL p1, POINT_TBL p2 WHERE p2.f1[0] BETWEEN 1 AND 1000; -- Overflow error SELECT p1.f1, p2.f1, p1.f1 / p2.f1 FROM POINT_TBL p1, POINT_TBL p2 WHERE p2.f1[0] > 1000; -- Division by 0 error SELECT p1.f1, p2.f1, p1.f1 / p2.f1 FROM POINT_TBL p1, POINT_TBL p2 WHERE p2.f1 ~= '(0,0)'::point; -- Distance to line SELECT p.f1, l.s, p.f1 <-> l.s FROM POINT_TBL p, LINE_TBL l; -- Distance to line segment SELECT p.f1, l.s, p.f1 <-> l.s FROM POINT_TBL p, LSEG_TBL l; -- Distance to box SELECT p.f1, b.f1, p.f1 <-> b.f1 FROM POINT_TBL p, BOX_TBL b; -- Distance to path SELECT p.f1, p1.f1, p.f1 <-> p1.f1 FROM POINT_TBL p, PATH_TBL p1; -- Distance to polygon SELECT p.f1, p1.f1, p.f1 <-> p1.f1 FROM POINT_TBL p, POLYGON_TBL p1; -- Closest point to line SELECT p.f1, l.s, p.f1 ## l.s FROM POINT_TBL p, LINE_TBL l; -- Closest point to line segment SELECT p.f1, l.s, p.f1 ## l.s FROM POINT_TBL p, LSEG_TBL l; -- Closest point to box SELECT p.f1, b.f1, p.f1 ## b.f1 FROM POINT_TBL p, BOX_TBL b; -- On line SELECT p.f1, l.s FROM POINT_TBL p, LINE_TBL l WHERE p.f1 <@ l.s; -- On line segment SELECT p.f1, l.s FROM POINT_TBL p, LSEG_TBL l WHERE p.f1 <@ l.s; -- On path SELECT p.f1, p1.f1 FROM POINT_TBL p, PATH_TBL p1 WHERE p.f1 <@ p1.f1; -- -- Lines -- -- Vertical SELECT s FROM LINE_TBL WHERE ?| s; -- Horizontal SELECT s FROM LINE_TBL WHERE ?- s; -- Same as line SELECT l1.s, l2.s FROM LINE_TBL l1, LINE_TBL l2 WHERE l1.s = l2.s; -- Parallel to line SELECT l1.s, l2.s FROM LINE_TBL l1, LINE_TBL l2 WHERE l1.s ?|| l2.s; -- Perpendicular to line SELECT l1.s, l2.s FROM LINE_TBL l1, LINE_TBL l2 WHERE l1.s ?-| l2.s; -- Distance to line SELECT l1.s, l2.s, l1.s <-> l2.s FROM LINE_TBL l1, LINE_TBL l2; -- Distance to box SELECT l.s, b.f1, l.s <-> b.f1 FROM LINE_TBL l, BOX_TBL b; -- Intersect with line SELECT l1.s, l2.s FROM LINE_TBL l1, LINE_TBL l2 WHERE l1.s ?# l2.s; -- Intersect with box SELECT l.s, b.f1 FROM LINE_TBL l, BOX_TBL b WHERE l.s ?# b.f1; -- Intersection point with line SELECT l1.s, l2.s, l1.s # l2.s FROM LINE_TBL l1, LINE_TBL l2; -- Closest point to line segment SELECT l.s, l1.s, l.s ## l1.s FROM LINE_TBL l, LSEG_TBL l1; -- Closest point to box SELECT l.s, b.f1, l.s ## b.f1 FROM LINE_TBL l, BOX_TBL b; -- -- Line segments -- -- intersection SELECT '' AS count, p.f1, l.s, l.s # p.f1 AS intersection FROM LSEG_TBL l, POINT_TBL p; -- Length SELECT s, @-@ s FROM LSEG_TBL; -- Vertical SELECT s FROM LSEG_TBL WHERE ?| s; -- Horizontal SELECT s FROM LSEG_TBL WHERE ?- s; -- Center SELECT s, @@ s FROM LSEG_TBL; -- To point SELECT s, s::point FROM LSEG_TBL; -- Has points less than line segment SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s < l2.s; -- Has points less than or equal to line segment SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s <= l2.s; -- Has points equal to line segment SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s = l2.s; -- Has points greater than or equal to line segment SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s >= l2.s; -- Has points greater than line segment SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s > l2.s; -- Has points not equal to line segment SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s != l2.s; -- Parallel with line segment SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s ?|| l2.s; -- Perpendicular with line segment SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s ?-| l2.s; -- Distance to line SELECT l.s, l1.s, l.s <-> l1.s FROM LSEG_TBL l, LINE_TBL l1; -- Distance to line segment SELECT l1.s, l2.s, l1.s <-> l2.s FROM LSEG_TBL l1, LSEG_TBL l2; -- Distance to box SELECT l.s, b.f1, l.s <-> b.f1 FROM LSEG_TBL l, BOX_TBL b; -- Intersect with line segment SELECT l.s, l1.s FROM LSEG_TBL l, LINE_TBL l1 WHERE l.s ?# l1.s; -- Intersect with box SELECT l.s, b.f1 FROM LSEG_TBL l, BOX_TBL b WHERE l.s ?# b.f1; -- Intersection point with line segment SELECT l1.s, l2.s, l1.s # l2.s FROM LSEG_TBL l1, LSEG_TBL l2; -- Closest point to line SELECT l.s, l1.s, l.s ## l1.s FROM LSEG_TBL l, LINE_TBL l1; -- Closest point to line segment SELECT l1.s, l2.s, l1.s ## l2.s FROM LSEG_TBL l1, LSEG_TBL l2; -- Closest point to box SELECT l.s, b.f1, l.s ## b.f1 FROM LSEG_TBL l, BOX_TBL b; -- On line SELECT l.s, l1.s FROM LSEG_TBL l, LINE_TBL l1 WHERE l.s <@ l1.s; -- On box SELECT l.s, b.f1 FROM LSEG_TBL l, BOX_TBL b WHERE l.s <@ b.f1; -- -- Boxes -- SELECT '' as six, box(f1) AS box FROM CIRCLE_TBL; -- translation SELECT '' AS twentyfour, b.f1 + p.f1 AS translation FROM BOX_TBL b, POINT_TBL p; SELECT '' AS twentyfour, b.f1 - p.f1 AS translation FROM BOX_TBL b, POINT_TBL p; -- Multiply with point SELECT b.f1, p.f1, b.f1 * p.f1 FROM BOX_TBL b, POINT_TBL p WHERE p.f1[0] BETWEEN 1 AND 1000; -- Overflow error SELECT b.f1, p.f1, b.f1 * p.f1 FROM BOX_TBL b, POINT_TBL p WHERE p.f1[0] > 1000; -- Divide by point SELECT b.f1, p.f1, b.f1 / p.f1 FROM BOX_TBL b, POINT_TBL p WHERE p.f1[0] BETWEEN 1 AND 1000; -- To box SELECT f1::box FROM POINT_TBL; SELECT bound_box(a.f1, b.f1) FROM BOX_TBL a, BOX_TBL b; -- Below box SELECT b1.f1, b2.f1, b1.f1 <^ b2.f1 FROM BOX_TBL b1, BOX_TBL b2; -- Above box SELECT b1.f1, b2.f1, b1.f1 >^ b2.f1 FROM BOX_TBL b1, BOX_TBL b2; -- Intersection point with box SELECT b1.f1, b2.f1, b1.f1 # b2.f1 FROM BOX_TBL b1, BOX_TBL b2; -- Diagonal SELECT f1, diagonal(f1) FROM BOX_TBL; -- Distance to box SELECT b1.f1, b2.f1, b1.f1 <-> b2.f1 FROM BOX_TBL b1, BOX_TBL b2; -- -- Paths -- -- Points SELECT f1, npoints(f1) FROM PATH_TBL; -- Area SELECT f1, area(f1) FROM PATH_TBL; -- Length SELECT f1, @-@ f1 FROM PATH_TBL; -- Center SELECT f1, @@ f1 FROM PATH_TBL; -- To polygon SELECT f1, f1::polygon FROM PATH_TBL WHERE isclosed(f1); -- Open path cannot be converted to polygon error SELECT f1, f1::polygon FROM PATH_TBL WHERE isopen(f1); -- Has points less than path SELECT p1.f1, p2.f1 FROM PATH_TBL p1, PATH_TBL p2 WHERE p1.f1 < p2.f1; -- Has points less than or equal to path SELECT p1.f1, p2.f1 FROM PATH_TBL p1, PATH_TBL p2 WHERE p1.f1 <= p2.f1; -- Has points equal to path SELECT p1.f1, p2.f1 FROM PATH_TBL p1, PATH_TBL p2 WHERE p1.f1 = p2.f1; -- Has points greater than or equal to path SELECT p1.f1, p2.f1 FROM PATH_TBL p1, PATH_TBL p2 WHERE p1.f1 >= p2.f1; -- Has points greater than path SELECT p1.f1, p2.f1 FROM PATH_TBL p1, PATH_TBL p2 WHERE p1.f1 > p2.f1; -- Add path SELECT p1.f1, p2.f1, p1.f1 + p2.f1 FROM PATH_TBL p1, PATH_TBL p2; -- Add point SELECT p.f1, p1.f1, p.f1 + p1.f1 FROM PATH_TBL p, POINT_TBL p1; -- Subtract point SELECT p.f1, p1.f1, p.f1 - p1.f1 FROM PATH_TBL p, POINT_TBL p1; -- Multiply with point SELECT p.f1, p1.f1, p.f1 * p1.f1 FROM PATH_TBL p, POINT_TBL p1; -- Divide by point SELECT p.f1, p1.f1, p.f1 / p1.f1 FROM PATH_TBL p, POINT_TBL p1 WHERE p1.f1[0] BETWEEN 1 AND 1000; -- Division by 0 error SELECT p.f1, p1.f1, p.f1 / p1.f1 FROM PATH_TBL p, POINT_TBL p1 WHERE p1.f1 ~= '(0,0)'::point; -- Distance to path SELECT p1.f1, p2.f1, p1.f1 <-> p2.f1 FROM PATH_TBL p1, PATH_TBL p2; -- -- Polygons -- -- containment SELECT '' AS twentyfour, p.f1, poly.f1, poly.f1 @> p.f1 AS contains FROM POLYGON_TBL poly, POINT_TBL p; SELECT '' AS twentyfour, p.f1, poly.f1, p.f1 <@ poly.f1 AS contained FROM POLYGON_TBL poly, POINT_TBL p; SELECT '' AS four, npoints(f1) AS npoints, f1 AS polygon FROM POLYGON_TBL; SELECT '' AS four, polygon(f1) FROM BOX_TBL; SELECT '' AS four, polygon(f1) FROM PATH_TBL WHERE isclosed(f1); SELECT '' AS four, f1 AS open_path, polygon( pclose(f1)) AS polygon FROM PATH_TBL WHERE isopen(f1); -- To box SELECT f1, f1::box FROM POLYGON_TBL; -- To path SELECT f1, f1::path FROM POLYGON_TBL; -- Same as polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 ~= p2.f1; -- Contained by polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 <@ p2.f1; -- Contains polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 @> p2.f1; -- Overlap with polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 && p2.f1; -- Left of polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 << p2.f1; -- Overlap of left of polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 &< p2.f1; -- Right of polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 >> p2.f1; -- Overlap of right of polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 &> p2.f1; -- Below polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 <<| p2.f1; -- Overlap or below polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 &<| p2.f1; -- Above polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 |>> p2.f1; -- Overlap or above polygon SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 |&> p2.f1; -- Distance to polygon SELECT p1.f1, p2.f1, p1.f1 <-> p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2; -- -- Circles -- SELECT '' AS six, circle(f1, 50.0) FROM POINT_TBL; SELECT '' AS four, circle(f1) FROM BOX_TBL; SELECT '' AS two, circle(f1) FROM POLYGON_TBL WHERE (# f1) >= 3; SELECT '' AS twentyfour, c1.f1 AS circle, p1.f1 AS point, (p1.f1 <-> c1.f1) AS distance FROM CIRCLE_TBL c1, POINT_TBL p1 WHERE (p1.f1 <-> c1.f1) > 0 ORDER BY distance, area(c1.f1), p1.f1[0]; -- To polygon SELECT f1, f1::polygon FROM CIRCLE_TBL WHERE f1 >= '<(0,0),1>'; -- To polygon with less points SELECT f1, polygon(8, f1) FROM CIRCLE_TBL WHERE f1 >= '<(0,0),1>'; -- Too less points error SELECT f1, polygon(1, f1) FROM CIRCLE_TBL WHERE f1 >= '<(0,0),1>'; -- Zero radius error SELECT f1, polygon(10, f1) FROM CIRCLE_TBL WHERE f1 < '<(0,0),1>'; -- Same as circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 ~= c2.f1; -- Overlap with circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 && c2.f1; -- Overlap or left of circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 &< c2.f1; -- Left of circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 << c2.f1; -- Right of circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 >> c2.f1; -- Overlap or right of circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 &> c2.f1; -- Contained by circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 <@ c2.f1; -- Contain by circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 @> c2.f1; -- Below circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 <<| c2.f1; -- Above circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 |>> c2.f1; -- Overlap or below circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 &<| c2.f1; -- Overlap or above circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 |&> c2.f1; -- Area equal with circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 = c2.f1; -- Area not equal with circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 != c2.f1; -- Area less than circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 < c2.f1; -- Area greater than circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 > c2.f1; -- Area less than or equal circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 <= c2.f1; -- Area greater than or equal circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 >= c2.f1; -- Area less than circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 < c2.f1; -- Area greater than circle SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 < c2.f1; -- Add point SELECT c.f1, p.f1, c.f1 + p.f1 FROM CIRCLE_TBL c, POINT_TBL p; -- Subtract point SELECT c.f1, p.f1, c.f1 - p.f1 FROM CIRCLE_TBL c, POINT_TBL p; -- Multiply with point SELECT c.f1, p.f1, c.f1 * p.f1 FROM CIRCLE_TBL c, POINT_TBL p; -- Divide by point SELECT c.f1, p.f1, c.f1 / p.f1 FROM CIRCLE_TBL c, POINT_TBL p WHERE p.f1[0] BETWEEN 1 AND 1000; -- Overflow error SELECT c.f1, p.f1, c.f1 / p.f1 FROM CIRCLE_TBL c, POINT_TBL p WHERE p.f1[0] > 1000; -- Division by 0 error SELECT c.f1, p.f1, c.f1 / p.f1 FROM CIRCLE_TBL c, POINT_TBL p WHERE p.f1 ~= '(0,0)'::point; -- Distance to polygon SELECT c.f1, p.f1, c.f1 <-> p.f1 FROM CIRCLE_TBL c, POLYGON_TBL p; pgFormatter-4.2/t/pg-test-files/sql/gin.sql000066400000000000000000000026261361326045100207030ustar00rootroot00000000000000-- -- Test GIN indexes. -- -- There are other tests to test different GIN opclassed. This is for testing -- GIN itself. -- Create and populate a test table with a GIN index. create table gin_test_tbl(i int4[]) with (autovacuum_enabled = off); create index gin_test_idx on gin_test_tbl using gin (i) with (fastupdate = on, gin_pending_list_limit = 4096); insert into gin_test_tbl select array[1, 2, g] from generate_series(1, 20000) g; insert into gin_test_tbl select array[1, 3, g] from generate_series(1, 1000) g; select gin_clean_pending_list('gin_test_idx')>10 as many; -- flush the fastupdate buffers insert into gin_test_tbl select array[3, 1, g] from generate_series(1, 1000) g; vacuum gin_test_tbl; -- flush the fastupdate buffers select gin_clean_pending_list('gin_test_idx'); -- nothing to flush -- Test vacuuming delete from gin_test_tbl where i @> array[2]; vacuum gin_test_tbl; -- Disable fastupdate, and do more insertions. With fastupdate enabled, most -- insertions (by flushing the list pages) cause page splits. Without -- fastupdate, we get more churn in the GIN data leaf pages, and exercise the -- recompression codepaths. alter index gin_test_idx set (fastupdate = off); insert into gin_test_tbl select array[1, 2, g] from generate_series(1, 1000) g; insert into gin_test_tbl select array[1, 3, g] from generate_series(1, 1000) g; delete from gin_test_tbl where i @> array[2]; vacuum gin_test_tbl; pgFormatter-4.2/t/pg-test-files/sql/gist.sql000066400000000000000000000104561361326045100210740ustar00rootroot00000000000000-- -- Test GiST indexes. -- -- There are other tests to test different GiST opclasses. This is for -- testing GiST code itself. Vacuuming in particular. create table gist_point_tbl(id int4, p point); create index gist_pointidx on gist_point_tbl using gist(p); -- Verify the fillfactor and buffering options create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50); create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off); create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto); drop index gist_pointidx2, gist_pointidx3, gist_pointidx4; -- Make sure bad values are refused create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value); create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9); create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101); -- Insert enough data to create a tree that's a couple of levels deep. insert into gist_point_tbl (id, p) select g, point(g*10, g*10) from generate_series(1, 10000) g; insert into gist_point_tbl (id, p) select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g; -- To test vacuum, delete some entries from all over the index. delete from gist_point_tbl where id % 2 = 1; -- And also delete some concentration of values. delete from gist_point_tbl where id > 5000; vacuum analyze gist_point_tbl; -- rebuild the index with a different fillfactor alter index gist_pointidx SET (fillfactor = 40); reindex index gist_pointidx; -- -- Test Index-only plans on GiST indexes -- create table gist_tbl (b box, p point, c circle); insert into gist_tbl select box(point(0.05*i, 0.05*i), point(0.05*i, 0.05*i)), point(0.05*i, 0.05*i), circle(point(0.05*i, 0.05*i), 1.0) from generate_series(0,10000) as i; vacuum analyze gist_tbl; set enable_seqscan=off; set enable_bitmapscan=off; set enable_indexonlyscan=on; -- Test index-only scan with point opclass create index gist_tbl_point_index on gist_tbl using gist (p); -- check that the planner chooses an index-only scan explain (costs off) select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5)); -- execute the same select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5)); -- Also test an index-only knn-search explain (costs off) select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5)) order by p <-> point(0.201, 0.201); select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5)) order by p <-> point(0.201, 0.201); -- Check commuted case as well explain (costs off) select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5)) order by point(0.101, 0.101) <-> p; select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5)) order by point(0.101, 0.101) <-> p; -- Check case with multiple rescans (bug #14641) explain (costs off) select p from (values (box(point(0,0), point(0.5,0.5))), (box(point(0.5,0.5), point(0.75,0.75))), (box(point(0.8,0.8), point(1.0,1.0)))) as v(bb) cross join lateral (select p from gist_tbl where p <@ bb order by p <-> bb[0] limit 2) ss; select p from (values (box(point(0,0), point(0.5,0.5))), (box(point(0.5,0.5), point(0.75,0.75))), (box(point(0.8,0.8), point(1.0,1.0)))) as v(bb) cross join lateral (select p from gist_tbl where p <@ bb order by p <-> bb[0] limit 2) ss; drop index gist_tbl_point_index; -- Test index-only scan with box opclass create index gist_tbl_box_index on gist_tbl using gist (b); -- check that the planner chooses an index-only scan explain (costs off) select b from gist_tbl where b <@ box(point(5,5), point(6,6)); -- execute the same select b from gist_tbl where b <@ box(point(5,5), point(6,6)); drop index gist_tbl_box_index; -- Test that an index-only scan is not chosen, when the query involves the -- circle column (the circle opclass does not support index-only scans). create index gist_tbl_multi_index on gist_tbl using gist (p, c); explain (costs off) select p, c from gist_tbl where p <@ box(point(5,5), point(6, 6)); -- execute the same select b, p from gist_tbl where b <@ box(point(4.5, 4.5), point(5.5, 5.5)) and p <@ box(point(5,5), point(6, 6)); drop index gist_tbl_multi_index; -- Clean up reset enable_seqscan; reset enable_bitmapscan; reset enable_indexonlyscan; drop table gist_tbl; pgFormatter-4.2/t/pg-test-files/sql/groupingsets.sql000066400000000000000000000376471361326045100226720ustar00rootroot00000000000000-- -- grouping sets -- -- test data sources create temp view gstest1(a,b,v) as values (1,1,10),(1,1,11),(1,2,12),(1,2,13),(1,3,14), (2,3,15), (3,3,16),(3,4,17), (4,1,18),(4,1,19); create temp table gstest2 (a integer, b integer, c integer, d integer, e integer, f integer, g integer, h integer); create temp table gstest3 (a integer, b integer, c integer, d integer); alter table gstest3 add primary key (a); create temp table gstest4(id integer, v integer, unhashable_col bit(4), unsortable_col xid); insert into gstest4 values (1,1,b'0000','1'), (2,2,b'0001','1'), (3,4,b'0010','2'), (4,8,b'0011','2'), (5,16,b'0000','2'), (6,32,b'0001','2'), (7,64,b'0010','1'), (8,128,b'0011','1'); create temp table gstest_empty (a integer, b integer, v integer); create function gstest_data(v integer, out a integer, out b integer) returns setof record as $f$ begin return query select v, i from generate_series(1,3) i; end; $f$ language plpgsql; -- basic functionality set enable_hashagg = false; -- test hashing explicitly later -- simple rollup with multiple plain aggregates, with and without ordering -- (and with ordering differing from grouping) select a, b, grouping(a,b), sum(v), count(*), max(v) from gstest1 group by rollup (a,b); select a, b, grouping(a,b), sum(v), count(*), max(v) from gstest1 group by rollup (a,b) order by a,b; select a, b, grouping(a,b), sum(v), count(*), max(v) from gstest1 group by rollup (a,b) order by b desc, a; select a, b, grouping(a,b), sum(v), count(*), max(v) from gstest1 group by rollup (a,b) order by coalesce(a,0)+coalesce(b,0); -- various types of ordered aggs select a, b, grouping(a,b), array_agg(v order by v), string_agg(v::text, ':' order by v desc), percentile_disc(0.5) within group (order by v), rank(1,2,12) within group (order by a,b,v) from gstest1 group by rollup (a,b) order by a,b; -- test usage of grouped columns in direct args of aggs select grouping(a), a, array_agg(b), rank(a) within group (order by b nulls first), rank(a) within group (order by b nulls last) from (values (1,1),(1,4),(1,5),(3,1),(3,2)) v(a,b) group by rollup (a) order by a; -- nesting with window functions select a, b, sum(c), sum(sum(c)) over (order by a,b) as rsum from gstest2 group by rollup (a,b) order by rsum, a, b; -- nesting with grouping sets select sum(c) from gstest2 group by grouping sets((), grouping sets((), grouping sets(()))) order by 1 desc; select sum(c) from gstest2 group by grouping sets((), grouping sets((), grouping sets(((a, b))))) order by 1 desc; select sum(c) from gstest2 group by grouping sets(grouping sets(rollup(c), grouping sets(cube(c)))) order by 1 desc; select sum(c) from gstest2 group by grouping sets(a, grouping sets(a, cube(b))) order by 1 desc; select sum(c) from gstest2 group by grouping sets(grouping sets((a, (b)))) order by 1 desc; select sum(c) from gstest2 group by grouping sets(grouping sets((a, b))) order by 1 desc; select sum(c) from gstest2 group by grouping sets(grouping sets(a, grouping sets(a), a)) order by 1 desc; select sum(c) from gstest2 group by grouping sets(grouping sets(a, grouping sets(a, grouping sets(a), ((a)), a, grouping sets(a), (a)), a)) order by 1 desc; select sum(c) from gstest2 group by grouping sets((a,(a,b)), grouping sets((a,(a,b)),a)) order by 1 desc; -- empty input: first is 0 rows, second 1, third 3 etc. select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),a); select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),()); select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),(),(),()); select sum(v), count(*) from gstest_empty group by grouping sets ((),(),()); -- empty input with joins tests some important code paths select t1.a, t2.b, sum(t1.v), count(*) from gstest_empty t1, gstest_empty t2 group by grouping sets ((t1.a,t2.b),()); -- simple joins, var resolution, GROUPING on join vars select t1.a, t2.b, grouping(t1.a, t2.b), sum(t1.v), max(t2.a) from gstest1 t1, gstest2 t2 group by grouping sets ((t1.a, t2.b), ()); select t1.a, t2.b, grouping(t1.a, t2.b), sum(t1.v), max(t2.a) from gstest1 t1 join gstest2 t2 on (t1.a=t2.a) group by grouping sets ((t1.a, t2.b), ()); select a, b, grouping(a, b), sum(t1.v), max(t2.c) from gstest1 t1 join gstest2 t2 using (a,b) group by grouping sets ((a, b), ()); -- check that functionally dependent cols are not nulled select a, d, grouping(a,b,c) from gstest3 group by grouping sets ((a,b), (a,c)); -- check that distinct grouping columns are kept separate -- even if they are equal() explain (costs off) select g as alias1, g as alias2 from generate_series(1,3) g group by alias1, rollup(alias2); select g as alias1, g as alias2 from generate_series(1,3) g group by alias1, rollup(alias2); -- check that pulled-up subquery outputs still go to null when appropriate select four, x from (select four, ten, 'foo'::text as x from tenk1) as t group by grouping sets (four, x) having x = 'foo'; select four, x || 'x' from (select four, ten, 'foo'::text as x from tenk1) as t group by grouping sets (four, x) order by four; select (x+y)*1, sum(z) from (select 1 as x, 2 as y, 3 as z) s group by grouping sets (x+y, x); select x, not x as not_x, q2 from (select *, q1 = 1 as x from int8_tbl i1) as t group by grouping sets(x, q2) order by x, q2; -- simple rescan tests select a, b, sum(v.x) from (values (1),(2)) v(x), gstest_data(v.x) group by rollup (a,b); select * from (values (1),(2)) v(x), lateral (select a, b, sum(v.x) from gstest_data(v.x) group by rollup (a,b)) s; -- min max optimization should still work with GROUP BY () explain (costs off) select min(unique1) from tenk1 GROUP BY (); -- Views with GROUPING SET queries CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); select pg_get_viewdef('gstest_view'::regclass, true); -- Nested queries with 3 or more levels of nesting select(select (select grouping(a,b) from (values (1)) v2(c)) from (values (1,2)) v1(a,b) group by (a,b)) from (values(6,7)) v3(e,f) GROUP BY ROLLUP(e,f); select(select (select grouping(e,f) from (values (1)) v2(c)) from (values (1,2)) v1(a,b) group by (a,b)) from (values(6,7)) v3(e,f) GROUP BY ROLLUP(e,f); select(select (select grouping(c) from (values (1)) v2(c) GROUP BY c) from (values (1,2)) v1(a,b) group by (a,b)) from (values(6,7)) v3(e,f) GROUP BY ROLLUP(e,f); -- Combinations of operations select a, b, c, d from gstest2 group by rollup(a,b),grouping sets(c,d); select a, b from (values (1,2),(2,3)) v(a,b) group by a,b, grouping sets(a); -- Tests for chained aggregates select a, b, grouping(a,b), sum(v), count(*), max(v) from gstest1 group by grouping sets ((a,b),(a+1,b+1),(a+2,b+2)) order by 3,6; select(select (select grouping(a,b) from (values (1)) v2(c)) from (values (1,2)) v1(a,b) group by (a,b)) from (values(6,7)) v3(e,f) GROUP BY ROLLUP((e+1),(f+1)); select(select (select grouping(a,b) from (values (1)) v2(c)) from (values (1,2)) v1(a,b) group by (a,b)) from (values(6,7)) v3(e,f) GROUP BY CUBE((e+1),(f+1)) ORDER BY (e+1),(f+1); select a, b, sum(c), sum(sum(c)) over (order by a,b) as rsum from gstest2 group by cube (a,b) order by rsum, a, b; select a, b, sum(c) from (values (1,1,10),(1,1,11),(1,2,12),(1,2,13),(1,3,14),(2,3,15),(3,3,16),(3,4,17),(4,1,18),(4,1,19)) v(a,b,c) group by rollup (a,b); select a, b, sum(v.x) from (values (1),(2)) v(x), gstest_data(v.x) group by cube (a,b) order by a,b; -- Agg level check. This query should error out. select (select grouping(a,b) from gstest2) from gstest2 group by a,b; --Nested queries select a, b, sum(c), count(*) from gstest2 group by grouping sets (rollup(a,b),a); -- HAVING queries select ten, sum(distinct four) from onek a group by grouping sets((ten,four),(ten)) having exists (select 1 from onek b where sum(distinct a.four) = b.four); -- Tests around pushdown of HAVING clauses, partially testing against previous bugs select a,count(*) from gstest2 group by rollup(a) order by a; select a,count(*) from gstest2 group by rollup(a) having a is distinct from 1 order by a; explain (costs off) select a,count(*) from gstest2 group by rollup(a) having a is distinct from 1 order by a; select v.c, (select count(*) from gstest2 group by () having v.c) from (values (false),(true)) v(c) order by v.c; explain (costs off) select v.c, (select count(*) from gstest2 group by () having v.c) from (values (false),(true)) v(c) order by v.c; -- HAVING with GROUPING queries select ten, grouping(ten) from onek group by grouping sets(ten) having grouping(ten) >= 0 order by 2,1; select ten, grouping(ten) from onek group by grouping sets(ten, four) having grouping(ten) > 0 order by 2,1; select ten, grouping(ten) from onek group by rollup(ten) having grouping(ten) > 0 order by 2,1; select ten, grouping(ten) from onek group by cube(ten) having grouping(ten) > 0 order by 2,1; select ten, grouping(ten) from onek group by (ten) having grouping(ten) >= 0 order by 2,1; -- FILTER queries select ten, sum(distinct four) filter (where four::text ~ '123') from onek a group by rollup(ten); -- More rescan tests select * from (values (1),(2)) v(a) left join lateral (select v.a, four, ten, count(*) from onek group by cube(four,ten)) s on true order by v.a,four,ten; select array(select row(v.a,s1.*) from (select two,four, count(*) from onek group by cube(two,four) order by two,four) s1) from (values (1),(2)) v(a); -- Grouping on text columns select sum(ten) from onek group by two, rollup(four::text) order by 1; select sum(ten) from onek group by rollup(four::text), two order by 1; -- hashing support set enable_hashagg = true; -- failure cases select count(*) from gstest4 group by rollup(unhashable_col,unsortable_col); select array_agg(v order by v) from gstest4 group by grouping sets ((id,unsortable_col),(id)); -- simple cases select a, b, grouping(a,b), sum(v), count(*), max(v) from gstest1 group by grouping sets ((a),(b)) order by 3,1,2; explain (costs off) select a, b, grouping(a,b), sum(v), count(*), max(v) from gstest1 group by grouping sets ((a),(b)) order by 3,1,2; select a, b, grouping(a,b), sum(v), count(*), max(v) from gstest1 group by cube(a,b) order by 3,1,2; explain (costs off) select a, b, grouping(a,b), sum(v), count(*), max(v) from gstest1 group by cube(a,b) order by 3,1,2; -- shouldn't try and hash explain (costs off) select a, b, grouping(a,b), array_agg(v order by v) from gstest1 group by cube(a,b); -- unsortable cases select unsortable_col, count(*) from gstest4 group by grouping sets ((unsortable_col),(unsortable_col)) order by unsortable_col::text; -- mixed hashable/sortable cases select unhashable_col, unsortable_col, grouping(unhashable_col, unsortable_col), count(*), sum(v) from gstest4 group by grouping sets ((unhashable_col),(unsortable_col)) order by 3, 5; explain (costs off) select unhashable_col, unsortable_col, grouping(unhashable_col, unsortable_col), count(*), sum(v) from gstest4 group by grouping sets ((unhashable_col),(unsortable_col)) order by 3,5; select unhashable_col, unsortable_col, grouping(unhashable_col, unsortable_col), count(*), sum(v) from gstest4 group by grouping sets ((v,unhashable_col),(v,unsortable_col)) order by 3,5; explain (costs off) select unhashable_col, unsortable_col, grouping(unhashable_col, unsortable_col), count(*), sum(v) from gstest4 group by grouping sets ((v,unhashable_col),(v,unsortable_col)) order by 3,5; -- empty input: first is 0 rows, second 1, third 3 etc. select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),a); explain (costs off) select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),a); select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),()); select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),(),(),()); explain (costs off) select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),(),(),()); select sum(v), count(*) from gstest_empty group by grouping sets ((),(),()); explain (costs off) select sum(v), count(*) from gstest_empty group by grouping sets ((),(),()); -- check that functionally dependent cols are not nulled select a, d, grouping(a,b,c) from gstest3 group by grouping sets ((a,b), (a,c)); explain (costs off) select a, d, grouping(a,b,c) from gstest3 group by grouping sets ((a,b), (a,c)); -- simple rescan tests select a, b, sum(v.x) from (values (1),(2)) v(x), gstest_data(v.x) group by grouping sets (a,b) order by 1, 2, 3; explain (costs off) select a, b, sum(v.x) from (values (1),(2)) v(x), gstest_data(v.x) group by grouping sets (a,b) order by 3, 1, 2; select * from (values (1),(2)) v(x), lateral (select a, b, sum(v.x) from gstest_data(v.x) group by grouping sets (a,b)) s; explain (costs off) select * from (values (1),(2)) v(x), lateral (select a, b, sum(v.x) from gstest_data(v.x) group by grouping sets (a,b)) s; -- Tests for chained aggregates select a, b, grouping(a,b), sum(v), count(*), max(v) from gstest1 group by grouping sets ((a,b),(a+1,b+1),(a+2,b+2)) order by 3,6; explain (costs off) select a, b, grouping(a,b), sum(v), count(*), max(v) from gstest1 group by grouping sets ((a,b),(a+1,b+1),(a+2,b+2)) order by 3,6; select a, b, sum(c), sum(sum(c)) over (order by a,b) as rsum from gstest2 group by cube (a,b) order by rsum, a, b; explain (costs off) select a, b, sum(c), sum(sum(c)) over (order by a,b) as rsum from gstest2 group by cube (a,b) order by rsum, a, b; select a, b, sum(v.x) from (values (1),(2)) v(x), gstest_data(v.x) group by cube (a,b) order by a,b; explain (costs off) select a, b, sum(v.x) from (values (1),(2)) v(x), gstest_data(v.x) group by cube (a,b) order by a,b; -- More rescan tests select * from (values (1),(2)) v(a) left join lateral (select v.a, four, ten, count(*) from onek group by cube(four,ten)) s on true order by v.a,four,ten; select array(select row(v.a,s1.*) from (select two,four, count(*) from onek group by cube(two,four) order by two,four) s1) from (values (1),(2)) v(a); -- Rescan logic changes when there are no empty grouping sets, so test -- that too: select * from (values (1),(2)) v(a) left join lateral (select v.a, four, ten, count(*) from onek group by grouping sets(four,ten)) s on true order by v.a,four,ten; select array(select row(v.a,s1.*) from (select two,four, count(*) from onek group by grouping sets(two,four) order by two,four) s1) from (values (1),(2)) v(a); -- test the knapsack set enable_indexscan = false; set work_mem = '64kB'; explain (costs off) select unique1, count(two), count(four), count(ten), count(hundred), count(thousand), count(twothousand), count(*) from tenk1 group by grouping sets (unique1,twothousand,thousand,hundred,ten,four,two); explain (costs off) select unique1, count(two), count(four), count(ten), count(hundred), count(thousand), count(twothousand), count(*) from tenk1 group by grouping sets (unique1,hundred,ten,four,two); set work_mem = '384kB'; explain (costs off) select unique1, count(two), count(four), count(ten), count(hundred), count(thousand), count(twothousand), count(*) from tenk1 group by grouping sets (unique1,twothousand,thousand,hundred,ten,four,two); -- check collation-sensitive matching between grouping expressions -- (similar to a check for aggregates, but there are additional code -- paths for GROUPING, so check again here) select v||'a', case grouping(v||'a') when 1 then 1 else 0 end, count(*) from unnest(array[1,1], array['a','b']) u(i,v) group by rollup(i, v||'a') order by 1,3; select v||'a', case when grouping(v||'a') = 1 then 1 else 0 end, count(*) from unnest(array[1,1], array['a','b']) u(i,v) group by rollup(i, v||'a') order by 1,3; -- end pgFormatter-4.2/t/pg-test-files/sql/guc.sql000066400000000000000000000171231361326045100207020ustar00rootroot00000000000000-- pg_regress should ensure that this default value applies; however -- we can't rely on any specific default value of vacuum_cost_delay SHOW datestyle; -- SET to some nondefault value SET vacuum_cost_delay TO 40; SET datestyle = 'ISO, YMD'; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; -- SET LOCAL has no effect outside of a transaction SET LOCAL vacuum_cost_delay TO 50; SHOW vacuum_cost_delay; SET LOCAL datestyle = 'SQL'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; -- SET LOCAL within a transaction that commits BEGIN; SET LOCAL vacuum_cost_delay TO 50; SHOW vacuum_cost_delay; SET LOCAL datestyle = 'SQL'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; COMMIT; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; -- SET should be reverted after ROLLBACK BEGIN; SET vacuum_cost_delay TO 60; SHOW vacuum_cost_delay; SET datestyle = 'German'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; ROLLBACK; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; -- Some tests with subtransactions BEGIN; SET vacuum_cost_delay TO 70; SET datestyle = 'MDY'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; SAVEPOINT first_sp; SET vacuum_cost_delay TO 80.1; SHOW vacuum_cost_delay; SET datestyle = 'German, DMY'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; ROLLBACK TO first_sp; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; SAVEPOINT second_sp; SET vacuum_cost_delay TO '900us'; SET datestyle = 'SQL, YMD'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; SAVEPOINT third_sp; SET vacuum_cost_delay TO 100; SHOW vacuum_cost_delay; SET datestyle = 'Postgres, MDY'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; ROLLBACK TO third_sp; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; ROLLBACK TO second_sp; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; ROLLBACK; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; -- SET LOCAL with Savepoints BEGIN; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; SAVEPOINT sp; SET LOCAL vacuum_cost_delay TO 30; SHOW vacuum_cost_delay; SET LOCAL datestyle = 'Postgres, MDY'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; ROLLBACK TO sp; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; ROLLBACK; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; -- SET LOCAL persists through RELEASE (which was not true in 8.0-8.2) BEGIN; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; SAVEPOINT sp; SET LOCAL vacuum_cost_delay TO 30; SHOW vacuum_cost_delay; SET LOCAL datestyle = 'Postgres, MDY'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; RELEASE SAVEPOINT sp; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; ROLLBACK; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; -- SET followed by SET LOCAL BEGIN; SET vacuum_cost_delay TO 40; SET LOCAL vacuum_cost_delay TO 50; SHOW vacuum_cost_delay; SET datestyle = 'ISO, DMY'; SET LOCAL datestyle = 'Postgres, MDY'; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; COMMIT; SHOW vacuum_cost_delay; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; -- -- Test RESET. We use datestyle because the reset value is forced by -- pg_regress, so it doesn't depend on the installation's configuration. -- SET datestyle = iso, ymd; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; RESET datestyle; SHOW datestyle; SELECT '2006-08-13 12:34:56'::timestamptz; -- Test some simple error cases SET seq_page_cost TO 'NaN'; SET vacuum_cost_delay TO '10s'; -- -- Test DISCARD TEMP -- CREATE TEMP TABLE reset_test ( data text ) ON COMMIT DELETE ROWS; SELECT relname FROM pg_class WHERE relname = 'reset_test'; DISCARD TEMP; SELECT relname FROM pg_class WHERE relname = 'reset_test'; -- -- Test DISCARD ALL -- -- do changes DECLARE foo CURSOR WITH HOLD FOR SELECT 1; PREPARE foo AS SELECT 1; LISTEN foo_event; SET vacuum_cost_delay = 13; CREATE TEMP TABLE tmp_foo (data text) ON COMMIT DELETE ROWS; CREATE ROLE regress_guc_user; SET SESSION AUTHORIZATION regress_guc_user; -- look changes SELECT pg_listening_channels(); SELECT name FROM pg_prepared_statements; SELECT name FROM pg_cursors; SHOW vacuum_cost_delay; SELECT relname from pg_class where relname = 'tmp_foo'; SELECT current_user = 'regress_guc_user'; -- discard everything DISCARD ALL; -- look again SELECT pg_listening_channels(); SELECT name FROM pg_prepared_statements; SELECT name FROM pg_cursors; SHOW vacuum_cost_delay; SELECT relname from pg_class where relname = 'tmp_foo'; SELECT current_user = 'regress_guc_user'; DROP ROLE regress_guc_user; -- -- search_path should react to changes in pg_namespace -- set search_path = foo, public, not_there_initially; select current_schemas(false); create schema not_there_initially; select current_schemas(false); drop schema not_there_initially; select current_schemas(false); reset search_path; -- -- Tests for function-local GUC settings -- set work_mem = '3MB'; create function report_guc(text) returns text as $$ select current_setting($1) $$ language sql set work_mem = '1MB'; select report_guc('work_mem'), current_setting('work_mem'); alter function report_guc(text) set work_mem = '2MB'; select report_guc('work_mem'), current_setting('work_mem'); alter function report_guc(text) reset all; select report_guc('work_mem'), current_setting('work_mem'); -- SET LOCAL is restricted by a function SET option create or replace function myfunc(int) returns text as $$ begin set local work_mem = '2MB'; return current_setting('work_mem'); end $$ language plpgsql set work_mem = '1MB'; select myfunc(0), current_setting('work_mem'); alter function myfunc(int) reset all; select myfunc(0), current_setting('work_mem'); set work_mem = '3MB'; -- but SET isn't create or replace function myfunc(int) returns text as $$ begin set work_mem = '2MB'; return current_setting('work_mem'); end $$ language plpgsql set work_mem = '1MB'; select myfunc(0), current_setting('work_mem'); set work_mem = '3MB'; -- it should roll back on error, though create or replace function myfunc(int) returns text as $$ begin set work_mem = '2MB'; perform 1/$1; return current_setting('work_mem'); end $$ language plpgsql set work_mem = '1MB'; select myfunc(0); select current_setting('work_mem'); select myfunc(1), current_setting('work_mem'); -- check current_setting()'s behavior with invalid setting name select current_setting('nosuch.setting'); -- FAIL select current_setting('nosuch.setting', false); -- FAIL select current_setting('nosuch.setting', true) is null; -- after this, all three cases should yield 'nada' set nosuch.setting = 'nada'; select current_setting('nosuch.setting'); select current_setting('nosuch.setting', false); select current_setting('nosuch.setting', true); -- Normally, CREATE FUNCTION should complain about invalid values in -- function SET options; but not if check_function_bodies is off, -- because that creates ordering hazards for pg_dump create function func_with_bad_set() returns int as $$ select 1 $$ language sql set default_text_search_config = no_such_config; set check_function_bodies = off; create function func_with_bad_set() returns int as $$ select 1 $$ language sql set default_text_search_config = no_such_config; select func_with_bad_set(); reset check_function_bodies; set default_with_oids to f; -- Should not allow to set it to true. set default_with_oids to t; pgFormatter-4.2/t/pg-test-files/sql/hash_func.sql000066400000000000000000000261061361326045100220630ustar00rootroot00000000000000-- -- Test hash functions -- -- When the salt is 0, the extended hash function should produce a result -- whose low 32 bits match the standard hash function. When the salt is -- not 0, we should get a different result. -- SELECT v as value, hashint2(v)::bit(32) as standard, hashint2extended(v, 0)::bit(32) as extended0, hashint2extended(v, 1)::bit(32) as extended1 FROM (VALUES (0::int2), (1::int2), (17::int2), (42::int2)) x(v) WHERE hashint2(v)::bit(32) != hashint2extended(v, 0)::bit(32) OR hashint2(v)::bit(32) = hashint2extended(v, 1)::bit(32); SELECT v as value, hashint4(v)::bit(32) as standard, hashint4extended(v, 0)::bit(32) as extended0, hashint4extended(v, 1)::bit(32) as extended1 FROM (VALUES (0), (1), (17), (42), (550273), (207112489)) x(v) WHERE hashint4(v)::bit(32) != hashint4extended(v, 0)::bit(32) OR hashint4(v)::bit(32) = hashint4extended(v, 1)::bit(32); SELECT v as value, hashint8(v)::bit(32) as standard, hashint8extended(v, 0)::bit(32) as extended0, hashint8extended(v, 1)::bit(32) as extended1 FROM (VALUES (0), (1), (17), (42), (550273), (207112489)) x(v) WHERE hashint8(v)::bit(32) != hashint8extended(v, 0)::bit(32) OR hashint8(v)::bit(32) = hashint8extended(v, 1)::bit(32); SELECT v as value, hashfloat4(v)::bit(32) as standard, hashfloat4extended(v, 0)::bit(32) as extended0, hashfloat4extended(v, 1)::bit(32) as extended1 FROM (VALUES (0), (1), (17), (42), (550273), (207112489)) x(v) WHERE hashfloat4(v)::bit(32) != hashfloat4extended(v, 0)::bit(32) OR hashfloat4(v)::bit(32) = hashfloat4extended(v, 1)::bit(32); SELECT v as value, hashfloat8(v)::bit(32) as standard, hashfloat8extended(v, 0)::bit(32) as extended0, hashfloat8extended(v, 1)::bit(32) as extended1 FROM (VALUES (0), (1), (17), (42), (550273), (207112489)) x(v) WHERE hashfloat8(v)::bit(32) != hashfloat8extended(v, 0)::bit(32) OR hashfloat8(v)::bit(32) = hashfloat8extended(v, 1)::bit(32); SELECT v as value, hashoid(v)::bit(32) as standard, hashoidextended(v, 0)::bit(32) as extended0, hashoidextended(v, 1)::bit(32) as extended1 FROM (VALUES (0), (1), (17), (42), (550273), (207112489)) x(v) WHERE hashoid(v)::bit(32) != hashoidextended(v, 0)::bit(32) OR hashoid(v)::bit(32) = hashoidextended(v, 1)::bit(32); SELECT v as value, hashchar(v)::bit(32) as standard, hashcharextended(v, 0)::bit(32) as extended0, hashcharextended(v, 1)::bit(32) as extended1 FROM (VALUES (NULL::"char"), ('1'), ('x'), ('X'), ('p'), ('N')) x(v) WHERE hashchar(v)::bit(32) != hashcharextended(v, 0)::bit(32) OR hashchar(v)::bit(32) = hashcharextended(v, 1)::bit(32); SELECT v as value, hashname(v)::bit(32) as standard, hashnameextended(v, 0)::bit(32) as extended0, hashnameextended(v, 1)::bit(32) as extended1 FROM (VALUES (NULL), ('PostgreSQL'), ('eIpUEtqmY89'), ('AXKEJBTK'), ('muop28x03'), ('yi3nm0d73')) x(v) WHERE hashname(v)::bit(32) != hashnameextended(v, 0)::bit(32) OR hashname(v)::bit(32) = hashnameextended(v, 1)::bit(32); SELECT v as value, hashtext(v)::bit(32) as standard, hashtextextended(v, 0)::bit(32) as extended0, hashtextextended(v, 1)::bit(32) as extended1 FROM (VALUES (NULL), ('PostgreSQL'), ('eIpUEtqmY89'), ('AXKEJBTK'), ('muop28x03'), ('yi3nm0d73')) x(v) WHERE hashtext(v)::bit(32) != hashtextextended(v, 0)::bit(32) OR hashtext(v)::bit(32) = hashtextextended(v, 1)::bit(32); SELECT v as value, hashoidvector(v)::bit(32) as standard, hashoidvectorextended(v, 0)::bit(32) as extended0, hashoidvectorextended(v, 1)::bit(32) as extended1 FROM (VALUES (NULL::oidvector), ('0 1 2 3 4'), ('17 18 19 20'), ('42 43 42 45'), ('550273 550273 570274'), ('207112489 207112499 21512 2155 372325 1363252')) x(v) WHERE hashoidvector(v)::bit(32) != hashoidvectorextended(v, 0)::bit(32) OR hashoidvector(v)::bit(32) = hashoidvectorextended(v, 1)::bit(32); SELECT v as value, hash_aclitem(v)::bit(32) as standard, hash_aclitem_extended(v, 0)::bit(32) as extended0, hash_aclitem_extended(v, 1)::bit(32) as extended1 FROM (SELECT DISTINCT(relacl[1]) FROM pg_class LIMIT 10) x(v) WHERE hash_aclitem(v)::bit(32) != hash_aclitem_extended(v, 0)::bit(32) OR hash_aclitem(v)::bit(32) = hash_aclitem_extended(v, 1)::bit(32); SELECT v as value, hashmacaddr(v)::bit(32) as standard, hashmacaddrextended(v, 0)::bit(32) as extended0, hashmacaddrextended(v, 1)::bit(32) as extended1 FROM (VALUES (NULL::macaddr), ('08:00:2b:01:02:04'), ('08:00:2b:01:02:04'), ('e2:7f:51:3e:70:49'), ('d6:a9:4a:78:1c:d5'), ('ea:29:b1:5e:1f:a5')) x(v) WHERE hashmacaddr(v)::bit(32) != hashmacaddrextended(v, 0)::bit(32) OR hashmacaddr(v)::bit(32) = hashmacaddrextended(v, 1)::bit(32); SELECT v as value, hashinet(v)::bit(32) as standard, hashinetextended(v, 0)::bit(32) as extended0, hashinetextended(v, 1)::bit(32) as extended1 FROM (VALUES (NULL::inet), ('192.168.100.128/25'), ('192.168.100.0/8'), ('172.168.10.126/16'), ('172.18.103.126/24'), ('192.188.13.16/32')) x(v) WHERE hashinet(v)::bit(32) != hashinetextended(v, 0)::bit(32) OR hashinet(v)::bit(32) = hashinetextended(v, 1)::bit(32); SELECT v as value, hash_numeric(v)::bit(32) as standard, hash_numeric_extended(v, 0)::bit(32) as extended0, hash_numeric_extended(v, 1)::bit(32) as extended1 FROM (VALUES (0), (1.149484958), (17.149484958), (42.149484958), (149484958.550273), (2071124898672)) x(v) WHERE hash_numeric(v)::bit(32) != hash_numeric_extended(v, 0)::bit(32) OR hash_numeric(v)::bit(32) = hash_numeric_extended(v, 1)::bit(32); SELECT v as value, hashmacaddr8(v)::bit(32) as standard, hashmacaddr8extended(v, 0)::bit(32) as extended0, hashmacaddr8extended(v, 1)::bit(32) as extended1 FROM (VALUES (NULL::macaddr8), ('08:00:2b:01:02:04:36:49'), ('08:00:2b:01:02:04:f0:e8'), ('e2:7f:51:3e:70:49:16:29'), ('d6:a9:4a:78:1c:d5:47:32'), ('ea:29:b1:5e:1f:a5')) x(v) WHERE hashmacaddr8(v)::bit(32) != hashmacaddr8extended(v, 0)::bit(32) OR hashmacaddr8(v)::bit(32) = hashmacaddr8extended(v, 1)::bit(32); SELECT v as value, hash_array(v)::bit(32) as standard, hash_array_extended(v, 0)::bit(32) as extended0, hash_array_extended(v, 1)::bit(32) as extended1 FROM (VALUES ('{0}'::int4[]), ('{0,1,2,3,4}'), ('{17,18,19,20}'), ('{42,34,65,98}'), ('{550273,590027, 870273}'), ('{207112489, 807112489}')) x(v) WHERE hash_array(v)::bit(32) != hash_array_extended(v, 0)::bit(32) OR hash_array(v)::bit(32) = hash_array_extended(v, 1)::bit(32); SELECT v as value, hashbpchar(v)::bit(32) as standard, hashbpcharextended(v, 0)::bit(32) as extended0, hashbpcharextended(v, 1)::bit(32) as extended1 FROM (VALUES (NULL), ('PostgreSQL'), ('eIpUEtqmY89'), ('AXKEJBTK'), ('muop28x03'), ('yi3nm0d73')) x(v) WHERE hashbpchar(v)::bit(32) != hashbpcharextended(v, 0)::bit(32) OR hashbpchar(v)::bit(32) = hashbpcharextended(v, 1)::bit(32); SELECT v as value, time_hash(v)::bit(32) as standard, time_hash_extended(v, 0)::bit(32) as extended0, time_hash_extended(v, 1)::bit(32) as extended1 FROM (VALUES (NULL::time), ('11:09:59'), ('1:09:59'), ('11:59:59'), ('7:9:59'), ('5:15:59')) x(v) WHERE time_hash(v)::bit(32) != time_hash_extended(v, 0)::bit(32) OR time_hash(v)::bit(32) = time_hash_extended(v, 1)::bit(32); SELECT v as value, timetz_hash(v)::bit(32) as standard, timetz_hash_extended(v, 0)::bit(32) as extended0, timetz_hash_extended(v, 1)::bit(32) as extended1 FROM (VALUES (NULL::timetz), ('00:11:52.518762-07'), ('00:11:52.51762-08'), ('00:11:52.62-01'), ('00:11:52.62+01'), ('11:59:59+04')) x(v) WHERE timetz_hash(v)::bit(32) != timetz_hash_extended(v, 0)::bit(32) OR timetz_hash(v)::bit(32) = timetz_hash_extended(v, 1)::bit(32); SELECT v as value, interval_hash(v)::bit(32) as standard, interval_hash_extended(v, 0)::bit(32) as extended0, interval_hash_extended(v, 1)::bit(32) as extended1 FROM (VALUES (NULL::interval), ('5 month 7 day 46 minutes'), ('1 year 7 day 46 minutes'), ('1 year 7 month 20 day 46 minutes'), ('5 month'), ('17 year 11 month 7 day 9 hours 46 minutes 5 seconds')) x(v) WHERE interval_hash(v)::bit(32) != interval_hash_extended(v, 0)::bit(32) OR interval_hash(v)::bit(32) = interval_hash_extended(v, 1)::bit(32); SELECT v as value, timestamp_hash(v)::bit(32) as standard, timestamp_hash_extended(v, 0)::bit(32) as extended0, timestamp_hash_extended(v, 1)::bit(32) as extended1 FROM (VALUES (NULL::timestamp), ('2017-08-22 00:09:59.518762'), ('2015-08-20 00:11:52.51762-08'), ('2017-05-22 00:11:52.62-01'), ('2013-08-22 00:11:52.62+01'), ('2013-08-22 11:59:59+04')) x(v) WHERE timestamp_hash(v)::bit(32) != timestamp_hash_extended(v, 0)::bit(32) OR timestamp_hash(v)::bit(32) = timestamp_hash_extended(v, 1)::bit(32); SELECT v as value, uuid_hash(v)::bit(32) as standard, uuid_hash_extended(v, 0)::bit(32) as extended0, uuid_hash_extended(v, 1)::bit(32) as extended1 FROM (VALUES (NULL::uuid), ('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'), ('5a9ba4ac-8d6f-11e7-bb31-be2e44b06b34'), ('99c6705c-d939-461c-a3c9-1690ad64ed7b'), ('7deed3ca-8d6f-11e7-bb31-be2e44b06b34'), ('9ad46d4f-6f2a-4edd-aadb-745993928e1e')) x(v) WHERE uuid_hash(v)::bit(32) != uuid_hash_extended(v, 0)::bit(32) OR uuid_hash(v)::bit(32) = uuid_hash_extended(v, 1)::bit(32); SELECT v as value, pg_lsn_hash(v)::bit(32) as standard, pg_lsn_hash_extended(v, 0)::bit(32) as extended0, pg_lsn_hash_extended(v, 1)::bit(32) as extended1 FROM (VALUES (NULL::pg_lsn), ('16/B374D84'), ('30/B374D84'), ('255/B374D84'), ('25/B379D90'), ('900/F37FD90')) x(v) WHERE pg_lsn_hash(v)::bit(32) != pg_lsn_hash_extended(v, 0)::bit(32) OR pg_lsn_hash(v)::bit(32) = pg_lsn_hash_extended(v, 1)::bit(32); CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy'); SELECT v as value, hashenum(v)::bit(32) as standard, hashenumextended(v, 0)::bit(32) as extended0, hashenumextended(v, 1)::bit(32) as extended1 FROM (VALUES ('sad'::mood), ('ok'), ('happy')) x(v) WHERE hashenum(v)::bit(32) != hashenumextended(v, 0)::bit(32) OR hashenum(v)::bit(32) = hashenumextended(v, 1)::bit(32); DROP TYPE mood; SELECT v as value, jsonb_hash(v)::bit(32) as standard, jsonb_hash_extended(v, 0)::bit(32) as extended0, jsonb_hash_extended(v, 1)::bit(32) as extended1 FROM (VALUES (NULL::jsonb), ('{"a": "aaa bbb ddd ccc", "b": ["eee fff ggg"], "c": {"d": "hhh iii"}}'), ('{"foo": [true, "bar"], "tags": {"e": 1, "f": null}}'), ('{"g": {"h": "value"}}')) x(v) WHERE jsonb_hash(v)::bit(32) != jsonb_hash_extended(v, 0)::bit(32) OR jsonb_hash(v)::bit(32) = jsonb_hash_extended(v, 1)::bit(32); SELECT v as value, hash_range(v)::bit(32) as standard, hash_range_extended(v, 0)::bit(32) as extended0, hash_range_extended(v, 1)::bit(32) as extended1 FROM (VALUES (int4range(10, 20)), (int4range(23, 43)), (int4range(5675, 550273)), (int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v) WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32) OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32); pgFormatter-4.2/t/pg-test-files/sql/hash_index.sql000066400000000000000000000111671361326045100222400ustar00rootroot00000000000000-- -- HASH_INDEX -- grep 843938989 hash.data -- SELECT * FROM hash_i4_heap WHERE hash_i4_heap.random = 843938989; -- -- hash index -- grep 66766766 hash.data -- SELECT * FROM hash_i4_heap WHERE hash_i4_heap.random = 66766766; -- -- hash index -- grep 1505703298 hash.data -- SELECT * FROM hash_name_heap WHERE hash_name_heap.random = '1505703298'::name; -- -- hash index -- grep 7777777 hash.data -- SELECT * FROM hash_name_heap WHERE hash_name_heap.random = '7777777'::name; -- -- hash index -- grep 1351610853 hash.data -- SELECT * FROM hash_txt_heap WHERE hash_txt_heap.random = '1351610853'::text; -- -- hash index -- grep 111111112222222233333333 hash.data -- SELECT * FROM hash_txt_heap WHERE hash_txt_heap.random = '111111112222222233333333'::text; -- -- hash index -- grep 444705537 hash.data -- SELECT * FROM hash_f8_heap WHERE hash_f8_heap.random = '444705537'::float8; -- -- hash index -- grep 88888888 hash.data -- SELECT * FROM hash_f8_heap WHERE hash_f8_heap.random = '88888888'::float8; -- -- hash index -- grep '^90[^0-9]' hashovfl.data -- -- SELECT count(*) AS i988 FROM hash_ovfl_heap -- WHERE x = 90; -- -- hash index -- grep '^1000[^0-9]' hashovfl.data -- -- SELECT count(*) AS i0 FROM hash_ovfl_heap -- WHERE x = 1000; -- -- HASH -- UPDATE hash_i4_heap SET random = 1 WHERE hash_i4_heap.seqno = 1492; SELECT h.seqno AS i1492, h.random AS i1 FROM hash_i4_heap h WHERE h.random = 1; UPDATE hash_i4_heap SET seqno = 20000 WHERE hash_i4_heap.random = 1492795354; SELECT h.seqno AS i20000 FROM hash_i4_heap h WHERE h.random = 1492795354; UPDATE hash_name_heap SET random = '0123456789abcdef'::name WHERE hash_name_heap.seqno = 6543; SELECT h.seqno AS i6543, h.random AS c0_to_f FROM hash_name_heap h WHERE h.random = '0123456789abcdef'::name; UPDATE hash_name_heap SET seqno = 20000 WHERE hash_name_heap.random = '76652222'::name; -- -- this is the row we just replaced; index scan should return zero rows -- SELECT h.seqno AS emptyset FROM hash_name_heap h WHERE h.random = '76652222'::name; UPDATE hash_txt_heap SET random = '0123456789abcdefghijklmnop'::text WHERE hash_txt_heap.seqno = 4002; SELECT h.seqno AS i4002, h.random AS c0_to_p FROM hash_txt_heap h WHERE h.random = '0123456789abcdefghijklmnop'::text; UPDATE hash_txt_heap SET seqno = 20000 WHERE hash_txt_heap.random = '959363399'::text; SELECT h.seqno AS t20000 FROM hash_txt_heap h WHERE h.random = '959363399'::text; UPDATE hash_f8_heap SET random = '-1234.1234'::float8 WHERE hash_f8_heap.seqno = 8906; SELECT h.seqno AS i8096, h.random AS f1234_1234 FROM hash_f8_heap h WHERE h.random = '-1234.1234'::float8; UPDATE hash_f8_heap SET seqno = 20000 WHERE hash_f8_heap.random = '488912369'::float8; SELECT h.seqno AS f20000 FROM hash_f8_heap h WHERE h.random = '488912369'::float8; -- UPDATE hash_ovfl_heap -- SET x = 1000 -- WHERE x = 90; -- this vacuums the index as well -- VACUUM hash_ovfl_heap; -- SELECT count(*) AS i0 FROM hash_ovfl_heap -- WHERE x = 90; -- SELECT count(*) AS i988 FROM hash_ovfl_heap -- WHERE x = 1000; -- -- Cause some overflow insert and splits. -- CREATE TABLE hash_split_heap (keycol INT); INSERT INTO hash_split_heap SELECT 1 FROM generate_series(1, 500) a; CREATE INDEX hash_split_index on hash_split_heap USING HASH (keycol); INSERT INTO hash_split_heap SELECT 1 FROM generate_series(1, 5000) a; -- Let's do a backward scan. BEGIN; SET enable_seqscan = OFF; SET enable_bitmapscan = OFF; DECLARE c CURSOR FOR SELECT * from hash_split_heap WHERE keycol = 1; MOVE FORWARD ALL FROM c; MOVE BACKWARD 10000 FROM c; MOVE BACKWARD ALL FROM c; CLOSE c; END; -- DELETE, INSERT, VACUUM. DELETE FROM hash_split_heap WHERE keycol = 1; INSERT INTO hash_split_heap SELECT a/2 FROM generate_series(1, 25000) a; VACUUM hash_split_heap; -- Rebuild the index using a different fillfactor ALTER INDEX hash_split_index SET (fillfactor = 10); REINDEX INDEX hash_split_index; -- Clean up. DROP TABLE hash_split_heap; -- Index on temp table. CREATE TEMP TABLE hash_temp_heap (x int, y int); INSERT INTO hash_temp_heap VALUES (1,1); CREATE INDEX hash_idx ON hash_temp_heap USING hash (x); DROP TABLE hash_temp_heap CASCADE; -- Float4 type. CREATE TABLE hash_heap_float4 (x float4, y int); INSERT INTO hash_heap_float4 VALUES (1.1,1); CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x); DROP TABLE hash_heap_float4 CASCADE; -- Test out-of-range fillfactor values CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9); CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101); pgFormatter-4.2/t/pg-test-files/sql/hash_part.sql000066400000000000000000000060671361326045100221020ustar00rootroot00000000000000-- -- Hash partitioning. -- -- Use hand-rolled hash functions and operator classes to get predictable -- result on different matchines. See the definitions of -- part_part_test_int4_ops and part_test_text_ops in insert.sql. CREATE TABLE mchash (a int, b text, c jsonb) PARTITION BY HASH (a part_test_int4_ops, b part_test_text_ops); CREATE TABLE mchash1 PARTITION OF mchash FOR VALUES WITH (MODULUS 4, REMAINDER 0); -- invalid OID, no such table SELECT satisfies_hash_partition(0, 4, 0, NULL); -- not partitioned SELECT satisfies_hash_partition('tenk1'::regclass, 4, 0, NULL); -- partition rather than the parent SELECT satisfies_hash_partition('mchash1'::regclass, 4, 0, NULL); -- invalid modulus SELECT satisfies_hash_partition('mchash'::regclass, 0, 0, NULL); -- remainder too small SELECT satisfies_hash_partition('mchash'::regclass, 1, -1, NULL); -- remainder too large SELECT satisfies_hash_partition('mchash'::regclass, 1, 1, NULL); -- modulus is null SELECT satisfies_hash_partition('mchash'::regclass, NULL, 0, NULL); -- remainder is null SELECT satisfies_hash_partition('mchash'::regclass, 4, NULL, NULL); -- too many arguments SELECT satisfies_hash_partition('mchash'::regclass, 4, 0, NULL::int, NULL::text, NULL::json); -- too few arguments SELECT satisfies_hash_partition('mchash'::regclass, 3, 1, NULL::int); -- wrong argument type SELECT satisfies_hash_partition('mchash'::regclass, 2, 1, NULL::int, NULL::int); -- ok, should be false SELECT satisfies_hash_partition('mchash'::regclass, 4, 0, 0, ''::text); -- ok, should be true SELECT satisfies_hash_partition('mchash'::regclass, 4, 0, 2, ''::text); -- argument via variadic syntax, should fail because not all partitioning -- columns are of the correct type SELECT satisfies_hash_partition('mchash'::regclass, 2, 1, variadic array[1,2]::int[]); -- multiple partitioning columns of the same type CREATE TABLE mcinthash (a int, b int, c jsonb) PARTITION BY HASH (a part_test_int4_ops, b part_test_int4_ops); -- now variadic should work, should be false SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0, variadic array[0, 0]); -- should be true SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0, variadic array[0, 1]); -- wrong length SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0, variadic array[]::int[]); -- wrong type SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0, variadic array[now(), now()]); -- check satisfies_hash_partition passes correct collation create table text_hashp (a text) partition by hash (a); create table text_hashp0 partition of text_hashp for values with (modulus 2, remainder 0); create table text_hashp1 partition of text_hashp for values with (modulus 2, remainder 1); -- The result here should always be true, because 'xxx' must belong to -- one of the two defined partitions select satisfies_hash_partition('text_hashp'::regclass, 2, 0, 'xxx'::text) OR satisfies_hash_partition('text_hashp'::regclass, 2, 1, 'xxx'::text) AS satisfies; -- cleanup DROP TABLE mchash; DROP TABLE mcinthash; DROP TABLE text_hashp; pgFormatter-4.2/t/pg-test-files/sql/horology.sql000066400000000000000000000504011361326045100217620ustar00rootroot00000000000000-- -- HOROLOGY -- SET DateStyle = 'Postgres, MDY'; -- -- Test various input formats -- SELECT timestamp with time zone '20011227 040506+08'; SELECT timestamp with time zone '20011227 040506-08'; SELECT timestamp with time zone '20011227 040506.789+08'; SELECT timestamp with time zone '20011227 040506.789-08'; SELECT timestamp with time zone '20011227T040506+08'; SELECT timestamp with time zone '20011227T040506-08'; SELECT timestamp with time zone '20011227T040506.789+08'; SELECT timestamp with time zone '20011227T040506.789-08'; SELECT timestamp with time zone '2001-12-27 04:05:06.789-08'; SELECT timestamp with time zone '2001.12.27 04:05:06.789-08'; SELECT timestamp with time zone '2001/12/27 04:05:06.789-08'; SELECT timestamp with time zone '12/27/2001 04:05:06.789-08'; -- should fail in mdy mode: SELECT timestamp with time zone '27/12/2001 04:05:06.789-08'; set datestyle to dmy; SELECT timestamp with time zone '27/12/2001 04:05:06.789-08'; reset datestyle; SELECT timestamp with time zone 'Y2001M12D27H04M05S06.789+08'; SELECT timestamp with time zone 'Y2001M12D27H04M05S06.789-08'; SELECT timestamp with time zone 'Y2001M12D27H04MM05S06.789+08'; SELECT timestamp with time zone 'Y2001M12D27H04MM05S06.789-08'; SELECT timestamp with time zone 'J2452271+08'; SELECT timestamp with time zone 'J2452271-08'; SELECT timestamp with time zone 'J2452271.5+08'; SELECT timestamp with time zone 'J2452271.5-08'; SELECT timestamp with time zone 'J2452271 04:05:06+08'; SELECT timestamp with time zone 'J2452271 04:05:06-08'; SELECT timestamp with time zone 'J2452271T040506+08'; SELECT timestamp with time zone 'J2452271T040506-08'; SELECT timestamp with time zone 'J2452271T040506.789+08'; SELECT timestamp with time zone 'J2452271T040506.789-08'; -- German/European-style dates with periods as delimiters SELECT timestamp with time zone '12.27.2001 04:05:06.789+08'; SELECT timestamp with time zone '12.27.2001 04:05:06.789-08'; SET DateStyle = 'German'; SELECT timestamp with time zone '27.12.2001 04:05:06.789+08'; SELECT timestamp with time zone '27.12.2001 04:05:06.789-08'; SET DateStyle = 'ISO'; -- As of 7.4, allow time without time zone having a time zone specified SELECT time without time zone '040506.789+08'; SELECT time without time zone '040506.789-08'; SELECT time without time zone 'T040506.789+08'; SELECT time without time zone 'T040506.789-08'; SELECT time with time zone '040506.789+08'; SELECT time with time zone '040506.789-08'; SELECT time with time zone 'T040506.789+08'; SELECT time with time zone 'T040506.789-08'; SELECT time with time zone 'T040506.789 +08'; SELECT time with time zone 'T040506.789 -08'; SET DateStyle = 'Postgres, MDY'; -- Check Julian dates BC SELECT date 'J1520447' AS "Confucius' Birthday"; SELECT date 'J0' AS "Julian Epoch"; -- -- date, time arithmetic -- SELECT date '1981-02-03' + time '04:05:06' AS "Date + Time"; SELECT date '1991-02-03' + time with time zone '04:05:06 PST' AS "Date + Time PST"; SELECT date '2001-02-03' + time with time zone '04:05:06 UTC' AS "Date + Time UTC"; SELECT date '1991-02-03' + interval '2 years' AS "Add Two Years"; SELECT date '2001-12-13' - interval '2 years' AS "Subtract Two Years"; -- subtract time from date should not make sense; use interval instead SELECT date '1991-02-03' - time '04:05:06' AS "Subtract Time"; SELECT date '1991-02-03' - time with time zone '04:05:06 UTC' AS "Subtract Time UTC"; -- -- timestamp, interval arithmetic -- SELECT timestamp without time zone '1996-03-01' - interval '1 second' AS "Feb 29"; SELECT timestamp without time zone '1999-03-01' - interval '1 second' AS "Feb 28"; SELECT timestamp without time zone '2000-03-01' - interval '1 second' AS "Feb 29"; SELECT timestamp without time zone '1999-12-01' + interval '1 month - 1 second' AS "Dec 31"; SELECT timestamp without time zone 'Jan 1, 4713 BC' + interval '106000000 days' AS "Feb 23, 285506"; SELECT timestamp without time zone 'Jan 1, 4713 BC' + interval '107000000 days' AS "Jan 20, 288244"; SELECT timestamp without time zone 'Jan 1, 4713 BC' + interval '109203489 days' AS "Dec 31, 294276"; SELECT timestamp without time zone '12/31/294276' - timestamp without time zone '12/23/1999' AS "106751991 Days"; -- Shorthand values -- Not directly usable for regression testing since these are not constants. -- So, just try to test parser and hope for the best - thomas 97/04/26 SELECT (timestamp without time zone 'today' = (timestamp without time zone 'yesterday' + interval '1 day')) as "True"; SELECT (timestamp without time zone 'today' = (timestamp without time zone 'tomorrow' - interval '1 day')) as "True"; SELECT (timestamp without time zone 'today 10:30' = (timestamp without time zone 'yesterday' + interval '1 day 10 hr 30 min')) as "True"; SELECT (timestamp without time zone '10:30 today' = (timestamp without time zone 'yesterday' + interval '1 day 10 hr 30 min')) as "True"; SELECT (timestamp without time zone 'tomorrow' = (timestamp without time zone 'yesterday' + interval '2 days')) as "True"; SELECT (timestamp without time zone 'tomorrow 16:00:00' = (timestamp without time zone 'today' + interval '1 day 16 hours')) as "True"; SELECT (timestamp without time zone '16:00:00 tomorrow' = (timestamp without time zone 'today' + interval '1 day 16 hours')) as "True"; SELECT (timestamp without time zone 'yesterday 12:34:56' = (timestamp without time zone 'tomorrow' - interval '2 days - 12:34:56')) as "True"; SELECT (timestamp without time zone '12:34:56 yesterday' = (timestamp without time zone 'tomorrow' - interval '2 days - 12:34:56')) as "True"; SELECT (timestamp without time zone 'tomorrow' > 'now') as "True"; -- Convert from date and time to timestamp -- This test used to be timestamp(date,time) but no longer allowed by grammar -- to enable support for SQL99 timestamp type syntax. SELECT date '1994-01-01' + time '11:00' AS "Jan_01_1994_11am"; SELECT date '1994-01-01' + time '10:00' AS "Jan_01_1994_10am"; SELECT date '1994-01-01' + timetz '11:00-5' AS "Jan_01_1994_8am"; SELECT timestamptz(date '1994-01-01', time with time zone '11:00-5') AS "Jan_01_1994_8am"; SELECT '' AS "64", d1 + interval '1 year' AS one_year FROM TIMESTAMP_TBL; SELECT '' AS "64", d1 - interval '1 year' AS one_year FROM TIMESTAMP_TBL; SELECT timestamp with time zone '1996-03-01' - interval '1 second' AS "Feb 29"; SELECT timestamp with time zone '1999-03-01' - interval '1 second' AS "Feb 28"; SELECT timestamp with time zone '2000-03-01' - interval '1 second' AS "Feb 29"; SELECT timestamp with time zone '1999-12-01' + interval '1 month - 1 second' AS "Dec 31"; SELECT (timestamp with time zone 'today' = (timestamp with time zone 'yesterday' + interval '1 day')) as "True"; SELECT (timestamp with time zone 'today' = (timestamp with time zone 'tomorrow' - interval '1 day')) as "True"; SELECT (timestamp with time zone 'tomorrow' = (timestamp with time zone 'yesterday' + interval '2 days')) as "True"; SELECT (timestamp with time zone 'tomorrow' > 'now') as "True"; -- timestamp with time zone, interval arithmetic around DST change SET TIME ZONE 'CST7CDT'; SELECT timestamp with time zone '2005-04-02 12:00-07' + interval '1 day' as "Apr 3, 12:00"; SELECT timestamp with time zone '2005-04-02 12:00-07' + interval '24 hours' as "Apr 3, 13:00"; SELECT timestamp with time zone '2005-04-03 12:00-06' - interval '1 day' as "Apr 2, 12:00"; SELECT timestamp with time zone '2005-04-03 12:00-06' - interval '24 hours' as "Apr 2, 11:00"; RESET TIME ZONE; SELECT timestamptz(date '1994-01-01', time '11:00') AS "Jan_01_1994_10am"; SELECT timestamptz(date '1994-01-01', time '10:00') AS "Jan_01_1994_9am"; SELECT timestamptz(date '1994-01-01', time with time zone '11:00-8') AS "Jan_01_1994_11am"; SELECT timestamptz(date '1994-01-01', time with time zone '10:00-8') AS "Jan_01_1994_10am"; SELECT timestamptz(date '1994-01-01', time with time zone '11:00-5') AS "Jan_01_1994_8am"; SELECT '' AS "64", d1 + interval '1 year' AS one_year FROM TIMESTAMPTZ_TBL; SELECT '' AS "64", d1 - interval '1 year' AS one_year FROM TIMESTAMPTZ_TBL; -- -- time, interval arithmetic -- SELECT CAST(time '01:02' AS interval) AS "+01:02"; SELECT CAST(interval '02:03' AS time) AS "02:03:00"; SELECT time '01:30' + interval '02:01' AS "03:31:00"; SELECT time '01:30' - interval '02:01' AS "23:29:00"; SELECT time '02:30' + interval '36:01' AS "14:31:00"; SELECT time '03:30' + interval '1 month 04:01' AS "07:31:00"; SELECT CAST(time with time zone '01:02-08' AS interval) AS "+00:01"; SELECT CAST(interval '02:03' AS time with time zone) AS "02:03:00-08"; SELECT time with time zone '01:30-08' - interval '02:01' AS "23:29:00-08"; SELECT time with time zone '02:30-08' + interval '36:01' AS "14:31:00-08"; -- These two tests cannot be used because they default to current timezone, -- which may be either -08 or -07 depending on the time of year. -- SELECT time with time zone '01:30' + interval '02:01' AS "03:31:00-08"; -- SELECT time with time zone '03:30' + interval '1 month 04:01' AS "07:31:00-08"; -- Try the following two tests instead, as a poor substitute SELECT CAST(CAST(date 'today' + time with time zone '05:30' + interval '02:01' AS time with time zone) AS time) AS "07:31:00"; SELECT CAST(cast(date 'today' + time with time zone '03:30' + interval '1 month 04:01' as timestamp without time zone) AS time) AS "07:31:00"; SELECT t.d1 AS t, i.f1 AS i, t.d1 + i.f1 AS "add", t.d1 - i.f1 AS "subtract" FROM TIMESTAMP_TBL t, INTERVAL_TBL i WHERE t.d1 BETWEEN '1990-01-01' AND '2001-01-01' AND i.f1 BETWEEN '00:00' AND '23:00' ORDER BY 1,2; SELECT t.f1 AS t, i.f1 AS i, t.f1 + i.f1 AS "add", t.f1 - i.f1 AS "subtract" FROM TIME_TBL t, INTERVAL_TBL i ORDER BY 1,2; SELECT t.f1 AS t, i.f1 AS i, t.f1 + i.f1 AS "add", t.f1 - i.f1 AS "subtract" FROM TIMETZ_TBL t, INTERVAL_TBL i ORDER BY 1,2; -- SQL9x OVERLAPS operator -- test with time zone SELECT (timestamp with time zone '2000-11-27', timestamp with time zone '2000-11-28') OVERLAPS (timestamp with time zone '2000-11-27 12:00', timestamp with time zone '2000-11-30') AS "True"; SELECT (timestamp with time zone '2000-11-26', timestamp with time zone '2000-11-27') OVERLAPS (timestamp with time zone '2000-11-27 12:00', timestamp with time zone '2000-11-30') AS "False"; SELECT (timestamp with time zone '2000-11-27', timestamp with time zone '2000-11-28') OVERLAPS (timestamp with time zone '2000-11-27 12:00', interval '1 day') AS "True"; SELECT (timestamp with time zone '2000-11-27', interval '12 hours') OVERLAPS (timestamp with time zone '2000-11-27 12:00', timestamp with time zone '2000-11-30') AS "False"; SELECT (timestamp with time zone '2000-11-27', interval '12 hours') OVERLAPS (timestamp with time zone '2000-11-27', interval '12 hours') AS "True"; SELECT (timestamp with time zone '2000-11-27', interval '12 hours') OVERLAPS (timestamp with time zone '2000-11-27 12:00', interval '12 hours') AS "False"; -- test without time zone SELECT (timestamp without time zone '2000-11-27', timestamp without time zone '2000-11-28') OVERLAPS (timestamp without time zone '2000-11-27 12:00', timestamp without time zone '2000-11-30') AS "True"; SELECT (timestamp without time zone '2000-11-26', timestamp without time zone '2000-11-27') OVERLAPS (timestamp without time zone '2000-11-27 12:00', timestamp without time zone '2000-11-30') AS "False"; SELECT (timestamp without time zone '2000-11-27', timestamp without time zone '2000-11-28') OVERLAPS (timestamp without time zone '2000-11-27 12:00', interval '1 day') AS "True"; SELECT (timestamp without time zone '2000-11-27', interval '12 hours') OVERLAPS (timestamp without time zone '2000-11-27 12:00', timestamp without time zone '2000-11-30') AS "False"; SELECT (timestamp without time zone '2000-11-27', interval '12 hours') OVERLAPS (timestamp without time zone '2000-11-27', interval '12 hours') AS "True"; SELECT (timestamp without time zone '2000-11-27', interval '12 hours') OVERLAPS (timestamp without time zone '2000-11-27 12:00', interval '12 hours') AS "False"; -- test time and interval SELECT (time '00:00', time '01:00') OVERLAPS (time '00:30', time '01:30') AS "True"; SELECT (time '00:00', interval '1 hour') OVERLAPS (time '00:30', interval '1 hour') AS "True"; SELECT (time '00:00', interval '1 hour') OVERLAPS (time '01:30', interval '1 hour') AS "False"; -- SQL99 seems to want this to be false (and we conform to the spec). -- istm that this *should* return true, on the theory that time -- intervals can wrap around the day boundary - thomas 2001-09-25 SELECT (time '00:00', interval '1 hour') OVERLAPS (time '01:30', interval '1 day') AS "False"; CREATE TABLE TEMP_TIMESTAMP (f1 timestamp with time zone); -- get some candidate input values INSERT INTO TEMP_TIMESTAMP (f1) SELECT d1 FROM TIMESTAMP_TBL WHERE d1 BETWEEN '13-jun-1957' AND '1-jan-1997' OR d1 BETWEEN '1-jan-1999' AND '1-jan-2010'; SELECT '' AS "16", f1 AS "timestamp" FROM TEMP_TIMESTAMP ORDER BY "timestamp"; SELECT '' AS "160", d.f1 AS "timestamp", t.f1 AS "interval", d.f1 + t.f1 AS plus FROM TEMP_TIMESTAMP d, INTERVAL_TBL t ORDER BY plus, "timestamp", "interval"; SELECT '' AS "160", d.f1 AS "timestamp", t.f1 AS "interval", d.f1 - t.f1 AS minus FROM TEMP_TIMESTAMP d, INTERVAL_TBL t WHERE isfinite(d.f1) ORDER BY minus, "timestamp", "interval"; SELECT '' AS "16", d.f1 AS "timestamp", timestamp with time zone '1980-01-06 00:00 GMT' AS gpstime_zero, d.f1 - timestamp with time zone '1980-01-06 00:00 GMT' AS difference FROM TEMP_TIMESTAMP d ORDER BY difference; SELECT '' AS "226", d1.f1 AS timestamp1, d2.f1 AS timestamp2, d1.f1 - d2.f1 AS difference FROM TEMP_TIMESTAMP d1, TEMP_TIMESTAMP d2 ORDER BY timestamp1, timestamp2, difference; -- -- Conversions -- SELECT '' AS "16", f1 AS "timestamp", date(f1) AS date FROM TEMP_TIMESTAMP WHERE f1 <> timestamp 'now' ORDER BY date, "timestamp"; DROP TABLE TEMP_TIMESTAMP; -- -- Formats -- SET DateStyle TO 'US,Postgres'; SHOW DateStyle; SELECT '' AS "64", d1 AS us_postgres FROM TIMESTAMP_TBL; SET DateStyle TO 'US,ISO'; SELECT '' AS "64", d1 AS us_iso FROM TIMESTAMP_TBL; SET DateStyle TO 'US,SQL'; SHOW DateStyle; SELECT '' AS "64", d1 AS us_sql FROM TIMESTAMP_TBL; SET DateStyle TO 'European,Postgres'; SHOW DateStyle; INSERT INTO TIMESTAMP_TBL VALUES('13/06/1957'); SELECT count(*) as one FROM TIMESTAMP_TBL WHERE d1 = 'Jun 13 1957'; SELECT '' AS "65", d1 AS european_postgres FROM TIMESTAMP_TBL; SET DateStyle TO 'European,ISO'; SHOW DateStyle; SELECT '' AS "65", d1 AS european_iso FROM TIMESTAMP_TBL; SET DateStyle TO 'European,SQL'; SHOW DateStyle; SELECT '' AS "65", d1 AS european_sql FROM TIMESTAMP_TBL; RESET DateStyle; -- -- to_timestamp() -- SELECT to_timestamp('0097/Feb/16 --> 08:14:30', 'YYYY/Mon/DD --> HH:MI:SS'); SELECT to_timestamp('97/2/16 8:14:30', 'FMYYYY/FMMM/FMDD FMHH:FMMI:FMSS'); SELECT to_timestamp('2011$03!18 23_38_15', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('1985 January 12', 'YYYY FMMonth DD'); SELECT to_timestamp('1985 FMMonth 12', 'YYYY "FMMonth" DD'); SELECT to_timestamp('1985 \ 12', 'YYYY \\ DD'); SELECT to_timestamp('My birthday-> Year: 1976, Month: May, Day: 16', '"My birthday-> Year:" YYYY, "Month:" FMMonth, "Day:" DD'); SELECT to_timestamp('1,582nd VIII 21', 'Y,YYYth FMRM DD'); SELECT to_timestamp('15 "text between quote marks" 98 54 45', E'HH24 "\\"text between quote marks\\"" YY MI SS'); SELECT to_timestamp('05121445482000', 'MMDDHH24MISSYYYY'); SELECT to_timestamp('2000January09Sunday', 'YYYYFMMonthDDFMDay'); SELECT to_timestamp('97/Feb/16', 'YYMonDD'); SELECT to_timestamp('97/Feb/16', 'YY:Mon:DD'); SELECT to_timestamp('97/Feb/16', 'FXYY:Mon:DD'); SELECT to_timestamp('97/Feb/16', 'FXYY/Mon/DD'); SELECT to_timestamp('19971116', 'YYYYMMDD'); SELECT to_timestamp('20000-1116', 'YYYY-MMDD'); SELECT to_timestamp('1997 AD 11 16', 'YYYY BC MM DD'); SELECT to_timestamp('1997 BC 11 16', 'YYYY BC MM DD'); SELECT to_timestamp('9-1116', 'Y-MMDD'); SELECT to_timestamp('95-1116', 'YY-MMDD'); SELECT to_timestamp('995-1116', 'YYY-MMDD'); SELECT to_timestamp('2005426', 'YYYYWWD'); SELECT to_timestamp('2005300', 'YYYYDDD'); SELECT to_timestamp('2005527', 'IYYYIWID'); SELECT to_timestamp('005527', 'IYYIWID'); SELECT to_timestamp('05527', 'IYIWID'); SELECT to_timestamp('5527', 'IIWID'); SELECT to_timestamp('2005364', 'IYYYIDDD'); SELECT to_timestamp('20050302', 'YYYYMMDD'); SELECT to_timestamp('2005 03 02', 'YYYYMMDD'); SELECT to_timestamp(' 2005 03 02', 'YYYYMMDD'); SELECT to_timestamp(' 20050302', 'YYYYMMDD'); SELECT to_timestamp('2011-12-18 11:38 AM', 'YYYY-MM-DD HH12:MI PM'); SELECT to_timestamp('2011-12-18 11:38 PM', 'YYYY-MM-DD HH12:MI PM'); SELECT to_timestamp('2011-12-18 11:38 +05', 'YYYY-MM-DD HH12:MI TZH'); SELECT to_timestamp('2011-12-18 11:38 -05', 'YYYY-MM-DD HH12:MI TZH'); SELECT to_timestamp('2011-12-18 11:38 +05:20', 'YYYY-MM-DD HH12:MI TZH:TZM'); SELECT to_timestamp('2011-12-18 11:38 -05:20', 'YYYY-MM-DD HH12:MI TZH:TZM'); SELECT to_timestamp('2011-12-18 11:38 20', 'YYYY-MM-DD HH12:MI TZM'); -- -- Check handling of multiple spaces in format and/or input -- SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2000+ JUN', 'YYYY/MON'); SELECT to_timestamp(' 2000 +JUN', 'YYYY/MON'); SELECT to_timestamp(' 2000 +JUN', 'YYYY//MON'); SELECT to_timestamp('2000 +JUN', 'YYYY//MON'); SELECT to_timestamp('2000 + JUN', 'YYYY MON'); SELECT to_timestamp('2000 ++ JUN', 'YYYY MON'); SELECT to_timestamp('2000 + + JUN', 'YYYY MON'); SELECT to_timestamp('2000 + + JUN', 'YYYY MON'); SELECT to_timestamp('2000 -10', 'YYYY TZH'); SELECT to_timestamp('2000 -10', 'YYYY TZH'); SELECT to_date('2011 12 18', 'YYYY MM DD'); SELECT to_date('2011 12 18', 'YYYY MM DD'); SELECT to_date('2011 12 18', 'YYYY MM DD'); SELECT to_date('2011 12 18', 'YYYY MM DD'); SELECT to_date('2011 12 18', 'YYYY MM DD'); SELECT to_date('2011 12 18', 'YYYY MM DD'); SELECT to_date('2011 12 18', 'YYYYxMMxDD'); SELECT to_date('2011x 12x 18', 'YYYYxMMxDD'); SELECT to_date('2011 x12 x18', 'YYYYxMMxDD'); -- -- Check errors for some incorrect usages of to_timestamp() and to_date() -- -- Mixture of date conventions (ISO week and Gregorian): SELECT to_timestamp('2005527', 'YYYYIWID'); -- Insufficient characters in the source string: SELECT to_timestamp('19971', 'YYYYMMDD'); -- Insufficient digit characters for a single node: SELECT to_timestamp('19971)24', 'YYYYMMDD'); -- Value clobbering: SELECT to_timestamp('1997-11-Jan-16', 'YYYY-MM-Mon-DD'); -- Non-numeric input: SELECT to_timestamp('199711xy', 'YYYYMMDD'); -- Input that doesn't fit in an int: SELECT to_timestamp('10000000000', 'FMYYYY'); -- Out-of-range and not-quite-out-of-range fields: SELECT to_timestamp('2016-06-13 25:00:00', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2016-06-13 15:60:00', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2016-06-13 15:50:60', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2016-06-13 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); -- ok SELECT to_timestamp('2016-06-13 15:50:55', 'YYYY-MM-DD HH:MI:SS'); SELECT to_timestamp('2016-13-01 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2016-02-30 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2016-02-29 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); -- ok SELECT to_timestamp('2015-02-29 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); SELECT to_timestamp('2015-02-11 86000', 'YYYY-MM-DD SSSS'); -- ok SELECT to_timestamp('2015-02-11 86400', 'YYYY-MM-DD SSSS'); SELECT to_date('2016-13-10', 'YYYY-MM-DD'); SELECT to_date('2016-02-30', 'YYYY-MM-DD'); SELECT to_date('2016-02-29', 'YYYY-MM-DD'); -- ok SELECT to_date('2015-02-29', 'YYYY-MM-DD'); SELECT to_date('2015 365', 'YYYY DDD'); -- ok SELECT to_date('2015 366', 'YYYY DDD'); SELECT to_date('2016 365', 'YYYY DDD'); -- ok SELECT to_date('2016 366', 'YYYY DDD'); -- ok SELECT to_date('2016 367', 'YYYY DDD'); -- -- Check behavior with SQL-style fixed-GMT-offset time zone (cf bug #8572) -- SET TIME ZONE 'America/New_York'; SET TIME ZONE '-1.5'; SHOW TIME ZONE; SELECT '2012-12-12 12:00'::timestamptz; SELECT '2012-12-12 12:00 America/New_York'::timestamptz; SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD HH:MI:SS TZ'); RESET TIME ZONE; pgFormatter-4.2/t/pg-test-files/sql/hs_primary_extremes.sql000066400000000000000000000026401361326045100242130ustar00rootroot00000000000000-- -- Hot Standby tests -- -- hs_primary_extremes.sql -- drop table if exists hs_extreme; create table hs_extreme (col1 integer); CREATE OR REPLACE FUNCTION hs_subxids (n integer) RETURNS void LANGUAGE plpgsql AS $$ BEGIN IF n <= 0 THEN RETURN; END IF; INSERT INTO hs_extreme VALUES (n); PERFORM hs_subxids(n - 1); RETURN; EXCEPTION WHEN raise_exception THEN NULL; END; $$; BEGIN; SELECT hs_subxids(257); ROLLBACK; BEGIN; SELECT hs_subxids(257); COMMIT; set client_min_messages = 'warning'; CREATE OR REPLACE FUNCTION hs_locks_create (n integer) RETURNS void LANGUAGE plpgsql AS $$ BEGIN IF n <= 0 THEN CHECKPOINT; RETURN; END IF; EXECUTE 'CREATE TABLE hs_locks_' || n::text || ' ()'; PERFORM hs_locks_create(n - 1); RETURN; EXCEPTION WHEN raise_exception THEN NULL; END; $$; CREATE OR REPLACE FUNCTION hs_locks_drop (n integer) RETURNS void LANGUAGE plpgsql AS $$ BEGIN IF n <= 0 THEN CHECKPOINT; RETURN; END IF; EXECUTE 'DROP TABLE IF EXISTS hs_locks_' || n::text; PERFORM hs_locks_drop(n - 1); RETURN; EXCEPTION WHEN raise_exception THEN NULL; END; $$; BEGIN; SELECT hs_locks_drop(257); SELECT hs_locks_create(257); SELECT count(*) > 257 FROM pg_locks; ROLLBACK; BEGIN; SELECT hs_locks_drop(257); SELECT hs_locks_create(257); SELECT count(*) > 257 FROM pg_locks; COMMIT; SELECT hs_locks_drop(257); SELECT pg_switch_wal(); pgFormatter-4.2/t/pg-test-files/sql/hs_primary_setup.sql000066400000000000000000000010161361326045100235130ustar00rootroot00000000000000-- -- Hot Standby tests -- -- hs_primary_setup.sql -- drop table if exists hs1; create table hs1 (col1 integer primary key); insert into hs1 values (1); drop table if exists hs2; create table hs2 (col1 integer primary key); insert into hs2 values (12); insert into hs2 values (13); drop table if exists hs3; create table hs3 (col1 integer primary key); insert into hs3 values (113); insert into hs3 values (114); insert into hs3 values (115); DROP sequence if exists hsseq; create sequence hsseq; SELECT pg_switch_wal(); pgFormatter-4.2/t/pg-test-files/sql/hs_standby_allowed.sql000066400000000000000000000037711361326045100237750ustar00rootroot00000000000000-- -- Hot Standby tests -- -- hs_standby_allowed.sql -- -- SELECT select count(*) as should_be_1 from hs1; select count(*) as should_be_2 from hs2; select count(*) as should_be_3 from hs3; \! cat /tmp/copy_test -- Access sequence directly select is_called from hsseq; -- Transactions begin; select count(*) as should_be_1 from hs1; end; begin transaction read only; select count(*) as should_be_1 from hs1; end; begin transaction isolation level repeatable read; select count(*) as should_be_1 from hs1; select count(*) as should_be_1 from hs1; select count(*) as should_be_1 from hs1; commit; begin; select count(*) as should_be_1 from hs1; commit; begin; select count(*) as should_be_1 from hs1; abort; start transaction; select count(*) as should_be_1 from hs1; commit; begin; select count(*) as should_be_1 from hs1; rollback; begin; select count(*) as should_be_1 from hs1; savepoint s; select count(*) as should_be_2 from hs2; commit; begin; select count(*) as should_be_1 from hs1; savepoint s; select count(*) as should_be_2 from hs2; release savepoint s; select count(*) as should_be_2 from hs2; savepoint s; select count(*) as should_be_3 from hs3; rollback to savepoint s; select count(*) as should_be_2 from hs2; commit; -- SET parameters -- has no effect on read only transactions, but we can still set it set synchronous_commit = on; show synchronous_commit; reset synchronous_commit; discard temp; discard all; -- CURSOR commands BEGIN; DECLARE hsc CURSOR FOR select * from hs3; FETCH next from hsc; fetch first from hsc; fetch last from hsc; fetch 1 from hsc; CLOSE hsc; COMMIT; -- Prepared plans PREPARE hsp AS select count(*) from hs1; PREPARE hsp_noexec (integer) AS insert into hs1 values ($1); EXECUTE hsp; DEALLOCATE hsp; -- LOCK BEGIN; LOCK hs1 IN ACCESS SHARE MODE; LOCK hs1 IN ROW SHARE MODE; LOCK hs1 IN ROW EXCLUSIVE MODE; COMMIT; -- UNLISTEN UNLISTEN a; UNLISTEN *; -- LOAD -- should work, easier if there is no test for that... -- ALLOWED COMMANDS CHECKPOINT; discard all; pgFormatter-4.2/t/pg-test-files/sql/hs_standby_check.sql000066400000000000000000000007541361326045100234210ustar00rootroot00000000000000-- -- Hot Standby tests -- -- hs_standby_check.sql -- -- -- If the query below returns false then all other tests will fail after it. -- select case pg_is_in_recovery() when false then 'These tests are intended only for execution on a standby server that is reading ' || 'WAL from a server upon which the regression database is already created and into ' || 'which src/test/regress/sql/hs_primary_setup.sql has been run' else 'Tests are running on a standby server during recovery' end; pgFormatter-4.2/t/pg-test-files/sql/hs_standby_disallowed.sql000066400000000000000000000025761361326045100244770ustar00rootroot00000000000000-- -- Hot Standby tests -- -- hs_standby_disallowed.sql -- SET transaction_read_only = off; begin transaction read write; commit; -- SELECT select * from hs1 FOR SHARE; select * from hs1 FOR UPDATE; -- DML BEGIN; insert into hs1 values (37); ROLLBACK; BEGIN; delete from hs1 where col1 = 1; ROLLBACK; BEGIN; update hs1 set col1 = NULL where col1 > 0; ROLLBACK; BEGIN; truncate hs3; ROLLBACK; -- DDL create temporary table hstemp1 (col1 integer); BEGIN; drop table hs2; ROLLBACK; BEGIN; create table hs4 (col1 integer); ROLLBACK; -- Sequences SELECT nextval('hsseq'); -- Two-phase commit transaction stuff BEGIN; SELECT count(*) FROM hs1; PREPARE TRANSACTION 'foobar'; ROLLBACK; BEGIN; SELECT count(*) FROM hs1; COMMIT PREPARED 'foobar'; ROLLBACK; BEGIN; SELECT count(*) FROM hs1; PREPARE TRANSACTION 'foobar'; ROLLBACK PREPARED 'foobar'; ROLLBACK; BEGIN; SELECT count(*) FROM hs1; ROLLBACK PREPARED 'foobar'; ROLLBACK; -- Locks BEGIN; LOCK hs1; COMMIT; BEGIN; LOCK hs1 IN SHARE UPDATE EXCLUSIVE MODE; COMMIT; BEGIN; LOCK hs1 IN SHARE MODE; COMMIT; BEGIN; LOCK hs1 IN SHARE ROW EXCLUSIVE MODE; COMMIT; BEGIN; LOCK hs1 IN EXCLUSIVE MODE; COMMIT; BEGIN; LOCK hs1 IN ACCESS EXCLUSIVE MODE; COMMIT; -- Listen listen a; notify a; -- disallowed commands ANALYZE hs1; VACUUM hs2; CLUSTER hs2 using hs1_pkey; REINDEX TABLE hs2; REVOKE SELECT ON hs1 FROM PUBLIC; GRANT SELECT ON hs1 TO PUBLIC; pgFormatter-4.2/t/pg-test-files/sql/hs_standby_functions.sql000066400000000000000000000007611361326045100243520ustar00rootroot00000000000000-- -- Hot Standby tests -- -- hs_standby_functions.sql -- -- should fail select txid_current(); select length(txid_current_snapshot()::text) >= 4; select pg_start_backup('should fail'); select pg_switch_wal(); select pg_stop_backup(); -- should return no rows select * from pg_prepared_xacts; -- just the startup process select locktype, virtualxid, virtualtransaction, mode, granted from pg_locks where virtualxid = '1/1'; -- suicide is painless select pg_cancel_backend(pg_backend_pid()); pgFormatter-4.2/t/pg-test-files/sql/identity.sql000066400000000000000000000171461361326045100217620ustar00rootroot00000000000000-- sanity check of system catalog SELECT attrelid, attname, attidentity FROM pg_attribute WHERE attidentity NOT IN ('', 'a', 'd'); CREATE TABLE itest1 (a int generated by default as identity, b text); CREATE TABLE itest2 (a bigint generated always as identity, b text); CREATE TABLE itest3 (a smallint generated by default as identity (start with 7 increment by 5), b text); ALTER TABLE itest3 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- error SELECT table_name, column_name, column_default, is_nullable, is_identity, identity_generation, identity_start, identity_increment, identity_maximum, identity_minimum, identity_cycle FROM information_schema.columns WHERE table_name LIKE 'itest_' ORDER BY 1, 2; -- internal sequences should not be shown here SELECT sequence_name FROM information_schema.sequences WHERE sequence_name LIKE 'itest%'; SELECT pg_get_serial_sequence('itest1', 'a'); \d itest1_a_seq CREATE TABLE itest4 (a int, b text); ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- error, requires NOT NULL ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL; ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- ok ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL; -- error, disallowed ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- error, already set ALTER TABLE itest4 ALTER COLUMN b ADD GENERATED ALWAYS AS IDENTITY; -- error, wrong data type -- for later ALTER TABLE itest4 ALTER COLUMN b SET DEFAULT ''; -- invalid column type CREATE TABLE itest_err_1 (a text generated by default as identity); -- duplicate identity CREATE TABLE itest_err_2 (a int generated always as identity generated by default as identity); -- cannot have default and identity CREATE TABLE itest_err_3 (a int default 5 generated by default as identity); -- cannot combine serial and identity CREATE TABLE itest_err_4 (a serial generated by default as identity); INSERT INTO itest1 DEFAULT VALUES; INSERT INTO itest1 DEFAULT VALUES; INSERT INTO itest2 DEFAULT VALUES; INSERT INTO itest2 DEFAULT VALUES; INSERT INTO itest3 DEFAULT VALUES; INSERT INTO itest3 DEFAULT VALUES; INSERT INTO itest4 DEFAULT VALUES; INSERT INTO itest4 DEFAULT VALUES; SELECT * FROM itest1; SELECT * FROM itest2; SELECT * FROM itest3; SELECT * FROM itest4; -- VALUES RTEs INSERT INTO itest3 VALUES (DEFAULT, 'a'); INSERT INTO itest3 VALUES (DEFAULT, 'b'), (DEFAULT, 'c'); SELECT * FROM itest3; -- OVERRIDING tests INSERT INTO itest1 VALUES (10, 'xyz'); INSERT INTO itest1 OVERRIDING USER VALUE VALUES (10, 'xyz'); SELECT * FROM itest1; INSERT INTO itest2 VALUES (10, 'xyz'); INSERT INTO itest2 OVERRIDING SYSTEM VALUE VALUES (10, 'xyz'); SELECT * FROM itest2; -- UPDATE tests UPDATE itest1 SET a = 101 WHERE a = 1; UPDATE itest1 SET a = DEFAULT WHERE a = 2; SELECT * FROM itest1; UPDATE itest2 SET a = 101 WHERE a = 1; UPDATE itest2 SET a = DEFAULT WHERE a = 2; SELECT * FROM itest2; -- COPY tests CREATE TABLE itest9 (a int GENERATED ALWAYS AS IDENTITY, b text, c bigint); SELECT * FROM itest9 ORDER BY c; -- DROP IDENTITY tests ALTER TABLE itest4 ALTER COLUMN a DROP IDENTITY; ALTER TABLE itest4 ALTER COLUMN a DROP IDENTITY; -- error ALTER TABLE itest4 ALTER COLUMN a DROP IDENTITY IF EXISTS; -- noop INSERT INTO itest4 DEFAULT VALUES; -- fails because NOT NULL is not dropped ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL; INSERT INTO itest4 DEFAULT VALUES; SELECT * FROM itest4; -- check that sequence is removed SELECT sequence_name FROM itest4_a_seq; -- test views CREATE TABLE itest10 (a int generated by default as identity, b text); CREATE TABLE itest11 (a int generated always as identity, b text); CREATE VIEW itestv10 AS SELECT * FROM itest10; CREATE VIEW itestv11 AS SELECT * FROM itest11; INSERT INTO itestv10 DEFAULT VALUES; INSERT INTO itestv10 DEFAULT VALUES; INSERT INTO itestv11 DEFAULT VALUES; INSERT INTO itestv11 DEFAULT VALUES; SELECT * FROM itestv10; SELECT * FROM itestv11; INSERT INTO itestv10 VALUES (10, 'xyz'); INSERT INTO itestv10 OVERRIDING USER VALUE VALUES (11, 'xyz'); SELECT * FROM itestv10; INSERT INTO itestv11 VALUES (10, 'xyz'); INSERT INTO itestv11 OVERRIDING SYSTEM VALUE VALUES (11, 'xyz'); SELECT * FROM itestv11; DROP VIEW itestv10, itestv11; -- ADD COLUMN CREATE TABLE itest13 (a int); -- add column to empty table ALTER TABLE itest13 ADD COLUMN b int GENERATED BY DEFAULT AS IDENTITY; INSERT INTO itest13 VALUES (1), (2), (3); -- add column to populated table ALTER TABLE itest13 ADD COLUMN c int GENERATED BY DEFAULT AS IDENTITY; SELECT * FROM itest13; -- various ALTER COLUMN tests -- fail, not allowed for identity columns ALTER TABLE itest1 ALTER COLUMN a SET DEFAULT 1; -- fail, not allowed, already has a default CREATE TABLE itest5 (a serial, b text); ALTER TABLE itest5 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; ALTER TABLE itest3 ALTER COLUMN a TYPE int; SELECT seqtypid::regtype FROM pg_sequence WHERE seqrelid = 'itest3_a_seq'::regclass; \d itest3 ALTER TABLE itest3 ALTER COLUMN a TYPE text; -- error -- kinda silly to change property in the same command, but it should work ALTER TABLE itest3 ADD COLUMN c int GENERATED BY DEFAULT AS IDENTITY, ALTER COLUMN c SET GENERATED ALWAYS; \d itest3 -- ALTER COLUMN ... SET CREATE TABLE itest6 (a int GENERATED ALWAYS AS IDENTITY, b text); INSERT INTO itest6 DEFAULT VALUES; ALTER TABLE itest6 ALTER COLUMN a SET GENERATED BY DEFAULT SET INCREMENT BY 2 SET START WITH 100 RESTART; INSERT INTO itest6 DEFAULT VALUES; INSERT INTO itest6 DEFAULT VALUES; SELECT * FROM itest6; SELECT table_name, column_name, is_identity, identity_generation FROM information_schema.columns WHERE table_name = 'itest6'; ALTER TABLE itest6 ALTER COLUMN b SET INCREMENT BY 2; -- fail, not identity -- prohibited direct modification of sequence ALTER SEQUENCE itest6_a_seq OWNED BY NONE; -- inheritance CREATE TABLE itest7 (a int GENERATED ALWAYS AS IDENTITY); INSERT INTO itest7 DEFAULT VALUES; SELECT * FROM itest7; -- identity property is not inherited CREATE TABLE itest7a (b text) INHERITS (itest7); -- make column identity in child table CREATE TABLE itest7b (a int); CREATE TABLE itest7c (a int GENERATED ALWAYS AS IDENTITY) INHERITS (itest7b); INSERT INTO itest7c DEFAULT VALUES; SELECT * FROM itest7c; CREATE TABLE itest7d (a int not null); CREATE TABLE itest7e () INHERITS (itest7d); ALTER TABLE itest7d ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; ALTER TABLE itest7d ADD COLUMN b int GENERATED ALWAYS AS IDENTITY; -- error SELECT table_name, column_name, is_nullable, is_identity, identity_generation FROM information_schema.columns WHERE table_name LIKE 'itest7%' ORDER BY 1, 2; -- These ALTER TABLE variants will not recurse. ALTER TABLE itest7 ALTER COLUMN a SET GENERATED BY DEFAULT; ALTER TABLE itest7 ALTER COLUMN a RESTART; ALTER TABLE itest7 ALTER COLUMN a DROP IDENTITY; -- privileges CREATE USER regress_identity_user1; CREATE TABLE itest8 (a int GENERATED ALWAYS AS IDENTITY, b text); GRANT SELECT, INSERT ON itest8 TO regress_identity_user1; SET ROLE regress_identity_user1; INSERT INTO itest8 DEFAULT VALUES; SELECT * FROM itest8; RESET ROLE; DROP TABLE itest8; DROP USER regress_identity_user1; -- typed tables (currently not supported) CREATE TYPE itest_type AS (f1 integer, f2 text, f3 bigint); CREATE TABLE itest12 OF itest_type (f1 WITH OPTIONS GENERATED ALWAYS AS IDENTITY); -- error DROP TYPE itest_type CASCADE; -- table partitions (currently not supported) CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1); CREATE TABLE itest_child PARTITION OF itest_parent ( f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY ) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error DROP TABLE itest_parent; pgFormatter-4.2/t/pg-test-files/sql/index_including.sql000066400000000000000000000236661361326045100233000ustar00rootroot00000000000000/* * 1.test CREATE INDEX * * Deliberately avoid dropping objects in this section, to get some pg_dump * coverage. */ -- Regular index with included columns CREATE TABLE tbl_include_reg (c1 int, c2 int, c3 int, c4 box); INSERT INTO tbl_include_reg SELECT x, 2*x, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x; CREATE INDEX tbl_include_reg_idx ON tbl_include_reg (c1, c2) INCLUDE (c3, c4); -- duplicate column is pretty pointless, but we allow it anyway CREATE INDEX ON tbl_include_reg (c1, c2) INCLUDE (c1, c3); SELECT pg_get_indexdef(i.indexrelid) FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid WHERE i.indrelid = 'tbl_include_reg'::regclass ORDER BY c.relname; \d tbl_include_reg_idx -- Unique index and unique constraint CREATE TABLE tbl_include_unique1 (c1 int, c2 int, c3 int, c4 box); INSERT INTO tbl_include_unique1 SELECT x, 2*x, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x; CREATE UNIQUE INDEX tbl_include_unique1_idx_unique ON tbl_include_unique1 using btree (c1, c2) INCLUDE (c3, c4); ALTER TABLE tbl_include_unique1 add UNIQUE USING INDEX tbl_include_unique1_idx_unique; ALTER TABLE tbl_include_unique1 add UNIQUE (c1, c2) INCLUDE (c3, c4); SELECT pg_get_indexdef(i.indexrelid) FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid WHERE i.indrelid = 'tbl_include_unique1'::regclass ORDER BY c.relname; -- Unique index and unique constraint. Both must fail. CREATE TABLE tbl_include_unique2 (c1 int, c2 int, c3 int, c4 box); INSERT INTO tbl_include_unique2 SELECT 1, 2, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x; CREATE UNIQUE INDEX tbl_include_unique2_idx_unique ON tbl_include_unique2 using btree (c1, c2) INCLUDE (c3, c4); ALTER TABLE tbl_include_unique2 add UNIQUE (c1, c2) INCLUDE (c3, c4); -- PK constraint CREATE TABLE tbl_include_pk (c1 int, c2 int, c3 int, c4 box); INSERT INTO tbl_include_pk SELECT 1, 2*x, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x; ALTER TABLE tbl_include_pk add PRIMARY KEY (c1, c2) INCLUDE (c3, c4); SELECT pg_get_indexdef(i.indexrelid) FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid WHERE i.indrelid = 'tbl_include_pk'::regclass ORDER BY c.relname; CREATE TABLE tbl_include_box (c1 int, c2 int, c3 int, c4 box); INSERT INTO tbl_include_box SELECT 1, 2*x, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x; CREATE UNIQUE INDEX tbl_include_box_idx_unique ON tbl_include_box using btree (c1, c2) INCLUDE (c3, c4); ALTER TABLE tbl_include_box add PRIMARY KEY USING INDEX tbl_include_box_idx_unique; SELECT pg_get_indexdef(i.indexrelid) FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid WHERE i.indrelid = 'tbl_include_box'::regclass ORDER BY c.relname; -- PK constraint. Must fail. CREATE TABLE tbl_include_box_pk (c1 int, c2 int, c3 int, c4 box); INSERT INTO tbl_include_box_pk SELECT 1, 2, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x; ALTER TABLE tbl_include_box_pk add PRIMARY KEY (c1, c2) INCLUDE (c3, c4); /* * 2. Test CREATE TABLE with constraint */ CREATE TABLE tbl (c1 int,c2 int, c3 int, c4 box, CONSTRAINT covering UNIQUE(c1,c2) INCLUDE(c3,c4)); SELECT indexrelid::regclass, indnatts, indnkeyatts, indisunique, indisprimary, indkey, indclass FROM pg_index WHERE indrelid = 'tbl'::regclass::oid; SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'tbl'::regclass::oid; -- ensure that constraint works INSERT INTO tbl SELECT 1, 2, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x; DROP TABLE tbl; CREATE TABLE tbl (c1 int,c2 int, c3 int, c4 box, CONSTRAINT covering PRIMARY KEY(c1,c2) INCLUDE(c3,c4)); SELECT indexrelid::regclass, indnatts, indnkeyatts, indisunique, indisprimary, indkey, indclass FROM pg_index WHERE indrelid = 'tbl'::regclass::oid; SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'tbl'::regclass::oid; -- ensure that constraint works INSERT INTO tbl SELECT 1, 2, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x; INSERT INTO tbl SELECT 1, NULL, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x; INSERT INTO tbl SELECT x, 2*x, NULL, NULL FROM generate_series(1,300) AS x; explain (costs off) select * from tbl where (c1,c2,c3) < (2,5,1); select * from tbl where (c1,c2,c3) < (2,5,1); -- row comparison that compares high key at page boundary SET enable_seqscan = off; explain (costs off) select * from tbl where (c1,c2,c3) < (262,1,1) limit 1; select * from tbl where (c1,c2,c3) < (262,1,1) limit 1; DROP TABLE tbl; RESET enable_seqscan; CREATE TABLE tbl (c1 int,c2 int, c3 int, c4 box, UNIQUE(c1,c2) INCLUDE(c3,c4)); SELECT indexrelid::regclass, indnatts, indnkeyatts, indisunique, indisprimary, indkey, indclass FROM pg_index WHERE indrelid = 'tbl'::regclass::oid; SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'tbl'::regclass::oid; -- ensure that constraint works INSERT INTO tbl SELECT 1, 2, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x; DROP TABLE tbl; CREATE TABLE tbl (c1 int,c2 int, c3 int, c4 box, PRIMARY KEY(c1,c2) INCLUDE(c3,c4)); SELECT indexrelid::regclass, indnatts, indnkeyatts, indisunique, indisprimary, indkey, indclass FROM pg_index WHERE indrelid = 'tbl'::regclass::oid; SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'tbl'::regclass::oid; -- ensure that constraint works INSERT INTO tbl SELECT 1, 2, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x; INSERT INTO tbl SELECT 1, NULL, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x; INSERT INTO tbl SELECT x, 2*x, NULL, NULL FROM generate_series(1,10) AS x; DROP TABLE tbl; CREATE TABLE tbl (c1 int,c2 int, c3 int, c4 box, EXCLUDE USING btree (c1 WITH =) INCLUDE(c3,c4)); SELECT indexrelid::regclass, indnatts, indnkeyatts, indisunique, indisprimary, indkey, indclass FROM pg_index WHERE indrelid = 'tbl'::regclass::oid; SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conrelid = 'tbl'::regclass::oid; -- ensure that constraint works INSERT INTO tbl SELECT 1, 2, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x; INSERT INTO tbl SELECT x, 2*x, NULL, NULL FROM generate_series(1,10) AS x; DROP TABLE tbl; /* * 3.0 Test ALTER TABLE DROP COLUMN. * Any column deletion leads to index deletion. */ CREATE TABLE tbl (c1 int,c2 int, c3 int, c4 int); CREATE UNIQUE INDEX tbl_idx ON tbl using btree(c1, c2, c3, c4); SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; ALTER TABLE tbl DROP COLUMN c3; SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; DROP TABLE tbl; /* * 3.1 Test ALTER TABLE DROP COLUMN. * Included column deletion leads to the index deletion, * AS well AS key columns deletion. It's explained in documentation. */ CREATE TABLE tbl (c1 int,c2 int, c3 int, c4 box); CREATE UNIQUE INDEX tbl_idx ON tbl using btree(c1, c2) INCLUDE(c3,c4); SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; ALTER TABLE tbl DROP COLUMN c3; SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; DROP TABLE tbl; /* * 3.2 Test ALTER TABLE DROP COLUMN. * Included column deletion leads to the index deletion. * AS well AS key columns deletion. It's explained in documentation. */ CREATE TABLE tbl (c1 int,c2 int, c3 int, c4 box, UNIQUE(c1, c2) INCLUDE(c3,c4)); SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; ALTER TABLE tbl DROP COLUMN c3; SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; ALTER TABLE tbl DROP COLUMN c1; SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; DROP TABLE tbl; /* * 3.3 Test ALTER TABLE SET STATISTICS */ CREATE TABLE tbl (c1 int, c2 int); CREATE INDEX tbl_idx ON tbl (c1, (c1+0)) INCLUDE (c2); ALTER INDEX tbl_idx ALTER COLUMN 1 SET STATISTICS 1000; ALTER INDEX tbl_idx ALTER COLUMN 2 SET STATISTICS 1000; ALTER INDEX tbl_idx ALTER COLUMN 3 SET STATISTICS 1000; ALTER INDEX tbl_idx ALTER COLUMN 4 SET STATISTICS 1000; DROP TABLE tbl; /* * 4. CREATE INDEX CONCURRENTLY */ CREATE TABLE tbl (c1 int,c2 int, c3 int, c4 box, UNIQUE(c1, c2) INCLUDE(c3,c4)); INSERT INTO tbl SELECT x, 2*x, 3*x, box('4,4,4,4') FROM generate_series(1,1000) AS x; CREATE UNIQUE INDEX CONCURRENTLY on tbl (c1, c2) INCLUDE (c3, c4); SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; DROP TABLE tbl; /* * 5. REINDEX */ CREATE TABLE tbl (c1 int,c2 int, c3 int, c4 box, UNIQUE(c1, c2) INCLUDE(c3,c4)); SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; ALTER TABLE tbl DROP COLUMN c3; SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; REINDEX INDEX tbl_c1_c2_c3_c4_key; SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; ALTER TABLE tbl DROP COLUMN c1; SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl' ORDER BY indexname; DROP TABLE tbl; /* * 7. Check various AMs. All but btree and gist must fail. */ CREATE TABLE tbl (c1 int,c2 int, c3 box, c4 box); CREATE INDEX on tbl USING brin(c1, c2) INCLUDE (c3, c4); CREATE INDEX on tbl USING gist(c3) INCLUDE (c1, c4); CREATE INDEX on tbl USING spgist(c3) INCLUDE (c4); CREATE INDEX on tbl USING gin(c1, c2) INCLUDE (c3, c4); CREATE INDEX on tbl USING hash(c1, c2) INCLUDE (c3, c4); CREATE INDEX on tbl USING rtree(c3) INCLUDE (c1, c4); CREATE INDEX on tbl USING btree(c1, c2) INCLUDE (c3, c4); DROP TABLE tbl; /* * 8. Update, delete values in indexed table. */ CREATE TABLE tbl (c1 int, c2 int, c3 int, c4 box); INSERT INTO tbl SELECT x, 2*x, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x; CREATE UNIQUE INDEX tbl_idx_unique ON tbl using btree(c1, c2) INCLUDE (c3,c4); UPDATE tbl SET c1 = 100 WHERE c1 = 2; UPDATE tbl SET c1 = 1 WHERE c1 = 3; -- should fail UPDATE tbl SET c2 = 2 WHERE c1 = 1; UPDATE tbl SET c3 = 1; DELETE FROM tbl WHERE c1 = 5 OR c3 = 12; DROP TABLE tbl; /* * 9. Alter column type. */ CREATE TABLE tbl (c1 int,c2 int, c3 int, c4 box, UNIQUE(c1, c2) INCLUDE(c3,c4)); INSERT INTO tbl SELECT x, 2*x, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x; ALTER TABLE tbl ALTER c1 TYPE bigint; ALTER TABLE tbl ALTER c3 TYPE bigint; \d tbl DROP TABLE tbl; pgFormatter-4.2/t/pg-test-files/sql/index_including_gist.sql000066400000000000000000000074401361326045100243160ustar00rootroot00000000000000/* * 1.1. test CREATE INDEX with buffered build */ -- Regular index with included columns CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box); -- size is chosen to exceed page size and trigger actual truncation INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,8000) AS x; CREATE INDEX tbl_gist_idx ON tbl_gist using gist (c4) INCLUDE (c1,c2,c3); SELECT pg_get_indexdef(i.indexrelid) FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid WHERE i.indrelid = 'tbl_gist'::regclass ORDER BY c.relname; SELECT * FROM tbl_gist where c4 <@ box(point(1,1),point(10,10)); SET enable_bitmapscan TO off; EXPLAIN (costs off) SELECT * FROM tbl_gist where c4 <@ box(point(1,1),point(10,10)); SET enable_bitmapscan TO default; DROP TABLE tbl_gist; /* * 1.2. test CREATE INDEX with inserts */ -- Regular index with included columns CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box); -- size is chosen to exceed page size and trigger actual truncation CREATE INDEX tbl_gist_idx ON tbl_gist using gist (c4) INCLUDE (c1,c2,c3); INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,8000) AS x; SELECT pg_get_indexdef(i.indexrelid) FROM pg_index i JOIN pg_class c ON i.indexrelid = c.oid WHERE i.indrelid = 'tbl_gist'::regclass ORDER BY c.relname; SELECT * FROM tbl_gist where c4 <@ box(point(1,1),point(10,10)); SET enable_bitmapscan TO off; EXPLAIN (costs off) SELECT * FROM tbl_gist where c4 <@ box(point(1,1),point(10,10)); SET enable_bitmapscan TO default; DROP TABLE tbl_gist; /* * 2. CREATE INDEX CONCURRENTLY */ CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box); INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,10) AS x; CREATE INDEX CONCURRENTLY tbl_gist_idx ON tbl_gist using gist (c4) INCLUDE (c1,c2,c3); SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl_gist' ORDER BY indexname; DROP TABLE tbl_gist; /* * 3. REINDEX */ CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box); INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,10) AS x; CREATE INDEX tbl_gist_idx ON tbl_gist using gist (c4) INCLUDE (c1,c3); SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl_gist' ORDER BY indexname; REINDEX INDEX tbl_gist_idx; SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl_gist' ORDER BY indexname; ALTER TABLE tbl_gist DROP COLUMN c1; SELECT indexdef FROM pg_indexes WHERE tablename = 'tbl_gist' ORDER BY indexname; DROP TABLE tbl_gist; /* * 4. Update, delete values in indexed table. */ CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box); INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,10) AS x; CREATE INDEX tbl_gist_idx ON tbl_gist using gist (c4) INCLUDE (c1,c3); UPDATE tbl_gist SET c1 = 100 WHERE c1 = 2; UPDATE tbl_gist SET c1 = 1 WHERE c1 = 3; DELETE FROM tbl_gist WHERE c1 = 5 OR c3 = 12; DROP TABLE tbl_gist; /* * 5. Alter column type. */ CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box); INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,10) AS x; CREATE INDEX tbl_gist_idx ON tbl_gist using gist (c4) INCLUDE (c1,c3); ALTER TABLE tbl_gist ALTER c1 TYPE bigint; ALTER TABLE tbl_gist ALTER c3 TYPE bigint; \d tbl_gist DROP TABLE tbl_gist; /* * 6. EXCLUDE constraint. */ CREATE TABLE tbl_gist (c1 int, c2 int, c3 int, c4 box, EXCLUDE USING gist (c4 WITH &&) INCLUDE (c1, c2, c3)); INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(x,x+1),point(2*x,2*x+1)) FROM generate_series(1,10) AS x; INSERT INTO tbl_gist SELECT x, 2*x, 3*x, box(point(3*x,2*x),point(3*x+1,2*x+1)) FROM generate_series(1,10) AS x; EXPLAIN (costs off) SELECT * FROM tbl_gist where c4 <@ box(point(1,1),point(10,10)); \d tbl_gist DROP TABLE tbl_gist; pgFormatter-4.2/t/pg-test-files/sql/indexing.sql000066400000000000000000001044771361326045100217420ustar00rootroot00000000000000-- Creating an index on a partitioned table makes the partitions -- automatically get the index create table idxpart (a int, b int, c text) partition by range (a); -- relhassubclass of a partitioned index is false before creating any partition. -- It will be set after the first partition is created. create index idxpart_idx on idxpart (a); select relhassubclass from pg_class where relname = 'idxpart_idx'; -- Check that partitioned indexes are present in pg_indexes. select indexdef from pg_indexes where indexname like 'idxpart_idx%'; drop index idxpart_idx; create table idxpart1 partition of idxpart for values from (0) to (10); create table idxpart2 partition of idxpart for values from (10) to (100) partition by range (b); create table idxpart21 partition of idxpart2 for values from (0) to (100); -- Even with partitions, relhassubclass should not be set if a partitioned -- index is created only on the parent. create index idxpart_idx on only idxpart(a); select relhassubclass from pg_class where relname = 'idxpart_idx'; drop index idxpart_idx; create index on idxpart (a); select relname, relkind, relhassubclass, inhparent::regclass from pg_class left join pg_index ix on (indexrelid = oid) left join pg_inherits on (ix.indexrelid = inhrelid) where relname like 'idxpart%' order by relname; drop table idxpart; -- Some unsupported features create table idxpart (a int, b int, c text) partition by range (a); create table idxpart1 partition of idxpart for values from (0) to (10); create index concurrently on idxpart (a); drop table idxpart; -- Verify bugfix with query on indexed partitioned table with no partitions -- https://postgr.es/m/20180124162006.pmapfiznhgngwtjf@alvherre.pgsql CREATE TABLE idxpart (col1 INT) PARTITION BY RANGE (col1); CREATE INDEX ON idxpart (col1); CREATE TABLE idxpart_two (col2 INT); SELECT col2 FROM idxpart_two fk LEFT OUTER JOIN idxpart pk ON (col1 = col2); DROP table idxpart, idxpart_two; -- Verify bugfix with index rewrite on ALTER TABLE / SET DATA TYPE -- https://postgr.es/m/CAKcux6mxNCGsgATwf5CGMF8g4WSupCXicCVMeKUTuWbyxHOMsQ@mail.gmail.com CREATE TABLE idxpart (a INT, b TEXT, c INT) PARTITION BY RANGE(a); CREATE TABLE idxpart1 PARTITION OF idxpart FOR VALUES FROM (MINVALUE) TO (MAXVALUE); CREATE INDEX partidx_abc_idx ON idxpart (a, b, c); INSERT INTO idxpart (a, b, c) SELECT i, i, i FROM generate_series(1, 50) i; ALTER TABLE idxpart ALTER COLUMN c TYPE numeric; DROP TABLE idxpart; -- If a table without index is attached as partition to a table with -- an index, the index is automatically created create table idxpart (a int, b int, c text) partition by range (a); create index idxparti on idxpart (a); create index idxparti2 on idxpart (b, c); create table idxpart1 (like idxpart); \d idxpart1 alter table idxpart attach partition idxpart1 for values from (0) to (10); \d idxpart1 \d+ idxpart1_a_idx \d+ idxpart1_b_c_idx drop table idxpart; -- If a partition already has an index, don't create a duplicative one create table idxpart (a int, b int) partition by range (a, b); create table idxpart1 partition of idxpart for values from (0, 0) to (10, 10); create index on idxpart1 (a, b); create index on idxpart (a, b); \d idxpart1 select relname, relkind, relhassubclass, inhparent::regclass from pg_class left join pg_index ix on (indexrelid = oid) left join pg_inherits on (ix.indexrelid = inhrelid) where relname like 'idxpart%' order by relname; drop table idxpart; -- DROP behavior for partitioned indexes create table idxpart (a int) partition by range (a); create index on idxpart (a); create table idxpart1 partition of idxpart for values from (0) to (10); drop index idxpart1_a_idx; -- no way drop index idxpart_a_idx; -- both indexes go away select relname, relkind from pg_class where relname like 'idxpart%' order by relname; create index on idxpart (a); drop table idxpart1; -- the index on partition goes away too select relname, relkind from pg_class where relname like 'idxpart%' order by relname; drop table idxpart; -- ALTER INDEX .. ATTACH, error cases create table idxpart (a int, b int) partition by range (a, b); create table idxpart1 partition of idxpart for values from (0, 0) to (10, 10); create index idxpart_a_b_idx on only idxpart (a, b); create index idxpart1_a_b_idx on idxpart1 (a, b); create index idxpart1_tst1 on idxpart1 (b, a); create index idxpart1_tst2 on idxpart1 using hash (a); create index idxpart1_tst3 on idxpart1 (a, b) where a > 10; alter index idxpart attach partition idxpart1; alter index idxpart_a_b_idx attach partition idxpart1; alter index idxpart_a_b_idx attach partition idxpart_a_b_idx; alter index idxpart_a_b_idx attach partition idxpart1_b_idx; alter index idxpart_a_b_idx attach partition idxpart1_tst1; alter index idxpart_a_b_idx attach partition idxpart1_tst2; alter index idxpart_a_b_idx attach partition idxpart1_tst3; -- OK alter index idxpart_a_b_idx attach partition idxpart1_a_b_idx; alter index idxpart_a_b_idx attach partition idxpart1_a_b_idx; -- quiet -- reject dupe create index idxpart1_2_a_b on idxpart1 (a, b); alter index idxpart_a_b_idx attach partition idxpart1_2_a_b; drop table idxpart; -- make sure everything's gone select indexrelid::regclass, indrelid::regclass from pg_index where indexrelid::regclass::text like 'idxpart%'; -- Don't auto-attach incompatible indexes create table idxpart (a int, b int) partition by range (a); create table idxpart1 (a int, b int); create index on idxpart1 using hash (a); create index on idxpart1 (a) where b > 1; create index on idxpart1 ((a + 0)); create index on idxpart1 (a, a); create index on idxpart (a); alter table idxpart attach partition idxpart1 for values from (0) to (1000); \d idxpart1 drop table idxpart; -- If CREATE INDEX ONLY, don't create indexes on partitions; and existing -- indexes on partitions don't change parent. ALTER INDEX ATTACH can change -- the parent after the fact. create table idxpart (a int) partition by range (a); create table idxpart1 partition of idxpart for values from (0) to (100); create table idxpart2 partition of idxpart for values from (100) to (1000) partition by range (a); create table idxpart21 partition of idxpart2 for values from (100) to (200); create table idxpart22 partition of idxpart2 for values from (200) to (300); create index on idxpart22 (a); create index on only idxpart2 (a); create index on idxpart (a); -- Here we expect that idxpart1 and idxpart2 have a new index, but idxpart21 -- does not; also, idxpart22 is not attached. \d idxpart1 \d idxpart2 \d idxpart21 select indexrelid::regclass, indrelid::regclass, inhparent::regclass from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid) where indexrelid::regclass::text like 'idxpart%' order by indexrelid::regclass::text collate "C"; alter index idxpart2_a_idx attach partition idxpart22_a_idx; select indexrelid::regclass, indrelid::regclass, inhparent::regclass from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid) where indexrelid::regclass::text like 'idxpart%' order by indexrelid::regclass::text collate "C"; -- attaching idxpart22 is not enough to set idxpart22_a_idx valid ... alter index idxpart2_a_idx attach partition idxpart22_a_idx; \d idxpart2 -- ... but this one is. create index on idxpart21 (a); alter index idxpart2_a_idx attach partition idxpart21_a_idx; \d idxpart2 drop table idxpart; -- When a table is attached a partition and it already has an index, a -- duplicate index should not get created, but rather the index becomes -- attached to the parent's index. create table idxpart (a int, b int, c text) partition by range (a); create index idxparti on idxpart (a); create index idxparti2 on idxpart (b, c); create table idxpart1 (like idxpart including indexes); \d idxpart1 select relname, relkind, inhparent::regclass from pg_class left join pg_index ix on (indexrelid = oid) left join pg_inherits on (ix.indexrelid = inhrelid) where relname like 'idxpart%' order by relname; alter table idxpart attach partition idxpart1 for values from (0) to (10); \d idxpart1 select relname, relkind, inhparent::regclass from pg_class left join pg_index ix on (indexrelid = oid) left join pg_inherits on (ix.indexrelid = inhrelid) where relname like 'idxpart%' order by relname; drop table idxpart; -- Verify that attaching an invalid index does not mark the parent index valid. -- On the other hand, attaching a valid index marks not only its direct -- ancestor valid, but also any indirect ancestor that was only missing the one -- that was just made valid create table idxpart (a int, b int) partition by range (a); create table idxpart1 partition of idxpart for values from (1) to (1000) partition by range (a); create table idxpart11 partition of idxpart1 for values from (1) to (100); create index on only idxpart1 (a); create index on only idxpart (a); -- this results in two invalid indexes: select relname, indisvalid from pg_class join pg_index on indexrelid = oid where relname like 'idxpart%' order by relname; -- idxpart1_a_idx is not valid, so idxpart_a_idx should not become valid: alter index idxpart_a_idx attach partition idxpart1_a_idx; select relname, indisvalid from pg_class join pg_index on indexrelid = oid where relname like 'idxpart%' order by relname; -- after creating and attaching this, both idxpart1_a_idx and idxpart_a_idx -- should become valid create index on idxpart11 (a); alter index idxpart1_a_idx attach partition idxpart11_a_idx; select relname, indisvalid from pg_class join pg_index on indexrelid = oid where relname like 'idxpart%' order by relname; drop table idxpart; -- verify dependency handling during ALTER TABLE DETACH PARTITION create table idxpart (a int) partition by range (a); create table idxpart1 (like idxpart); create index on idxpart1 (a); create index on idxpart (a); create table idxpart2 (like idxpart); alter table idxpart attach partition idxpart1 for values from (0000) to (1000); alter table idxpart attach partition idxpart2 for values from (1000) to (2000); create table idxpart3 partition of idxpart for values from (2000) to (3000); select relname, relkind from pg_class where relname like 'idxpart%' order by relname; -- a) after detaching partitions, the indexes can be dropped independently alter table idxpart detach partition idxpart1; alter table idxpart detach partition idxpart2; alter table idxpart detach partition idxpart3; drop index idxpart1_a_idx; drop index idxpart2_a_idx; drop index idxpart3_a_idx; select relname, relkind from pg_class where relname like 'idxpart%' order by relname; drop table idxpart, idxpart1, idxpart2, idxpart3; select relname, relkind from pg_class where relname like 'idxpart%' order by relname; create table idxpart (a int) partition by range (a); create table idxpart1 (like idxpart); create index on idxpart1 (a); create index on idxpart (a); create table idxpart2 (like idxpart); alter table idxpart attach partition idxpart1 for values from (0000) to (1000); alter table idxpart attach partition idxpart2 for values from (1000) to (2000); create table idxpart3 partition of idxpart for values from (2000) to (3000); -- b) after detaching, dropping the index on parent does not remove the others select relname, relkind from pg_class where relname like 'idxpart%' order by relname; alter table idxpart detach partition idxpart1; alter table idxpart detach partition idxpart2; alter table idxpart detach partition idxpart3; drop index idxpart_a_idx; select relname, relkind from pg_class where relname like 'idxpart%' order by relname; drop table idxpart, idxpart1, idxpart2, idxpart3; select relname, relkind from pg_class where relname like 'idxpart%' order by relname; create table idxpart (a int, b int, c int) partition by range(a); create index on idxpart(c); create table idxpart1 partition of idxpart for values from (0) to (250); create table idxpart2 partition of idxpart for values from (250) to (500); alter table idxpart detach partition idxpart2; \d idxpart2 alter table idxpart2 drop column c; \d idxpart2 drop table idxpart, idxpart2; -- Verify that expression indexes inherit correctly create table idxpart (a int, b int) partition by range (a); create table idxpart1 (like idxpart); create index on idxpart1 ((a + b)); create index on idxpart ((a + b)); create table idxpart2 (like idxpart); alter table idxpart attach partition idxpart1 for values from (0000) to (1000); alter table idxpart attach partition idxpart2 for values from (1000) to (2000); create table idxpart3 partition of idxpart for values from (2000) to (3000); select relname as child, inhparent::regclass as parent, pg_get_indexdef as childdef from pg_class join pg_inherits on inhrelid = oid, lateral pg_get_indexdef(pg_class.oid) where relkind in ('i', 'I') and relname like 'idxpart%' order by relname; drop table idxpart; -- Verify behavior for collation (mis)matches create table idxpart (a text) partition by range (a); create table idxpart1 (like idxpart); create table idxpart2 (like idxpart); create index on idxpart2 (a collate "POSIX"); create index on idxpart2 (a); create index on idxpart2 (a collate "C"); alter table idxpart attach partition idxpart1 for values from ('aaa') to ('bbb'); alter table idxpart attach partition idxpart2 for values from ('bbb') to ('ccc'); create table idxpart3 partition of idxpart for values from ('ccc') to ('ddd'); create index on idxpart (a collate "C"); create table idxpart4 partition of idxpart for values from ('ddd') to ('eee'); select relname as child, inhparent::regclass as parent, pg_get_indexdef as childdef from pg_class left join pg_inherits on inhrelid = oid, lateral pg_get_indexdef(pg_class.oid) where relkind in ('i', 'I') and relname like 'idxpart%' order by relname; drop table idxpart; -- Verify behavior for opclass (mis)matches create table idxpart (a text) partition by range (a); create table idxpart1 (like idxpart); create table idxpart2 (like idxpart); create index on idxpart2 (a); alter table idxpart attach partition idxpart1 for values from ('aaa') to ('bbb'); alter table idxpart attach partition idxpart2 for values from ('bbb') to ('ccc'); create table idxpart3 partition of idxpart for values from ('ccc') to ('ddd'); create index on idxpart (a text_pattern_ops); create table idxpart4 partition of idxpart for values from ('ddd') to ('eee'); -- must *not* have attached the index we created on idxpart2 select relname as child, inhparent::regclass as parent, pg_get_indexdef as childdef from pg_class left join pg_inherits on inhrelid = oid, lateral pg_get_indexdef(pg_class.oid) where relkind in ('i', 'I') and relname like 'idxpart%' order by relname; drop index idxpart_a_idx; create index on only idxpart (a text_pattern_ops); -- must reject alter index idxpart_a_idx attach partition idxpart2_a_idx; drop table idxpart; -- Verify that attaching indexes maps attribute numbers correctly create table idxpart (col1 int, a int, col2 int, b int) partition by range (a); create table idxpart1 (b int, col1 int, col2 int, col3 int, a int); alter table idxpart drop column col1, drop column col2; alter table idxpart1 drop column col1, drop column col2, drop column col3; alter table idxpart attach partition idxpart1 for values from (0) to (1000); create index idxpart_1_idx on only idxpart (b, a); create index idxpart1_1_idx on idxpart1 (b, a); create index idxpart1_1b_idx on idxpart1 (b); -- test expressions and partial-index predicate, too create index idxpart_2_idx on only idxpart ((b + a)) where a > 1; create index idxpart1_2_idx on idxpart1 ((b + a)) where a > 1; create index idxpart1_2b_idx on idxpart1 ((a + b)) where a > 1; create index idxpart1_2c_idx on idxpart1 ((b + a)) where b > 1; alter index idxpart_1_idx attach partition idxpart1_1b_idx; -- fail alter index idxpart_1_idx attach partition idxpart1_1_idx; alter index idxpart_2_idx attach partition idxpart1_2b_idx; -- fail alter index idxpart_2_idx attach partition idxpart1_2c_idx; -- fail alter index idxpart_2_idx attach partition idxpart1_2_idx; -- ok select relname as child, inhparent::regclass as parent, pg_get_indexdef as childdef from pg_class left join pg_inherits on inhrelid = oid, lateral pg_get_indexdef(pg_class.oid) where relkind in ('i', 'I') and relname like 'idxpart%' order by relname; drop table idxpart; -- Make sure the partition columns are mapped correctly create table idxpart (a int, b int, c text) partition by range (a); create index idxparti on idxpart (a); create index idxparti2 on idxpart (c, b); create table idxpart1 (c text, a int, b int); alter table idxpart attach partition idxpart1 for values from (0) to (10); create table idxpart2 (c text, a int, b int); create index on idxpart2 (a); create index on idxpart2 (c, b); alter table idxpart attach partition idxpart2 for values from (10) to (20); select c.relname, pg_get_indexdef(indexrelid) from pg_class c join pg_index i on c.oid = i.indexrelid where indrelid::regclass::text like 'idxpart%' order by indexrelid::regclass::text collate "C"; drop table idxpart; -- Verify that columns are mapped correctly in expression indexes create table idxpart (col1 int, col2 int, a int, b int) partition by range (a); create table idxpart1 (col2 int, b int, col1 int, a int); create table idxpart2 (col1 int, col2 int, b int, a int); alter table idxpart drop column col1, drop column col2; alter table idxpart1 drop column col1, drop column col2; alter table idxpart2 drop column col1, drop column col2; create index on idxpart2 (abs(b)); alter table idxpart attach partition idxpart2 for values from (0) to (1); create index on idxpart (abs(b)); create index on idxpart ((b + 1)); alter table idxpart attach partition idxpart1 for values from (1) to (2); select c.relname, pg_get_indexdef(indexrelid) from pg_class c join pg_index i on c.oid = i.indexrelid where indrelid::regclass::text like 'idxpart%' order by indexrelid::regclass::text collate "C"; drop table idxpart; -- Verify that columns are mapped correctly for WHERE in a partial index create table idxpart (col1 int, a int, col3 int, b int) partition by range (a); alter table idxpart drop column col1, drop column col3; create table idxpart1 (col1 int, col2 int, col3 int, col4 int, b int, a int); alter table idxpart1 drop column col1, drop column col2, drop column col3, drop column col4; alter table idxpart attach partition idxpart1 for values from (0) to (1000); create table idxpart2 (col1 int, col2 int, b int, a int); create index on idxpart2 (a) where b > 1000; alter table idxpart2 drop column col1, drop column col2; alter table idxpart attach partition idxpart2 for values from (1000) to (2000); create index on idxpart (a) where b > 1000; select c.relname, pg_get_indexdef(indexrelid) from pg_class c join pg_index i on c.oid = i.indexrelid where indrelid::regclass::text like 'idxpart%' order by indexrelid::regclass::text collate "C"; drop table idxpart; -- Column number mapping: dropped columns in the partition create table idxpart1 (drop_1 int, drop_2 int, col_keep int, drop_3 int); alter table idxpart1 drop column drop_1; alter table idxpart1 drop column drop_2; alter table idxpart1 drop column drop_3; create index on idxpart1 (col_keep); create table idxpart (col_keep int) partition by range (col_keep); create index on idxpart (col_keep); alter table idxpart attach partition idxpart1 for values from (0) to (1000); \d idxpart \d idxpart1 select attrelid::regclass, attname, attnum from pg_attribute where attrelid::regclass::text like 'idxpart%' and attnum > 0 order by attrelid::regclass, attnum; drop table idxpart; -- Column number mapping: dropped columns in the parent table create table idxpart(drop_1 int, drop_2 int, col_keep int, drop_3 int) partition by range (col_keep); alter table idxpart drop column drop_1; alter table idxpart drop column drop_2; alter table idxpart drop column drop_3; create table idxpart1 (col_keep int); create index on idxpart1 (col_keep); create index on idxpart (col_keep); alter table idxpart attach partition idxpart1 for values from (0) to (1000); \d idxpart \d idxpart1 select attrelid::regclass, attname, attnum from pg_attribute where attrelid::regclass::text like 'idxpart%' and attnum > 0 order by attrelid::regclass, attnum; drop table idxpart; -- -- Constraint-related indexes -- -- Verify that it works to add primary key / unique to partitioned tables create table idxpart (a int primary key, b int) partition by range (a); \d idxpart -- multiple primary key on child should fail create table failpart partition of idxpart (b primary key) for values from (0) to (100); drop table idxpart; -- primary key on child is okay if there's no PK in the parent, though create table idxpart (a int) partition by range (a); create table idxpart1pk partition of idxpart (a primary key) for values from (0) to (100); \d idxpart1pk drop table idxpart; -- Failing to use the full partition key is not allowed create table idxpart (a int unique, b int) partition by range (a, b); create table idxpart (a int, b int unique) partition by range (a, b); create table idxpart (a int primary key, b int) partition by range (b, a); create table idxpart (a int, b int primary key) partition by range (b, a); -- OK if you use them in some other order create table idxpart (a int, b int, c text, primary key (a, b, c)) partition by range (b, c, a); drop table idxpart; -- not other types of index-based constraints create table idxpart (a int, exclude (a with = )) partition by range (a); -- no expressions in partition key for PK/UNIQUE create table idxpart (a int primary key, b int) partition by range ((b + a)); create table idxpart (a int unique, b int) partition by range ((b + a)); -- use ALTER TABLE to add a primary key create table idxpart (a int, b int, c text) partition by range (a, b); alter table idxpart add primary key (a); -- not an incomplete one though alter table idxpart add primary key (a, b); -- this works \d idxpart create table idxpart1 partition of idxpart for values from (0, 0) to (1000, 1000); \d idxpart1 drop table idxpart; -- use ALTER TABLE to add a unique constraint create table idxpart (a int, b int) partition by range (a, b); alter table idxpart add unique (a); -- not an incomplete one though alter table idxpart add unique (b, a); -- this works \d idxpart drop table idxpart; -- Exclusion constraints cannot be added create table idxpart (a int, b int) partition by range (a); alter table idxpart add exclude (a with =); drop table idxpart; -- When (sub)partitions are created, they also contain the constraint create table idxpart (a int, b int, primary key (a, b)) partition by range (a, b); create table idxpart1 partition of idxpart for values from (1, 1) to (10, 10); create table idxpart2 partition of idxpart for values from (10, 10) to (20, 20) partition by range (b); create table idxpart21 partition of idxpart2 for values from (10) to (15); create table idxpart22 partition of idxpart2 for values from (15) to (20); create table idxpart3 (b int not null, a int not null); alter table idxpart attach partition idxpart3 for values from (20, 20) to (30, 30); select conname, contype, conrelid::regclass, conindid::regclass, conkey from pg_constraint where conrelid::regclass::text like 'idxpart%' order by conname; drop table idxpart; -- Verify that multi-layer partitioning honors the requirement that all -- columns in the partition key must appear in primary/unique key create table idxpart (a int, b int, primary key (a)) partition by range (a); create table idxpart2 partition of idxpart for values from (0) to (1000) partition by range (b); -- fail drop table idxpart; -- Ditto for the ATTACH PARTITION case create table idxpart (a int unique, b int) partition by range (a); create table idxpart1 (a int not null, b int, unique (a, b)) partition by range (a, b); alter table idxpart attach partition idxpart1 for values from (1) to (1000); DROP TABLE idxpart, idxpart1; -- Multi-layer partitioning works correctly in this case: create table idxpart (a int, b int, primary key (a, b)) partition by range (a); create table idxpart2 partition of idxpart for values from (0) to (1000) partition by range (b); create table idxpart21 partition of idxpart2 for values from (0) to (1000); select conname, contype, conrelid::regclass, conindid::regclass, conkey from pg_constraint where conrelid::regclass::text like 'idxpart%' order by conname; drop table idxpart; -- If a partitioned table has a unique/PK constraint, then it's not possible -- to drop the corresponding constraint in the children; nor it's possible -- to drop the indexes individually. Dropping the constraint in the parent -- gets rid of the lot. create table idxpart (i int) partition by hash (i); create table idxpart0 partition of idxpart (i) for values with (modulus 2, remainder 0); create table idxpart1 partition of idxpart (i) for values with (modulus 2, remainder 1); alter table idxpart0 add primary key(i); alter table idxpart add primary key(i); select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid, conname, conislocal, coninhcount, connoinherit, convalidated from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid) left join pg_constraint con on (idx.indexrelid = con.conindid) where indrelid::regclass::text like 'idxpart%' order by indexrelid::regclass::text collate "C"; drop index idxpart0_pkey; -- fail drop index idxpart1_pkey; -- fail alter table idxpart0 drop constraint idxpart0_pkey; -- fail alter table idxpart1 drop constraint idxpart1_pkey; -- fail alter table idxpart drop constraint idxpart_pkey; -- ok select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid, conname, conislocal, coninhcount, connoinherit, convalidated from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid) left join pg_constraint con on (idx.indexrelid = con.conindid) where indrelid::regclass::text like 'idxpart%' order by indexrelid::regclass::text collate "C"; drop table idxpart; -- If the partition to be attached already has a primary key, fail if -- it doesn't match the parent's PK. CREATE TABLE idxpart (c1 INT PRIMARY KEY, c2 INT, c3 VARCHAR(10)) PARTITION BY RANGE(c1); CREATE TABLE idxpart1 (LIKE idxpart); ALTER TABLE idxpart1 ADD PRIMARY KEY (c1, c2); ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (100) TO (200); DROP TABLE idxpart, idxpart1; -- Ditto if there is some distance between the PKs (subpartitioning) create table idxpart (a int, b int, primary key (a)) partition by range (a); create table idxpart1 (a int not null, b int) partition by range (a); create table idxpart11 (a int not null, b int primary key); alter table idxpart1 attach partition idxpart11 for values from (0) to (1000); alter table idxpart attach partition idxpart1 for values from (0) to (10000); drop table idxpart, idxpart1, idxpart11; -- If a partitioned table has a constraint whose index is not valid, -- attaching a missing partition makes it valid. create table idxpart (a int) partition by range (a); create table idxpart0 (like idxpart); alter table idxpart0 add primary key (a); alter table idxpart attach partition idxpart0 for values from (0) to (1000); alter table only idxpart add primary key (a); select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid, conname, conislocal, coninhcount, connoinherit, convalidated from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid) left join pg_constraint con on (idx.indexrelid = con.conindid) where indrelid::regclass::text like 'idxpart%' order by indexrelid::regclass::text collate "C"; alter index idxpart_pkey attach partition idxpart0_pkey; select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid, conname, conislocal, coninhcount, connoinherit, convalidated from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid) left join pg_constraint con on (idx.indexrelid = con.conindid) where indrelid::regclass::text like 'idxpart%' order by indexrelid::regclass::text collate "C"; drop table idxpart; -- Related to the above scenario: ADD PRIMARY KEY on the parent mustn't -- automatically propagate NOT NULL to child columns. create table idxpart (a int) partition by range (a); create table idxpart0 (like idxpart); alter table idxpart0 add unique (a); alter table idxpart attach partition idxpart0 default; alter table only idxpart add primary key (a); -- fail, no NOT NULL constraint alter table idxpart0 alter column a set not null; alter table only idxpart add primary key (a); -- now it works alter table idxpart0 alter column a drop not null; -- fail, pkey needs it drop table idxpart; -- if a partition has a unique index without a constraint, does not attach -- automatically; creates a new index instead. create table idxpart (a int, b int) partition by range (a); create table idxpart1 (a int not null, b int); create unique index on idxpart1 (a); alter table idxpart add primary key (a); alter table idxpart attach partition idxpart1 for values from (1) to (1000); select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid, conname, conislocal, coninhcount, connoinherit, convalidated from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid) left join pg_constraint con on (idx.indexrelid = con.conindid) where indrelid::regclass::text like 'idxpart%' order by indexrelid::regclass::text collate "C"; drop table idxpart; -- Can't attach an index without a corresponding constraint create table idxpart (a int, b int) partition by range (a); create table idxpart1 (a int not null, b int); create unique index on idxpart1 (a); alter table idxpart attach partition idxpart1 for values from (1) to (1000); alter table only idxpart add primary key (a); alter index idxpart_pkey attach partition idxpart1_a_idx; -- fail drop table idxpart; -- Test that unique constraints are working create table idxpart (a int, b text, primary key (a, b)) partition by range (a); create table idxpart1 partition of idxpart for values from (0) to (100000); create table idxpart2 (c int, like idxpart); insert into idxpart2 (c, a, b) values (42, 572814, 'inserted first'); alter table idxpart2 drop column c; create unique index on idxpart (a); alter table idxpart attach partition idxpart2 for values from (100000) to (1000000); insert into idxpart values (0, 'zero'), (42, 'life'), (2^16, 'sixteen'); insert into idxpart select 2^g, format('two to power of %s', g) from generate_series(15, 17) g; insert into idxpart values (16, 'sixteen'); insert into idxpart (b, a) values ('one', 142857), ('two', 285714); insert into idxpart select a * 2, b || b from idxpart where a between 2^16 and 2^19; insert into idxpart values (572814, 'five'); insert into idxpart values (857142, 'six'); select tableoid::regclass, * from idxpart order by a; drop table idxpart; -- intentionally leave some objects around create table idxpart (a int) partition by range (a); create table idxpart1 partition of idxpart for values from (0) to (100); create table idxpart2 partition of idxpart for values from (100) to (1000) partition by range (a); create table idxpart21 partition of idxpart2 for values from (100) to (200); create table idxpart22 partition of idxpart2 for values from (200) to (300); create index on idxpart22 (a); create index on only idxpart2 (a); alter index idxpart2_a_idx attach partition idxpart22_a_idx; create index on idxpart (a); create table idxpart_another (a int, b int, primary key (a, b)) partition by range (a); create table idxpart_another_1 partition of idxpart_another for values from (0) to (100); create table idxpart3 (c int, b int, a int) partition by range (a); alter table idxpart3 drop column b, drop column c; create table idxpart31 partition of idxpart3 for values from (1000) to (1200); create table idxpart32 partition of idxpart3 for values from (1200) to (1400); alter table idxpart attach partition idxpart3 for values from (1000) to (2000); -- More objects intentionally left behind, to verify some pg_dump/pg_upgrade -- behavior; see https://postgr.es/m/20190321204928.GA17535@alvherre.pgsql create schema regress_indexing; set search_path to regress_indexing; create table pk (a int primary key) partition by range (a); create table pk1 partition of pk for values from (0) to (1000); create table pk2 (b int, a int); alter table pk2 drop column b; alter table pk2 alter a set not null; alter table pk attach partition pk2 for values from (1000) to (2000); create table pk3 partition of pk for values from (2000) to (3000); create table pk4 (like pk); alter table pk attach partition pk4 for values from (3000) to (4000); create table pk5 (like pk) partition by range (a); create table pk51 partition of pk5 for values from (4000) to (4500); create table pk52 partition of pk5 for values from (4500) to (5000); alter table pk attach partition pk5 for values from (4000) to (5000); reset search_path; -- Test that covering partitioned indexes work in various cases create table covidxpart (a int, b int) partition by list (a); create unique index on covidxpart (a) include (b); create table covidxpart1 partition of covidxpart for values in (1); create table covidxpart2 partition of covidxpart for values in (2); insert into covidxpart values (1, 1); insert into covidxpart values (1, 1); create table covidxpart3 (b int, c int, a int); alter table covidxpart3 drop c; alter table covidxpart attach partition covidxpart3 for values in (3); insert into covidxpart values (3, 1); insert into covidxpart values (3, 1); create table covidxpart4 (b int, a int); create unique index on covidxpart4 (a) include (b); create unique index on covidxpart4 (a); alter table covidxpart attach partition covidxpart4 for values in (4); insert into covidxpart values (4, 1); insert into covidxpart values (4, 1); create unique index on covidxpart (b) include (a); -- should fail -- check that detaching a partition also detaches the primary key constraint create table parted_pk_detach_test (a int primary key) partition by list (a); create table parted_pk_detach_test1 partition of parted_pk_detach_test for values in (1); alter table parted_pk_detach_test1 drop constraint parted_pk_detach_test1_pkey; -- should fail alter table parted_pk_detach_test detach partition parted_pk_detach_test1; alter table parted_pk_detach_test1 drop constraint parted_pk_detach_test1_pkey; drop table parted_pk_detach_test, parted_pk_detach_test1; create table parted_uniq_detach_test (a int unique) partition by list (a); create table parted_uniq_detach_test1 partition of parted_uniq_detach_test for values in (1); alter table parted_uniq_detach_test1 drop constraint parted_uniq_detach_test1_a_key; -- should fail alter table parted_uniq_detach_test detach partition parted_uniq_detach_test1; alter table parted_uniq_detach_test1 drop constraint parted_uniq_detach_test1_a_key; drop table parted_uniq_detach_test, parted_uniq_detach_test1; pgFormatter-4.2/t/pg-test-files/sql/indirect_toast.sql000066400000000000000000000053301361326045100231340ustar00rootroot00000000000000CREATE TABLE indtoasttest(descr text, cnt int DEFAULT 0, f1 text, f2 text); INSERT INTO indtoasttest(descr, f1, f2) VALUES('two-compressed', repeat('1234567890',1000), repeat('1234567890',1000)); INSERT INTO indtoasttest(descr, f1, f2) VALUES('two-toasted', repeat('1234567890',30000), repeat('1234567890',50000)); INSERT INTO indtoasttest(descr, f1, f2) VALUES('one-compressed,one-null', NULL, repeat('1234567890',1000)); INSERT INTO indtoasttest(descr, f1, f2) VALUES('one-toasted,one-null', NULL, repeat('1234567890',50000)); -- check whether indirect tuples works on the most basic level SELECT descr, substring(make_tuple_indirect(indtoasttest)::text, 1, 200) FROM indtoasttest; -- modification without changing varlenas UPDATE indtoasttest SET cnt = cnt +1 RETURNING substring(indtoasttest::text, 1, 200); -- modification without modifying assigned value UPDATE indtoasttest SET cnt = cnt +1, f1 = f1 RETURNING substring(indtoasttest::text, 1, 200); -- modification modifying, but effectively not changing UPDATE indtoasttest SET cnt = cnt +1, f1 = f1||'' RETURNING substring(indtoasttest::text, 1, 200); UPDATE indtoasttest SET cnt = cnt +1, f1 = '-'||f1||'-' RETURNING substring(indtoasttest::text, 1, 200); SELECT substring(indtoasttest::text, 1, 200) FROM indtoasttest; -- check we didn't screw with main/toast tuple visibility VACUUM FREEZE indtoasttest; SELECT substring(indtoasttest::text, 1, 200) FROM indtoasttest; -- now create a trigger that forces all Datums to be indirect ones CREATE FUNCTION update_using_indirect() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NEW := make_tuple_indirect(NEW); RETURN NEW; END$$; CREATE TRIGGER indtoasttest_update_indirect BEFORE INSERT OR UPDATE ON indtoasttest FOR EACH ROW EXECUTE PROCEDURE update_using_indirect(); -- modification without changing varlenas UPDATE indtoasttest SET cnt = cnt +1 RETURNING substring(indtoasttest::text, 1, 200); -- modification without modifying assigned value UPDATE indtoasttest SET cnt = cnt +1, f1 = f1 RETURNING substring(indtoasttest::text, 1, 200); -- modification modifying, but effectively not changing UPDATE indtoasttest SET cnt = cnt +1, f1 = f1||'' RETURNING substring(indtoasttest::text, 1, 200); UPDATE indtoasttest SET cnt = cnt +1, f1 = '-'||f1||'-' RETURNING substring(indtoasttest::text, 1, 200); INSERT INTO indtoasttest(descr, f1, f2) VALUES('one-toasted,one-null, via indirect', repeat('1234567890',30000), NULL); SELECT substring(indtoasttest::text, 1, 200) FROM indtoasttest; -- check we didn't screw with main/toast tuple visibility VACUUM FREEZE indtoasttest; SELECT substring(indtoasttest::text, 1, 200) FROM indtoasttest; DROP TABLE indtoasttest; DROP FUNCTION update_using_indirect(); pgFormatter-4.2/t/pg-test-files/sql/inet.sql000066400000000000000000000154141361326045100210640ustar00rootroot00000000000000-- -- INET -- -- prepare the table... DROP TABLE INET_TBL; CREATE TABLE INET_TBL (c cidr, i inet); INSERT INTO INET_TBL (c, i) VALUES ('192.168.1', '192.168.1.226/24'); INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.0/26', '192.168.1.226'); INSERT INTO INET_TBL (c, i) VALUES ('192.168.1', '192.168.1.0/24'); INSERT INTO INET_TBL (c, i) VALUES ('192.168.1', '192.168.1.0/25'); INSERT INTO INET_TBL (c, i) VALUES ('192.168.1', '192.168.1.255/24'); INSERT INTO INET_TBL (c, i) VALUES ('192.168.1', '192.168.1.255/25'); INSERT INTO INET_TBL (c, i) VALUES ('10', '10.1.2.3/8'); INSERT INTO INET_TBL (c, i) VALUES ('10.0.0.0', '10.1.2.3/8'); INSERT INTO INET_TBL (c, i) VALUES ('10.1.2.3', '10.1.2.3/32'); INSERT INTO INET_TBL (c, i) VALUES ('10.1.2', '10.1.2.3/24'); INSERT INTO INET_TBL (c, i) VALUES ('10.1', '10.1.2.3/16'); INSERT INTO INET_TBL (c, i) VALUES ('10', '10.1.2.3/8'); INSERT INTO INET_TBL (c, i) VALUES ('10', '11.1.2.3/8'); INSERT INTO INET_TBL (c, i) VALUES ('10', '9.1.2.3/8'); INSERT INTO INET_TBL (c, i) VALUES ('10:23::f1', '10:23::f1/64'); INSERT INTO INET_TBL (c, i) VALUES ('10:23::8000/113', '10:23::ffff'); INSERT INTO INET_TBL (c, i) VALUES ('::ffff:1.2.3.4', '::4.3.2.1/24'); -- check that CIDR rejects invalid input: INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.2/30', '192.168.1.226'); INSERT INTO INET_TBL (c, i) VALUES ('1234::1234::1234', '::1.2.3.4'); -- check that CIDR rejects invalid input when converting from text: INSERT INTO INET_TBL (c, i) VALUES (cidr('192.168.1.2/30'), '192.168.1.226'); INSERT INTO INET_TBL (c, i) VALUES (cidr('ffff:ffff:ffff:ffff::/24'), '::192.168.1.226'); SELECT '' AS ten, c AS cidr, i AS inet FROM INET_TBL; -- now test some support functions SELECT '' AS ten, i AS inet, host(i), text(i), family(i) FROM INET_TBL; SELECT '' AS ten, c AS cidr, broadcast(c), i AS inet, broadcast(i) FROM INET_TBL; SELECT '' AS ten, c AS cidr, network(c) AS "network(cidr)", i AS inet, network(i) AS "network(inet)" FROM INET_TBL; SELECT '' AS ten, c AS cidr, masklen(c) AS "masklen(cidr)", i AS inet, masklen(i) AS "masklen(inet)" FROM INET_TBL; SELECT '' AS four, c AS cidr, masklen(c) AS "masklen(cidr)", i AS inet, masklen(i) AS "masklen(inet)" FROM INET_TBL WHERE masklen(c) <= 8; SELECT '' AS six, c AS cidr, i AS inet FROM INET_TBL WHERE c = i; SELECT '' AS ten, i, c, i < c AS lt, i <= c AS le, i = c AS eq, i >= c AS ge, i > c AS gt, i <> c AS ne, i << c AS sb, i <<= c AS sbe, i >> c AS sup, i >>= c AS spe, i && c AS ovr FROM INET_TBL; SELECT max(i) AS max, min(i) AS min FROM INET_TBL; SELECT max(c) AS max, min(c) AS min FROM INET_TBL; -- check the conversion to/from text and set_netmask SELECT '' AS ten, set_masklen(inet(text(i)), 24) FROM INET_TBL; -- check that btree index works correctly CREATE INDEX inet_idx1 ON inet_tbl(i); SET enable_seqscan TO off; EXPLAIN (COSTS OFF) SELECT * FROM inet_tbl WHERE i<<'192.168.1.0/24'::cidr; SELECT * FROM inet_tbl WHERE i<<'192.168.1.0/24'::cidr; EXPLAIN (COSTS OFF) SELECT * FROM inet_tbl WHERE i<<='192.168.1.0/24'::cidr; SELECT * FROM inet_tbl WHERE i<<='192.168.1.0/24'::cidr; EXPLAIN (COSTS OFF) SELECT * FROM inet_tbl WHERE '192.168.1.0/24'::cidr >>= i; SELECT * FROM inet_tbl WHERE '192.168.1.0/24'::cidr >>= i; EXPLAIN (COSTS OFF) SELECT * FROM inet_tbl WHERE '192.168.1.0/24'::cidr >> i; SELECT * FROM inet_tbl WHERE '192.168.1.0/24'::cidr >> i; SET enable_seqscan TO on; DROP INDEX inet_idx1; -- check that gist index works correctly CREATE INDEX inet_idx2 ON inet_tbl using gist (i inet_ops); SET enable_seqscan TO off; SELECT * FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i <<= '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i && '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i >>= '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i >> '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i < '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i <= '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i = '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i >= '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i > '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i <> '192.168.1.0/24'::cidr ORDER BY i; -- test index-only scans EXPLAIN (COSTS OFF) SELECT i FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i; SELECT i FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i; SET enable_seqscan TO on; DROP INDEX inet_idx2; -- check that spgist index works correctly CREATE INDEX inet_idx3 ON inet_tbl using spgist (i); SET enable_seqscan TO off; SELECT * FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i <<= '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i && '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i >>= '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i >> '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i < '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i <= '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i = '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i >= '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i > '192.168.1.0/24'::cidr ORDER BY i; SELECT * FROM inet_tbl WHERE i <> '192.168.1.0/24'::cidr ORDER BY i; -- test index-only scans EXPLAIN (COSTS OFF) SELECT i FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i; SELECT i FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i; SET enable_seqscan TO on; DROP INDEX inet_idx3; -- simple tests of inet boolean and arithmetic operators SELECT i, ~i AS "~i" FROM inet_tbl; SELECT i, c, i & c AS "and" FROM inet_tbl; SELECT i, c, i | c AS "or" FROM inet_tbl; SELECT i, i + 500 AS "i+500" FROM inet_tbl; SELECT i, i - 500 AS "i-500" FROM inet_tbl; SELECT i, c, i - c AS "minus" FROM inet_tbl; SELECT '127.0.0.1'::inet + 257; SELECT ('127.0.0.1'::inet + 257) - 257; SELECT '127::1'::inet + 257; SELECT ('127::1'::inet + 257) - 257; SELECT '127.0.0.2'::inet - ('127.0.0.2'::inet + 500); SELECT '127.0.0.2'::inet - ('127.0.0.2'::inet - 500); SELECT '127::2'::inet - ('127::2'::inet + 500); SELECT '127::2'::inet - ('127::2'::inet - 500); -- these should give overflow errors: SELECT '127.0.0.1'::inet + 10000000000; SELECT '127.0.0.1'::inet - 10000000000; SELECT '126::1'::inet - '127::2'::inet; SELECT '127::1'::inet - '126::2'::inet; -- but not these SELECT '127::1'::inet + 10000000000; SELECT '127::1'::inet - '127::2'::inet; -- insert one more row with addressed from different families INSERT INTO INET_TBL (c, i) VALUES ('10', '10::/8'); -- now, this one should fail SELECT inet_merge(c, i) FROM INET_TBL; -- fix it by inet_same_family() condition SELECT inet_merge(c, i) FROM INET_TBL WHERE inet_same_family(c, i); pgFormatter-4.2/t/pg-test-files/sql/inherit.sql000066400000000000000000001051301361326045100215620ustar00rootroot00000000000000-- -- Test inheritance features -- CREATE TABLE a (aa TEXT); CREATE TABLE b (bb TEXT) INHERITS (a); CREATE TABLE c (cc TEXT) INHERITS (a); CREATE TABLE d (dd TEXT) INHERITS (b,c,a); INSERT INTO a(aa) VALUES('aaa'); INSERT INTO a(aa) VALUES('aaaa'); INSERT INTO a(aa) VALUES('aaaaa'); INSERT INTO a(aa) VALUES('aaaaaa'); INSERT INTO a(aa) VALUES('aaaaaaa'); INSERT INTO a(aa) VALUES('aaaaaaaa'); INSERT INTO b(aa) VALUES('bbb'); INSERT INTO b(aa) VALUES('bbbb'); INSERT INTO b(aa) VALUES('bbbbb'); INSERT INTO b(aa) VALUES('bbbbbb'); INSERT INTO b(aa) VALUES('bbbbbbb'); INSERT INTO b(aa) VALUES('bbbbbbbb'); INSERT INTO c(aa) VALUES('ccc'); INSERT INTO c(aa) VALUES('cccc'); INSERT INTO c(aa) VALUES('ccccc'); INSERT INTO c(aa) VALUES('cccccc'); INSERT INTO c(aa) VALUES('ccccccc'); INSERT INTO c(aa) VALUES('cccccccc'); INSERT INTO d(aa) VALUES('ddd'); INSERT INTO d(aa) VALUES('dddd'); INSERT INTO d(aa) VALUES('ddddd'); INSERT INTO d(aa) VALUES('dddddd'); INSERT INTO d(aa) VALUES('ddddddd'); INSERT INTO d(aa) VALUES('dddddddd'); SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid; SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid; SELECT relname, c.* FROM c, pg_class where c.tableoid = pg_class.oid; SELECT relname, d.* FROM d, pg_class where d.tableoid = pg_class.oid; SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid; SELECT relname, b.* FROM ONLY b, pg_class where b.tableoid = pg_class.oid; SELECT relname, c.* FROM ONLY c, pg_class where c.tableoid = pg_class.oid; SELECT relname, d.* FROM ONLY d, pg_class where d.tableoid = pg_class.oid; UPDATE a SET aa='zzzz' WHERE aa='aaaa'; UPDATE ONLY a SET aa='zzzzz' WHERE aa='aaaaa'; UPDATE b SET aa='zzz' WHERE aa='aaa'; UPDATE ONLY b SET aa='zzz' WHERE aa='aaa'; UPDATE a SET aa='zzzzzz' WHERE aa LIKE 'aaa%'; SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid; SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid; SELECT relname, c.* FROM c, pg_class where c.tableoid = pg_class.oid; SELECT relname, d.* FROM d, pg_class where d.tableoid = pg_class.oid; SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid; SELECT relname, b.* FROM ONLY b, pg_class where b.tableoid = pg_class.oid; SELECT relname, c.* FROM ONLY c, pg_class where c.tableoid = pg_class.oid; SELECT relname, d.* FROM ONLY d, pg_class where d.tableoid = pg_class.oid; UPDATE b SET aa='new'; SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid; SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid; SELECT relname, c.* FROM c, pg_class where c.tableoid = pg_class.oid; SELECT relname, d.* FROM d, pg_class where d.tableoid = pg_class.oid; SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid; SELECT relname, b.* FROM ONLY b, pg_class where b.tableoid = pg_class.oid; SELECT relname, c.* FROM ONLY c, pg_class where c.tableoid = pg_class.oid; SELECT relname, d.* FROM ONLY d, pg_class where d.tableoid = pg_class.oid; UPDATE a SET aa='new'; DELETE FROM ONLY c WHERE aa='new'; SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid; SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid; SELECT relname, c.* FROM c, pg_class where c.tableoid = pg_class.oid; SELECT relname, d.* FROM d, pg_class where d.tableoid = pg_class.oid; SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid; SELECT relname, b.* FROM ONLY b, pg_class where b.tableoid = pg_class.oid; SELECT relname, c.* FROM ONLY c, pg_class where c.tableoid = pg_class.oid; SELECT relname, d.* FROM ONLY d, pg_class where d.tableoid = pg_class.oid; DELETE FROM a; SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid; SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid; SELECT relname, c.* FROM c, pg_class where c.tableoid = pg_class.oid; SELECT relname, d.* FROM d, pg_class where d.tableoid = pg_class.oid; SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid; SELECT relname, b.* FROM ONLY b, pg_class where b.tableoid = pg_class.oid; SELECT relname, c.* FROM ONLY c, pg_class where c.tableoid = pg_class.oid; SELECT relname, d.* FROM ONLY d, pg_class where d.tableoid = pg_class.oid; -- Confirm PRIMARY KEY adds NOT NULL constraint to child table CREATE TEMP TABLE z (b TEXT, PRIMARY KEY(aa, b)) inherits (a); INSERT INTO z VALUES (NULL, 'text'); -- should fail -- Check inherited UPDATE with all children excluded create table some_tab (a int, b int); create table some_tab_child () inherits (some_tab); insert into some_tab_child values(1,2); explain (verbose, costs off) update some_tab set a = a + 1 where false; update some_tab set a = a + 1 where false; explain (verbose, costs off) update some_tab set a = a + 1 where false returning b, a; update some_tab set a = a + 1 where false returning b, a; table some_tab; drop table some_tab cascade; -- Check UPDATE with inherited target and an inherited source table create temp table foo(f1 int, f2 int); create temp table foo2(f3 int) inherits (foo); create temp table bar(f1 int, f2 int); create temp table bar2(f3 int) inherits (bar); insert into foo values(1,1); insert into foo values(3,3); insert into foo2 values(2,2,2); insert into foo2 values(3,3,3); insert into bar values(1,1); insert into bar values(2,2); insert into bar values(3,3); insert into bar values(4,4); insert into bar2 values(1,1,1); insert into bar2 values(2,2,2); insert into bar2 values(3,3,3); insert into bar2 values(4,4,4); update bar set f2 = f2 + 100 where f1 in (select f1 from foo); select tableoid::regclass::text as relname, bar.* from bar order by 1,2; -- Check UPDATE with inherited target and an appendrel subquery update bar set f2 = f2 + 100 from ( select f1 from foo union all select f1+3 from foo ) ss where bar.f1 = ss.f1; select tableoid::regclass::text as relname, bar.* from bar order by 1,2; -- Check UPDATE with *partitioned* inherited target and an appendrel subquery create table some_tab (a int); insert into some_tab values (0); create table some_tab_child () inherits (some_tab); insert into some_tab_child values (1); create table parted_tab (a int, b char) partition by list (a); create table parted_tab_part1 partition of parted_tab for values in (1); create table parted_tab_part2 partition of parted_tab for values in (2); create table parted_tab_part3 partition of parted_tab for values in (3); insert into parted_tab values (1, 'a'), (2, 'a'), (3, 'a'); update parted_tab set b = 'b' from (select a from some_tab union all select a+1 from some_tab) ss (a) where parted_tab.a = ss.a; select tableoid::regclass::text as relname, parted_tab.* from parted_tab order by 1,2; truncate parted_tab; insert into parted_tab values (1, 'a'), (2, 'a'), (3, 'a'); update parted_tab set b = 'b' from (select 0 from parted_tab union all select 1 from parted_tab) ss (a) where parted_tab.a = ss.a; select tableoid::regclass::text as relname, parted_tab.* from parted_tab order by 1,2; -- modifies partition key, but no rows will actually be updated explain update parted_tab set a = 2 where false; drop table parted_tab; -- Check UPDATE with multi-level partitioned inherited target create table mlparted_tab (a int, b char, c text) partition by list (a); create table mlparted_tab_part1 partition of mlparted_tab for values in (1); create table mlparted_tab_part2 partition of mlparted_tab for values in (2) partition by list (b); create table mlparted_tab_part3 partition of mlparted_tab for values in (3); create table mlparted_tab_part2a partition of mlparted_tab_part2 for values in ('a'); create table mlparted_tab_part2b partition of mlparted_tab_part2 for values in ('b'); insert into mlparted_tab values (1, 'a'), (2, 'a'), (2, 'b'), (3, 'a'); update mlparted_tab mlp set c = 'xxx' from (select a from some_tab union all select a+1 from some_tab) ss (a) where (mlp.a = ss.a and mlp.b = 'b') or mlp.a = 3; select tableoid::regclass::text as relname, mlparted_tab.* from mlparted_tab order by 1,2; drop table mlparted_tab; drop table some_tab cascade; /* Test multiple inheritance of column defaults */ CREATE TABLE firstparent (tomorrow date default now()::date + 1); CREATE TABLE secondparent (tomorrow date default now() :: date + 1); CREATE TABLE jointchild () INHERITS (firstparent, secondparent); -- ok CREATE TABLE thirdparent (tomorrow date default now()::date - 1); CREATE TABLE otherchild () INHERITS (firstparent, thirdparent); -- not ok CREATE TABLE otherchild (tomorrow date default now()) INHERITS (firstparent, thirdparent); -- ok, child resolves ambiguous default DROP TABLE firstparent, secondparent, jointchild, thirdparent, otherchild; -- Test changing the type of inherited columns insert into d values('test','one','two','three'); alter table a alter column aa type integer using bit_length(aa); select * from d; -- Test non-inheritable parent constraints create table p1(ff1 int); alter table p1 add constraint p1chk check (ff1 > 0) no inherit; alter table p1 add constraint p2chk check (ff1 > 10); -- connoinherit should be true for NO INHERIT constraint select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.connoinherit from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname = 'p1' order by 1,2; -- Test that child does not inherit NO INHERIT constraints create table c1 () inherits (p1); \d p1 \d c1 -- Test that child does not override inheritable constraints of the parent create table c2 (constraint p2chk check (ff1 > 10) no inherit) inherits (p1); --fails drop table p1 cascade; -- Tests for casting between the rowtypes of parent and child -- tables. See the pgsql-hackers thread beginning Dec. 4/04 create table base (i integer); create table derived () inherits (base); create table more_derived (like derived, b int) inherits (derived); insert into derived (i) values (0); select derived::base from derived; select NULL::derived::base; -- remove redundant conversions. explain (verbose on, costs off) select row(i, b)::more_derived::derived::base from more_derived; explain (verbose on, costs off) select (1, 2)::more_derived::derived::base; drop table more_derived; drop table derived; drop table base; create table p1(ff1 int); create table p2(f1 text); create function p2text(p2) returns text as 'select $1.f1' language sql; create table c1(f3 int) inherits(p1,p2); insert into c1 values(123456789, 'hi', 42); select p2text(c1.*) from c1; drop function p2text(p2); drop table c1; drop table p2; drop table p1; CREATE TABLE ac (aa TEXT); alter table ac add constraint ac_check check (aa is not null); CREATE TABLE bc (bb TEXT) INHERITS (ac); select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) as consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2; insert into ac (aa) values (NULL); insert into bc (aa) values (NULL); alter table bc drop constraint ac_check; -- fail, disallowed alter table ac drop constraint ac_check; select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) as consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2; -- try the unnamed-constraint case alter table ac add check (aa is not null); select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) as consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2; insert into ac (aa) values (NULL); insert into bc (aa) values (NULL); alter table bc drop constraint ac_aa_check; -- fail, disallowed alter table ac drop constraint ac_aa_check; select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) as consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2; alter table ac add constraint ac_check check (aa is not null); alter table bc no inherit ac; select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) as consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2; alter table bc drop constraint ac_check; select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) as consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2; alter table ac drop constraint ac_check; select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) as consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2; drop table bc; drop table ac; create table ac (a int constraint check_a check (a <> 0)); create table bc (a int constraint check_a check (a <> 0), b int constraint check_b check (b <> 0)) inherits (ac); select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) as consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2; drop table bc; drop table ac; create table ac (a int constraint check_a check (a <> 0)); create table bc (b int constraint check_b check (b <> 0)); create table cc (c int constraint check_c check (c <> 0)) inherits (ac, bc); select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) as consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc', 'cc') order by 1,2; alter table cc no inherit bc; select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) as consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc', 'cc') order by 1,2; drop table cc; drop table bc; drop table ac; create table p1(f1 int); create table p2(f2 int); create table c1(f3 int) inherits(p1,p2); insert into c1 values(1,-1,2); alter table p2 add constraint cc check (f2>0); -- fail alter table p2 add check (f2>0); -- check it without a name, too delete from c1; insert into c1 values(1,1,2); alter table p2 add check (f2>0); insert into c1 values(1,-1,2); -- fail create table c2(f3 int) inherits(p1,p2); \d c2 create table c3 (f4 int) inherits(c1,c2); \d c3 drop table p1 cascade; drop table p2 cascade; create table pp1 (f1 int); create table cc1 (f2 text, f3 int) inherits (pp1); alter table pp1 add column a1 int check (a1 > 0); \d cc1 create table cc2(f4 float) inherits(pp1,cc1); \d cc2 alter table pp1 add column a2 int check (a2 > 0); \d cc2 drop table pp1 cascade; -- Test for renaming in simple multiple inheritance CREATE TABLE inht1 (a int, b int); CREATE TABLE inhs1 (b int, c int); CREATE TABLE inhts (d int) INHERITS (inht1, inhs1); ALTER TABLE inht1 RENAME a TO aa; ALTER TABLE inht1 RENAME b TO bb; -- to be failed ALTER TABLE inhts RENAME aa TO aaa; -- to be failed ALTER TABLE inhts RENAME d TO dd; \d+ inhts DROP TABLE inhts; -- Test for renaming in diamond inheritance CREATE TABLE inht2 (x int) INHERITS (inht1); CREATE TABLE inht3 (y int) INHERITS (inht1); CREATE TABLE inht4 (z int) INHERITS (inht2, inht3); ALTER TABLE inht1 RENAME aa TO aaa; \d+ inht4 CREATE TABLE inhts (d int) INHERITS (inht2, inhs1); ALTER TABLE inht1 RENAME aaa TO aaaa; ALTER TABLE inht1 RENAME b TO bb; -- to be failed \d+ inhts WITH RECURSIVE r AS ( SELECT 'inht1'::regclass AS inhrelid UNION ALL SELECT c.inhrelid FROM pg_inherits c, r WHERE r.inhrelid = c.inhparent ) SELECT a.attrelid::regclass, a.attname, a.attinhcount, e.expected FROM (SELECT inhrelid, count(*) AS expected FROM pg_inherits WHERE inhparent IN (SELECT inhrelid FROM r) GROUP BY inhrelid) e JOIN pg_attribute a ON e.inhrelid = a.attrelid WHERE NOT attislocal ORDER BY a.attrelid::regclass::name, a.attnum; DROP TABLE inht1, inhs1 CASCADE; -- Test non-inheritable indices [UNIQUE, EXCLUDE] constraints CREATE TABLE test_constraints (id int, val1 varchar, val2 int, UNIQUE(val1, val2)); CREATE TABLE test_constraints_inh () INHERITS (test_constraints); \d+ test_constraints ALTER TABLE ONLY test_constraints DROP CONSTRAINT test_constraints_val1_val2_key; \d+ test_constraints \d+ test_constraints_inh DROP TABLE test_constraints_inh; DROP TABLE test_constraints; CREATE TABLE test_ex_constraints ( c circle, EXCLUDE USING gist (c WITH &&) ); CREATE TABLE test_ex_constraints_inh () INHERITS (test_ex_constraints); \d+ test_ex_constraints ALTER TABLE test_ex_constraints DROP CONSTRAINT test_ex_constraints_c_excl; \d+ test_ex_constraints \d+ test_ex_constraints_inh DROP TABLE test_ex_constraints_inh; DROP TABLE test_ex_constraints; -- Test non-inheritable foreign key constraints CREATE TABLE test_primary_constraints(id int PRIMARY KEY); CREATE TABLE test_foreign_constraints(id1 int REFERENCES test_primary_constraints(id)); CREATE TABLE test_foreign_constraints_inh () INHERITS (test_foreign_constraints); \d+ test_primary_constraints \d+ test_foreign_constraints ALTER TABLE test_foreign_constraints DROP CONSTRAINT test_foreign_constraints_id1_fkey; \d+ test_foreign_constraints \d+ test_foreign_constraints_inh DROP TABLE test_foreign_constraints_inh; DROP TABLE test_foreign_constraints; DROP TABLE test_primary_constraints; -- Test foreign key behavior create table inh_fk_1 (a int primary key); insert into inh_fk_1 values (1), (2), (3); create table inh_fk_2 (x int primary key, y int references inh_fk_1 on delete cascade); insert into inh_fk_2 values (11, 1), (22, 2), (33, 3); create table inh_fk_2_child () inherits (inh_fk_2); insert into inh_fk_2_child values (111, 1), (222, 2); delete from inh_fk_1 where a = 1; select * from inh_fk_1 order by 1; select * from inh_fk_2 order by 1, 2; drop table inh_fk_1, inh_fk_2, inh_fk_2_child; -- Test that parent and child CHECK constraints can be created in either order create table p1(f1 int); create table p1_c1() inherits(p1); alter table p1 add constraint inh_check_constraint1 check (f1 > 0); alter table p1_c1 add constraint inh_check_constraint1 check (f1 > 0); alter table p1_c1 add constraint inh_check_constraint2 check (f1 < 10); alter table p1 add constraint inh_check_constraint2 check (f1 < 10); select conrelid::regclass::text as relname, conname, conislocal, coninhcount from pg_constraint where conname like 'inh\_check\_constraint%' order by 1, 2; drop table p1 cascade; -- Test that a valid child can have not-valid parent, but not vice versa create table invalid_check_con(f1 int); create table invalid_check_con_child() inherits(invalid_check_con); alter table invalid_check_con_child add constraint inh_check_constraint check(f1 > 0) not valid; alter table invalid_check_con add constraint inh_check_constraint check(f1 > 0); -- fail alter table invalid_check_con_child drop constraint inh_check_constraint; insert into invalid_check_con values(0); alter table invalid_check_con_child add constraint inh_check_constraint check(f1 > 0); alter table invalid_check_con add constraint inh_check_constraint check(f1 > 0) not valid; insert into invalid_check_con values(0); -- fail insert into invalid_check_con_child values(0); -- fail select conrelid::regclass::text as relname, conname, convalidated, conislocal, coninhcount, connoinherit from pg_constraint where conname like 'inh\_check\_constraint%' order by 1, 2; -- We don't drop the invalid_check_con* tables, to test dump/reload with -- -- Test parameterized append plans for inheritance trees -- create temp table patest0 (id, x) as select x, x from generate_series(0,1000) x; create temp table patest1() inherits (patest0); insert into patest1 select x, x from generate_series(0,1000) x; create temp table patest2() inherits (patest0); insert into patest2 select x, x from generate_series(0,1000) x; create index patest0i on patest0(id); create index patest1i on patest1(id); create index patest2i on patest2(id); analyze patest0; analyze patest1; analyze patest2; explain (costs off) select * from patest0 join (select f1 from int4_tbl limit 1) ss on id = f1; select * from patest0 join (select f1 from int4_tbl limit 1) ss on id = f1; drop index patest2i; explain (costs off) select * from patest0 join (select f1 from int4_tbl limit 1) ss on id = f1; select * from patest0 join (select f1 from int4_tbl limit 1) ss on id = f1; drop table patest0 cascade; -- -- Test merge-append plans for inheritance trees -- create table matest0 (id serial primary key, name text); create table matest1 (id integer primary key) inherits (matest0); create table matest2 (id integer primary key) inherits (matest0); create table matest3 (id integer primary key) inherits (matest0); create index matest0i on matest0 ((1-id)); create index matest1i on matest1 ((1-id)); -- create index matest2i on matest2 ((1-id)); -- intentionally missing create index matest3i on matest3 ((1-id)); insert into matest1 (name) values ('Test 1'); insert into matest1 (name) values ('Test 2'); insert into matest2 (name) values ('Test 3'); insert into matest2 (name) values ('Test 4'); insert into matest3 (name) values ('Test 5'); insert into matest3 (name) values ('Test 6'); set enable_indexscan = off; -- force use of seqscan/sort, so no merge explain (verbose, costs off) select * from matest0 order by 1-id; select * from matest0 order by 1-id; explain (verbose, costs off) select min(1-id) from matest0; select min(1-id) from matest0; reset enable_indexscan; set enable_seqscan = off; -- plan with fewest seqscans should be merge set enable_parallel_append = off; -- Don't let parallel-append interfere explain (verbose, costs off) select * from matest0 order by 1-id; select * from matest0 order by 1-id; explain (verbose, costs off) select min(1-id) from matest0; select min(1-id) from matest0; reset enable_seqscan; reset enable_parallel_append; drop table matest0 cascade; -- -- Check that use of an index with an extraneous column doesn't produce -- a plan with extraneous sorting -- create table matest0 (a int, b int, c int, d int); create table matest1 () inherits(matest0); create index matest0i on matest0 (b, c); create index matest1i on matest1 (b, c); set enable_nestloop = off; -- we want a plan with two MergeAppends explain (costs off) select t1.* from matest0 t1, matest0 t2 where t1.b = t2.b and t2.c = t2.d order by t1.b limit 10; reset enable_nestloop; drop table matest0 cascade; -- -- Test merge-append for UNION ALL append relations -- set enable_seqscan = off; set enable_indexscan = on; set enable_bitmapscan = off; -- Check handling of duplicated, constant, or volatile targetlist items explain (costs off) SELECT thousand, tenthous FROM tenk1 UNION ALL SELECT thousand, thousand FROM tenk1 ORDER BY thousand, tenthous; explain (costs off) SELECT thousand, tenthous, thousand+tenthous AS x FROM tenk1 UNION ALL SELECT 42, 42, hundred FROM tenk1 ORDER BY thousand, tenthous; explain (costs off) SELECT thousand, tenthous FROM tenk1 UNION ALL SELECT thousand, random()::integer FROM tenk1 ORDER BY thousand, tenthous; -- Check min/max aggregate optimization explain (costs off) SELECT min(x) FROM (SELECT unique1 AS x FROM tenk1 a UNION ALL SELECT unique2 AS x FROM tenk1 b) s; explain (costs off) SELECT min(y) FROM (SELECT unique1 AS x, unique1 AS y FROM tenk1 a UNION ALL SELECT unique2 AS x, unique2 AS y FROM tenk1 b) s; -- XXX planner doesn't recognize that index on unique2 is sufficiently sorted explain (costs off) SELECT x, y FROM (SELECT thousand AS x, tenthous AS y FROM tenk1 a UNION ALL SELECT unique2 AS x, unique2 AS y FROM tenk1 b) s ORDER BY x, y; -- exercise rescan code path via a repeatedly-evaluated subquery explain (costs off) SELECT ARRAY(SELECT f.i FROM ( (SELECT d + g.i FROM generate_series(4, 30, 3) d ORDER BY 1) UNION ALL (SELECT d + g.i FROM generate_series(0, 30, 5) d ORDER BY 1) ) f(i) ORDER BY f.i LIMIT 10) FROM generate_series(1, 3) g(i); SELECT ARRAY(SELECT f.i FROM ( (SELECT d + g.i FROM generate_series(4, 30, 3) d ORDER BY 1) UNION ALL (SELECT d + g.i FROM generate_series(0, 30, 5) d ORDER BY 1) ) f(i) ORDER BY f.i LIMIT 10) FROM generate_series(1, 3) g(i); reset enable_seqscan; reset enable_indexscan; reset enable_bitmapscan; -- -- Check handling of a constant-null CHECK constraint -- create table cnullparent (f1 int); create table cnullchild (check (f1 = 1 or f1 = null)) inherits(cnullparent); insert into cnullchild values(1); insert into cnullchild values(2); insert into cnullchild values(null); select * from cnullparent; select * from cnullparent where f1 = 2; drop table cnullparent cascade; -- -- Check use of temporary tables with inheritance trees -- create table inh_perm_parent (a1 int); create temp table inh_temp_parent (a1 int); create temp table inh_temp_child () inherits (inh_perm_parent); -- ok create table inh_perm_child () inherits (inh_temp_parent); -- error create temp table inh_temp_child_2 () inherits (inh_temp_parent); -- ok insert into inh_perm_parent values (1); insert into inh_temp_parent values (2); insert into inh_temp_child values (3); insert into inh_temp_child_2 values (4); select tableoid::regclass, a1 from inh_perm_parent; select tableoid::regclass, a1 from inh_temp_parent; drop table inh_perm_parent cascade; drop table inh_temp_parent cascade; -- -- Check that constraint exclusion works correctly with partitions using -- implicit constraints generated from the partition bound information. -- create table list_parted ( a varchar ) partition by list (a); create table part_ab_cd partition of list_parted for values in ('ab', 'cd'); create table part_ef_gh partition of list_parted for values in ('ef', 'gh'); create table part_null_xy partition of list_parted for values in (null, 'xy'); explain (costs off) select * from list_parted; explain (costs off) select * from list_parted where a is null; explain (costs off) select * from list_parted where a is not null; explain (costs off) select * from list_parted where a in ('ab', 'cd', 'ef'); explain (costs off) select * from list_parted where a = 'ab' or a in (null, 'cd'); explain (costs off) select * from list_parted where a = 'ab'; create table range_list_parted ( a int, b char(2) ) partition by range (a); create table part_1_10 partition of range_list_parted for values from (1) to (10) partition by list (b); create table part_1_10_ab partition of part_1_10 for values in ('ab'); create table part_1_10_cd partition of part_1_10 for values in ('cd'); create table part_10_20 partition of range_list_parted for values from (10) to (20) partition by list (b); create table part_10_20_ab partition of part_10_20 for values in ('ab'); create table part_10_20_cd partition of part_10_20 for values in ('cd'); create table part_21_30 partition of range_list_parted for values from (21) to (30) partition by list (b); create table part_21_30_ab partition of part_21_30 for values in ('ab'); create table part_21_30_cd partition of part_21_30 for values in ('cd'); create table part_40_inf partition of range_list_parted for values from (40) to (maxvalue) partition by list (b); create table part_40_inf_ab partition of part_40_inf for values in ('ab'); create table part_40_inf_cd partition of part_40_inf for values in ('cd'); create table part_40_inf_null partition of part_40_inf for values in (null); explain (costs off) select * from range_list_parted; explain (costs off) select * from range_list_parted where a = 5; explain (costs off) select * from range_list_parted where b = 'ab'; explain (costs off) select * from range_list_parted where a between 3 and 23 and b in ('ab'); /* Should select no rows because range partition key cannot be null */ explain (costs off) select * from range_list_parted where a is null; /* Should only select rows from the null-accepting partition */ explain (costs off) select * from range_list_parted where b is null; explain (costs off) select * from range_list_parted where a is not null and a < 67; explain (costs off) select * from range_list_parted where a >= 30; drop table list_parted; drop table range_list_parted; -- check that constraint exclusion is able to cope with the partition -- constraint emitted for multi-column range partitioned tables create table mcrparted (a int, b int, c int) partition by range (a, abs(b), c); create table mcrparted_def partition of mcrparted default; create table mcrparted0 partition of mcrparted for values from (minvalue, minvalue, minvalue) to (1, 1, 1); create table mcrparted1 partition of mcrparted for values from (1, 1, 1) to (10, 5, 10); create table mcrparted2 partition of mcrparted for values from (10, 5, 10) to (10, 10, 10); create table mcrparted3 partition of mcrparted for values from (11, 1, 1) to (20, 10, 10); create table mcrparted4 partition of mcrparted for values from (20, 10, 10) to (20, 20, 20); create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to (maxvalue, maxvalue, maxvalue); explain (costs off) select * from mcrparted where a = 0; -- scans mcrparted0, mcrparted_def explain (costs off) select * from mcrparted where a = 10 and abs(b) < 5; -- scans mcrparted1, mcrparted_def explain (costs off) select * from mcrparted where a = 10 and abs(b) = 5; -- scans mcrparted1, mcrparted2, mcrparted_def explain (costs off) select * from mcrparted where abs(b) = 5; -- scans all partitions explain (costs off) select * from mcrparted where a > -1; -- scans all partitions explain (costs off) select * from mcrparted where a = 20 and abs(b) = 10 and c > 10; -- scans mcrparted4 explain (costs off) select * from mcrparted where a = 20 and c > 20; -- scans mcrparted3, mcrparte4, mcrparte5, mcrparted_def -- check that partitioned table Appends cope with being referenced in -- subplans create table parted_minmax (a int, b varchar(16)) partition by range (a); create table parted_minmax1 partition of parted_minmax for values from (1) to (10); create index parted_minmax1i on parted_minmax1 (a, b); insert into parted_minmax values (1,'12345'); explain (costs off) select min(a), max(a) from parted_minmax where b = '12345'; select min(a), max(a) from parted_minmax where b = '12345'; drop table parted_minmax; -- Test code that uses Append nodes in place of MergeAppend when the -- partition ordering matches the desired ordering. create index mcrparted_a_abs_c_idx on mcrparted (a, abs(b), c); -- MergeAppend must be used when a default partition exists explain (costs off) select * from mcrparted order by a, abs(b), c; drop table mcrparted_def; -- Append is used for a RANGE partitioned table with no default -- and no subpartitions explain (costs off) select * from mcrparted order by a, abs(b), c; -- Append is used with subpaths in reverse order with backwards index scans explain (costs off) select * from mcrparted order by a desc, abs(b) desc, c desc; -- check that Append plan is used containing a MergeAppend for sub-partitions -- that are unordered. drop table mcrparted5; create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to (maxvalue, maxvalue, maxvalue) partition by list (a); create table mcrparted5a partition of mcrparted5 for values in(20); create table mcrparted5_def partition of mcrparted5 default; explain (costs off) select * from mcrparted order by a, abs(b), c; drop table mcrparted5_def; -- check that an Append plan is used and the sub-partitions are flattened -- into the main Append when the sub-partition is unordered but contains -- just a single sub-partition. explain (costs off) select a, abs(b) from mcrparted order by a, abs(b), c; -- check that Append is used when the sub-partitioned tables are pruned -- during planning. explain (costs off) select * from mcrparted where a < 20 order by a, abs(b), c; create table mclparted (a int) partition by list(a); create table mclparted1 partition of mclparted for values in(1); create table mclparted2 partition of mclparted for values in(2); create index on mclparted (a); -- Ensure an Append is used for a list partition with an order by. explain (costs off) select * from mclparted order by a; -- Ensure a MergeAppend is used when a partition exists with interleaved -- datums in the partition bound. create table mclparted3_5 partition of mclparted for values in(3,5); create table mclparted4 partition of mclparted for values in(4); explain (costs off) select * from mclparted order by a; drop table mclparted; -- Ensure subplans which don't have a path with the correct pathkeys get -- sorted correctly. drop index mcrparted_a_abs_c_idx; create index on mcrparted1 (a, abs(b), c); create index on mcrparted2 (a, abs(b), c); create index on mcrparted3 (a, abs(b), c); create index on mcrparted4 (a, abs(b), c); explain (costs off) select * from mcrparted where a < 20 order by a, abs(b), c limit 1; set enable_bitmapscan = 0; -- Ensure Append node can be used when the partition is ordered by some -- pathkeys which were deemed redundant. explain (costs off) select * from mcrparted where a = 10 order by a, abs(b), c; reset enable_bitmapscan; drop table mcrparted; -- Ensure LIST partitions allow an Append to be used instead of a MergeAppend create table bool_lp (b bool) partition by list(b); create table bool_lp_true partition of bool_lp for values in(true); create table bool_lp_false partition of bool_lp for values in(false); create index on bool_lp (b); explain (costs off) select * from bool_lp order by b; drop table bool_lp; -- Ensure const bool quals can be properly detected as redundant create table bool_rp (b bool, a int) partition by range(b,a); create table bool_rp_false_1k partition of bool_rp for values from (false,0) to (false,1000); create table bool_rp_true_1k partition of bool_rp for values from (true,0) to (true,1000); create table bool_rp_false_2k partition of bool_rp for values from (false,1000) to (false,2000); create table bool_rp_true_2k partition of bool_rp for values from (true,1000) to (true,2000); create index on bool_rp (b,a); explain (costs off) select * from bool_rp where b = true order by b,a; explain (costs off) select * from bool_rp where b = false order by b,a; explain (costs off) select * from bool_rp where b = true order by a; explain (costs off) select * from bool_rp where b = false order by a; drop table bool_rp; -- Ensure an Append scan is chosen when the partition order is a subset of -- the required order. create table range_parted (a int, b int, c int) partition by range(a, b); create table range_parted1 partition of range_parted for values from (0,0) to (10,10); create table range_parted2 partition of range_parted for values from (10,10) to (20,20); create index on range_parted (a,b,c); explain (costs off) select * from range_parted order by a,b,c; explain (costs off) select * from range_parted order by a desc,b desc,c desc; drop table range_parted; pgFormatter-4.2/t/pg-test-files/sql/init_privs.sql000066400000000000000000000005541361326045100223120ustar00rootroot00000000000000-- Test initial privileges -- There should always be some initial privileges, set up by initdb SELECT count(*) > 0 FROM pg_init_privs; -- Intentionally include some non-initial privs for pg_dump to dump out GRANT SELECT ON pg_proc TO CURRENT_USER; GRANT SELECT (prosrc) ON pg_proc TO CURRENT_USER; GRANT SELECT (rolname, rolsuper) ON pg_authid TO CURRENT_USER; pgFormatter-4.2/t/pg-test-files/sql/insert.sql000066400000000000000000000612041361326045100214270ustar00rootroot00000000000000-- -- insert with DEFAULT in the target_list -- create table inserttest (col1 int4, col2 int4 NOT NULL, col3 text default 'testing'); insert into inserttest (col1, col2, col3) values (DEFAULT, DEFAULT, DEFAULT); insert into inserttest (col2, col3) values (3, DEFAULT); insert into inserttest (col1, col2, col3) values (DEFAULT, 5, DEFAULT); insert into inserttest values (DEFAULT, 5, 'test'); insert into inserttest values (DEFAULT, 7); select * from inserttest; -- -- insert with similar expression / target_list values (all fail) -- insert into inserttest (col1, col2, col3) values (DEFAULT, DEFAULT); insert into inserttest (col1, col2, col3) values (1, 2); insert into inserttest (col1) values (1, 2); insert into inserttest (col1) values (DEFAULT, DEFAULT); select * from inserttest; -- -- VALUES test -- insert into inserttest values(10, 20, '40'), (-1, 2, DEFAULT), ((select 2), (select i from (values(3)) as foo (i)), 'values are fun!'); select * from inserttest; -- -- TOASTed value test -- insert into inserttest values(30, 50, repeat('x', 10000)); select col1, col2, char_length(col3) from inserttest; drop table inserttest; -- -- check indirection (field/array assignment), cf bug #14265 -- -- these tests are aware that transformInsertStmt has 3 separate code paths -- create type insert_test_type as (if1 int, if2 text[]); create table inserttest (f1 int, f2 int[], f3 insert_test_type, f4 insert_test_type[]); insert into inserttest (f2[1], f2[2]) values (1,2); insert into inserttest (f2[1], f2[2]) values (3,4), (5,6); insert into inserttest (f2[1], f2[2]) select 7,8; insert into inserttest (f2[1], f2[2]) values (1,default); -- not supported insert into inserttest (f3.if1, f3.if2) values (1,array['foo']); insert into inserttest (f3.if1, f3.if2) values (1,'{foo}'), (2,'{bar}'); insert into inserttest (f3.if1, f3.if2) select 3, '{baz,quux}'; insert into inserttest (f3.if1, f3.if2) values (1,default); -- not supported insert into inserttest (f3.if2[1], f3.if2[2]) values ('foo', 'bar'); insert into inserttest (f3.if2[1], f3.if2[2]) values ('foo', 'bar'), ('baz', 'quux'); insert into inserttest (f3.if2[1], f3.if2[2]) select 'bear', 'beer'; insert into inserttest (f4[1].if2[1], f4[1].if2[2]) values ('foo', 'bar'); insert into inserttest (f4[1].if2[1], f4[1].if2[2]) values ('foo', 'bar'), ('baz', 'quux'); insert into inserttest (f4[1].if2[1], f4[1].if2[2]) select 'bear', 'beer'; select * from inserttest; -- also check reverse-listing create table inserttest2 (f1 bigint, f2 text); create rule irule1 as on insert to inserttest2 do also insert into inserttest (f3.if2[1], f3.if2[2]) values (new.f1,new.f2); create rule irule2 as on insert to inserttest2 do also insert into inserttest (f4[1].if1, f4[1].if2[2]) values (1,'fool'),(new.f1,new.f2); create rule irule3 as on insert to inserttest2 do also insert into inserttest (f4[1].if1, f4[1].if2[2]) select new.f1, new.f2; \d+ inserttest2 drop table inserttest2; drop table inserttest; drop type insert_test_type; -- direct partition inserts should check partition bound constraint create table range_parted ( a text, b int ) partition by range (a, (b+0)); -- no partitions, so fail insert into range_parted values ('a', 11); create table part1 partition of range_parted for values from ('a', 1) to ('a', 10); create table part2 partition of range_parted for values from ('a', 10) to ('a', 20); create table part3 partition of range_parted for values from ('b', 1) to ('b', 10); create table part4 partition of range_parted for values from ('b', 10) to ('b', 20); -- fail insert into part1 values ('a', 11); insert into part1 values ('b', 1); -- ok insert into part1 values ('a', 1); -- fail insert into part4 values ('b', 21); insert into part4 values ('a', 10); -- ok insert into part4 values ('b', 10); -- fail (partition key a has a NOT NULL constraint) insert into part1 values (null); -- fail (expression key (b+0) cannot be null either) insert into part1 values (1); create table list_parted ( a text, b int ) partition by list (lower(a)); create table part_aa_bb partition of list_parted FOR VALUES IN ('aa', 'bb'); create table part_cc_dd partition of list_parted FOR VALUES IN ('cc', 'dd'); create table part_null partition of list_parted FOR VALUES IN (null); -- fail insert into part_aa_bb values ('cc', 1); insert into part_aa_bb values ('AAa', 1); insert into part_aa_bb values (null); -- ok insert into part_cc_dd values ('cC', 1); insert into part_null values (null, 0); -- check in case of multi-level partitioned table create table part_ee_ff partition of list_parted for values in ('ee', 'ff') partition by range (b); create table part_ee_ff1 partition of part_ee_ff for values from (1) to (10); create table part_ee_ff2 partition of part_ee_ff for values from (10) to (20); -- test default partition create table part_default partition of list_parted default; -- Negative test: a row, which would fit in other partition, does not fit -- default partition, even when inserted directly insert into part_default values ('aa', 2); insert into part_default values (null, 2); -- ok insert into part_default values ('Zz', 2); -- test if default partition works as expected for multi-level partitioned -- table as well as when default partition itself is further partitioned drop table part_default; create table part_xx_yy partition of list_parted for values in ('xx', 'yy') partition by list (a); create table part_xx_yy_p1 partition of part_xx_yy for values in ('xx'); create table part_xx_yy_defpart partition of part_xx_yy default; create table part_default partition of list_parted default partition by range(b); create table part_default_p1 partition of part_default for values from (20) to (30); create table part_default_p2 partition of part_default for values from (30) to (40); -- fail insert into part_ee_ff1 values ('EE', 11); insert into part_default_p2 values ('gg', 43); -- fail (even the parent's, ie, part_ee_ff's partition constraint applies) insert into part_ee_ff1 values ('cc', 1); insert into part_default values ('gg', 43); -- ok insert into part_ee_ff1 values ('ff', 1); insert into part_ee_ff2 values ('ff', 11); insert into part_default_p1 values ('cd', 25); insert into part_default_p2 values ('de', 35); insert into list_parted values ('ab', 21); insert into list_parted values ('xx', 1); insert into list_parted values ('yy', 2); select tableoid::regclass, * from list_parted; -- Check tuple routing for partitioned tables -- fail insert into range_parted values ('a', 0); -- ok insert into range_parted values ('a', 1); insert into range_parted values ('a', 10); -- fail insert into range_parted values ('a', 20); -- ok insert into range_parted values ('b', 1); insert into range_parted values ('b', 10); -- fail (partition key (b+0) is null) insert into range_parted values ('a'); -- Check default partition create table part_def partition of range_parted default; -- fail insert into part_def values ('b', 10); -- ok insert into part_def values ('c', 10); insert into range_parted values (null, null); insert into range_parted values ('a', null); insert into range_parted values (null, 19); insert into range_parted values ('b', 20); select tableoid::regclass, * from range_parted; -- ok insert into list_parted values (null, 1); insert into list_parted (a) values ('aA'); -- fail (partition of part_ee_ff not found in both cases) insert into list_parted values ('EE', 0); insert into part_ee_ff values ('EE', 0); -- ok insert into list_parted values ('EE', 1); insert into part_ee_ff values ('EE', 10); select tableoid::regclass, * from list_parted; -- some more tests to exercise tuple-routing with multi-level partitioning create table part_gg partition of list_parted for values in ('gg') partition by range (b); create table part_gg1 partition of part_gg for values from (minvalue) to (1); create table part_gg2 partition of part_gg for values from (1) to (10) partition by range (b); create table part_gg2_1 partition of part_gg2 for values from (1) to (5); create table part_gg2_2 partition of part_gg2 for values from (5) to (10); create table part_ee_ff3 partition of part_ee_ff for values from (20) to (30) partition by range (b); create table part_ee_ff3_1 partition of part_ee_ff3 for values from (20) to (25); create table part_ee_ff3_2 partition of part_ee_ff3 for values from (25) to (30); truncate list_parted; insert into list_parted values ('aa'), ('cc'); insert into list_parted select 'Ff', s.a from generate_series(1, 29) s(a); insert into list_parted select 'gg', s.a from generate_series(1, 9) s(a); insert into list_parted (b) values (1); select tableoid::regclass::text, a, min(b) as min_b, max(b) as max_b from list_parted group by 1, 2 order by 1; -- direct partition inserts should check hash partition bound constraint -- Use hand-rolled hash functions and operator classes to get predictable -- result on different matchines. The hash function for int4 simply returns -- the sum of the values passed to it and the one for text returns the length -- of the non-empty string value passed to it or 0. create or replace function part_hashint4_noop(value int4, seed int8) returns int8 as $$ select value + seed; $$ language sql immutable; create operator class part_test_int4_ops for type int4 using hash as operator 1 =, function 2 part_hashint4_noop(int4, int8); create or replace function part_hashtext_length(value text, seed int8) RETURNS int8 AS $$ select length(coalesce(value, ''))::int8 $$ language sql immutable; create operator class part_test_text_ops for type text using hash as operator 1 =, function 2 part_hashtext_length(text, int8); create table hash_parted ( a int ) partition by hash (a part_test_int4_ops); create table hpart0 partition of hash_parted for values with (modulus 4, remainder 0); create table hpart1 partition of hash_parted for values with (modulus 4, remainder 1); create table hpart2 partition of hash_parted for values with (modulus 4, remainder 2); create table hpart3 partition of hash_parted for values with (modulus 4, remainder 3); insert into hash_parted values(generate_series(1,10)); -- direct insert of values divisible by 4 - ok; insert into hpart0 values(12),(16); -- fail; insert into hpart0 values(11); -- 11 % 4 -> 3 remainder i.e. valid data for hpart3 partition insert into hpart3 values(11); -- view data select tableoid::regclass as part, a, a%4 as "remainder = a % 4" from hash_parted order by part; -- test \d+ output on a table which has both partitioned and unpartitioned -- partitions \d+ list_parted -- cleanup drop table range_parted, list_parted; drop table hash_parted; -- test that a default partition added as the first partition accepts any value -- including null create table list_parted (a int) partition by list (a); create table part_default partition of list_parted default; \d+ part_default insert into part_default values (null); insert into part_default values (1); insert into part_default values (-1); select tableoid::regclass, a from list_parted; -- cleanup drop table list_parted; -- more tests for certain multi-level partitioning scenarios create table mlparted (a int, b int) partition by range (a, b); create table mlparted1 (b int not null, a int not null) partition by range ((b+0)); create table mlparted11 (like mlparted1); alter table mlparted11 drop a; alter table mlparted11 add a int; alter table mlparted11 drop a; alter table mlparted11 add a int not null; -- attnum for key attribute 'a' is different in mlparted, mlparted1, and mlparted11 select attrelid::regclass, attname, attnum from pg_attribute where attname = 'a' and (attrelid = 'mlparted'::regclass or attrelid = 'mlparted1'::regclass or attrelid = 'mlparted11'::regclass) order by attrelid::regclass::text; alter table mlparted1 attach partition mlparted11 for values from (2) to (5); alter table mlparted attach partition mlparted1 for values from (1, 2) to (1, 10); -- check that "(1, 2)" is correctly routed to mlparted11. insert into mlparted values (1, 2); select tableoid::regclass, * from mlparted; -- check that proper message is shown after failure to route through mlparted1 insert into mlparted (a, b) values (1, 5); truncate mlparted; alter table mlparted add constraint check_b check (b = 3); -- have a BR trigger modify the row such that the check_b is violated create function mlparted11_trig_fn() returns trigger AS $$ begin NEW.b := 4; return NEW; end; $$ language plpgsql; create trigger mlparted11_trig before insert ON mlparted11 for each row execute procedure mlparted11_trig_fn(); -- check that the correct row is shown when constraint check_b fails after -- "(1, 2)" is routed to mlparted11 (actually "(1, 4)" would be shown due -- to the BR trigger mlparted11_trig_fn) insert into mlparted values (1, 2); drop trigger mlparted11_trig on mlparted11; drop function mlparted11_trig_fn(); -- check that inserting into an internal partition successfully results in -- checking its partition constraint before inserting into the leaf partition -- selected by tuple-routing insert into mlparted1 (a, b) values (2, 3); -- check routing error through a list partitioned table when the key is null create table lparted_nonullpart (a int, b char) partition by list (b); create table lparted_nonullpart_a partition of lparted_nonullpart for values in ('a'); insert into lparted_nonullpart values (1); drop table lparted_nonullpart; -- check that RETURNING works correctly with tuple-routing alter table mlparted drop constraint check_b; create table mlparted12 partition of mlparted1 for values from (5) to (10); create table mlparted2 (b int not null, a int not null); alter table mlparted attach partition mlparted2 for values from (1, 10) to (1, 20); create table mlparted3 partition of mlparted for values from (1, 20) to (1, 30); create table mlparted4 (like mlparted); alter table mlparted4 drop a; alter table mlparted4 add a int not null; alter table mlparted attach partition mlparted4 for values from (1, 30) to (1, 40); with ins (a, b, c) as (insert into mlparted (b, a) select s.a, 1 from generate_series(2, 39) s(a) returning tableoid::regclass, *) select a, b, min(c), max(c) from ins group by a, b order by 1; alter table mlparted add c text; create table mlparted5 (c text, a int not null, b int not null) partition by list (c); create table mlparted5a (a int not null, c text, b int not null); alter table mlparted5 attach partition mlparted5a for values in ('a'); alter table mlparted attach partition mlparted5 for values from (1, 40) to (1, 50); alter table mlparted add constraint check_b check (a = 1 and b < 45); insert into mlparted values (1, 45, 'a'); create function mlparted5abrtrig_func() returns trigger as $$ begin new.c = 'b'; return new; end; $$ language plpgsql; create trigger mlparted5abrtrig before insert on mlparted5a for each row execute procedure mlparted5abrtrig_func(); insert into mlparted5 (a, b, c) values (1, 40, 'a'); drop table mlparted5; alter table mlparted drop constraint check_b; -- Check multi-level default partition create table mlparted_def partition of mlparted default partition by range(a); create table mlparted_def1 partition of mlparted_def for values from (40) to (50); create table mlparted_def2 partition of mlparted_def for values from (50) to (60); insert into mlparted values (40, 100); insert into mlparted_def1 values (42, 100); insert into mlparted_def2 values (54, 50); -- fail insert into mlparted values (70, 100); insert into mlparted_def1 values (52, 50); insert into mlparted_def2 values (34, 50); -- ok create table mlparted_defd partition of mlparted_def default; insert into mlparted values (70, 100); select tableoid::regclass, * from mlparted_def; -- Check multi-level tuple routing with attributes dropped from the -- top-most parent. First remove the last attribute. alter table mlparted add d int, add e int; alter table mlparted drop e; create table mlparted5 partition of mlparted for values from (1, 40) to (1, 50) partition by range (c); create table mlparted5_ab partition of mlparted5 for values from ('a') to ('c') partition by list (c); create table mlparted5_a partition of mlparted5_ab for values in ('a'); create table mlparted5_b (d int, b int, c text, a int); alter table mlparted5_ab attach partition mlparted5_b for values in ('b'); truncate mlparted; insert into mlparted values (1, 2, 'a', 1); insert into mlparted values (1, 40, 'a', 1); -- goes to mlparted5_a insert into mlparted values (1, 45, 'b', 1); -- goes to mlparted5_b select tableoid::regclass, * from mlparted order by a, b, c, d; alter table mlparted drop d; truncate mlparted; -- Remove the before last attribute. alter table mlparted add e int, add d int; alter table mlparted drop e; insert into mlparted values (1, 2, 'a', 1); insert into mlparted values (1, 40, 'a', 1); -- goes to mlparted5_a insert into mlparted values (1, 45, 'b', 1); -- goes to mlparted5_b select tableoid::regclass, * from mlparted order by a, b, c, d; alter table mlparted drop d; drop table mlparted5; -- check that message shown after failure to find a partition shows the -- appropriate key description (or none) in various situations create table key_desc (a int, b int) partition by list ((a+0)); create table key_desc_1 partition of key_desc for values in (1) partition by range (b); create user regress_insert_other_user; grant select (a) on key_desc_1 to regress_insert_other_user; grant insert on key_desc to regress_insert_other_user; set role regress_insert_other_user; -- no key description is shown insert into key_desc values (1, 1); reset role; grant select (b) on key_desc_1 to regress_insert_other_user; set role regress_insert_other_user; -- key description (b)=(1) is now shown insert into key_desc values (1, 1); -- key description is not shown if key contains expression insert into key_desc values (2, 1); reset role; revoke all on key_desc from regress_insert_other_user; revoke all on key_desc_1 from regress_insert_other_user; drop role regress_insert_other_user; drop table key_desc, key_desc_1; -- test minvalue/maxvalue restrictions create table mcrparted (a int, b int, c int) partition by range (a, abs(b), c); create table mcrparted0 partition of mcrparted for values from (minvalue, 0, 0) to (1, maxvalue, maxvalue); create table mcrparted2 partition of mcrparted for values from (10, 6, minvalue) to (10, maxvalue, minvalue); create table mcrparted4 partition of mcrparted for values from (21, minvalue, 0) to (30, 20, minvalue); -- check multi-column range partitioning expression enforces the same -- constraint as what tuple-routing would determine it to be create table mcrparted0 partition of mcrparted for values from (minvalue, minvalue, minvalue) to (1, maxvalue, maxvalue); create table mcrparted1 partition of mcrparted for values from (2, 1, minvalue) to (10, 5, 10); create table mcrparted2 partition of mcrparted for values from (10, 6, minvalue) to (10, maxvalue, maxvalue); create table mcrparted3 partition of mcrparted for values from (11, 1, 1) to (20, 10, 10); create table mcrparted4 partition of mcrparted for values from (21, minvalue, minvalue) to (30, 20, maxvalue); create table mcrparted5 partition of mcrparted for values from (30, 21, 20) to (maxvalue, maxvalue, maxvalue); -- null not allowed in range partition insert into mcrparted values (null, null, null); -- routed to mcrparted0 insert into mcrparted values (0, 1, 1); insert into mcrparted0 values (0, 1, 1); -- routed to mcparted1 insert into mcrparted values (9, 1000, 1); insert into mcrparted1 values (9, 1000, 1); insert into mcrparted values (10, 5, -1); insert into mcrparted1 values (10, 5, -1); insert into mcrparted values (2, 1, 0); insert into mcrparted1 values (2, 1, 0); -- routed to mcparted2 insert into mcrparted values (10, 6, 1000); insert into mcrparted2 values (10, 6, 1000); insert into mcrparted values (10, 1000, 1000); insert into mcrparted2 values (10, 1000, 1000); -- no partition exists, nor does mcrparted3 accept it insert into mcrparted values (11, 1, -1); insert into mcrparted3 values (11, 1, -1); -- routed to mcrparted5 insert into mcrparted values (30, 21, 20); insert into mcrparted5 values (30, 21, 20); insert into mcrparted4 values (30, 21, 20); -- error -- check rows select tableoid::regclass::text, * from mcrparted order by 1; -- cleanup drop table mcrparted; -- check that a BR constraint can't make partition contain violating rows create table brtrigpartcon (a int, b text) partition by list (a); create table brtrigpartcon1 partition of brtrigpartcon for values in (1); create or replace function brtrigpartcon1trigf() returns trigger as $$begin new.a := 2; return new; end$$ language plpgsql; create trigger brtrigpartcon1trig before insert on brtrigpartcon1 for each row execute procedure brtrigpartcon1trigf(); insert into brtrigpartcon values (1, 'hi there'); insert into brtrigpartcon1 values (1, 'hi there'); -- check that the message shows the appropriate column description in a -- situation where the partitioned table is not the primary ModifyTable node create table inserttest3 (f1 text default 'foo', f2 text default 'bar', f3 int); create role regress_coldesc_role; grant insert on inserttest3 to regress_coldesc_role; grant insert on brtrigpartcon to regress_coldesc_role; revoke select on brtrigpartcon from regress_coldesc_role; set role regress_coldesc_role; with result as (insert into brtrigpartcon values (1, 'hi there') returning 1) insert into inserttest3 (f3) select * from result; reset role; -- cleanup revoke all on inserttest3 from regress_coldesc_role; revoke all on brtrigpartcon from regress_coldesc_role; drop role regress_coldesc_role; drop table inserttest3; drop table brtrigpartcon; drop function brtrigpartcon1trigf(); -- check that "do nothing" BR triggers work with tuple-routing (this checks -- that estate->es_result_relation_info is appropriately set/reset for each -- routed tuple) create table donothingbrtrig_test (a int, b text) partition by list (a); create table donothingbrtrig_test1 (b text, a int); create table donothingbrtrig_test2 (c text, b text, a int); alter table donothingbrtrig_test2 drop column c; create or replace function donothingbrtrig_func() returns trigger as $$begin raise notice 'b: %', new.b; return NULL; end$$ language plpgsql; create trigger donothingbrtrig1 before insert on donothingbrtrig_test1 for each row execute procedure donothingbrtrig_func(); create trigger donothingbrtrig2 before insert on donothingbrtrig_test2 for each row execute procedure donothingbrtrig_func(); alter table donothingbrtrig_test attach partition donothingbrtrig_test1 for values in (1); alter table donothingbrtrig_test attach partition donothingbrtrig_test2 for values in (2); insert into donothingbrtrig_test values (1, 'foo'), (2, 'bar'); select tableoid::regclass, * from donothingbrtrig_test; -- cleanup drop table donothingbrtrig_test; drop function donothingbrtrig_func(); -- check multi-column range partitioning with minvalue/maxvalue constraints create table mcrparted (a text, b int) partition by range(a, b); create table mcrparted1_lt_b partition of mcrparted for values from (minvalue, minvalue) to ('b', minvalue); create table mcrparted2_b partition of mcrparted for values from ('b', minvalue) to ('c', minvalue); create table mcrparted3_c_to_common partition of mcrparted for values from ('c', minvalue) to ('common', minvalue); create table mcrparted4_common_lt_0 partition of mcrparted for values from ('common', minvalue) to ('common', 0); create table mcrparted5_common_0_to_10 partition of mcrparted for values from ('common', 0) to ('common', 10); create table mcrparted6_common_ge_10 partition of mcrparted for values from ('common', 10) to ('common', maxvalue); create table mcrparted7_gt_common_lt_d partition of mcrparted for values from ('common', maxvalue) to ('d', minvalue); create table mcrparted8_ge_d partition of mcrparted for values from ('d', minvalue) to (maxvalue, maxvalue); \d+ mcrparted \d+ mcrparted1_lt_b \d+ mcrparted2_b \d+ mcrparted3_c_to_common \d+ mcrparted4_common_lt_0 \d+ mcrparted5_common_0_to_10 \d+ mcrparted6_common_ge_10 \d+ mcrparted7_gt_common_lt_d \d+ mcrparted8_ge_d insert into mcrparted values ('aaa', 0), ('b', 0), ('bz', 10), ('c', -10), ('comm', -10), ('common', -10), ('common', 0), ('common', 10), ('commons', 0), ('d', -10), ('e', 0); select tableoid::regclass, * from mcrparted order by a, b; drop table mcrparted; -- check that wholerow vars in the RETURNING list work with partitioned tables create table returningwrtest (a int) partition by list (a); create table returningwrtest1 partition of returningwrtest for values in (1); insert into returningwrtest values (1) returning returningwrtest; -- check also that the wholerow vars in RETURNING list are converted as needed alter table returningwrtest add b text; create table returningwrtest2 (b text, c int, a int); alter table returningwrtest2 drop c; alter table returningwrtest attach partition returningwrtest2 for values in (2); insert into returningwrtest values (2, 'foo') returning returningwrtest; drop table returningwrtest; pgFormatter-4.2/t/pg-test-files/sql/insert_conflict.sql000066400000000000000000000670551361326045100233220ustar00rootroot00000000000000-- -- insert...on conflict do unique index inference -- create table insertconflicttest(key int4, fruit text); -- -- Test unique index inference with operator class specifications and -- named collations -- create unique index op_index_key on insertconflicttest(key, fruit text_pattern_ops); create unique index collation_index_key on insertconflicttest(key, fruit collate "C"); create unique index both_index_key on insertconflicttest(key, fruit collate "C" text_pattern_ops); create unique index both_index_expr_key on insertconflicttest(key, lower(fruit) collate "C" text_pattern_ops); -- fails explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key) do nothing; explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (fruit) do nothing; -- succeeds explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key, fruit) do nothing; explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (fruit, key, fruit, key) do nothing; explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (lower(fruit), key, lower(fruit), key) do nothing; explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key, fruit) do update set fruit = excluded.fruit where exists (select 1 from insertconflicttest ii where ii.key = excluded.key); -- Neither collation nor operator class specifications are required -- -- supplying them merely *limits* matches to indexes with matching opclasses -- used for relevant indexes explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key, fruit text_pattern_ops) do nothing; -- Okay, arbitrates using both index where text_pattern_ops opclass does and -- does not appear. explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key, fruit collate "C") do nothing; -- Okay, but only accepts the single index where both opclass and collation are -- specified explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (fruit collate "C" text_pattern_ops, key) do nothing; -- Okay, but only accepts the single index where both opclass and collation are -- specified (plus expression variant) explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (lower(fruit) collate "C", key, key) do nothing; -- Attribute appears twice, while not all attributes/expressions on attributes -- appearing within index definition match in terms of both opclass and -- collation. -- -- Works because every attribute in inference specification needs to be -- satisfied once or more by cataloged index attribute, and as always when an -- attribute in the cataloged definition has a non-default opclass/collation, -- it still satisfied some inference attribute lacking any particular -- opclass/collation specification. -- -- The implementation is liberal in accepting inference specifications on the -- assumption that multiple inferred unique indexes will prevent problematic -- cases. It rolls with unique indexes where attributes redundantly appear -- multiple times, too (which is not tested here). explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (fruit, key, fruit text_pattern_ops, key) do nothing; explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (lower(fruit) collate "C" text_pattern_ops, key, key) do nothing; drop index op_index_key; drop index collation_index_key; drop index both_index_key; drop index both_index_expr_key; -- -- Make sure that cross matching of attribute opclass/collation does not occur -- create unique index cross_match on insertconflicttest(lower(fruit) collate "C", upper(fruit) text_pattern_ops); -- fails: explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (lower(fruit) text_pattern_ops, upper(fruit) collate "C") do nothing; -- works: explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (lower(fruit) collate "C", upper(fruit) text_pattern_ops) do nothing; drop index cross_match; -- -- Single key tests -- create unique index key_index on insertconflicttest(key); -- -- Explain tests -- explain (costs off) insert into insertconflicttest values (0, 'Bilberry') on conflict (key) do update set fruit = excluded.fruit; -- Should display qual actually attributable to internal sequential scan: explain (costs off) insert into insertconflicttest values (0, 'Bilberry') on conflict (key) do update set fruit = excluded.fruit where insertconflicttest.fruit != 'Cawesh'; -- With EXCLUDED.* expression in scan node: explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key) do update set fruit = excluded.fruit where excluded.fruit != 'Elderberry'; -- Does the same, but JSON format shows "Conflict Arbiter Index" as JSON array: explain (costs off, format json) insert into insertconflicttest values (0, 'Bilberry') on conflict (key) do update set fruit = excluded.fruit where insertconflicttest.fruit != 'Lime' returning *; -- Fails (no unique index inference specification, required for do update variant): insert into insertconflicttest values (1, 'Apple') on conflict do update set fruit = excluded.fruit; -- inference succeeds: insert into insertconflicttest values (1, 'Apple') on conflict (key) do update set fruit = excluded.fruit; insert into insertconflicttest values (2, 'Orange') on conflict (key, key, key) do update set fruit = excluded.fruit; -- Succeed, since multi-assignment does not involve subquery: insert into insertconflicttest values (1, 'Apple'), (2, 'Orange') on conflict (key) do update set (fruit, key) = (excluded.fruit, excluded.key); -- Give good diagnostic message when EXCLUDED.* spuriously referenced from -- RETURNING: insert into insertconflicttest values (1, 'Apple') on conflict (key) do update set fruit = excluded.fruit RETURNING excluded.fruit; -- Only suggest
.* column when inference element misspelled: insert into insertconflicttest values (1, 'Apple') on conflict (keyy) do update set fruit = excluded.fruit; -- Have useful HINT for EXCLUDED.* RTE within UPDATE: insert into insertconflicttest values (1, 'Apple') on conflict (key) do update set fruit = excluded.fruitt; -- inference fails: insert into insertconflicttest values (3, 'Kiwi') on conflict (key, fruit) do update set fruit = excluded.fruit; insert into insertconflicttest values (4, 'Mango') on conflict (fruit, key) do update set fruit = excluded.fruit; insert into insertconflicttest values (5, 'Lemon') on conflict (fruit) do update set fruit = excluded.fruit; insert into insertconflicttest values (6, 'Passionfruit') on conflict (lower(fruit)) do update set fruit = excluded.fruit; -- Check the target relation can be aliased insert into insertconflicttest AS ict values (6, 'Passionfruit') on conflict (key) do update set fruit = excluded.fruit; -- ok, no reference to target table insert into insertconflicttest AS ict values (6, 'Passionfruit') on conflict (key) do update set fruit = ict.fruit; -- ok, alias insert into insertconflicttest AS ict values (6, 'Passionfruit') on conflict (key) do update set fruit = insertconflicttest.fruit; -- error, references aliased away name drop index key_index; -- -- Composite key tests -- create unique index comp_key_index on insertconflicttest(key, fruit); -- inference succeeds: insert into insertconflicttest values (7, 'Raspberry') on conflict (key, fruit) do update set fruit = excluded.fruit; insert into insertconflicttest values (8, 'Lime') on conflict (fruit, key) do update set fruit = excluded.fruit; -- inference fails: insert into insertconflicttest values (9, 'Banana') on conflict (key) do update set fruit = excluded.fruit; insert into insertconflicttest values (10, 'Blueberry') on conflict (key, key, key) do update set fruit = excluded.fruit; insert into insertconflicttest values (11, 'Cherry') on conflict (key, lower(fruit)) do update set fruit = excluded.fruit; insert into insertconflicttest values (12, 'Date') on conflict (lower(fruit), key) do update set fruit = excluded.fruit; drop index comp_key_index; -- -- Partial index tests, no inference predicate specified -- create unique index part_comp_key_index on insertconflicttest(key, fruit) where key < 5; create unique index expr_part_comp_key_index on insertconflicttest(key, lower(fruit)) where key < 5; -- inference fails: insert into insertconflicttest values (13, 'Grape') on conflict (key, fruit) do update set fruit = excluded.fruit; insert into insertconflicttest values (14, 'Raisin') on conflict (fruit, key) do update set fruit = excluded.fruit; insert into insertconflicttest values (15, 'Cranberry') on conflict (key) do update set fruit = excluded.fruit; insert into insertconflicttest values (16, 'Melon') on conflict (key, key, key) do update set fruit = excluded.fruit; insert into insertconflicttest values (17, 'Mulberry') on conflict (key, lower(fruit)) do update set fruit = excluded.fruit; insert into insertconflicttest values (18, 'Pineapple') on conflict (lower(fruit), key) do update set fruit = excluded.fruit; drop index part_comp_key_index; drop index expr_part_comp_key_index; -- -- Expression index tests -- create unique index expr_key_index on insertconflicttest(lower(fruit)); -- inference succeeds: insert into insertconflicttest values (20, 'Quince') on conflict (lower(fruit)) do update set fruit = excluded.fruit; insert into insertconflicttest values (21, 'Pomegranate') on conflict (lower(fruit), lower(fruit)) do update set fruit = excluded.fruit; -- inference fails: insert into insertconflicttest values (22, 'Apricot') on conflict (upper(fruit)) do update set fruit = excluded.fruit; insert into insertconflicttest values (23, 'Blackberry') on conflict (fruit) do update set fruit = excluded.fruit; drop index expr_key_index; -- -- Expression index tests (with regular column) -- create unique index expr_comp_key_index on insertconflicttest(key, lower(fruit)); create unique index tricky_expr_comp_key_index on insertconflicttest(key, lower(fruit), upper(fruit)); -- inference succeeds: insert into insertconflicttest values (24, 'Plum') on conflict (key, lower(fruit)) do update set fruit = excluded.fruit; insert into insertconflicttest values (25, 'Peach') on conflict (lower(fruit), key) do update set fruit = excluded.fruit; -- Should not infer "tricky_expr_comp_key_index" index: explain (costs off) insert into insertconflicttest values (26, 'Fig') on conflict (lower(fruit), key, lower(fruit), key) do update set fruit = excluded.fruit; -- inference fails: insert into insertconflicttest values (27, 'Prune') on conflict (key, upper(fruit)) do update set fruit = excluded.fruit; insert into insertconflicttest values (28, 'Redcurrant') on conflict (fruit, key) do update set fruit = excluded.fruit; insert into insertconflicttest values (29, 'Nectarine') on conflict (key) do update set fruit = excluded.fruit; drop index expr_comp_key_index; drop index tricky_expr_comp_key_index; -- -- Non-spurious duplicate violation tests -- create unique index key_index on insertconflicttest(key); create unique index fruit_index on insertconflicttest(fruit); -- succeeds, since UPDATE happens to update "fruit" to existing value: insert into insertconflicttest values (26, 'Fig') on conflict (key) do update set fruit = excluded.fruit; -- fails, since UPDATE is to row with key value 26, and we're updating "fruit" -- to a value that happens to exist in another row ('peach'): insert into insertconflicttest values (26, 'Peach') on conflict (key) do update set fruit = excluded.fruit; -- succeeds, since "key" isn't repeated/referenced in UPDATE, and "fruit" -- arbitrates that statement updates existing "Fig" row: insert into insertconflicttest values (25, 'Fig') on conflict (fruit) do update set fruit = excluded.fruit; drop index key_index; drop index fruit_index; -- -- Test partial unique index inference -- create unique index partial_key_index on insertconflicttest(key) where fruit like '%berry'; -- Succeeds insert into insertconflicttest values (23, 'Blackberry') on conflict (key) where fruit like '%berry' do update set fruit = excluded.fruit; insert into insertconflicttest values (23, 'Blackberry') on conflict (key) where fruit like '%berry' and fruit = 'inconsequential' do nothing; -- fails insert into insertconflicttest values (23, 'Blackberry') on conflict (key) do update set fruit = excluded.fruit; insert into insertconflicttest values (23, 'Blackberry') on conflict (key) where fruit like '%berry' or fruit = 'consequential' do nothing; insert into insertconflicttest values (23, 'Blackberry') on conflict (fruit) where fruit like '%berry' do update set fruit = excluded.fruit; drop index partial_key_index; -- -- Test that wholerow references to ON CONFLICT's EXCLUDED work -- create unique index plain on insertconflicttest(key); -- Succeeds, updates existing row: insert into insertconflicttest as i values (23, 'Jackfruit') on conflict (key) do update set fruit = excluded.fruit where i.* != excluded.* returning *; -- No update this time, though: insert into insertconflicttest as i values (23, 'Jackfruit') on conflict (key) do update set fruit = excluded.fruit where i.* != excluded.* returning *; -- Predicate changed to require match rather than non-match, so updates once more: insert into insertconflicttest as i values (23, 'Jackfruit') on conflict (key) do update set fruit = excluded.fruit where i.* = excluded.* returning *; -- Assign: insert into insertconflicttest as i values (23, 'Avocado') on conflict (key) do update set fruit = excluded.*::text returning *; -- deparse whole row var in WHERE and SET clauses: explain (costs off) insert into insertconflicttest as i values (23, 'Avocado') on conflict (key) do update set fruit = excluded.fruit where excluded.* is null; explain (costs off) insert into insertconflicttest as i values (23, 'Avocado') on conflict (key) do update set fruit = excluded.*::text; drop index plain; -- Cleanup drop table insertconflicttest; -- -- Verify that EXCLUDED does not allow system column references. These -- do not make sense because EXCLUDED isn't an already stored tuple -- (and thus doesn't have a ctid etc). -- create table syscolconflicttest(key int4, data text); insert into syscolconflicttest values (1); insert into syscolconflicttest values (1) on conflict (key) do update set data = excluded.ctid::text; drop table syscolconflicttest; -- -- Previous tests all managed to not test any expressions requiring -- planner preprocessing ... -- create table insertconflict (a bigint, b bigint); create unique index insertconflicti1 on insertconflict(coalesce(a, 0)); create unique index insertconflicti2 on insertconflict(b) where coalesce(a, 1) > 0; insert into insertconflict values (1, 2) on conflict (coalesce(a, 0)) do nothing; insert into insertconflict values (1, 2) on conflict (b) where coalesce(a, 1) > 0 do nothing; insert into insertconflict values (1, 2) on conflict (b) where coalesce(a, 1) > 1 do nothing; drop table insertconflict; -- -- test insertion through view -- create table insertconflict (f1 int primary key, f2 text); create view insertconflictv as select * from insertconflict with cascaded check option; insert into insertconflictv values (1,'foo') on conflict (f1) do update set f2 = excluded.f2; select * from insertconflict; insert into insertconflictv values (1,'bar') on conflict (f1) do update set f2 = excluded.f2; select * from insertconflict; drop view insertconflictv; drop table insertconflict; -- ****************************************************************** -- * * -- * Test inheritance (example taken from tutorial) * -- * * -- ****************************************************************** create table cities ( name text, population float8, altitude int -- (in ft) ); create table capitals ( state char(2) ) inherits (cities); -- Create unique indexes. Due to a general limitation of inheritance, -- uniqueness is only enforced per-relation. Unique index inference -- specification will do the right thing, though. create unique index cities_names_unique on cities (name); create unique index capitals_names_unique on capitals (name); -- prepopulate the tables. insert into cities values ('San Francisco', 7.24E+5, 63); insert into cities values ('Las Vegas', 2.583E+5, 2174); insert into cities values ('Mariposa', 1200, 1953); insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA'); insert into capitals values ('Madison', 1.913E+5, 845, 'WI'); -- Tests proper for inheritance: select * from capitals; -- Succeeds: insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict do nothing; insert into capitals values ('Sacramento', 4664.E+5, 30, 'CA') on conflict (name) do update set population = excluded.population; -- Wrong "Sacramento", so do nothing: insert into capitals values ('Sacramento', 50, 2267, 'NE') on conflict (name) do nothing; select * from capitals; insert into cities values ('Las Vegas', 5.83E+5, 2001) on conflict (name) do update set population = excluded.population, altitude = excluded.altitude; select tableoid::regclass, * from cities; insert into capitals values ('Las Vegas', 5.83E+5, 2222, 'NV') on conflict (name) do update set population = excluded.population; -- Capitals will contain new capital, Las Vegas: select * from capitals; -- Cities contains two instances of "Las Vegas", since unique constraints don't -- work across inheritance: select tableoid::regclass, * from cities; -- This only affects "cities" version of "Las Vegas": insert into cities values ('Las Vegas', 5.86E+5, 2223) on conflict (name) do update set population = excluded.population, altitude = excluded.altitude; select tableoid::regclass, * from cities; -- clean up drop table capitals; drop table cities; -- Make sure a table named excluded is handled properly create table excluded(key int primary key, data text); insert into excluded values(1, '1'); -- error, ambiguous insert into excluded values(1, '2') on conflict (key) do update set data = excluded.data RETURNING *; -- ok, aliased insert into excluded AS target values(1, '2') on conflict (key) do update set data = excluded.data RETURNING *; -- ok, aliased insert into excluded AS target values(1, '2') on conflict (key) do update set data = target.data RETURNING *; -- make sure excluded isn't a problem in returning clause insert into excluded values(1, '2') on conflict (key) do update set data = 3 RETURNING excluded.*; -- clean up drop table excluded; -- check that references to columns after dropped columns are handled correctly create table dropcol(key int primary key, drop1 int, keep1 text, drop2 numeric, keep2 float); insert into dropcol(key, drop1, keep1, drop2, keep2) values(1, 1, '1', '1', 1); -- set using excluded insert into dropcol(key, drop1, keep1, drop2, keep2) values(1, 2, '2', '2', 2) on conflict(key) do update set drop1 = excluded.drop1, keep1 = excluded.keep1, drop2 = excluded.drop2, keep2 = excluded.keep2 where excluded.drop1 is not null and excluded.keep1 is not null and excluded.drop2 is not null and excluded.keep2 is not null and dropcol.drop1 is not null and dropcol.keep1 is not null and dropcol.drop2 is not null and dropcol.keep2 is not null returning *; ; -- set using existing table insert into dropcol(key, drop1, keep1, drop2, keep2) values(1, 3, '3', '3', 3) on conflict(key) do update set drop1 = dropcol.drop1, keep1 = dropcol.keep1, drop2 = dropcol.drop2, keep2 = dropcol.keep2 returning *; ; alter table dropcol drop column drop1, drop column drop2; -- set using excluded insert into dropcol(key, keep1, keep2) values(1, '4', 4) on conflict(key) do update set keep1 = excluded.keep1, keep2 = excluded.keep2 where excluded.keep1 is not null and excluded.keep2 is not null and dropcol.keep1 is not null and dropcol.keep2 is not null returning *; ; -- set using existing table insert into dropcol(key, keep1, keep2) values(1, '5', 5) on conflict(key) do update set keep1 = dropcol.keep1, keep2 = dropcol.keep2 returning *; ; DROP TABLE dropcol; -- check handling of regular btree constraint along with gist constraint create table twoconstraints (f1 int unique, f2 box, exclude using gist(f2 with &&)); insert into twoconstraints values(1, '((0,0),(1,1))'); insert into twoconstraints values(1, '((2,2),(3,3))'); -- fail on f1 insert into twoconstraints values(2, '((0,0),(1,2))'); -- fail on f2 insert into twoconstraints values(2, '((0,0),(1,2))') on conflict on constraint twoconstraints_f1_key do nothing; -- fail on f2 insert into twoconstraints values(2, '((0,0),(1,2))') on conflict on constraint twoconstraints_f2_excl do nothing; -- do nothing select * from twoconstraints; drop table twoconstraints; -- check handling of self-conflicts at various isolation levels create table selfconflict (f1 int primary key, f2 int); begin transaction isolation level read committed; insert into selfconflict values (1,1), (1,2) on conflict do nothing; commit; begin transaction isolation level repeatable read; insert into selfconflict values (2,1), (2,2) on conflict do nothing; commit; begin transaction isolation level serializable; insert into selfconflict values (3,1), (3,2) on conflict do nothing; commit; begin transaction isolation level read committed; insert into selfconflict values (4,1), (4,2) on conflict(f1) do update set f2 = 0; commit; begin transaction isolation level repeatable read; insert into selfconflict values (5,1), (5,2) on conflict(f1) do update set f2 = 0; commit; begin transaction isolation level serializable; insert into selfconflict values (6,1), (6,2) on conflict(f1) do update set f2 = 0; commit; select * from selfconflict; drop table selfconflict; -- check ON CONFLICT handling with partitioned tables create table parted_conflict_test (a int unique, b char) partition by list (a); create table parted_conflict_test_1 partition of parted_conflict_test (b unique) for values in (1, 2); -- no indexes required here insert into parted_conflict_test values (1, 'a') on conflict do nothing; -- index on a required, which does exist in parent insert into parted_conflict_test values (1, 'a') on conflict (a) do nothing; insert into parted_conflict_test values (1, 'a') on conflict (a) do update set b = excluded.b; -- targeting partition directly will work insert into parted_conflict_test_1 values (1, 'a') on conflict (a) do nothing; insert into parted_conflict_test_1 values (1, 'b') on conflict (a) do update set b = excluded.b; -- index on b required, which doesn't exist in parent insert into parted_conflict_test values (2, 'b') on conflict (b) do update set a = excluded.a; -- targeting partition directly will work insert into parted_conflict_test_1 values (2, 'b') on conflict (b) do update set a = excluded.a; -- should see (2, 'b') select * from parted_conflict_test order by a; -- now check that DO UPDATE works correctly for target partition with -- different attribute numbers create table parted_conflict_test_2 (b char, a int unique); alter table parted_conflict_test attach partition parted_conflict_test_2 for values in (3); truncate parted_conflict_test; insert into parted_conflict_test values (3, 'a') on conflict (a) do update set b = excluded.b; insert into parted_conflict_test values (3, 'b') on conflict (a) do update set b = excluded.b; -- should see (3, 'b') select * from parted_conflict_test order by a; -- case where parent will have a dropped column, but the partition won't alter table parted_conflict_test drop b, add b char; create table parted_conflict_test_3 partition of parted_conflict_test for values in (4); truncate parted_conflict_test; insert into parted_conflict_test (a, b) values (4, 'a') on conflict (a) do update set b = excluded.b; insert into parted_conflict_test (a, b) values (4, 'b') on conflict (a) do update set b = excluded.b where parted_conflict_test.b = 'a'; -- should see (4, 'b') select * from parted_conflict_test order by a; -- case with multi-level partitioning create table parted_conflict_test_4 partition of parted_conflict_test for values in (5) partition by list (a); create table parted_conflict_test_4_1 partition of parted_conflict_test_4 for values in (5); truncate parted_conflict_test; insert into parted_conflict_test (a, b) values (5, 'a') on conflict (a) do update set b = excluded.b; insert into parted_conflict_test (a, b) values (5, 'b') on conflict (a) do update set b = excluded.b where parted_conflict_test.b = 'a'; -- should see (5, 'b') select * from parted_conflict_test order by a; -- test with multiple rows truncate parted_conflict_test; insert into parted_conflict_test (a, b) values (1, 'a'), (2, 'a'), (4, 'a') on conflict (a) do update set b = excluded.b where excluded.b = 'b'; insert into parted_conflict_test (a, b) values (1, 'b'), (2, 'c'), (4, 'b') on conflict (a) do update set b = excluded.b where excluded.b = 'b'; -- should see (1, 'b'), (2, 'a'), (4, 'b') select * from parted_conflict_test order by a; drop table parted_conflict_test; -- test behavior of inserting a conflicting tuple into an intermediate -- partitioning level create table parted_conflict (a int primary key, b text) partition by range (a); create table parted_conflict_1 partition of parted_conflict for values from (0) to (1000) partition by range (a); create table parted_conflict_1_1 partition of parted_conflict_1 for values from (0) to (500); insert into parted_conflict values (40, 'forty'); insert into parted_conflict_1 values (40, 'cuarenta') on conflict (a) do update set b = excluded.b; drop table parted_conflict; -- same thing, but this time try to use an index that's created not in the -- partition create table parted_conflict (a int, b text) partition by range (a); create table parted_conflict_1 partition of parted_conflict for values from (0) to (1000) partition by range (a); create table parted_conflict_1_1 partition of parted_conflict_1 for values from (0) to (500); create unique index on only parted_conflict_1 (a); create unique index on only parted_conflict (a); alter index parted_conflict_a_idx attach partition parted_conflict_1_a_idx; insert into parted_conflict values (40, 'forty'); insert into parted_conflict_1 values (40, 'cuarenta') on conflict (a) do update set b = excluded.b; drop table parted_conflict; -- test whole-row Vars in ON CONFLICT expressions create table parted_conflict (a int, b text, c int) partition by range (a); create table parted_conflict_1 (drp text, c int, a int, b text); alter table parted_conflict_1 drop column drp; create unique index on parted_conflict (a, b); alter table parted_conflict attach partition parted_conflict_1 for values from (0) to (1000); truncate parted_conflict; insert into parted_conflict values (50, 'cincuenta', 1); insert into parted_conflict values (50, 'cincuenta', 2) on conflict (a, b) do update set (a, b, c) = row(excluded.*) where parted_conflict = (50, text 'cincuenta', 1) and excluded = (50, text 'cincuenta', 2); -- should see (50, 'cincuenta', 2) select * from parted_conflict order by a; -- test with statement level triggers create or replace function parted_conflict_update_func() returns trigger as $$ declare r record; begin for r in select * from inserted loop raise notice 'a = %, b = %, c = %', r.a, r.b, r.c; end loop; return new; end; $$ language plpgsql; create trigger parted_conflict_update after update on parted_conflict referencing new table as inserted for each statement execute procedure parted_conflict_update_func(); truncate parted_conflict; insert into parted_conflict values (0, 'cero', 1); insert into parted_conflict values(0, 'cero', 1) on conflict (a,b) do update set c = parted_conflict.c + 1; drop table parted_conflict; drop function parted_conflict_update_func(); pgFormatter-4.2/t/pg-test-files/sql/int2.sql000066400000000000000000000062521361326045100210010ustar00rootroot00000000000000-- -- INT2 -- CREATE TABLE INT2_TBL(f1 int2); INSERT INTO INT2_TBL(f1) VALUES ('0 '); INSERT INTO INT2_TBL(f1) VALUES (' 1234 '); INSERT INTO INT2_TBL(f1) VALUES (' -1234'); INSERT INTO INT2_TBL(f1) VALUES ('34.5'); -- largest and smallest values INSERT INTO INT2_TBL(f1) VALUES ('32767'); INSERT INTO INT2_TBL(f1) VALUES ('-32767'); -- bad input values -- should give errors INSERT INTO INT2_TBL(f1) VALUES ('100000'); INSERT INTO INT2_TBL(f1) VALUES ('asdf'); INSERT INTO INT2_TBL(f1) VALUES (' '); INSERT INTO INT2_TBL(f1) VALUES ('- 1234'); INSERT INTO INT2_TBL(f1) VALUES ('4 444'); INSERT INTO INT2_TBL(f1) VALUES ('123 dt'); INSERT INTO INT2_TBL(f1) VALUES (''); SELECT '' AS five, * FROM INT2_TBL; SELECT '' AS four, i.* FROM INT2_TBL i WHERE i.f1 <> int2 '0'; SELECT '' AS four, i.* FROM INT2_TBL i WHERE i.f1 <> int4 '0'; SELECT '' AS one, i.* FROM INT2_TBL i WHERE i.f1 = int2 '0'; SELECT '' AS one, i.* FROM INT2_TBL i WHERE i.f1 = int4 '0'; SELECT '' AS two, i.* FROM INT2_TBL i WHERE i.f1 < int2 '0'; SELECT '' AS two, i.* FROM INT2_TBL i WHERE i.f1 < int4 '0'; SELECT '' AS three, i.* FROM INT2_TBL i WHERE i.f1 <= int2 '0'; SELECT '' AS three, i.* FROM INT2_TBL i WHERE i.f1 <= int4 '0'; SELECT '' AS two, i.* FROM INT2_TBL i WHERE i.f1 > int2 '0'; SELECT '' AS two, i.* FROM INT2_TBL i WHERE i.f1 > int4 '0'; SELECT '' AS three, i.* FROM INT2_TBL i WHERE i.f1 >= int2 '0'; SELECT '' AS three, i.* FROM INT2_TBL i WHERE i.f1 >= int4 '0'; -- positive odds SELECT '' AS one, i.* FROM INT2_TBL i WHERE (i.f1 % int2 '2') = int2 '1'; -- any evens SELECT '' AS three, i.* FROM INT2_TBL i WHERE (i.f1 % int4 '2') = int2 '0'; SELECT '' AS five, i.f1, i.f1 * int2 '2' AS x FROM INT2_TBL i; SELECT '' AS five, i.f1, i.f1 * int2 '2' AS x FROM INT2_TBL i WHERE abs(f1) < 16384; SELECT '' AS five, i.f1, i.f1 * int4 '2' AS x FROM INT2_TBL i; SELECT '' AS five, i.f1, i.f1 + int2 '2' AS x FROM INT2_TBL i; SELECT '' AS five, i.f1, i.f1 + int2 '2' AS x FROM INT2_TBL i WHERE f1 < 32766; SELECT '' AS five, i.f1, i.f1 + int4 '2' AS x FROM INT2_TBL i; SELECT '' AS five, i.f1, i.f1 - int2 '2' AS x FROM INT2_TBL i; SELECT '' AS five, i.f1, i.f1 - int2 '2' AS x FROM INT2_TBL i WHERE f1 > -32767; SELECT '' AS five, i.f1, i.f1 - int4 '2' AS x FROM INT2_TBL i; SELECT '' AS five, i.f1, i.f1 / int2 '2' AS x FROM INT2_TBL i; SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT2_TBL i; -- corner cases SELECT (-1::int2<<15)::text; SELECT ((-1::int2<<15)+1::int2)::text; -- check sane handling of INT16_MIN overflow cases SELECT (-32768)::int2 * (-1)::int2; SELECT (-32768)::int2 / (-1)::int2; SELECT (-32768)::int2 % (-1)::int2; -- check rounding when casting from float SELECT x, x::int2 AS int2_value FROM (VALUES (-2.5::float8), (-1.5::float8), (-0.5::float8), (0.0::float8), (0.5::float8), (1.5::float8), (2.5::float8)) t(x); -- check rounding when casting from numeric SELECT x, x::int2 AS int2_value FROM (VALUES (-2.5::numeric), (-1.5::numeric), (-0.5::numeric), (0.0::numeric), (0.5::numeric), (1.5::numeric), (2.5::numeric)) t(x); pgFormatter-4.2/t/pg-test-files/sql/int4.sql000066400000000000000000000102031361326045100207720ustar00rootroot00000000000000-- -- INT4 -- CREATE TABLE INT4_TBL(f1 int4); INSERT INTO INT4_TBL(f1) VALUES (' 0 '); INSERT INTO INT4_TBL(f1) VALUES ('123456 '); INSERT INTO INT4_TBL(f1) VALUES (' -123456'); INSERT INTO INT4_TBL(f1) VALUES ('34.5'); -- largest and smallest values INSERT INTO INT4_TBL(f1) VALUES ('2147483647'); INSERT INTO INT4_TBL(f1) VALUES ('-2147483647'); -- bad input values -- should give errors INSERT INTO INT4_TBL(f1) VALUES ('1000000000000'); INSERT INTO INT4_TBL(f1) VALUES ('asdf'); INSERT INTO INT4_TBL(f1) VALUES (' '); INSERT INTO INT4_TBL(f1) VALUES (' asdf '); INSERT INTO INT4_TBL(f1) VALUES ('- 1234'); INSERT INTO INT4_TBL(f1) VALUES ('123 5'); INSERT INTO INT4_TBL(f1) VALUES (''); SELECT '' AS five, * FROM INT4_TBL; SELECT '' AS four, i.* FROM INT4_TBL i WHERE i.f1 <> int2 '0'; SELECT '' AS four, i.* FROM INT4_TBL i WHERE i.f1 <> int4 '0'; SELECT '' AS one, i.* FROM INT4_TBL i WHERE i.f1 = int2 '0'; SELECT '' AS one, i.* FROM INT4_TBL i WHERE i.f1 = int4 '0'; SELECT '' AS two, i.* FROM INT4_TBL i WHERE i.f1 < int2 '0'; SELECT '' AS two, i.* FROM INT4_TBL i WHERE i.f1 < int4 '0'; SELECT '' AS three, i.* FROM INT4_TBL i WHERE i.f1 <= int2 '0'; SELECT '' AS three, i.* FROM INT4_TBL i WHERE i.f1 <= int4 '0'; SELECT '' AS two, i.* FROM INT4_TBL i WHERE i.f1 > int2 '0'; SELECT '' AS two, i.* FROM INT4_TBL i WHERE i.f1 > int4 '0'; SELECT '' AS three, i.* FROM INT4_TBL i WHERE i.f1 >= int2 '0'; SELECT '' AS three, i.* FROM INT4_TBL i WHERE i.f1 >= int4 '0'; -- positive odds SELECT '' AS one, i.* FROM INT4_TBL i WHERE (i.f1 % int2 '2') = int2 '1'; -- any evens SELECT '' AS three, i.* FROM INT4_TBL i WHERE (i.f1 % int4 '2') = int2 '0'; SELECT '' AS five, i.f1, i.f1 * int2 '2' AS x FROM INT4_TBL i; SELECT '' AS five, i.f1, i.f1 * int2 '2' AS x FROM INT4_TBL i WHERE abs(f1) < 1073741824; SELECT '' AS five, i.f1, i.f1 * int4 '2' AS x FROM INT4_TBL i; SELECT '' AS five, i.f1, i.f1 * int4 '2' AS x FROM INT4_TBL i WHERE abs(f1) < 1073741824; SELECT '' AS five, i.f1, i.f1 + int2 '2' AS x FROM INT4_TBL i; SELECT '' AS five, i.f1, i.f1 + int2 '2' AS x FROM INT4_TBL i WHERE f1 < 2147483646; SELECT '' AS five, i.f1, i.f1 + int4 '2' AS x FROM INT4_TBL i; SELECT '' AS five, i.f1, i.f1 + int4 '2' AS x FROM INT4_TBL i WHERE f1 < 2147483646; SELECT '' AS five, i.f1, i.f1 - int2 '2' AS x FROM INT4_TBL i; SELECT '' AS five, i.f1, i.f1 - int2 '2' AS x FROM INT4_TBL i WHERE f1 > -2147483647; SELECT '' AS five, i.f1, i.f1 - int4 '2' AS x FROM INT4_TBL i; SELECT '' AS five, i.f1, i.f1 - int4 '2' AS x FROM INT4_TBL i WHERE f1 > -2147483647; SELECT '' AS five, i.f1, i.f1 / int2 '2' AS x FROM INT4_TBL i; SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT4_TBL i; -- -- more complex expressions -- -- variations on unary minus parsing SELECT -2+3 AS one; SELECT 4-2 AS two; SELECT 2- -1 AS three; SELECT 2 - -2 AS four; SELECT int2 '2' * int2 '2' = int2 '16' / int2 '4' AS true; SELECT int4 '2' * int2 '2' = int2 '16' / int4 '4' AS true; SELECT int2 '2' * int4 '2' = int4 '16' / int2 '4' AS true; SELECT int4 '1000' < int4 '999' AS false; SELECT 4! AS twenty_four; SELECT !!3 AS six; SELECT 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 AS ten; SELECT 2 + 2 / 2 AS three; SELECT (2 + 2) / 2 AS two; -- corner case SELECT (-1::int4<<31)::text; SELECT ((-1::int4<<31)+1)::text; -- check sane handling of INT_MIN overflow cases SELECT (-2147483648)::int4 * (-1)::int4; SELECT (-2147483648)::int4 / (-1)::int4; SELECT (-2147483648)::int4 % (-1)::int4; SELECT (-2147483648)::int4 * (-1)::int2; SELECT (-2147483648)::int4 / (-1)::int2; SELECT (-2147483648)::int4 % (-1)::int2; -- check rounding when casting from float SELECT x, x::int4 AS int4_value FROM (VALUES (-2.5::float8), (-1.5::float8), (-0.5::float8), (0.0::float8), (0.5::float8), (1.5::float8), (2.5::float8)) t(x); -- check rounding when casting from numeric SELECT x, x::int4 AS int4_value FROM (VALUES (-2.5::numeric), (-1.5::numeric), (-0.5::numeric), (0.0::numeric), (0.5::numeric), (1.5::numeric), (2.5::numeric)) t(x); pgFormatter-4.2/t/pg-test-files/sql/int8.sql000066400000000000000000000212121361326045100210000ustar00rootroot00000000000000-- -- INT8 -- Test int8 64-bit integers. -- CREATE TABLE INT8_TBL(q1 int8, q2 int8); INSERT INTO INT8_TBL VALUES(' 123 ',' 456'); INSERT INTO INT8_TBL VALUES('123 ','4567890123456789'); INSERT INTO INT8_TBL VALUES('4567890123456789','123'); INSERT INTO INT8_TBL VALUES(+4567890123456789,'4567890123456789'); INSERT INTO INT8_TBL VALUES('+4567890123456789','-4567890123456789'); -- bad inputs INSERT INTO INT8_TBL(q1) VALUES (' '); INSERT INTO INT8_TBL(q1) VALUES ('xxx'); INSERT INTO INT8_TBL(q1) VALUES ('3908203590239580293850293850329485'); INSERT INTO INT8_TBL(q1) VALUES ('-1204982019841029840928340329840934'); INSERT INTO INT8_TBL(q1) VALUES ('- 123'); INSERT INTO INT8_TBL(q1) VALUES (' 345 5'); INSERT INTO INT8_TBL(q1) VALUES (''); SELECT * FROM INT8_TBL; -- int8/int8 cmp SELECT * FROM INT8_TBL WHERE q2 = 4567890123456789; SELECT * FROM INT8_TBL WHERE q2 <> 4567890123456789; SELECT * FROM INT8_TBL WHERE q2 < 4567890123456789; SELECT * FROM INT8_TBL WHERE q2 > 4567890123456789; SELECT * FROM INT8_TBL WHERE q2 <= 4567890123456789; SELECT * FROM INT8_TBL WHERE q2 >= 4567890123456789; -- int8/int4 cmp SELECT * FROM INT8_TBL WHERE q2 = 456; SELECT * FROM INT8_TBL WHERE q2 <> 456; SELECT * FROM INT8_TBL WHERE q2 < 456; SELECT * FROM INT8_TBL WHERE q2 > 456; SELECT * FROM INT8_TBL WHERE q2 <= 456; SELECT * FROM INT8_TBL WHERE q2 >= 456; -- int4/int8 cmp SELECT * FROM INT8_TBL WHERE 123 = q1; SELECT * FROM INT8_TBL WHERE 123 <> q1; SELECT * FROM INT8_TBL WHERE 123 < q1; SELECT * FROM INT8_TBL WHERE 123 > q1; SELECT * FROM INT8_TBL WHERE 123 <= q1; SELECT * FROM INT8_TBL WHERE 123 >= q1; -- int8/int2 cmp SELECT * FROM INT8_TBL WHERE q2 = '456'::int2; SELECT * FROM INT8_TBL WHERE q2 <> '456'::int2; SELECT * FROM INT8_TBL WHERE q2 < '456'::int2; SELECT * FROM INT8_TBL WHERE q2 > '456'::int2; SELECT * FROM INT8_TBL WHERE q2 <= '456'::int2; SELECT * FROM INT8_TBL WHERE q2 >= '456'::int2; -- int2/int8 cmp SELECT * FROM INT8_TBL WHERE '123'::int2 = q1; SELECT * FROM INT8_TBL WHERE '123'::int2 <> q1; SELECT * FROM INT8_TBL WHERE '123'::int2 < q1; SELECT * FROM INT8_TBL WHERE '123'::int2 > q1; SELECT * FROM INT8_TBL WHERE '123'::int2 <= q1; SELECT * FROM INT8_TBL WHERE '123'::int2 >= q1; SELECT '' AS five, q1 AS plus, -q1 AS minus FROM INT8_TBL; SELECT '' AS five, q1, q2, q1 + q2 AS plus FROM INT8_TBL; SELECT '' AS five, q1, q2, q1 - q2 AS minus FROM INT8_TBL; SELECT '' AS three, q1, q2, q1 * q2 AS multiply FROM INT8_TBL; SELECT '' AS three, q1, q2, q1 * q2 AS multiply FROM INT8_TBL WHERE q1 < 1000 or (q2 > 0 and q2 < 1000); SELECT '' AS five, q1, q2, q1 / q2 AS divide, q1 % q2 AS mod FROM INT8_TBL; SELECT '' AS five, q1, float8(q1) FROM INT8_TBL; SELECT '' AS five, q2, float8(q2) FROM INT8_TBL; SELECT 37 + q1 AS plus4 FROM INT8_TBL; SELECT 37 - q1 AS minus4 FROM INT8_TBL; SELECT '' AS five, 2 * q1 AS "twice int4" FROM INT8_TBL; SELECT '' AS five, q1 * 2 AS "twice int4" FROM INT8_TBL; -- int8 op int4 SELECT q1 + 42::int4 AS "8plus4", q1 - 42::int4 AS "8minus4", q1 * 42::int4 AS "8mul4", q1 / 42::int4 AS "8div4" FROM INT8_TBL; -- int4 op int8 SELECT 246::int4 + q1 AS "4plus8", 246::int4 - q1 AS "4minus8", 246::int4 * q1 AS "4mul8", 246::int4 / q1 AS "4div8" FROM INT8_TBL; -- int8 op int2 SELECT q1 + 42::int2 AS "8plus2", q1 - 42::int2 AS "8minus2", q1 * 42::int2 AS "8mul2", q1 / 42::int2 AS "8div2" FROM INT8_TBL; -- int2 op int8 SELECT 246::int2 + q1 AS "2plus8", 246::int2 - q1 AS "2minus8", 246::int2 * q1 AS "2mul8", 246::int2 / q1 AS "2div8" FROM INT8_TBL; SELECT q2, abs(q2) FROM INT8_TBL; SELECT min(q1), min(q2) FROM INT8_TBL; SELECT max(q1), max(q2) FROM INT8_TBL; -- TO_CHAR() -- SELECT '' AS to_char_1, to_char(q1, '9G999G999G999G999G999'), to_char(q2, '9,999,999,999,999,999') FROM INT8_TBL; SELECT '' AS to_char_2, to_char(q1, '9G999G999G999G999G999D999G999'), to_char(q2, '9,999,999,999,999,999.999,999') FROM INT8_TBL; SELECT '' AS to_char_3, to_char( (q1 * -1), '9999999999999999PR'), to_char( (q2 * -1), '9999999999999999.999PR') FROM INT8_TBL; SELECT '' AS to_char_4, to_char( (q1 * -1), '9999999999999999S'), to_char( (q2 * -1), 'S9999999999999999') FROM INT8_TBL; SELECT '' AS to_char_5, to_char(q2, 'MI9999999999999999') FROM INT8_TBL; SELECT '' AS to_char_6, to_char(q2, 'FMS9999999999999999') FROM INT8_TBL; SELECT '' AS to_char_7, to_char(q2, 'FM9999999999999999THPR') FROM INT8_TBL; SELECT '' AS to_char_8, to_char(q2, 'SG9999999999999999th') FROM INT8_TBL; SELECT '' AS to_char_9, to_char(q2, '0999999999999999') FROM INT8_TBL; SELECT '' AS to_char_10, to_char(q2, 'S0999999999999999') FROM INT8_TBL; SELECT '' AS to_char_11, to_char(q2, 'FM0999999999999999') FROM INT8_TBL; SELECT '' AS to_char_12, to_char(q2, 'FM9999999999999999.000') FROM INT8_TBL; SELECT '' AS to_char_13, to_char(q2, 'L9999999999999999.000') FROM INT8_TBL; SELECT '' AS to_char_14, to_char(q2, 'FM9999999999999999.999') FROM INT8_TBL; SELECT '' AS to_char_15, to_char(q2, 'S 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 . 9 9 9') FROM INT8_TBL; SELECT '' AS to_char_16, to_char(q2, E'99999 "text" 9999 "9999" 999 "\\"text between quote marks\\"" 9999') FROM INT8_TBL; SELECT '' AS to_char_17, to_char(q2, '999999SG9999999999') FROM INT8_TBL; -- check min/max values and overflow behavior select '-9223372036854775808'::int8; select '-9223372036854775809'::int8; select '9223372036854775807'::int8; select '9223372036854775808'::int8; select -('-9223372036854775807'::int8); select -('-9223372036854775808'::int8); select '9223372036854775800'::int8 + '9223372036854775800'::int8; select '-9223372036854775800'::int8 + '-9223372036854775800'::int8; select '9223372036854775800'::int8 - '-9223372036854775800'::int8; select '-9223372036854775800'::int8 - '9223372036854775800'::int8; select '9223372036854775800'::int8 * '9223372036854775800'::int8; select '9223372036854775800'::int8 / '0'::int8; select '9223372036854775800'::int8 % '0'::int8; select abs('-9223372036854775808'::int8); select '9223372036854775800'::int8 + '100'::int4; select '-9223372036854775800'::int8 - '100'::int4; select '9223372036854775800'::int8 * '100'::int4; select '100'::int4 + '9223372036854775800'::int8; select '-100'::int4 - '9223372036854775800'::int8; select '100'::int4 * '9223372036854775800'::int8; select '9223372036854775800'::int8 + '100'::int2; select '-9223372036854775800'::int8 - '100'::int2; select '9223372036854775800'::int8 * '100'::int2; select '-9223372036854775808'::int8 / '0'::int2; select '100'::int2 + '9223372036854775800'::int8; select '-100'::int2 - '9223372036854775800'::int8; select '100'::int2 * '9223372036854775800'::int8; select '100'::int2 / '0'::int8; SELECT CAST(q1 AS int4) FROM int8_tbl WHERE q2 = 456; SELECT CAST(q1 AS int4) FROM int8_tbl WHERE q2 <> 456; SELECT CAST(q1 AS int2) FROM int8_tbl WHERE q2 = 456; SELECT CAST(q1 AS int2) FROM int8_tbl WHERE q2 <> 456; SELECT CAST('42'::int2 AS int8), CAST('-37'::int2 AS int8); SELECT CAST(q1 AS float4), CAST(q2 AS float8) FROM INT8_TBL; SELECT CAST('36854775807.0'::float4 AS int8); SELECT CAST('922337203685477580700.0'::float8 AS int8); SELECT CAST(q1 AS oid) FROM INT8_TBL; SELECT oid::int8 FROM pg_class WHERE relname = 'pg_class'; -- bit operations SELECT q1, q2, q1 & q2 AS "and", q1 | q2 AS "or", q1 # q2 AS "xor", ~q1 AS "not" FROM INT8_TBL; SELECT q1, q1 << 2 AS "shl", q1 >> 3 AS "shr" FROM INT8_TBL; -- generate_series SELECT * FROM generate_series('+4567890123456789'::int8, '+4567890123456799'::int8); SELECT * FROM generate_series('+4567890123456789'::int8, '+4567890123456799'::int8, 0); SELECT * FROM generate_series('+4567890123456789'::int8, '+4567890123456799'::int8, 2); -- corner case SELECT (-1::int8<<63)::text; SELECT ((-1::int8<<63)+1)::text; -- check sane handling of INT64_MIN overflow cases SELECT (-9223372036854775808)::int8 * (-1)::int8; SELECT (-9223372036854775808)::int8 / (-1)::int8; SELECT (-9223372036854775808)::int8 % (-1)::int8; SELECT (-9223372036854775808)::int8 * (-1)::int4; SELECT (-9223372036854775808)::int8 / (-1)::int4; SELECT (-9223372036854775808)::int8 % (-1)::int4; SELECT (-9223372036854775808)::int8 * (-1)::int2; SELECT (-9223372036854775808)::int8 / (-1)::int2; SELECT (-9223372036854775808)::int8 % (-1)::int2; -- check rounding when casting from float SELECT x, x::int8 AS int8_value FROM (VALUES (-2.5::float8), (-1.5::float8), (-0.5::float8), (0.0::float8), (0.5::float8), (1.5::float8), (2.5::float8)) t(x); -- check rounding when casting from numeric SELECT x, x::int8 AS int8_value FROM (VALUES (-2.5::numeric), (-1.5::numeric), (-0.5::numeric), (0.0::numeric), (0.5::numeric), (1.5::numeric), (2.5::numeric)) t(x); pgFormatter-4.2/t/pg-test-files/sql/interval.sql000066400000000000000000000274011361326045100217500ustar00rootroot00000000000000-- -- INTERVAL -- SET DATESTYLE = 'ISO'; SET IntervalStyle to postgres; -- check acceptance of "time zone style" SELECT INTERVAL '01:00' AS "One hour"; SELECT INTERVAL '+02:00' AS "Two hours"; SELECT INTERVAL '-08:00' AS "Eight hours"; SELECT INTERVAL '-1 +02:03' AS "22 hours ago..."; SELECT INTERVAL '-1 days +02:03' AS "22 hours ago..."; SELECT INTERVAL '1.5 weeks' AS "Ten days twelve hours"; SELECT INTERVAL '1.5 months' AS "One month 15 days"; SELECT INTERVAL '10 years -11 month -12 days +13:14' AS "9 years..."; CREATE TABLE INTERVAL_TBL (f1 interval); INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 1 minute'); INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 5 hour'); INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 10 day'); INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 34 year'); INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 3 months'); INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 14 seconds ago'); INSERT INTO INTERVAL_TBL (f1) VALUES ('1 day 2 hours 3 minutes 4 seconds'); INSERT INTO INTERVAL_TBL (f1) VALUES ('6 years'); INSERT INTO INTERVAL_TBL (f1) VALUES ('5 months'); INSERT INTO INTERVAL_TBL (f1) VALUES ('5 months 12 hours'); -- badly formatted interval INSERT INTO INTERVAL_TBL (f1) VALUES ('badly formatted interval'); INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 30 eons ago'); -- test interval operators SELECT '' AS ten, * FROM INTERVAL_TBL; SELECT '' AS nine, * FROM INTERVAL_TBL WHERE INTERVAL_TBL.f1 <> interval '@ 10 days'; SELECT '' AS three, * FROM INTERVAL_TBL WHERE INTERVAL_TBL.f1 <= interval '@ 5 hours'; SELECT '' AS three, * FROM INTERVAL_TBL WHERE INTERVAL_TBL.f1 < interval '@ 1 day'; SELECT '' AS one, * FROM INTERVAL_TBL WHERE INTERVAL_TBL.f1 = interval '@ 34 years'; SELECT '' AS five, * FROM INTERVAL_TBL WHERE INTERVAL_TBL.f1 >= interval '@ 1 month'; SELECT '' AS nine, * FROM INTERVAL_TBL WHERE INTERVAL_TBL.f1 > interval '@ 3 seconds ago'; SELECT '' AS fortyfive, r1.*, r2.* FROM INTERVAL_TBL r1, INTERVAL_TBL r2 WHERE r1.f1 > r2.f1 ORDER BY r1.f1, r2.f1; -- Test intervals that are large enough to overflow 64 bits in comparisons CREATE TEMP TABLE INTERVAL_TBL_OF (f1 interval); INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('2147483647 days 2147483647 months'), ('2147483647 days -2147483648 months'), ('1 year'), ('-2147483648 days 2147483647 months'), ('-2147483648 days -2147483648 months'); -- these should fail as out-of-range INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('2147483648 days'); INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('-2147483649 days'); INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('2147483647 years'); INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('-2147483648 years'); SELECT r1.*, r2.* FROM INTERVAL_TBL_OF r1, INTERVAL_TBL_OF r2 WHERE r1.f1 > r2.f1 ORDER BY r1.f1, r2.f1; CREATE INDEX ON INTERVAL_TBL_OF USING btree (f1); SET enable_seqscan TO false; EXPLAIN (COSTS OFF) SELECT f1 FROM INTERVAL_TBL_OF r1 ORDER BY f1; SELECT f1 FROM INTERVAL_TBL_OF r1 ORDER BY f1; RESET enable_seqscan; DROP TABLE INTERVAL_TBL_OF; -- Test multiplication and division with intervals. -- Floating point arithmetic rounding errors can lead to unexpected results, -- though the code attempts to do the right thing and round up to days and -- minutes to avoid results such as '3 days 24:00 hours' or '14:20:60'. -- Note that it is expected for some day components to be greater than 29 and -- some time components be greater than 23:59:59 due to how intervals are -- stored internally. CREATE TABLE INTERVAL_MULDIV_TBL (span interval); SELECT span * 0.3 AS product FROM INTERVAL_MULDIV_TBL; SELECT span * 8.2 AS product FROM INTERVAL_MULDIV_TBL; SELECT span / 10 AS quotient FROM INTERVAL_MULDIV_TBL; SELECT span / 100 AS quotient FROM INTERVAL_MULDIV_TBL; DROP TABLE INTERVAL_MULDIV_TBL; SET DATESTYLE = 'postgres'; SET IntervalStyle to postgres_verbose; SELECT '' AS ten, * FROM INTERVAL_TBL; -- test avg(interval), which is somewhat fragile since people have been -- known to change the allowed input syntax for type interval without -- updating pg_aggregate.agginitval select avg(f1) from interval_tbl; -- test long interval input select '4 millenniums 5 centuries 4 decades 1 year 4 months 4 days 17 minutes 31 seconds'::interval; -- test long interval output -- Note: the actual maximum length of the interval output is longer, -- but we need the test to work for both integer and floating-point -- timestamps. select '100000000y 10mon -1000000000d -100000h -10min -10.000001s ago'::interval; -- test justify_hours() and justify_days() SELECT justify_hours(interval '6 months 3 days 52 hours 3 minutes 2 seconds') as "6 mons 5 days 4 hours 3 mins 2 seconds"; SELECT justify_days(interval '6 months 36 days 5 hours 4 minutes 3 seconds') as "7 mons 6 days 5 hours 4 mins 3 seconds"; -- test justify_interval() SELECT justify_interval(interval '1 month -1 hour') as "1 month -1 hour"; -- test fractional second input, and detection of duplicate units SET DATESTYLE = 'ISO'; SET IntervalStyle TO postgres; SELECT '1 millisecond'::interval, '1 microsecond'::interval, '500 seconds 99 milliseconds 51 microseconds'::interval; SELECT '3 days 5 milliseconds'::interval; SELECT '1 second 2 seconds'::interval; -- error SELECT '10 milliseconds 20 milliseconds'::interval; -- error SELECT '5.5 seconds 3 milliseconds'::interval; -- error SELECT '1:20:05 5 microseconds'::interval; -- error SELECT '1 day 1 day'::interval; -- error SELECT interval '1-2'; -- SQL year-month literal SELECT interval '999' second; -- oversize leading field is ok SELECT interval '999' minute; SELECT interval '999' hour; SELECT interval '999' day; SELECT interval '999' month; -- test SQL-spec syntaxes for restricted field sets SELECT interval '1' year; SELECT interval '2' month; SELECT interval '3' day; SELECT interval '4' hour; SELECT interval '5' minute; SELECT interval '6' second; SELECT interval '1' year to month; SELECT interval '1-2' year to month; SELECT interval '1 2' day to hour; SELECT interval '1 2:03' day to hour; SELECT interval '1 2:03:04' day to hour; SELECT interval '1 2' day to minute; SELECT interval '1 2:03' day to minute; SELECT interval '1 2:03:04' day to minute; SELECT interval '1 2' day to second; SELECT interval '1 2:03' day to second; SELECT interval '1 2:03:04' day to second; SELECT interval '1 2' hour to minute; SELECT interval '1 2:03' hour to minute; SELECT interval '1 2:03:04' hour to minute; SELECT interval '1 2' hour to second; SELECT interval '1 2:03' hour to second; SELECT interval '1 2:03:04' hour to second; SELECT interval '1 2' minute to second; SELECT interval '1 2:03' minute to second; SELECT interval '1 2:03:04' minute to second; SELECT interval '1 +2:03' minute to second; SELECT interval '1 +2:03:04' minute to second; SELECT interval '1 -2:03' minute to second; SELECT interval '1 -2:03:04' minute to second; SELECT interval '123 11' day to hour; -- ok SELECT interval '123 11' day; -- not ok SELECT interval '123 11'; -- not ok, too ambiguous SELECT interval '123 2:03 -2:04'; -- not ok, redundant hh:mm fields -- test syntaxes for restricted precision SELECT interval(0) '1 day 01:23:45.6789'; SELECT interval(2) '1 day 01:23:45.6789'; SELECT interval '12:34.5678' minute to second(2); -- per SQL spec SELECT interval '1.234' second; SELECT interval '1.234' second(2); SELECT interval '1 2.345' day to second(2); SELECT interval '1 2:03' day to second(2); SELECT interval '1 2:03.4567' day to second(2); SELECT interval '1 2:03:04.5678' day to second(2); SELECT interval '1 2.345' hour to second(2); SELECT interval '1 2:03.45678' hour to second(2); SELECT interval '1 2:03:04.5678' hour to second(2); SELECT interval '1 2.3456' minute to second(2); SELECT interval '1 2:03.5678' minute to second(2); SELECT interval '1 2:03:04.5678' minute to second(2); -- test casting to restricted precision (bug #14479) SELECT f1, f1::INTERVAL DAY TO MINUTE AS "minutes", (f1 + INTERVAL '1 month')::INTERVAL MONTH::INTERVAL YEAR AS "years" FROM interval_tbl; -- test inputting and outputting SQL standard interval literals SET IntervalStyle TO sql_standard; SELECT interval '0' AS "zero", interval '1-2' year to month AS "year-month", interval '1 2:03:04' day to second AS "day-time", - interval '1-2' AS "negative year-month", - interval '1 2:03:04' AS "negative day-time"; -- test input of some not-quite-standard interval values in the sql style SET IntervalStyle TO postgres; SELECT interval '+1 -1:00:00', interval '-1 +1:00:00', interval '+1-2 -3 +4:05:06.789', interval '-1-2 +3 -4:05:06.789'; -- test output of couple non-standard interval values in the sql style SET IntervalStyle TO sql_standard; SELECT interval '1 day -1 hours', interval '-1 days +1 hours', interval '1 years 2 months -3 days 4 hours 5 minutes 6.789 seconds', - interval '1 years 2 months -3 days 4 hours 5 minutes 6.789 seconds'; -- test outputting iso8601 intervals SET IntervalStyle to iso_8601; select interval '0' AS "zero", interval '1-2' AS "a year 2 months", interval '1 2:03:04' AS "a bit over a day", interval '2:03:04.45679' AS "a bit over 2 hours", (interval '1-2' + interval '3 4:05:06.7') AS "all fields", (interval '1-2' - interval '3 4:05:06.7') AS "mixed sign", (- interval '1-2' + interval '3 4:05:06.7') AS "negative"; -- test inputting ISO 8601 4.4.2.1 "Format With Time Unit Designators" SET IntervalStyle to sql_standard; select interval 'P0Y' AS "zero", interval 'P1Y2M' AS "a year 2 months", interval 'P1W' AS "a week", interval 'P1DT2H3M4S' AS "a bit over a day", interval 'P1Y2M3DT4H5M6.7S' AS "all fields", interval 'P-1Y-2M-3DT-4H-5M-6.7S' AS "negative", interval 'PT-0.1S' AS "fractional second"; -- test inputting ISO 8601 4.4.2.2 "Alternative Format" SET IntervalStyle to postgres; select interval 'P00021015T103020' AS "ISO8601 Basic Format", interval 'P0002-10-15T10:30:20' AS "ISO8601 Extended Format"; -- Make sure optional ISO8601 alternative format fields are optional. select interval 'P0002' AS "year only", interval 'P0002-10' AS "year month", interval 'P0002-10-15' AS "year month day", interval 'P0002T1S' AS "year only plus time", interval 'P0002-10T1S' AS "year month plus time", interval 'P0002-10-15T1S' AS "year month day plus time", interval 'PT10' AS "hour only", interval 'PT10:30' AS "hour minute"; -- test a couple rounding cases that changed since 8.3 w/ HAVE_INT64_TIMESTAMP. SET IntervalStyle to postgres_verbose; select interval '-10 mons -3 days +03:55:06.70'; select interval '1 year 2 mons 3 days 04:05:06.699999'; select interval '0:0:0.7', interval '@ 0.70 secs', interval '0.7 seconds'; -- check that '30 days' equals '1 month' according to the hash function select '30 days'::interval = '1 month'::interval as t; select interval_hash('30 days'::interval) = interval_hash('1 month'::interval) as t; -- numeric constructor select make_interval(years := 2); select make_interval(years := 1, months := 6); select make_interval(years := 1, months := -1, weeks := 5, days := -7, hours := 25, mins := -180); select make_interval() = make_interval(years := 0, months := 0, weeks := 0, days := 0, mins := 0, secs := 0.0); select make_interval(hours := -2, mins := -10, secs := -25.3); select make_interval(years := 'inf'::float::int); select make_interval(months := 'NaN'::float::int); select make_interval(secs := 'inf'); select make_interval(secs := 'NaN'); select make_interval(secs := 7e12); pgFormatter-4.2/t/pg-test-files/sql/join.sql000066400000000000000000001663241361326045100210730ustar00rootroot00000000000000-- -- JOIN -- Test JOIN clauses -- CREATE TABLE J1_TBL ( i integer, j integer, t text ); CREATE TABLE J2_TBL ( i integer, k integer ); INSERT INTO J1_TBL VALUES (1, 4, 'one'); INSERT INTO J1_TBL VALUES (2, 3, 'two'); INSERT INTO J1_TBL VALUES (3, 2, 'three'); INSERT INTO J1_TBL VALUES (4, 1, 'four'); INSERT INTO J1_TBL VALUES (5, 0, 'five'); INSERT INTO J1_TBL VALUES (6, 6, 'six'); INSERT INTO J1_TBL VALUES (7, 7, 'seven'); INSERT INTO J1_TBL VALUES (8, 8, 'eight'); INSERT INTO J1_TBL VALUES (0, NULL, 'zero'); INSERT INTO J1_TBL VALUES (NULL, NULL, 'null'); INSERT INTO J1_TBL VALUES (NULL, 0, 'zero'); INSERT INTO J2_TBL VALUES (1, -1); INSERT INTO J2_TBL VALUES (2, 2); INSERT INTO J2_TBL VALUES (3, -3); INSERT INTO J2_TBL VALUES (2, 4); INSERT INTO J2_TBL VALUES (5, -5); INSERT INTO J2_TBL VALUES (5, -5); INSERT INTO J2_TBL VALUES (0, NULL); INSERT INTO J2_TBL VALUES (NULL, NULL); INSERT INTO J2_TBL VALUES (NULL, 0); -- useful in some tests below create temp table onerow(); insert into onerow default values; analyze onerow; -- -- CORRELATION NAMES -- Make sure that table/column aliases are supported -- before diving into more complex join syntax. -- SELECT '' AS "xxx", * FROM J1_TBL AS tx; SELECT '' AS "xxx", * FROM J1_TBL tx; SELECT '' AS "xxx", * FROM J1_TBL AS t1 (a, b, c); SELECT '' AS "xxx", * FROM J1_TBL t1 (a, b, c); SELECT '' AS "xxx", * FROM J1_TBL t1 (a, b, c), J2_TBL t2 (d, e); SELECT '' AS "xxx", t1.a, t2.e FROM J1_TBL t1 (a, b, c), J2_TBL t2 (d, e) WHERE t1.a = t2.d; -- -- CROSS JOIN -- Qualifications are not allowed on cross joins, -- which degenerate into a standard unqualified inner join. -- SELECT '' AS "xxx", * FROM J1_TBL CROSS JOIN J2_TBL; -- ambiguous column SELECT '' AS "xxx", i, k, t FROM J1_TBL CROSS JOIN J2_TBL; -- resolve previous ambiguity by specifying the table name SELECT '' AS "xxx", t1.i, k, t FROM J1_TBL t1 CROSS JOIN J2_TBL t2; SELECT '' AS "xxx", ii, tt, kk FROM (J1_TBL CROSS JOIN J2_TBL) AS tx (ii, jj, tt, ii2, kk); SELECT '' AS "xxx", tx.ii, tx.jj, tx.kk FROM (J1_TBL t1 (a, b, c) CROSS JOIN J2_TBL t2 (d, e)) AS tx (ii, jj, tt, ii2, kk); SELECT '' AS "xxx", * FROM J1_TBL CROSS JOIN J2_TBL a CROSS JOIN J2_TBL b; -- -- -- Inner joins (equi-joins) -- -- -- -- Inner joins (equi-joins) with USING clause -- The USING syntax changes the shape of the resulting table -- by including a column in the USING clause only once in the result. -- -- Inner equi-join on specified column SELECT '' AS "xxx", * FROM J1_TBL INNER JOIN J2_TBL USING (i); -- Same as above, slightly different syntax SELECT '' AS "xxx", * FROM J1_TBL JOIN J2_TBL USING (i); SELECT '' AS "xxx", * FROM J1_TBL t1 (a, b, c) JOIN J2_TBL t2 (a, d) USING (a) ORDER BY a, d; SELECT '' AS "xxx", * FROM J1_TBL t1 (a, b, c) JOIN J2_TBL t2 (a, b) USING (b) ORDER BY b, t1.a; -- -- NATURAL JOIN -- Inner equi-join on all columns with the same name -- SELECT '' AS "xxx", * FROM J1_TBL NATURAL JOIN J2_TBL; SELECT '' AS "xxx", * FROM J1_TBL t1 (a, b, c) NATURAL JOIN J2_TBL t2 (a, d); SELECT '' AS "xxx", * FROM J1_TBL t1 (a, b, c) NATURAL JOIN J2_TBL t2 (d, a); -- mismatch number of columns -- currently, Postgres will fill in with underlying names SELECT '' AS "xxx", * FROM J1_TBL t1 (a, b) NATURAL JOIN J2_TBL t2 (a); -- -- Inner joins (equi-joins) -- SELECT '' AS "xxx", * FROM J1_TBL JOIN J2_TBL ON (J1_TBL.i = J2_TBL.i); SELECT '' AS "xxx", * FROM J1_TBL JOIN J2_TBL ON (J1_TBL.i = J2_TBL.k); -- -- Non-equi-joins -- SELECT '' AS "xxx", * FROM J1_TBL JOIN J2_TBL ON (J1_TBL.i <= J2_TBL.k); -- -- Outer joins -- Note that OUTER is a noise word -- SELECT '' AS "xxx", * FROM J1_TBL LEFT OUTER JOIN J2_TBL USING (i) ORDER BY i, k, t; SELECT '' AS "xxx", * FROM J1_TBL LEFT JOIN J2_TBL USING (i) ORDER BY i, k, t; SELECT '' AS "xxx", * FROM J1_TBL RIGHT OUTER JOIN J2_TBL USING (i); SELECT '' AS "xxx", * FROM J1_TBL RIGHT JOIN J2_TBL USING (i); SELECT '' AS "xxx", * FROM J1_TBL FULL OUTER JOIN J2_TBL USING (i) ORDER BY i, k, t; SELECT '' AS "xxx", * FROM J1_TBL FULL JOIN J2_TBL USING (i) ORDER BY i, k, t; SELECT '' AS "xxx", * FROM J1_TBL LEFT JOIN J2_TBL USING (i) WHERE (k = 1); SELECT '' AS "xxx", * FROM J1_TBL LEFT JOIN J2_TBL USING (i) WHERE (i = 1); -- -- semijoin selectivity for <> -- explain (costs off) select * from int4_tbl i4, tenk1 a where exists(select * from tenk1 b where a.twothousand = b.twothousand and a.fivethous <> b.fivethous) and i4.f1 = a.tenthous; -- -- More complicated constructs -- -- -- Multiway full join -- CREATE TABLE t1 (name TEXT, n INTEGER); CREATE TABLE t2 (name TEXT, n INTEGER); CREATE TABLE t3 (name TEXT, n INTEGER); INSERT INTO t1 VALUES ( 'bb', 11 ); INSERT INTO t2 VALUES ( 'bb', 12 ); INSERT INTO t2 VALUES ( 'cc', 22 ); INSERT INTO t2 VALUES ( 'ee', 42 ); INSERT INTO t3 VALUES ( 'bb', 13 ); INSERT INTO t3 VALUES ( 'cc', 23 ); INSERT INTO t3 VALUES ( 'dd', 33 ); SELECT * FROM t1 FULL JOIN t2 USING (name) FULL JOIN t3 USING (name); -- -- Test interactions of join syntax and subqueries -- -- Basic cases (we expect planner to pull up the subquery here) SELECT * FROM (SELECT * FROM t2) as s2 INNER JOIN (SELECT * FROM t3) s3 USING (name); SELECT * FROM (SELECT * FROM t2) as s2 LEFT JOIN (SELECT * FROM t3) s3 USING (name); SELECT * FROM (SELECT * FROM t2) as s2 FULL JOIN (SELECT * FROM t3) s3 USING (name); -- Cases with non-nullable expressions in subquery results; -- make sure these go to null as expected SELECT * FROM (SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2 NATURAL INNER JOIN (SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3; SELECT * FROM (SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2 NATURAL LEFT JOIN (SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3; SELECT * FROM (SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2 NATURAL FULL JOIN (SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3; SELECT * FROM (SELECT name, n as s1_n, 1 as s1_1 FROM t1) as s1 NATURAL INNER JOIN (SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2 NATURAL INNER JOIN (SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3; SELECT * FROM (SELECT name, n as s1_n, 1 as s1_1 FROM t1) as s1 NATURAL FULL JOIN (SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2 NATURAL FULL JOIN (SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3; SELECT * FROM (SELECT name, n as s1_n FROM t1) as s1 NATURAL FULL JOIN (SELECT * FROM (SELECT name, n as s2_n FROM t2) as s2 NATURAL FULL JOIN (SELECT name, n as s3_n FROM t3) as s3 ) ss2; SELECT * FROM (SELECT name, n as s1_n FROM t1) as s1 NATURAL FULL JOIN (SELECT * FROM (SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2 NATURAL FULL JOIN (SELECT name, n as s3_n FROM t3) as s3 ) ss2; -- Constants as join keys can also be problematic SELECT * FROM (SELECT name, n as s1_n FROM t1) as s1 FULL JOIN (SELECT name, 2 as s2_n FROM t2) as s2 ON (s1_n = s2_n); -- Test for propagation of nullability constraints into sub-joins create temp table x (x1 int, x2 int); insert into x values (1,11); insert into x values (2,22); insert into x values (3,null); insert into x values (4,44); insert into x values (5,null); create temp table y (y1 int, y2 int); insert into y values (1,111); insert into y values (2,222); insert into y values (3,333); insert into y values (4,null); select * from x; select * from y; select * from x left join y on (x1 = y1 and x2 is not null); select * from x left join y on (x1 = y1 and y2 is not null); select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2) on (x1 = xx1); select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2) on (x1 = xx1 and x2 is not null); select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2) on (x1 = xx1 and y2 is not null); select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2) on (x1 = xx1 and xx2 is not null); -- these should NOT give the same answers as above select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2) on (x1 = xx1) where (x2 is not null); select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2) on (x1 = xx1) where (y2 is not null); select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2) on (x1 = xx1) where (xx2 is not null); -- -- regression test: check for bug with propagation of implied equality -- to outside an IN -- select count(*) from tenk1 a where unique1 in (select unique1 from tenk1 b join tenk1 c using (unique1) where b.unique2 = 42); -- -- regression test: check for failure to generate a plan with multiple -- degenerate IN clauses -- select count(*) from tenk1 x where x.unique1 in (select a.f1 from int4_tbl a,float8_tbl b where a.f1=b.f1) and x.unique1 = 0 and x.unique1 in (select aa.f1 from int4_tbl aa,float8_tbl bb where aa.f1=bb.f1); -- try that with GEQO too begin; set geqo = on; set geqo_threshold = 2; select count(*) from tenk1 x where x.unique1 in (select a.f1 from int4_tbl a,float8_tbl b where a.f1=b.f1) and x.unique1 = 0 and x.unique1 in (select aa.f1 from int4_tbl aa,float8_tbl bb where aa.f1=bb.f1); rollback; -- -- regression test: be sure we cope with proven-dummy append rels -- explain (costs off) select aa, bb, unique1, unique1 from tenk1 right join b on aa = unique1 where bb < bb and bb is null; select aa, bb, unique1, unique1 from tenk1 right join b on aa = unique1 where bb < bb and bb is null; -- -- regression test: check handling of empty-FROM subquery underneath outer join -- explain (costs off) select * from int8_tbl i1 left join (int8_tbl i2 join (select 123 as x) ss on i2.q1 = x) on i1.q2 = i2.q2 order by 1, 2; select * from int8_tbl i1 left join (int8_tbl i2 join (select 123 as x) ss on i2.q1 = x) on i1.q2 = i2.q2 order by 1, 2; -- -- regression test: check a case where join_clause_is_movable_into() gives -- an imprecise result, causing an assertion failure -- select count(*) from (select t3.tenthous as x1, coalesce(t1.stringu1, t2.stringu1) as x2 from tenk1 t1 left join tenk1 t2 on t1.unique1 = t2.unique1 join tenk1 t3 on t1.unique2 = t3.unique2) ss, tenk1 t4, tenk1 t5 where t4.thousand = t5.unique1 and ss.x1 = t4.tenthous and ss.x2 = t5.stringu1; -- -- regression test: check a case where we formerly missed including an EC -- enforcement clause because it was expected to be handled at scan level -- explain (costs off) select a.f1, b.f1, t.thousand, t.tenthous from tenk1 t, (select sum(f1)+1 as f1 from int4_tbl i4a) a, (select sum(f1) as f1 from int4_tbl i4b) b where b.f1 = t.thousand and a.f1 = b.f1 and (a.f1+b.f1+999) = t.tenthous; select a.f1, b.f1, t.thousand, t.tenthous from tenk1 t, (select sum(f1)+1 as f1 from int4_tbl i4a) a, (select sum(f1) as f1 from int4_tbl i4b) b where b.f1 = t.thousand and a.f1 = b.f1 and (a.f1+b.f1+999) = t.tenthous; -- -- check a case where we formerly got confused by conflicting sort orders -- in redundant merge join path keys -- explain (costs off) select * from j1_tbl full join (select * from j2_tbl order by j2_tbl.i desc, j2_tbl.k asc) j2_tbl on j1_tbl.i = j2_tbl.i and j1_tbl.i = j2_tbl.k; select * from j1_tbl full join (select * from j2_tbl order by j2_tbl.i desc, j2_tbl.k asc) j2_tbl on j1_tbl.i = j2_tbl.i and j1_tbl.i = j2_tbl.k; -- -- a different check for handling of redundant sort keys in merge joins -- explain (costs off) select count(*) from (select * from tenk1 x order by x.thousand, x.twothousand, x.fivethous) x left join (select * from tenk1 y order by y.unique2) y on x.thousand = y.unique2 and x.twothousand = y.hundred and x.fivethous = y.unique2; select count(*) from (select * from tenk1 x order by x.thousand, x.twothousand, x.fivethous) x left join (select * from tenk1 y order by y.unique2) y on x.thousand = y.unique2 and x.twothousand = y.hundred and x.fivethous = y.unique2; -- -- Clean up -- DROP TABLE t1; DROP TABLE t2; DROP TABLE t3; DROP TABLE J1_TBL; DROP TABLE J2_TBL; -- Both DELETE and UPDATE allow the specification of additional tables -- to "join" against to determine which rows should be modified. CREATE TEMP TABLE t1 (a int, b int); CREATE TEMP TABLE t2 (a int, b int); CREATE TEMP TABLE t3 (x int, y int); INSERT INTO t1 VALUES (5, 10); INSERT INTO t1 VALUES (15, 20); INSERT INTO t1 VALUES (100, 100); INSERT INTO t1 VALUES (200, 1000); INSERT INTO t2 VALUES (200, 2000); INSERT INTO t3 VALUES (5, 20); INSERT INTO t3 VALUES (6, 7); INSERT INTO t3 VALUES (7, 8); INSERT INTO t3 VALUES (500, 100); DELETE FROM t3 USING t1 table1 WHERE t3.x = table1.a; SELECT * FROM t3; DELETE FROM t3 USING t1 JOIN t2 USING (a) WHERE t3.x > t1.a; SELECT * FROM t3; DELETE FROM t3 USING t3 t3_other WHERE t3.x = t3_other.x AND t3.y = t3_other.y; SELECT * FROM t3; -- Test join against inheritance tree create temp table t2a () inherits (t2); insert into t2a values (200, 2001); select * from t1 left join t2 on (t1.a = t2.a); -- Test matching of column name with wrong alias select t1.x from t1 join t3 on (t1.a = t3.x); -- -- regression test for 8.1 merge right join bug -- CREATE TEMP TABLE tt1 ( tt1_id int4, joincol int4 ); INSERT INTO tt1 VALUES (1, 11); INSERT INTO tt1 VALUES (2, NULL); CREATE TEMP TABLE tt2 ( tt2_id int4, joincol int4 ); INSERT INTO tt2 VALUES (21, 11); INSERT INTO tt2 VALUES (22, 11); set enable_hashjoin to off; set enable_nestloop to off; -- these should give the same results select tt1.*, tt2.* from tt1 left join tt2 on tt1.joincol = tt2.joincol; select tt1.*, tt2.* from tt2 right join tt1 on tt1.joincol = tt2.joincol; reset enable_hashjoin; reset enable_nestloop; -- -- regression test for bug #13908 (hash join with skew tuples & nbatch increase) -- set work_mem to '64kB'; set enable_mergejoin to off; explain (costs off) select count(*) from tenk1 a, tenk1 b where a.hundred = b.thousand and (b.fivethous % 10) < 10; select count(*) from tenk1 a, tenk1 b where a.hundred = b.thousand and (b.fivethous % 10) < 10; reset work_mem; reset enable_mergejoin; -- -- regression test for 8.2 bug with improper re-ordering of left joins -- create temp table tt3(f1 int, f2 text); insert into tt3 select x, repeat('xyzzy', 100) from generate_series(1,10000) x; create index tt3i on tt3(f1); analyze tt3; create temp table tt4(f1 int); insert into tt4 values (0),(1),(9999); analyze tt4; SELECT a.f1 FROM tt4 a LEFT JOIN ( SELECT b.f1 FROM tt3 b LEFT JOIN tt3 c ON (b.f1 = c.f1) WHERE c.f1 IS NULL ) AS d ON (a.f1 = d.f1) WHERE d.f1 IS NULL; -- -- regression test for proper handling of outer joins within antijoins -- create temp table tt4x(c1 int, c2 int, c3 int); explain (costs off) select * from tt4x t1 where not exists ( select 1 from tt4x t2 left join tt4x t3 on t2.c3 = t3.c1 left join ( select t5.c1 as c1 from tt4x t4 left join tt4x t5 on t4.c2 = t5.c1 ) a1 on t3.c2 = a1.c1 where t1.c1 = t2.c2 ); -- -- regression test for problems of the sort depicted in bug #3494 -- create temp table tt5(f1 int, f2 int); create temp table tt6(f1 int, f2 int); insert into tt5 values(1, 10); insert into tt5 values(1, 11); insert into tt6 values(1, 9); insert into tt6 values(1, 2); insert into tt6 values(2, 9); select * from tt5,tt6 where tt5.f1 = tt6.f1 and tt5.f1 = tt5.f2 - tt6.f2; -- -- regression test for problems of the sort depicted in bug #3588 -- create temp table xx (pkxx int); create temp table yy (pkyy int, pkxx int); insert into xx values (1); insert into xx values (2); insert into xx values (3); insert into yy values (101, 1); insert into yy values (201, 2); insert into yy values (301, NULL); select yy.pkyy as yy_pkyy, yy.pkxx as yy_pkxx, yya.pkyy as yya_pkyy, xxa.pkxx as xxa_pkxx, xxb.pkxx as xxb_pkxx from yy left join (SELECT * FROM yy where pkyy = 101) as yya ON yy.pkyy = yya.pkyy left join xx xxa on yya.pkxx = xxa.pkxx left join xx xxb on coalesce (xxa.pkxx, 1) = xxb.pkxx; -- -- regression test for improper pushing of constants across outer-join clauses -- (as seen in early 8.2.x releases) -- create temp table zt1 (f1 int primary key); create temp table zt2 (f2 int primary key); create temp table zt3 (f3 int primary key); insert into zt1 values(53); insert into zt2 values(53); select * from zt2 left join zt3 on (f2 = f3) left join zt1 on (f3 = f1) where f2 = 53; create temp view zv1 as select *,'dummy'::text AS junk from zt1; select * from zt2 left join zt3 on (f2 = f3) left join zv1 on (f3 = f1) where f2 = 53; -- -- regression test for improper extraction of OR indexqual conditions -- (as seen in early 8.3.x releases) -- select a.unique2, a.ten, b.tenthous, b.unique2, b.hundred from tenk1 a left join tenk1 b on a.unique2 = b.tenthous where a.unique1 = 42 and ((b.unique2 is null and a.ten = 2) or b.hundred = 3); -- -- test proper positioning of one-time quals in EXISTS (8.4devel bug) -- prepare foo(bool) as select count(*) from tenk1 a left join tenk1 b on (a.unique2 = b.unique1 and exists (select 1 from tenk1 c where c.thousand = b.unique2 and $1)); execute foo(true); execute foo(false); -- -- test for sane behavior with noncanonical merge clauses, per bug #4926 -- begin; set enable_mergejoin = 1; set enable_hashjoin = 0; set enable_nestloop = 0; create temp table a (i integer); create temp table b (x integer, y integer); select * from a left join b on i = x and i = y and x = i; rollback; -- -- test handling of merge clauses using record_ops -- begin; create type mycomptype as (id int, v bigint); create temp table tidv (idv mycomptype); create index on tidv (idv); explain (costs off) select a.idv, b.idv from tidv a, tidv b where a.idv = b.idv; set enable_mergejoin = 0; explain (costs off) select a.idv, b.idv from tidv a, tidv b where a.idv = b.idv; rollback; -- -- test NULL behavior of whole-row Vars, per bug #5025 -- select t1.q2, count(t2.*) from int8_tbl t1 left join int8_tbl t2 on (t1.q2 = t2.q1) group by t1.q2 order by 1; select t1.q2, count(t2.*) from int8_tbl t1 left join (select * from int8_tbl) t2 on (t1.q2 = t2.q1) group by t1.q2 order by 1; select t1.q2, count(t2.*) from int8_tbl t1 left join (select * from int8_tbl offset 0) t2 on (t1.q2 = t2.q1) group by t1.q2 order by 1; select t1.q2, count(t2.*) from int8_tbl t1 left join (select q1, case when q2=1 then 1 else q2 end as q2 from int8_tbl) t2 on (t1.q2 = t2.q1) group by t1.q2 order by 1; -- -- test incorrect failure to NULL pulled-up subexpressions -- begin; create temp table a ( code char not null, constraint a_pk primary key (code) ); create temp table b ( a char not null, num integer not null, constraint b_pk primary key (a, num) ); create temp table c ( name char not null, a char, constraint c_pk primary key (name) ); insert into a (code) values ('p'); insert into a (code) values ('q'); insert into b (a, num) values ('p', 1); insert into b (a, num) values ('p', 2); insert into c (name, a) values ('A', 'p'); insert into c (name, a) values ('B', 'q'); insert into c (name, a) values ('C', null); select c.name, ss.code, ss.b_cnt, ss.const from c left join (select a.code, coalesce(b_grp.cnt, 0) as b_cnt, -1 as const from a left join (select count(1) as cnt, b.a from b group by b.a) as b_grp on a.code = b_grp.a ) as ss on (c.a = ss.code) order by c.name; rollback; -- -- test incorrect handling of placeholders that only appear in targetlists, -- per bug #6154 -- SELECT * FROM ( SELECT 1 as key1 ) sub1 LEFT JOIN ( SELECT sub3.key3, sub4.value2, COALESCE(sub4.value2, 66) as value3 FROM ( SELECT 1 as key3 ) sub3 LEFT JOIN ( SELECT sub5.key5, COALESCE(sub6.value1, 1) as value2 FROM ( SELECT 1 as key5 ) sub5 LEFT JOIN ( SELECT 2 as key6, 42 as value1 ) sub6 ON sub5.key5 = sub6.key6 ) sub4 ON sub4.key5 = sub3.key3 ) sub2 ON sub1.key1 = sub2.key3; -- test the path using join aliases, too SELECT * FROM ( SELECT 1 as key1 ) sub1 LEFT JOIN ( SELECT sub3.key3, value2, COALESCE(value2, 66) as value3 FROM ( SELECT 1 as key3 ) sub3 LEFT JOIN ( SELECT sub5.key5, COALESCE(sub6.value1, 1) as value2 FROM ( SELECT 1 as key5 ) sub5 LEFT JOIN ( SELECT 2 as key6, 42 as value1 ) sub6 ON sub5.key5 = sub6.key6 ) sub4 ON sub4.key5 = sub3.key3 ) sub2 ON sub1.key1 = sub2.key3; -- -- test case where a PlaceHolderVar is used as a nestloop parameter -- EXPLAIN (COSTS OFF) SELECT qq, unique1 FROM ( SELECT COALESCE(q1, 0) AS qq FROM int8_tbl a ) AS ss1 FULL OUTER JOIN ( SELECT COALESCE(q2, -1) AS qq FROM int8_tbl b ) AS ss2 USING (qq) INNER JOIN tenk1 c ON qq = unique2; SELECT qq, unique1 FROM ( SELECT COALESCE(q1, 0) AS qq FROM int8_tbl a ) AS ss1 FULL OUTER JOIN ( SELECT COALESCE(q2, -1) AS qq FROM int8_tbl b ) AS ss2 USING (qq) INNER JOIN tenk1 c ON qq = unique2; -- -- nested nestloops can require nested PlaceHolderVars -- create temp table nt1 ( id int primary key, a1 boolean, a2 boolean ); create temp table nt2 ( id int primary key, nt1_id int, b1 boolean, b2 boolean, foreign key (nt1_id) references nt1(id) ); create temp table nt3 ( id int primary key, nt2_id int, c1 boolean, foreign key (nt2_id) references nt2(id) ); insert into nt1 values (1,true,true); insert into nt1 values (2,true,false); insert into nt1 values (3,false,false); insert into nt2 values (1,1,true,true); insert into nt2 values (2,2,true,false); insert into nt2 values (3,3,false,false); insert into nt3 values (1,1,true); insert into nt3 values (2,2,false); insert into nt3 values (3,3,true); explain (costs off) select nt3.id from nt3 as nt3 left join (select nt2.*, (nt2.b1 and ss1.a3) AS b3 from nt2 as nt2 left join (select nt1.*, (nt1.id is not null) as a3 from nt1) as ss1 on ss1.id = nt2.nt1_id ) as ss2 on ss2.id = nt3.nt2_id where nt3.id = 1 and ss2.b3; select nt3.id from nt3 as nt3 left join (select nt2.*, (nt2.b1 and ss1.a3) AS b3 from nt2 as nt2 left join (select nt1.*, (nt1.id is not null) as a3 from nt1) as ss1 on ss1.id = nt2.nt1_id ) as ss2 on ss2.id = nt3.nt2_id where nt3.id = 1 and ss2.b3; -- -- test case where a PlaceHolderVar is propagated into a subquery -- explain (costs off) select * from int8_tbl t1 left join (select q1 as x, 42 as y from int8_tbl t2) ss on t1.q2 = ss.x where 1 = (select 1 from int8_tbl t3 where ss.y is not null limit 1) order by 1,2; select * from int8_tbl t1 left join (select q1 as x, 42 as y from int8_tbl t2) ss on t1.q2 = ss.x where 1 = (select 1 from int8_tbl t3 where ss.y is not null limit 1) order by 1,2; -- -- test the corner cases FULL JOIN ON TRUE and FULL JOIN ON FALSE -- select * from int4_tbl a full join int4_tbl b on true; select * from int4_tbl a full join int4_tbl b on false; -- -- test for ability to use a cartesian join when necessary -- explain (costs off) select * from tenk1 join int4_tbl on f1 = twothousand, int4(sin(1)) q1, int4(sin(0)) q2 where q1 = thousand or q2 = thousand; explain (costs off) select * from tenk1 join int4_tbl on f1 = twothousand, int4(sin(1)) q1, int4(sin(0)) q2 where thousand = (q1 + q2); -- -- test ability to generate a suitable plan for a star-schema query -- explain (costs off) select * from tenk1, int8_tbl a, int8_tbl b where thousand = a.q1 and tenthous = b.q1 and a.q2 = 1 and b.q2 = 2; -- -- test a corner case in which we shouldn't apply the star-schema optimization -- explain (costs off) select t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 from tenk1 t1 inner join int4_tbl i1 left join (select v1.x2, v2.y1, 11 AS d1 from (select 1,0 from onerow) v1(x1,x2) left join (select 3,1 from onerow) v2(y1,y2) on v1.x1 = v2.y2) subq1 on (i1.f1 = subq1.x2) on (t1.unique2 = subq1.d1) left join tenk1 t2 on (subq1.y1 = t2.unique1) where t1.unique2 < 42 and t1.stringu1 > t2.stringu2; select t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 from tenk1 t1 inner join int4_tbl i1 left join (select v1.x2, v2.y1, 11 AS d1 from (select 1,0 from onerow) v1(x1,x2) left join (select 3,1 from onerow) v2(y1,y2) on v1.x1 = v2.y2) subq1 on (i1.f1 = subq1.x2) on (t1.unique2 = subq1.d1) left join tenk1 t2 on (subq1.y1 = t2.unique1) where t1.unique2 < 42 and t1.stringu1 > t2.stringu2; -- variant that isn't quite a star-schema case select ss1.d1 from tenk1 as t1 inner join tenk1 as t2 on t1.tenthous = t2.ten inner join int8_tbl as i8 left join int4_tbl as i4 inner join (select 64::information_schema.cardinal_number as d1 from tenk1 t3, lateral (select abs(t3.unique1) + random()) ss0(x) where t3.fivethous < 0) as ss1 on i4.f1 = ss1.d1 on i8.q1 = i4.f1 on t1.tenthous = ss1.d1 where t1.unique1 < i4.f1; -- this variant is foldable by the remove-useless-RESULT-RTEs code explain (costs off) select t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 from tenk1 t1 inner join int4_tbl i1 left join (select v1.x2, v2.y1, 11 AS d1 from (values(1,0)) v1(x1,x2) left join (values(3,1)) v2(y1,y2) on v1.x1 = v2.y2) subq1 on (i1.f1 = subq1.x2) on (t1.unique2 = subq1.d1) left join tenk1 t2 on (subq1.y1 = t2.unique1) where t1.unique2 < 42 and t1.stringu1 > t2.stringu2; select t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 from tenk1 t1 inner join int4_tbl i1 left join (select v1.x2, v2.y1, 11 AS d1 from (values(1,0)) v1(x1,x2) left join (values(3,1)) v2(y1,y2) on v1.x1 = v2.y2) subq1 on (i1.f1 = subq1.x2) on (t1.unique2 = subq1.d1) left join tenk1 t2 on (subq1.y1 = t2.unique1) where t1.unique2 < 42 and t1.stringu1 > t2.stringu2; -- -- test extraction of restriction OR clauses from join OR clause -- (we used to only do this for indexable clauses) -- explain (costs off) select * from tenk1 a join tenk1 b on (a.unique1 = 1 and b.unique1 = 2) or (a.unique2 = 3 and b.hundred = 4); explain (costs off) select * from tenk1 a join tenk1 b on (a.unique1 = 1 and b.unique1 = 2) or (a.unique2 = 3 and b.ten = 4); explain (costs off) select * from tenk1 a join tenk1 b on (a.unique1 = 1 and b.unique1 = 2) or ((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4); -- -- test placement of movable quals in a parameterized join tree -- explain (costs off) select * from tenk1 t1 left join (tenk1 t2 join tenk1 t3 on t2.thousand = t3.unique2) on t1.hundred = t2.hundred and t1.ten = t3.ten where t1.unique1 = 1; explain (costs off) select * from tenk1 t1 left join (tenk1 t2 join tenk1 t3 on t2.thousand = t3.unique2) on t1.hundred = t2.hundred and t1.ten + t2.ten = t3.ten where t1.unique1 = 1; explain (costs off) select count(*) from tenk1 a join tenk1 b on a.unique1 = b.unique2 left join tenk1 c on a.unique2 = b.unique1 and c.thousand = a.thousand join int4_tbl on b.thousand = f1; select count(*) from tenk1 a join tenk1 b on a.unique1 = b.unique2 left join tenk1 c on a.unique2 = b.unique1 and c.thousand = a.thousand join int4_tbl on b.thousand = f1; explain (costs off) select b.unique1 from tenk1 a join tenk1 b on a.unique1 = b.unique2 left join tenk1 c on b.unique1 = 42 and c.thousand = a.thousand join int4_tbl i1 on b.thousand = f1 right join int4_tbl i2 on i2.f1 = b.tenthous order by 1; select b.unique1 from tenk1 a join tenk1 b on a.unique1 = b.unique2 left join tenk1 c on b.unique1 = 42 and c.thousand = a.thousand join int4_tbl i1 on b.thousand = f1 right join int4_tbl i2 on i2.f1 = b.tenthous order by 1; explain (costs off) select * from ( select unique1, q1, coalesce(unique1, -1) + q1 as fault from int8_tbl left join tenk1 on (q2 = unique2) ) ss where fault = 122 order by fault; select * from ( select unique1, q1, coalesce(unique1, -1) + q1 as fault from int8_tbl left join tenk1 on (q2 = unique2) ) ss where fault = 122 order by fault; explain (costs off) select * from (values (1, array[10,20]), (2, array[20,30])) as v1(v1x,v1ys) left join (values (1, 10), (2, 20)) as v2(v2x,v2y) on v2x = v1x left join unnest(v1ys) as u1(u1y) on u1y = v2y; select * from (values (1, array[10,20]), (2, array[20,30])) as v1(v1x,v1ys) left join (values (1, 10), (2, 20)) as v2(v2x,v2y) on v2x = v1x left join unnest(v1ys) as u1(u1y) on u1y = v2y; -- -- test handling of potential equivalence clauses above outer joins -- explain (costs off) select q1, unique2, thousand, hundred from int8_tbl a left join tenk1 b on q1 = unique2 where coalesce(thousand,123) = q1 and q1 = coalesce(hundred,123); select q1, unique2, thousand, hundred from int8_tbl a left join tenk1 b on q1 = unique2 where coalesce(thousand,123) = q1 and q1 = coalesce(hundred,123); explain (costs off) select f1, unique2, case when unique2 is null then f1 else 0 end from int4_tbl a left join tenk1 b on f1 = unique2 where (case when unique2 is null then f1 else 0 end) = 0; select f1, unique2, case when unique2 is null then f1 else 0 end from int4_tbl a left join tenk1 b on f1 = unique2 where (case when unique2 is null then f1 else 0 end) = 0; -- -- another case with equivalence clauses above outer joins (bug #8591) -- explain (costs off) select a.unique1, b.unique1, c.unique1, coalesce(b.twothousand, a.twothousand) from tenk1 a left join tenk1 b on b.thousand = a.unique1 left join tenk1 c on c.unique2 = coalesce(b.twothousand, a.twothousand) where a.unique2 < 10 and coalesce(b.twothousand, a.twothousand) = 44; select a.unique1, b.unique1, c.unique1, coalesce(b.twothousand, a.twothousand) from tenk1 a left join tenk1 b on b.thousand = a.unique1 left join tenk1 c on c.unique2 = coalesce(b.twothousand, a.twothousand) where a.unique2 < 10 and coalesce(b.twothousand, a.twothousand) = 44; -- -- check handling of join aliases when flattening multiple levels of subquery -- explain (verbose, costs off) select foo1.join_key as foo1_id, foo3.join_key AS foo3_id, bug_field from (values (0),(1)) foo1(join_key) left join (select join_key, bug_field from (select ss1.join_key, ss1.bug_field from (select f1 as join_key, 666 as bug_field from int4_tbl i1) ss1 ) foo2 left join (select unique2 as join_key from tenk1 i2) ss2 using (join_key) ) foo3 using (join_key); select foo1.join_key as foo1_id, foo3.join_key AS foo3_id, bug_field from (values (0),(1)) foo1(join_key) left join (select join_key, bug_field from (select ss1.join_key, ss1.bug_field from (select f1 as join_key, 666 as bug_field from int4_tbl i1) ss1 ) foo2 left join (select unique2 as join_key from tenk1 i2) ss2 using (join_key) ) foo3 using (join_key); -- -- test successful handling of nested outer joins with degenerate join quals -- explain (verbose, costs off) select t1.* from text_tbl t1 left join (select *, '***'::text as d1 from int8_tbl i8b1) b1 left join int8_tbl i8 left join (select *, null::int as d2 from int8_tbl i8b2) b2 on (i8.q1 = b2.q1) on (b2.d2 = b1.q2) on (t1.f1 = b1.d1) left join int4_tbl i4 on (i8.q2 = i4.f1); select t1.* from text_tbl t1 left join (select *, '***'::text as d1 from int8_tbl i8b1) b1 left join int8_tbl i8 left join (select *, null::int as d2 from int8_tbl i8b2) b2 on (i8.q1 = b2.q1) on (b2.d2 = b1.q2) on (t1.f1 = b1.d1) left join int4_tbl i4 on (i8.q2 = i4.f1); explain (verbose, costs off) select t1.* from text_tbl t1 left join (select *, '***'::text as d1 from int8_tbl i8b1) b1 left join int8_tbl i8 left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2) b2 on (i8.q1 = b2.q1) on (b2.d2 = b1.q2) on (t1.f1 = b1.d1) left join int4_tbl i4 on (i8.q2 = i4.f1); select t1.* from text_tbl t1 left join (select *, '***'::text as d1 from int8_tbl i8b1) b1 left join int8_tbl i8 left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2) b2 on (i8.q1 = b2.q1) on (b2.d2 = b1.q2) on (t1.f1 = b1.d1) left join int4_tbl i4 on (i8.q2 = i4.f1); explain (verbose, costs off) select t1.* from text_tbl t1 left join (select *, '***'::text as d1 from int8_tbl i8b1) b1 left join int8_tbl i8 left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2 where q1 = f1) b2 on (i8.q1 = b2.q1) on (b2.d2 = b1.q2) on (t1.f1 = b1.d1) left join int4_tbl i4 on (i8.q2 = i4.f1); select t1.* from text_tbl t1 left join (select *, '***'::text as d1 from int8_tbl i8b1) b1 left join int8_tbl i8 left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2 where q1 = f1) b2 on (i8.q1 = b2.q1) on (b2.d2 = b1.q2) on (t1.f1 = b1.d1) left join int4_tbl i4 on (i8.q2 = i4.f1); explain (verbose, costs off) select * from text_tbl t1 inner join int8_tbl i8 on i8.q2 = 456 right join text_tbl t2 on t1.f1 = 'doh!' left join int4_tbl i4 on i8.q1 = i4.f1; select * from text_tbl t1 inner join int8_tbl i8 on i8.q2 = 456 right join text_tbl t2 on t1.f1 = 'doh!' left join int4_tbl i4 on i8.q1 = i4.f1; -- -- test for appropriate join order in the presence of lateral references -- explain (verbose, costs off) select * from text_tbl t1 left join int8_tbl i8 on i8.q2 = 123, lateral (select i8.q1, t2.f1 from text_tbl t2 limit 1) as ss where t1.f1 = ss.f1; select * from text_tbl t1 left join int8_tbl i8 on i8.q2 = 123, lateral (select i8.q1, t2.f1 from text_tbl t2 limit 1) as ss where t1.f1 = ss.f1; explain (verbose, costs off) select * from text_tbl t1 left join int8_tbl i8 on i8.q2 = 123, lateral (select i8.q1, t2.f1 from text_tbl t2 limit 1) as ss1, lateral (select ss1.* from text_tbl t3 limit 1) as ss2 where t1.f1 = ss2.f1; select * from text_tbl t1 left join int8_tbl i8 on i8.q2 = 123, lateral (select i8.q1, t2.f1 from text_tbl t2 limit 1) as ss1, lateral (select ss1.* from text_tbl t3 limit 1) as ss2 where t1.f1 = ss2.f1; explain (verbose, costs off) select 1 from text_tbl as tt1 inner join text_tbl as tt2 on (tt1.f1 = 'foo') left join text_tbl as tt3 on (tt3.f1 = 'foo') left join text_tbl as tt4 on (tt3.f1 = tt4.f1), lateral (select tt4.f1 as c0 from text_tbl as tt5 limit 1) as ss1 where tt1.f1 = ss1.c0; select 1 from text_tbl as tt1 inner join text_tbl as tt2 on (tt1.f1 = 'foo') left join text_tbl as tt3 on (tt3.f1 = 'foo') left join text_tbl as tt4 on (tt3.f1 = tt4.f1), lateral (select tt4.f1 as c0 from text_tbl as tt5 limit 1) as ss1 where tt1.f1 = ss1.c0; -- -- check a case in which a PlaceHolderVar forces join order -- explain (verbose, costs off) select ss2.* from int4_tbl i41 left join int8_tbl i8 join (select i42.f1 as c1, i43.f1 as c2, 42 as c3 from int4_tbl i42, int4_tbl i43) ss1 on i8.q1 = ss1.c2 on i41.f1 = ss1.c1, lateral (select i41.*, i8.*, ss1.* from text_tbl limit 1) ss2 where ss1.c2 = 0; select ss2.* from int4_tbl i41 left join int8_tbl i8 join (select i42.f1 as c1, i43.f1 as c2, 42 as c3 from int4_tbl i42, int4_tbl i43) ss1 on i8.q1 = ss1.c2 on i41.f1 = ss1.c1, lateral (select i41.*, i8.*, ss1.* from text_tbl limit 1) ss2 where ss1.c2 = 0; -- -- test successful handling of full join underneath left join (bug #14105) -- explain (costs off) select * from (select 1 as id) as xx left join (tenk1 as a1 full join (select 1 as id) as yy on (a1.unique1 = yy.id)) on (xx.id = coalesce(yy.id)); select * from (select 1 as id) as xx left join (tenk1 as a1 full join (select 1 as id) as yy on (a1.unique1 = yy.id)) on (xx.id = coalesce(yy.id)); -- -- test ability to push constants through outer join clauses -- explain (costs off) select * from int4_tbl a left join tenk1 b on f1 = unique2 where f1 = 0; explain (costs off) select * from tenk1 a full join tenk1 b using(unique2) where unique2 = 42; -- -- test that quals attached to an outer join have correct semantics, -- specifically that they don't re-use expressions computed below the join; -- we force a mergejoin so that coalesce(b.q1, 1) appears as a join input -- set enable_hashjoin to off; set enable_nestloop to off; explain (verbose, costs off) select a.q2, b.q1 from int8_tbl a left join int8_tbl b on a.q2 = coalesce(b.q1, 1) where coalesce(b.q1, 1) > 0; select a.q2, b.q1 from int8_tbl a left join int8_tbl b on a.q2 = coalesce(b.q1, 1) where coalesce(b.q1, 1) > 0; reset enable_hashjoin; reset enable_nestloop; -- -- test join removal -- begin; CREATE TEMP TABLE a (id int PRIMARY KEY, b_id int); CREATE TEMP TABLE b (id int PRIMARY KEY, c_id int); CREATE TEMP TABLE c (id int PRIMARY KEY); CREATE TEMP TABLE d (a int, b int); INSERT INTO a VALUES (0, 0), (1, NULL); INSERT INTO b VALUES (0, 0), (1, NULL); INSERT INTO c VALUES (0), (1); INSERT INTO d VALUES (1,3), (2,2), (3,1); -- all three cases should be optimizable into a simple seqscan explain (costs off) SELECT a.* FROM a LEFT JOIN b ON a.b_id = b.id; explain (costs off) SELECT b.* FROM b LEFT JOIN c ON b.c_id = c.id; explain (costs off) SELECT a.* FROM a LEFT JOIN (b left join c on b.c_id = c.id) ON (a.b_id = b.id); -- check optimization of outer join within another special join explain (costs off) select id from a where id in ( select b.id from b left join c on b.id = c.id ); -- check that join removal works for a left join when joining a subquery -- that is guaranteed to be unique by its GROUP BY clause explain (costs off) select d.* from d left join (select * from b group by b.id, b.c_id) s on d.a = s.id and d.b = s.c_id; -- similarly, but keying off a DISTINCT clause explain (costs off) select d.* from d left join (select distinct * from b) s on d.a = s.id and d.b = s.c_id; -- join removal is not possible when the GROUP BY contains a column that is -- not in the join condition. (Note: as of 9.6, we notice that b.id is a -- primary key and so drop b.c_id from the GROUP BY of the resulting plan; -- but this happens too late for join removal in the outer plan level.) explain (costs off) select d.* from d left join (select * from b group by b.id, b.c_id) s on d.a = s.id; -- similarly, but keying off a DISTINCT clause explain (costs off) select d.* from d left join (select distinct * from b) s on d.a = s.id; -- check join removal works when uniqueness of the join condition is enforced -- by a UNION explain (costs off) select d.* from d left join (select id from a union select id from b) s on d.a = s.id; -- check join removal with a cross-type comparison operator explain (costs off) select i8.* from int8_tbl i8 left join (select f1 from int4_tbl group by f1) i4 on i8.q1 = i4.f1; -- check join removal with lateral references explain (costs off) select 1 from (select a.id FROM a left join b on a.b_id = b.id) q, lateral generate_series(1, q.id) gs(i) where q.id = gs.i; rollback; create temp table parent (k int primary key, pd int); create temp table child (k int unique, cd int); insert into parent values (1, 10), (2, 20), (3, 30); insert into child values (1, 100), (4, 400); -- this case is optimizable select p.* from parent p left join child c on (p.k = c.k); explain (costs off) select p.* from parent p left join child c on (p.k = c.k); -- this case is not select p.*, linked from parent p left join (select c.*, true as linked from child c) as ss on (p.k = ss.k); explain (costs off) select p.*, linked from parent p left join (select c.*, true as linked from child c) as ss on (p.k = ss.k); -- check for a 9.0rc1 bug: join removal breaks pseudoconstant qual handling select p.* from parent p left join child c on (p.k = c.k) where p.k = 1 and p.k = 2; explain (costs off) select p.* from parent p left join child c on (p.k = c.k) where p.k = 1 and p.k = 2; select p.* from (parent p left join child c on (p.k = c.k)) join parent x on p.k = x.k where p.k = 1 and p.k = 2; explain (costs off) select p.* from (parent p left join child c on (p.k = c.k)) join parent x on p.k = x.k where p.k = 1 and p.k = 2; -- bug 5255: this is not optimizable by join removal begin; CREATE TEMP TABLE a (id int PRIMARY KEY); CREATE TEMP TABLE b (id int PRIMARY KEY, a_id int); INSERT INTO a VALUES (0), (1); INSERT INTO b VALUES (0, 0), (1, NULL); SELECT * FROM b LEFT JOIN a ON (b.a_id = a.id) WHERE (a.id IS NULL OR a.id > 0); SELECT b.* FROM b LEFT JOIN a ON (b.a_id = a.id) WHERE (a.id IS NULL OR a.id > 0); rollback; -- another join removal bug: this is not optimizable, either begin; create temp table innertab (id int8 primary key, dat1 int8); insert into innertab values(123, 42); SELECT * FROM (SELECT 1 AS x) ss1 LEFT JOIN (SELECT q1, q2, COALESCE(dat1, q1) AS y FROM int8_tbl LEFT JOIN innertab ON q2 = id) ss2 ON true; rollback; -- another join removal bug: we must clean up correctly when removing a PHV begin; create temp table uniquetbl (f1 text unique); explain (costs off) select t1.* from uniquetbl as t1 left join (select *, '***'::text as d1 from uniquetbl) t2 on t1.f1 = t2.f1 left join uniquetbl t3 on t2.d1 = t3.f1; explain (costs off) select t0.* from text_tbl t0 left join (select case t1.ten when 0 then 'doh!'::text else null::text end as case1, t1.stringu2 from tenk1 t1 join int4_tbl i4 ON i4.f1 = t1.unique2 left join uniquetbl u1 ON u1.f1 = t1.string4) ss on t0.f1 = ss.case1 where ss.stringu2 !~* ss.case1; select t0.* from text_tbl t0 left join (select case t1.ten when 0 then 'doh!'::text else null::text end as case1, t1.stringu2 from tenk1 t1 join int4_tbl i4 ON i4.f1 = t1.unique2 left join uniquetbl u1 ON u1.f1 = t1.string4) ss on t0.f1 = ss.case1 where ss.stringu2 !~* ss.case1; rollback; -- bug #8444: we've historically allowed duplicate aliases within aliased JOINs select * from int8_tbl x join (int4_tbl x cross join int4_tbl y) j on q1 = f1; -- error select * from int8_tbl x join (int4_tbl x cross join int4_tbl y) j on q1 = y.f1; -- error select * from int8_tbl x join (int4_tbl x cross join int4_tbl y(ff)) j on q1 = f1; -- ok -- -- Test hints given on incorrect column references are useful -- select t1.uunique1 from tenk1 t1 join tenk2 t2 on t1.two = t2.two; -- error, prefer "t1" suggestion select t2.uunique1 from tenk1 t1 join tenk2 t2 on t1.two = t2.two; -- error, prefer "t2" suggestion select uunique1 from tenk1 t1 join tenk2 t2 on t1.two = t2.two; -- error, suggest both at once -- -- Take care to reference the correct RTE -- select atts.relid::regclass, s.* from pg_stats s join pg_attribute a on s.attname = a.attname and s.tablename = a.attrelid::regclass::text join (select unnest(indkey) attnum, indexrelid from pg_index i) atts on atts.attnum = a.attnum where schemaname != 'pg_catalog'; -- -- Test LATERAL -- select unique2, x.* from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x; explain (costs off) select unique2, x.* from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x; select unique2, x.* from int4_tbl x, lateral (select unique2 from tenk1 where f1 = unique1) ss; explain (costs off) select unique2, x.* from int4_tbl x, lateral (select unique2 from tenk1 where f1 = unique1) ss; explain (costs off) select unique2, x.* from int4_tbl x cross join lateral (select unique2 from tenk1 where f1 = unique1) ss; select unique2, x.* from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on true; explain (costs off) select unique2, x.* from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on true; -- check scoping of lateral versus parent references -- the first of these should return int8_tbl.q2, the second int8_tbl.q1 select *, (select r from (select q1 as q2) x, (select q2 as r) y) from int8_tbl; select *, (select r from (select q1 as q2) x, lateral (select q2 as r) y) from int8_tbl; -- lateral with function in FROM select count(*) from tenk1 a, lateral generate_series(1,two) g; explain (costs off) select count(*) from tenk1 a, lateral generate_series(1,two) g; explain (costs off) select count(*) from tenk1 a cross join lateral generate_series(1,two) g; -- don't need the explicit LATERAL keyword for functions explain (costs off) select count(*) from tenk1 a, generate_series(1,two) g; -- lateral with UNION ALL subselect explain (costs off) select * from generate_series(100,200) g, lateral (select * from int8_tbl a where g = q1 union all select * from int8_tbl b where g = q2) ss; select * from generate_series(100,200) g, lateral (select * from int8_tbl a where g = q1 union all select * from int8_tbl b where g = q2) ss; -- lateral with VALUES explain (costs off) select count(*) from tenk1 a, tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x; select count(*) from tenk1 a, tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x; -- lateral with VALUES, no flattening possible explain (costs off) select count(*) from tenk1 a, tenk1 b join lateral (values(a.unique1),(-1)) ss(x) on b.unique2 = ss.x; select count(*) from tenk1 a, tenk1 b join lateral (values(a.unique1),(-1)) ss(x) on b.unique2 = ss.x; -- lateral injecting a strange outer join condition explain (costs off) select * from int8_tbl a, int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z) on x.q2 = ss.z order by a.q1, a.q2, x.q1, x.q2, ss.z; select * from int8_tbl a, int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z) on x.q2 = ss.z order by a.q1, a.q2, x.q1, x.q2, ss.z; -- lateral reference to a join alias variable select * from (select f1/2 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1, lateral (select x) ss2(y); select * from (select f1 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1, lateral (values(x)) ss2(y); select * from ((select f1/2 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1) j, lateral (select x) ss2(y); -- lateral references requiring pullup select * from (values(1)) x(lb), lateral generate_series(lb,4) x4; select * from (select f1/1000000000 from int4_tbl) x(lb), lateral generate_series(lb,4) x4; select * from (values(1)) x(lb), lateral (values(lb)) y(lbcopy); select * from (values(1)) x(lb), lateral (select lb from int4_tbl) y(lbcopy); select * from int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, lateral (values(x.q1,y.q1,y.q2)) v(xq1,yq1,yq2); select * from int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2); select x.* from int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2); select v.* from (int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1) left join int4_tbl z on z.f1 = x.q2, lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); select v.* from (int8_tbl x left join (select q1,(select coalesce(q2,0)) q2 from int8_tbl) y on x.q2 = y.q1) left join int4_tbl z on z.f1 = x.q2, lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); select v.* from (int8_tbl x left join (select q1,(select coalesce(q2,0)) q2 from int8_tbl) y on x.q2 = y.q1) left join int4_tbl z on z.f1 = x.q2, lateral (select x.q1,y.q1 from onerow union all select x.q2,y.q2 from onerow) v(vx,vy); explain (verbose, costs off) select * from int8_tbl a left join lateral (select *, a.q2 as x from int8_tbl b) ss on a.q2 = ss.q1; select * from int8_tbl a left join lateral (select *, a.q2 as x from int8_tbl b) ss on a.q2 = ss.q1; explain (verbose, costs off) select * from int8_tbl a left join lateral (select *, coalesce(a.q2, 42) as x from int8_tbl b) ss on a.q2 = ss.q1; select * from int8_tbl a left join lateral (select *, coalesce(a.q2, 42) as x from int8_tbl b) ss on a.q2 = ss.q1; -- lateral can result in join conditions appearing below their -- real semantic level explain (verbose, costs off) select * from int4_tbl i left join lateral (select * from int2_tbl j where i.f1 = j.f1) k on true; select * from int4_tbl i left join lateral (select * from int2_tbl j where i.f1 = j.f1) k on true; explain (verbose, costs off) select * from int4_tbl i left join lateral (select coalesce(i) from int2_tbl j where i.f1 = j.f1) k on true; select * from int4_tbl i left join lateral (select coalesce(i) from int2_tbl j where i.f1 = j.f1) k on true; explain (verbose, costs off) select * from int4_tbl a, lateral ( select * from int4_tbl b left join int8_tbl c on (b.f1 = q1 and a.f1 = q2) ) ss; select * from int4_tbl a, lateral ( select * from int4_tbl b left join int8_tbl c on (b.f1 = q1 and a.f1 = q2) ) ss; -- lateral reference in a PlaceHolderVar evaluated at join level explain (verbose, costs off) select * from int8_tbl a left join lateral (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from int8_tbl b cross join int8_tbl c) ss on a.q2 = ss.bq1; select * from int8_tbl a left join lateral (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from int8_tbl b cross join int8_tbl c) ss on a.q2 = ss.bq1; -- case requiring nested PlaceHolderVars explain (verbose, costs off) select * from int8_tbl c left join ( int8_tbl a left join (select q1, coalesce(q2,42) as x from int8_tbl b) ss1 on a.q2 = ss1.q1 cross join lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2 ) on c.q2 = ss2.q1, lateral (select ss2.y offset 0) ss3; -- case that breaks the old ph_may_need optimization explain (verbose, costs off) select c.*,a.*,ss1.q1,ss2.q1,ss3.* from int8_tbl c left join ( int8_tbl a left join (select q1, coalesce(q2,f1) as x from int8_tbl b, int4_tbl b2 where q1 < f1) ss1 on a.q2 = ss1.q1 cross join lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2 ) on c.q2 = ss2.q1, lateral (select * from int4_tbl i where ss2.y > f1) ss3; -- check processing of postponed quals (bug #9041) explain (verbose, costs off) select * from (select 1 as x offset 0) x cross join (select 2 as y offset 0) y left join lateral ( select * from (select 3 as z offset 0) z where z.z = x.x ) zz on zz.z = y.y; -- check dummy rels with lateral references (bug #15694) explain (verbose, costs off) select * from int8_tbl i8 left join lateral (select *, i8.q2 from int4_tbl where false) ss on true; explain (verbose, costs off) select * from int8_tbl i8 left join lateral (select *, i8.q2 from int4_tbl i1, int4_tbl i2 where false) ss on true; -- check handling of nested appendrels inside LATERAL select * from ((select 2 as v) union all (select 3 as v)) as q1 cross join lateral ((select * from ((select 4 as v) union all (select 5 as v)) as q3) union all (select q1.v) ) as q2; -- check we don't try to do a unique-ified semijoin with LATERAL explain (verbose, costs off) select * from (values (0,9998), (1,1000)) v(id,x), lateral (select f1 from int4_tbl where f1 = any (select unique1 from tenk1 where unique2 = v.x offset 0)) ss; select * from (values (0,9998), (1,1000)) v(id,x), lateral (select f1 from int4_tbl where f1 = any (select unique1 from tenk1 where unique2 = v.x offset 0)) ss; -- check proper extParam/allParam handling (this isn't exactly a LATERAL issue, -- but we can make the test case much more compact with LATERAL) explain (verbose, costs off) select * from (values (0), (1)) v(id), lateral (select * from int8_tbl t1, lateral (select * from (select * from int8_tbl t2 where q1 = any (select q2 from int8_tbl t3 where q2 = (select greatest(t1.q1,t2.q2)) and (select v.id=0)) offset 0) ss2) ss where t1.q1 = ss.q2) ss0; select * from (values (0), (1)) v(id), lateral (select * from int8_tbl t1, lateral (select * from (select * from int8_tbl t2 where q1 = any (select q2 from int8_tbl t3 where q2 = (select greatest(t1.q1,t2.q2)) and (select v.id=0)) offset 0) ss2) ss where t1.q1 = ss.q2) ss0; -- test some error cases where LATERAL should have been used but wasn't select f1,g from int4_tbl a, (select f1 as g) ss; select f1,g from int4_tbl a, (select a.f1 as g) ss; select f1,g from int4_tbl a cross join (select f1 as g) ss; select f1,g from int4_tbl a cross join (select a.f1 as g) ss; -- SQL:2008 says the left table is in scope but illegal to access here select f1,g from int4_tbl a right join lateral generate_series(0, a.f1) g on true; select f1,g from int4_tbl a full join lateral generate_series(0, a.f1) g on true; -- check we complain about ambiguous table references select * from int8_tbl x cross join (int4_tbl x cross join lateral (select x.f1) ss); -- LATERAL can be used to put an aggregate into the FROM clause of its query select 1 from tenk1 a, lateral (select max(a.unique1) from int4_tbl b) ss; -- check behavior of LATERAL in UPDATE/DELETE create temp table xx1 as select f1 as x1, -f1 as x2 from int4_tbl; -- error, can't do this: update xx1 set x2 = f1 from (select * from int4_tbl where f1 = x1) ss; update xx1 set x2 = f1 from (select * from int4_tbl where f1 = xx1.x1) ss; -- can't do it even with LATERAL: update xx1 set x2 = f1 from lateral (select * from int4_tbl where f1 = x1) ss; -- we might in future allow something like this, but for now it's an error: update xx1 set x2 = f1 from xx1, lateral (select * from int4_tbl where f1 = x1) ss; -- also errors: delete from xx1 using (select * from int4_tbl where f1 = x1) ss; delete from xx1 using (select * from int4_tbl where f1 = xx1.x1) ss; delete from xx1 using lateral (select * from int4_tbl where f1 = x1) ss; -- -- test LATERAL reference propagation down a multi-level inheritance hierarchy -- produced for a multi-level partitioned table hierarchy. -- create table join_pt1 (a int, b int, c varchar) partition by range(a); create table join_pt1p1 partition of join_pt1 for values from (0) to (100) partition by range(b); create table join_pt1p2 partition of join_pt1 for values from (100) to (200); create table join_pt1p1p1 partition of join_pt1p1 for values from (0) to (100); insert into join_pt1 values (1, 1, 'x'), (101, 101, 'y'); create table join_ut1 (a int, b int, c varchar); insert into join_ut1 values (101, 101, 'y'), (2, 2, 'z'); explain (verbose, costs off) select t1.b, ss.phv from join_ut1 t1 left join lateral (select t2.a as t2a, t3.a t3a, least(t1.a, t2.a, t3.a) phv from join_pt1 t2 join join_ut1 t3 on t2.a = t3.b) ss on t1.a = ss.t2a order by t1.a; select t1.b, ss.phv from join_ut1 t1 left join lateral (select t2.a as t2a, t3.a t3a, least(t1.a, t2.a, t3.a) phv from join_pt1 t2 join join_ut1 t3 on t2.a = t3.b) ss on t1.a = ss.t2a order by t1.a; drop table join_pt1; drop table join_ut1; -- -- test that foreign key join estimation performs sanely for outer joins -- begin; create table fkest (a int, b int, c int unique, primary key(a,b)); create table fkest1 (a int, b int, primary key(a,b)); insert into fkest select x/10, x%10, x from generate_series(1,1000) x; insert into fkest1 select x/10, x%10 from generate_series(1,1000) x; alter table fkest1 add constraint fkest1_a_b_fkey foreign key (a,b) references fkest; analyze fkest; analyze fkest1; explain (costs off) select * from fkest f left join fkest1 f1 on f.a = f1.a and f.b = f1.b left join fkest1 f2 on f.a = f2.a and f.b = f2.b left join fkest1 f3 on f.a = f3.a and f.b = f3.b where f.c = 1; rollback; -- -- test planner's ability to mark joins as unique -- create table j1 (id int primary key); create table j2 (id int primary key); create table j3 (id int); insert into j1 values(1),(2),(3); insert into j2 values(1),(2),(3); insert into j3 values(1),(1); analyze j1; analyze j2; analyze j3; -- ensure join is properly marked as unique explain (verbose, costs off) select * from j1 inner join j2 on j1.id = j2.id; -- ensure join is not unique when not an equi-join explain (verbose, costs off) select * from j1 inner join j2 on j1.id > j2.id; -- ensure non-unique rel is not chosen as inner explain (verbose, costs off) select * from j1 inner join j3 on j1.id = j3.id; -- ensure left join is marked as unique explain (verbose, costs off) select * from j1 left join j2 on j1.id = j2.id; -- ensure right join is marked as unique explain (verbose, costs off) select * from j1 right join j2 on j1.id = j2.id; -- ensure full join is marked as unique explain (verbose, costs off) select * from j1 full join j2 on j1.id = j2.id; -- a clauseless (cross) join can't be unique explain (verbose, costs off) select * from j1 cross join j2; -- ensure a natural join is marked as unique explain (verbose, costs off) select * from j1 natural join j2; -- ensure a distinct clause allows the inner to become unique explain (verbose, costs off) select * from j1 inner join (select distinct id from j3) j3 on j1.id = j3.id; -- ensure group by clause allows the inner to become unique explain (verbose, costs off) select * from j1 inner join (select id from j3 group by id) j3 on j1.id = j3.id; drop table j1; drop table j2; drop table j3; -- test more complex permutations of unique joins create table j1 (id1 int, id2 int, primary key(id1,id2)); create table j2 (id1 int, id2 int, primary key(id1,id2)); create table j3 (id1 int, id2 int, primary key(id1,id2)); insert into j1 values(1,1),(1,2); insert into j2 values(1,1); insert into j3 values(1,1); analyze j1; analyze j2; analyze j3; -- ensure there's no unique join when not all columns which are part of the -- unique index are seen in the join clause explain (verbose, costs off) select * from j1 inner join j2 on j1.id1 = j2.id1; -- ensure proper unique detection with multiple join quals explain (verbose, costs off) select * from j1 inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2; -- ensure we don't detect the join to be unique when quals are not part of the -- join condition explain (verbose, costs off) select * from j1 inner join j2 on j1.id1 = j2.id1 where j1.id2 = 1; -- as above, but for left joins. explain (verbose, costs off) select * from j1 left join j2 on j1.id1 = j2.id1 where j1.id2 = 1; -- validate logic in merge joins which skips mark and restore. -- it should only do this if all quals which were used to detect the unique -- are present as join quals, and not plain quals. set enable_nestloop to 0; set enable_hashjoin to 0; set enable_sort to 0; -- create indexes that will be preferred over the PKs to perform the join create index j1_id1_idx on j1 (id1) where id1 % 1000 = 1; create index j2_id1_idx on j2 (id1) where id1 % 1000 = 1; -- need an additional row in j2, if we want j2_id1_idx to be preferred insert into j2 values(1,2); analyze j2; explain (costs off) select * from j1 inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2 where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1; select * from j1 inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2 where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1; reset enable_nestloop; reset enable_hashjoin; reset enable_sort; drop table j1; drop table j2; drop table j3; -- check that semijoin inner is not seen as unique for a portion of the outerrel explain (verbose, costs off) select t1.unique1, t2.hundred from onek t1, tenk1 t2 where exists (select 1 from tenk1 t3 where t3.thousand = t1.unique1 and t3.tenthous = t2.hundred) and t1.unique1 < 1; -- ... unless it actually is unique create table j3 as select unique1, tenthous from onek; vacuum analyze j3; create unique index on j3(unique1, tenthous); explain (verbose, costs off) select t1.unique1, t2.hundred from onek t1, tenk1 t2 where exists (select 1 from j3 where j3.unique1 = t1.unique1 and j3.tenthous = t2.hundred) and t1.unique1 < 1; drop table j3; pgFormatter-4.2/t/pg-test-files/sql/join_hash.sql000066400000000000000000000401321361326045100220620ustar00rootroot00000000000000-- -- exercises for the hash join code -- begin; set local min_parallel_table_scan_size = 0; set local parallel_setup_cost = 0; -- Extract bucket and batch counts from an explain analyze plan. In -- general we can't make assertions about how many batches (or -- buckets) will be required because it can vary, but we can in some -- special cases and we can check for growth. create or replace function find_hash(node json) returns json language plpgsql as $$ declare x json; child json; begin if node->>'Node Type' = 'Hash' then return node; else for child in select json_array_elements(node->'Plans') loop x := find_hash(child); if x is not null then return x; end if; end loop; return null; end if; end; $$; create or replace function hash_join_batches(query text) returns table (original int, final int) language plpgsql as $$ declare whole_plan json; hash_node json; begin for whole_plan in execute 'explain (analyze, format ''json'') ' || query loop hash_node := find_hash(json_extract_path(whole_plan, '0', 'Plan')); original := hash_node->>'Original Hash Batches'; final := hash_node->>'Hash Batches'; return next; end loop; end; $$; -- Make a simple relation with well distributed keys and correctly -- estimated size. create table simple as select generate_series(1, 20000) AS id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; alter table simple set (parallel_workers = 2); analyze simple; -- Make a relation whose size we will under-estimate. We want stats -- to say 1000 rows, but actually there are 20,000 rows. create table bigger_than_it_looks as select generate_series(1, 20000) as id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; alter table bigger_than_it_looks set (autovacuum_enabled = 'false'); alter table bigger_than_it_looks set (parallel_workers = 2); analyze bigger_than_it_looks; update pg_class set reltuples = 1000 where relname = 'bigger_than_it_looks'; -- Make a relation whose size we underestimate and that also has a -- kind of skew that breaks our batching scheme. We want stats to say -- 2 rows, but actually there are 20,000 rows with the same key. create table extremely_skewed (id int, t text); alter table extremely_skewed set (autovacuum_enabled = 'false'); alter table extremely_skewed set (parallel_workers = 2); analyze extremely_skewed; insert into extremely_skewed select 42 as id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' from generate_series(1, 20000); update pg_class set reltuples = 2, relpages = pg_relation_size('extremely_skewed') / 8192 where relname = 'extremely_skewed'; -- Make a relation with a couple of enormous tuples. create table wide as select generate_series(1, 2) as id, rpad('', 320000, 'x') as t; alter table wide set (parallel_workers = 2); -- The "optimal" case: the hash table fits in memory; we plan for 1 -- batch, we stick to that number, and peak memory usage stays within -- our work_mem budget -- non-parallel savepoint settings; set local max_parallel_workers_per_gather = 0; set local work_mem = '4MB'; explain (costs off) select count(*) from simple r join simple s using (id); select count(*) from simple r join simple s using (id); select original > 1 as initially_multibatch, final > original as increased_batches from hash_join_batches( $$ select count(*) from simple r join simple s using (id); $$); rollback to settings; -- parallel with parallel-oblivious hash join savepoint settings; set local max_parallel_workers_per_gather = 2; set local work_mem = '4MB'; set local enable_parallel_hash = off; explain (costs off) select count(*) from simple r join simple s using (id); select count(*) from simple r join simple s using (id); select original > 1 as initially_multibatch, final > original as increased_batches from hash_join_batches( $$ select count(*) from simple r join simple s using (id); $$); rollback to settings; -- parallel with parallel-aware hash join savepoint settings; set local max_parallel_workers_per_gather = 2; set local work_mem = '4MB'; set local enable_parallel_hash = on; explain (costs off) select count(*) from simple r join simple s using (id); select count(*) from simple r join simple s using (id); select original > 1 as initially_multibatch, final > original as increased_batches from hash_join_batches( $$ select count(*) from simple r join simple s using (id); $$); rollback to settings; -- The "good" case: batches required, but we plan the right number; we -- plan for some number of batches, and we stick to that number, and -- peak memory usage says within our work_mem budget -- non-parallel savepoint settings; set local max_parallel_workers_per_gather = 0; set local work_mem = '128kB'; explain (costs off) select count(*) from simple r join simple s using (id); select count(*) from simple r join simple s using (id); select original > 1 as initially_multibatch, final > original as increased_batches from hash_join_batches( $$ select count(*) from simple r join simple s using (id); $$); rollback to settings; -- parallel with parallel-oblivious hash join savepoint settings; set local max_parallel_workers_per_gather = 2; set local work_mem = '128kB'; set local enable_parallel_hash = off; explain (costs off) select count(*) from simple r join simple s using (id); select count(*) from simple r join simple s using (id); select original > 1 as initially_multibatch, final > original as increased_batches from hash_join_batches( $$ select count(*) from simple r join simple s using (id); $$); rollback to settings; -- parallel with parallel-aware hash join savepoint settings; set local max_parallel_workers_per_gather = 2; set local work_mem = '192kB'; set local enable_parallel_hash = on; explain (costs off) select count(*) from simple r join simple s using (id); select count(*) from simple r join simple s using (id); select original > 1 as initially_multibatch, final > original as increased_batches from hash_join_batches( $$ select count(*) from simple r join simple s using (id); $$); rollback to settings; -- The "bad" case: during execution we need to increase number of -- batches; in this case we plan for 1 batch, and increase at least a -- couple of times, and peak memory usage stays within our work_mem -- budget -- non-parallel savepoint settings; set local max_parallel_workers_per_gather = 0; set local work_mem = '128kB'; explain (costs off) select count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); select count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); select original > 1 as initially_multibatch, final > original as increased_batches from hash_join_batches( $$ select count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); $$); rollback to settings; -- parallel with parallel-oblivious hash join savepoint settings; set local max_parallel_workers_per_gather = 2; set local work_mem = '128kB'; set local enable_parallel_hash = off; explain (costs off) select count(*) from simple r join bigger_than_it_looks s using (id); select count(*) from simple r join bigger_than_it_looks s using (id); select original > 1 as initially_multibatch, final > original as increased_batches from hash_join_batches( $$ select count(*) from simple r join bigger_than_it_looks s using (id); $$); rollback to settings; -- parallel with parallel-aware hash join savepoint settings; set local max_parallel_workers_per_gather = 1; set local work_mem = '192kB'; set local enable_parallel_hash = on; explain (costs off) select count(*) from simple r join bigger_than_it_looks s using (id); select count(*) from simple r join bigger_than_it_looks s using (id); select original > 1 as initially_multibatch, final > original as increased_batches from hash_join_batches( $$ select count(*) from simple r join bigger_than_it_looks s using (id); $$); rollback to settings; -- The "ugly" case: increasing the number of batches during execution -- doesn't help, so stop trying to fit in work_mem and hope for the -- best; in this case we plan for 1 batch, increases just once and -- then stop increasing because that didn't help at all, so we blow -- right through the work_mem budget and hope for the best... -- non-parallel savepoint settings; set local max_parallel_workers_per_gather = 0; set local work_mem = '128kB'; explain (costs off) select count(*) from simple r join extremely_skewed s using (id); select count(*) from simple r join extremely_skewed s using (id); select * from hash_join_batches( $$ select count(*) from simple r join extremely_skewed s using (id); $$); rollback to settings; -- parallel with parallel-oblivious hash join savepoint settings; set local max_parallel_workers_per_gather = 2; set local work_mem = '128kB'; set local enable_parallel_hash = off; explain (costs off) select count(*) from simple r join extremely_skewed s using (id); select count(*) from simple r join extremely_skewed s using (id); select * from hash_join_batches( $$ select count(*) from simple r join extremely_skewed s using (id); $$); rollback to settings; -- parallel with parallel-aware hash join savepoint settings; set local max_parallel_workers_per_gather = 1; set local work_mem = '128kB'; set local enable_parallel_hash = on; explain (costs off) select count(*) from simple r join extremely_skewed s using (id); select count(*) from simple r join extremely_skewed s using (id); select * from hash_join_batches( $$ select count(*) from simple r join extremely_skewed s using (id); $$); rollback to settings; -- A couple of other hash join tests unrelated to work_mem management. -- Check that EXPLAIN ANALYZE has data even if the leader doesn't participate savepoint settings; set local max_parallel_workers_per_gather = 2; set local work_mem = '4MB'; set local parallel_leader_participation = off; select * from hash_join_batches( $$ select count(*) from simple r join simple s using (id); $$); rollback to settings; -- Exercise rescans. We'll turn off parallel_leader_participation so -- that we can check that instrumentation comes back correctly. create table join_foo as select generate_series(1, 3) as id, 'xxxxx'::text as t; alter table join_foo set (parallel_workers = 0); create table join_bar as select generate_series(1, 10000) as id, 'xxxxx'::text as t; alter table join_bar set (parallel_workers = 2); -- multi-batch with rescan, parallel-oblivious savepoint settings; set enable_parallel_hash = off; set parallel_leader_participation = off; set min_parallel_table_scan_size = 0; set parallel_setup_cost = 0; set parallel_tuple_cost = 0; set max_parallel_workers_per_gather = 2; set enable_material = off; set enable_mergejoin = off; set work_mem = '64kB'; explain (costs off) select count(*) from join_foo left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; select count(*) from join_foo left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; select final > 1 as multibatch from hash_join_batches( $$ select count(*) from join_foo left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; $$); rollback to settings; -- single-batch with rescan, parallel-oblivious savepoint settings; set enable_parallel_hash = off; set parallel_leader_participation = off; set min_parallel_table_scan_size = 0; set parallel_setup_cost = 0; set parallel_tuple_cost = 0; set max_parallel_workers_per_gather = 2; set enable_material = off; set enable_mergejoin = off; set work_mem = '4MB'; explain (costs off) select count(*) from join_foo left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; select count(*) from join_foo left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; select final > 1 as multibatch from hash_join_batches( $$ select count(*) from join_foo left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; $$); rollback to settings; -- multi-batch with rescan, parallel-aware savepoint settings; set enable_parallel_hash = on; set parallel_leader_participation = off; set min_parallel_table_scan_size = 0; set parallel_setup_cost = 0; set parallel_tuple_cost = 0; set max_parallel_workers_per_gather = 2; set enable_material = off; set enable_mergejoin = off; set work_mem = '64kB'; explain (costs off) select count(*) from join_foo left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; select count(*) from join_foo left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; select final > 1 as multibatch from hash_join_batches( $$ select count(*) from join_foo left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; $$); rollback to settings; -- single-batch with rescan, parallel-aware savepoint settings; set enable_parallel_hash = on; set parallel_leader_participation = off; set min_parallel_table_scan_size = 0; set parallel_setup_cost = 0; set parallel_tuple_cost = 0; set max_parallel_workers_per_gather = 2; set enable_material = off; set enable_mergejoin = off; set work_mem = '4MB'; explain (costs off) select count(*) from join_foo left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; select count(*) from join_foo left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; select final > 1 as multibatch from hash_join_batches( $$ select count(*) from join_foo left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; $$); rollback to settings; -- A full outer join where every record is matched. -- non-parallel savepoint settings; set local max_parallel_workers_per_gather = 0; explain (costs off) select count(*) from simple r full outer join simple s using (id); select count(*) from simple r full outer join simple s using (id); rollback to settings; -- parallelism not possible with parallel-oblivious outer hash join savepoint settings; set local max_parallel_workers_per_gather = 2; explain (costs off) select count(*) from simple r full outer join simple s using (id); select count(*) from simple r full outer join simple s using (id); rollback to settings; -- An full outer join where every record is not matched. -- non-parallel savepoint settings; set local max_parallel_workers_per_gather = 0; explain (costs off) select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); rollback to settings; -- parallelism not possible with parallel-oblivious outer hash join savepoint settings; set local max_parallel_workers_per_gather = 2; explain (costs off) select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); rollback to settings; -- exercise special code paths for huge tuples (note use of non-strict -- expression and left join required to get the detoasted tuple into -- the hash table) -- parallel with parallel-aware hash join (hits ExecParallelHashLoadTuple and -- sts_puttuple oversized tuple cases because it's multi-batch) savepoint settings; set max_parallel_workers_per_gather = 2; set enable_parallel_hash = on; set work_mem = '128kB'; explain (costs off) select length(max(s.t)) from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); select length(max(s.t)) from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); select final > 1 as multibatch from hash_join_batches( $$ select length(max(s.t)) from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); $$); rollback to settings; rollback; pgFormatter-4.2/t/pg-test-files/sql/json.sql000066400000000000000000001034761361326045100211040ustar00rootroot00000000000000-- Strings. SELECT '""'::json; -- OK. SELECT $$''$$::json; -- ERROR, single quotes are not allowed SELECT '"abc"'::json; -- OK SELECT '"abc'::json; -- ERROR, quotes not closed SELECT '"abc def"'::json; -- ERROR, unescaped newline in string constant SELECT '"\n\"\\"'::json; -- OK, legal escapes SELECT '"\v"'::json; -- ERROR, not a valid JSON escape -- see json_encoding test for input with unicode escapes -- Numbers. SELECT '1'::json; -- OK SELECT '0'::json; -- OK SELECT '01'::json; -- ERROR, not valid according to JSON spec SELECT '0.1'::json; -- OK SELECT '9223372036854775808'::json; -- OK, even though it's too large for int8 SELECT '1e100'::json; -- OK SELECT '1.3e100'::json; -- OK SELECT '1f2'::json; -- ERROR SELECT '0.x1'::json; -- ERROR SELECT '1.3ex100'::json; -- ERROR -- Arrays. SELECT '[]'::json; -- OK SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::json; -- OK SELECT '[1,2]'::json; -- OK SELECT '[1,2,]'::json; -- ERROR, trailing comma SELECT '[1,2'::json; -- ERROR, no closing bracket SELECT '[1,[2]'::json; -- ERROR, no closing bracket -- Objects. SELECT '{}'::json; -- OK SELECT '{"abc"}'::json; -- ERROR, no value SELECT '{"abc":1}'::json; -- OK SELECT '{1:"abc"}'::json; -- ERROR, keys must be strings SELECT '{"abc",1}'::json; -- ERROR, wrong separator SELECT '{"abc"=1}'::json; -- ERROR, totally wrong separator SELECT '{"abc"::1}'::json; -- ERROR, another wrong separator SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::json; -- OK SELECT '{"abc":1:2}'::json; -- ERROR, colon in wrong spot SELECT '{"abc":1,3}'::json; -- ERROR, no value -- Recursion. SET max_stack_depth = '100kB'; SELECT repeat('[', 10000)::json; SELECT repeat('{"a":', 10000)::json; RESET max_stack_depth; -- Miscellaneous stuff. SELECT 'true'::json; -- OK SELECT 'false'::json; -- OK SELECT 'null'::json; -- OK SELECT ' true '::json; -- OK, even with extra whitespace SELECT 'true false'::json; -- ERROR, too many values SELECT 'true, false'::json; -- ERROR, too many values SELECT 'truf'::json; -- ERROR, not a keyword SELECT 'trues'::json; -- ERROR, not a keyword SELECT ''::json; -- ERROR, no value SELECT ' '::json; -- ERROR, no value --constructors -- array_to_json SELECT array_to_json(array(select 1 as a)); SELECT array_to_json(array_agg(q),false) from (select x as b, x * 2 as c from generate_series(1,3) x) q; SELECT array_to_json(array_agg(q),true) from (select x as b, x * 2 as c from generate_series(1,3) x) q; SELECT array_to_json(array_agg(q),false) FROM ( SELECT $$a$$ || x AS b, y AS c, ARRAY[ROW(x.*,ARRAY[1,2,3]), ROW(y.*,ARRAY[4,5,6])] AS z FROM generate_series(1,2) x, generate_series(4,5) y) q; SELECT array_to_json(array_agg(x),false) from generate_series(5,10) x; SELECT array_to_json('{{1,5},{99,100}}'::int[]); -- row_to_json SELECT row_to_json(row(1,'foo')); SELECT row_to_json(q) FROM (SELECT $$a$$ || x AS b, y AS c, ARRAY[ROW(x.*,ARRAY[1,2,3]), ROW(y.*,ARRAY[4,5,6])] AS z FROM generate_series(1,2) x, generate_series(4,5) y) q; SELECT row_to_json(q,true) FROM (SELECT $$a$$ || x AS b, y AS c, ARRAY[ROW(x.*,ARRAY[1,2,3]), ROW(y.*,ARRAY[4,5,6])] AS z FROM generate_series(1,2) x, generate_series(4,5) y) q; CREATE TEMP TABLE rows AS SELECT x, 'txt' || x as y FROM generate_series(1,3) AS x; SELECT row_to_json(q,true) FROM rows q; SELECT row_to_json(row((select array_agg(x) as d from generate_series(5,10) x)),false); -- anyarray column select to_json(histogram_bounds) histogram_bounds from pg_stats where attname = 'tmplname' and tablename = 'pg_pltemplate'; -- to_json, timestamps select to_json(timestamp '2014-05-28 12:22:35.614298'); BEGIN; SET LOCAL TIME ZONE 10.5; select to_json(timestamptz '2014-05-28 12:22:35.614298-04'); SET LOCAL TIME ZONE -8; select to_json(timestamptz '2014-05-28 12:22:35.614298-04'); COMMIT; select to_json(date '2014-05-28'); select to_json(date 'Infinity'); select to_json(date '-Infinity'); select to_json(timestamp 'Infinity'); select to_json(timestamp '-Infinity'); select to_json(timestamptz 'Infinity'); select to_json(timestamptz '-Infinity'); --json_agg SELECT json_agg(q) FROM ( SELECT $$a$$ || x AS b, y AS c, ARRAY[ROW(x.*,ARRAY[1,2,3]), ROW(y.*,ARRAY[4,5,6])] AS z FROM generate_series(1,2) x, generate_series(4,5) y) q; SELECT json_agg(q ORDER BY x, y) FROM rows q; UPDATE rows SET x = NULL WHERE x = 1; SELECT json_agg(q ORDER BY x NULLS FIRST, y) FROM rows q; -- non-numeric output SELECT row_to_json(q) FROM (SELECT 'NaN'::float8 AS "float8field") q; SELECT row_to_json(q) FROM (SELECT 'Infinity'::float8 AS "float8field") q; SELECT row_to_json(q) FROM (SELECT '-Infinity'::float8 AS "float8field") q; -- json input SELECT row_to_json(q) FROM (SELECT '{"a":1,"b": [2,3,4,"d","e","f"],"c":{"p":1,"q":2}}'::json AS "jsonfield") q; -- json extraction functions CREATE TEMP TABLE test_json ( json_type text, test_json json ); INSERT INTO test_json VALUES ('scalar','"a scalar"'), ('array','["zero", "one","two",null,"four","five", [1,2,3],{"f1":9}]'), ('object','{"field1":"val1","field2":"val2","field3":null, "field4": 4, "field5": [1,2,3], "field6": {"f1":9}}'); SELECT test_json -> 'x' FROM test_json WHERE json_type = 'scalar'; SELECT test_json -> 'x' FROM test_json WHERE json_type = 'array'; SELECT test_json -> 'x' FROM test_json WHERE json_type = 'object'; SELECT test_json->'field2' FROM test_json WHERE json_type = 'object'; SELECT test_json->>'field2' FROM test_json WHERE json_type = 'object'; SELECT test_json -> 2 FROM test_json WHERE json_type = 'scalar'; SELECT test_json -> 2 FROM test_json WHERE json_type = 'array'; SELECT test_json -> -1 FROM test_json WHERE json_type = 'array'; SELECT test_json -> 2 FROM test_json WHERE json_type = 'object'; SELECT test_json->>2 FROM test_json WHERE json_type = 'array'; SELECT test_json ->> 6 FROM test_json WHERE json_type = 'array'; SELECT test_json ->> 7 FROM test_json WHERE json_type = 'array'; SELECT test_json ->> 'field4' FROM test_json WHERE json_type = 'object'; SELECT test_json ->> 'field5' FROM test_json WHERE json_type = 'object'; SELECT test_json ->> 'field6' FROM test_json WHERE json_type = 'object'; SELECT json_object_keys(test_json) FROM test_json WHERE json_type = 'scalar'; SELECT json_object_keys(test_json) FROM test_json WHERE json_type = 'array'; SELECT json_object_keys(test_json) FROM test_json WHERE json_type = 'object'; -- test extending object_keys resultset - initial resultset size is 256 select count(*) from (select json_object_keys(json_object(array_agg(g))) from (select unnest(array['f'||n,n::text])as g from generate_series(1,300) as n) x ) y; -- nulls select (test_json->'field3') is null as expect_false from test_json where json_type = 'object'; select (test_json->>'field3') is null as expect_true from test_json where json_type = 'object'; select (test_json->3) is null as expect_false from test_json where json_type = 'array'; select (test_json->>3) is null as expect_true from test_json where json_type = 'array'; -- corner cases select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json -> null::text; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json -> null::int; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json -> 1; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json -> -1; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json -> 'z'; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json -> ''; select '[{"b": "c"}, {"b": "cc"}]'::json -> 1; select '[{"b": "c"}, {"b": "cc"}]'::json -> 3; select '[{"b": "c"}, {"b": "cc"}]'::json -> 'z'; select '{"a": "c", "b": null}'::json -> 'b'; select '"foo"'::json -> 1; select '"foo"'::json -> 'z'; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json ->> null::text; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json ->> null::int; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json ->> 1; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json ->> 'z'; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json ->> ''; select '[{"b": "c"}, {"b": "cc"}]'::json ->> 1; select '[{"b": "c"}, {"b": "cc"}]'::json ->> 3; select '[{"b": "c"}, {"b": "cc"}]'::json ->> 'z'; select '{"a": "c", "b": null}'::json ->> 'b'; select '"foo"'::json ->> 1; select '"foo"'::json ->> 'z'; -- array length SELECT json_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]'); SELECT json_array_length('[]'); SELECT json_array_length('{"f1":1,"f2":[5,6]}'); SELECT json_array_length('4'); -- each select json_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}'); select * from json_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q; select json_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}'); select * from json_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q; -- extract_path, extract_path_as_text select json_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6'); select json_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2'); select json_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text); select json_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text); select json_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6'); select json_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2'); select json_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text); select json_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text); -- extract_path nulls select json_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false; select json_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true; select json_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false; select json_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true; -- extract_path operators select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json#>array['f4','f6']; select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json#>array['f2']; select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::json#>array['f2','0']; select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::json#>array['f2','1']; select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json#>>array['f4','f6']; select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json#>>array['f2']; select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::json#>>array['f2','0']; select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::json#>>array['f2','1']; -- corner cases for same select '{"a": {"b":{"c": "foo"}}}'::json #> '{}'; select '[1,2,3]'::json #> '{}'; select '"foo"'::json #> '{}'; select '42'::json #> '{}'; select 'null'::json #> '{}'; select '{"a": {"b":{"c": "foo"}}}'::json #> array['a']; select '{"a": {"b":{"c": "foo"}}}'::json #> array['a', null]; select '{"a": {"b":{"c": "foo"}}}'::json #> array['a', '']; select '{"a": {"b":{"c": "foo"}}}'::json #> array['a','b']; select '{"a": {"b":{"c": "foo"}}}'::json #> array['a','b','c']; select '{"a": {"b":{"c": "foo"}}}'::json #> array['a','b','c','d']; select '{"a": {"b":{"c": "foo"}}}'::json #> array['a','z','c']; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json #> array['a','1','b']; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json #> array['a','z','b']; select '[{"b": "c"}, {"b": "cc"}]'::json #> array['1','b']; select '[{"b": "c"}, {"b": "cc"}]'::json #> array['z','b']; select '[{"b": "c"}, {"b": null}]'::json #> array['1','b']; select '"foo"'::json #> array['z']; select '42'::json #> array['f2']; select '42'::json #> array['0']; select '{"a": {"b":{"c": "foo"}}}'::json #>> '{}'; select '[1,2,3]'::json #>> '{}'; select '"foo"'::json #>> '{}'; select '42'::json #>> '{}'; select 'null'::json #>> '{}'; select '{"a": {"b":{"c": "foo"}}}'::json #>> array['a']; select '{"a": {"b":{"c": "foo"}}}'::json #>> array['a', null]; select '{"a": {"b":{"c": "foo"}}}'::json #>> array['a', '']; select '{"a": {"b":{"c": "foo"}}}'::json #>> array['a','b']; select '{"a": {"b":{"c": "foo"}}}'::json #>> array['a','b','c']; select '{"a": {"b":{"c": "foo"}}}'::json #>> array['a','b','c','d']; select '{"a": {"b":{"c": "foo"}}}'::json #>> array['a','z','c']; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json #>> array['a','1','b']; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json #>> array['a','z','b']; select '[{"b": "c"}, {"b": "cc"}]'::json #>> array['1','b']; select '[{"b": "c"}, {"b": "cc"}]'::json #>> array['z','b']; select '[{"b": "c"}, {"b": null}]'::json #>> array['1','b']; select '"foo"'::json #>> array['z']; select '42'::json #>> array['f2']; select '42'::json #>> array['0']; -- array_elements select json_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false,"stringy"]'); select * from json_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false,"stringy"]') q; select json_array_elements_text('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false,"stringy"]'); select * from json_array_elements_text('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false,"stringy"]') q; -- populate_record create type jpop as (a text, b int, c timestamp); CREATE DOMAIN js_int_not_null AS int NOT NULL; CREATE DOMAIN js_int_array_1d AS int[] CHECK(array_length(VALUE, 1) = 3); CREATE DOMAIN js_int_array_2d AS int[][] CHECK(array_length(VALUE, 2) = 3); create type j_unordered_pair as (x int, y int); create domain j_ordered_pair as j_unordered_pair check((value).x <= (value).y); CREATE TYPE jsrec AS ( i int, ia _int4, ia1 int[], ia2 int[][], ia3 int[][][], ia1d js_int_array_1d, ia2d js_int_array_2d, t text, ta text[], c char(10), ca char(10)[], ts timestamp, js json, jsb jsonb, jsa json[], rec jpop, reca jpop[] ); CREATE TYPE jsrec_i_not_null AS ( i js_int_not_null ); select * from json_populate_record(null::jpop,'{"a":"blurfl","x":43.2}') q; select * from json_populate_record(row('x',3,'2012-12-31 15:30:56')::jpop,'{"a":"blurfl","x":43.2}') q; select * from json_populate_record(null::jpop,'{"a":"blurfl","x":43.2}') q; select * from json_populate_record(row('x',3,'2012-12-31 15:30:56')::jpop,'{"a":"blurfl","x":43.2}') q; select * from json_populate_record(null::jpop,'{"a":[100,200,false],"x":43.2}') q; select * from json_populate_record(row('x',3,'2012-12-31 15:30:56')::jpop,'{"a":[100,200,false],"x":43.2}') q; select * from json_populate_record(row('x',3,'2012-12-31 15:30:56')::jpop,'{"c":[100,200,false],"x":43.2}') q; select * from json_populate_record(row('x',3,'2012-12-31 15:30:56')::jpop,'{}') q; SELECT i FROM json_populate_record(NULL::jsrec_i_not_null, '{"x": 43.2}') q; SELECT i FROM json_populate_record(NULL::jsrec_i_not_null, '{"i": null}') q; SELECT i FROM json_populate_record(NULL::jsrec_i_not_null, '{"i": 12345}') q; SELECT ia FROM json_populate_record(NULL::jsrec, '{"ia": null}') q; SELECT ia FROM json_populate_record(NULL::jsrec, '{"ia": 123}') q; SELECT ia FROM json_populate_record(NULL::jsrec, '{"ia": [1, "2", null, 4]}') q; SELECT ia FROM json_populate_record(NULL::jsrec, '{"ia": [[1, 2], [3, 4]]}') q; SELECT ia FROM json_populate_record(NULL::jsrec, '{"ia": [[1], 2]}') q; SELECT ia FROM json_populate_record(NULL::jsrec, '{"ia": [[1], [2, 3]]}') q; SELECT ia FROM json_populate_record(NULL::jsrec, '{"ia": "{1,2,3}"}') q; SELECT ia1 FROM json_populate_record(NULL::jsrec, '{"ia1": null}') q; SELECT ia1 FROM json_populate_record(NULL::jsrec, '{"ia1": 123}') q; SELECT ia1 FROM json_populate_record(NULL::jsrec, '{"ia1": [1, "2", null, 4]}') q; SELECT ia1 FROM json_populate_record(NULL::jsrec, '{"ia1": [[1, 2, 3]]}') q; SELECT ia1d FROM json_populate_record(NULL::jsrec, '{"ia1d": null}') q; SELECT ia1d FROM json_populate_record(NULL::jsrec, '{"ia1d": 123}') q; SELECT ia1d FROM json_populate_record(NULL::jsrec, '{"ia1d": [1, "2", null, 4]}') q; SELECT ia1d FROM json_populate_record(NULL::jsrec, '{"ia1d": [1, "2", null]}') q; SELECT ia2 FROM json_populate_record(NULL::jsrec, '{"ia2": [1, "2", null, 4]}') q; SELECT ia2 FROM json_populate_record(NULL::jsrec, '{"ia2": [[1, 2], [null, 4]]}') q; SELECT ia2 FROM json_populate_record(NULL::jsrec, '{"ia2": [[], []]}') q; SELECT ia2 FROM json_populate_record(NULL::jsrec, '{"ia2": [[1, 2], [3]]}') q; SELECT ia2 FROM json_populate_record(NULL::jsrec, '{"ia2": [[1, 2], 3, 4]}') q; SELECT ia2d FROM json_populate_record(NULL::jsrec, '{"ia2d": [[1, "2"], [null, 4]]}') q; SELECT ia2d FROM json_populate_record(NULL::jsrec, '{"ia2d": [[1, "2", 3], [null, 5, 6]]}') q; SELECT ia3 FROM json_populate_record(NULL::jsrec, '{"ia3": [1, "2", null, 4]}') q; SELECT ia3 FROM json_populate_record(NULL::jsrec, '{"ia3": [[1, 2], [null, 4]]}') q; SELECT ia3 FROM json_populate_record(NULL::jsrec, '{"ia3": [ [[], []], [[], []], [[], []] ]}') q; SELECT ia3 FROM json_populate_record(NULL::jsrec, '{"ia3": [ [[1, 2]], [[3, 4]] ]}') q; SELECT ia3 FROM json_populate_record(NULL::jsrec, '{"ia3": [ [[1, 2], [3, 4]], [[5, 6], [7, 8]] ]}') q; SELECT ia3 FROM json_populate_record(NULL::jsrec, '{"ia3": [ [[1, 2], [3, 4]], [[5, 6], [7, 8], [9, 10]] ]}') q; SELECT ta FROM json_populate_record(NULL::jsrec, '{"ta": null}') q; SELECT ta FROM json_populate_record(NULL::jsrec, '{"ta": 123}') q; SELECT ta FROM json_populate_record(NULL::jsrec, '{"ta": [1, "2", null, 4]}') q; SELECT ta FROM json_populate_record(NULL::jsrec, '{"ta": [[1, 2, 3], {"k": "v"}]}') q; SELECT c FROM json_populate_record(NULL::jsrec, '{"c": null}') q; SELECT c FROM json_populate_record(NULL::jsrec, '{"c": "aaa"}') q; SELECT c FROM json_populate_record(NULL::jsrec, '{"c": "aaaaaaaaaa"}') q; SELECT c FROM json_populate_record(NULL::jsrec, '{"c": "aaaaaaaaaaaaa"}') q; SELECT ca FROM json_populate_record(NULL::jsrec, '{"ca": null}') q; SELECT ca FROM json_populate_record(NULL::jsrec, '{"ca": 123}') q; SELECT ca FROM json_populate_record(NULL::jsrec, '{"ca": [1, "2", null, 4]}') q; SELECT ca FROM json_populate_record(NULL::jsrec, '{"ca": ["aaaaaaaaaaaaaaaa"]}') q; SELECT ca FROM json_populate_record(NULL::jsrec, '{"ca": [[1, 2, 3], {"k": "v"}]}') q; SELECT js FROM json_populate_record(NULL::jsrec, '{"js": null}') q; SELECT js FROM json_populate_record(NULL::jsrec, '{"js": true}') q; SELECT js FROM json_populate_record(NULL::jsrec, '{"js": 123.45}') q; SELECT js FROM json_populate_record(NULL::jsrec, '{"js": "123.45"}') q; SELECT js FROM json_populate_record(NULL::jsrec, '{"js": "abc"}') q; SELECT js FROM json_populate_record(NULL::jsrec, '{"js": [123, "123", null, {"key": "value"}]}') q; SELECT js FROM json_populate_record(NULL::jsrec, '{"js": {"a": "bbb", "b": null, "c": 123.45}}') q; SELECT jsb FROM json_populate_record(NULL::jsrec, '{"jsb": null}') q; SELECT jsb FROM json_populate_record(NULL::jsrec, '{"jsb": true}') q; SELECT jsb FROM json_populate_record(NULL::jsrec, '{"jsb": 123.45}') q; SELECT jsb FROM json_populate_record(NULL::jsrec, '{"jsb": "123.45"}') q; SELECT jsb FROM json_populate_record(NULL::jsrec, '{"jsb": "abc"}') q; SELECT jsb FROM json_populate_record(NULL::jsrec, '{"jsb": [123, "123", null, {"key": "value"}]}') q; SELECT jsb FROM json_populate_record(NULL::jsrec, '{"jsb": {"a": "bbb", "b": null, "c": 123.45}}') q; SELECT jsa FROM json_populate_record(NULL::jsrec, '{"jsa": null}') q; SELECT jsa FROM json_populate_record(NULL::jsrec, '{"jsa": 123}') q; SELECT jsa FROM json_populate_record(NULL::jsrec, '{"jsa": [1, "2", null, 4]}') q; SELECT jsa FROM json_populate_record(NULL::jsrec, '{"jsa": ["aaa", null, [1, 2, "3", {}], { "k" : "v" }]}') q; SELECT rec FROM json_populate_record(NULL::jsrec, '{"rec": 123}') q; SELECT rec FROM json_populate_record(NULL::jsrec, '{"rec": [1, 2]}') q; SELECT rec FROM json_populate_record(NULL::jsrec, '{"rec": {"a": "abc", "c": "01.02.2003", "x": 43.2}}') q; SELECT rec FROM json_populate_record(NULL::jsrec, '{"rec": "(abc,42,01.02.2003)"}') q; SELECT reca FROM json_populate_record(NULL::jsrec, '{"reca": 123}') q; SELECT reca FROM json_populate_record(NULL::jsrec, '{"reca": [1, 2]}') q; SELECT reca FROM json_populate_record(NULL::jsrec, '{"reca": [{"a": "abc", "b": 456}, null, {"c": "01.02.2003", "x": 43.2}]}') q; SELECT reca FROM json_populate_record(NULL::jsrec, '{"reca": ["(abc,42,01.02.2003)"]}') q; SELECT reca FROM json_populate_record(NULL::jsrec, '{"reca": "{\"(abc,42,01.02.2003)\"}"}') q; SELECT rec FROM json_populate_record( row(NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, row('x',3,'2012-12-31 15:30:56')::jpop,NULL)::jsrec, '{"rec": {"a": "abc", "c": "01.02.2003", "x": 43.2}}' ) q; -- anonymous record type SELECT json_populate_record(null::record, '{"x": 0, "y": 1}'); SELECT json_populate_record(row(1,2), '{"f1": 0, "f2": 1}'); -- composite domain SELECT json_populate_record(null::j_ordered_pair, '{"x": 0, "y": 1}'); SELECT json_populate_record(row(1,2)::j_ordered_pair, '{"x": 0}'); SELECT json_populate_record(row(1,2)::j_ordered_pair, '{"x": 1, "y": 0}'); -- populate_recordset select * from json_populate_recordset(null::jpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; select * from json_populate_recordset(null::jpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q; select * from json_populate_recordset(row('def',99,null)::jpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q; create type jpop2 as (a int, b json, c int, d int); select * from json_populate_recordset(null::jpop2, '[{"a":2,"c":3,"b":{"z":4},"d":6}]') q; select * from json_populate_recordset(null::jpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q; -- anonymous record type SELECT json_populate_recordset(null::record, '[{"x": 0, "y": 1}]'); SELECT json_populate_recordset(row(1,2), '[{"f1": 0, "f2": 1}]'); SELECT i, json_populate_recordset(row(i,50), '[{"f1":"42"},{"f2":"43"}]') FROM (VALUES (1),(2)) v(i); -- empty array is a corner case SELECT json_populate_recordset(null::record, '[]'); SELECT json_populate_recordset(row(1,2), '[]'); SELECT * FROM json_populate_recordset(NULL::jpop,'[]') q; -- composite domain SELECT json_populate_recordset(null::j_ordered_pair, '[{"x": 0, "y": 1}]'); SELECT json_populate_recordset(row(1,2)::j_ordered_pair, '[{"x": 0}, {"y": 3}]'); SELECT json_populate_recordset(row(1,2)::j_ordered_pair, '[{"x": 1, "y": 0}]'); -- negative cases where the wrong record type is supplied select * from json_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text); select * from json_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text); select * from json_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text); select * from json_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text); -- test type info caching in json_populate_record() CREATE TEMP TABLE jspoptest (js json); INSERT INTO jspoptest SELECT '{ "jsa": [1, "2", null, 4], "rec": {"a": "abc", "c": "01.02.2003", "x": 43.2}, "reca": [{"a": "abc", "b": 456}, null, {"c": "01.02.2003", "x": 43.2}] }'::json FROM generate_series(1, 3); SELECT (json_populate_record(NULL::jsrec, js)).* FROM jspoptest; DROP TYPE jsrec; DROP TYPE jsrec_i_not_null; DROP DOMAIN js_int_not_null; DROP DOMAIN js_int_array_1d; DROP DOMAIN js_int_array_2d; DROP DOMAIN j_ordered_pair; DROP TYPE j_unordered_pair; --json_typeof() function select value, json_typeof(value) from (values (json '123.4'), (json '-1'), (json '"foo"'), (json 'true'), (json 'false'), (json 'null'), (json '[1, 2, 3]'), (json '[]'), (json '{"x":"foo", "y":123}'), (json '{}'), (NULL::json)) as data(value); -- json_build_array, json_build_object, json_object_agg SELECT json_build_array('a',1,'b',1.2,'c',true,'d',null,'e',json '{"x": 3, "y": [1,2,3]}'); SELECT json_build_array('a', NULL); -- ok SELECT json_build_array(VARIADIC NULL::text[]); -- ok SELECT json_build_array(VARIADIC '{}'::text[]); -- ok SELECT json_build_array(VARIADIC '{a,b,c}'::text[]); -- ok SELECT json_build_array(VARIADIC ARRAY['a', NULL]::text[]); -- ok SELECT json_build_array(VARIADIC '{1,2,3,4}'::text[]); -- ok SELECT json_build_array(VARIADIC '{1,2,3,4}'::int[]); -- ok SELECT json_build_array(VARIADIC '{{1,4},{2,5},{3,6}}'::int[][]); -- ok SELECT json_build_object('a',1,'b',1.2,'c',true,'d',null,'e',json '{"x": 3, "y": [1,2,3]}'); SELECT json_build_object( 'a', json_build_object('b',false,'c',99), 'd', json_build_object('e',array[9,8,7]::int[], 'f', (select row_to_json(r) from ( select relkind, oid::regclass as name from pg_class where relname = 'pg_class') r))); SELECT json_build_object('{a,b,c}'::text[]); -- error SELECT json_build_object('{a,b,c}'::text[], '{d,e,f}'::text[]); -- error, key cannot be array SELECT json_build_object('a', 'b', 'c'); -- error SELECT json_build_object(NULL, 'a'); -- error, key cannot be NULL SELECT json_build_object('a', NULL); -- ok SELECT json_build_object(VARIADIC NULL::text[]); -- ok SELECT json_build_object(VARIADIC '{}'::text[]); -- ok SELECT json_build_object(VARIADIC '{a,b,c}'::text[]); -- error SELECT json_build_object(VARIADIC ARRAY['a', NULL]::text[]); -- ok SELECT json_build_object(VARIADIC ARRAY[NULL, 'a']::text[]); -- error, key cannot be NULL SELECT json_build_object(VARIADIC '{1,2,3,4}'::text[]); -- ok SELECT json_build_object(VARIADIC '{1,2,3,4}'::int[]); -- ok SELECT json_build_object(VARIADIC '{{1,4},{2,5},{3,6}}'::int[][]); -- ok -- empty objects/arrays SELECT json_build_array(); SELECT json_build_object(); -- make sure keys are quoted SELECT json_build_object(1,2); -- keys must be scalar and not null SELECT json_build_object(null,2); SELECT json_build_object(r,2) FROM (SELECT 1 AS a, 2 AS b) r; SELECT json_build_object(json '{"a":1,"b":2}', 3); SELECT json_build_object('{1,2,3}'::int[], 3); CREATE TEMP TABLE foo (serial_num int, name text, type text); INSERT INTO foo VALUES (847001,'t15','GE1043'); INSERT INTO foo VALUES (847002,'t16','GE1043'); INSERT INTO foo VALUES (847003,'sub-alpha','GESS90'); SELECT json_build_object('turbines',json_object_agg(serial_num,json_build_object('name',name,'type',type))) FROM foo; SELECT json_object_agg(name, type) FROM foo; INSERT INTO foo VALUES (999999, NULL, 'bar'); SELECT json_object_agg(name, type) FROM foo; -- json_object -- empty object, one dimension SELECT json_object('{}'); -- empty object, two dimensions SELECT json_object('{}', '{}'); -- one dimension SELECT json_object('{a,1,b,2,3,NULL,"d e f","a b c"}'); -- same but with two dimensions SELECT json_object('{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}'); -- odd number error SELECT json_object('{a,b,c}'); -- one column error SELECT json_object('{{a},{b}}'); -- too many columns error SELECT json_object('{{a,b,c},{b,c,d}}'); -- too many dimensions error SELECT json_object('{{{a,b},{c,d}},{{b,c},{d,e}}}'); --two argument form of json_object select json_object('{a,b,c,"d e f"}','{1,2,3,"a b c"}'); -- too many dimensions SELECT json_object('{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}', '{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}'); -- mismatched dimensions select json_object('{a,b,c,"d e f",g}','{1,2,3,"a b c"}'); select json_object('{a,b,c,"d e f"}','{1,2,3,"a b c",g}'); -- null key error select json_object('{a,b,NULL,"d e f"}','{1,2,3,"a b c"}'); -- empty key is allowed select json_object('{a,b,"","d e f"}','{1,2,3,"a b c"}'); -- json_to_record and json_to_recordset select * from json_to_record('{"a":1,"b":"foo","c":"bar"}') as x(a int, b text, d text); select * from json_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar","c":true}]') as x(a int, b text, c boolean); select * from json_to_recordset('[{"a":1,"b":{"d":"foo"},"c":true},{"a":2,"c":false,"b":{"d":"bar"}}]') as x(a int, b json, c boolean); select *, c is null as c_is_null from json_to_record('{"a":1, "b":{"c":16, "d":2}, "x":8, "ca": ["1 2", 3], "ia": [[1,2],[3,4]], "r": {"a": "aaa", "b": 123}}'::json) as t(a int, b json, c text, x int, ca char(5)[], ia int[][], r jpop); select *, c is null as c_is_null from json_to_recordset('[{"a":1, "b":{"c":16, "d":2}, "x":8}]'::json) as t(a int, b json, c text, x int); select * from json_to_record('{"ia": null}') as x(ia _int4); select * from json_to_record('{"ia": 123}') as x(ia _int4); select * from json_to_record('{"ia": [1, "2", null, 4]}') as x(ia _int4); select * from json_to_record('{"ia": [[1, 2], [3, 4]]}') as x(ia _int4); select * from json_to_record('{"ia": [[1], 2]}') as x(ia _int4); select * from json_to_record('{"ia": [[1], [2, 3]]}') as x(ia _int4); select * from json_to_record('{"ia2": [1, 2, 3]}') as x(ia2 int[][]); select * from json_to_record('{"ia2": [[1, 2], [3, 4]]}') as x(ia2 int4[][]); select * from json_to_record('{"ia2": [[[1], [2], [3]]]}') as x(ia2 int4[][]); -- json_strip_nulls select json_strip_nulls(null); select json_strip_nulls('1'); select json_strip_nulls('"a string"'); select json_strip_nulls('null'); select json_strip_nulls('[1,2,null,3,4]'); select json_strip_nulls('{"a":1,"b":null,"c":[2,null,3],"d":{"e":4,"f":null}}'); select json_strip_nulls('[1,{"a":1,"b":null,"c":2},3]'); -- an empty object is not null and should not be stripped select json_strip_nulls('{"a": {"b": null, "c": null}, "d": {} }'); -- json to tsvector select to_tsvector('{"a": "aaa bbb ddd ccc", "b": ["eee fff ggg"], "c": {"d": "hhh iii"}}'::json); -- json to tsvector with config select to_tsvector('simple', '{"a": "aaa bbb ddd ccc", "b": ["eee fff ggg"], "c": {"d": "hhh iii"}}'::json); -- json to tsvector with stop words select to_tsvector('english', '{"a": "aaa in bbb ddd ccc", "b": ["the eee fff ggg"], "c": {"d": "hhh. iii"}}'::json); -- json to tsvector with numeric values select to_tsvector('english', '{"a": "aaa in bbb ddd ccc", "b": 123, "c": 456}'::json); -- json_to_tsvector select json_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"all"'); select json_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"key"'); select json_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"string"'); select json_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"numeric"'); select json_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"boolean"'); select json_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '["string", "numeric"]'); select json_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"all"'); select json_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"key"'); select json_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"string"'); select json_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"numeric"'); select json_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '"boolean"'); select json_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '["string", "numeric"]'); -- to_tsvector corner cases select to_tsvector('""'::json); select to_tsvector('{}'::json); select to_tsvector('[]'::json); select to_tsvector('null'::json); -- json_to_tsvector corner cases select json_to_tsvector('""'::json, '"all"'); select json_to_tsvector('{}'::json, '"all"'); select json_to_tsvector('[]'::json, '"all"'); select json_to_tsvector('null'::json, '"all"'); select json_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '""'); select json_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '{}'); select json_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '[]'); select json_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, 'null'); select json_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::json, '["all", null]'); -- ts_headline for json select ts_headline('{"a": "aaa bbb", "b": {"c": "ccc ddd fff", "c1": "ccc1 ddd1"}, "d": ["ggg hhh", "iii jjj"]}'::json, tsquery('bbb & ddd & hhh')); select ts_headline('english', '{"a": "aaa bbb", "b": {"c": "ccc ddd fff"}, "d": ["ggg hhh", "iii jjj"]}'::json, tsquery('bbb & ddd & hhh')); select ts_headline('{"a": "aaa bbb", "b": {"c": "ccc ddd fff", "c1": "ccc1 ddd1"}, "d": ["ggg hhh", "iii jjj"]}'::json, tsquery('bbb & ddd & hhh'), 'StartSel = <, StopSel = >'); select ts_headline('english', '{"a": "aaa bbb", "b": {"c": "ccc ddd fff", "c1": "ccc1 ddd1"}, "d": ["ggg hhh", "iii jjj"]}'::json, tsquery('bbb & ddd & hhh'), 'StartSel = <, StopSel = >'); -- corner cases for ts_headline with json select ts_headline('null'::json, tsquery('aaa & bbb')); select ts_headline('{}'::json, tsquery('aaa & bbb')); select ts_headline('[]'::json, tsquery('aaa & bbb')); pgFormatter-4.2/t/pg-test-files/sql/json_encoding.sql000066400000000000000000000061571361326045100227500ustar00rootroot00000000000000 -- encoding-sensitive tests for json and jsonb -- first json -- basic unicode input SELECT '"\u"'::json; -- ERROR, incomplete escape SELECT '"\u00"'::json; -- ERROR, incomplete escape SELECT '"\u000g"'::json; -- ERROR, g is not a hex digit SELECT '"\u0000"'::json; -- OK, legal escape SELECT '"\uaBcD"'::json; -- OK, uppercase and lower case both OK -- handling of unicode surrogate pairs select json '{ "a": "\ud83d\ude04\ud83d\udc36" }' -> 'a' as correct_in_utf8; select json '{ "a": "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row select json '{ "a": "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order select json '{ "a": "\ud83dX" }' -> 'a'; -- orphan high surrogate select json '{ "a": "\ude04X" }' -> 'a'; -- orphan low surrogate --handling of simple unicode escapes select json '{ "a": "the Copyright \u00a9 sign" }' as correct_in_utf8; select json '{ "a": "dollar \u0024 character" }' as correct_everywhere; select json '{ "a": "dollar \\u0024 character" }' as not_an_escape; select json '{ "a": "null \u0000 escape" }' as not_unescaped; select json '{ "a": "null \\u0000 escape" }' as not_an_escape; select json '{ "a": "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8; select json '{ "a": "dollar \u0024 character" }' ->> 'a' as correct_everywhere; select json '{ "a": "dollar \\u0024 character" }' ->> 'a' as not_an_escape; select json '{ "a": "null \u0000 escape" }' ->> 'a' as fails; select json '{ "a": "null \\u0000 escape" }' ->> 'a' as not_an_escape; -- then jsonb -- basic unicode input SELECT '"\u"'::jsonb; -- ERROR, incomplete escape SELECT '"\u00"'::jsonb; -- ERROR, incomplete escape SELECT '"\u000g"'::jsonb; -- ERROR, g is not a hex digit SELECT '"\u0045"'::jsonb; -- OK, legal escape SELECT '"\u0000"'::jsonb; -- ERROR, we don't support U+0000 -- use octet_length here so we don't get an odd unicode char in the -- output SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK -- handling of unicode surrogate pairs SELECT octet_length((jsonb '{ "a": "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text) AS correct_in_utf8; SELECT jsonb '{ "a": "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row SELECT jsonb '{ "a": "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order SELECT jsonb '{ "a": "\ud83dX" }' -> 'a'; -- orphan high surrogate SELECT jsonb '{ "a": "\ude04X" }' -> 'a'; -- orphan low surrogate -- handling of simple unicode escapes SELECT jsonb '{ "a": "the Copyright \u00a9 sign" }' as correct_in_utf8; SELECT jsonb '{ "a": "dollar \u0024 character" }' as correct_everywhere; SELECT jsonb '{ "a": "dollar \\u0024 character" }' as not_an_escape; SELECT jsonb '{ "a": "null \u0000 escape" }' as fails; SELECT jsonb '{ "a": "null \\u0000 escape" }' as not_an_escape; SELECT jsonb '{ "a": "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8; SELECT jsonb '{ "a": "dollar \u0024 character" }' ->> 'a' as correct_everywhere; SELECT jsonb '{ "a": "dollar \\u0024 character" }' ->> 'a' as not_an_escape; SELECT jsonb '{ "a": "null \u0000 escape" }' ->> 'a' as fails; SELECT jsonb '{ "a": "null \\u0000 escape" }' ->> 'a' as not_an_escape; pgFormatter-4.2/t/pg-test-files/sql/jsonb.sql000066400000000000000000001675721361326045100212550ustar00rootroot00000000000000-- Strings. SELECT '""'::jsonb; -- OK. SELECT $$''$$::jsonb; -- ERROR, single quotes are not allowed SELECT '"abc"'::jsonb; -- OK SELECT '"abc'::jsonb; -- ERROR, quotes not closed SELECT '"abc def"'::jsonb; -- ERROR, unescaped newline in string constant SELECT '"\n\"\\"'::jsonb; -- OK, legal escapes SELECT '"\v"'::jsonb; -- ERROR, not a valid JSON escape -- see json_encoding test for input with unicode escapes -- Numbers. SELECT '1'::jsonb; -- OK SELECT '0'::jsonb; -- OK SELECT '01'::jsonb; -- ERROR, not valid according to JSON spec SELECT '0.1'::jsonb; -- OK SELECT '9223372036854775808'::jsonb; -- OK, even though it's too large for int8 SELECT '1e100'::jsonb; -- OK SELECT '1.3e100'::jsonb; -- OK SELECT '1f2'::jsonb; -- ERROR SELECT '0.x1'::jsonb; -- ERROR SELECT '1.3ex100'::jsonb; -- ERROR -- Arrays. SELECT '[]'::jsonb; -- OK SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb; -- OK SELECT '[1,2]'::jsonb; -- OK SELECT '[1,2,]'::jsonb; -- ERROR, trailing comma SELECT '[1,2'::jsonb; -- ERROR, no closing bracket SELECT '[1,[2]'::jsonb; -- ERROR, no closing bracket -- Objects. SELECT '{}'::jsonb; -- OK SELECT '{"abc"}'::jsonb; -- ERROR, no value SELECT '{"abc":1}'::jsonb; -- OK SELECT '{1:"abc"}'::jsonb; -- ERROR, keys must be strings SELECT '{"abc",1}'::jsonb; -- ERROR, wrong separator SELECT '{"abc"=1}'::jsonb; -- ERROR, totally wrong separator SELECT '{"abc"::1}'::jsonb; -- ERROR, another wrong separator SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK SELECT '{"abc":1:2}'::jsonb; -- ERROR, colon in wrong spot SELECT '{"abc":1,3}'::jsonb; -- ERROR, no value -- Recursion. SET max_stack_depth = '100kB'; SELECT repeat('[', 10000)::jsonb; SELECT repeat('{"a":', 10000)::jsonb; RESET max_stack_depth; -- Miscellaneous stuff. SELECT 'true'::jsonb; -- OK SELECT 'false'::jsonb; -- OK SELECT 'null'::jsonb; -- OK SELECT ' true '::jsonb; -- OK, even with extra whitespace SELECT 'true false'::jsonb; -- ERROR, too many values SELECT 'true, false'::jsonb; -- ERROR, too many values SELECT 'truf'::jsonb; -- ERROR, not a keyword SELECT 'trues'::jsonb; -- ERROR, not a keyword SELECT ''::jsonb; -- ERROR, no value SELECT ' '::jsonb; -- ERROR, no value -- make sure jsonb is passed through json generators without being escaped SELECT array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']); -- anyarray column select to_jsonb(histogram_bounds) histogram_bounds from pg_stats where attname = 'tmplname' and tablename = 'pg_pltemplate'; -- to_jsonb, timestamps select to_jsonb(timestamp '2014-05-28 12:22:35.614298'); BEGIN; SET LOCAL TIME ZONE 10.5; select to_jsonb(timestamptz '2014-05-28 12:22:35.614298-04'); SET LOCAL TIME ZONE -8; select to_jsonb(timestamptz '2014-05-28 12:22:35.614298-04'); COMMIT; select to_jsonb(date '2014-05-28'); select to_jsonb(date 'Infinity'); select to_jsonb(date '-Infinity'); select to_jsonb(timestamp 'Infinity'); select to_jsonb(timestamp '-Infinity'); select to_jsonb(timestamptz 'Infinity'); select to_jsonb(timestamptz '-Infinity'); --jsonb_agg CREATE TEMP TABLE rows AS SELECT x, 'txt' || x as y FROM generate_series(1,3) AS x; SELECT jsonb_agg(q) FROM ( SELECT $$a$$ || x AS b, y AS c, ARRAY[ROW(x.*,ARRAY[1,2,3]), ROW(y.*,ARRAY[4,5,6])] AS z FROM generate_series(1,2) x, generate_series(4,5) y) q; SELECT jsonb_agg(q ORDER BY x, y) FROM rows q; UPDATE rows SET x = NULL WHERE x = 1; SELECT jsonb_agg(q ORDER BY x NULLS FIRST, y) FROM rows q; -- jsonb extraction functions CREATE TEMP TABLE test_jsonb ( json_type text, test_json jsonb ); INSERT INTO test_jsonb VALUES ('scalar','"a scalar"'), ('array','["zero", "one","two",null,"four","five", [1,2,3],{"f1":9}]'), ('object','{"field1":"val1","field2":"val2","field3":null, "field4": 4, "field5": [1,2,3], "field6": {"f1":9}}'); SELECT test_json -> 'x' FROM test_jsonb WHERE json_type = 'scalar'; SELECT test_json -> 'x' FROM test_jsonb WHERE json_type = 'array'; SELECT test_json -> 'x' FROM test_jsonb WHERE json_type = 'object'; SELECT test_json -> 'field2' FROM test_jsonb WHERE json_type = 'object'; SELECT test_json ->> 'field2' FROM test_jsonb WHERE json_type = 'scalar'; SELECT test_json ->> 'field2' FROM test_jsonb WHERE json_type = 'array'; SELECT test_json ->> 'field2' FROM test_jsonb WHERE json_type = 'object'; SELECT test_json -> 2 FROM test_jsonb WHERE json_type = 'scalar'; SELECT test_json -> 2 FROM test_jsonb WHERE json_type = 'array'; SELECT test_json -> 9 FROM test_jsonb WHERE json_type = 'array'; SELECT test_json -> 2 FROM test_jsonb WHERE json_type = 'object'; SELECT test_json ->> 6 FROM test_jsonb WHERE json_type = 'array'; SELECT test_json ->> 7 FROM test_jsonb WHERE json_type = 'array'; SELECT test_json ->> 'field4' FROM test_jsonb WHERE json_type = 'object'; SELECT test_json ->> 'field5' FROM test_jsonb WHERE json_type = 'object'; SELECT test_json ->> 'field6' FROM test_jsonb WHERE json_type = 'object'; SELECT test_json ->> 2 FROM test_jsonb WHERE json_type = 'scalar'; SELECT test_json ->> 2 FROM test_jsonb WHERE json_type = 'array'; SELECT test_json ->> 2 FROM test_jsonb WHERE json_type = 'object'; SELECT jsonb_object_keys(test_json) FROM test_jsonb WHERE json_type = 'scalar'; SELECT jsonb_object_keys(test_json) FROM test_jsonb WHERE json_type = 'array'; SELECT jsonb_object_keys(test_json) FROM test_jsonb WHERE json_type = 'object'; -- nulls SELECT (test_json->'field3') IS NULL AS expect_false FROM test_jsonb WHERE json_type = 'object'; SELECT (test_json->>'field3') IS NULL AS expect_true FROM test_jsonb WHERE json_type = 'object'; SELECT (test_json->3) IS NULL AS expect_false FROM test_jsonb WHERE json_type = 'array'; SELECT (test_json->>3) IS NULL AS expect_true FROM test_jsonb WHERE json_type = 'array'; -- corner cases select '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb -> null::text; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb -> null::int; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb -> 1; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb -> 'z'; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb -> ''; select '[{"b": "c"}, {"b": "cc"}]'::jsonb -> 1; select '[{"b": "c"}, {"b": "cc"}]'::jsonb -> 3; select '[{"b": "c"}, {"b": "cc"}]'::jsonb -> 'z'; select '{"a": "c", "b": null}'::jsonb -> 'b'; select '"foo"'::jsonb -> 1; select '"foo"'::jsonb -> 'z'; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb ->> null::text; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb ->> null::int; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb ->> 1; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb ->> 'z'; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb ->> ''; select '[{"b": "c"}, {"b": "cc"}]'::jsonb ->> 1; select '[{"b": "c"}, {"b": "cc"}]'::jsonb ->> 3; select '[{"b": "c"}, {"b": "cc"}]'::jsonb ->> 'z'; select '{"a": "c", "b": null}'::jsonb ->> 'b'; select '"foo"'::jsonb ->> 1; select '"foo"'::jsonb ->> 'z'; -- equality and inequality SELECT '{"x":"y"}'::jsonb = '{"x":"y"}'::jsonb; SELECT '{"x":"y"}'::jsonb = '{"x":"z"}'::jsonb; SELECT '{"x":"y"}'::jsonb <> '{"x":"y"}'::jsonb; SELECT '{"x":"y"}'::jsonb <> '{"x":"z"}'::jsonb; -- containment SELECT jsonb_contains('{"a":"b", "b":1, "c":null}', '{"a":"b"}'); SELECT jsonb_contains('{"a":"b", "b":1, "c":null}', '{"a":"b", "c":null}'); SELECT jsonb_contains('{"a":"b", "b":1, "c":null}', '{"a":"b", "g":null}'); SELECT jsonb_contains('{"a":"b", "b":1, "c":null}', '{"g":null}'); SELECT jsonb_contains('{"a":"b", "b":1, "c":null}', '{"a":"c"}'); SELECT jsonb_contains('{"a":"b", "b":1, "c":null}', '{"a":"b"}'); SELECT jsonb_contains('{"a":"b", "b":1, "c":null}', '{"a":"b", "c":"q"}'); SELECT '{"a":"b", "b":1, "c":null}'::jsonb @> '{"a":"b"}'; SELECT '{"a":"b", "b":1, "c":null}'::jsonb @> '{"a":"b", "c":null}'; SELECT '{"a":"b", "b":1, "c":null}'::jsonb @> '{"a":"b", "g":null}'; SELECT '{"a":"b", "b":1, "c":null}'::jsonb @> '{"g":null}'; SELECT '{"a":"b", "b":1, "c":null}'::jsonb @> '{"a":"c"}'; SELECT '{"a":"b", "b":1, "c":null}'::jsonb @> '{"a":"b"}'; SELECT '{"a":"b", "b":1, "c":null}'::jsonb @> '{"a":"b", "c":"q"}'; SELECT '[1,2]'::jsonb @> '[1,2,2]'::jsonb; SELECT '[1,1,2]'::jsonb @> '[1,2,2]'::jsonb; SELECT '[[1,2]]'::jsonb @> '[[1,2,2]]'::jsonb; SELECT '[1,2,2]'::jsonb <@ '[1,2]'::jsonb; SELECT '[1,2,2]'::jsonb <@ '[1,1,2]'::jsonb; SELECT '[[1,2,2]]'::jsonb <@ '[[1,2]]'::jsonb; SELECT jsonb_contained('{"a":"b"}', '{"a":"b", "b":1, "c":null}'); SELECT jsonb_contained('{"a":"b", "c":null}', '{"a":"b", "b":1, "c":null}'); SELECT jsonb_contained('{"a":"b", "g":null}', '{"a":"b", "b":1, "c":null}'); SELECT jsonb_contained('{"g":null}', '{"a":"b", "b":1, "c":null}'); SELECT jsonb_contained('{"a":"c"}', '{"a":"b", "b":1, "c":null}'); SELECT jsonb_contained('{"a":"b"}', '{"a":"b", "b":1, "c":null}'); SELECT jsonb_contained('{"a":"b", "c":"q"}', '{"a":"b", "b":1, "c":null}'); SELECT '{"a":"b"}'::jsonb <@ '{"a":"b", "b":1, "c":null}'; SELECT '{"a":"b", "c":null}'::jsonb <@ '{"a":"b", "b":1, "c":null}'; SELECT '{"a":"b", "g":null}'::jsonb <@ '{"a":"b", "b":1, "c":null}'; SELECT '{"g":null}'::jsonb <@ '{"a":"b", "b":1, "c":null}'; SELECT '{"a":"c"}'::jsonb <@ '{"a":"b", "b":1, "c":null}'; SELECT '{"a":"b"}'::jsonb <@ '{"a":"b", "b":1, "c":null}'; SELECT '{"a":"b", "c":"q"}'::jsonb <@ '{"a":"b", "b":1, "c":null}'; -- Raw scalar may contain another raw scalar, array may contain a raw scalar SELECT '[5]'::jsonb @> '[5]'; SELECT '5'::jsonb @> '5'; SELECT '[5]'::jsonb @> '5'; -- But a raw scalar cannot contain an array SELECT '5'::jsonb @> '[5]'; -- In general, one thing should always contain itself. Test array containment: SELECT '["9", ["7", "3"], 1]'::jsonb @> '["9", ["7", "3"], 1]'::jsonb; SELECT '["9", ["7", "3"], ["1"]]'::jsonb @> '["9", ["7", "3"], ["1"]]'::jsonb; -- array containment string matching confusion bug SELECT '{ "name": "Bob", "tags": [ "enim", "qui"]}'::jsonb @> '{"tags":["qu"]}'; -- array length SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]'); SELECT jsonb_array_length('[]'); SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}'); SELECT jsonb_array_length('4'); -- each SELECT jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}'); SELECT jsonb_each('{"a":{"b":"c","c":"b","1":"first"},"b":[1,2],"c":"cc","1":"first","n":null}'::jsonb) AS q; SELECT * FROM jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q; SELECT * FROM jsonb_each('{"a":{"b":"c","c":"b","1":"first"},"b":[1,2],"c":"cc","1":"first","n":null}'::jsonb) AS q; SELECT jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}'); SELECT jsonb_each_text('{"a":{"b":"c","c":"b","1":"first"},"b":[1,2],"c":"cc","1":"first","n":null}'::jsonb) AS q; SELECT * FROM jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q; SELECT * FROM jsonb_each_text('{"a":{"b":"c","c":"b","1":"first"},"b":[1,2],"c":"cc","1":"first","n":null}'::jsonb) AS q; -- exists SELECT jsonb_exists('{"a":null, "b":"qq"}', 'a'); SELECT jsonb_exists('{"a":null, "b":"qq"}', 'b'); SELECT jsonb_exists('{"a":null, "b":"qq"}', 'c'); SELECT jsonb_exists('{"a":"null", "b":"qq"}', 'a'); SELECT jsonb '{"a":null, "b":"qq"}' ? 'a'; SELECT jsonb '{"a":null, "b":"qq"}' ? 'b'; SELECT jsonb '{"a":null, "b":"qq"}' ? 'c'; SELECT jsonb '{"a":"null", "b":"qq"}' ? 'a'; -- array exists - array elements should behave as keys SELECT count(*) from testjsonb WHERE j->'array' ? 'bar'; -- type sensitive array exists - should return no rows (since "exists" only -- matches strings that are either object keys or array elements) SELECT count(*) from testjsonb WHERE j->'array' ? '5'::text; -- However, a raw scalar is *contained* within the array SELECT count(*) from testjsonb WHERE j->'array' @> '5'::jsonb; SELECT jsonb_exists_any('{"a":null, "b":"qq"}', ARRAY['a','b']); SELECT jsonb_exists_any('{"a":null, "b":"qq"}', ARRAY['b','a']); SELECT jsonb_exists_any('{"a":null, "b":"qq"}', ARRAY['c','a']); SELECT jsonb_exists_any('{"a":null, "b":"qq"}', ARRAY['c','d']); SELECT jsonb_exists_any('{"a":null, "b":"qq"}', '{}'::text[]); SELECT jsonb '{"a":null, "b":"qq"}' ?| ARRAY['a','b']; SELECT jsonb '{"a":null, "b":"qq"}' ?| ARRAY['b','a']; SELECT jsonb '{"a":null, "b":"qq"}' ?| ARRAY['c','a']; SELECT jsonb '{"a":null, "b":"qq"}' ?| ARRAY['c','d']; SELECT jsonb '{"a":null, "b":"qq"}' ?| '{}'::text[]; SELECT jsonb_exists_all('{"a":null, "b":"qq"}', ARRAY['a','b']); SELECT jsonb_exists_all('{"a":null, "b":"qq"}', ARRAY['b','a']); SELECT jsonb_exists_all('{"a":null, "b":"qq"}', ARRAY['c','a']); SELECT jsonb_exists_all('{"a":null, "b":"qq"}', ARRAY['c','d']); SELECT jsonb_exists_all('{"a":null, "b":"qq"}', '{}'::text[]); SELECT jsonb '{"a":null, "b":"qq"}' ?& ARRAY['a','b']; SELECT jsonb '{"a":null, "b":"qq"}' ?& ARRAY['b','a']; SELECT jsonb '{"a":null, "b":"qq"}' ?& ARRAY['c','a']; SELECT jsonb '{"a":null, "b":"qq"}' ?& ARRAY['c','d']; SELECT jsonb '{"a":null, "b":"qq"}' ?& ARRAY['a','a', 'b', 'b', 'b']; SELECT jsonb '{"a":null, "b":"qq"}' ?& '{}'::text[]; -- typeof SELECT jsonb_typeof('{}') AS object; SELECT jsonb_typeof('{"c":3,"p":"o"}') AS object; SELECT jsonb_typeof('[]') AS array; SELECT jsonb_typeof('["a", 1]') AS array; SELECT jsonb_typeof('null') AS "null"; SELECT jsonb_typeof('1') AS number; SELECT jsonb_typeof('-1') AS number; SELECT jsonb_typeof('1.0') AS number; SELECT jsonb_typeof('1e2') AS number; SELECT jsonb_typeof('-1.0') AS number; SELECT jsonb_typeof('true') AS boolean; SELECT jsonb_typeof('false') AS boolean; SELECT jsonb_typeof('"hello"') AS string; SELECT jsonb_typeof('"true"') AS string; SELECT jsonb_typeof('"1.0"') AS string; -- jsonb_build_array, jsonb_build_object, jsonb_object_agg SELECT jsonb_build_array('a',1,'b',1.2,'c',true,'d',null,'e',json '{"x": 3, "y": [1,2,3]}'); SELECT jsonb_build_array('a', NULL); -- ok SELECT jsonb_build_array(VARIADIC NULL::text[]); -- ok SELECT jsonb_build_array(VARIADIC '{}'::text[]); -- ok SELECT jsonb_build_array(VARIADIC '{a,b,c}'::text[]); -- ok SELECT jsonb_build_array(VARIADIC ARRAY['a', NULL]::text[]); -- ok SELECT jsonb_build_array(VARIADIC '{1,2,3,4}'::text[]); -- ok SELECT jsonb_build_array(VARIADIC '{1,2,3,4}'::int[]); -- ok SELECT jsonb_build_array(VARIADIC '{{1,4},{2,5},{3,6}}'::int[][]); -- ok SELECT jsonb_build_object('a',1,'b',1.2,'c',true,'d',null,'e',json '{"x": 3, "y": [1,2,3]}'); SELECT jsonb_build_object( 'a', jsonb_build_object('b',false,'c',99), 'd', jsonb_build_object('e',array[9,8,7]::int[], 'f', (select row_to_json(r) from ( select relkind, oid::regclass as name from pg_class where relname = 'pg_class') r))); SELECT jsonb_build_object('{a,b,c}'::text[]); -- error SELECT jsonb_build_object('{a,b,c}'::text[], '{d,e,f}'::text[]); -- error, key cannot be array SELECT jsonb_build_object('a', 'b', 'c'); -- error SELECT jsonb_build_object(NULL, 'a'); -- error, key cannot be NULL SELECT jsonb_build_object('a', NULL); -- ok SELECT jsonb_build_object(VARIADIC NULL::text[]); -- ok SELECT jsonb_build_object(VARIADIC '{}'::text[]); -- ok SELECT jsonb_build_object(VARIADIC '{a,b,c}'::text[]); -- error SELECT jsonb_build_object(VARIADIC ARRAY['a', NULL]::text[]); -- ok SELECT jsonb_build_object(VARIADIC ARRAY[NULL, 'a']::text[]); -- error, key cannot be NULL SELECT jsonb_build_object(VARIADIC '{1,2,3,4}'::text[]); -- ok SELECT jsonb_build_object(VARIADIC '{1,2,3,4}'::int[]); -- ok SELECT jsonb_build_object(VARIADIC '{{1,4},{2,5},{3,6}}'::int[][]); -- ok -- empty objects/arrays SELECT jsonb_build_array(); SELECT jsonb_build_object(); -- make sure keys are quoted SELECT jsonb_build_object(1,2); -- keys must be scalar and not null SELECT jsonb_build_object(null,2); SELECT jsonb_build_object(r,2) FROM (SELECT 1 AS a, 2 AS b) r; SELECT jsonb_build_object(json '{"a":1,"b":2}', 3); SELECT jsonb_build_object('{1,2,3}'::int[], 3); -- handling of NULL values SELECT jsonb_object_agg(1, NULL::jsonb); SELECT jsonb_object_agg(NULL, '{"a":1}'); CREATE TEMP TABLE foo (serial_num int, name text, type text); INSERT INTO foo VALUES (847001,'t15','GE1043'); INSERT INTO foo VALUES (847002,'t16','GE1043'); INSERT INTO foo VALUES (847003,'sub-alpha','GESS90'); SELECT jsonb_build_object('turbines',jsonb_object_agg(serial_num,jsonb_build_object('name',name,'type',type))) FROM foo; SELECT jsonb_object_agg(name, type) FROM foo; INSERT INTO foo VALUES (999999, NULL, 'bar'); SELECT jsonb_object_agg(name, type) FROM foo; -- jsonb_object -- empty object, one dimension SELECT jsonb_object('{}'); -- empty object, two dimensions SELECT jsonb_object('{}', '{}'); -- one dimension SELECT jsonb_object('{a,1,b,2,3,NULL,"d e f","a b c"}'); -- same but with two dimensions SELECT jsonb_object('{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}'); -- odd number error SELECT jsonb_object('{a,b,c}'); -- one column error SELECT jsonb_object('{{a},{b}}'); -- too many columns error SELECT jsonb_object('{{a,b,c},{b,c,d}}'); -- too many dimensions error SELECT jsonb_object('{{{a,b},{c,d}},{{b,c},{d,e}}}'); --two argument form of jsonb_object select jsonb_object('{a,b,c,"d e f"}','{1,2,3,"a b c"}'); -- too many dimensions SELECT jsonb_object('{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}', '{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}'); -- mismatched dimensions select jsonb_object('{a,b,c,"d e f",g}','{1,2,3,"a b c"}'); select jsonb_object('{a,b,c,"d e f"}','{1,2,3,"a b c",g}'); -- null key error select jsonb_object('{a,b,NULL,"d e f"}','{1,2,3,"a b c"}'); -- empty key is allowed select jsonb_object('{a,b,"","d e f"}','{1,2,3,"a b c"}'); -- extract_path, extract_path_as_text SELECT jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6'); SELECT jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2'); SELECT jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text); SELECT jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text); SELECT jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6'); SELECT jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2'); SELECT jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text); SELECT jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text); -- extract_path nulls SELECT jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') IS NULL AS expect_false; SELECT jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') IS NULL AS expect_true; SELECT jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') IS NULL AS expect_false; SELECT jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') IS NULL AS expect_true; -- extract_path operators SELECT '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6']; SELECT '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2']; SELECT '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0']; SELECT '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1']; SELECT '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6']; SELECT '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2']; SELECT '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0']; SELECT '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1']; -- corner cases for same select '{"a": {"b":{"c": "foo"}}}'::jsonb #> '{}'; select '[1,2,3]'::jsonb #> '{}'; select '"foo"'::jsonb #> '{}'; select '42'::jsonb #> '{}'; select 'null'::jsonb #> '{}'; select '{"a": {"b":{"c": "foo"}}}'::jsonb #> array['a']; select '{"a": {"b":{"c": "foo"}}}'::jsonb #> array['a', null]; select '{"a": {"b":{"c": "foo"}}}'::jsonb #> array['a', '']; select '{"a": {"b":{"c": "foo"}}}'::jsonb #> array['a','b']; select '{"a": {"b":{"c": "foo"}}}'::jsonb #> array['a','b','c']; select '{"a": {"b":{"c": "foo"}}}'::jsonb #> array['a','b','c','d']; select '{"a": {"b":{"c": "foo"}}}'::jsonb #> array['a','z','c']; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb #> array['a','1','b']; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb #> array['a','z','b']; select '[{"b": "c"}, {"b": "cc"}]'::jsonb #> array['1','b']; select '[{"b": "c"}, {"b": "cc"}]'::jsonb #> array['z','b']; select '[{"b": "c"}, {"b": null}]'::jsonb #> array['1','b']; select '"foo"'::jsonb #> array['z']; select '42'::jsonb #> array['f2']; select '42'::jsonb #> array['0']; select '{"a": {"b":{"c": "foo"}}}'::jsonb #>> '{}'; select '[1,2,3]'::jsonb #>> '{}'; select '"foo"'::jsonb #>> '{}'; select '42'::jsonb #>> '{}'; select 'null'::jsonb #>> '{}'; select '{"a": {"b":{"c": "foo"}}}'::jsonb #>> array['a']; select '{"a": {"b":{"c": "foo"}}}'::jsonb #>> array['a', null]; select '{"a": {"b":{"c": "foo"}}}'::jsonb #>> array['a', '']; select '{"a": {"b":{"c": "foo"}}}'::jsonb #>> array['a','b']; select '{"a": {"b":{"c": "foo"}}}'::jsonb #>> array['a','b','c']; select '{"a": {"b":{"c": "foo"}}}'::jsonb #>> array['a','b','c','d']; select '{"a": {"b":{"c": "foo"}}}'::jsonb #>> array['a','z','c']; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb #>> array['a','1','b']; select '{"a": [{"b": "c"}, {"b": "cc"}]}'::jsonb #>> array['a','z','b']; select '[{"b": "c"}, {"b": "cc"}]'::jsonb #>> array['1','b']; select '[{"b": "c"}, {"b": "cc"}]'::jsonb #>> array['z','b']; select '[{"b": "c"}, {"b": null}]'::jsonb #>> array['1','b']; select '"foo"'::jsonb #>> array['z']; select '42'::jsonb #>> array['f2']; select '42'::jsonb #>> array['0']; -- array_elements SELECT jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]'); SELECT * FROM jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q; SELECT jsonb_array_elements_text('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false,"stringy"]'); SELECT * FROM jsonb_array_elements_text('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false,"stringy"]') q; -- populate_record CREATE TYPE jbpop AS (a text, b int, c timestamp); CREATE DOMAIN jsb_int_not_null AS int NOT NULL; CREATE DOMAIN jsb_int_array_1d AS int[] CHECK(array_length(VALUE, 1) = 3); CREATE DOMAIN jsb_int_array_2d AS int[][] CHECK(array_length(VALUE, 2) = 3); create type jb_unordered_pair as (x int, y int); create domain jb_ordered_pair as jb_unordered_pair check((value).x <= (value).y); CREATE TYPE jsbrec AS ( i int, ia _int4, ia1 int[], ia2 int[][], ia3 int[][][], ia1d jsb_int_array_1d, ia2d jsb_int_array_2d, t text, ta text[], c char(10), ca char(10)[], ts timestamp, js json, jsb jsonb, jsa json[], rec jbpop, reca jbpop[] ); CREATE TYPE jsbrec_i_not_null AS ( i jsb_int_not_null ); SELECT * FROM jsonb_populate_record(NULL::jbpop,'{"a":"blurfl","x":43.2}') q; SELECT * FROM jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q; SELECT * FROM jsonb_populate_record(NULL::jbpop,'{"a":"blurfl","x":43.2}') q; SELECT * FROM jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q; SELECT * FROM jsonb_populate_record(NULL::jbpop,'{"a":[100,200,false],"x":43.2}') q; SELECT * FROM jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}') q; SELECT * FROM jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}') q; SELECT * FROM jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop, '{}') q; SELECT i FROM jsonb_populate_record(NULL::jsbrec_i_not_null, '{"x": 43.2}') q; SELECT i FROM jsonb_populate_record(NULL::jsbrec_i_not_null, '{"i": null}') q; SELECT i FROM jsonb_populate_record(NULL::jsbrec_i_not_null, '{"i": 12345}') q; SELECT ia FROM jsonb_populate_record(NULL::jsbrec, '{"ia": null}') q; SELECT ia FROM jsonb_populate_record(NULL::jsbrec, '{"ia": 123}') q; SELECT ia FROM jsonb_populate_record(NULL::jsbrec, '{"ia": [1, "2", null, 4]}') q; SELECT ia FROM jsonb_populate_record(NULL::jsbrec, '{"ia": [[1, 2], [3, 4]]}') q; SELECT ia FROM jsonb_populate_record(NULL::jsbrec, '{"ia": [[1], 2]}') q; SELECT ia FROM jsonb_populate_record(NULL::jsbrec, '{"ia": [[1], [2, 3]]}') q; SELECT ia FROM jsonb_populate_record(NULL::jsbrec, '{"ia": "{1,2,3}"}') q; SELECT ia1 FROM jsonb_populate_record(NULL::jsbrec, '{"ia1": null}') q; SELECT ia1 FROM jsonb_populate_record(NULL::jsbrec, '{"ia1": 123}') q; SELECT ia1 FROM jsonb_populate_record(NULL::jsbrec, '{"ia1": [1, "2", null, 4]}') q; SELECT ia1 FROM jsonb_populate_record(NULL::jsbrec, '{"ia1": [[1, 2, 3]]}') q; SELECT ia1d FROM jsonb_populate_record(NULL::jsbrec, '{"ia1d": null}') q; SELECT ia1d FROM jsonb_populate_record(NULL::jsbrec, '{"ia1d": 123}') q; SELECT ia1d FROM jsonb_populate_record(NULL::jsbrec, '{"ia1d": [1, "2", null, 4]}') q; SELECT ia1d FROM jsonb_populate_record(NULL::jsbrec, '{"ia1d": [1, "2", null]}') q; SELECT ia2 FROM jsonb_populate_record(NULL::jsbrec, '{"ia2": [1, "2", null, 4]}') q; SELECT ia2 FROM jsonb_populate_record(NULL::jsbrec, '{"ia2": [[1, 2], [null, 4]]}') q; SELECT ia2 FROM jsonb_populate_record(NULL::jsbrec, '{"ia2": [[], []]}') q; SELECT ia2 FROM jsonb_populate_record(NULL::jsbrec, '{"ia2": [[1, 2], [3]]}') q; SELECT ia2 FROM jsonb_populate_record(NULL::jsbrec, '{"ia2": [[1, 2], 3, 4]}') q; SELECT ia2d FROM jsonb_populate_record(NULL::jsbrec, '{"ia2d": [[1, "2"], [null, 4]]}') q; SELECT ia2d FROM jsonb_populate_record(NULL::jsbrec, '{"ia2d": [[1, "2", 3], [null, 5, 6]]}') q; SELECT ia3 FROM jsonb_populate_record(NULL::jsbrec, '{"ia3": [1, "2", null, 4]}') q; SELECT ia3 FROM jsonb_populate_record(NULL::jsbrec, '{"ia3": [[1, 2], [null, 4]]}') q; SELECT ia3 FROM jsonb_populate_record(NULL::jsbrec, '{"ia3": [ [[], []], [[], []], [[], []] ]}') q; SELECT ia3 FROM jsonb_populate_record(NULL::jsbrec, '{"ia3": [ [[1, 2]], [[3, 4]] ]}') q; SELECT ia3 FROM jsonb_populate_record(NULL::jsbrec, '{"ia3": [ [[1, 2], [3, 4]], [[5, 6], [7, 8]] ]}') q; SELECT ia3 FROM jsonb_populate_record(NULL::jsbrec, '{"ia3": [ [[1, 2], [3, 4]], [[5, 6], [7, 8], [9, 10]] ]}') q; SELECT ta FROM jsonb_populate_record(NULL::jsbrec, '{"ta": null}') q; SELECT ta FROM jsonb_populate_record(NULL::jsbrec, '{"ta": 123}') q; SELECT ta FROM jsonb_populate_record(NULL::jsbrec, '{"ta": [1, "2", null, 4]}') q; SELECT ta FROM jsonb_populate_record(NULL::jsbrec, '{"ta": [[1, 2, 3], {"k": "v"}]}') q; SELECT c FROM jsonb_populate_record(NULL::jsbrec, '{"c": null}') q; SELECT c FROM jsonb_populate_record(NULL::jsbrec, '{"c": "aaa"}') q; SELECT c FROM jsonb_populate_record(NULL::jsbrec, '{"c": "aaaaaaaaaa"}') q; SELECT c FROM jsonb_populate_record(NULL::jsbrec, '{"c": "aaaaaaaaaaaaa"}') q; SELECT ca FROM jsonb_populate_record(NULL::jsbrec, '{"ca": null}') q; SELECT ca FROM jsonb_populate_record(NULL::jsbrec, '{"ca": 123}') q; SELECT ca FROM jsonb_populate_record(NULL::jsbrec, '{"ca": [1, "2", null, 4]}') q; SELECT ca FROM jsonb_populate_record(NULL::jsbrec, '{"ca": ["aaaaaaaaaaaaaaaa"]}') q; SELECT ca FROM jsonb_populate_record(NULL::jsbrec, '{"ca": [[1, 2, 3], {"k": "v"}]}') q; SELECT js FROM jsonb_populate_record(NULL::jsbrec, '{"js": null}') q; SELECT js FROM jsonb_populate_record(NULL::jsbrec, '{"js": true}') q; SELECT js FROM jsonb_populate_record(NULL::jsbrec, '{"js": 123.45}') q; SELECT js FROM jsonb_populate_record(NULL::jsbrec, '{"js": "123.45"}') q; SELECT js FROM jsonb_populate_record(NULL::jsbrec, '{"js": "abc"}') q; SELECT js FROM jsonb_populate_record(NULL::jsbrec, '{"js": [123, "123", null, {"key": "value"}]}') q; SELECT js FROM jsonb_populate_record(NULL::jsbrec, '{"js": {"a": "bbb", "b": null, "c": 123.45}}') q; SELECT jsb FROM jsonb_populate_record(NULL::jsbrec, '{"jsb": null}') q; SELECT jsb FROM jsonb_populate_record(NULL::jsbrec, '{"jsb": true}') q; SELECT jsb FROM jsonb_populate_record(NULL::jsbrec, '{"jsb": 123.45}') q; SELECT jsb FROM jsonb_populate_record(NULL::jsbrec, '{"jsb": "123.45"}') q; SELECT jsb FROM jsonb_populate_record(NULL::jsbrec, '{"jsb": "abc"}') q; SELECT jsb FROM jsonb_populate_record(NULL::jsbrec, '{"jsb": [123, "123", null, {"key": "value"}]}') q; SELECT jsb FROM jsonb_populate_record(NULL::jsbrec, '{"jsb": {"a": "bbb", "b": null, "c": 123.45}}') q; SELECT jsa FROM jsonb_populate_record(NULL::jsbrec, '{"jsa": null}') q; SELECT jsa FROM jsonb_populate_record(NULL::jsbrec, '{"jsa": 123}') q; SELECT jsa FROM jsonb_populate_record(NULL::jsbrec, '{"jsa": [1, "2", null, 4]}') q; SELECT jsa FROM jsonb_populate_record(NULL::jsbrec, '{"jsa": ["aaa", null, [1, 2, "3", {}], { "k" : "v" }]}') q; SELECT rec FROM jsonb_populate_record(NULL::jsbrec, '{"rec": 123}') q; SELECT rec FROM jsonb_populate_record(NULL::jsbrec, '{"rec": [1, 2]}') q; SELECT rec FROM jsonb_populate_record(NULL::jsbrec, '{"rec": {"a": "abc", "c": "01.02.2003", "x": 43.2}}') q; SELECT rec FROM jsonb_populate_record(NULL::jsbrec, '{"rec": "(abc,42,01.02.2003)"}') q; SELECT reca FROM jsonb_populate_record(NULL::jsbrec, '{"reca": 123}') q; SELECT reca FROM jsonb_populate_record(NULL::jsbrec, '{"reca": [1, 2]}') q; SELECT reca FROM jsonb_populate_record(NULL::jsbrec, '{"reca": [{"a": "abc", "b": 456}, null, {"c": "01.02.2003", "x": 43.2}]}') q; SELECT reca FROM jsonb_populate_record(NULL::jsbrec, '{"reca": ["(abc,42,01.02.2003)"]}') q; SELECT reca FROM jsonb_populate_record(NULL::jsbrec, '{"reca": "{\"(abc,42,01.02.2003)\"}"}') q; SELECT rec FROM jsonb_populate_record( row(NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, row('x',3,'2012-12-31 15:30:56')::jbpop,NULL)::jsbrec, '{"rec": {"a": "abc", "c": "01.02.2003", "x": 43.2}}' ) q; -- anonymous record type SELECT jsonb_populate_record(null::record, '{"x": 0, "y": 1}'); SELECT jsonb_populate_record(row(1,2), '{"f1": 0, "f2": 1}'); -- composite domain SELECT jsonb_populate_record(null::jb_ordered_pair, '{"x": 0, "y": 1}'); SELECT jsonb_populate_record(row(1,2)::jb_ordered_pair, '{"x": 0}'); SELECT jsonb_populate_record(row(1,2)::jb_ordered_pair, '{"x": 1, "y": 0}'); -- populate_recordset SELECT * FROM jsonb_populate_recordset(NULL::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM jsonb_populate_recordset(row('def',99,NULL)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM jsonb_populate_recordset(NULL::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM jsonb_populate_recordset(row('def',99,NULL)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM jsonb_populate_recordset(row('def',99,NULL)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM jsonb_populate_recordset(row('def',99,NULL)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM jsonb_populate_recordset(NULL::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM jsonb_populate_recordset(row('def',99,NULL)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q; SELECT * FROM jsonb_populate_recordset(row('def',99,NULL)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q; -- anonymous record type SELECT jsonb_populate_recordset(null::record, '[{"x": 0, "y": 1}]'); SELECT jsonb_populate_recordset(row(1,2), '[{"f1": 0, "f2": 1}]'); SELECT i, jsonb_populate_recordset(row(i,50), '[{"f1":"42"},{"f2":"43"}]') FROM (VALUES (1),(2)) v(i); -- empty array is a corner case SELECT jsonb_populate_recordset(null::record, '[]'); SELECT jsonb_populate_recordset(row(1,2), '[]'); SELECT * FROM jsonb_populate_recordset(NULL::jbpop,'[]') q; -- composite domain SELECT jsonb_populate_recordset(null::jb_ordered_pair, '[{"x": 0, "y": 1}]'); SELECT jsonb_populate_recordset(row(1,2)::jb_ordered_pair, '[{"x": 0}, {"y": 3}]'); SELECT jsonb_populate_recordset(row(1,2)::jb_ordered_pair, '[{"x": 1, "y": 0}]'); -- negative cases where the wrong record type is supplied select * from jsonb_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text); select * from jsonb_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text); select * from jsonb_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text); select * from jsonb_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text); -- jsonb_to_record and jsonb_to_recordset select * from jsonb_to_record('{"a":1,"b":"foo","c":"bar"}') as x(a int, b text, d text); select * from jsonb_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar","c":true}]') as x(a int, b text, c boolean); select *, c is null as c_is_null from jsonb_to_record('{"a":1, "b":{"c":16, "d":2}, "x":8, "ca": ["1 2", 3], "ia": [[1,2],[3,4]], "r": {"a": "aaa", "b": 123}}'::jsonb) as t(a int, b jsonb, c text, x int, ca char(5)[], ia int[][], r jbpop); select *, c is null as c_is_null from jsonb_to_recordset('[{"a":1, "b":{"c":16, "d":2}, "x":8}]'::jsonb) as t(a int, b jsonb, c text, x int); select * from jsonb_to_record('{"ia": null}') as x(ia _int4); select * from jsonb_to_record('{"ia": 123}') as x(ia _int4); select * from jsonb_to_record('{"ia": [1, "2", null, 4]}') as x(ia _int4); select * from jsonb_to_record('{"ia": [[1, 2], [3, 4]]}') as x(ia _int4); select * from jsonb_to_record('{"ia": [[1], 2]}') as x(ia _int4); select * from jsonb_to_record('{"ia": [[1], [2, 3]]}') as x(ia _int4); select * from jsonb_to_record('{"ia2": [1, 2, 3]}') as x(ia2 int[][]); select * from jsonb_to_record('{"ia2": [[1, 2], [3, 4]]}') as x(ia2 int4[][]); select * from jsonb_to_record('{"ia2": [[[1], [2], [3]]]}') as x(ia2 int4[][]); -- test type info caching in jsonb_populate_record() CREATE TEMP TABLE jsbpoptest (js jsonb); INSERT INTO jsbpoptest SELECT '{ "jsa": [1, "2", null, 4], "rec": {"a": "abc", "c": "01.02.2003", "x": 43.2}, "reca": [{"a": "abc", "b": 456}, null, {"c": "01.02.2003", "x": 43.2}] }'::jsonb FROM generate_series(1, 3); SELECT (jsonb_populate_record(NULL::jsbrec, js)).* FROM jsbpoptest; DROP TYPE jsbrec; DROP TYPE jsbrec_i_not_null; DROP DOMAIN jsb_int_not_null; DROP DOMAIN jsb_int_array_1d; DROP DOMAIN jsb_int_array_2d; DROP DOMAIN jb_ordered_pair; DROP TYPE jb_unordered_pair; -- indexing SELECT count(*) FROM testjsonb WHERE j @> '{"wait":null}'; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":"CC"}'; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":"CC", "public":true}'; SELECT count(*) FROM testjsonb WHERE j @> '{"age":25}'; SELECT count(*) FROM testjsonb WHERE j @> '{"age":25.0}'; SELECT count(*) FROM testjsonb WHERE j ? 'public'; SELECT count(*) FROM testjsonb WHERE j ? 'bar'; SELECT count(*) FROM testjsonb WHERE j ?| ARRAY['public','disabled']; SELECT count(*) FROM testjsonb WHERE j ?& ARRAY['public','disabled']; SELECT count(*) FROM testjsonb WHERE j @@ '$.wait == null'; SELECT count(*) FROM testjsonb WHERE j @@ '"CC" == $.wait'; SELECT count(*) FROM testjsonb WHERE j @@ '$.wait == "CC" && true == $.public'; SELECT count(*) FROM testjsonb WHERE j @@ '$.age == 25'; SELECT count(*) FROM testjsonb WHERE j @@ '$.age == 25.0'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($)'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.public)'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.bar)'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.public) || exists($.disabled)'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.public) && exists($.disabled)'; SELECT count(*) FROM testjsonb WHERE j @? '$.wait ? (@ == null)'; SELECT count(*) FROM testjsonb WHERE j @? '$.wait ? ("CC" == @)'; SELECT count(*) FROM testjsonb WHERE j @? '$ ? (@.wait == "CC" && true == @.public)'; SELECT count(*) FROM testjsonb WHERE j @? '$.age ? (@ == 25)'; SELECT count(*) FROM testjsonb WHERE j @? '$ ? (@.age == 25.0)'; SELECT count(*) FROM testjsonb WHERE j @? '$'; SELECT count(*) FROM testjsonb WHERE j @? '$.public'; SELECT count(*) FROM testjsonb WHERE j @? '$.bar'; CREATE INDEX jidx ON testjsonb USING gin (j); SET enable_seqscan = off; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":null}'; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":"CC"}'; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":"CC", "public":true}'; SELECT count(*) FROM testjsonb WHERE j @> '{"age":25}'; SELECT count(*) FROM testjsonb WHERE j @> '{"age":25.0}'; SELECT count(*) FROM testjsonb WHERE j @> '{"array":["foo"]}'; SELECT count(*) FROM testjsonb WHERE j @> '{"array":["bar"]}'; -- exercise GIN_SEARCH_MODE_ALL SELECT count(*) FROM testjsonb WHERE j @> '{}'; SELECT count(*) FROM testjsonb WHERE j ? 'public'; SELECT count(*) FROM testjsonb WHERE j ? 'bar'; SELECT count(*) FROM testjsonb WHERE j ?| ARRAY['public','disabled']; SELECT count(*) FROM testjsonb WHERE j ?& ARRAY['public','disabled']; EXPLAIN (COSTS OFF) SELECT count(*) FROM testjsonb WHERE j @@ '$.wait == null'; SELECT count(*) FROM testjsonb WHERE j @@ '$.wait == null'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($ ? (@.wait == null))'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.wait ? (@ == null))'; SELECT count(*) FROM testjsonb WHERE j @@ '"CC" == $.wait'; SELECT count(*) FROM testjsonb WHERE j @@ '$.wait == "CC" && true == $.public'; SELECT count(*) FROM testjsonb WHERE j @@ '$.age == 25'; SELECT count(*) FROM testjsonb WHERE j @@ '$.age == 25.0'; SELECT count(*) FROM testjsonb WHERE j @@ '$.array[*] == "foo"'; SELECT count(*) FROM testjsonb WHERE j @@ '$.array[*] == "bar"'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($ ? (@.array[*] == "bar"))'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.array ? (@[*] == "bar"))'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.array[*] ? (@ == "bar"))'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($)'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.public)'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.bar)'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.public) || exists($.disabled)'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.public) && exists($.disabled)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM testjsonb WHERE j @? '$.wait ? (@ == null)'; SELECT count(*) FROM testjsonb WHERE j @? '$.wait ? (@ == null)'; SELECT count(*) FROM testjsonb WHERE j @? '$.wait ? ("CC" == @)'; SELECT count(*) FROM testjsonb WHERE j @? '$ ? (@.wait == "CC" && true == @.public)'; SELECT count(*) FROM testjsonb WHERE j @? '$.age ? (@ == 25)'; SELECT count(*) FROM testjsonb WHERE j @? '$ ? (@.age == 25.0)'; SELECT count(*) FROM testjsonb WHERE j @? '$ ? (@.array[*] == "bar")'; SELECT count(*) FROM testjsonb WHERE j @? '$.array ? (@[*] == "bar")'; SELECT count(*) FROM testjsonb WHERE j @? '$.array[*] ? (@ == "bar")'; SELECT count(*) FROM testjsonb WHERE j @? '$'; SELECT count(*) FROM testjsonb WHERE j @? '$.public'; SELECT count(*) FROM testjsonb WHERE j @? '$.bar'; -- array exists - array elements should behave as keys (for GIN index scans too) CREATE INDEX jidx_array ON testjsonb USING gin((j->'array')); SELECT count(*) from testjsonb WHERE j->'array' ? 'bar'; -- type sensitive array exists - should return no rows (since "exists" only -- matches strings that are either object keys or array elements) SELECT count(*) from testjsonb WHERE j->'array' ? '5'::text; -- However, a raw scalar is *contained* within the array SELECT count(*) from testjsonb WHERE j->'array' @> '5'::jsonb; RESET enable_seqscan; SELECT count(*) FROM (SELECT (jsonb_each(j)).key FROM testjsonb) AS wow; SELECT key, count(*) FROM (SELECT (jsonb_each(j)).key FROM testjsonb) AS wow GROUP BY key ORDER BY count DESC, key; -- sort/hash SELECT count(distinct j) FROM testjsonb; SET enable_hashagg = off; SELECT count(*) FROM (SELECT j FROM (SELECT * FROM testjsonb UNION ALL SELECT * FROM testjsonb) js GROUP BY j) js2; SET enable_hashagg = on; SET enable_sort = off; SELECT count(*) FROM (SELECT j FROM (SELECT * FROM testjsonb UNION ALL SELECT * FROM testjsonb) js GROUP BY j) js2; SELECT distinct * FROM (values (jsonb '{}' || ''::text),('{}')) v(j); SET enable_sort = on; RESET enable_hashagg; RESET enable_sort; DROP INDEX jidx; DROP INDEX jidx_array; -- btree CREATE INDEX jidx ON testjsonb USING btree (j); SET enable_seqscan = off; SELECT count(*) FROM testjsonb WHERE j > '{"p":1}'; SELECT count(*) FROM testjsonb WHERE j = '{"pos":98, "line":371, "node":"CBA", "indexed":true}'; --gin path opclass DROP INDEX jidx; CREATE INDEX jidx ON testjsonb USING gin (j jsonb_path_ops); SET enable_seqscan = off; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":null}'; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":"CC"}'; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":"CC", "public":true}'; SELECT count(*) FROM testjsonb WHERE j @> '{"age":25}'; SELECT count(*) FROM testjsonb WHERE j @> '{"age":25.0}'; -- exercise GIN_SEARCH_MODE_ALL SELECT count(*) FROM testjsonb WHERE j @> '{}'; SELECT count(*) FROM testjsonb WHERE j @@ '$.wait == null'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($ ? (@.wait == null))'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.wait ? (@ == null))'; SELECT count(*) FROM testjsonb WHERE j @@ '"CC" == $.wait'; SELECT count(*) FROM testjsonb WHERE j @@ '$.wait == "CC" && true == $.public'; SELECT count(*) FROM testjsonb WHERE j @@ '$.age == 25'; SELECT count(*) FROM testjsonb WHERE j @@ '$.age == 25.0'; SELECT count(*) FROM testjsonb WHERE j @@ '$.array[*] == "foo"'; SELECT count(*) FROM testjsonb WHERE j @@ '$.array[*] == "bar"'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($ ? (@.array[*] == "bar"))'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.array ? (@[*] == "bar"))'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($.array[*] ? (@ == "bar"))'; SELECT count(*) FROM testjsonb WHERE j @@ 'exists($)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM testjsonb WHERE j @? '$.wait ? (@ == null)'; SELECT count(*) FROM testjsonb WHERE j @? '$.wait ? (@ == null)'; SELECT count(*) FROM testjsonb WHERE j @? '$.wait ? ("CC" == @)'; SELECT count(*) FROM testjsonb WHERE j @? '$ ? (@.wait == "CC" && true == @.public)'; SELECT count(*) FROM testjsonb WHERE j @? '$.age ? (@ == 25)'; SELECT count(*) FROM testjsonb WHERE j @? '$ ? (@.age == 25.0)'; SELECT count(*) FROM testjsonb WHERE j @? '$ ? (@.array[*] == "bar")'; SELECT count(*) FROM testjsonb WHERE j @? '$.array ? (@[*] == "bar")'; SELECT count(*) FROM testjsonb WHERE j @? '$.array[*] ? (@ == "bar")'; SELECT count(*) FROM testjsonb WHERE j @? '$'; SELECT count(*) FROM testjsonb WHERE j @? '$.public'; SELECT count(*) FROM testjsonb WHERE j @? '$.bar'; RESET enable_seqscan; DROP INDEX jidx; -- nested tests SELECT '{"ff":{"a":12,"b":16}}'::jsonb; SELECT '{"ff":{"a":12,"b":16},"qq":123}'::jsonb; SELECT '{"aa":["a","aaa"],"qq":{"a":12,"b":16,"c":["c1","c2"],"d":{"d1":"d1","d2":"d2","d1":"d3"}}}'::jsonb; SELECT '{"aa":["a","aaa"],"qq":{"a":"12","b":"16","c":["c1","c2"],"d":{"d1":"d1","d2":"d2"}}}'::jsonb; SELECT '{"aa":["a","aaa"],"qq":{"a":"12","b":"16","c":["c1","c2",["c3"],{"c4":4}],"d":{"d1":"d1","d2":"d2"}}}'::jsonb; SELECT '{"ff":["a","aaa"]}'::jsonb; SELECT '{"ff":{"a":12,"b":16},"qq":123,"x":[1,2],"Y":null}'::jsonb -> 'ff', '{"ff":{"a":12,"b":16},"qq":123,"x":[1,2],"Y":null}'::jsonb -> 'qq', ('{"ff":{"a":12,"b":16},"qq":123,"x":[1,2],"Y":null}'::jsonb -> 'Y') IS NULL AS f, ('{"ff":{"a":12,"b":16},"qq":123,"x":[1,2],"Y":null}'::jsonb ->> 'Y') IS NULL AS t, '{"ff":{"a":12,"b":16},"qq":123,"x":[1,2],"Y":null}'::jsonb -> 'x'; -- nested containment SELECT '{"a":[1,2],"c":"b"}'::jsonb @> '{"a":[1,2]}'; SELECT '{"a":[2,1],"c":"b"}'::jsonb @> '{"a":[1,2]}'; SELECT '{"a":{"1":2},"c":"b"}'::jsonb @> '{"a":[1,2]}'; SELECT '{"a":{"2":1},"c":"b"}'::jsonb @> '{"a":[1,2]}'; SELECT '{"a":{"1":2},"c":"b"}'::jsonb @> '{"a":{"1":2}}'; SELECT '{"a":{"2":1},"c":"b"}'::jsonb @> '{"a":{"1":2}}'; SELECT '["a","b"]'::jsonb @> '["a","b","c","b"]'; SELECT '["a","b","c","b"]'::jsonb @> '["a","b"]'; SELECT '["a","b","c",[1,2]]'::jsonb @> '["a",[1,2]]'; SELECT '["a","b","c",[1,2]]'::jsonb @> '["b",[1,2]]'; SELECT '{"a":[1,2],"c":"b"}'::jsonb @> '{"a":[1]}'; SELECT '{"a":[1,2],"c":"b"}'::jsonb @> '{"a":[2]}'; SELECT '{"a":[1,2],"c":"b"}'::jsonb @> '{"a":[3]}'; SELECT '{"a":[1,2,{"c":3,"x":4}],"c":"b"}'::jsonb @> '{"a":[{"c":3}]}'; SELECT '{"a":[1,2,{"c":3,"x":4}],"c":"b"}'::jsonb @> '{"a":[{"x":4}]}'; SELECT '{"a":[1,2,{"c":3,"x":4}],"c":"b"}'::jsonb @> '{"a":[{"x":4},3]}'; SELECT '{"a":[1,2,{"c":3,"x":4}],"c":"b"}'::jsonb @> '{"a":[{"x":4},1]}'; -- check some corner cases for indexed nested containment (bug #13756) create temp table nestjsonb (j jsonb); insert into nestjsonb (j) values ('{"a":[["b",{"x":1}],["b",{"x":2}]],"c":3}'); insert into nestjsonb (j) values ('[[14,2,3]]'); insert into nestjsonb (j) values ('[1,[14,2,3]]'); create index on nestjsonb using gin(j jsonb_path_ops); set enable_seqscan = on; set enable_bitmapscan = off; select * from nestjsonb where j @> '{"a":[[{"x":2}]]}'::jsonb; select * from nestjsonb where j @> '{"c":3}'; select * from nestjsonb where j @> '[[14]]'; set enable_seqscan = off; set enable_bitmapscan = on; select * from nestjsonb where j @> '{"a":[[{"x":2}]]}'::jsonb; select * from nestjsonb where j @> '{"c":3}'; select * from nestjsonb where j @> '[[14]]'; reset enable_seqscan; reset enable_bitmapscan; -- nested object field / array index lookup SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 'n'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 'a'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 'b'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 'c'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 'd'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 'd' -> '1'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 'e'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 0; --expecting error SELECT '["a","b","c",[1,2],null]'::jsonb -> 0; SELECT '["a","b","c",[1,2],null]'::jsonb -> 1; SELECT '["a","b","c",[1,2],null]'::jsonb -> 2; SELECT '["a","b","c",[1,2],null]'::jsonb -> 3; SELECT '["a","b","c",[1,2],null]'::jsonb -> 3 -> 1; SELECT '["a","b","c",[1,2],null]'::jsonb -> 4; SELECT '["a","b","c",[1,2],null]'::jsonb -> 5; SELECT '["a","b","c",[1,2],null]'::jsonb -> -1; SELECT '["a","b","c",[1,2],null]'::jsonb -> -5; SELECT '["a","b","c",[1,2],null]'::jsonb -> -6; --nested path extraction SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{0}'; SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{a}'; SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{c}'; SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{c,0}'; SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{c,1}'; SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{c,2}'; SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{c,3}'; SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{c,-1}'; SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{c,-3}'; SELECT '{"a":"b","c":[1,2,3]}'::jsonb #> '{c,-4}'; SELECT '[0,1,2,[3,4],{"5":"five"}]'::jsonb #> '{0}'; SELECT '[0,1,2,[3,4],{"5":"five"}]'::jsonb #> '{3}'; SELECT '[0,1,2,[3,4],{"5":"five"}]'::jsonb #> '{4}'; SELECT '[0,1,2,[3,4],{"5":"five"}]'::jsonb #> '{4,5}'; --nested exists SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb ? 'n'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb ? 'a'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb ? 'b'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb ? 'c'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb ? 'd'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb ? 'e'; -- jsonb_strip_nulls select jsonb_strip_nulls(null); select jsonb_strip_nulls('1'); select jsonb_strip_nulls('"a string"'); select jsonb_strip_nulls('null'); select jsonb_strip_nulls('[1,2,null,3,4]'); select jsonb_strip_nulls('{"a":1,"b":null,"c":[2,null,3],"d":{"e":4,"f":null}}'); select jsonb_strip_nulls('[1,{"a":1,"b":null,"c":2},3]'); -- an empty object is not null and should not be stripped select jsonb_strip_nulls('{"a": {"b": null, "c": null}, "d": {} }'); select jsonb_pretty('{"a": "test", "b": [1, 2, 3], "c": "test3", "d":{"dd": "test4", "dd2":{"ddd": "test5"}}}'); select jsonb_pretty('[{"f1":1,"f2":null},2,null,[[{"x":true},6,7],8],3]'); select jsonb_pretty('{"a":["b", "c"], "d": {"e":"f"}}'); select jsonb_concat('{"d": "test", "a": [1, 2]}', '{"g": "test2", "c": {"c1":1, "c2":2}}'); select '{"aa":1 , "b":2, "cq":3}'::jsonb || '{"cq":"l", "b":"g", "fg":false}'; select '{"aa":1 , "b":2, "cq":3}'::jsonb || '{"aq":"l"}'; select '{"aa":1 , "b":2, "cq":3}'::jsonb || '{"aa":"l"}'; select '{"aa":1 , "b":2, "cq":3}'::jsonb || '{}'; select '["a", "b"]'::jsonb || '["c"]'; select '["a", "b"]'::jsonb || '["c", "d"]'; select '["c"]' || '["a", "b"]'::jsonb; select '["a", "b"]'::jsonb || '"c"'; select '"c"' || '["a", "b"]'::jsonb; select '[]'::jsonb || '["a"]'::jsonb; select '[]'::jsonb || '"a"'::jsonb; select '"b"'::jsonb || '"a"'::jsonb; select '{}'::jsonb || '{"a":"b"}'::jsonb; select '[]'::jsonb || '{"a":"b"}'::jsonb; select '{"a":"b"}'::jsonb || '[]'::jsonb; select '"a"'::jsonb || '{"a":1}'; select '{"a":1}' || '"a"'::jsonb; select '["a", "b"]'::jsonb || '{"c":1}'; select '{"c": 1}'::jsonb || '["a", "b"]'; select '{}'::jsonb || '{"cq":"l", "b":"g", "fg":false}'; select pg_column_size('{}'::jsonb || '{}'::jsonb) = pg_column_size('{}'::jsonb); select pg_column_size('{"aa":1}'::jsonb || '{"b":2}'::jsonb) = pg_column_size('{"aa":1, "b":2}'::jsonb); select pg_column_size('{"aa":1, "b":2}'::jsonb || '{}'::jsonb) = pg_column_size('{"aa":1, "b":2}'::jsonb); select pg_column_size('{}'::jsonb || '{"aa":1, "b":2}'::jsonb) = pg_column_size('{"aa":1, "b":2}'::jsonb); select jsonb_delete('{"a":1 , "b":2, "c":3}'::jsonb, 'a'); select jsonb_delete('{"a":null , "b":2, "c":3}'::jsonb, 'a'); select jsonb_delete('{"a":1 , "b":2, "c":3}'::jsonb, 'b'); select jsonb_delete('{"a":1 , "b":2, "c":3}'::jsonb, 'c'); select jsonb_delete('{"a":1 , "b":2, "c":3}'::jsonb, 'd'); select '{"a":1 , "b":2, "c":3}'::jsonb - 'a'; select '{"a":null , "b":2, "c":3}'::jsonb - 'a'; select '{"a":1 , "b":2, "c":3}'::jsonb - 'b'; select '{"a":1 , "b":2, "c":3}'::jsonb - 'c'; select '{"a":1 , "b":2, "c":3}'::jsonb - 'd'; select pg_column_size('{"a":1 , "b":2, "c":3}'::jsonb - 'b') = pg_column_size('{"a":1, "b":2}'::jsonb); select '["a","b","c"]'::jsonb - 3; select '["a","b","c"]'::jsonb - 2; select '["a","b","c"]'::jsonb - 1; select '["a","b","c"]'::jsonb - 0; select '["a","b","c"]'::jsonb - -1; select '["a","b","c"]'::jsonb - -2; select '["a","b","c"]'::jsonb - -3; select '["a","b","c"]'::jsonb - -4; select '{"a":1 , "b":2, "c":3}'::jsonb - '{b}'::text[]; select '{"a":1 , "b":2, "c":3}'::jsonb - '{c,b}'::text[]; select '{"a":1 , "b":2, "c":3}'::jsonb - '{}'::text[]; select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '[1,2,3]'); select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '[1,2,3]'); select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '[1,2,3]'); select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '[1,2,3]'); select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '{"1": 2}'); select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"1": 2}'); select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '{"1": 2}'); select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '{"1": 2}'); select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '"test"'); select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"f": "test"}'); select jsonb_delete_path('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}', '{n}'); select jsonb_delete_path('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}', '{b,-1}'); select jsonb_delete_path('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}', '{d,1,0}'); select '{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb #- '{n}'; select '{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb #- '{b,-1}'; select '{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb #- '{b,-1e}'; -- invalid array subscript select '{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb #- '{d,1,0}'; -- empty structure and error conditions for delete and replace select '"a"'::jsonb - 'a'; -- error select '{}'::jsonb - 'a'; select '[]'::jsonb - 'a'; select '"a"'::jsonb - 1; -- error select '{}'::jsonb - 1; -- error select '[]'::jsonb - 1; select '"a"'::jsonb #- '{a}'; -- error select '{}'::jsonb #- '{a}'; select '[]'::jsonb #- '{a}'; select jsonb_set('"a"','{a}','"b"'); --error select jsonb_set('{}','{a}','"b"', false); select jsonb_set('[]','{1}','"b"', false); select jsonb_set('[{"f1":1,"f2":null},2,null,3]', '{0}','[2,3,4]', false); -- jsonb_set adding instead of replacing -- prepend to array select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{b,-33}','{"foo":123}'); -- append to array select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{b,33}','{"foo":123}'); -- check nesting levels addition select jsonb_set('{"a":1,"b":[4,5,[0,1,2],6,7],"c":{"d":4}}','{b,2,33}','{"foo":123}'); -- add new key select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{c,e}','{"foo":123}'); -- adding doesn't do anything if elements before last aren't present select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{x,-33}','{"foo":123}'); select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{x,y}','{"foo":123}'); -- add to empty object select jsonb_set('{}','{x}','{"foo":123}'); --add to empty array select jsonb_set('[]','{0}','{"foo":123}'); select jsonb_set('[]','{99}','{"foo":123}'); select jsonb_set('[]','{-99}','{"foo":123}'); select jsonb_set('{"a": [1, 2, 3]}', '{a, non_integer}', '"new_value"'); select jsonb_set('{"a": {"b": [1, 2, 3]}}', '{a, b, non_integer}', '"new_value"'); select jsonb_set('{"a": {"b": [1, 2, 3]}}', '{a, b, NULL}', '"new_value"'); -- jsonb_insert select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"'); select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"', true); select jsonb_insert('{"a": {"b": {"c": [0, 1, "test1", "test2"]}}}', '{a, b, c, 2}', '"new_value"'); select jsonb_insert('{"a": {"b": {"c": [0, 1, "test1", "test2"]}}}', '{a, b, c, 2}', '"new_value"', true); select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '{"b": "value"}'); select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '["value1", "value2"]'); -- edge cases select jsonb_insert('{"a": [0,1,2]}', '{a, 0}', '"new_value"'); select jsonb_insert('{"a": [0,1,2]}', '{a, 0}', '"new_value"', true); select jsonb_insert('{"a": [0,1,2]}', '{a, 2}', '"new_value"'); select jsonb_insert('{"a": [0,1,2]}', '{a, 2}', '"new_value"', true); select jsonb_insert('{"a": [0,1,2]}', '{a, -1}', '"new_value"'); select jsonb_insert('{"a": [0,1,2]}', '{a, -1}', '"new_value"', true); select jsonb_insert('[]', '{1}', '"new_value"'); select jsonb_insert('[]', '{1}', '"new_value"', true); select jsonb_insert('{"a": []}', '{a, 1}', '"new_value"'); select jsonb_insert('{"a": []}', '{a, 1}', '"new_value"', true); select jsonb_insert('{"a": [0,1,2]}', '{a, 10}', '"new_value"'); select jsonb_insert('{"a": [0,1,2]}', '{a, -10}', '"new_value"'); -- jsonb_insert should be able to insert new value for objects, but not to replace select jsonb_insert('{"a": {"b": "value"}}', '{a, c}', '"new_value"'); select jsonb_insert('{"a": {"b": "value"}}', '{a, c}', '"new_value"', true); select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"'); select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"', true); -- jsonb to tsvector select to_tsvector('{"a": "aaa bbb ddd ccc", "b": ["eee fff ggg"], "c": {"d": "hhh iii"}}'::jsonb); -- jsonb to tsvector with config select to_tsvector('simple', '{"a": "aaa bbb ddd ccc", "b": ["eee fff ggg"], "c": {"d": "hhh iii"}}'::jsonb); -- jsonb to tsvector with stop words select to_tsvector('english', '{"a": "aaa in bbb ddd ccc", "b": ["the eee fff ggg"], "c": {"d": "hhh. iii"}}'::jsonb); -- jsonb to tsvector with numeric values select to_tsvector('english', '{"a": "aaa in bbb ddd ccc", "b": 123, "c": 456}'::jsonb); -- jsonb_to_tsvector select jsonb_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"all"'); select jsonb_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"key"'); select jsonb_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"string"'); select jsonb_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"numeric"'); select jsonb_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"boolean"'); select jsonb_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '["string", "numeric"]'); select jsonb_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"all"'); select jsonb_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"key"'); select jsonb_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"string"'); select jsonb_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"numeric"'); select jsonb_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '"boolean"'); select jsonb_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '["string", "numeric"]'); -- to_tsvector corner cases select to_tsvector('""'::jsonb); select to_tsvector('{}'::jsonb); select to_tsvector('[]'::jsonb); select to_tsvector('null'::jsonb); -- jsonb_to_tsvector corner cases select jsonb_to_tsvector('""'::jsonb, '"all"'); select jsonb_to_tsvector('{}'::jsonb, '"all"'); select jsonb_to_tsvector('[]'::jsonb, '"all"'); select jsonb_to_tsvector('null'::jsonb, '"all"'); select jsonb_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '""'); select jsonb_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '{}'); select jsonb_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '[]'); select jsonb_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, 'null'); select jsonb_to_tsvector('english', '{"a": "aaa in bbb", "b": 123, "c": 456, "d": true, "f": false, "g": null}'::jsonb, '["all", null]'); -- ts_headline for jsonb select ts_headline('{"a": "aaa bbb", "b": {"c": "ccc ddd fff", "c1": "ccc1 ddd1"}, "d": ["ggg hhh", "iii jjj"]}'::jsonb, tsquery('bbb & ddd & hhh')); select ts_headline('english', '{"a": "aaa bbb", "b": {"c": "ccc ddd fff"}, "d": ["ggg hhh", "iii jjj"]}'::jsonb, tsquery('bbb & ddd & hhh')); select ts_headline('{"a": "aaa bbb", "b": {"c": "ccc ddd fff", "c1": "ccc1 ddd1"}, "d": ["ggg hhh", "iii jjj"]}'::jsonb, tsquery('bbb & ddd & hhh'), 'StartSel = <, StopSel = >'); select ts_headline('english', '{"a": "aaa bbb", "b": {"c": "ccc ddd fff", "c1": "ccc1 ddd1"}, "d": ["ggg hhh", "iii jjj"]}'::jsonb, tsquery('bbb & ddd & hhh'), 'StartSel = <, StopSel = >'); -- corner cases for ts_headline with jsonb select ts_headline('null'::jsonb, tsquery('aaa & bbb')); select ts_headline('{}'::jsonb, tsquery('aaa & bbb')); select ts_headline('[]'::jsonb, tsquery('aaa & bbb')); -- casts select 'true'::jsonb::bool; select '[]'::jsonb::bool; select '1.0'::jsonb::float; select '[1.0]'::jsonb::float; select '12345'::jsonb::int4; select '"hello"'::jsonb::int4; select '12345'::jsonb::numeric; select '{}'::jsonb::numeric; select '12345.05'::jsonb::numeric; select '12345.05'::jsonb::float4; select '12345.05'::jsonb::float8; select '12345.05'::jsonb::int2; select '12345.05'::jsonb::int4; select '12345.05'::jsonb::int8; select '12345.0000000000000000000000000000000000000000000005'::jsonb::numeric; select '12345.0000000000000000000000000000000000000000000005'::jsonb::float4; select '12345.0000000000000000000000000000000000000000000005'::jsonb::float8; select '12345.0000000000000000000000000000000000000000000005'::jsonb::int2; select '12345.0000000000000000000000000000000000000000000005'::jsonb::int4; select '12345.0000000000000000000000000000000000000000000005'::jsonb::int8; pgFormatter-4.2/t/pg-test-files/sql/jsonb_jsonpath.sql000066400000000000000000000522251361326045100231470ustar00rootroot00000000000000select jsonb '{"a": 12}' @? '$'; select jsonb '{"a": 12}' @? '1'; select jsonb '{"a": 12}' @? '$.a.b'; select jsonb '{"a": 12}' @? '$.b'; select jsonb '{"a": 12}' @? '$.a + 2'; select jsonb '{"a": 12}' @? '$.b + 2'; select jsonb '{"a": {"a": 12}}' @? '$.a.a'; select jsonb '{"a": {"a": 12}}' @? '$.*.a'; select jsonb '{"b": {"a": 12}}' @? '$.*.a'; select jsonb '{"b": {"a": 12}}' @? '$.*.b'; select jsonb '{"b": {"a": 12}}' @? 'strict $.*.b'; select jsonb '{}' @? '$.*'; select jsonb '{"a": 1}' @? '$.*'; select jsonb '{"a": {"b": 1}}' @? 'lax $.**{1}'; select jsonb '{"a": {"b": 1}}' @? 'lax $.**{2}'; select jsonb '{"a": {"b": 1}}' @? 'lax $.**{3}'; select jsonb '[]' @? '$[*]'; select jsonb '[1]' @? '$[*]'; select jsonb '[1]' @? '$[1]'; select jsonb '[1]' @? 'strict $[1]'; select jsonb_path_query('[1]', 'strict $[1]'); select jsonb_path_query('[1]', 'strict $[1]', silent => true); select jsonb '[1]' @? 'lax $[10000000000000000]'; select jsonb '[1]' @? 'strict $[10000000000000000]'; select jsonb_path_query('[1]', 'lax $[10000000000000000]'); select jsonb_path_query('[1]', 'strict $[10000000000000000]'); select jsonb '[1]' @? '$[0]'; select jsonb '[1]' @? '$[0.3]'; select jsonb '[1]' @? '$[0.5]'; select jsonb '[1]' @? '$[0.9]'; select jsonb '[1]' @? '$[1.2]'; select jsonb '[1]' @? 'strict $[1.2]'; select jsonb '{"a": [1,2,3], "b": [3,4,5]}' @? '$ ? (@.a[*] > @.b[*])'; select jsonb '{"a": [1,2,3], "b": [3,4,5]}' @? '$ ? (@.a[*] >= @.b[*])'; select jsonb '{"a": [1,2,3], "b": [3,4,"5"]}' @? '$ ? (@.a[*] >= @.b[*])'; select jsonb '{"a": [1,2,3], "b": [3,4,"5"]}' @? 'strict $ ? (@.a[*] >= @.b[*])'; select jsonb '{"a": [1,2,3], "b": [3,4,null]}' @? '$ ? (@.a[*] >= @.b[*])'; select jsonb '1' @? '$ ? ((@ == "1") is unknown)'; select jsonb '1' @? '$ ? ((@ == 1) is unknown)'; select jsonb '[{"a": 1}, {"a": 2}]' @? '$[0 to 1] ? (@.a > 1)'; select jsonb_path_exists('[{"a": 1}, {"a": 2}, 3]', 'lax $[*].a', silent => false); select jsonb_path_exists('[{"a": 1}, {"a": 2}, 3]', 'lax $[*].a', silent => true); select jsonb_path_exists('[{"a": 1}, {"a": 2}, 3]', 'strict $[*].a', silent => false); select jsonb_path_exists('[{"a": 1}, {"a": 2}, 3]', 'strict $[*].a', silent => true); select jsonb_path_query('1', 'lax $.a'); select jsonb_path_query('1', 'strict $.a'); select jsonb_path_query('1', 'strict $.*'); select jsonb_path_query('1', 'strict $.a', silent => true); select jsonb_path_query('1', 'strict $.*', silent => true); select jsonb_path_query('[]', 'lax $.a'); select jsonb_path_query('[]', 'strict $.a'); select jsonb_path_query('[]', 'strict $.a', silent => true); select jsonb_path_query('{}', 'lax $.a'); select jsonb_path_query('{}', 'strict $.a'); select jsonb_path_query('{}', 'strict $.a', silent => true); select jsonb_path_query('1', 'strict $[1]'); select jsonb_path_query('1', 'strict $[*]'); select jsonb_path_query('[]', 'strict $[1]'); select jsonb_path_query('[]', 'strict $["a"]'); select jsonb_path_query('1', 'strict $[1]', silent => true); select jsonb_path_query('1', 'strict $[*]', silent => true); select jsonb_path_query('[]', 'strict $[1]', silent => true); select jsonb_path_query('[]', 'strict $["a"]', silent => true); select jsonb_path_query('{"a": 12, "b": {"a": 13}}', '$.a'); select jsonb_path_query('{"a": 12, "b": {"a": 13}}', '$.b'); select jsonb_path_query('{"a": 12, "b": {"a": 13}}', '$.*'); select jsonb_path_query('{"a": 12, "b": {"a": 13}}', 'lax $.*.a'); select jsonb_path_query('[12, {"a": 13}, {"b": 14}]', 'lax $[*].a'); select jsonb_path_query('[12, {"a": 13}, {"b": 14}]', 'lax $[*].*'); select jsonb_path_query('[12, {"a": 13}, {"b": 14}]', 'lax $[0].a'); select jsonb_path_query('[12, {"a": 13}, {"b": 14}]', 'lax $[1].a'); select jsonb_path_query('[12, {"a": 13}, {"b": 14}]', 'lax $[2].a'); select jsonb_path_query('[12, {"a": 13}, {"b": 14}]', 'lax $[0,1].a'); select jsonb_path_query('[12, {"a": 13}, {"b": 14}]', 'lax $[0 to 10].a'); select jsonb_path_query('[12, {"a": 13}, {"b": 14}]', 'lax $[0 to 10 / 0].a'); select jsonb_path_query('[12, {"a": 13}, {"b": 14}, "ccc", true]', '$[2.5 - 1 to $.size() - 2]'); select jsonb_path_query('1', 'lax $[0]'); select jsonb_path_query('1', 'lax $[*]'); select jsonb_path_query('[1]', 'lax $[0]'); select jsonb_path_query('[1]', 'lax $[*]'); select jsonb_path_query('[1,2,3]', 'lax $[*]'); select jsonb_path_query('[1,2,3]', 'strict $[*].a'); select jsonb_path_query('[1,2,3]', 'strict $[*].a', silent => true); select jsonb_path_query('[]', '$[last]'); select jsonb_path_query('[]', '$[last ? (exists(last))]'); select jsonb_path_query('[]', 'strict $[last]'); select jsonb_path_query('[]', 'strict $[last]', silent => true); select jsonb_path_query('[1]', '$[last]'); select jsonb_path_query('[1,2,3]', '$[last]'); select jsonb_path_query('[1,2,3]', '$[last - 1]'); select jsonb_path_query('[1,2,3]', '$[last ? (@.type() == "number")]'); select jsonb_path_query('[1,2,3]', '$[last ? (@.type() == "string")]'); select jsonb_path_query('[1,2,3]', '$[last ? (@.type() == "string")]', silent => true); select * from jsonb_path_query('{"a": 10}', '$'); select * from jsonb_path_query('{"a": 10}', '$ ? (@.a < $value)'); select * from jsonb_path_query('{"a": 10}', '$ ? (@.a < $value)', '1'); select * from jsonb_path_query('{"a": 10}', '$ ? (@.a < $value)', '[{"value" : 13}]'); select * from jsonb_path_query('{"a": 10}', '$ ? (@.a < $value)', '{"value" : 13}'); select * from jsonb_path_query('{"a": 10}', '$ ? (@.a < $value)', '{"value" : 8}'); select * from jsonb_path_query('{"a": 10}', '$.a ? (@ < $value)', '{"value" : 13}'); select * from jsonb_path_query('[10,11,12,13,14,15]', '$[*] ? (@ < $value)', '{"value" : 13}'); select * from jsonb_path_query('[10,11,12,13,14,15]', '$[0,1] ? (@ < $x.value)', '{"x": {"value" : 13}}'); select * from jsonb_path_query('[10,11,12,13,14,15]', '$[0 to 2] ? (@ < $value)', '{"value" : 15}'); select * from jsonb_path_query('[1,"1",2,"2",null]', '$[*] ? (@ == "1")'); select * from jsonb_path_query('[1,"1",2,"2",null]', '$[*] ? (@ == $value)', '{"value" : "1"}'); select * from jsonb_path_query('[1,"1",2,"2",null]', '$[*] ? (@ == $value)', '{"value" : null}'); select * from jsonb_path_query('[1, "2", null]', '$[*] ? (@ != null)'); select * from jsonb_path_query('[1, "2", null]', '$[*] ? (@ == null)'); select * from jsonb_path_query('{}', '$ ? (@ == @)'); select * from jsonb_path_query('[]', 'strict $ ? (@ == @)'); select jsonb_path_query('{"a": {"b": 1}}', 'lax $.**'); select jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{0}'); select jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{0 to last}'); select jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{1}'); select jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{1 to last}'); select jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{2}'); select jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{2 to last}'); select jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{3 to last}'); select jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{last}'); select jsonb_path_query('{"a": {"b": 1}}', 'lax $.**.b ? (@ > 0)'); select jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{0}.b ? (@ > 0)'); select jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{1}.b ? (@ > 0)'); select jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{0 to last}.b ? (@ > 0)'); select jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{1 to last}.b ? (@ > 0)'); select jsonb_path_query('{"a": {"b": 1}}', 'lax $.**{1 to 2}.b ? (@ > 0)'); select jsonb_path_query('{"a": {"c": {"b": 1}}}', 'lax $.**.b ? (@ > 0)'); select jsonb_path_query('{"a": {"c": {"b": 1}}}', 'lax $.**{0}.b ? (@ > 0)'); select jsonb_path_query('{"a": {"c": {"b": 1}}}', 'lax $.**{1}.b ? (@ > 0)'); select jsonb_path_query('{"a": {"c": {"b": 1}}}', 'lax $.**{0 to last}.b ? (@ > 0)'); select jsonb_path_query('{"a": {"c": {"b": 1}}}', 'lax $.**{1 to last}.b ? (@ > 0)'); select jsonb_path_query('{"a": {"c": {"b": 1}}}', 'lax $.**{1 to 2}.b ? (@ > 0)'); select jsonb_path_query('{"a": {"c": {"b": 1}}}', 'lax $.**{2 to 3}.b ? (@ > 0)'); select jsonb '{"a": {"b": 1}}' @? '$.**.b ? ( @ > 0)'; select jsonb '{"a": {"b": 1}}' @? '$.**{0}.b ? ( @ > 0)'; select jsonb '{"a": {"b": 1}}' @? '$.**{1}.b ? ( @ > 0)'; select jsonb '{"a": {"b": 1}}' @? '$.**{0 to last}.b ? ( @ > 0)'; select jsonb '{"a": {"b": 1}}' @? '$.**{1 to last}.b ? ( @ > 0)'; select jsonb '{"a": {"b": 1}}' @? '$.**{1 to 2}.b ? ( @ > 0)'; select jsonb '{"a": {"c": {"b": 1}}}' @? '$.**.b ? ( @ > 0)'; select jsonb '{"a": {"c": {"b": 1}}}' @? '$.**{0}.b ? ( @ > 0)'; select jsonb '{"a": {"c": {"b": 1}}}' @? '$.**{1}.b ? ( @ > 0)'; select jsonb '{"a": {"c": {"b": 1}}}' @? '$.**{0 to last}.b ? ( @ > 0)'; select jsonb '{"a": {"c": {"b": 1}}}' @? '$.**{1 to last}.b ? ( @ > 0)'; select jsonb '{"a": {"c": {"b": 1}}}' @? '$.**{1 to 2}.b ? ( @ > 0)'; select jsonb '{"a": {"c": {"b": 1}}}' @? '$.**{2 to 3}.b ? ( @ > 0)'; select jsonb_path_query('{"g": {"x": 2}}', '$.g ? (exists (@.x))'); select jsonb_path_query('{"g": {"x": 2}}', '$.g ? (exists (@.y))'); select jsonb_path_query('{"g": {"x": 2}}', '$.g ? (exists (@.x ? (@ >= 2) ))'); select jsonb_path_query('{"g": [{"x": 2}, {"y": 3}]}', 'lax $.g ? (exists (@.x))'); select jsonb_path_query('{"g": [{"x": 2}, {"y": 3}]}', 'lax $.g ? (exists (@.x + "3"))'); select jsonb_path_query('{"g": [{"x": 2}, {"y": 3}]}', 'lax $.g ? ((exists (@.x + "3")) is unknown)'); select jsonb_path_query('{"g": [{"x": 2}, {"y": 3}]}', 'strict $.g[*] ? (exists (@.x))'); select jsonb_path_query('{"g": [{"x": 2}, {"y": 3}]}', 'strict $.g[*] ? ((exists (@.x)) is unknown)'); select jsonb_path_query('{"g": [{"x": 2}, {"y": 3}]}', 'strict $.g ? (exists (@[*].x))'); select jsonb_path_query('{"g": [{"x": 2}, {"y": 3}]}', 'strict $.g ? ((exists (@[*].x)) is unknown)'); --test ternary logic select x, y, jsonb_path_query( '[true, false, null]', '$[*] ? (@ == true && ($x == true && $y == true) || @ == false && !($x == true && $y == true) || @ == null && ($x == true && $y == true) is unknown)', jsonb_build_object('x', x, 'y', y) ) as "x && y" from (values (jsonb 'true'), ('false'), ('"null"')) x(x), (values (jsonb 'true'), ('false'), ('"null"')) y(y); select x, y, jsonb_path_query( '[true, false, null]', '$[*] ? (@ == true && ($x == true || $y == true) || @ == false && !($x == true || $y == true) || @ == null && ($x == true || $y == true) is unknown)', jsonb_build_object('x', x, 'y', y) ) as "x || y" from (values (jsonb 'true'), ('false'), ('"null"')) x(x), (values (jsonb 'true'), ('false'), ('"null"')) y(y); select jsonb '{"a": 1, "b":1}' @? '$ ? (@.a == @.b)'; select jsonb '{"c": {"a": 1, "b":1}}' @? '$ ? (@.a == @.b)'; select jsonb '{"c": {"a": 1, "b":1}}' @? '$.c ? (@.a == @.b)'; select jsonb '{"c": {"a": 1, "b":1}}' @? '$.c ? ($.c.a == @.b)'; select jsonb '{"c": {"a": 1, "b":1}}' @? '$.* ? (@.a == @.b)'; select jsonb '{"a": 1, "b":1}' @? '$.** ? (@.a == @.b)'; select jsonb '{"c": {"a": 1, "b":1}}' @? '$.** ? (@.a == @.b)'; select jsonb_path_query('{"c": {"a": 2, "b":1}}', '$.** ? (@.a == 1 + 1)'); select jsonb_path_query('{"c": {"a": 2, "b":1}}', '$.** ? (@.a == (1 + 1))'); select jsonb_path_query('{"c": {"a": 2, "b":1}}', '$.** ? (@.a == @.b + 1)'); select jsonb_path_query('{"c": {"a": 2, "b":1}}', '$.** ? (@.a == (@.b + 1))'); select jsonb '{"c": {"a": -1, "b":1}}' @? '$.** ? (@.a == - 1)'; select jsonb '{"c": {"a": -1, "b":1}}' @? '$.** ? (@.a == -1)'; select jsonb '{"c": {"a": -1, "b":1}}' @? '$.** ? (@.a == -@.b)'; select jsonb '{"c": {"a": -1, "b":1}}' @? '$.** ? (@.a == - @.b)'; select jsonb '{"c": {"a": 0, "b":1}}' @? '$.** ? (@.a == 1 - @.b)'; select jsonb '{"c": {"a": 2, "b":1}}' @? '$.** ? (@.a == 1 - - @.b)'; select jsonb '{"c": {"a": 0, "b":1}}' @? '$.** ? (@.a == 1 - +@.b)'; select jsonb '[1,2,3]' @? '$ ? (+@[*] > +2)'; select jsonb '[1,2,3]' @? '$ ? (+@[*] > +3)'; select jsonb '[1,2,3]' @? '$ ? (-@[*] < -2)'; select jsonb '[1,2,3]' @? '$ ? (-@[*] < -3)'; select jsonb '1' @? '$ ? ($ > 0)'; -- arithmetic errors select jsonb_path_query('[1,2,0,3]', '$[*] ? (2 / @ > 0)'); select jsonb_path_query('[1,2,0,3]', '$[*] ? ((2 / @ > 0) is unknown)'); select jsonb_path_query('0', '1 / $'); select jsonb_path_query('0', '1 / $ + 2'); select jsonb_path_query('0', '-(3 + 1 % $)'); select jsonb_path_query('1', '$ + "2"'); select jsonb_path_query('[1, 2]', '3 * $'); select jsonb_path_query('"a"', '-$'); select jsonb_path_query('[1,"2",3]', '+$'); select jsonb_path_query('1', '$ + "2"', silent => true); select jsonb_path_query('[1, 2]', '3 * $', silent => true); select jsonb_path_query('"a"', '-$', silent => true); select jsonb_path_query('[1,"2",3]', '+$', silent => true); select jsonb '["1",2,0,3]' @? '-$[*]'; select jsonb '[1,"2",0,3]' @? '-$[*]'; select jsonb '["1",2,0,3]' @? 'strict -$[*]'; select jsonb '[1,"2",0,3]' @? 'strict -$[*]'; -- unwrapping of operator arguments in lax mode select jsonb_path_query('{"a": [2]}', 'lax $.a * 3'); select jsonb_path_query('{"a": [2]}', 'lax $.a + 3'); select jsonb_path_query('{"a": [2, 3, 4]}', 'lax -$.a'); -- should fail select jsonb_path_query('{"a": [1, 2]}', 'lax $.a * 3'); select jsonb_path_query('{"a": [1, 2]}', 'lax $.a * 3', silent => true); -- extension: boolean expressions select jsonb_path_query('2', '$ > 1'); select jsonb_path_query('2', '$ <= 1'); select jsonb_path_query('2', '$ == "2"'); select jsonb '2' @? '$ == "2"'; select jsonb '2' @@ '$ > 1'; select jsonb '2' @@ '$ <= 1'; select jsonb '2' @@ '$ == "2"'; select jsonb '2' @@ '1'; select jsonb '{}' @@ '$'; select jsonb '[]' @@ '$'; select jsonb '[1,2,3]' @@ '$[*]'; select jsonb '[]' @@ '$[*]'; select jsonb_path_match('[[1, true], [2, false]]', 'strict $[*] ? (@[0] > $x) [1]', '{"x": 1}'); select jsonb_path_match('[[1, true], [2, false]]', 'strict $[*] ? (@[0] < $x) [1]', '{"x": 2}'); select jsonb_path_match('[{"a": 1}, {"a": 2}, 3]', 'lax exists($[*].a)', silent => false); select jsonb_path_match('[{"a": 1}, {"a": 2}, 3]', 'lax exists($[*].a)', silent => true); select jsonb_path_match('[{"a": 1}, {"a": 2}, 3]', 'strict exists($[*].a)', silent => false); select jsonb_path_match('[{"a": 1}, {"a": 2}, 3]', 'strict exists($[*].a)', silent => true); select jsonb_path_query('[null,1,true,"a",[],{}]', '$.type()'); select jsonb_path_query('[null,1,true,"a",[],{}]', 'lax $.type()'); select jsonb_path_query('[null,1,true,"a",[],{}]', '$[*].type()'); select jsonb_path_query('null', 'null.type()'); select jsonb_path_query('null', 'true.type()'); select jsonb_path_query('null', '(123).type()'); select jsonb_path_query('null', '"123".type()'); select jsonb_path_query('{"a": 2}', '($.a - 5).abs() + 10'); select jsonb_path_query('{"a": 2.5}', '-($.a * $.a).floor() % 4.3'); select jsonb_path_query('[1, 2, 3]', '($[*] > 2) ? (@ == true)'); select jsonb_path_query('[1, 2, 3]', '($[*] > 3).type()'); select jsonb_path_query('[1, 2, 3]', '($[*].a > 3).type()'); select jsonb_path_query('[1, 2, 3]', 'strict ($[*].a > 3).type()'); select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'strict $[*].size()'); select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'strict $[*].size()', silent => true); select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'lax $[*].size()'); select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].abs()'); select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].floor()'); select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].ceiling()'); select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].ceiling().abs()'); select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].ceiling().abs().type()'); select jsonb_path_query('[{},1]', '$[*].keyvalue()'); select jsonb_path_query('[{},1]', '$[*].keyvalue()', silent => true); select jsonb_path_query('{}', '$.keyvalue()'); select jsonb_path_query('{"a": 1, "b": [1, 2], "c": {"a": "bbb"}}', '$.keyvalue()'); select jsonb_path_query('[{"a": 1, "b": [1, 2]}, {"c": {"a": "bbb"}}]', '$[*].keyvalue()'); select jsonb_path_query('[{"a": 1, "b": [1, 2]}, {"c": {"a": "bbb"}}]', 'strict $.keyvalue()'); select jsonb_path_query('[{"a": 1, "b": [1, 2]}, {"c": {"a": "bbb"}}]', 'lax $.keyvalue()'); select jsonb_path_query('[{"a": 1, "b": [1, 2]}, {"c": {"a": "bbb"}}]', 'strict $.keyvalue().a'); select jsonb '{"a": 1, "b": [1, 2]}' @? 'lax $.keyvalue()'; select jsonb '{"a": 1, "b": [1, 2]}' @? 'lax $.keyvalue().key'; select jsonb_path_query('null', '$.double()'); select jsonb_path_query('true', '$.double()'); select jsonb_path_query('null', '$.double()', silent => true); select jsonb_path_query('true', '$.double()', silent => true); select jsonb_path_query('[]', '$.double()'); select jsonb_path_query('[]', 'strict $.double()'); select jsonb_path_query('{}', '$.double()'); select jsonb_path_query('[]', 'strict $.double()', silent => true); select jsonb_path_query('{}', '$.double()', silent => true); select jsonb_path_query('1.23', '$.double()'); select jsonb_path_query('"1.23"', '$.double()'); select jsonb_path_query('"1.23aaa"', '$.double()'); select jsonb_path_query('"nan"', '$.double()'); select jsonb_path_query('"NaN"', '$.double()'); select jsonb_path_query('"inf"', '$.double()'); select jsonb_path_query('"-inf"', '$.double()'); select jsonb_path_query('"inf"', '$.double()', silent => true); select jsonb_path_query('"-inf"', '$.double()', silent => true); select jsonb_path_query('{}', '$.abs()'); select jsonb_path_query('true', '$.floor()'); select jsonb_path_query('"1.2"', '$.ceiling()'); select jsonb_path_query('{}', '$.abs()', silent => true); select jsonb_path_query('true', '$.floor()', silent => true); select jsonb_path_query('"1.2"', '$.ceiling()', silent => true); select jsonb_path_query('["", "a", "abc", "abcabc"]', '$[*] ? (@ starts with "abc")'); select jsonb_path_query('["", "a", "abc", "abcabc"]', 'strict $ ? (@[*] starts with "abc")'); select jsonb_path_query('["", "a", "abd", "abdabc"]', 'strict $ ? (@[*] starts with "abc")'); select jsonb_path_query('["abc", "abcabc", null, 1]', 'strict $ ? (@[*] starts with "abc")'); select jsonb_path_query('["abc", "abcabc", null, 1]', 'strict $ ? ((@[*] starts with "abc") is unknown)'); select jsonb_path_query('[[null, 1, "abc", "abcabc"]]', 'lax $ ? (@[*] starts with "abc")'); select jsonb_path_query('[[null, 1, "abd", "abdabc"]]', 'lax $ ? ((@[*] starts with "abc") is unknown)'); select jsonb_path_query('[null, 1, "abd", "abdabc"]', 'lax $[*] ? ((@ starts with "abc") is unknown)'); select jsonb_path_query('[null, 1, "abc", "abd", "aBdC", "abdacb", "adc\nabc", "babc"]', 'lax $[*] ? (@ like_regex "^ab.*c")'); select jsonb_path_query('[null, 1, "abc", "abd", "aBdC", "abdacb", "adc\nabc", "babc"]', 'lax $[*] ? (@ like_regex "^a b.* c " flag "ix")'); select jsonb_path_query('[null, 1, "abc", "abd", "aBdC", "abdacb", "adc\nabc", "babc"]', 'lax $[*] ? (@ like_regex "^ab.*c" flag "m")'); select jsonb_path_query('[null, 1, "abc", "abd", "aBdC", "abdacb", "adc\nabc", "babc"]', 'lax $[*] ? (@ like_regex "^ab.*c" flag "s")'); -- jsonpath operators SELECT jsonb_path_query('[{"a": 1}, {"a": 2}]', '$[*]'); SELECT jsonb_path_query('[{"a": 1}, {"a": 2}]', '$[*] ? (@.a > 10)'); SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}, {}]', 'strict $[*].a'); SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}]', '$[*].a'); SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}]', '$[*].a ? (@ == 1)'); SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}]', '$[*].a ? (@ > 10)'); SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*].a ? (@ > $min && @ < $max)', vars => '{"min": 1, "max": 4}'); SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*].a ? (@ > $min && @ < $max)', vars => '{"min": 3, "max": 4}'); SELECT jsonb_path_query_first('[{"a": 1}, {"a": 2}, {}]', 'strict $[*].a'); SELECT jsonb_path_query_first('[{"a": 1}, {"a": 2}, {}]', 'strict $[*].a', silent => true); SELECT jsonb_path_query_first('[{"a": 1}, {"a": 2}]', '$[*].a'); SELECT jsonb_path_query_first('[{"a": 1}, {"a": 2}]', '$[*].a ? (@ == 1)'); SELECT jsonb_path_query_first('[{"a": 1}, {"a": 2}]', '$[*].a ? (@ > 10)'); SELECT jsonb_path_query_first('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*].a ? (@ > $min && @ < $max)', vars => '{"min": 1, "max": 4}'); SELECT jsonb_path_query_first('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*].a ? (@ > $min && @ < $max)', vars => '{"min": 3, "max": 4}'); SELECT jsonb '[{"a": 1}, {"a": 2}]' @? '$[*].a ? (@ > 1)'; SELECT jsonb '[{"a": 1}, {"a": 2}]' @? '$[*] ? (@.a > 2)'; SELECT jsonb_path_exists('[{"a": 1}, {"a": 2}]', '$[*].a ? (@ > 1)'); SELECT jsonb_path_exists('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*] ? (@.a > $min && @.a < $max)', vars => '{"min": 1, "max": 4}'); SELECT jsonb_path_exists('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*] ? (@.a > $min && @.a < $max)', vars => '{"min": 3, "max": 4}'); SELECT jsonb_path_match('true', '$', silent => false); SELECT jsonb_path_match('false', '$', silent => false); SELECT jsonb_path_match('null', '$', silent => false); SELECT jsonb_path_match('1', '$', silent => true); SELECT jsonb_path_match('1', '$', silent => false); SELECT jsonb_path_match('"a"', '$', silent => false); SELECT jsonb_path_match('{}', '$', silent => false); SELECT jsonb_path_match('[true]', '$', silent => false); SELECT jsonb_path_match('{}', 'lax $.a', silent => false); SELECT jsonb_path_match('{}', 'strict $.a', silent => false); SELECT jsonb_path_match('{}', 'strict $.a', silent => true); SELECT jsonb_path_match('[true, true]', '$[*]', silent => false); SELECT jsonb '[{"a": 1}, {"a": 2}]' @@ '$[*].a > 1'; SELECT jsonb '[{"a": 1}, {"a": 2}]' @@ '$[*].a > 2'; SELECT jsonb_path_match('[{"a": 1}, {"a": 2}]', '$[*].a > 1'); pgFormatter-4.2/t/pg-test-files/sql/jsonpath.sql000066400000000000000000000137471361326045100217620ustar00rootroot00000000000000--jsonpath io select ''::jsonpath; select '$'::jsonpath; select 'strict $'::jsonpath; select 'lax $'::jsonpath; select '$.a'::jsonpath; select '$.a.v'::jsonpath; select '$.a.*'::jsonpath; select '$.*[*]'::jsonpath; select '$.a[*]'::jsonpath; select '$.a[*][*]'::jsonpath; select '$[*]'::jsonpath; select '$[0]'::jsonpath; select '$[*][0]'::jsonpath; select '$[*].a'::jsonpath; select '$[*][0].a.b'::jsonpath; select '$.a.**.b'::jsonpath; select '$.a.**{2}.b'::jsonpath; select '$.a.**{2 to 2}.b'::jsonpath; select '$.a.**{2 to 5}.b'::jsonpath; select '$.a.**{0 to 5}.b'::jsonpath; select '$.a.**{5 to last}.b'::jsonpath; select '$.a.**{last}.b'::jsonpath; select '$.a.**{last to 5}.b'::jsonpath; select '$+1'::jsonpath; select '$-1'::jsonpath; select '$--+1'::jsonpath; select '$.a/+-1'::jsonpath; select '1 * 2 + 4 % -3 != false'::jsonpath; select '"\b\f\r\n\t\v\"\''\\"'::jsonpath; select '''\b\f\r\n\t\v\"\''\\'''::jsonpath; select '"\x50\u0067\u{53}\u{051}\u{00004C}"'::jsonpath; select '''\x50\u0067\u{53}\u{051}\u{00004C}'''::jsonpath; select '$.foo\x50\u0067\u{53}\u{051}\u{00004C}\t\"bar'::jsonpath; select '$.g ? ($.a == 1)'::jsonpath; select '$.g ? (@ == 1)'::jsonpath; select '$.g ? (@.a == 1)'::jsonpath; select '$.g ? (@.a == 1 || @.a == 4)'::jsonpath; select '$.g ? (@.a == 1 && @.a == 4)'::jsonpath; select '$.g ? (@.a == 1 || @.a == 4 && @.b == 7)'::jsonpath; select '$.g ? (@.a == 1 || !(@.a == 4) && @.b == 7)'::jsonpath; select '$.g ? (@.a == 1 || !(@.x >= 123 || @.a == 4) && @.b == 7)'::jsonpath; select '$.g ? (@.x >= @[*]?(@.a > "abc"))'::jsonpath; select '$.g ? ((@.x >= 123 || @.a == 4) is unknown)'::jsonpath; select '$.g ? (exists (@.x))'::jsonpath; select '$.g ? (exists (@.x ? (@ == 14)))'::jsonpath; select '$.g ? ((@.x >= 123 || @.a == 4) && exists (@.x ? (@ == 14)))'::jsonpath; select '$.g ? (+@.x >= +-(+@.a + 2))'::jsonpath; select '$a'::jsonpath; select '$a.b'::jsonpath; select '$a[*]'::jsonpath; select '$.g ? (@.zip == $zip)'::jsonpath; select '$.a[1,2, 3 to 16]'::jsonpath; select '$.a[$a + 1, ($b[*]) to -($[0] * 2)]'::jsonpath; select '$.a[$.a.size() - 3]'::jsonpath; select 'last'::jsonpath; select '"last"'::jsonpath; select '$.last'::jsonpath; select '$ ? (last > 0)'::jsonpath; select '$[last]'::jsonpath; select '$[$[0] ? (last > 0)]'::jsonpath; select 'null.type()'::jsonpath; select '1.type()'::jsonpath; select '(1).type()'::jsonpath; select '1.2.type()'::jsonpath; select '"aaa".type()'::jsonpath; select 'true.type()'::jsonpath; select '$.double().floor().ceiling().abs()'::jsonpath; select '$.keyvalue().key'::jsonpath; select '$ ? (@ starts with "abc")'::jsonpath; select '$ ? (@ starts with $var)'::jsonpath; select '$ ? (@ like_regex "(invalid pattern")'::jsonpath; select '$ ? (@ like_regex "pattern")'::jsonpath; select '$ ? (@ like_regex "pattern" flag "")'::jsonpath; select '$ ? (@ like_regex "pattern" flag "i")'::jsonpath; select '$ ? (@ like_regex "pattern" flag "is")'::jsonpath; select '$ ? (@ like_regex "pattern" flag "isim")'::jsonpath; select '$ ? (@ like_regex "pattern" flag "xsms")'::jsonpath; select '$ ? (@ like_regex "pattern" flag "a")'::jsonpath; select '$ < 1'::jsonpath; select '($ < 1) || $.a.b <= $x'::jsonpath; select '@ + 1'::jsonpath; select '($).a.b'::jsonpath; select '($.a.b).c.d'::jsonpath; select '($.a.b + -$.x.y).c.d'::jsonpath; select '(-+$.a.b).c.d'::jsonpath; select '1 + ($.a.b + 2).c.d'::jsonpath; select '1 + ($.a.b > 2).c.d'::jsonpath; select '($)'::jsonpath; select '(($))'::jsonpath; select '((($ + 1)).a + ((2)).b ? ((((@ > 1)) || (exists(@.c)))))'::jsonpath; select '$ ? (@.a < 1)'::jsonpath; select '$ ? (@.a < -1)'::jsonpath; select '$ ? (@.a < +1)'::jsonpath; select '$ ? (@.a < .1)'::jsonpath; select '$ ? (@.a < -.1)'::jsonpath; select '$ ? (@.a < +.1)'::jsonpath; select '$ ? (@.a < 0.1)'::jsonpath; select '$ ? (@.a < -0.1)'::jsonpath; select '$ ? (@.a < +0.1)'::jsonpath; select '$ ? (@.a < 10.1)'::jsonpath; select '$ ? (@.a < -10.1)'::jsonpath; select '$ ? (@.a < +10.1)'::jsonpath; select '$ ? (@.a < 1e1)'::jsonpath; select '$ ? (@.a < -1e1)'::jsonpath; select '$ ? (@.a < +1e1)'::jsonpath; select '$ ? (@.a < .1e1)'::jsonpath; select '$ ? (@.a < -.1e1)'::jsonpath; select '$ ? (@.a < +.1e1)'::jsonpath; select '$ ? (@.a < 0.1e1)'::jsonpath; select '$ ? (@.a < -0.1e1)'::jsonpath; select '$ ? (@.a < +0.1e1)'::jsonpath; select '$ ? (@.a < 10.1e1)'::jsonpath; select '$ ? (@.a < -10.1e1)'::jsonpath; select '$ ? (@.a < +10.1e1)'::jsonpath; select '$ ? (@.a < 1e-1)'::jsonpath; select '$ ? (@.a < -1e-1)'::jsonpath; select '$ ? (@.a < +1e-1)'::jsonpath; select '$ ? (@.a < .1e-1)'::jsonpath; select '$ ? (@.a < -.1e-1)'::jsonpath; select '$ ? (@.a < +.1e-1)'::jsonpath; select '$ ? (@.a < 0.1e-1)'::jsonpath; select '$ ? (@.a < -0.1e-1)'::jsonpath; select '$ ? (@.a < +0.1e-1)'::jsonpath; select '$ ? (@.a < 10.1e-1)'::jsonpath; select '$ ? (@.a < -10.1e-1)'::jsonpath; select '$ ? (@.a < +10.1e-1)'::jsonpath; select '$ ? (@.a < 1e+1)'::jsonpath; select '$ ? (@.a < -1e+1)'::jsonpath; select '$ ? (@.a < +1e+1)'::jsonpath; select '$ ? (@.a < .1e+1)'::jsonpath; select '$ ? (@.a < -.1e+1)'::jsonpath; select '$ ? (@.a < +.1e+1)'::jsonpath; select '$ ? (@.a < 0.1e+1)'::jsonpath; select '$ ? (@.a < -0.1e+1)'::jsonpath; select '$ ? (@.a < +0.1e+1)'::jsonpath; select '$ ? (@.a < 10.1e+1)'::jsonpath; select '$ ? (@.a < -10.1e+1)'::jsonpath; select '$ ? (@.a < +10.1e+1)'::jsonpath; select '0'::jsonpath; select '00'::jsonpath; select '0.0'::jsonpath; select '0.000'::jsonpath; select '0.000e1'::jsonpath; select '0.000e2'::jsonpath; select '0.000e3'::jsonpath; select '0.0010'::jsonpath; select '0.0010e-1'::jsonpath; select '0.0010e+1'::jsonpath; select '0.0010e+2'::jsonpath; select '1e'::jsonpath; select '1.e'::jsonpath; select '1.2e'::jsonpath; select '1.2.e'::jsonpath; select '(1.2).e'::jsonpath; select '1e3'::jsonpath; select '1.e3'::jsonpath; select '1.e3.e'::jsonpath; select '1.e3.e4'::jsonpath; select '1.2e3'::jsonpath; select '1.2.e3'::jsonpath; select '(1.2).e3'::jsonpath; select '1..e'::jsonpath; select '1..e3'::jsonpath; select '(1.).e'::jsonpath; select '(1.).e3'::jsonpath; pgFormatter-4.2/t/pg-test-files/sql/jsonpath_encoding.sql000066400000000000000000000062611361326045100236210ustar00rootroot00000000000000 -- encoding-sensitive tests for jsonpath -- checks for double-quoted values -- basic unicode input SELECT '"\u"'::jsonpath; -- ERROR, incomplete escape SELECT '"\u00"'::jsonpath; -- ERROR, incomplete escape SELECT '"\u000g"'::jsonpath; -- ERROR, g is not a hex digit SELECT '"\u0000"'::jsonpath; -- OK, legal escape SELECT '"\uaBcD"'::jsonpath; -- OK, uppercase and lower case both OK -- handling of unicode surrogate pairs select '"\ud83d\ude04\ud83d\udc36"'::jsonpath as correct_in_utf8; select '"\ud83d\ud83d"'::jsonpath; -- 2 high surrogates in a row select '"\ude04\ud83d"'::jsonpath; -- surrogates in wrong order select '"\ud83dX"'::jsonpath; -- orphan high surrogate select '"\ude04X"'::jsonpath; -- orphan low surrogate --handling of simple unicode escapes select '"the Copyright \u00a9 sign"'::jsonpath as correct_in_utf8; select '"dollar \u0024 character"'::jsonpath as correct_everywhere; select '"dollar \\u0024 character"'::jsonpath as not_an_escape; select '"null \u0000 escape"'::jsonpath as not_unescaped; select '"null \\u0000 escape"'::jsonpath as not_an_escape; -- checks for single-quoted values -- basic unicode input SELECT E'\'\u\''::jsonpath; -- ERROR, incomplete escape SELECT E'\'\u00\''::jsonpath; -- ERROR, incomplete escape SELECT E'\'\u000g\''::jsonpath; -- ERROR, g is not a hex digit SELECT E'\'\u0000\''::jsonpath; -- OK, legal escape SELECT E'\'\uaBcD\''::jsonpath; -- OK, uppercase and lower case both OK -- handling of unicode surrogate pairs select E'\'\ud83d\ude04\ud83d\udc36\''::jsonpath as correct_in_utf8; select E'\'\ud83d\ud83d\''::jsonpath; -- 2 high surrogates in a row select E'\'\ude04\ud83d\''::jsonpath; -- surrogates in wrong order select E'\'\ud83dX\''::jsonpath; -- orphan high surrogate select E'\'\ude04X\''::jsonpath; -- orphan low surrogate --handling of simple unicode escapes select E'\'the Copyright \u00a9 sign\''::jsonpath as correct_in_utf8; select E'\'dollar \u0024 character\''::jsonpath as correct_everywhere; select E'\'dollar \\u0024 character\''::jsonpath as not_an_escape; select E'\'null \u0000 escape\''::jsonpath as not_unescaped; select E'\'null \\u0000 escape\''::jsonpath as not_an_escape; -- checks for quoted key names -- basic unicode input SELECT '$."\u"'::jsonpath; -- ERROR, incomplete escape SELECT '$."\u00"'::jsonpath; -- ERROR, incomplete escape SELECT '$."\u000g"'::jsonpath; -- ERROR, g is not a hex digit SELECT '$."\u0000"'::jsonpath; -- OK, legal escape SELECT '$."\uaBcD"'::jsonpath; -- OK, uppercase and lower case both OK -- handling of unicode surrogate pairs select '$."\ud83d\ude04\ud83d\udc36"'::jsonpath as correct_in_utf8; select '$."\ud83d\ud83d"'::jsonpath; -- 2 high surrogates in a row select '$."\ude04\ud83d"'::jsonpath; -- surrogates in wrong order select '$."\ud83dX"'::jsonpath; -- orphan high surrogate select '$."\ude04X"'::jsonpath; -- orphan low surrogate --handling of simple unicode escapes select '$."the Copyright \u00a9 sign"'::jsonpath as correct_in_utf8; select '$."dollar \u0024 character"'::jsonpath as correct_everywhere; select '$."dollar \\u0024 character"'::jsonpath as not_an_escape; select '$."null \u0000 escape"'::jsonpath as not_unescaped; select '$."null \\u0000 escape"'::jsonpath as not_an_escape; pgFormatter-4.2/t/pg-test-files/sql/limit.sql000066400000000000000000000102551361326045100212410ustar00rootroot00000000000000-- -- LIMIT -- Check the LIMIT/OFFSET feature of SELECT -- SELECT ''::text AS two, unique1, unique2, stringu1 FROM onek WHERE unique1 > 50 ORDER BY unique1 LIMIT 2; SELECT ''::text AS five, unique1, unique2, stringu1 FROM onek WHERE unique1 > 60 ORDER BY unique1 LIMIT 5; SELECT ''::text AS two, unique1, unique2, stringu1 FROM onek WHERE unique1 > 60 AND unique1 < 63 ORDER BY unique1 LIMIT 5; SELECT ''::text AS three, unique1, unique2, stringu1 FROM onek WHERE unique1 > 100 ORDER BY unique1 LIMIT 3 OFFSET 20; SELECT ''::text AS zero, unique1, unique2, stringu1 FROM onek WHERE unique1 < 50 ORDER BY unique1 DESC LIMIT 8 OFFSET 99; SELECT ''::text AS eleven, unique1, unique2, stringu1 FROM onek WHERE unique1 < 50 ORDER BY unique1 DESC LIMIT 20 OFFSET 39; SELECT ''::text AS ten, unique1, unique2, stringu1 FROM onek ORDER BY unique1 OFFSET 990; SELECT ''::text AS five, unique1, unique2, stringu1 FROM onek ORDER BY unique1 OFFSET 990 LIMIT 5; SELECT ''::text AS five, unique1, unique2, stringu1 FROM onek ORDER BY unique1 LIMIT 5 OFFSET 900; -- Test null limit and offset. The planner would discard a simple null -- constant, so to ensure executor is exercised, do this: select * from int8_tbl limit (case when random() < 0.5 then null::bigint end); select * from int8_tbl offset (case when random() < 0.5 then null::bigint end); -- Test assorted cases involving backwards fetch from a LIMIT plan node begin; declare c1 cursor for select * from int8_tbl limit 10; fetch all in c1; fetch 1 in c1; fetch backward 1 in c1; fetch backward all in c1; fetch backward 1 in c1; fetch all in c1; declare c2 cursor for select * from int8_tbl limit 3; fetch all in c2; fetch 1 in c2; fetch backward 1 in c2; fetch backward all in c2; fetch backward 1 in c2; fetch all in c2; declare c3 cursor for select * from int8_tbl offset 3; fetch all in c3; fetch 1 in c3; fetch backward 1 in c3; fetch backward all in c3; fetch backward 1 in c3; fetch all in c3; declare c4 cursor for select * from int8_tbl offset 10; fetch all in c4; fetch 1 in c4; fetch backward 1 in c4; fetch backward all in c4; fetch backward 1 in c4; fetch all in c4; rollback; -- Stress test for variable LIMIT in conjunction with bounded-heap sorting SELECT (SELECT n FROM (VALUES (1)) AS x, (SELECT n FROM generate_series(1,10) AS n ORDER BY n LIMIT 1 OFFSET s-1) AS y) AS z FROM generate_series(1,10) AS s; -- -- Test behavior of volatile and set-returning functions in conjunction -- with ORDER BY and LIMIT. -- create temp sequence testseq; explain (verbose, costs off) select unique1, unique2, nextval('testseq') from tenk1 order by unique2 limit 10; select unique1, unique2, nextval('testseq') from tenk1 order by unique2 limit 10; select currval('testseq'); explain (verbose, costs off) select unique1, unique2, nextval('testseq') from tenk1 order by tenthous limit 10; select unique1, unique2, nextval('testseq') from tenk1 order by tenthous limit 10; select currval('testseq'); explain (verbose, costs off) select unique1, unique2, generate_series(1,10) from tenk1 order by unique2 limit 7; select unique1, unique2, generate_series(1,10) from tenk1 order by unique2 limit 7; explain (verbose, costs off) select unique1, unique2, generate_series(1,10) from tenk1 order by tenthous limit 7; select unique1, unique2, generate_series(1,10) from tenk1 order by tenthous limit 7; -- use of random() is to keep planner from folding the expressions together explain (verbose, costs off) select generate_series(0,2) as s1, generate_series((random()*.1)::int,2) as s2; select generate_series(0,2) as s1, generate_series((random()*.1)::int,2) as s2; explain (verbose, costs off) select generate_series(0,2) as s1, generate_series((random()*.1)::int,2) as s2 order by s2 desc; select generate_series(0,2) as s1, generate_series((random()*.1)::int,2) as s2 order by s2 desc; -- test for failure to set all aggregates' aggtranstype explain (verbose, costs off) select sum(tenthous) as s1, sum(tenthous) + random()*0 as s2 from tenk1 group by thousand order by thousand limit 3; select sum(tenthous) as s1, sum(tenthous) + random()*0 as s2 from tenk1 group by thousand order by thousand limit 3; pgFormatter-4.2/t/pg-test-files/sql/line.sql000066400000000000000000000024771361326045100210610ustar00rootroot00000000000000-- -- LINE -- Infinite lines -- --DROP TABLE LINE_TBL; CREATE TABLE LINE_TBL (s line); INSERT INTO LINE_TBL VALUES ('{0,-1,5}'); -- A == 0 INSERT INTO LINE_TBL VALUES ('{1,0,5}'); -- B == 0 INSERT INTO LINE_TBL VALUES ('{0,3,0}'); -- A == C == 0 INSERT INTO LINE_TBL VALUES (' (0,0), (6,6)'); INSERT INTO LINE_TBL VALUES ('10,-10 ,-5,-4'); INSERT INTO LINE_TBL VALUES ('[-1e6,2e2,3e5, -4e1]'); INSERT INTO LINE_TBL VALUES ('{3,NaN,5}'); INSERT INTO LINE_TBL VALUES ('{NaN,NaN,NaN}'); -- horizontal INSERT INTO LINE_TBL VALUES ('[(1,3),(2,3)]'); -- vertical INSERT INTO LINE_TBL VALUES (line(point '(3,1)', point '(3,2)')); -- bad values for parser testing INSERT INTO LINE_TBL VALUES ('{}'); INSERT INTO LINE_TBL VALUES ('{0'); INSERT INTO LINE_TBL VALUES ('{0,0}'); INSERT INTO LINE_TBL VALUES ('{0,0,1'); INSERT INTO LINE_TBL VALUES ('{0,0,1}'); INSERT INTO LINE_TBL VALUES ('{0,0,1} x'); INSERT INTO LINE_TBL VALUES ('(3asdf,2 ,3,4r2)'); INSERT INTO LINE_TBL VALUES ('[1,2,3, 4'); INSERT INTO LINE_TBL VALUES ('[(,2),(3,4)]'); INSERT INTO LINE_TBL VALUES ('[(1,2),(3,4)'); INSERT INTO LINE_TBL VALUES ('[(1,2),(1,2)]'); INSERT INTO LINE_TBL VALUES (line(point '(1,0)', point '(1,0)')); select * from LINE_TBL; select '{nan, 1, nan}'::line = '{nan, 1, nan}'::line as true, '{nan, 1, nan}'::line = '{nan, 2, nan}'::line as false; pgFormatter-4.2/t/pg-test-files/sql/lock.sql000066400000000000000000000107201361326045100210500ustar00rootroot00000000000000-- -- Test the LOCK statement -- -- Setup CREATE SCHEMA lock_schema1; SET search_path = lock_schema1; CREATE TABLE lock_tbl1 (a BIGINT); CREATE TABLE lock_tbl1a (a BIGINT); CREATE VIEW lock_view1 AS SELECT * FROM lock_tbl1; CREATE VIEW lock_view2(a,b) AS SELECT * FROM lock_tbl1, lock_tbl1a; CREATE VIEW lock_view3 AS SELECT * from lock_view2; CREATE VIEW lock_view4 AS SELECT (select a from lock_tbl1a limit 1) from lock_tbl1; CREATE VIEW lock_view5 AS SELECT * from lock_tbl1 where a in (select * from lock_tbl1a); CREATE VIEW lock_view6 AS SELECT * from (select * from lock_tbl1) sub; CREATE ROLE regress_rol_lock1; ALTER ROLE regress_rol_lock1 SET search_path = lock_schema1; GRANT USAGE ON SCHEMA lock_schema1 TO regress_rol_lock1; -- Try all valid lock options; also try omitting the optional TABLE keyword. BEGIN TRANSACTION; LOCK TABLE lock_tbl1 IN ACCESS SHARE MODE; LOCK lock_tbl1 IN ROW SHARE MODE; LOCK TABLE lock_tbl1 IN ROW EXCLUSIVE MODE; LOCK TABLE lock_tbl1 IN SHARE UPDATE EXCLUSIVE MODE; LOCK TABLE lock_tbl1 IN SHARE MODE; LOCK lock_tbl1 IN SHARE ROW EXCLUSIVE MODE; LOCK TABLE lock_tbl1 IN EXCLUSIVE MODE; LOCK TABLE lock_tbl1 IN ACCESS EXCLUSIVE MODE; ROLLBACK; -- Try using NOWAIT along with valid options. BEGIN TRANSACTION; LOCK TABLE lock_tbl1 IN ACCESS SHARE MODE NOWAIT; LOCK TABLE lock_tbl1 IN ROW SHARE MODE NOWAIT; LOCK TABLE lock_tbl1 IN ROW EXCLUSIVE MODE NOWAIT; LOCK TABLE lock_tbl1 IN SHARE UPDATE EXCLUSIVE MODE NOWAIT; LOCK TABLE lock_tbl1 IN SHARE MODE NOWAIT; LOCK TABLE lock_tbl1 IN SHARE ROW EXCLUSIVE MODE NOWAIT; LOCK TABLE lock_tbl1 IN EXCLUSIVE MODE NOWAIT; LOCK TABLE lock_tbl1 IN ACCESS EXCLUSIVE MODE NOWAIT; ROLLBACK; -- Verify that we can lock views. BEGIN TRANSACTION; LOCK TABLE lock_view1 IN EXCLUSIVE MODE; -- lock_view1 and lock_tbl1 are locked. select relname from pg_locks l, pg_class c where l.relation = c.oid and relname like '%lock_%' and mode = 'ExclusiveLock' order by relname; ROLLBACK; BEGIN TRANSACTION; LOCK TABLE lock_view2 IN EXCLUSIVE MODE; -- lock_view1, lock_tbl1, and lock_tbl1a are locked. select relname from pg_locks l, pg_class c where l.relation = c.oid and relname like '%lock_%' and mode = 'ExclusiveLock' order by relname; ROLLBACK; BEGIN TRANSACTION; LOCK TABLE lock_view3 IN EXCLUSIVE MODE; -- lock_view3, lock_view2, lock_tbl1, and lock_tbl1a are locked recursively. select relname from pg_locks l, pg_class c where l.relation = c.oid and relname like '%lock_%' and mode = 'ExclusiveLock' order by relname; ROLLBACK; BEGIN TRANSACTION; LOCK TABLE lock_view4 IN EXCLUSIVE MODE; -- lock_view4, lock_tbl1, and lock_tbl1a are locked. select relname from pg_locks l, pg_class c where l.relation = c.oid and relname like '%lock_%' and mode = 'ExclusiveLock' order by relname; ROLLBACK; BEGIN TRANSACTION; LOCK TABLE lock_view5 IN EXCLUSIVE MODE; -- lock_view5, lock_tbl1, and lock_tbl1a are locked. select relname from pg_locks l, pg_class c where l.relation = c.oid and relname like '%lock_%' and mode = 'ExclusiveLock' order by relname; ROLLBACK; BEGIN TRANSACTION; LOCK TABLE lock_view6 IN EXCLUSIVE MODE; -- lock_view6 an lock_tbl1 are locked. select relname from pg_locks l, pg_class c where l.relation = c.oid and relname like '%lock_%' and mode = 'ExclusiveLock' order by relname; ROLLBACK; -- detecting infinite recursions in view definitions CREATE OR REPLACE VIEW lock_view2 AS SELECT * from lock_view3; BEGIN TRANSACTION; LOCK TABLE lock_view2 IN EXCLUSIVE MODE; ROLLBACK; CREATE VIEW lock_view7 AS SELECT * from lock_view2; BEGIN TRANSACTION; LOCK TABLE lock_view7 IN EXCLUSIVE MODE; ROLLBACK; -- Verify that we can lock a table with inheritance children. CREATE TABLE lock_tbl2 (b BIGINT) INHERITS (lock_tbl1); CREATE TABLE lock_tbl3 () INHERITS (lock_tbl2); BEGIN TRANSACTION; LOCK TABLE lock_tbl1 * IN ACCESS EXCLUSIVE MODE; ROLLBACK; -- Verify that we can't lock a child table just because we have permission -- on the parent, but that we can lock the parent only. GRANT UPDATE ON TABLE lock_tbl1 TO regress_rol_lock1; SET ROLE regress_rol_lock1; BEGIN; LOCK TABLE lock_tbl1 * IN ACCESS EXCLUSIVE MODE; ROLLBACK; BEGIN; LOCK TABLE ONLY lock_tbl1; ROLLBACK; RESET ROLE; -- -- Clean up -- DROP VIEW lock_view7; DROP VIEW lock_view6; DROP VIEW lock_view5; DROP VIEW lock_view4; DROP VIEW lock_view3 CASCADE; DROP VIEW lock_view1; DROP TABLE lock_tbl3; DROP TABLE lock_tbl2; DROP TABLE lock_tbl1; DROP TABLE lock_tbl1a; DROP SCHEMA lock_schema1 CASCADE; DROP ROLE regress_rol_lock1; -- atomic ops tests RESET search_path; SELECT test_atomic_ops(); pgFormatter-4.2/t/pg-test-files/sql/lseg.sql000066400000000000000000000014121361326045100210500ustar00rootroot00000000000000-- -- LSEG -- Line segments -- --DROP TABLE LSEG_TBL; CREATE TABLE LSEG_TBL (s lseg); INSERT INTO LSEG_TBL VALUES ('[(1,2),(3,4)]'); INSERT INTO LSEG_TBL VALUES ('(0,0),(6,6)'); INSERT INTO LSEG_TBL VALUES ('10,-10 ,-3,-4'); INSERT INTO LSEG_TBL VALUES ('[-1e6,2e2,3e5, -4e1]'); INSERT INTO LSEG_TBL VALUES (lseg(point(11, 22), point(33,44))); INSERT INTO LSEG_TBL VALUES ('[(-10,2),(-10,3)]'); -- vertical INSERT INTO LSEG_TBL VALUES ('[(0,-20),(30,-20)]'); -- horizontal INSERT INTO LSEG_TBL VALUES ('[(NaN,1),(NaN,90)]'); -- NaN -- bad values for parser testing INSERT INTO LSEG_TBL VALUES ('(3asdf,2 ,3,4r2)'); INSERT INTO LSEG_TBL VALUES ('[1,2,3, 4'); INSERT INTO LSEG_TBL VALUES ('[(,2),(3,4)]'); INSERT INTO LSEG_TBL VALUES ('[(1,2),(3,4)'); select * from LSEG_TBL; pgFormatter-4.2/t/pg-test-files/sql/macaddr.sql000066400000000000000000000034671361326045100215250ustar00rootroot00000000000000-- -- macaddr -- CREATE TABLE macaddr_data (a int, b macaddr); INSERT INTO macaddr_data VALUES (1, '08:00:2b:01:02:03'); INSERT INTO macaddr_data VALUES (2, '08-00-2b-01-02-03'); INSERT INTO macaddr_data VALUES (3, '08002b:010203'); INSERT INTO macaddr_data VALUES (4, '08002b-010203'); INSERT INTO macaddr_data VALUES (5, '0800.2b01.0203'); INSERT INTO macaddr_data VALUES (6, '0800-2b01-0203'); INSERT INTO macaddr_data VALUES (7, '08002b010203'); INSERT INTO macaddr_data VALUES (8, '0800:2b01:0203'); -- invalid INSERT INTO macaddr_data VALUES (9, 'not even close'); -- invalid INSERT INTO macaddr_data VALUES (10, '08:00:2b:01:02:04'); INSERT INTO macaddr_data VALUES (11, '08:00:2b:01:02:02'); INSERT INTO macaddr_data VALUES (12, '08:00:2a:01:02:03'); INSERT INTO macaddr_data VALUES (13, '08:00:2c:01:02:03'); INSERT INTO macaddr_data VALUES (14, '08:00:2a:01:02:04'); SELECT * FROM macaddr_data; CREATE INDEX macaddr_data_btree ON macaddr_data USING btree (b); CREATE INDEX macaddr_data_hash ON macaddr_data USING hash (b); SELECT a, b, trunc(b) FROM macaddr_data ORDER BY 2, 1; SELECT b < '08:00:2b:01:02:04' FROM macaddr_data WHERE a = 1; -- true SELECT b > '08:00:2b:01:02:04' FROM macaddr_data WHERE a = 1; -- false SELECT b > '08:00:2b:01:02:03' FROM macaddr_data WHERE a = 1; -- false SELECT b <= '08:00:2b:01:02:04' FROM macaddr_data WHERE a = 1; -- true SELECT b >= '08:00:2b:01:02:04' FROM macaddr_data WHERE a = 1; -- false SELECT b = '08:00:2b:01:02:03' FROM macaddr_data WHERE a = 1; -- true SELECT b <> '08:00:2b:01:02:04' FROM macaddr_data WHERE a = 1; -- true SELECT b <> '08:00:2b:01:02:03' FROM macaddr_data WHERE a = 1; -- false SELECT ~b FROM macaddr_data; SELECT b & '00:00:00:ff:ff:ff' FROM macaddr_data; SELECT b | '01:02:03:04:05:06' FROM macaddr_data; DROP TABLE macaddr_data; pgFormatter-4.2/t/pg-test-files/sql/macaddr8.sql000066400000000000000000000103001361326045100215750ustar00rootroot00000000000000-- -- macaddr8 -- -- test various cases of valid and invalid input -- valid SELECT '08:00:2b:01:02:03 '::macaddr8; SELECT ' 08:00:2b:01:02:03 '::macaddr8; SELECT ' 08:00:2b:01:02:03'::macaddr8; SELECT '08:00:2b:01:02:03:04:05 '::macaddr8; SELECT ' 08:00:2b:01:02:03:04:05 '::macaddr8; SELECT ' 08:00:2b:01:02:03:04:05'::macaddr8; SELECT '123 08:00:2b:01:02:03'::macaddr8; -- invalid SELECT '08:00:2b:01:02:03 123'::macaddr8; -- invalid SELECT '123 08:00:2b:01:02:03:04:05'::macaddr8; -- invalid SELECT '08:00:2b:01:02:03:04:05 123'::macaddr8; -- invalid SELECT '08:00:2b:01:02:03:04:05:06:07'::macaddr8; -- invalid SELECT '08-00-2b-01-02-03-04-05-06-07'::macaddr8; -- invalid SELECT '08002b:01020304050607'::macaddr8; -- invalid SELECT '08002b01020304050607'::macaddr8; -- invalid SELECT '0z002b0102030405'::macaddr8; -- invalid SELECT '08002b010203xyza'::macaddr8; -- invalid SELECT '08:00-2b:01:02:03:04:05'::macaddr8; -- invalid SELECT '08:00-2b:01:02:03:04:05'::macaddr8; -- invalid SELECT '08:00:2b:01.02:03:04:05'::macaddr8; -- invalid SELECT '08:00:2b:01.02:03:04:05'::macaddr8; -- invalid -- test converting a MAC address to modified EUI-64 for inclusion -- in an ipv6 address SELECT macaddr8_set7bit('00:08:2b:01:02:03'::macaddr8); CREATE TABLE macaddr8_data (a int, b macaddr8); INSERT INTO macaddr8_data VALUES (1, '08:00:2b:01:02:03'); INSERT INTO macaddr8_data VALUES (2, '08-00-2b-01-02-03'); INSERT INTO macaddr8_data VALUES (3, '08002b:010203'); INSERT INTO macaddr8_data VALUES (4, '08002b-010203'); INSERT INTO macaddr8_data VALUES (5, '0800.2b01.0203'); INSERT INTO macaddr8_data VALUES (6, '0800-2b01-0203'); INSERT INTO macaddr8_data VALUES (7, '08002b010203'); INSERT INTO macaddr8_data VALUES (8, '0800:2b01:0203'); INSERT INTO macaddr8_data VALUES (9, 'not even close'); -- invalid INSERT INTO macaddr8_data VALUES (10, '08:00:2b:01:02:04'); INSERT INTO macaddr8_data VALUES (11, '08:00:2b:01:02:02'); INSERT INTO macaddr8_data VALUES (12, '08:00:2a:01:02:03'); INSERT INTO macaddr8_data VALUES (13, '08:00:2c:01:02:03'); INSERT INTO macaddr8_data VALUES (14, '08:00:2a:01:02:04'); INSERT INTO macaddr8_data VALUES (15, '08:00:2b:01:02:03:04:05'); INSERT INTO macaddr8_data VALUES (16, '08-00-2b-01-02-03-04-05'); INSERT INTO macaddr8_data VALUES (17, '08002b:0102030405'); INSERT INTO macaddr8_data VALUES (18, '08002b-0102030405'); INSERT INTO macaddr8_data VALUES (19, '0800.2b01.0203.0405'); INSERT INTO macaddr8_data VALUES (20, '08002b01:02030405'); INSERT INTO macaddr8_data VALUES (21, '08002b0102030405'); SELECT * FROM macaddr8_data ORDER BY 1; CREATE INDEX macaddr8_data_btree ON macaddr8_data USING btree (b); CREATE INDEX macaddr8_data_hash ON macaddr8_data USING hash (b); SELECT a, b, trunc(b) FROM macaddr8_data ORDER BY 2, 1; SELECT b < '08:00:2b:01:02:04' FROM macaddr8_data WHERE a = 1; -- true SELECT b > '08:00:2b:ff:fe:01:02:04' FROM macaddr8_data WHERE a = 1; -- false SELECT b > '08:00:2b:ff:fe:01:02:03' FROM macaddr8_data WHERE a = 1; -- false SELECT b::macaddr <= '08:00:2b:01:02:04' FROM macaddr8_data WHERE a = 1; -- true SELECT b::macaddr >= '08:00:2b:01:02:04' FROM macaddr8_data WHERE a = 1; -- false SELECT b = '08:00:2b:ff:fe:01:02:03' FROM macaddr8_data WHERE a = 1; -- true SELECT b::macaddr <> '08:00:2b:01:02:04'::macaddr FROM macaddr8_data WHERE a = 1; -- true SELECT b::macaddr <> '08:00:2b:01:02:03'::macaddr FROM macaddr8_data WHERE a = 1; -- false SELECT b < '08:00:2b:01:02:03:04:06' FROM macaddr8_data WHERE a = 15; -- true SELECT b > '08:00:2b:01:02:03:04:06' FROM macaddr8_data WHERE a = 15; -- false SELECT b > '08:00:2b:01:02:03:04:05' FROM macaddr8_data WHERE a = 15; -- false SELECT b <= '08:00:2b:01:02:03:04:06' FROM macaddr8_data WHERE a = 15; -- true SELECT b >= '08:00:2b:01:02:03:04:06' FROM macaddr8_data WHERE a = 15; -- false SELECT b = '08:00:2b:01:02:03:04:05' FROM macaddr8_data WHERE a = 15; -- true SELECT b <> '08:00:2b:01:02:03:04:06' FROM macaddr8_data WHERE a = 15; -- true SELECT b <> '08:00:2b:01:02:03:04:05' FROM macaddr8_data WHERE a = 15; -- false SELECT ~b FROM macaddr8_data; SELECT b & '00:00:00:ff:ff:ff' FROM macaddr8_data; SELECT b | '01:02:03:04:05:06' FROM macaddr8_data; DROP TABLE macaddr8_data; pgFormatter-4.2/t/pg-test-files/sql/matview.sql000066400000000000000000000231601361326045100215760ustar00rootroot00000000000000-- create a table to use as a basis for views and materialized views in various combinations CREATE TABLE mvtest_t (id int NOT NULL PRIMARY KEY, type text NOT NULL, amt numeric NOT NULL); INSERT INTO mvtest_t VALUES (1, 'x', 2), (2, 'x', 3), (3, 'y', 5), (4, 'y', 7), (5, 'z', 11); -- we want a view based on the table, too, since views present additional challenges CREATE VIEW mvtest_tv AS SELECT type, sum(amt) AS totamt FROM mvtest_t GROUP BY type; SELECT * FROM mvtest_tv ORDER BY type; -- create a materialized view with no data, and confirm correct behavior EXPLAIN (costs off) CREATE MATERIALIZED VIEW mvtest_tm AS SELECT type, sum(amt) AS totamt FROM mvtest_t GROUP BY type WITH NO DATA; CREATE MATERIALIZED VIEW mvtest_tm AS SELECT type, sum(amt) AS totamt FROM mvtest_t GROUP BY type WITH NO DATA; SELECT relispopulated FROM pg_class WHERE oid = 'mvtest_tm'::regclass; SELECT * FROM mvtest_tm ORDER BY type; REFRESH MATERIALIZED VIEW mvtest_tm; SELECT relispopulated FROM pg_class WHERE oid = 'mvtest_tm'::regclass; CREATE UNIQUE INDEX mvtest_tm_type ON mvtest_tm (type); SELECT * FROM mvtest_tm ORDER BY type; -- create various views EXPLAIN (costs off) CREATE MATERIALIZED VIEW mvtest_tvm AS SELECT * FROM mvtest_tv ORDER BY type; CREATE MATERIALIZED VIEW mvtest_tvm AS SELECT * FROM mvtest_tv ORDER BY type; SELECT * FROM mvtest_tvm; CREATE MATERIALIZED VIEW mvtest_tmm AS SELECT sum(totamt) AS grandtot FROM mvtest_tm; CREATE MATERIALIZED VIEW mvtest_tvmm AS SELECT sum(totamt) AS grandtot FROM mvtest_tvm; CREATE UNIQUE INDEX mvtest_tvmm_expr ON mvtest_tvmm ((grandtot > 0)); CREATE UNIQUE INDEX mvtest_tvmm_pred ON mvtest_tvmm (grandtot) WHERE grandtot < 0; CREATE VIEW mvtest_tvv AS SELECT sum(totamt) AS grandtot FROM mvtest_tv; EXPLAIN (costs off) CREATE MATERIALIZED VIEW mvtest_tvvm AS SELECT * FROM mvtest_tvv; CREATE MATERIALIZED VIEW mvtest_tvvm AS SELECT * FROM mvtest_tvv; CREATE VIEW mvtest_tvvmv AS SELECT * FROM mvtest_tvvm; CREATE MATERIALIZED VIEW mvtest_bb AS SELECT * FROM mvtest_tvvmv; CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); -- check that plans seem reasonable \d+ mvtest_tvm \d+ mvtest_tvm \d+ mvtest_tvvm \d+ mvtest_bb -- test schema behavior CREATE SCHEMA mvtest_mvschema; ALTER MATERIALIZED VIEW mvtest_tvm SET SCHEMA mvtest_mvschema; \d+ mvtest_tvm \d+ mvtest_tvmm SET search_path = mvtest_mvschema, public; \d+ mvtest_tvm -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); -- confirm pre- and post-refresh contents of fairly simple materialized views SELECT * FROM mvtest_tm ORDER BY type; SELECT * FROM mvtest_tvm ORDER BY type; REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_tm; REFRESH MATERIALIZED VIEW mvtest_tvm; SELECT * FROM mvtest_tm ORDER BY type; SELECT * FROM mvtest_tvm ORDER BY type; RESET search_path; -- confirm pre- and post-refresh contents of nested materialized views EXPLAIN (costs off) SELECT * FROM mvtest_tmm; EXPLAIN (costs off) SELECT * FROM mvtest_tvmm; EXPLAIN (costs off) SELECT * FROM mvtest_tvvm; SELECT * FROM mvtest_tmm; SELECT * FROM mvtest_tvmm; SELECT * FROM mvtest_tvvm; REFRESH MATERIALIZED VIEW mvtest_tmm; REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_tvmm; REFRESH MATERIALIZED VIEW mvtest_tvmm; REFRESH MATERIALIZED VIEW mvtest_tvvm; EXPLAIN (costs off) SELECT * FROM mvtest_tmm; EXPLAIN (costs off) SELECT * FROM mvtest_tvmm; EXPLAIN (costs off) SELECT * FROM mvtest_tvvm; SELECT * FROM mvtest_tmm; SELECT * FROM mvtest_tvmm; SELECT * FROM mvtest_tvvm; -- test diemv when the mv does not exist DROP MATERIALIZED VIEW IF EXISTS no_such_mv; -- make sure invalid combination of options is prohibited REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_tvmm WITH NO DATA; -- no tuple locks on materialized views SELECT * FROM mvtest_tvvm FOR SHARE; -- test join of mv and view SELECT type, m.totamt AS mtot, v.totamt AS vtot FROM mvtest_tm m LEFT JOIN mvtest_tv v USING (type) ORDER BY type; -- make sure that dependencies are reported properly when they block the drop DROP TABLE mvtest_t; -- make sure dependencies are dropped and reported -- and make sure that transactional behavior is correct on rollback -- incidentally leaving some interesting materialized views for pg_dump testing BEGIN; DROP TABLE mvtest_t CASCADE; ROLLBACK; -- some additional tests not using base tables CREATE VIEW mvtest_vt1 AS SELECT 1 moo; CREATE VIEW mvtest_vt2 AS SELECT moo, 2*moo FROM mvtest_vt1 UNION ALL SELECT moo, 3*moo FROM mvtest_vt1; \d+ mvtest_vt2 CREATE MATERIALIZED VIEW mv_test2 AS SELECT moo, 2*moo FROM mvtest_vt2 UNION ALL SELECT moo, 3*moo FROM mvtest_vt2; \d+ mv_test2 CREATE MATERIALIZED VIEW mv_test3 AS SELECT * FROM mv_test2 WHERE moo = 12345; SELECT relispopulated FROM pg_class WHERE oid = 'mv_test3'::regclass; DROP VIEW mvtest_vt1 CASCADE; -- test that duplicate values on unique index prevent refresh CREATE TABLE mvtest_foo(a, b) AS VALUES(1, 10); CREATE MATERIALIZED VIEW mvtest_mv AS SELECT * FROM mvtest_foo; CREATE UNIQUE INDEX ON mvtest_mv(a); INSERT INTO mvtest_foo SELECT * FROM mvtest_foo; REFRESH MATERIALIZED VIEW mvtest_mv; REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_mv; DROP TABLE mvtest_foo CASCADE; -- make sure that all columns covered by unique indexes works CREATE TABLE mvtest_foo(a, b, c) AS VALUES(1, 2, 3); CREATE MATERIALIZED VIEW mvtest_mv AS SELECT * FROM mvtest_foo; CREATE UNIQUE INDEX ON mvtest_mv (a); CREATE UNIQUE INDEX ON mvtest_mv (b); CREATE UNIQUE INDEX on mvtest_mv (c); INSERT INTO mvtest_foo VALUES(2, 3, 4); INSERT INTO mvtest_foo VALUES(3, 4, 5); REFRESH MATERIALIZED VIEW mvtest_mv; REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_mv; DROP TABLE mvtest_foo CASCADE; -- allow subquery to reference unpopulated matview if WITH NO DATA is specified CREATE MATERIALIZED VIEW mvtest_mv1 AS SELECT 1 AS col1 WITH NO DATA; CREATE MATERIALIZED VIEW mvtest_mv2 AS SELECT * FROM mvtest_mv1 WHERE col1 = (SELECT LEAST(col1) FROM mvtest_mv1) WITH NO DATA; DROP MATERIALIZED VIEW mvtest_mv1 CASCADE; -- make sure that types with unusual equality tests work CREATE TABLE mvtest_boxes (id serial primary key, b box); INSERT INTO mvtest_boxes (b) VALUES ('(32,32),(31,31)'), ('(2.0000004,2.0000004),(1,1)'), ('(1.9999996,1.9999996),(1,1)'); CREATE MATERIALIZED VIEW mvtest_boxmv AS SELECT * FROM mvtest_boxes; CREATE UNIQUE INDEX mvtest_boxmv_id ON mvtest_boxmv (id); UPDATE mvtest_boxes SET b = '(2,2),(1,1)' WHERE id = 2; REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_boxmv; SELECT * FROM mvtest_boxmv ORDER BY id; DROP TABLE mvtest_boxes CASCADE; -- make sure that column names are handled correctly CREATE TABLE mvtest_v (i int, j int); CREATE MATERIALIZED VIEW mvtest_mv_v (ii, jj, kk) AS SELECT i, j FROM mvtest_v; -- error CREATE MATERIALIZED VIEW mvtest_mv_v (ii, jj) AS SELECT i, j FROM mvtest_v; -- ok CREATE MATERIALIZED VIEW mvtest_mv_v_2 (ii) AS SELECT i, j FROM mvtest_v; -- ok CREATE MATERIALIZED VIEW mvtest_mv_v_3 (ii, jj, kk) AS SELECT i, j FROM mvtest_v WITH NO DATA; -- error CREATE MATERIALIZED VIEW mvtest_mv_v_3 (ii, jj) AS SELECT i, j FROM mvtest_v WITH NO DATA; -- ok CREATE MATERIALIZED VIEW mvtest_mv_v_4 (ii) AS SELECT i, j FROM mvtest_v WITH NO DATA; -- ok ALTER TABLE mvtest_v RENAME COLUMN i TO x; INSERT INTO mvtest_v values (1, 2); CREATE UNIQUE INDEX mvtest_mv_v_ii ON mvtest_mv_v (ii); REFRESH MATERIALIZED VIEW mvtest_mv_v; UPDATE mvtest_v SET j = 3 WHERE x = 1; REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_mv_v; REFRESH MATERIALIZED VIEW mvtest_mv_v_2; REFRESH MATERIALIZED VIEW mvtest_mv_v_3; REFRESH MATERIALIZED VIEW mvtest_mv_v_4; SELECT * FROM mvtest_v; SELECT * FROM mvtest_mv_v; SELECT * FROM mvtest_mv_v_2; SELECT * FROM mvtest_mv_v_3; SELECT * FROM mvtest_mv_v_4; DROP TABLE mvtest_v CASCADE; -- Check that unknown literals are converted to "text" in CREATE MATVIEW, -- so that we don't end up with unknown-type columns. CREATE MATERIALIZED VIEW mv_unspecified_types AS SELECT 42 as i, 42.5 as num, 'foo' as u, 'foo'::unknown as u2, null as n; \d+ mv_unspecified_types SELECT * FROM mv_unspecified_types; DROP MATERIALIZED VIEW mv_unspecified_types; -- make sure that create WITH NO DATA does not plan the query (bug #13907) create materialized view mvtest_error as select 1/0 as x; -- fail create materialized view mvtest_error as select 1/0 as x with no data; refresh materialized view mvtest_error; -- fail here drop materialized view mvtest_error; -- make sure that matview rows can be referenced as source rows (bug #9398) CREATE TABLE mvtest_v AS SELECT generate_series(1,10) AS a; CREATE MATERIALIZED VIEW mvtest_mv_v AS SELECT a FROM mvtest_v WHERE a <= 5; DELETE FROM mvtest_v WHERE EXISTS ( SELECT * FROM mvtest_mv_v WHERE mvtest_mv_v.a = mvtest_v.a ); SELECT * FROM mvtest_v; SELECT * FROM mvtest_mv_v; DROP TABLE mvtest_v CASCADE; -- make sure running as superuser works when MV owned by another role (bug #11208) CREATE ROLE regress_user_mvtest; SET ROLE regress_user_mvtest; CREATE TABLE mvtest_foo_data AS SELECT i, md5(random()::text) FROM generate_series(1, 10) i; CREATE MATERIALIZED VIEW mvtest_mv_foo AS SELECT * FROM mvtest_foo_data; CREATE MATERIALIZED VIEW mvtest_mv_foo AS SELECT * FROM mvtest_foo_data; CREATE MATERIALIZED VIEW IF NOT EXISTS mvtest_mv_foo AS SELECT * FROM mvtest_foo_data; CREATE UNIQUE INDEX ON mvtest_mv_foo (i); RESET ROLE; REFRESH MATERIALIZED VIEW mvtest_mv_foo; REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_mv_foo; DROP OWNED BY regress_user_mvtest CASCADE; DROP ROLE regress_user_mvtest; -- make sure that create WITH NO DATA works via SPI BEGIN; CREATE FUNCTION mvtest_func() RETURNS void AS $$ BEGIN CREATE MATERIALIZED VIEW mvtest1 AS SELECT 1 AS x; CREATE MATERIALIZED VIEW mvtest2 AS SELECT 1 AS x WITH NO DATA; END; $$ LANGUAGE plpgsql; SELECT mvtest_func(); SELECT * FROM mvtest1; SELECT * FROM mvtest2; ROLLBACK; pgFormatter-4.2/t/pg-test-files/sql/misc_functions.sql000066400000000000000000000041111361326045100231400ustar00rootroot00000000000000-- -- num_nulls() -- SELECT num_nonnulls(NULL); SELECT num_nonnulls('1'); SELECT num_nonnulls(NULL::text); SELECT num_nonnulls(NULL::text, NULL::int); SELECT num_nonnulls(1, 2, NULL::text, NULL::point, '', int8 '9', 1.0 / NULL); SELECT num_nonnulls(VARIADIC '{1,2,NULL,3}'::int[]); SELECT num_nonnulls(VARIADIC '{"1","2","3","4"}'::text[]); SELECT num_nonnulls(VARIADIC ARRAY(SELECT CASE WHEN i <> 40 THEN i END FROM generate_series(1, 100) i)); SELECT num_nulls(NULL); SELECT num_nulls('1'); SELECT num_nulls(NULL::text); SELECT num_nulls(NULL::text, NULL::int); SELECT num_nulls(1, 2, NULL::text, NULL::point, '', int8 '9', 1.0 / NULL); SELECT num_nulls(VARIADIC '{1,2,NULL,3}'::int[]); SELECT num_nulls(VARIADIC '{"1","2","3","4"}'::text[]); SELECT num_nulls(VARIADIC ARRAY(SELECT CASE WHEN i <> 40 THEN i END FROM generate_series(1, 100) i)); -- special cases SELECT num_nonnulls(VARIADIC NULL::text[]); SELECT num_nonnulls(VARIADIC '{}'::int[]); SELECT num_nulls(VARIADIC NULL::text[]); SELECT num_nulls(VARIADIC '{}'::int[]); -- should fail, one or more arguments is required SELECT num_nonnulls(); SELECT num_nulls(); -- -- Test adding a support function to a subject function -- CREATE FUNCTION my_int_eq(int, int) RETURNS bool LANGUAGE internal STRICT IMMUTABLE PARALLEL SAFE AS $$int4eq$$; -- By default, planner does not think that's selective EXPLAIN (COSTS OFF) SELECT * FROM tenk1 a JOIN tenk1 b ON a.unique1 = b.unique1 WHERE my_int_eq(a.unique2, 42); -- With support function that knows it's int4eq, we get a different plan ALTER FUNCTION my_int_eq(int, int) SUPPORT test_support_func; EXPLAIN (COSTS OFF) SELECT * FROM tenk1 a JOIN tenk1 b ON a.unique1 = b.unique1 WHERE my_int_eq(a.unique2, 42); -- Also test non-default rowcount estimate CREATE FUNCTION my_gen_series(int, int) RETURNS SETOF integer LANGUAGE internal STRICT IMMUTABLE PARALLEL SAFE AS $$generate_series_int4$$ SUPPORT test_support_func; EXPLAIN (COSTS OFF) SELECT * FROM tenk1 a JOIN my_gen_series(1,1000) g ON a.unique1 = g; EXPLAIN (COSTS OFF) SELECT * FROM tenk1 a JOIN my_gen_series(1,10) g ON a.unique1 = g; pgFormatter-4.2/t/pg-test-files/sql/misc_sanity.sql000066400000000000000000000067041361326045100224510ustar00rootroot00000000000000-- -- MISC_SANITY -- Sanity checks for common errors in making system tables that don't fit -- comfortably into either opr_sanity or type_sanity. -- -- Every test failure in this file should be closely inspected. -- The description of the failing test should be read carefully before -- adjusting the expected output. In most cases, the queries should -- not find *any* matching entries. -- -- NB: run this test early, because some later tests create bogus entries. -- **************** pg_depend **************** -- Look for illegal values in pg_depend fields. -- classid/objid can be zero, but only in 'p' entries SELECT * FROM pg_depend as d1 WHERE refclassid = 0 OR refobjid = 0 OR deptype NOT IN ('a', 'e', 'i', 'n', 'p') OR (deptype != 'p' AND (classid = 0 OR objid = 0)) OR (deptype = 'p' AND (classid != 0 OR objid != 0 OR objsubid != 0)); -- **************** pg_shdepend **************** -- Look for illegal values in pg_shdepend fields. -- classid/objid can be zero, but only in 'p' entries SELECT * FROM pg_shdepend as d1 WHERE refclassid = 0 OR refobjid = 0 OR deptype NOT IN ('a', 'o', 'p', 'r') OR (deptype != 'p' AND (classid = 0 OR objid = 0)) OR (deptype = 'p' AND (dbid != 0 OR classid != 0 OR objid != 0 OR objsubid != 0)); -- Check each OID-containing system catalog to see if its lowest-numbered OID -- is pinned. If not, and if that OID was generated during initdb, then -- perhaps initdb forgot to scan that catalog for pinnable entries. -- Generally, it's okay for a catalog to be listed in the output of this -- test if that catalog is scanned by initdb.c's setup_depend() function; -- whatever OID the test is complaining about must have been added later -- in initdb, where it intentionally isn't pinned. Legitimate exceptions -- to that rule are listed in the comments in setup_depend(). do $$ declare relnm text; reloid oid; shared bool; lowoid oid; pinned bool; begin for relnm, reloid, shared in select relname, oid, relisshared from pg_class where EXISTS( SELECT * FROM pg_attribute WHERE attrelid = pg_class.oid AND attname = 'oid') and relkind = 'r' and oid < 16384 order by 1 loop execute 'select min(oid) from ' || relnm into lowoid; continue when lowoid is null or lowoid >= 16384; if shared then pinned := exists(select 1 from pg_shdepend where refclassid = reloid and refobjid = lowoid and deptype = 'p'); else pinned := exists(select 1 from pg_depend where refclassid = reloid and refobjid = lowoid and deptype = 'p'); end if; if not pinned then raise notice '% contains unpinned initdb-created object(s)', relnm; end if; end loop; end$$; -- **************** pg_class **************** -- Look for system tables with varlena columns but no toast table. All -- system tables with toastable columns should have toast tables, with -- the following exceptions: -- 1. pg_class, pg_attribute, and pg_index, due to fear of recursive -- dependencies as toast tables depend on them. -- 2. pg_largeobject and pg_largeobject_metadata. Large object catalogs -- and toast tables are mutually exclusive and large object data is handled -- as user data by pg_upgrade, which would cause failures. SELECT relname, attname, atttypid::regtype FROM pg_class c JOIN pg_attribute a ON c.oid = attrelid WHERE c.oid < 16384 AND reltoastrelid = 0 AND relkind = 'r' AND attstorage != 'p' ORDER BY 1, 2; pgFormatter-4.2/t/pg-test-files/sql/money.sql000066400000000000000000000071161361326045100212540ustar00rootroot00000000000000-- -- MONEY -- CREATE TABLE money_data (m money); INSERT INTO money_data VALUES ('123'); SELECT * FROM money_data; SELECT m + '123' FROM money_data; SELECT m + '123.45' FROM money_data; SELECT m - '123.45' FROM money_data; SELECT m / '2'::money FROM money_data; SELECT m * 2 FROM money_data; SELECT 2 * m FROM money_data; SELECT m / 2 FROM money_data; SELECT m * 2::int2 FROM money_data; SELECT 2::int2 * m FROM money_data; SELECT m / 2::int2 FROM money_data; SELECT m * 2::int8 FROM money_data; SELECT 2::int8 * m FROM money_data; SELECT m / 2::int8 FROM money_data; SELECT m * 2::float8 FROM money_data; SELECT 2::float8 * m FROM money_data; SELECT m / 2::float8 FROM money_data; SELECT m * 2::float4 FROM money_data; SELECT 2::float4 * m FROM money_data; SELECT m / 2::float4 FROM money_data; -- All true SELECT m = '$123.00' FROM money_data; SELECT m != '$124.00' FROM money_data; SELECT m <= '$123.00' FROM money_data; SELECT m >= '$123.00' FROM money_data; SELECT m < '$124.00' FROM money_data; SELECT m > '$122.00' FROM money_data; -- All false SELECT m = '$123.01' FROM money_data; SELECT m != '$123.00' FROM money_data; SELECT m <= '$122.99' FROM money_data; SELECT m >= '$123.01' FROM money_data; SELECT m > '$124.00' FROM money_data; SELECT m < '$122.00' FROM money_data; SELECT cashlarger(m, '$124.00') FROM money_data; SELECT cashsmaller(m, '$124.00') FROM money_data; SELECT cash_words(m) FROM money_data; SELECT cash_words(m + '1.23') FROM money_data; DELETE FROM money_data; INSERT INTO money_data VALUES ('$123.45'); SELECT * FROM money_data; DELETE FROM money_data; INSERT INTO money_data VALUES ('$123.451'); SELECT * FROM money_data; DELETE FROM money_data; INSERT INTO money_data VALUES ('$123.454'); SELECT * FROM money_data; DELETE FROM money_data; INSERT INTO money_data VALUES ('$123.455'); SELECT * FROM money_data; DELETE FROM money_data; INSERT INTO money_data VALUES ('$123.456'); SELECT * FROM money_data; DELETE FROM money_data; INSERT INTO money_data VALUES ('$123.459'); SELECT * FROM money_data; -- input checks SELECT '1234567890'::money; SELECT '12345678901234567'::money; SELECT '123456789012345678'::money; SELECT '9223372036854775807'::money; SELECT '-12345'::money; SELECT '-1234567890'::money; SELECT '-12345678901234567'::money; SELECT '-123456789012345678'::money; SELECT '-9223372036854775808'::money; -- special characters SELECT '(1)'::money; SELECT '($123,456.78)'::money; -- documented minimums and maximums SELECT '-92233720368547758.08'::money; SELECT '92233720368547758.07'::money; SELECT '-92233720368547758.09'::money; SELECT '92233720368547758.08'::money; -- rounding SELECT '-92233720368547758.085'::money; SELECT '92233720368547758.075'::money; -- rounding vs. truncation in division SELECT '878.08'::money / 11::float8; SELECT '878.08'::money / 11::float4; SELECT '878.08'::money / 11::bigint; SELECT '878.08'::money / 11::int; SELECT '878.08'::money / 11::smallint; -- check for precision loss in division SELECT '90000000000000099.00'::money / 10::bigint; SELECT '90000000000000099.00'::money / 10::int; SELECT '90000000000000099.00'::money / 10::smallint; -- Cast int4/int8/numeric to money SELECT 1234567890::money; SELECT 12345678901234567::money; SELECT (-12345)::money; SELECT (-1234567890)::money; SELECT (-12345678901234567)::money; SELECT 1234567890::int4::money; SELECT 12345678901234567::int8::money; SELECT 12345678901234567::numeric::money; SELECT (-1234567890)::int4::money; SELECT (-12345678901234567)::int8::money; SELECT (-12345678901234567)::numeric::money; -- Cast from money SELECT '12345678901234567'::money::numeric; SELECT '-12345678901234567'::money::numeric; pgFormatter-4.2/t/pg-test-files/sql/name.sql000066400000000000000000000067151361326045100210510ustar00rootroot00000000000000-- -- NAME -- all inputs are silently truncated at NAMEDATALEN-1 (63) characters -- -- fixed-length by reference SELECT name 'name string' = name 'name string' AS "True"; SELECT name 'name string' = name 'name string ' AS "False"; -- -- -- CREATE TABLE NAME_TBL(f1 name); INSERT INTO NAME_TBL(f1) VALUES ('1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR'); INSERT INTO NAME_TBL(f1) VALUES ('1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqr'); INSERT INTO NAME_TBL(f1) VALUES ('asdfghjkl;'); INSERT INTO NAME_TBL(f1) VALUES ('343f%2a'); INSERT INTO NAME_TBL(f1) VALUES ('d34aaasdf'); INSERT INTO NAME_TBL(f1) VALUES (''); INSERT INTO NAME_TBL(f1) VALUES ('1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ'); SELECT '' AS seven, * FROM NAME_TBL; SELECT '' AS six, c.f1 FROM NAME_TBL c WHERE c.f1 <> '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR'; SELECT '' AS one, c.f1 FROM NAME_TBL c WHERE c.f1 = '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR'; SELECT '' AS three, c.f1 FROM NAME_TBL c WHERE c.f1 < '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR'; SELECT '' AS four, c.f1 FROM NAME_TBL c WHERE c.f1 <= '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR'; SELECT '' AS three, c.f1 FROM NAME_TBL c WHERE c.f1 > '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR'; SELECT '' AS four, c.f1 FROM NAME_TBL c WHERE c.f1 >= '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR'; SELECT '' AS seven, c.f1 FROM NAME_TBL c WHERE c.f1 ~ '.*'; SELECT '' AS zero, c.f1 FROM NAME_TBL c WHERE c.f1 !~ '.*'; SELECT '' AS three, c.f1 FROM NAME_TBL c WHERE c.f1 ~ '[0-9]'; SELECT '' AS two, c.f1 FROM NAME_TBL c WHERE c.f1 ~ '.*asdf.*'; DROP TABLE NAME_TBL; DO $$ DECLARE r text[]; BEGIN r := parse_ident('Schemax.Tabley'); RAISE NOTICE '%', format('%I.%I', r[1], r[2]); r := parse_ident('"SchemaX"."TableY"'); RAISE NOTICE '%', format('%I.%I', r[1], r[2]); END; $$; SELECT parse_ident('foo.boo'); SELECT parse_ident('foo.boo[]'); -- should fail SELECT parse_ident('foo.boo[]', strict => false); -- ok -- should fail SELECT parse_ident(' '); SELECT parse_ident(' .aaa'); SELECT parse_ident(' aaa . '); SELECT parse_ident('aaa.a%b'); SELECT parse_ident(E'X\rXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'); SELECT length(a[1]), length(a[2]) from parse_ident('"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy') as a ; SELECT parse_ident(' first . " second " ." third ". " ' || repeat('x',66) || '"'); SELECT parse_ident(' first . " second " ." third ". " ' || repeat('x',66) || '"')::name[]; SELECT parse_ident(E'"c".X XXXX\002XXXXXX'); SELECT parse_ident('1020'); SELECT parse_ident('10.20'); SELECT parse_ident('.'); SELECT parse_ident('.1020'); SELECT parse_ident('xxx.1020'); pgFormatter-4.2/t/pg-test-files/sql/namespace.sql000066400000000000000000000026011361326045100220530ustar00rootroot00000000000000-- -- Regression tests for schemas (namespaces) -- CREATE SCHEMA test_ns_schema_1 CREATE UNIQUE INDEX abc_a_idx ON abc (a) CREATE VIEW abc_view AS SELECT a+1 AS a, b+1 AS b FROM abc CREATE TABLE abc ( a serial, b int UNIQUE ); -- verify that the objects were created SELECT COUNT(*) FROM pg_class WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'test_ns_schema_1'); INSERT INTO test_ns_schema_1.abc DEFAULT VALUES; INSERT INTO test_ns_schema_1.abc DEFAULT VALUES; INSERT INTO test_ns_schema_1.abc DEFAULT VALUES; SELECT * FROM test_ns_schema_1.abc; SELECT * FROM test_ns_schema_1.abc_view; ALTER SCHEMA test_ns_schema_1 RENAME TO test_ns_schema_renamed; SELECT COUNT(*) FROM pg_class WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'test_ns_schema_1'); -- test IF NOT EXISTS cases CREATE SCHEMA test_ns_schema_renamed; -- fail, already exists CREATE SCHEMA IF NOT EXISTS test_ns_schema_renamed; -- ok with notice CREATE SCHEMA IF NOT EXISTS test_ns_schema_renamed -- fail, disallowed CREATE TABLE abc ( a serial, b int UNIQUE ); DROP SCHEMA test_ns_schema_renamed CASCADE; -- verify that the objects were dropped SELECT COUNT(*) FROM pg_class WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'test_ns_schema_renamed'); pgFormatter-4.2/t/pg-test-files/sql/numeric.sql000066400000000000000000001313351361326045100215700ustar00rootroot00000000000000-- -- NUMERIC -- CREATE TABLE num_data (id int4, val numeric(210,10)); CREATE TABLE num_exp_add (id1 int4, id2 int4, expected numeric(210,10)); CREATE TABLE num_exp_sub (id1 int4, id2 int4, expected numeric(210,10)); CREATE TABLE num_exp_div (id1 int4, id2 int4, expected numeric(210,10)); CREATE TABLE num_exp_mul (id1 int4, id2 int4, expected numeric(210,10)); CREATE TABLE num_exp_sqrt (id int4, expected numeric(210,10)); CREATE TABLE num_exp_ln (id int4, expected numeric(210,10)); CREATE TABLE num_exp_log10 (id int4, expected numeric(210,10)); CREATE TABLE num_exp_power_10_ln (id int4, expected numeric(210,10)); CREATE TABLE num_result (id1 int4, id2 int4, result numeric(210,10)); -- ****************************** -- * The following EXPECTED results are computed by bc(1) -- * with a scale of 200 -- ****************************** BEGIN TRANSACTION; INSERT INTO num_exp_add VALUES (0,0,'0'); INSERT INTO num_exp_sub VALUES (0,0,'0'); INSERT INTO num_exp_mul VALUES (0,0,'0'); INSERT INTO num_exp_div VALUES (0,0,'NaN'); INSERT INTO num_exp_add VALUES (0,1,'0'); INSERT INTO num_exp_sub VALUES (0,1,'0'); INSERT INTO num_exp_mul VALUES (0,1,'0'); INSERT INTO num_exp_div VALUES (0,1,'NaN'); INSERT INTO num_exp_add VALUES (0,2,'-34338492.215397047'); INSERT INTO num_exp_sub VALUES (0,2,'34338492.215397047'); INSERT INTO num_exp_mul VALUES (0,2,'0'); INSERT INTO num_exp_div VALUES (0,2,'0'); INSERT INTO num_exp_add VALUES (0,3,'4.31'); INSERT INTO num_exp_sub VALUES (0,3,'-4.31'); INSERT INTO num_exp_mul VALUES (0,3,'0'); INSERT INTO num_exp_div VALUES (0,3,'0'); INSERT INTO num_exp_add VALUES (0,4,'7799461.4119'); INSERT INTO num_exp_sub VALUES (0,4,'-7799461.4119'); INSERT INTO num_exp_mul VALUES (0,4,'0'); INSERT INTO num_exp_div VALUES (0,4,'0'); INSERT INTO num_exp_add VALUES (0,5,'16397.038491'); INSERT INTO num_exp_sub VALUES (0,5,'-16397.038491'); INSERT INTO num_exp_mul VALUES (0,5,'0'); INSERT INTO num_exp_div VALUES (0,5,'0'); INSERT INTO num_exp_add VALUES (0,6,'93901.57763026'); INSERT INTO num_exp_sub VALUES (0,6,'-93901.57763026'); INSERT INTO num_exp_mul VALUES (0,6,'0'); INSERT INTO num_exp_div VALUES (0,6,'0'); INSERT INTO num_exp_add VALUES (0,7,'-83028485'); INSERT INTO num_exp_sub VALUES (0,7,'83028485'); INSERT INTO num_exp_mul VALUES (0,7,'0'); INSERT INTO num_exp_div VALUES (0,7,'0'); INSERT INTO num_exp_add VALUES (0,8,'74881'); INSERT INTO num_exp_sub VALUES (0,8,'-74881'); INSERT INTO num_exp_mul VALUES (0,8,'0'); INSERT INTO num_exp_div VALUES (0,8,'0'); INSERT INTO num_exp_add VALUES (0,9,'-24926804.045047420'); INSERT INTO num_exp_sub VALUES (0,9,'24926804.045047420'); INSERT INTO num_exp_mul VALUES (0,9,'0'); INSERT INTO num_exp_div VALUES (0,9,'0'); INSERT INTO num_exp_add VALUES (1,0,'0'); INSERT INTO num_exp_sub VALUES (1,0,'0'); INSERT INTO num_exp_mul VALUES (1,0,'0'); INSERT INTO num_exp_div VALUES (1,0,'NaN'); INSERT INTO num_exp_add VALUES (1,1,'0'); INSERT INTO num_exp_sub VALUES (1,1,'0'); INSERT INTO num_exp_mul VALUES (1,1,'0'); INSERT INTO num_exp_div VALUES (1,1,'NaN'); INSERT INTO num_exp_add VALUES (1,2,'-34338492.215397047'); INSERT INTO num_exp_sub VALUES (1,2,'34338492.215397047'); INSERT INTO num_exp_mul VALUES (1,2,'0'); INSERT INTO num_exp_div VALUES (1,2,'0'); INSERT INTO num_exp_add VALUES (1,3,'4.31'); INSERT INTO num_exp_sub VALUES (1,3,'-4.31'); INSERT INTO num_exp_mul VALUES (1,3,'0'); INSERT INTO num_exp_div VALUES (1,3,'0'); INSERT INTO num_exp_add VALUES (1,4,'7799461.4119'); INSERT INTO num_exp_sub VALUES (1,4,'-7799461.4119'); INSERT INTO num_exp_mul VALUES (1,4,'0'); INSERT INTO num_exp_div VALUES (1,4,'0'); INSERT INTO num_exp_add VALUES (1,5,'16397.038491'); INSERT INTO num_exp_sub VALUES (1,5,'-16397.038491'); INSERT INTO num_exp_mul VALUES (1,5,'0'); INSERT INTO num_exp_div VALUES (1,5,'0'); INSERT INTO num_exp_add VALUES (1,6,'93901.57763026'); INSERT INTO num_exp_sub VALUES (1,6,'-93901.57763026'); INSERT INTO num_exp_mul VALUES (1,6,'0'); INSERT INTO num_exp_div VALUES (1,6,'0'); INSERT INTO num_exp_add VALUES (1,7,'-83028485'); INSERT INTO num_exp_sub VALUES (1,7,'83028485'); INSERT INTO num_exp_mul VALUES (1,7,'0'); INSERT INTO num_exp_div VALUES (1,7,'0'); INSERT INTO num_exp_add VALUES (1,8,'74881'); INSERT INTO num_exp_sub VALUES (1,8,'-74881'); INSERT INTO num_exp_mul VALUES (1,8,'0'); INSERT INTO num_exp_div VALUES (1,8,'0'); INSERT INTO num_exp_add VALUES (1,9,'-24926804.045047420'); INSERT INTO num_exp_sub VALUES (1,9,'24926804.045047420'); INSERT INTO num_exp_mul VALUES (1,9,'0'); INSERT INTO num_exp_div VALUES (1,9,'0'); INSERT INTO num_exp_add VALUES (2,0,'-34338492.215397047'); INSERT INTO num_exp_sub VALUES (2,0,'-34338492.215397047'); INSERT INTO num_exp_mul VALUES (2,0,'0'); INSERT INTO num_exp_div VALUES (2,0,'NaN'); INSERT INTO num_exp_add VALUES (2,1,'-34338492.215397047'); INSERT INTO num_exp_sub VALUES (2,1,'-34338492.215397047'); INSERT INTO num_exp_mul VALUES (2,1,'0'); INSERT INTO num_exp_div VALUES (2,1,'NaN'); INSERT INTO num_exp_add VALUES (2,2,'-68676984.430794094'); INSERT INTO num_exp_sub VALUES (2,2,'0'); INSERT INTO num_exp_mul VALUES (2,2,'1179132047626883.596862135856320209'); INSERT INTO num_exp_div VALUES (2,2,'1.00000000000000000000'); INSERT INTO num_exp_add VALUES (2,3,'-34338487.905397047'); INSERT INTO num_exp_sub VALUES (2,3,'-34338496.525397047'); INSERT INTO num_exp_mul VALUES (2,3,'-147998901.44836127257'); INSERT INTO num_exp_div VALUES (2,3,'-7967167.56737750510440835266'); INSERT INTO num_exp_add VALUES (2,4,'-26539030.803497047'); INSERT INTO num_exp_sub VALUES (2,4,'-42137953.627297047'); INSERT INTO num_exp_mul VALUES (2,4,'-267821744976817.8111137106593'); INSERT INTO num_exp_div VALUES (2,4,'-4.40267480046830116685'); INSERT INTO num_exp_add VALUES (2,5,'-34322095.176906047'); INSERT INTO num_exp_sub VALUES (2,5,'-34354889.253888047'); INSERT INTO num_exp_mul VALUES (2,5,'-563049578578.769242506736077'); INSERT INTO num_exp_div VALUES (2,5,'-2094.18866914563535496429'); INSERT INTO num_exp_add VALUES (2,6,'-34244590.637766787'); INSERT INTO num_exp_sub VALUES (2,6,'-34432393.793027307'); INSERT INTO num_exp_mul VALUES (2,6,'-3224438592470.18449811926184222'); INSERT INTO num_exp_div VALUES (2,6,'-365.68599891479766440940'); INSERT INTO num_exp_add VALUES (2,7,'-117366977.215397047'); INSERT INTO num_exp_sub VALUES (2,7,'48689992.784602953'); INSERT INTO num_exp_mul VALUES (2,7,'2851072985828710.485883795'); INSERT INTO num_exp_div VALUES (2,7,'.41357483778485235518'); INSERT INTO num_exp_add VALUES (2,8,'-34263611.215397047'); INSERT INTO num_exp_sub VALUES (2,8,'-34413373.215397047'); INSERT INTO num_exp_mul VALUES (2,8,'-2571300635581.146276407'); INSERT INTO num_exp_div VALUES (2,8,'-458.57416721727870888476'); INSERT INTO num_exp_add VALUES (2,9,'-59265296.260444467'); INSERT INTO num_exp_sub VALUES (2,9,'-9411688.170349627'); INSERT INTO num_exp_mul VALUES (2,9,'855948866655588.453741509242968740'); INSERT INTO num_exp_div VALUES (2,9,'1.37757299946438931811'); INSERT INTO num_exp_add VALUES (3,0,'4.31'); INSERT INTO num_exp_sub VALUES (3,0,'4.31'); INSERT INTO num_exp_mul VALUES (3,0,'0'); INSERT INTO num_exp_div VALUES (3,0,'NaN'); INSERT INTO num_exp_add VALUES (3,1,'4.31'); INSERT INTO num_exp_sub VALUES (3,1,'4.31'); INSERT INTO num_exp_mul VALUES (3,1,'0'); INSERT INTO num_exp_div VALUES (3,1,'NaN'); INSERT INTO num_exp_add VALUES (3,2,'-34338487.905397047'); INSERT INTO num_exp_sub VALUES (3,2,'34338496.525397047'); INSERT INTO num_exp_mul VALUES (3,2,'-147998901.44836127257'); INSERT INTO num_exp_div VALUES (3,2,'-.00000012551512084352'); INSERT INTO num_exp_add VALUES (3,3,'8.62'); INSERT INTO num_exp_sub VALUES (3,3,'0'); INSERT INTO num_exp_mul VALUES (3,3,'18.5761'); INSERT INTO num_exp_div VALUES (3,3,'1.00000000000000000000'); INSERT INTO num_exp_add VALUES (3,4,'7799465.7219'); INSERT INTO num_exp_sub VALUES (3,4,'-7799457.1019'); INSERT INTO num_exp_mul VALUES (3,4,'33615678.685289'); INSERT INTO num_exp_div VALUES (3,4,'.00000055260225961552'); INSERT INTO num_exp_add VALUES (3,5,'16401.348491'); INSERT INTO num_exp_sub VALUES (3,5,'-16392.728491'); INSERT INTO num_exp_mul VALUES (3,5,'70671.23589621'); INSERT INTO num_exp_div VALUES (3,5,'.00026285234387695504'); INSERT INTO num_exp_add VALUES (3,6,'93905.88763026'); INSERT INTO num_exp_sub VALUES (3,6,'-93897.26763026'); INSERT INTO num_exp_mul VALUES (3,6,'404715.7995864206'); INSERT INTO num_exp_div VALUES (3,6,'.00004589912234457595'); INSERT INTO num_exp_add VALUES (3,7,'-83028480.69'); INSERT INTO num_exp_sub VALUES (3,7,'83028489.31'); INSERT INTO num_exp_mul VALUES (3,7,'-357852770.35'); INSERT INTO num_exp_div VALUES (3,7,'-.00000005190989574240'); INSERT INTO num_exp_add VALUES (3,8,'74885.31'); INSERT INTO num_exp_sub VALUES (3,8,'-74876.69'); INSERT INTO num_exp_mul VALUES (3,8,'322737.11'); INSERT INTO num_exp_div VALUES (3,8,'.00005755799201399553'); INSERT INTO num_exp_add VALUES (3,9,'-24926799.735047420'); INSERT INTO num_exp_sub VALUES (3,9,'24926808.355047420'); INSERT INTO num_exp_mul VALUES (3,9,'-107434525.43415438020'); INSERT INTO num_exp_div VALUES (3,9,'-.00000017290624149854'); INSERT INTO num_exp_add VALUES (4,0,'7799461.4119'); INSERT INTO num_exp_sub VALUES (4,0,'7799461.4119'); INSERT INTO num_exp_mul VALUES (4,0,'0'); INSERT INTO num_exp_div VALUES (4,0,'NaN'); INSERT INTO num_exp_add VALUES (4,1,'7799461.4119'); INSERT INTO num_exp_sub VALUES (4,1,'7799461.4119'); INSERT INTO num_exp_mul VALUES (4,1,'0'); INSERT INTO num_exp_div VALUES (4,1,'NaN'); INSERT INTO num_exp_add VALUES (4,2,'-26539030.803497047'); INSERT INTO num_exp_sub VALUES (4,2,'42137953.627297047'); INSERT INTO num_exp_mul VALUES (4,2,'-267821744976817.8111137106593'); INSERT INTO num_exp_div VALUES (4,2,'-.22713465002993920385'); INSERT INTO num_exp_add VALUES (4,3,'7799465.7219'); INSERT INTO num_exp_sub VALUES (4,3,'7799457.1019'); INSERT INTO num_exp_mul VALUES (4,3,'33615678.685289'); INSERT INTO num_exp_div VALUES (4,3,'1809619.81714617169373549883'); INSERT INTO num_exp_add VALUES (4,4,'15598922.8238'); INSERT INTO num_exp_sub VALUES (4,4,'0'); INSERT INTO num_exp_mul VALUES (4,4,'60831598315717.14146161'); INSERT INTO num_exp_div VALUES (4,4,'1.00000000000000000000'); INSERT INTO num_exp_add VALUES (4,5,'7815858.450391'); INSERT INTO num_exp_sub VALUES (4,5,'7783064.373409'); INSERT INTO num_exp_mul VALUES (4,5,'127888068979.9935054429'); INSERT INTO num_exp_div VALUES (4,5,'475.66281046305802686061'); INSERT INTO num_exp_add VALUES (4,6,'7893362.98953026'); INSERT INTO num_exp_sub VALUES (4,6,'7705559.83426974'); INSERT INTO num_exp_mul VALUES (4,6,'732381731243.745115764094'); INSERT INTO num_exp_div VALUES (4,6,'83.05996138436129499606'); INSERT INTO num_exp_add VALUES (4,7,'-75229023.5881'); INSERT INTO num_exp_sub VALUES (4,7,'90827946.4119'); INSERT INTO num_exp_mul VALUES (4,7,'-647577464846017.9715'); INSERT INTO num_exp_div VALUES (4,7,'-.09393717604145131637'); INSERT INTO num_exp_add VALUES (4,8,'7874342.4119'); INSERT INTO num_exp_sub VALUES (4,8,'7724580.4119'); INSERT INTO num_exp_mul VALUES (4,8,'584031469984.4839'); INSERT INTO num_exp_div VALUES (4,8,'104.15808298366741897143'); INSERT INTO num_exp_add VALUES (4,9,'-17127342.633147420'); INSERT INTO num_exp_sub VALUES (4,9,'32726265.456947420'); INSERT INTO num_exp_mul VALUES (4,9,'-194415646271340.1815956522980'); INSERT INTO num_exp_div VALUES (4,9,'-.31289456112403769409'); INSERT INTO num_exp_add VALUES (5,0,'16397.038491'); INSERT INTO num_exp_sub VALUES (5,0,'16397.038491'); INSERT INTO num_exp_mul VALUES (5,0,'0'); INSERT INTO num_exp_div VALUES (5,0,'NaN'); INSERT INTO num_exp_add VALUES (5,1,'16397.038491'); INSERT INTO num_exp_sub VALUES (5,1,'16397.038491'); INSERT INTO num_exp_mul VALUES (5,1,'0'); INSERT INTO num_exp_div VALUES (5,1,'NaN'); INSERT INTO num_exp_add VALUES (5,2,'-34322095.176906047'); INSERT INTO num_exp_sub VALUES (5,2,'34354889.253888047'); INSERT INTO num_exp_mul VALUES (5,2,'-563049578578.769242506736077'); INSERT INTO num_exp_div VALUES (5,2,'-.00047751189505192446'); INSERT INTO num_exp_add VALUES (5,3,'16401.348491'); INSERT INTO num_exp_sub VALUES (5,3,'16392.728491'); INSERT INTO num_exp_mul VALUES (5,3,'70671.23589621'); INSERT INTO num_exp_div VALUES (5,3,'3804.41728329466357308584'); INSERT INTO num_exp_add VALUES (5,4,'7815858.450391'); INSERT INTO num_exp_sub VALUES (5,4,'-7783064.373409'); INSERT INTO num_exp_mul VALUES (5,4,'127888068979.9935054429'); INSERT INTO num_exp_div VALUES (5,4,'.00210232958726897192'); INSERT INTO num_exp_add VALUES (5,5,'32794.076982'); INSERT INTO num_exp_sub VALUES (5,5,'0'); INSERT INTO num_exp_mul VALUES (5,5,'268862871.275335557081'); INSERT INTO num_exp_div VALUES (5,5,'1.00000000000000000000'); INSERT INTO num_exp_add VALUES (5,6,'110298.61612126'); INSERT INTO num_exp_sub VALUES (5,6,'-77504.53913926'); INSERT INTO num_exp_mul VALUES (5,6,'1539707782.76899778633766'); INSERT INTO num_exp_div VALUES (5,6,'.17461941433576102689'); INSERT INTO num_exp_add VALUES (5,7,'-83012087.961509'); INSERT INTO num_exp_sub VALUES (5,7,'83044882.038491'); INSERT INTO num_exp_mul VALUES (5,7,'-1361421264394.416135'); INSERT INTO num_exp_div VALUES (5,7,'-.00019748690453643710'); INSERT INTO num_exp_add VALUES (5,8,'91278.038491'); INSERT INTO num_exp_sub VALUES (5,8,'-58483.961509'); INSERT INTO num_exp_mul VALUES (5,8,'1227826639.244571'); INSERT INTO num_exp_div VALUES (5,8,'.21897461960978085228'); INSERT INTO num_exp_add VALUES (5,9,'-24910407.006556420'); INSERT INTO num_exp_sub VALUES (5,9,'24943201.083538420'); INSERT INTO num_exp_mul VALUES (5,9,'-408725765384.257043660243220'); INSERT INTO num_exp_div VALUES (5,9,'-.00065780749354660427'); INSERT INTO num_exp_add VALUES (6,0,'93901.57763026'); INSERT INTO num_exp_sub VALUES (6,0,'93901.57763026'); INSERT INTO num_exp_mul VALUES (6,0,'0'); INSERT INTO num_exp_div VALUES (6,0,'NaN'); INSERT INTO num_exp_add VALUES (6,1,'93901.57763026'); INSERT INTO num_exp_sub VALUES (6,1,'93901.57763026'); INSERT INTO num_exp_mul VALUES (6,1,'0'); INSERT INTO num_exp_div VALUES (6,1,'NaN'); INSERT INTO num_exp_add VALUES (6,2,'-34244590.637766787'); INSERT INTO num_exp_sub VALUES (6,2,'34432393.793027307'); INSERT INTO num_exp_mul VALUES (6,2,'-3224438592470.18449811926184222'); INSERT INTO num_exp_div VALUES (6,2,'-.00273458651128995823'); INSERT INTO num_exp_add VALUES (6,3,'93905.88763026'); INSERT INTO num_exp_sub VALUES (6,3,'93897.26763026'); INSERT INTO num_exp_mul VALUES (6,3,'404715.7995864206'); INSERT INTO num_exp_div VALUES (6,3,'21786.90896293735498839907'); INSERT INTO num_exp_add VALUES (6,4,'7893362.98953026'); INSERT INTO num_exp_sub VALUES (6,4,'-7705559.83426974'); INSERT INTO num_exp_mul VALUES (6,4,'732381731243.745115764094'); INSERT INTO num_exp_div VALUES (6,4,'.01203949512295682469'); INSERT INTO num_exp_add VALUES (6,5,'110298.61612126'); INSERT INTO num_exp_sub VALUES (6,5,'77504.53913926'); INSERT INTO num_exp_mul VALUES (6,5,'1539707782.76899778633766'); INSERT INTO num_exp_div VALUES (6,5,'5.72674008674192359679'); INSERT INTO num_exp_add VALUES (6,6,'187803.15526052'); INSERT INTO num_exp_sub VALUES (6,6,'0'); INSERT INTO num_exp_mul VALUES (6,6,'8817506281.4517452372676676'); INSERT INTO num_exp_div VALUES (6,6,'1.00000000000000000000'); INSERT INTO num_exp_add VALUES (6,7,'-82934583.42236974'); INSERT INTO num_exp_sub VALUES (6,7,'83122386.57763026'); INSERT INTO num_exp_mul VALUES (6,7,'-7796505729750.37795610'); INSERT INTO num_exp_div VALUES (6,7,'-.00113095617281538980'); INSERT INTO num_exp_add VALUES (6,8,'168782.57763026'); INSERT INTO num_exp_sub VALUES (6,8,'19020.57763026'); INSERT INTO num_exp_mul VALUES (6,8,'7031444034.53149906'); INSERT INTO num_exp_div VALUES (6,8,'1.25401073209839612184'); INSERT INTO num_exp_add VALUES (6,9,'-24832902.467417160'); INSERT INTO num_exp_sub VALUES (6,9,'25020705.622677680'); INSERT INTO num_exp_mul VALUES (6,9,'-2340666225110.29929521292692920'); INSERT INTO num_exp_div VALUES (6,9,'-.00376709254265256789'); INSERT INTO num_exp_add VALUES (7,0,'-83028485'); INSERT INTO num_exp_sub VALUES (7,0,'-83028485'); INSERT INTO num_exp_mul VALUES (7,0,'0'); INSERT INTO num_exp_div VALUES (7,0,'NaN'); INSERT INTO num_exp_add VALUES (7,1,'-83028485'); INSERT INTO num_exp_sub VALUES (7,1,'-83028485'); INSERT INTO num_exp_mul VALUES (7,1,'0'); INSERT INTO num_exp_div VALUES (7,1,'NaN'); INSERT INTO num_exp_add VALUES (7,2,'-117366977.215397047'); INSERT INTO num_exp_sub VALUES (7,2,'-48689992.784602953'); INSERT INTO num_exp_mul VALUES (7,2,'2851072985828710.485883795'); INSERT INTO num_exp_div VALUES (7,2,'2.41794207151503385700'); INSERT INTO num_exp_add VALUES (7,3,'-83028480.69'); INSERT INTO num_exp_sub VALUES (7,3,'-83028489.31'); INSERT INTO num_exp_mul VALUES (7,3,'-357852770.35'); INSERT INTO num_exp_div VALUES (7,3,'-19264149.65197215777262180974'); INSERT INTO num_exp_add VALUES (7,4,'-75229023.5881'); INSERT INTO num_exp_sub VALUES (7,4,'-90827946.4119'); INSERT INTO num_exp_mul VALUES (7,4,'-647577464846017.9715'); INSERT INTO num_exp_div VALUES (7,4,'-10.64541262725136247686'); INSERT INTO num_exp_add VALUES (7,5,'-83012087.961509'); INSERT INTO num_exp_sub VALUES (7,5,'-83044882.038491'); INSERT INTO num_exp_mul VALUES (7,5,'-1361421264394.416135'); INSERT INTO num_exp_div VALUES (7,5,'-5063.62688881730941836574'); INSERT INTO num_exp_add VALUES (7,6,'-82934583.42236974'); INSERT INTO num_exp_sub VALUES (7,6,'-83122386.57763026'); INSERT INTO num_exp_mul VALUES (7,6,'-7796505729750.37795610'); INSERT INTO num_exp_div VALUES (7,6,'-884.20756174009028770294'); INSERT INTO num_exp_add VALUES (7,7,'-166056970'); INSERT INTO num_exp_sub VALUES (7,7,'0'); INSERT INTO num_exp_mul VALUES (7,7,'6893729321395225'); INSERT INTO num_exp_div VALUES (7,7,'1.00000000000000000000'); INSERT INTO num_exp_add VALUES (7,8,'-82953604'); INSERT INTO num_exp_sub VALUES (7,8,'-83103366'); INSERT INTO num_exp_mul VALUES (7,8,'-6217255985285'); INSERT INTO num_exp_div VALUES (7,8,'-1108.80577182462841041118'); INSERT INTO num_exp_add VALUES (7,9,'-107955289.045047420'); INSERT INTO num_exp_sub VALUES (7,9,'-58101680.954952580'); INSERT INTO num_exp_mul VALUES (7,9,'2069634775752159.035758700'); INSERT INTO num_exp_div VALUES (7,9,'3.33089171198810413382'); INSERT INTO num_exp_add VALUES (8,0,'74881'); INSERT INTO num_exp_sub VALUES (8,0,'74881'); INSERT INTO num_exp_mul VALUES (8,0,'0'); INSERT INTO num_exp_div VALUES (8,0,'NaN'); INSERT INTO num_exp_add VALUES (8,1,'74881'); INSERT INTO num_exp_sub VALUES (8,1,'74881'); INSERT INTO num_exp_mul VALUES (8,1,'0'); INSERT INTO num_exp_div VALUES (8,1,'NaN'); INSERT INTO num_exp_add VALUES (8,2,'-34263611.215397047'); INSERT INTO num_exp_sub VALUES (8,2,'34413373.215397047'); INSERT INTO num_exp_mul VALUES (8,2,'-2571300635581.146276407'); INSERT INTO num_exp_div VALUES (8,2,'-.00218067233500788615'); INSERT INTO num_exp_add VALUES (8,3,'74885.31'); INSERT INTO num_exp_sub VALUES (8,3,'74876.69'); INSERT INTO num_exp_mul VALUES (8,3,'322737.11'); INSERT INTO num_exp_div VALUES (8,3,'17373.78190255220417633410'); INSERT INTO num_exp_add VALUES (8,4,'7874342.4119'); INSERT INTO num_exp_sub VALUES (8,4,'-7724580.4119'); INSERT INTO num_exp_mul VALUES (8,4,'584031469984.4839'); INSERT INTO num_exp_div VALUES (8,4,'.00960079113741758956'); INSERT INTO num_exp_add VALUES (8,5,'91278.038491'); INSERT INTO num_exp_sub VALUES (8,5,'58483.961509'); INSERT INTO num_exp_mul VALUES (8,5,'1227826639.244571'); INSERT INTO num_exp_div VALUES (8,5,'4.56673929509287019456'); INSERT INTO num_exp_add VALUES (8,6,'168782.57763026'); INSERT INTO num_exp_sub VALUES (8,6,'-19020.57763026'); INSERT INTO num_exp_mul VALUES (8,6,'7031444034.53149906'); INSERT INTO num_exp_div VALUES (8,6,'.79744134113322314424'); INSERT INTO num_exp_add VALUES (8,7,'-82953604'); INSERT INTO num_exp_sub VALUES (8,7,'83103366'); INSERT INTO num_exp_mul VALUES (8,7,'-6217255985285'); INSERT INTO num_exp_div VALUES (8,7,'-.00090187120721280172'); INSERT INTO num_exp_add VALUES (8,8,'149762'); INSERT INTO num_exp_sub VALUES (8,8,'0'); INSERT INTO num_exp_mul VALUES (8,8,'5607164161'); INSERT INTO num_exp_div VALUES (8,8,'1.00000000000000000000'); INSERT INTO num_exp_add VALUES (8,9,'-24851923.045047420'); INSERT INTO num_exp_sub VALUES (8,9,'25001685.045047420'); INSERT INTO num_exp_mul VALUES (8,9,'-1866544013697.195857020'); INSERT INTO num_exp_div VALUES (8,9,'-.00300403532938582735'); INSERT INTO num_exp_add VALUES (9,0,'-24926804.045047420'); INSERT INTO num_exp_sub VALUES (9,0,'-24926804.045047420'); INSERT INTO num_exp_mul VALUES (9,0,'0'); INSERT INTO num_exp_div VALUES (9,0,'NaN'); INSERT INTO num_exp_add VALUES (9,1,'-24926804.045047420'); INSERT INTO num_exp_sub VALUES (9,1,'-24926804.045047420'); INSERT INTO num_exp_mul VALUES (9,1,'0'); INSERT INTO num_exp_div VALUES (9,1,'NaN'); INSERT INTO num_exp_add VALUES (9,2,'-59265296.260444467'); INSERT INTO num_exp_sub VALUES (9,2,'9411688.170349627'); INSERT INTO num_exp_mul VALUES (9,2,'855948866655588.453741509242968740'); INSERT INTO num_exp_div VALUES (9,2,'.72591434384152961526'); INSERT INTO num_exp_add VALUES (9,3,'-24926799.735047420'); INSERT INTO num_exp_sub VALUES (9,3,'-24926808.355047420'); INSERT INTO num_exp_mul VALUES (9,3,'-107434525.43415438020'); INSERT INTO num_exp_div VALUES (9,3,'-5783481.21694835730858468677'); INSERT INTO num_exp_add VALUES (9,4,'-17127342.633147420'); INSERT INTO num_exp_sub VALUES (9,4,'-32726265.456947420'); INSERT INTO num_exp_mul VALUES (9,4,'-194415646271340.1815956522980'); INSERT INTO num_exp_div VALUES (9,4,'-3.19596478892958416484'); INSERT INTO num_exp_add VALUES (9,5,'-24910407.006556420'); INSERT INTO num_exp_sub VALUES (9,5,'-24943201.083538420'); INSERT INTO num_exp_mul VALUES (9,5,'-408725765384.257043660243220'); INSERT INTO num_exp_div VALUES (9,5,'-1520.20159364322004505807'); INSERT INTO num_exp_add VALUES (9,6,'-24832902.467417160'); INSERT INTO num_exp_sub VALUES (9,6,'-25020705.622677680'); INSERT INTO num_exp_mul VALUES (9,6,'-2340666225110.29929521292692920'); INSERT INTO num_exp_div VALUES (9,6,'-265.45671195426965751280'); INSERT INTO num_exp_add VALUES (9,7,'-107955289.045047420'); INSERT INTO num_exp_sub VALUES (9,7,'58101680.954952580'); INSERT INTO num_exp_mul VALUES (9,7,'2069634775752159.035758700'); INSERT INTO num_exp_div VALUES (9,7,'.30021990699995814689'); INSERT INTO num_exp_add VALUES (9,8,'-24851923.045047420'); INSERT INTO num_exp_sub VALUES (9,8,'-25001685.045047420'); INSERT INTO num_exp_mul VALUES (9,8,'-1866544013697.195857020'); INSERT INTO num_exp_div VALUES (9,8,'-332.88556569820675471748'); INSERT INTO num_exp_add VALUES (9,9,'-49853608.090094840'); INSERT INTO num_exp_sub VALUES (9,9,'0'); INSERT INTO num_exp_mul VALUES (9,9,'621345559900192.420120630048656400'); INSERT INTO num_exp_div VALUES (9,9,'1.00000000000000000000'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_exp_sqrt VALUES (0,'0'); INSERT INTO num_exp_sqrt VALUES (1,'0'); INSERT INTO num_exp_sqrt VALUES (2,'5859.90547836712524903505'); INSERT INTO num_exp_sqrt VALUES (3,'2.07605394920266944396'); INSERT INTO num_exp_sqrt VALUES (4,'2792.75158435189147418923'); INSERT INTO num_exp_sqrt VALUES (5,'128.05092147657509145473'); INSERT INTO num_exp_sqrt VALUES (6,'306.43364311096782703406'); INSERT INTO num_exp_sqrt VALUES (7,'9111.99676251039939975230'); INSERT INTO num_exp_sqrt VALUES (8,'273.64392922189960397542'); INSERT INTO num_exp_sqrt VALUES (9,'4992.67503899937593364766'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_exp_ln VALUES (0,'NaN'); INSERT INTO num_exp_ln VALUES (1,'NaN'); INSERT INTO num_exp_ln VALUES (2,'17.35177750493897715514'); INSERT INTO num_exp_ln VALUES (3,'1.46093790411565641971'); INSERT INTO num_exp_ln VALUES (4,'15.86956523951936572464'); INSERT INTO num_exp_ln VALUES (5,'9.70485601768871834038'); INSERT INTO num_exp_ln VALUES (6,'11.45000246622944403127'); INSERT INTO num_exp_ln VALUES (7,'18.23469429965478772991'); INSERT INTO num_exp_ln VALUES (8,'11.22365546576315513668'); INSERT INTO num_exp_ln VALUES (9,'17.03145425013166006962'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_exp_log10 VALUES (0,'NaN'); INSERT INTO num_exp_log10 VALUES (1,'NaN'); INSERT INTO num_exp_log10 VALUES (2,'7.53578122160797276459'); INSERT INTO num_exp_log10 VALUES (3,'.63447727016073160075'); INSERT INTO num_exp_log10 VALUES (4,'6.89206461372691743345'); INSERT INTO num_exp_log10 VALUES (5,'4.21476541614777768626'); INSERT INTO num_exp_log10 VALUES (6,'4.97267288886207207671'); INSERT INTO num_exp_log10 VALUES (7,'7.91922711353275546914'); INSERT INTO num_exp_log10 VALUES (8,'4.87437163556421004138'); INSERT INTO num_exp_log10 VALUES (9,'7.39666659961986567059'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_exp_power_10_ln VALUES (0,'NaN'); INSERT INTO num_exp_power_10_ln VALUES (1,'NaN'); INSERT INTO num_exp_power_10_ln VALUES (2,'224790267919917955.13261618583642653184'); INSERT INTO num_exp_power_10_ln VALUES (3,'28.90266599445155957393'); INSERT INTO num_exp_power_10_ln VALUES (4,'7405685069594999.07733999469386277636'); INSERT INTO num_exp_power_10_ln VALUES (5,'5068226527.32127265408584640098'); INSERT INTO num_exp_power_10_ln VALUES (6,'281839893606.99372343357047819067'); INSERT INTO num_exp_power_10_ln VALUES (7,'1716699575118597095.42330819910640247627'); INSERT INTO num_exp_power_10_ln VALUES (8,'167361463828.07491320069016125952'); INSERT INTO num_exp_power_10_ln VALUES (9,'107511333880052007.04141124673540337457'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_data VALUES (0, '0'); INSERT INTO num_data VALUES (1, '0'); INSERT INTO num_data VALUES (2, '-34338492.215397047'); INSERT INTO num_data VALUES (3, '4.31'); INSERT INTO num_data VALUES (4, '7799461.4119'); INSERT INTO num_data VALUES (5, '16397.038491'); INSERT INTO num_data VALUES (6, '93901.57763026'); INSERT INTO num_data VALUES (7, '-83028485'); INSERT INTO num_data VALUES (8, '74881'); INSERT INTO num_data VALUES (9, '-24926804.045047420'); COMMIT TRANSACTION; -- ****************************** -- * Create indices for faster checks -- ****************************** CREATE UNIQUE INDEX num_exp_add_idx ON num_exp_add (id1, id2); CREATE UNIQUE INDEX num_exp_sub_idx ON num_exp_sub (id1, id2); CREATE UNIQUE INDEX num_exp_div_idx ON num_exp_div (id1, id2); CREATE UNIQUE INDEX num_exp_mul_idx ON num_exp_mul (id1, id2); CREATE UNIQUE INDEX num_exp_sqrt_idx ON num_exp_sqrt (id); CREATE UNIQUE INDEX num_exp_ln_idx ON num_exp_ln (id); CREATE UNIQUE INDEX num_exp_log10_idx ON num_exp_log10 (id); CREATE UNIQUE INDEX num_exp_power_10_ln_idx ON num_exp_power_10_ln (id); VACUUM ANALYZE num_exp_add; VACUUM ANALYZE num_exp_sub; VACUUM ANALYZE num_exp_div; VACUUM ANALYZE num_exp_mul; VACUUM ANALYZE num_exp_sqrt; VACUUM ANALYZE num_exp_ln; VACUUM ANALYZE num_exp_log10; VACUUM ANALYZE num_exp_power_10_ln; -- ****************************** -- * Now check the behaviour of the NUMERIC type -- ****************************** -- ****************************** -- * Addition check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, t1.val + t2.val FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, t2.expected FROM num_result t1, num_exp_add t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != t2.expected; DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, round(t1.val + t2.val, 10) FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, round(t2.expected, 10) as expected FROM num_result t1, num_exp_add t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != round(t2.expected, 10); -- ****************************** -- * Subtraction check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, t1.val - t2.val FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, t2.expected FROM num_result t1, num_exp_sub t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != t2.expected; DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, round(t1.val - t2.val, 40) FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, round(t2.expected, 40) FROM num_result t1, num_exp_sub t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != round(t2.expected, 40); -- ****************************** -- * Multiply check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, t1.val * t2.val FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, t2.expected FROM num_result t1, num_exp_mul t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != t2.expected; DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, round(t1.val * t2.val, 30) FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, round(t2.expected, 30) as expected FROM num_result t1, num_exp_mul t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != round(t2.expected, 30); -- ****************************** -- * Division check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, t1.val / t2.val FROM num_data t1, num_data t2 WHERE t2.val != '0.0'; SELECT t1.id1, t1.id2, t1.result, t2.expected FROM num_result t1, num_exp_div t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != t2.expected; DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, round(t1.val / t2.val, 80) FROM num_data t1, num_data t2 WHERE t2.val != '0.0'; SELECT t1.id1, t1.id2, t1.result, round(t2.expected, 80) as expected FROM num_result t1, num_exp_div t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != round(t2.expected, 80); -- ****************************** -- * Square root check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT id, 0, SQRT(ABS(val)) FROM num_data; SELECT t1.id1, t1.result, t2.expected FROM num_result t1, num_exp_sqrt t2 WHERE t1.id1 = t2.id AND t1.result != t2.expected; -- ****************************** -- * Natural logarithm check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT id, 0, LN(ABS(val)) FROM num_data WHERE val != '0.0'; SELECT t1.id1, t1.result, t2.expected FROM num_result t1, num_exp_ln t2 WHERE t1.id1 = t2.id AND t1.result != t2.expected; -- ****************************** -- * Logarithm base 10 check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT id, 0, LOG(numeric '10', ABS(val)) FROM num_data WHERE val != '0.0'; SELECT t1.id1, t1.result, t2.expected FROM num_result t1, num_exp_log10 t2 WHERE t1.id1 = t2.id AND t1.result != t2.expected; -- ****************************** -- * POWER(10, LN(value)) check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT id, 0, POWER(numeric '10', LN(ABS(round(val,200)))) FROM num_data WHERE val != '0.0'; SELECT t1.id1, t1.result, t2.expected FROM num_result t1, num_exp_power_10_ln t2 WHERE t1.id1 = t2.id AND t1.result != t2.expected; -- ****************************** -- * miscellaneous checks for things that have been broken in the past... -- ****************************** -- numeric AVG used to fail on some platforms SELECT AVG(val) FROM num_data; SELECT STDDEV(val) FROM num_data; SELECT VARIANCE(val) FROM num_data; -- Check for appropriate rounding and overflow CREATE TABLE fract_only (id int, val numeric(4,4)); INSERT INTO fract_only VALUES (1, '0.0'); INSERT INTO fract_only VALUES (2, '0.1'); INSERT INTO fract_only VALUES (3, '1.0'); -- should fail INSERT INTO fract_only VALUES (4, '-0.9999'); INSERT INTO fract_only VALUES (5, '0.99994'); INSERT INTO fract_only VALUES (6, '0.99995'); -- should fail INSERT INTO fract_only VALUES (7, '0.00001'); INSERT INTO fract_only VALUES (8, '0.00017'); SELECT * FROM fract_only; DROP TABLE fract_only; -- Check inf/nan conversion behavior SELECT 'NaN'::float8::numeric; SELECT 'Infinity'::float8::numeric; SELECT '-Infinity'::float8::numeric; SELECT 'NaN'::float4::numeric; SELECT 'Infinity'::float4::numeric; SELECT '-Infinity'::float4::numeric; -- Simple check that ceil(), floor(), and round() work correctly CREATE TABLE ceil_floor_round (a numeric); INSERT INTO ceil_floor_round VALUES ('-5.5'); INSERT INTO ceil_floor_round VALUES ('-5.499999'); INSERT INTO ceil_floor_round VALUES ('9.5'); INSERT INTO ceil_floor_round VALUES ('9.4999999'); INSERT INTO ceil_floor_round VALUES ('0.0'); INSERT INTO ceil_floor_round VALUES ('0.0000001'); INSERT INTO ceil_floor_round VALUES ('-0.000001'); SELECT a, ceil(a), ceiling(a), floor(a), round(a) FROM ceil_floor_round; DROP TABLE ceil_floor_round; -- Check rounding, it should round ties away from zero. SELECT i as pow, round((-2.5 * 10 ^ i)::numeric, -i), round((-1.5 * 10 ^ i)::numeric, -i), round((-0.5 * 10 ^ i)::numeric, -i), round((0.5 * 10 ^ i)::numeric, -i), round((1.5 * 10 ^ i)::numeric, -i), round((2.5 * 10 ^ i)::numeric, -i) FROM generate_series(-5,5) AS t(i); -- Testing for width_bucket(). For convenience, we test both the -- numeric and float8 versions of the function in this file. -- errors SELECT width_bucket(5.0, 3.0, 4.0, 0); SELECT width_bucket(5.0, 3.0, 4.0, -5); SELECT width_bucket(3.5, 3.0, 3.0, 888); SELECT width_bucket(5.0::float8, 3.0::float8, 4.0::float8, 0); SELECT width_bucket(5.0::float8, 3.0::float8, 4.0::float8, -5); SELECT width_bucket(3.5::float8, 3.0::float8, 3.0::float8, 888); SELECT width_bucket('NaN', 3.0, 4.0, 888); SELECT width_bucket(0::float8, 'NaN', 4.0::float8, 888); -- normal operation CREATE TABLE width_bucket_test (operand_num numeric, operand_f8 float8); UPDATE width_bucket_test SET operand_f8 = operand_num::float8; SELECT operand_num, width_bucket(operand_num, 0, 10, 5) AS wb_1, width_bucket(operand_f8, 0, 10, 5) AS wb_1f, width_bucket(operand_num, 10, 0, 5) AS wb_2, width_bucket(operand_f8, 10, 0, 5) AS wb_2f, width_bucket(operand_num, 2, 8, 4) AS wb_3, width_bucket(operand_f8, 2, 8, 4) AS wb_3f, width_bucket(operand_num, 5.0, 5.5, 20) AS wb_4, width_bucket(operand_f8, 5.0, 5.5, 20) AS wb_4f, width_bucket(operand_num, -25, 25, 10) AS wb_5, width_bucket(operand_f8, -25, 25, 10) AS wb_5f FROM width_bucket_test; -- for float8 only, check positive and negative infinity: we require -- finite bucket bounds, but allow an infinite operand SELECT width_bucket(0.0::float8, 'Infinity'::float8, 5, 10); -- error SELECT width_bucket(0.0::float8, 5, '-Infinity'::float8, 20); -- error SELECT width_bucket('Infinity'::float8, 1, 10, 10), width_bucket('-Infinity'::float8, 1, 10, 10); DROP TABLE width_bucket_test; -- TO_CHAR() -- SELECT '' AS to_char_1, to_char(val, '9G999G999G999G999G999') FROM num_data; SELECT '' AS to_char_2, to_char(val, '9G999G999G999G999G999D999G999G999G999G999') FROM num_data; SELECT '' AS to_char_3, to_char(val, '9999999999999999.999999999999999PR') FROM num_data; SELECT '' AS to_char_4, to_char(val, '9999999999999999.999999999999999S') FROM num_data; SELECT '' AS to_char_5, to_char(val, 'MI9999999999999999.999999999999999') FROM num_data; SELECT '' AS to_char_6, to_char(val, 'FMS9999999999999999.999999999999999') FROM num_data; SELECT '' AS to_char_7, to_char(val, 'FM9999999999999999.999999999999999THPR') FROM num_data; SELECT '' AS to_char_8, to_char(val, 'SG9999999999999999.999999999999999th') FROM num_data; SELECT '' AS to_char_9, to_char(val, '0999999999999999.999999999999999') FROM num_data; SELECT '' AS to_char_10, to_char(val, 'S0999999999999999.999999999999999') FROM num_data; SELECT '' AS to_char_11, to_char(val, 'FM0999999999999999.999999999999999') FROM num_data; SELECT '' AS to_char_12, to_char(val, 'FM9999999999999999.099999999999999') FROM num_data; SELECT '' AS to_char_13, to_char(val, 'FM9999999999990999.990999999999999') FROM num_data; SELECT '' AS to_char_14, to_char(val, 'FM0999999999999999.999909999999999') FROM num_data; SELECT '' AS to_char_15, to_char(val, 'FM9999999990999999.099999999999999') FROM num_data; SELECT '' AS to_char_16, to_char(val, 'L9999999999999999.099999999999999') FROM num_data; SELECT '' AS to_char_17, to_char(val, 'FM9999999999999999.99999999999999') FROM num_data; SELECT '' AS to_char_18, to_char(val, 'S 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 . 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9') FROM num_data; SELECT '' AS to_char_19, to_char(val, 'FMS 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 . 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9') FROM num_data; SELECT '' AS to_char_20, to_char(val, E'99999 "text" 9999 "9999" 999 "\\"text between quote marks\\"" 9999') FROM num_data; SELECT '' AS to_char_21, to_char(val, '999999SG9999999999') FROM num_data; SELECT '' AS to_char_22, to_char(val, 'FM9999999999999999.999999999999999') FROM num_data; SELECT '' AS to_char_23, to_char(val, '9.999EEEE') FROM num_data; SELECT '' AS to_char_24, to_char('100'::numeric, 'FM999.9'); SELECT '' AS to_char_25, to_char('100'::numeric, 'FM999.'); SELECT '' AS to_char_26, to_char('100'::numeric, 'FM999'); -- Check parsing of literal text in a format string SELECT '' AS to_char_27, to_char('100'::numeric, 'foo999'); SELECT '' AS to_char_28, to_char('100'::numeric, 'f\oo999'); SELECT '' AS to_char_29, to_char('100'::numeric, 'f\\oo999'); SELECT '' AS to_char_30, to_char('100'::numeric, 'f\"oo999'); SELECT '' AS to_char_31, to_char('100'::numeric, 'f\\"oo999'); SELECT '' AS to_char_32, to_char('100'::numeric, 'f"ool"999'); SELECT '' AS to_char_33, to_char('100'::numeric, 'f"\ool"999'); SELECT '' AS to_char_34, to_char('100'::numeric, 'f"\\ool"999'); SELECT '' AS to_char_35, to_char('100'::numeric, 'f"ool\"999'); SELECT '' AS to_char_36, to_char('100'::numeric, 'f"ool\\"999'); -- TO_NUMBER() -- SET lc_numeric = 'C'; SELECT '' AS to_number_1, to_number('-34,338,492', '99G999G999'); SELECT '' AS to_number_2, to_number('-34,338,492.654,878', '99G999G999D999G999'); SELECT '' AS to_number_3, to_number('<564646.654564>', '999999.999999PR'); SELECT '' AS to_number_4, to_number('0.00001-', '9.999999S'); SELECT '' AS to_number_5, to_number('5.01-', 'FM9.999999S'); SELECT '' AS to_number_5, to_number('5.01-', 'FM9.999999MI'); SELECT '' AS to_number_7, to_number('5 4 4 4 4 8 . 7 8', '9 9 9 9 9 9 . 9 9'); SELECT '' AS to_number_8, to_number('.01', 'FM9.99'); SELECT '' AS to_number_9, to_number('.0', '99999999.99999999'); SELECT '' AS to_number_10, to_number('0', '99.99'); SELECT '' AS to_number_11, to_number('.-01', 'S99.99'); SELECT '' AS to_number_12, to_number('.01-', '99.99S'); SELECT '' AS to_number_13, to_number(' . 0 1-', ' 9 9 . 9 9 S'); SELECT '' AS to_number_14, to_number('34,50','999,99'); SELECT '' AS to_number_15, to_number('123,000','999G'); SELECT '' AS to_number_16, to_number('123456','999G999'); SELECT '' AS to_number_17, to_number('$1234.56','L9,999.99'); SELECT '' AS to_number_18, to_number('$1234.56','L99,999.99'); SELECT '' AS to_number_19, to_number('$1,234.56','L99,999.99'); SELECT '' AS to_number_20, to_number('1234.56','L99,999.99'); SELECT '' AS to_number_21, to_number('1,234.56','L99,999.99'); SELECT '' AS to_number_22, to_number('42nd', '99th'); RESET lc_numeric; -- -- Input syntax -- CREATE TABLE num_input_test (n1 numeric); -- good inputs INSERT INTO num_input_test(n1) VALUES (' 123'); INSERT INTO num_input_test(n1) VALUES (' 3245874 '); INSERT INTO num_input_test(n1) VALUES (' -93853'); INSERT INTO num_input_test(n1) VALUES ('555.50'); INSERT INTO num_input_test(n1) VALUES ('-555.50'); INSERT INTO num_input_test(n1) VALUES ('NaN '); INSERT INTO num_input_test(n1) VALUES (' nan'); -- bad inputs INSERT INTO num_input_test(n1) VALUES (' '); INSERT INTO num_input_test(n1) VALUES (' 1234 %'); INSERT INTO num_input_test(n1) VALUES ('xyz'); INSERT INTO num_input_test(n1) VALUES ('- 1234'); INSERT INTO num_input_test(n1) VALUES ('5 . 0'); INSERT INTO num_input_test(n1) VALUES ('5. 0 '); INSERT INTO num_input_test(n1) VALUES (''); INSERT INTO num_input_test(n1) VALUES (' N aN '); SELECT * FROM num_input_test; -- -- Test some corner cases for multiplication -- select 4790999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999; select 4789999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999; select 4770999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999; select 4769999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999; -- -- Test some corner cases for division -- select 999999999999999999999::numeric/1000000000000000000000; select div(999999999999999999999::numeric,1000000000000000000000); select mod(999999999999999999999::numeric,1000000000000000000000); select div(-9999999999999999999999::numeric,1000000000000000000000); select mod(-9999999999999999999999::numeric,1000000000000000000000); select div(-9999999999999999999999::numeric,1000000000000000000000)*1000000000000000000000 + mod(-9999999999999999999999::numeric,1000000000000000000000); select mod (70.0,70) ; select div (70.0,70) ; select 70.0 / 70 ; select 12345678901234567890 % 123; select 12345678901234567890 / 123; select div(12345678901234567890, 123); select div(12345678901234567890, 123) * 123 + 12345678901234567890 % 123; -- -- Test code path for raising to integer powers -- select 10.0 ^ -2147483648 as rounds_to_zero; select 10.0 ^ -2147483647 as rounds_to_zero; select 10.0 ^ 2147483647 as overflows; select 117743296169.0 ^ 1000000000 as overflows; -- cases that used to return inaccurate results select 3.789 ^ 21; select 3.789 ^ 35; select 1.2 ^ 345; select 0.12 ^ (-20); -- cases that used to error out select 0.12 ^ (-25); select 0.5678 ^ (-85); -- -- Tests for raising to non-integer powers -- -- special cases select 0.0 ^ 0.0; select (-12.34) ^ 0.0; select 12.34 ^ 0.0; select 0.0 ^ 12.34; -- NaNs select 'NaN'::numeric ^ 'NaN'::numeric; select 'NaN'::numeric ^ 0; select 'NaN'::numeric ^ 1; select 0 ^ 'NaN'::numeric; select 1 ^ 'NaN'::numeric; -- invalid inputs select 0.0 ^ (-12.34); select (-12.34) ^ 1.2; -- cases that used to generate inaccurate results select 32.1 ^ 9.8; select 32.1 ^ (-9.8); select 12.3 ^ 45.6; select 12.3 ^ (-45.6); -- big test select 1.234 ^ 5678; -- -- Tests for EXP() -- -- special cases select exp(0.0); select exp(1.0); select exp(1.0::numeric(71,70)); -- cases that used to generate inaccurate results select exp(32.999); select exp(-32.999); select exp(123.456); select exp(-123.456); -- big test select exp(1234.5678); -- -- Tests for generate_series -- select * from generate_series(0.0::numeric, 4.0::numeric); select * from generate_series(0.1::numeric, 4.0::numeric, 1.3::numeric); select * from generate_series(4.0::numeric, -1.5::numeric, -2.2::numeric); -- Trigger errors select * from generate_series(-100::numeric, 100::numeric, 0::numeric); select * from generate_series(-100::numeric, 100::numeric, 'nan'::numeric); select * from generate_series('nan'::numeric, 100::numeric, 10::numeric); select * from generate_series(0::numeric, 'nan'::numeric, 10::numeric); -- Checks maximum, output is truncated select (i / (10::numeric ^ 131071))::numeric(1,0) from generate_series(6 * (10::numeric ^ 131071), 9 * (10::numeric ^ 131071), 10::numeric ^ 131071) as a(i); -- Check usage with variables select * from generate_series(1::numeric, 3::numeric) i, generate_series(i,3) j; select * from generate_series(1::numeric, 3::numeric) i, generate_series(1,i) j; select * from generate_series(1::numeric, 3::numeric) i, generate_series(1,5,i) j; -- -- Tests for LN() -- -- Invalid inputs select ln(-12.34); select ln(0.0); -- Some random tests select ln(1.2345678e-28); select ln(0.0456789); select ln(0.349873948359354029493948309745709580730482050975); select ln(0.99949452); select ln(1.00049687395); select ln(1234.567890123456789); select ln(5.80397490724e5); select ln(9.342536355e34); -- -- Tests for LOG() (base 10) -- -- invalid inputs select log(-12.34); select log(0.0); -- some random tests select log(1.234567e-89); select log(3.4634998359873254962349856073435545); select log(9.999999999999999999); select log(10.00000000000000000); select log(10.00000000000000001); select log(590489.45235237); -- -- Tests for LOG() (arbitrary base) -- -- invalid inputs select log(-12.34, 56.78); select log(-12.34, -56.78); select log(12.34, -56.78); select log(0.0, 12.34); select log(12.34, 0.0); select log(1.0, 12.34); -- some random tests select log(1.23e-89, 6.4689e45); select log(0.99923, 4.58934e34); select log(1.000016, 8.452010e18); select log(3.1954752e47, 9.4792021e-73); -- -- Tests for scale() -- select scale(numeric 'NaN'); select scale(NULL::numeric); select scale(1.12); select scale(0); select scale(0.00); select scale(1.12345); select scale(110123.12475871856128); select scale(-1123.12471856128); select scale(-13.000000000000000); -- -- Tests for SUM() -- -- cases that need carry propagation SELECT SUM(9999::numeric) FROM generate_series(1, 100000); SELECT SUM((-9999)::numeric) FROM generate_series(1, 100000); pgFormatter-4.2/t/pg-test-files/sql/numeric_big.sql000066400000000000000000011027041361326045100224100ustar00rootroot00000000000000-- ****************************** -- * Test suite for the Postgres NUMERIC data type -- ****************************** -- Must drop tables created by short numeric test. DROP TABLE num_data; DROP TABLE num_exp_add; DROP TABLE num_exp_sub; DROP TABLE num_exp_div; DROP TABLE num_exp_mul; DROP TABLE num_exp_sqrt; DROP TABLE num_exp_ln; DROP TABLE num_exp_log10; DROP TABLE num_exp_power_10_ln; DROP TABLE num_result; CREATE TABLE num_data (id int4, val numeric(1000,800)); CREATE TABLE num_exp_add (id1 int4, id2 int4, expected numeric(1000,800)); CREATE TABLE num_exp_sub (id1 int4, id2 int4, expected numeric(1000,800)); CREATE TABLE num_exp_div (id1 int4, id2 int4, expected numeric(1000,800)); CREATE TABLE num_exp_mul (id1 int4, id2 int4, expected numeric(1000,800)); CREATE TABLE num_exp_sqrt (id int4, expected numeric(1000,800)); CREATE TABLE num_exp_ln (id int4, expected numeric(1000,800)); CREATE TABLE num_exp_log10 (id int4, expected numeric(1000,800)); CREATE TABLE num_exp_power_10_ln (id int4, expected numeric(1000,800)); CREATE TABLE num_result (id1 int4, id2 int4, result numeric(1000,800)); -- ****************************** -- * The following EXPECTED results are computed by bc(1) -- * with a scale of 1000 -- ****************************** BEGIN TRANSACTION; INSERT INTO num_exp_add VALUES (0,0,'0'); INSERT INTO num_exp_sub VALUES (0,0,'0'); INSERT INTO num_exp_mul VALUES (0,0,'0'); INSERT INTO num_exp_div VALUES (0,0,'NaN'); INSERT INTO num_exp_add VALUES (0,1,'85243.39540024977626076239847863600785982737155858270959890014613035727868293618673807776733416230953723818527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_sub VALUES (0,1,'-85243.39540024977626076239847863600785982737155858270959890014613035727868293618673807776733416230953723818527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (0,1,'0'); INSERT INTO num_exp_div VALUES (0,1,'0'); INSERT INTO num_exp_add VALUES (0,2,'-994877526002806872754342148749241.04353023451866590387054959174736129501310680280823383331007646306243540953499740615246583399296334239109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_sub VALUES (0,2,'994877526002806872754342148749241.04353023451866590387054959174736129501310680280823383331007646306243540953499740615246583399296334239109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_mul VALUES (0,2,'0'); INSERT INTO num_exp_div VALUES (0,2,'0'); INSERT INTO num_exp_add VALUES (0,3,'-60302029489319384367663884408085757480.2322712404088283093870869198708849258097125725036189625900174440196153781995220721511009855207486224837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (0,3,'60302029489319384367663884408085757480.2322712404088283093870869198708849258097125725036189625900174440196153781995220721511009855207486224837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (0,3,'0'); INSERT INTO num_exp_div VALUES (0,3,'0'); INSERT INTO num_exp_add VALUES (0,4,'5329378275943663322215245.24931765987630429629836382184742348353920297283690739124220773955591340709935970062776650204659187764581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (0,4,'-5329378275943663322215245.24931765987630429629836382184742348353920297283690739124220773955591340709935970062776650204659187764581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (0,4,'0'); INSERT INTO num_exp_div VALUES (0,4,'0'); INSERT INTO num_exp_add VALUES (0,5,'-652755630.43456071828048833552593835051449845484289562110789582081210403487973096161149072377955192388469356112505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (0,5,'652755630.43456071828048833552593835051449845484289562110789582081210403487973096161149072377955192388469356112505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (0,5,'0'); INSERT INTO num_exp_div VALUES (0,5,'0'); INSERT INTO num_exp_add VALUES (0,6,'.0469370721950711508944806393077762204079964905145503836835397203739563036579760026190241480514409364'); INSERT INTO num_exp_sub VALUES (0,6,'-.0469370721950711508944806393077762204079964905145503836835397203739563036579760026190241480514409364'); INSERT INTO num_exp_mul VALUES (0,6,'0'); INSERT INTO num_exp_div VALUES (0,6,'0'); INSERT INTO num_exp_add VALUES (0,7,'-818934540071845742'); INSERT INTO num_exp_sub VALUES (0,7,'818934540071845742'); INSERT INTO num_exp_mul VALUES (0,7,'0'); INSERT INTO num_exp_div VALUES (0,7,'0'); INSERT INTO num_exp_add VALUES (0,8,'8496986223.64063724353165506167988570717591150432282238795309964705925283285060558038824227595710139960766584401003765241409149793494330798800'); INSERT INTO num_exp_sub VALUES (0,8,'-8496986223.64063724353165506167988570717591150432282238795309964705925283285060558038824227595710139960766584401003765241409149793494330798800'); INSERT INTO num_exp_mul VALUES (0,8,'0'); INSERT INTO num_exp_div VALUES (0,8,'0'); INSERT INTO num_exp_add VALUES (0,9,'54863480.34685027005508022756223282084742813020271603840941647350440860843570182437301045468670059279379903480024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_sub VALUES (0,9,'-54863480.34685027005508022756223282084742813020271603840941647350440860843570182437301045468670059279379903480024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_mul VALUES (0,9,'0'); INSERT INTO num_exp_div VALUES (0,9,'0'); INSERT INTO num_exp_add VALUES (1,0,'85243.39540024977626076239847863600785982737155858270959890014613035727868293618673807776733416230953723818527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_sub VALUES (1,0,'85243.39540024977626076239847863600785982737155858270959890014613035727868293618673807776733416230953723818527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (1,0,'0'); INSERT INTO num_exp_div VALUES (1,0,'NaN'); INSERT INTO num_exp_add VALUES (1,1,'170486.79080049955252152479695727201571965474311716541919780029226071455736587237347615553466832461907447637054203186991790701615551214692555785671028648640897898741246882118067609728317430043806625387779037980513762118868084887015059202190301421555269486602797852927777567694581746398790609996101506730430853942556475840126871131898407356048450541232591147357021858041662012293323494543567675306406079659294204054863522259037763051870433216859794083051717080761509518250300466106939998045710070'); INSERT INTO num_exp_sub VALUES (1,1,'0'); INSERT INTO num_exp_mul VALUES (1,1,'7266436459.363324713115467666113895787027372854351303425444968800459979742082292257107107767894843498525848597439323325297125474674300428669958003640228730876886174255457103020291514229439701871032118057857763809224712818579091741996335014138185389554630910658876423205103697147288306070059640369158894028731728589073730895396494400175420670713113234800826523252075036892246807434088405522834549449664122407363485486902219500109237667016524913027290777216477989904700729228025571098410870506256758678625928245828210775042611512394316804583459576285681159178280400209217948833631961377519855502763611693070238579591463373484424582723121059964236704135695706864890193388054537703767833595331866551990460050750959493829603581882430597105627056085260296454181999581594565113210481151487049158699087454047624433576922179904629'); INSERT INTO num_exp_div VALUES (1,1,'1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); INSERT INTO num_exp_add VALUES (1,2,'-994877526002806872754342148663997.64812998474240514147207095573950146764154822009863493316394610578375247334825932838513167168342610420582834742950389452212867974756590355021495169819086060202117180229196935525386766373096687306110481009743118940565957556492470398904849289222365256698601073536111216152709126800604695001949246634784573028721762079936564434050796321975774729383704426321489070979168993853338252728216162346796960170352897972568238870481118474064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_sub VALUES (1,2,'994877526002806872754342148834484.43893048429492666626902822775522112238466538551783273345620682034111834572173548391979999630250058057637037929942180153828419189449146140692523818459983958943364062347264545253704196416903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (1,2,'-84806738323879544552397401815149740513.8505875535743013876823142649666132764556588225959336097903898464616542203793600590311980154402068027051522932586050753865288419084437796768749509032177577451738712965496693249429231838833655025794915864261585848007162358912070811805298210095333433397862313304655108809804359760907473898420016370058274978588765092161529583480924554820756527238472641797198545539410039895140087686344382628317530286295498797849942258314364503000942821309916954725689781458590617068629906894951122301020797266469357701283289275708774593896770378558232444454118891917258610753077932026885574920166837998049508644891327208474213193224700658584824407382455480657734911543930195324144216374573825'); INSERT INTO num_exp_div VALUES (1,2,'-.000000000000000000000000000085682300757901809257711279577127388124986344391495296640171942990079130291883279872719240502687189411421655284515420074848478500192127657883342858267913417679786356766341637336955924836847768457039175660279784295612167899455618405343686908907695358239088351870495830739180518509859269437015797489301844593920484927630172344269378248455657186218762679357609204333669024237648538465053048724383898528808961206696787294681884412485427843796696788390072124570957047672341581447744981862017791206857428430183366004980966398716823512288330174863890117558744630102020144500158878244146399686532935435591262767487823942606452349972401012308378888947381934278131785907155692007064636085000405504866631011593239041758448995933095907216863744502344014999804306234830774259496097549717476344048'); INSERT INTO num_exp_add VALUES (1,3,'-60302029489319384367663884408085672236.83687099063256754698860828386302509843815398979402006244388708674093244201278399438376682321121138429850885935540924586964982855913223221441591310211730902799041126800414795030815514254713522692405212716783388698431088814919226444677188004928663343696636297536500970117716818423689175692808344185016908913828066250587407384563498516598672584120143890364303296142744031320345312431817858545326010704685255237541162931904446804064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_sub VALUES (1,3,'60302029489319384367663884408085842723.62767149018508907178556555587874475318127115521321786273614780129829831438626014991843514783028586066905089122532715288580534070605779007112619958852628801540288008918482404759132944298520148080184250697297150817299173701934285646867489426483932830299434150464278537812298564822479785688909850915447762856384542090714278516461905872647123125352735037721325154184406043613668806975385533851732090363979459292404685190942209855935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (1,3,'-5140349743195574373979577554212527512597024.162480344833040409158673429491690439298506850052285119390701002577176786023622062742050099464897084793357329597395417632908812044304066963549928478520702505283307379218587635434673128958824348493758429380623577527186462464399974242800361134191519694694139153279582776168995426125926314513926640766117733774558011741611075336271613675760116784769700605008122422944290652448956922432960815546502965310676913079866511016221573557684245901002643719965652152439520727383305120298495304784052489867651462175349450610643411043707261107569691076730261762793560088893354750383257372118118753366377402045596735023445172252225346164608897913115394905485106225627590643805003075069931177395059698550161546962768768895596088478488887530518018212441345360153523733317120037436403475909117998647781920105313938836144009539683'); INSERT INTO num_exp_div VALUES (1,3,'-.000000000000000000000000000000001413607404628860353773457807436398753936801768769045711604884548436548520368932184112069166807060840219636509423284498981041814526856251281381511288768719259120481595036745286884246627534964287523188738499223075292690431699417313258943941279343383979626641848305343592679057491670166887054819766294147341982669243114259272404203080347707713358471397866402657818267495050115642987782080912962056565478445923456884713049272637646637760989004917643369240372476411912794578381690666695711891846833983534126217706309741885844723208036219144146342212915129560758201609824034610223907791643110990898577049488934294259106725414517181607988173722432655731491050637087261030314548853334338835938120502930424813699221083197863303458179445322810087784892821862085562891180364134284641396475'); INSERT INTO num_exp_add VALUES (1,4,'5329378275943663322300488.64471790965256505869684245785528331091076155554650629138833809683459634328609777839510066435612911583108717191216693735823717997111970662575497378762952496582183738308720094529950793570383580785385569873278068217936841324404119828637880370718028782103860007754579779716996004352284614661690063919125301052941328989181561787543541920734755989452320799185700078241880935083616978140555713297241612718277766918005268951861880490889884082730841740604517529391011862694381726143520658746305661338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (1,4,'-5329378275943663322130001.85391741010004353389988518583956365616764439012730849109607738227723047091262162286043233973705463946054514004224903034208166782419414876904468730122054597840936856190652484801633363526576955397606531892764306099068756437389060626447578949162759295501062154826802212022414257953494004665588557188694447110384853149054690655645134564686305448219729651828678220200218922790293483596988037990835533058983562863141746692824117439019450865871047657552800448629502344444081260036580660700595591338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (1,4,'454294299613767152878025320780.534199313974295807138790763501115780294529340799108297697573066187975311338382917022391830256203305238757334106943821060545424417350991354829668286194840925251162479496893943917530660694097932059166013476064988623431110002057735318529554555260199417935495388243829261809007709919225000608711536928171687251088217591210419208480251102484043683131687013687838713055660405381318396419588727500715930145098362997142075433472039319292466570912777345841400769387321465602989947078951135489852486382469990409873227894248208197179481868230244584527040573428134962626267135732247029762468417273891700661832893497067151409134724061246612631376075173287264787886064622106855886785805818642123776489793586531950438285720668411465570116161790343538663297713926678759640594912243360541590368666922379919514826022141331900181'); INSERT INTO num_exp_div VALUES (1,4,'.000000000000000000015994998100440878014888861029956505927201309704413242103407885948184870841766875212766910686894450511886242468216220470061916924303252919423028993720180330014505454865704155281502763018913215741264982350384245753394656021401865680441649920273268554396350483440173848850052788410943178207336328451359951614056237100465802151856198860908371340425459435127133071447273887829397881221098443685586506647314622864702873235212396755866459409263439958011711379929751157260020133239574261188528305921244365838405372320186907437842180388704854605498842516581811515413843298370501194935797268161171428747542997504369133579105180311662221854071962295818264211400101689450830279979372422749150894553349570063000769685274875561760334738424509532610467832951796852051505383374693614022043010735004494395190'); INSERT INTO num_exp_add VALUES (1,5,'-652670387.03916046850422757312745971450663862747133703839829692066597367760104802542475264601221776157515632293978442027199108085723617181683235487266149426304575903892721468296143475297345699313102262188759506518376019936160961709578829069446312051432780603656651983414612264636232727512091101057374054475214114364113300402823059519499217878746766275164739724770556122895799337810694888119810524986616938847385753562624139431982468828696587199570410008890188532132652095915565323400735066310142303225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (1,5,'652840873.82996096805674909792441698652235828221445420381749472095823439215841389779822880154688608619423079931032645214190898787339168396375791272937178074945473802633968350414211085025663129356908887576538544498889782055029046596593888271636613472988050090259449836342389832330814473910881711053475561205644968306669776242949930651397625234795216816397330872127577980937461350104018382663378200293023018506679957617487661691020231880567020416430204091941905612894161614165865789507675064355852373225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (1,5,'-55643106304872.575994253221940844841058071061962511162776681458310912066379595519265546225338405882027547140476045378015935579066580347282075024392379464189067155567624835346798806677988850250198082355055954078446421075165109896091047534711081616362392995575466807084807876544560268050611445006601394735810211678919646667455478469014906335433468365011768049600750224822391684377238242162320161552720449713229523135506671063115436813348612986916614320012995541575293478341408982118538094438068036422562665160411591652618670802973618768526197813319204816293073794413317669922144705633308090832805914096147659820167569140291210526520361556881576175809360614782817717579318298657744021133210954279487777567785280633309576696708168342539425395482429923273623865667723482418178781573723597156804085501875735112311466228778929147929'); INSERT INTO num_exp_div VALUES (1,5,'-.000130590057635351941758745900947472461593749814351229292370661147301124533787181489468804246182606762727711479707901680546780430454163647774077629503207962424213266902732555945190365467801995495570282501722505521485829885605904543846887348545254658726343578684749830307120625129857380290225370772763609458975555029415082569247186899112975387051141777417911244576134390940441209829852154391377911942082738699481875795620569383196133124499983396562167632007454221121465745085962247988140942672429187053671899537331280701003778040796615094903602095098880716919238394057384949891444700347825726273725378453454782330181608182747900774711384845635284701538541452235224216112380245660177463043471814071809869894647262285332580556739424040615194137651616350340752691170045698234853734471923738591898290468792787543896'); INSERT INTO num_exp_add VALUES (1,6,'85243.44233732197133191329295927531563604777955507322414928382967007765263923984471408038635831036097817458527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_sub VALUES (1,6,'85243.34846317758118961150399799670008360696356209219504851646259063690472663252876207514831001425809630178527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (1,6,'4001.075404054519813215296429095020391062109905613738157927030437221793757373268325953178030040276107574363822832168160758728653712686313134828282109532831190239521843808940611025488601517574653932032236616573457735900045655665690517797280666732780030171712864961531623060353548802466577910774711998056232872212688464691036260746751992072745518373073825852119460094113694393273456369345499434994672730920070410547163082189385645712866100999708173472360864669110044660667614583576570496399103026286828660558854973376227247132815728164629722965145778698957093136175449225024685874279280018547740'); INSERT INTO num_exp_div VALUES (1,6,'1816120.848909727306817960620941575637231136442992819290405125420545200026620306446043740992108329883383706060582482495616151605111275635501481354526017831484915013545483361715432312183101964395505340188909970344423950565285639911521082834494088840596716495422427543520536844348040681236845850482165744696068209384509064196671206362539077218412355776790921130042376467606683622970728503408501481791356294886150690067651815776445750760428874351556866105285911902433352126498951242195408782804314174041618879250740246352525074791310920062276490422853700893340860452528740673590486626464460321410814395342850270921486724297414692313177440726749004398703147904603937755702369682956482832074779404350351752662820773690162594400557957241676636030332988289683112176900913522668426137377289536793838959751008646843014106876005'); INSERT INTO num_exp_add VALUES (1,7,'-818934540071760498.60459975022373923760152136399214017262844141729040109985386964272131706381326192223266583769046276181472898406504104649192224392653722107164485675679551050629376558940966195135841284978096687306110481009743118940565957556492470398904849289222365256698601073536111216152709126800604695001949246634784573028721762079936564434050796321975774729383704426321489070979168993853338252728216162346796960170352897972568238870481118474064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_sub VALUES (1,7,'818934540071930985.39540024977626076239847863600785982737155858270959890014613035727868293618673807776733416230953723818527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (1,7,'-69808760806266041400340.70700818693892852138813934414383886494691670042143650609934777814995087699409404201920249076407981012095999320858479644760715204999741683528746097757549835956359129287002171391961763797857794730120426599135099619822532290339000466211195776337667123320942107370731349851576864242697412616810236323676004067839744992733887503405311090677026008324895177587064547630828026123718296429295638934384446325302964896473296829265805737112709269803814942537657996725913938408781715328945194948010970'); INSERT INTO num_exp_div VALUES (1,7,'-.000000000000104090609479936344103210175655521317012597986331111866307697262848964666360492361638117930801818899121383806224630563676018240181412174154250663423230239912527388431901852952893943812666142740182651125508583527237123596541789628675379232473721293630968882045044077795828674268595016625198802475186587918019739056755398151182369187670251750080227679555002307777300392769289647975058449905106584837938556260801229545589323224752038795423164214112897202147313792076165011373139219134850954217300915326944185918762838321705825423789073869940092569940135329697980600082436317664012683589681419530904283106912171330819469065141821685734295058255484933744156717782754922568796985634397878149984177882018261742637463462647452140104146195353696596211873925359508622779658904411330975862442989437933211964821'); INSERT INTO num_exp_add VALUES (1,8,'8497071467.03603749330791582407836434318377133169438097066269854720538319012928851657498035372443556191720308219530866834905045144302106406146277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_sub VALUES (1,8,'-8496900980.24523699375539429928140707116805167695126380524350074691312247557192264420150419818976723729812860582476663647913254442686555191453722107164485675679551050629376558940966195135841284978096687306110481009743118940565957556492470398904849289222365256698601073536111216152709126800604695001949246634784573028721762079936564434050796321975774729383704426321489070979168993853338252728216162346796960170352897972568238870481118474064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_mul VALUES (1,8,'724311956372274.0135050255361637906710330203036651743488213007179039756514944640108625580172737414192938789413338554327986697518463087452612658955180411327002900979574347739956600177846996063741787205122007268468674386396156638261992679442768654367111433834151087792255469957061758837789341439211010331332174981459471333376067541234901538285101103690622656631026001337239036711179989456674399137008584021283568040818388709554256523118702728176420022080138548890713013682480239784198421500241995499841675772793497485550923152267616622892846304530712344886979674416990935007952941652591352603797627920865960622077762568060903908151958000'); INSERT INTO num_exp_div VALUES (1,8,'.000010032191786198542900505683562217892317481076466949299850809276743457759270150820565375820388277409258249926696079166209409657808406245382887790534127749833677458375931047385994887406206232330491317602830654688957983804698568410728278089250379255157030886262396950539100566975000094268415749476738358914633948867977798590927055566888255636132486899287919515638902721543629183577900872078173883974905921239149419877613723476347774771230668479296621531969573505480695490386225866950545725121902534610730154727385072738079149623798073810167706094070842646222833137345669922898403368997676634709281456818189049718956207208697021706186341405575300648248555331280690778367620868775005181264547924615247991795542738868003191757946979714250339430363902549866892041102771965653407197094250270379367437342632741280710'); INSERT INTO num_exp_add VALUES (1,9,'54948723.74225051983134098996071145685528795757427462111901537365053896571438476055974853245403475510333627298551845046116291696445177112567064282766115207407461565363967417615506303416694032848457927390574251904212425813072768882213388082765916956736282110801611726537663292922699021333445658549608928179155685881583228490235606377831724593358583903616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_sub VALUES (1,9,'-54778236.95145002027881946516375418483956830283115745569981757335827825115701888818627237691936643048426179661497641859124500994829625897874508497095086558766563666622720535497438693688376602804651302002795213923698663694204683995198328880575615535181012624198813873609885725228117274934655048553507421448724831939026752650108735245933317237310133362383704426321489070979168993853338252728216162346796960170352897972568238870481118474064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_mul VALUES (1,9,'4676749348240.390309875431213992853550297086049749814750492488995108783145961719774217441193547534210468967573344456866203963659951312519988497979489304488948342258375915152429008993288817366720647491166024151209542534474867042837694499222928509320280684557676243780452100132238968233413333851595648146954975713386711764268506890884764704949969602122157394714663532141060559896359465918874990769222345665160127552795532197771168442486088776803398878354288847069602460071745966589164282641033852314335279121191855487126430176047553895892632834940595958394834437871886013513058514896870683979585091413977173250824451205330441299000850618134248917380244749589254309567551846327349592529960432446947239714236828401206843011440433362544797025114476612133622499094287321570559088587999417440664282418005102546343020409520421747216'); INSERT INTO num_exp_div VALUES (1,9,'.001553736563217204408368240901181555234014339476186598647410198373122572205209277343865051610898136462487966496673511261433286284257044548634547569923035899634327495195510767312478861719221916387940027268721306540663743713345337497285507595251328382906111997524508729275471287648008479480805967901972481289402930660848950039779707354469389216931774094174326513465502460315792834278614886136688161679443873815113442220055827192996984074129528034845339130162104547166079591654852164993577408422015514100323825529286511720963047269483211930770803479398243069649400360625259869765138545866815758888670363356947311319523139395191102286838888146829667276592755438606664644975648828848738708349790766370694194763606850690923803984129157519048493985198591771429264967247245289970213262206709011468289046840862597010969'); INSERT INTO num_exp_add VALUES (2,0,'-994877526002806872754342148749241.04353023451866590387054959174736129501310680280823383331007646306243540953499740615246583399296334239109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_sub VALUES (2,0,'-994877526002806872754342148749241.04353023451866590387054959174736129501310680280823383331007646306243540953499740615246583399296334239109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_mul VALUES (2,0,'0'); INSERT INTO num_exp_div VALUES (2,0,'NaN'); INSERT INTO num_exp_add VALUES (2,1,'-994877526002806872754342148663997.64812998474240514147207095573950146764154822009863493316394610578375247334825932838513167168342610420582834742950389452212867974756590355021495169819086060202117180229196935525386766373096687306110481009743118940565957556492470398904849289222365256698601073536111216152709126800604695001949246634784573028721762079936564434050796321975774729383704426321489070979168993853338252728216162346796960170352897972568238870481118474064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_sub VALUES (2,1,'-994877526002806872754342148834484.43893048429492666626902822775522112238466538551783273345620682034111834572173548391979999630250058057637037929942180153828419189449146140692523818459983958943364062347264545253704196416903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (2,1,'-84806738323879544552397401815149740513.8505875535743013876823142649666132764556588225959336097903898464616542203793600590311980154402068027051522932586050753865288419084437796768749509032177577451738712965496693249429231838833655025794915864261585848007162358912070811805298210095333433397862313304655108809804359760907473898420016370058274978588765092161529583480924554820756527238472641797198545539410039895140087686344382628317530286295498797849942258314364503000942821309916954725689781458590617068629906894951122301020797266469357701283289275708774593896770378558232444454118891917258610753077932026885574920166837998049508644891327208474213193224700658584824407382455480657734911543930195324144216374573825'); INSERT INTO num_exp_div VALUES (2,1,'-11671021799770914903865020509.301561107153561058074179843542446420696517132461554451075945807420674211966679216615407057626541711186781735967334896541890595771915856783008831770988426637435694856170266346306640678577376310547806764332837625966429200996250687908930748245035578756314083608655163891041399241377675534416837659335561005203219889972336214863417948542956735403991871098341470996860469878038840964359144637726669728240650066795729910649523281308716277906908340457162235831526838308777581569974551673352306004330423694524256415657620427590352277556907586751621496248973165690360552007637570957980230685679819820147036159174977086193494572117089582758015847544798464543446227632367713941117001423437766840744488426025388612316819120660814681298624293065972395923651314350558006567251033289878238407790871784676348196394482477767774'); INSERT INTO num_exp_add VALUES (2,2,'-1989755052005613745508684297498482.08706046903733180774109918349472259002621360561646766662015292612487081906999481230493166798592668478219872672892569606041287164205736495714018988279070019145481242576461480779090962790'); INSERT INTO num_exp_sub VALUES (2,2,'0'); INSERT INTO num_exp_mul VALUES (2,2,'989781291745465665243281323944996915810556285052564220274237162526.1617859904902612197894543199389468971679632139059029459520163585971122643624316475417489000981872666677202334180945949860058384424993911721081868337499377890298636260338063268639283065887210924895929155083478140340889209440025415565915964293989840603863813531303253038823629712989041722072693449251635519992922148998556112923060331794396659338057474019846675262291146025'); INSERT INTO num_exp_div VALUES (2,2,'1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); INSERT INTO num_exp_add VALUES (2,3,'-60303024366845387174536638750234506721.2758014749274942132576365116182462208228193753118527959000939070820507877345194783035668195137119648748792386548310474079340204536236936213411512867171486174240518914767934028451971067161683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (2,3,'60301034611793381560791130065937008239.1887410058901624055165373281235236307966057696953851292799409809571799686645246659986351515277852800926805119259053513475211488115663286642009614039264484259692394657121785950542874788161683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (2,3,'59993133911282372667149627097418449223835595194300848703012380022306762.154418449236691515146061305380465061074531890529497774836941002526095632166401249277270674802626154774328055399254982998368191676630276960361274433270795772477146870294928855773172789856196219950097157391050424577381777627004101100872747943673762087675405200265837631665464736842180920496158545887039337399558993437594084473932658319914390365451919627956823980800124880375978662052111797881386060353490432427832058851094210488804887183034572364751639107535041308434932952695103493677600969712634416241541391613699710826602011076372592299807609658979777598672141389319098817824624950794758296679318319299142035'); INSERT INTO num_exp_div VALUES (2,3,'.000016498242835741013709859217005931279826178662180173096568520102488480129191427472581644597420895622947234184547373944996197105916093347103336318249582032230903680989710242610024298937774441533502282949127537125997753002819456724709929935850697744632904111143787011103837624936502324835260843148595669524694347566421203164808527739207590986975750648112133699756328511947175496694080071202064255118777680958612315513441989609682655431197367166056616661045712867189326408877133865572680407329449150282415810958772293869902662884761202424695742898573841869524376684740249281181605067345203479719345061595919652192297531638467223956758315591610733251562492794891852151639643060692698365496208796638230566761231611376199140556503620471090364900792180618741355091923808605890415081571900697282725022629812561702118'); INSERT INTO num_exp_add VALUES (2,4,'-994877520673428596810678826533995.79421257464236160757218576989993781147390382997132644206786872350652200243563770552469933194637146474528320738725486418004701192337175478117026439697031462361180324038544450723753402846519731908503949116978812841497201119103409772457270340059605961197538918709309004130294868847110690336360689446090125918336908930881873778405661757289469281163974774492810850778950071063044769131228124355961427111369335109426492177657001035045332525699055300921341010989742896430768506909949340276549373661076950964959025967328861569387160956730002517417236732463510495205173523163676450203614971844583064927040066684531931069310935516821795449174271052747559395296525950219449541557191520903507653089998307641491381797101485104546410643'); INSERT INTO num_exp_sub VALUES (2,4,'-994877531332185148698005470964486.29284789439497020016891341359478477855230977564514122455228420261834881663435710678023233603955522003691551934167083188036585971868561017596992548582038556784300918537917030055337559943480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (2,4,'-5302078674303935968062773235453828254014583744527466365136.236414807326868572353809920518232561005161225922028750078608989965741402418802255050636954800114792425419735155504035469350521800895164087027043476055514245942961100610551646034472084954313670284875310691807937254054948742125729353864014122131419164449567115006621212424805182687707372956385102095255735458593389920872596796806885847543910224476727171570873698525606016990229936284811067826588349092841322512643043008589065847223683467371925773023109720951609815041012521485326120380123169545818055967455575736140138663815073081494226676896278654189873597341203197903408668523514375373841493189836809506003729379742035629498519683885268256481104619815130659628225053833297766479068686119691010593208135616363994230674606991733148502293102108193522604968743948323130517040609601859735899914987426089053869350663'); INSERT INTO num_exp_div VALUES (2,4,'-186677971.517539861245390308778107722315862721823627804195528485535806132067679059453022306691281662574091826898288146790399178357754908901382135796783067563944022498807930452234032896817601590728156392188660701355670595952594500812333935362955625137944589981298793332621503315902294100258945995827423279442031218510259915311555745581797315793010762585658196457363672908315687720174516274528662385172326028870945153551774300419158584379602045442200523311437013776079979639415633358878239012925000523542907592866797199229858272764668664323316251874027468128770456766875866492004650352654523634716923150212263912760225390093339729495231675627059805624175587380165509763048913150826017167286786277908970769297060278191518730887417202276531151575412404467497036737825989088867451153485938272367300939127313445244028528055624'); INSERT INTO num_exp_add VALUES (2,5,'-994877526002806872754342801504871.47809095279915423939648794226185974985600242391612965412218049794216637114648812993201775787765690351615479957141288239552036371132381627958673244764559862836085530643408020551049895730005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (2,5,'-994877526002806872754341495993610.60896951623817756834461124123286284017021118170033801249797242818270444792350668237291391010826978126604392715751281366489250793073354867755345743514510156309395711933053460228041067059994425117350974491367099004404995846913641329458537237789584653041949090121498951516476399288513593944575192159570458664608461677113504914551578443229008454218964701550932948083369656042643364608405637360180021322967144409944099438498649645368196191999692949583952927486593144959284443545794934667002661774373364219852712996869245745722896071593910890197478196462961042627387162830776094709087748993678069776845437889735782063'); INSERT INTO num_exp_mul VALUES (2,5,'649411906691138274293985410502516861224852.2323455192714410716272307781034189160865613770320102043319541634113746032638191509585045862973333645830298922352816245477556264222094036953195419857712804755170632292914187367964994214922001758104594052499795564860466055599417895782179851297585155129541589802249540436678824225950907268084876110445460948679383611117263673106597132046331719468816839434908155684738864149955129235751738204036443603521478609787295079710078973503970964790273461142497259987849074597264522099648376356902360358310245001183020992360260836105404118742418040965190000718736837422434593694808973939805954329718232693154128543253581495885789333274488461716809104532693754070810202831113003978085636579574171344721710232931261731022478029314435363413498991740750878099825781577297965642009156858479681236085226911858782115'); INSERT INTO num_exp_div VALUES (2,5,'1524119409495532727030986.638577103454261465522025182901477334004986357902177024959076085490119358611626688213654669281670407680244740174673394111775678935383154847014211641601227316639834450258566053805263858706381900273201146454036688771735398324537667996974210741719621449948660517037619359095556637235980122706739013220201060795557114248610410815988952748489854367480813823114296393315170621979351958306734282429929421779129764262568942699813166237466796852578307944635545174715298176546980314973426586923195248536376403319094417073026382024413817222396402299695717290716014320518777088811749776114378145110676170242861393274018655137797545194817703831240390631723050378397773341835222892981773205967439339460305257986693600088957772328044922955990976285151896366292514128607363007421484320868718566256882080399264346243272770200676'); INSERT INTO num_exp_add VALUES (2,6,'-994877526002806872754342148749240.99659316232359475297606895243958507460511031229368344962653674268847910587702140353344168594152240599109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_sub VALUES (2,6,'-994877526002806872754342148749241.09046730671373705476503023105513751542110329332278421699361618343639171319297340877148998204440427879109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_mul VALUES (2,6,'-46696638263247522384986521136500.479312417066793299922708112595886608370451213741279484136907754744903470430131032928908162742687359367826808123516519335458861613010646992354378739165872253762686683966945711430182491860196341344982195078000259063231136011430995647812149294224699587849791008794261026932467933475782780'); INSERT INTO num_exp_div VALUES (2,6,'-21195986018643887410662481595901800.342199657994285865579781485758715114242459388977583220756870314514884887803267837816669111279417861218648323488364513921592045485003563036021370174294475403630933854767386355037781881144701319212711655881277140183173924089814927297045029394618083349813549439341772734606115369911736164723942330187830605893993276674913563980890459604886172701331890746621222114280438198802989678877404376001410627722336243835841751052795437979198996482216031399073597399901975686733315751292369326904428230195579137225651689857057115970784985439417129044974524632220457594191305254649113470116960582543784928547885740020507755033347968928034294570497118410435615856155184563329718831512839630769097935523279881940380220955993456451396417879773380305142918906742431812580562496634831735169817705720949712410595406012323294829461'); INSERT INTO num_exp_add VALUES (2,7,'-994877526002807691688882220594983.04353023451866590387054959174736129501310680280823383331007646306243540953499740615246583399296334239109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_sub VALUES (2,7,'-994877526002806053819802076903499.04353023451866590387054959174736129501310680280823383331007646306243540953499740615246583399296334239109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_mul VALUES (2,7,'814739569184924399102711674444306584731316176345067.39834031417849342571224916231092924046722938910652929295271097903377854123984307101079073134405782275535446337229706620713104545454319555885847481531722101704765783025789147453570970090'); INSERT INTO num_exp_div VALUES (2,7,'1214843772391778.127361407585140553741220126410637250571020684739034685508176000812180032686291124045768750332493129822580347351032145964983629059968936201592138368806173099130176852606440296388856520582890650384142745607345709716826703676313341953999327129144154152914234659001555055379537780751567782847296067128932113870102563522810980359433259696591977617184951677390423898232135100000764121508662830515405980450892222598485287609657612482190264517684867291774820716746063133066053446257163185646067618679478975882247893469409405379034723543061767846895135644429012095930584952053545016706315299076691015196261253199176743281648949731423486208098120903720124071047872917636988241710583721537777321338769039241700203546247947405745989053846970910400831817998342969657501678430211657755864160072525313889413731419647001970593'); INSERT INTO num_exp_add VALUES (2,8,'-994877526002806872754333651763017.40289299098701084219066388457144979069028441485513418625082363021182982914675513019536443438529749838106171095037135009526312783302868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_sub VALUES (2,8,'-994877526002806872754350645735464.68416747805032096555043529892327279933592919076133348036932929591304098992323968210956723360062918640113701577855434596514974380902868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_mul VALUES (2,8,'-8453460632655529853033389979024265783461224.3195241893307807116624750282852146303290708492834695194274289713076935297734670940696121761483641291930931061232942894577813178566088927221374036301485916497770984757492912292002695944367308880163698595015497307574177176409203214324418237020500352652934909632442547242092296504047310806151851207329042221920888326000'); INSERT INTO num_exp_div VALUES (2,8,'-117085929036205907700251.219065234073336548829793284434494573185718678644093751558890746941383215425734761534822966779511801033216479269605150574332107020180872343673157350081102818832254463561564431056604957702984438484261858890324442581609284935850435611342611117035589511568432559140282381526487115307554496353616929034919886387903446436924514812698404129456069856633480965357915969548215985452939172313964007318881987188665231550330515412104367728617802960792164260429920719961650164518261501571220901151359208484337831586551714193024143212288426326740373893030225940355268499071669300664200888186064836443459131985786957267268845966279576380786883200277187591448294590370986026461176853573555996139940001165172158855197070946665074838360933025833716166930231164328918316437195201546383664484983447934244744303265471044295601062898'); INSERT INTO num_exp_add VALUES (2,9,'-994877526002806872754342093885760.69667996446358567630831677089993316481039076439881735980566785462673358516198695146576524119916430759085192883825888457383242076882081857926408611052522393579396644731758241837010163568445385303315267086044455246361273561294141518329233754041352632499787199926225490924591851865949646448441825186059741089695009429827829188117479084665641367'); INSERT INTO num_exp_sub VALUES (2,9,'-994877526002806872754342203612721.39038050457374613143278241259478942521582284121765030681448507149813723390800786083916642678676237719134679789066681148658045087323654637787610377226547625566084597844703238942080799221554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_mul VALUES (2,9,'-54582443595378013373024060492546032003692.4875677735896411267274323339692558458420972958075073392126734000341372096298914875892612108329218081214550050039133117695428196702128258481789017059073444323729583900855712795086447886053552786449313809589992185978097430132940882612817775035217244553616977182049775786664446683332098226841743818600819221587510039430478859412452506872131851471967577741190323481953867845129745440745526578327709351120432530702446916035797432129052518980799424635406993848916727957825620638983706180841278402925286540375225365057191075559133035'); INSERT INTO num_exp_div VALUES (2,9,'-18133693300409132895168796.074616314168631402221003009151140409826855230810646429042722071403306917323628118792142878282108022292754325022530103525285999179488507720688317761243448898240836430183645778132937666952111134601563043980164547020295727057908447220163534134835130866457657964382363853570827467081988390359191484798677813656413640874450449802233520570178139244957518604566383671867773821069602665918688868868894979351219381089954104823746091972754649316823714354000113723793845707472924569647945844436702275724514171940901057842455729977729388911537391920702753167125695758365521631000334183494148229356487592577177344247694925635113222720411958290166668659311154664393442690740373285505786584987609789805525300762074682544164213490532272590665630428583216403362629445153016404037983825555019274338559686335405719430737559715778'); INSERT INTO num_exp_add VALUES (3,0,'-60302029489319384367663884408085757480.2322712404088283093870869198708849258097125725036189625900174440196153781995220721511009855207486224837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (3,0,'-60302029489319384367663884408085757480.2322712404088283093870869198708849258097125725036189625900174440196153781995220721511009855207486224837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (3,0,'0'); INSERT INTO num_exp_div VALUES (3,0,'NaN'); INSERT INTO num_exp_add VALUES (3,1,'-60302029489319384367663884408085672236.83687099063256754698860828386302509843815398979402006244388708674093244201278399438376682321121138429850885935540924586964982855913223221441591310211730902799041126800414795030815514254713522692405212716783388698431088814919226444677188004928663343696636297536500970117716818423689175692808344185016908913828066250587407384563498516598672584120143890364303296142744031320345312431817858545326010704685255237541162931904446804064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_sub VALUES (3,1,'-60302029489319384367663884408085842723.62767149018508907178556555587874475318127115521321786273614780129829831438626014991843514783028586066905089122532715288580534070605779007112619958852628801540288008918482404759132944298520148080184250697297150817299173701934285646867489426483932830299434150464278537812298564822479785688909850915447762856384542090714278516461905872647123125352735037721325154184406043613668806975385533851732090363979459292404685190942209855935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (3,1,'-5140349743195574373979577554212527512597024.162480344833040409158673429491690439298506850052285119390701002577176786023622062742050099464897084793357329597395417632908812044304066963549928478520702505283307379218587635434673128958824348493758429380623577527186462464399974242800361134191519694694139153279582776168995426125926314513926640766117733774558011741611075336271613675760116784769700605008122422944290652448956922432960815546502965310676913079866511016221573557684245901002643719965652152439520727383305120298495304784052489867651462175349450610643411043707261107569691076730261762793560088893354750383257372118118753366377402045596735023445172252225346164608897913115394905485106225627590643805003075069931177395059698550161546962768768895596088478488887530518018212441345360153523733317120037436403475909117998647781920105313938836144009539683'); INSERT INTO num_exp_div VALUES (3,1,'-707409990019504668223608170643582.082425157530076679823177950190511141917761066423266390864536360056345386873500583953954967225431526056199231768143978526582904071798714789552447782850723926323452633811653766838064983821149041415149067433978085927687765773012158659685363079191901396502099956189371719135315616249471739677995520904113581848295732911534266040260836644379296158092198514963023001686666281725991605685524015227112003429486755206848316731257322742428352116058878710728614841247581716185886403744830796740424927494009978599974431617064012221450054532987372285996679180090592706458366967534834069977644215413076082570497451654516268857039718730203921980307096740864747006176117071983875364434497517026142488015705391255750729200497229031250705777282987863242056223584453312226818451807347197583925624299372040413470456696588043062815'); INSERT INTO num_exp_add VALUES (3,2,'-60303024366845387174536638750234506721.2758014749274942132576365116182462208228193753118527959000939070820507877345194783035668195137119648748792386548310474079340204536236936213411512867171486174240518914767934028451971067161683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (3,2,'-60301034611793381560791130065937008239.1887410058901624055165373281235236307966057696953851292799409809571799686645246659986351515277852800926805119259053513475211488115663286642009614039264484259692394657121785950542874788161683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (3,2,'59993133911282372667149627097418449223835595194300848703012380022306762.154418449236691515146061305380465061074531890529497774836941002526095632166401249277270674802626154774328055399254982998368191676630276960361274433270795772477146870294928855773172789856196219950097157391050424577381777627004101100872747943673762087675405200265837631665464736842180920496158545887039337399558993437594084473932658319914390365451919627956823980800124880375978662052111797881386060353490432427832058851094210488804887183034572364751639107535041308434932952695103493677600969712634416241541391613699710826602011076372592299807609658979777598672141389319098817824624950794758296679318319299142035'); INSERT INTO num_exp_div VALUES (3,2,'60612.515523995516156897729403721504966784736064970538891936016753206905080265887046037910122269129293912171105589512464185386239562077778499936203155976336284324712221812806801062157592930664021782540155687632208890794166119782594464410498356083266087045927038416810562596141871858142749062925965665039981381277808608946877852933015970874447235220989360704166270479475802673572039541121473138382812420076284458769543418652217394352637294823914346726065145538710933281768776286965107974980550163605068693568717671571780028113969794125200592691656568731359981803586296135840575095063824258761205175762907549288801963550628589530419118771779395037240198270853609924445368393952404606326559485235840170339343865253618184271158932135392539396160392488927771488269959497352568205940636180870805982484030168838833607478593'); INSERT INTO num_exp_add VALUES (3,3,'-120604058978638768735327768816171514960.4645424808176566187741738397417698516194251450072379251800348880392307563990441443022019710414972449675597505807363987554551692651900222855421126906435970433932913571889719978994845855323367077258946341408053951573026251685351209154467743141259617399607044800077950793001538324616896138171819510046467177021260834130168590102540438924579570947287892808562845032715007493401411940720339239705810106866471452994584812284665666'); INSERT INTO num_exp_sub VALUES (3,3,'0'); INSERT INTO num_exp_mul VALUES (3,3,'3636334760530744652235488357607657374520053530993537920755375319352615385278.023608692512217812784472508939511216316773023870624171279878340621219698109986095090336065266376220109007718694455520948311677863167090936408887147442375455695868593092154861636486745490748828207939155392396090682312136290864359484540126174821846208064763823279315343506148025281475729723686566174395516982893064510403581479746673749128344955124070957545815390178764940816628194640888255387443237798761377617383817511745005525149990207764725040109364671749403389999498572538135588695345112358160274671918953118753964073105250116426665508214894805722798842017943220605600452911496071424281587802689830031742105619630787641205011894680546049982654601956546154572720177337696285354350903475239411654436042931409507429892682706228354459580412759920815932840348933425754970917910500027837428631661182510071352138858'); INSERT INTO num_exp_div VALUES (3,3,'1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); INSERT INTO num_exp_add VALUES (3,4,'-60302029489314054989387940744763542234.98295358053252401308872309802346144227050959966671157134780970446370197110016237152333448347415674483796371931316021552756816073493808344537122580089676304958104270609762310229182150728136567294798680824019082599362332377530165818229609055765904048195574142709698758095302560470195171027219786996322461803443213101532716728918363951912367135900414238535625075942525108530051828834829820554490477645701692374399416239080329365045332525699055300921341010989742896430768506909949340276549373661076950964959025967328861569387160956730002517417236732463510495205173523163676450203614971844583064927040066684531931069310935516821795449174271052747559395296525950219449541557191520903507653089998307641491381797101485104546410643'); INSERT INTO num_exp_sub VALUES (3,4,'-60302029489324713745939828071407972725.48158890028513260568545074171830840934891554534052635383222518357552878529888177277886748756734050012959603126757618322788700853025193884017088688974683399381224865109134889560766307825097103477790782590061456916367930139323346273315068375646692125800496305291080749834712822775973790354498408104142209966769395239768969172107040437333428573572464689550003374384624966403962290572373571842567623422963022155546431883766327294954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (3,4,'-321372325955692885069615337209737469749246561535004445508427591.072860243358366933071485495726715620133686420023451450292996945184959542770492705998350644739298629407567812798540119555932604687814429669592481327761428042980782672136901602006622227365754036664912989085940235439697789102358431343119457114603363936544931303133371137532006899162833369543279729021228901466728220729625107362063321334489394782322741444425117731922691457341543446841167138481424319752111748042440994701571955325673470021626946676976482516292402239416632497972073915818846704053624707839813514171497746804751780741682011937606462260710753056669269928580460921188286249923152921382198282201761171043384698319895970192114563900025573490442674225227682235790590616707857188385274186584856872573669591460447105688151281208238908470285147895678001948902280493477604361481216667716971590499226735103039'); INSERT INTO num_exp_div VALUES (3,4,'-11315021446594.877643290091276308982961654569173523687151347727612592478433578066762912541361898899908505997444632820107356713116459078630334224890355872486337973552333755378190316811715776951317058334754704988120078733912131691682869448731717816749620336196719541702138949084375907248656748314375183301372633028246109596775255074617515860012417935744433243071057057560464360663978361945666099558526069794464437818864063206829678640156992474597480916575712563493776637239091589972373682399519931569163592317107392231951775499293572134702843085474656152913351183535194499521618027894129537558509428098859715020703897463518891082573242502356303078754574312965093639182648263511466558336912294702019648266054331227425119096294871153811412169351624751542166779635702042223762951850816568617453355571302500885410532963789364822647'); INSERT INTO num_exp_add VALUES (3,5,'-60302029489319384367663884408738513110.66683195868931664491302527038538338065260819361151478340212147889934633981101279593065290940544218360883531149731823374304151252289014494378769385157204705433009477214625880056478643611622410268943757215673170753460135411513114716313801477916713433956086133878890802448531292334570886746283905390661877220497842493537338035961123751393889400517474762491881277080205381424363695095196058838349029211365212855028824622924678684631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (3,5,'-60302029489319384367663884407433001849.79771052212833997386114856935638647096681695139572314177791340913988441658803134837154906163605506135872443908341816501241365674229987734175441883907154998906319658504271319733469814941611260503645706198407368762270127105340397375230875953495882740039984314121888705481484090911598074635434289709802794549714765847764347865064280637851906308955404165593747173246944693509650424312007333558709071857299501674917023499921977975368196191999692949583952927486593144959284443545794934667002661774373364219852712996869245745722896071593910890197478196462961042627387162830776094709087748993678069776845437889735782063'); INSERT INTO num_exp_mul VALUES (3,5,'39362489275784146262776411377472433635883331946.794473520543457442955620133347015506556162839462623905489255080102447195050109095701660164272430316804466254467810714209179752718730906325952685817112992943656292503112803950215110778476301809440329937774061163668461957943313261962261081942055908935814323069621279128270849852239727888939033546870208376394878842958202403235309372240005941467570230067124830916866857395233038346727879951123599893174252558078732888910139309038957525961212820831321973219557165558911222848692996406741318948607549825343491479728117062814094258484536263158005174429922237853707635743736923521032098496725445243775790161216159399180889906705265012270270348146530113428221072591696851818281866095288773371414866822270689959827332258348570976075184933893434327278299820594014788148344260948638847457822697682605612771344335201258128'); INSERT INTO num_exp_div VALUES (3,5,'92380711368470856513514428781.033155715252174277753317877861994356621252232374386687048394529670637693505779282500567256835271428113529026462111032257747830329068594622091282098767000694818101994264352932243278144124687156236926607422077479412495979777588932692081795130282128890441931602671468684153168580234070246201722180460130467506344034452687371838907269162119534950946217165384250603250357360223255177692065141037447374172264943732616165429783010079281851748804739433821308362193703012671569249508710820679009084891198169587484117171861141580870066764275087111843275285564262902405980617569581840831518012986031156042600391943605532635833608358301306456966765206853910579231447150839538731157206153540873916893579943906851149770881336811951119112558311734171557608362620988555075663589827484854016702489324791126228380209309587206299'); INSERT INTO num_exp_add VALUES (3,6,'-60302029489319384367663884408085757480.1853341682137571584926062805631087054017160819890685789064777236456590745415460695320768374693076860837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (3,6,'-60302029489319384367663884408085757480.2792083126038994602815675591786611462177090630181693462735571643935716818574980747701251335721895588837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (3,6,'-2830400711649493468815157129316992649.40542786074520931471973065281957756940496588853021620372179463538053123396140685749478530925306163968207226329985017644835203709485594362663495728106061878665324856417118064730721101615473194292620972173690618491026470353143141125614124440035267592258385099934706896692953497971326605145704135723011753705907329979207428661473172503098296622281647255008204864404416199384701720347319806375450632245634238172654086373193251877533131784268854289406126119630708578053354762596511353053106459297339360827562281168219966099848212'); INSERT INTO num_exp_div VALUES (3,6,'-1284742031601444539630782308463065726620.121021225455596762466053504195700643301310745151565435123335541550963124666304408503436412726848834604336377169205828654564329888653766451656774534718709065521243637375270687684572524302099749018591530352756390467862377335526634920857924031482455373589053524922608255779040656019538392173139295812160325688504210040741075388404155144782519528791757450256668977268409265390016721724966592135644698341754332845002439113523127047593325646484654291494607100188094186116001064043796216982681807318598789324900462932294782971663150070521334398542559480877366424630693734132836518604260869235580641521264976411493166969530737254118968281271908306432918913600567757535151861421384835424322504855607676315840963696944683182767935565256136130185809101891760917733694553800748568697830680328155128016670099315391685422333'); INSERT INTO num_exp_add VALUES (3,7,'-60302029489319384368482818948157603222.2322712404088283093870869198708849258097125725036189625900174440196153781995220721511009855207486224837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (3,7,'-60302029489319384366844949868013911738.2322712404088283093870869198708849258097125725036189625900174440196153781995220721511009855207486224837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (3,7,'49383414785234649002982046297226894664526726187218771083.0993243619030008310875293647868815940421844461627295157812843657782639833900543200310573708100000958929315945039020410482966753145208427035917753919085618457760620513481628641658765820294863970581642745379331727722585319163262763708386199720411053619449096019862596221607526610103408936214184850115071874430846697061554769773328338028749631552202705583855831155461651414320570061181212214810086436100771547030013079997847086'); INSERT INTO num_exp_div VALUES (3,7,'73634737013325927185.787791148221519354461791539553527545166847382784629235192342551464898036004011575416717008403527685470842765455409054592207142526523023201841973047779202013398235864494503216973882479116841765663948294836180515686647139678530220909072497288527276378202532400736141014848907023234659020093073127450778982904578906877634654521825977382116752537063128793631412296206704078569268566614023846282524151679028060869175439188773864994186109445961525301841201265289707928211114515861536069733921800160245586536759625418951427346236213019358749196674633237197452976517130405065120577692737021174118093373953642724512531935525024447977867020930500433287279183436509990047372809400167546185096048971157700858970777301410692908939206693154161335335755844997198191427289546263182822280127912118140820265025555165337881999926'); INSERT INTO num_exp_add VALUES (3,8,'-60302029489319384367663884399588771256.5916339968771732477072012126949734214868901845505193155307646111690097978112797961939995859130827784737422228762767014427842766445950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (3,8,'-60302029489319384367663884416582743703.8729084839404833710669726270467964301325349604567186096492702768702209585877643481082023851284144664938175277044596973126708926205950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (3,8,'-512385513828318260570283740065493064477880918352.732624553690077857674083796435724202494963885926573907185100543184828131859183999195040110586155435203949963570735841632689374488877298209082579317039061893012560130258753218955057387206477423088065663401594359617882154814262843273526859406265633827109554791772242178864873774889091687515990672487380368975556580539271333144212685871370972163560839446696514092637412587953506052848750866803569213269271165856310101244342151576488190595936869490659700946174362872797854591188391982770203203644172999264143929484089237665313698600170041324566984832357000400'); INSERT INTO num_exp_div VALUES (3,8,'-7096872691348467943606706217.907270287823269424282176534343841939501231816905820949045946136373255017076943323578903040918266385724756894003692978391468202345397178445216069294845721607024056189567609414049207292919519881725733381453217071918292453682942046440563446278374996563501512335133749731529362537349288419883140401056747081065947774593869673146309163791076953204291951821124894409171722911526435445719071769008713367057971351892550570642991097981458696464929009464411568672010548002196406312721789582428747564855324072212842315229302959908665089850886951261233852165624100634055045684536311382452553544676139507899503993644452161529145849579200003677255968757773363970434791501820320494192909660871475590637419913907191608957830524390049664686282439567943053924245852983990958276537000732363895444894582579142752920882750130052682'); INSERT INTO num_exp_add VALUES (3,9,'-60302029489319384367663884408030893999.8854209703537480818248540990234567956069965340942024890856088355839135538265116174644003927269495876835324407641642359213535695803871472434650475144516723617632059130297610134243891145006222068960999879308472500422640481972089756410157246974765071949782242392661524488959954348903412713930092273629207697480131360047867213863018127928853922173643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (3,9,'-60302029489319384367663884408140620960.5791215104639085369493197407183130560124286109130354360944260524553172025725325268378015783145476572840273098165721628341015996848028750420770651761919246816300854441592109844750954710317145008297946462099581451150385769713261452744310496166494545449824802407416426304041583975713483424241727236417259479541129474082301376239522310995725648773643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (3,9,'-3308379209762459471107480259839508279070920437.883503980178028214343751083865562028455061662673132221930429904398963590401793045470444301883103141901787466923883803951815572606105617157736442670792467625964359169270739534412932791178258858918086886061702512427989129732248215348301444245772127142869263635282888226326427510486246184233225114523636171202034558843515894542952126988613018789833835507734620046994907453602573865012044120483116345444810078666601100257620969379968264504287700045822481492526688635364586344704730579892342786173395802035361824932075736340405960099542224953439044947229246847140957298841482874444906129049023002897135347878048572628834749795298712449864571996898774444932083319581439741625832405434317985988163261591679157437224404970927012111196724239860528859217322132733404472897289'); INSERT INTO num_exp_div VALUES (3,9,'-1099128766678422054524173986658.839339966689456265703816212189145237878729886466041806078542573981227645802109969871638687985985845489422516004202630099080709709893022100481258818112345013009059633421290241583864468453396484606925071369550998772875840640325758308835852391176503689677263605949075815552026731067384737231681068134099746550363063940273625924224721503126912810251607546172009765059506591787282558727077669973711491157840340631805422942099954647016059576777054339588421998882440726473698513560202030309804089250300097589174314677765341104767702983421063649104691583044460507666600260994707192787133590502137391691330098102374713996115782701417107878938473243874299874872852713499024851414757892169376458916467621226859152075901273014182163212783658933754507272478777304254191033562324994395916168496097385872331012258027431094381'); INSERT INTO num_exp_add VALUES (4,0,'5329378275943663322215245.24931765987630429629836382184742348353920297283690739124220773955591340709935970062776650204659187764581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (4,0,'5329378275943663322215245.24931765987630429629836382184742348353920297283690739124220773955591340709935970062776650204659187764581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (4,0,'0'); INSERT INTO num_exp_div VALUES (4,0,'NaN'); INSERT INTO num_exp_add VALUES (4,1,'5329378275943663322300488.64471790965256505869684245785528331091076155554650629138833809683459634328609777839510066435612911583108717191216693735823717997111970662575497378762952496582183738308720094529950793570383580785385569873278068217936841324404119828637880370718028782103860007754579779716996004352284614661690063919125301052941328989181561787543541920734755989452320799185700078241880935083616978140555713297241612718277766918005268951861880490889884082730841740604517529391011862694381726143520658746305661338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (4,1,'5329378275943663322130001.85391741010004353389988518583956365616764439012730849109607738227723047091262162286043233973705463946054514004224903034208166782419414876904468730122054597840936856190652484801633363526576955397606531892764306099068756437389060626447578949162759295501062154826802212022414257953494004665588557188694447110384853149054690655645134564686305448219729651828678220200218922790293483596988037990835533058983562863141746692824117439019450865871047657552800448629502344444081260036580660700595591338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (4,1,'454294299613767152878025320780.534199313974295807138790763501115780294529340799108297697573066187975311338382917022391830256203305238757334106943821060545424417350991354829668286194840925251162479496893943917530660694097932059166013476064988623431110002057735318529554555260199417935495388243829261809007709919225000608711536928171687251088217591210419208480251102484043683131687013687838713055660405381318396419588727500715930145098362997142075433472039319292466570912777345841400769387321465602989947078951135489852486382469990409873227894248208197179481868230244584527040573428134962626267135732247029762468417273891700661832893497067151409134724061246612631376075173287264787886064622106855886785805818642123776489793586531950438285720668411465570116161790343538663297713926678759640594912243360541590368666922379919514826022141331900181'); INSERT INTO num_exp_div VALUES (4,1,'62519544780217042176.800424689664850775296526267109332647921183817056683200043718160298562843864918741523494444361916531159341418970534833628106062976341639276761669219281771109561175175033739624472497927501467465456946098280878993371659461957361369508794842102784763955539708800574418468150309301129490186416766691183270872711413796386178009615777589066235359283212636467980113350635181915492452697347977967985810294150853782607014649150457138118264698071689065469752702524632313088938504181640435324554007553994564705401249228914199354821595855823113730697333390936834057091883654016371107974899726642500486005445063301647520527084320363513388355471718583708935211830796440056542408492723718088396437530207347815505844074508948817594746824098278470533148171941442049323578854023683167934569551595335539887777638716651319134577441'); INSERT INTO num_exp_add VALUES (4,2,'-994877520673428596810678826533995.79421257464236160757218576989993781147390382997132644206786872350652200243563770552469933194637146474528320738725486418004701192337175478117026439697031462361180324038544450723753402846519731908503949116978812841497201119103409772457270340059605961197538918709309004130294868847110690336360689446090125918336908930881873778405661757289469281163974774492810850778950071063044769131228124355961427111369335109426492177657001035045332525699055300921341010989742896430768506909949340276549373661076950964959025967328861569387160956730002517417236732463510495205173523163676450203614971844583064927040066684531931069310935516821795449174271052747559395296525950219449541557191520903507653089998307641491381797101485104546410643'); INSERT INTO num_exp_sub VALUES (4,2,'994877531332185148698005470964486.29284789439497020016891341359478477855230977564514122455228420261834881663435710678023233603955522003691551934167083188036585971868561017596992548582038556784300918537917030055337559943480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (4,2,'-5302078674303935968062773235453828254014583744527466365136.236414807326868572353809920518232561005161225922028750078608989965741402418802255050636954800114792425419735155504035469350521800895164087027043476055514245942961100610551646034472084954313670284875310691807937254054948742125729353864014122131419164449567115006621212424805182687707372956385102095255735458593389920872596796806885847543910224476727171570873698525606016990229936284811067826588349092841322512643043008589065847223683467371925773023109720951609815041012521485326120380123169545818055967455575736140138663815073081494226676896278654189873597341203197903408668523514375373841493189836809506003729379742035629498519683885268256481104619815130659628225053833297766479068686119691010593208135616363994230674606991733148502293102108193522604968743948323130517040609601859735899914987426089053869350663'); INSERT INTO num_exp_div VALUES (4,2,'-.000000005356818439105666775800262590702859770599410113087721172791624002387236505438218124867814437523686300450045582100868990117124343222534568799037421944272316277130975314766456260710406160143182498931595199129228915695802952695510723443157825968340043198200740606202264287904755124946591110599335909404657109057432686191440989434662797205973563889238804413861126260401987949920244286377128599413927273444061572120561496904543200956508673923547626768641271397088562966176629018606103663605145666976048261236691866387601532424530473754175270500777679603569715192364542901360534980926452487443629100484491344001509360344122933911316486556042277769848194790964257060927912344609376571637126617813506411190014141992988288983968823792971270853369317867326071952900448455162898476163801382836761898292684175721846'); INSERT INTO num_exp_add VALUES (4,3,'-60302029489314054989387940744763542234.98295358053252401308872309802346144227050959966671157134780970446370197110016237152333448347415674483796371931316021552756816073493808344537122580089676304958104270609762310229182150728136567294798680824019082599362332377530165818229609055765904048195574142709698758095302560470195171027219786996322461803443213101532716728918363951912367135900414238535625075942525108530051828834829820554490477645701692374399416239080329365045332525699055300921341010989742896430768506909949340276549373661076950964959025967328861569387160956730002517417236732463510495205173523163676450203614971844583064927040066684531931069310935516821795449174271052747559395296525950219449541557191520903507653089998307641491381797101485104546410643'); INSERT INTO num_exp_sub VALUES (4,3,'60302029489324713745939828071407972725.48158890028513260568545074171830840934891554534052635383222518357552878529888177277886748756734050012959603126757618322788700853025193884017088688974683399381224865109134889560766307825097103477790782590061456916367930139323346273315068375646692125800496305291080749834712822775973790354498408104142209966769395239768969172107040437333428573572464689550003374384624966403962290572373571842567623422963022155546431883766327294954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (4,3,'-321372325955692885069615337209737469749246561535004445508427591.072860243358366933071485495726715620133686420023451450292996945184959542770492705998350644739298629407567812798540119555932604687814429669592481327761428042980782672136901602006622227365754036664912989085940235439697789102358431343119457114603363936544931303133371137532006899162833369543279729021228901466728220729625107362063321334489394782322741444425117731922691457341543446841167138481424319752111748042440994701571955325673470021626946676976482516292402239416632497972073915818846704053624707839813514171497746804751780741682011937606462260710753056669269928580460921188286249923152921382198282201761171043384698319895970192114563900025573490442674225227682235790590616707857188385274186584856872573669591460447105688151281208238908470285147895678001948902280493477604361481216667716971590499226735103039'); INSERT INTO num_exp_div VALUES (4,3,'-.000000000000088378091435340426596348183959201660680284222502095357746364378698792730669202270228092348823133529449019715406417264278615046537007844589547485282959556860316942508808911542109265489435572674031608663747132688980867386885961271358592278360097086532747883342438036287136994589308551796702164612609710942175900921197001888540314760352113821737014875886635147123114456910985089625906448913621495025509697742196814421833448856595853403450682101743559369637786458968714240975228615283970739279506239628546165569688434254286341567486905374255702980370754235630955328837646999003123103831262789115646588779721625156078607919060762857866951417867378220773543985422722165221371084387943737083254760594128718841665355053236168688218864433967871311858292181233490194833547273501436630325295640020916257836404'); INSERT INTO num_exp_add VALUES (4,4,'10658756551887326644430490.49863531975260859259672764369484696707840594567381478248441547911182681419871940125553300409318375529163231195441596770031884779531385539479966108885007094423120594499372579331584157096960536182992101766042374317005597761793180455085459319880788077604922162581381991739410262305778619327278621107819748163326182138236252443188676485421061437672050451014378298442099857873910461737543751288077145777261329781147015644685997929909334948601889398157317978020514207138462986180101319446901252677846098070081948065342276861225678086539994965165526535072979009589652953672647099592770056310833870145919866630936137861378128966356409101651457894504881209406948099561100916885616958192984693820003384717017236405797029790907178714'); INSERT INTO num_exp_sub VALUES (4,4,'0'); INSERT INTO num_exp_mul VALUES (4,4,'28402272808100253242547006276715304015308580784958.804614276533085644370816876160290159450291717634111299841065255625515058118012211808741402904995080624675460593676923639082981788732031193774047612589113654423166826140872334380708795266307037944059108148612979119729408762532396036043629484049508789880964586236575769826806092391573178899640321403656891487586452524427223891405519836671312830183895761747460911777623703557946796784873885800089025388390522992806365773290733075927321101736155663727528284512100509273076328103465333687228713897893434161293693971954442699482857938492961830350598789444266860160794913830991304996676299650460125000959751177037694425217989910261807246272771711816326991282202653917488360776928533800529297474279497910326579608191975246060946079639658615178160271122713225105861574160788280907842327681375920919676063500116492292319'); INSERT INTO num_exp_div VALUES (4,4,'1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); INSERT INTO num_exp_add VALUES (4,5,'5329378275943662669459614.81475694159581596077242547133292502869630735172901157043010370467618244548786897684821457816189831652076071977025794948484549600736179389638319303817478693948215387894509009504287664213474693208847025374388286162907794727810231557001266897729978691844410171412189947386181530441402903608214502713480332746271552746231631136145916685939539173054989927058122097304419584979598595477177513004218594211597809300517607260841648610322863666300637648662611916496850248528515936635845594390453288113296413254893687029540384176335735114863908372780241463999450547422213639667099644505472777149095004849805371205203850993689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (4,5,'5329378275943663974970875.68387837815679263182430217236192193838209859394480321205431177443564436871085042440731842593128543877087159218415801821547335178795206149841646805067528400474905206604863569827296492883485842974145076391654088154097803033982948898084192422150809385760511991169192044353228731864375715719064118394339415417054629392004621307042759799481522264617060523956256201137680272894311866260366238283858551565663520480629408383844349319586471282301251749494706061523663958609947049544255725056447964564549684815188261035801892684889942971676086592385285071073528462167439314005547455087297279161738865296114495425732286867689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (4,5,'-3478781676337858247983014311182511.567538638808357215203593479841446379226774481291286361639429856698999485760647422501864626078375852610019829111004807806660731243672830787729048847342063218718651165150612717759770504648306347926061960607388621011846314969634048226452709389995594961695723139571002939804473057725442880410434039783304583526414509590532906062732322732569475349107437896717416548237633532805602064623969799081086996320156575550896200848758685986331692388099427314008504506503745527468550106879602399030419569897808150076298414568875477195447656904373310322813412927463518325927626891046356679526447117311923853482118502868148386882363449163182892615259995945992014431502761210899772725227648729095696228388558331052524469604046072203605897109629560683446827492904111565278516043939137760721315953500281379039771826554155511347152'); INSERT INTO num_exp_div VALUES (4,5,'-8164430956184510.184223536017248184022252663660196916321116266103608317725855237211273642694947892658721606226082017525816544904635887836163201565923338826779819876742736219975639586566502584026349778499211535661173597356253186281116862244165796632756909578140184577853088376334255860281874385669242675881761388233070861374295536603371778669602656670852115614651462552069294889723058758969660566508798011830996965570446030123780674316363670374970480994905368006454513642480180066435609577311074332150098288374616437489163254821095377348025470309665651059603665062887597814064136313866690824972464351274062540825405003954064175728198182815347642172934453828192850870808373638597839434504241236228591053696481146252072190903430582534862988719805163692697482513169856291048966811374872266165034373412719593685881972700171726777938'); INSERT INTO num_exp_add VALUES (4,6,'5329378275943663322215245.29625473207137544719284446115519970394719946335145777492574745992986971075733570324679065009803281404581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (4,6,'5329378275943663322215245.20238058768123314540388318253964726313120648232235700755866801918195710344138369800874235399515094124581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (4,6,'250145412892811547138949.592621291590152419206270097656346630226508074074623894951308487425470437268130465956063593951784820669318897182831355375451719125809800516979013437732298382708070979871283132689492336823087794373113039154669229889503700598930220858275174342776478898670277868700384853696009897221747924643343353942154528501454689084608965009561564638167714973711022212547096732831847202912862290958304510651828842182545311077713664465815992616213663619529378061133917572474298028065850515876361609671565914027186063801852554353160801534696062207299890867876199323530337336273950892723090754719547285920090419070001019943385293110663922226230169381423410428577990604776655422105400452217085311617728003688836185608912367677734364834577573255789160419371322775733777518997638403409000055707558465286469808848200141192627396502735'); INSERT INTO num_exp_div VALUES (4,6,'113543048739697485358574290.758354267447744932153707340542459183720907885610125346262898114677742971240785031722334497858930434531517077525413654346644836353208132641713415396062580605566225794048569430676355036264762949452090151450855446984773994337170590068740235544320694721909983307239491151139099779296496785240814600627140543144068640768857707110930453204162312973998304574796413938461971472337040811785231390930046688391955000749644938061585377150632133417156866197053052425576957646564943278156977176976876921235395711611898108821587442609611001702344783440618040704066809035404237786023075676374788819144406909313755996914145273176359246052899650387182222905558751208368173052381982668563471143298720677965028880626152749773712037769548408324298835212547215352657271696665387200792785056233953536347605130973626194099064678842085'); INSERT INTO num_exp_add VALUES (4,7,'5329377457009123250369503.24931765987630429629836382184742348353920297283690739124220773955591340709935970062776650204659187764581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (4,7,'5329379094878203394060987.24931765987630429629836382184742348353920297283690739124220773955591340709935970062776650204659187764581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (4,7,'-4364411947278810125327066890819882483326918.05664098958260550284395870948992407314161088028674246708928421994893923699743452802989464864039994566042797942433140378990308345483670828497915478397481687305406460330009319949623844175096007381662809083363069100235985794575399268709260901964834244796150883807308976949196661411035264619638771824190014274817662519438658481432363824187693821267613212631153175155634316128036152465184903927860719447693468054624663668062006049759837326188252927823612718163916100588143128358998656306593393889422386501730237442526450419990376323903182669190482615734972147533221144682538647497701130447816148459762464395194383090936159579764712919396391813914821973715879062992249315474841639591907249142779103650773383644785606333916967894'); INSERT INTO num_exp_div VALUES (4,7,'-6507697.520580964829176145824902679560705744817573189143227837387224410616222039115571544850095278317993922427931439719549137387753697989249394347047436951117850128104928719365703899136632100669607126357491484781141296021264049762417528697619931558728863308905257358126654378784709213859234056696519305650316810797382293500878834933984458810656133463638442959750083607649924453935287420620424368291770694630751828333903156364366745210911640207075765008558904788350844410055253643515389003711759818446776538393914018427075074171758415188027562645239606914126802490579848138218395145734902830046359100742374008993296019987093605275289913663224324033923096998194326249508491872193747944673057257521552387923218450155737056841633810711295424578984452176016198348344913655301417872189073133147510027427530833694019910340299'); INSERT INTO num_exp_add VALUES (4,8,'5329378275943671819201468.88995490340795935797824952902333498786202536079000703830146057240651898748760197658486790165425772165585380839129948178510273188565692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (4,8,'5329378275943654825229021.60868041634464923461847811467151197921638058488380774418295490670530782671111742467066510243892603363577850356311648591521611590965692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (4,8,'45283653791262997781451381354094822.762732909505051438036873220502792213670540454778361182993875916509061144859281577740137081988678361247725064336120451090222456518107029158304937620179032477664627949959143233370320432203497828243297406462513350790251761540074946469824444452248386782451723637769289822576372357189700319768797708375563651655860093365309717823602754924352327588945034832436331911584742966378275504545736896430718939807674966738116698454215555860047859161126694019895490767779791933882712567492115664113775047192011252893773389940988533801360010782816196288710063568554147458866942816721046004257953642508395867837127678980002737669139369781058046396738606563716339660654364541530532834806205571191828994250708412638796240377704994928921528330863683630622922959130920715261879547446054261914770022377059156125037157979236658010950'); INSERT INTO num_exp_div VALUES (4,8,'627208063620965.397582272040628872773601055303353339700043792111288801181637510303989399395425313995651311362368773096988861977687484912995632130587762386590996099363383976320342247076516604162469063709298438133327434461462906199160715395064249299615054970359309619951777972710299484596875999967582794277241285253106817446259313281064844416249524876385699646393555435017820686376877981018047574348711991428666249794623006175739581915209218834701034964043360823844816042368184094857692062884223864639972005010863342567608351008172649209459933114800143792514183138995700133608613158857147417653998048890116531052767737435620558349226865105888201598712435680481803901906613772821370519525404423549161696526405320391828194356063547089626322474164332505209233143121068245585662919687001395119229263995765376465304715643388771609446'); INSERT INTO num_exp_add VALUES (4,9,'5329378275943663377078725.59616792993138452386059664269485161374191901124632386474661634799161523147237015531446709484039091244606359050341194730653343894986479159670583937529516163204904273806158788218327396375034882788180783796976731912141525319602448709213495905899041406302673881364465504945113279286939663215197485367850132991968081639290297033476859158044889351836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (4,9,'5329378275943663267351764.90246738982122406873613100099999535333648693442749091773779913112021158272634924594106590925279284284556872145100402039378540884544906379809382171355490931218216320693213791113256760721925653394811317969065642404864072442190731745871963413981746671302248281216916486794296983018838956112081135739969615171358100498945955409711817327376172085836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (4,9,'292388240303165948041827159734686.255558469787242316676287235194652580157149226950109397295920730296960145548003120827363226435916209781396711693581454960342091452830648929118261388933297036933167543189308061917640517578583521401267417187854611829815212778183983326568586118831109538377828156118900313778053576483381085207892754728937946691892849474364477434665960112125254104966566712906532318984871145605839506991591027939136026602051635433295687547552796828217859648186757719639965988287173297286034098497871707197092627676226053609131138590878743560287292934815277894463305001278326023708395571840850120055316276256138004565442099731931051413153564744766098053176049414330146267604802971221161572130161432525297614616942172815141372973870720928125699420370428856022295499447755488148545048400795053604349570217878099721865670458104653570360'); INSERT INTO num_exp_div VALUES (4,9,'97138902640718538.241246716463110895614166618530828908023040947887095196830690221211560526562522274118188963051412359798837957512805692731972838989047910709158995922699598619854907969493232150042212406549916252602794415099066259707018021422154933830674786488990033885447289593742424717170197810316367637885248684134204152352748803532396210051700193575105804898183523770153431536054848843504020390623875664696278263569145547515663340450903772852615789980257449146000410036925975898331113013857953289990299253584950458042598491897496393582249411290555264437893099880371008957017323366523688894303458743415715114628052487518110654201696604914159777300997374156315186315524817636714210119873791848535246674326877611945112249137224923201544452904111118569299934059002046318394345055859769572070097973298522564724884895879226870720839'); INSERT INTO num_exp_add VALUES (5,0,'-652755630.43456071828048833552593835051449845484289562110789582081210403487973096161149072377955192388469356112505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (5,0,'-652755630.43456071828048833552593835051449845484289562110789582081210403487973096161149072377955192388469356112505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (5,0,'0'); INSERT INTO num_exp_div VALUES (5,0,'NaN'); INSERT INTO num_exp_add VALUES (5,1,'-652670387.03916046850422757312745971450663862747133703839829692066597367760104802542475264601221776157515632293978442027199108085723617181683235487266149426304575903892721468296143475297345699313102262188759506518376019936160961709578829069446312051432780603656651983414612264636232727512091101057374054475214114364113300402823059519499217878746766275164739724770556122895799337810694888119810524986616938847385753562624139431982468828696587199570410008890188532132652095915565323400735066310142303225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (5,1,'-652840873.82996096805674909792441698652235828221445420381749472095823439215841389779822880154688608619423079931032645214190898787339168396375791272937178074945473802633968350414211085025663129356908887576538544498889782055029046596593888271636613472988050090259449836342389832330814473910881711053475561205644968306669776242949930651397625234795216816397330872127577980937461350104018382663378200293023018506679957617487661691020231880567020416430204091941905612894161614165865789507675064355852373225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (5,1,'-55643106304872.575994253221940844841058071061962511162776681458310912066379595519265546225338405882027547140476045378015935579066580347282075024392379464189067155567624835346798806677988850250198082355055954078446421075165109896091047534711081616362392995575466807084807876544560268050611445006601394735810211678919646667455478469014906335433468365011768049600750224822391684377238242162320161552720449713229523135506671063115436813348612986916614320012995541575293478341408982118538094438068036422562665160411591652618670802973618768526197813319204816293073794413317669922144705633308090832805914096147659820167569140291210526520361556881576175809360614782817717579318298657744021133210954279487777567785280633309576696708168342539425395482429923273623865667723482418178781573723597156804085501875735112311466228778929147929'); INSERT INTO num_exp_div VALUES (5,1,'-7657.550797567691019915353529993301413746369700087741672762343206271266232635965032053368224472333368713006346867984576168784127503674579531243603836945595880917241997606783133673324236134063757452734295148763280059050480246827193380861494669624151921824660313516974440913733511526807313019192263170823268678149435664224184903925632177789052038092611394447709922076676981043877747276056677801802695466205531230350209787298926245402046182150996849906836743231861317120171583577624262765589605263477198809166390259128339127005924586833372241946051704497188891325715185091060185547236923494393813210904033520844572880475265306843414506359253445517738473745552980984097762509546161690823646176501838559393690565709795724159196133663168004773260451322595899506776323262195323943138344537866088159583331807728944620284996'); INSERT INTO num_exp_add VALUES (5,2,'-994877526002806872754342801504871.47809095279915423939648794226185974985600242391612965412218049794216637114648812993201775787765690351615479957141288239552036371132381627958673244764559862836085530643408020551049895730005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (5,2,'994877526002806872754341495993610.60896951623817756834461124123286284017021118170033801249797242818270444792350668237291391010826978126604392715751281366489250793073354867755345743514510156309395711933053460228041067059994425117350974491367099004404995846913641329458537237789584653041949090121498951516476399288513593944575192159570458664608461677113504914551578443229008454218964701550932948083369656042643364608405637360180021322967144409944099438498649645368196191999692949583952927486593144959284443545794934667002661774373364219852712996869245745722896071593910890197478196462961042627387162830776094709087748993678069776845437889735782063'); INSERT INTO num_exp_mul VALUES (5,2,'649411906691138274293985410502516861224852.2323455192714410716272307781034189160865613770320102043319541634113746032638191509585045862973333645830298922352816245477556264222094036953195419857712804755170632292914187367964994214922001758104594052499795564860466055599417895782179851297585155129541589802249540436678824225950907268084876110445460948679383611117263673106597132046331719468816839434908155684738864149955129235751738204036443603521478609787295079710078973503970964790273461142497259987849074597264522099648376356902360358310245001183020992360260836105404118742418040965190000718736837422434593694808973939805954329718232693154128543253581495885789333274488461716809104532693754070810202831113003978085636579574171344721710232931261731022478029314435363413498991740750878099825781577297965642009156858479681236085226911858782115'); INSERT INTO num_exp_div VALUES (5,2,'.000000000000000000000000656116570506105776235076334177868550033347254561166417969910286926369599900073757929714260350320362090452092025380232792749476245042480546813848702351830607516880397305138543526307608094143028291193163613755680419049060162928958489964834941920423432354996040147818253087783193280640282263490705632002572757216731766513434035163528102590524432221718194164133959630768718395847710529339782880381264265894322494716854757290930538739000043383104085867828258790010654331660516512156519838978751447311068903958136482041673109857552178367614498426226323001399275980281507353231821022591045797658991388304873240910526149138339658220844723880158150606035181559877351791752701872877147074033569061408920725522180134133183999181370354585872214368766629114773129541658653693832843354053701079334077'); INSERT INTO num_exp_add VALUES (5,3,'-60302029489319384367663884408738513110.66683195868931664491302527038538338065260819361151478340212147889934633981101279593065290940544218360883531149731823374304151252289014494378769385157204705433009477214625880056478643611622410268943757215673170753460135411513114716313801477916713433956086133878890802448531292334570886746283905390661877220497842493537338035961123751393889400517474762491881277080205381424363695095196058838349029211365212855028824622924678684631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (5,3,'60302029489319384367663884407433001849.79771052212833997386114856935638647096681695139572314177791340913988441658803134837154906163605506135872443908341816501241365674229987734175441883907154998906319658504271319733469814941611260503645706198407368762270127105340397375230875953495882740039984314121888705481484090911598074635434289709802794549714765847764347865064280637851906308955404165593747173246944693509650424312007333558709071857299501674917023499921977975368196191999692949583952927486593144959284443545794934667002661774373364219852712996869245745722896071593910890197478196462961042627387162830776094709087748993678069776845437889735782063'); INSERT INTO num_exp_mul VALUES (5,3,'39362489275784146262776411377472433635883331946.794473520543457442955620133347015506556162839462623905489255080102447195050109095701660164272430316804466254467810714209179752718730906325952685817112992943656292503112803950215110778476301809440329937774061163668461957943313261962261081942055908935814323069621279128270849852239727888939033546870208376394878842958202403235309372240005941467570230067124830916866857395233038346727879951123599893174252558078732888910139309038957525961212820831321973219557165558911222848692996406741318948607549825343491479728117062814094258484536263158005174429922237853707635743736923521032098496725445243775790161216159399180889906705265012270270348146530113428221072591696851818281866095288773371414866822270689959827332258348570976075184933893434327278299820594014788148344260948638847457822697682605612771344335201258128'); INSERT INTO num_exp_div VALUES (5,3,'.000000000000000000000000000010824770508763323320533297369674519056450544793568147911931789010432012750062661590994728968589403602468229106206242395792957238667714358401601098858606386995096923432407249369639633268143022787987190106724545750803196130511146323174462918572423414631798141263222875752767731279138952850500369328934959764805948568471324562210715908420467881411844098258193571194910997918428786213948547748701831331312040839544355427357749520227124858111324859160114175254197992204974033767300989488517391063188153561391320190653403747521648794370679322504188364455328709488846777004202196382575648619395139553279192346251133156445942281048959845827006761160755031086836046398020850814350246219929303018051720203943879538087954853996826539712240458022307680912400297508925714946398031304516583939283'); INSERT INTO num_exp_add VALUES (5,4,'5329378275943662669459614.81475694159581596077242547133292502869630735172901157043010370467618244548786897684821457816189831652076071977025794948484549600736179389638319303817478693948215387894509009504287664213474693208847025374388286162907794727810231557001266897729978691844410171412189947386181530441402903608214502713480332746271552746231631136145916685939539173054989927058122097304419584979598595477177513004218594211597809300517607260841648610322863666300637648662611916496850248528515936635845594390453288113296413254893687029540384176335735114863908372780241463999450547422213639667099644505472777149095004849805371205203850993689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (5,4,'-5329378275943663974970875.68387837815679263182430217236192193838209859394480321205431177443564436871085042440731842593128543877087159218415801821547335178795206149841646805067528400474905206604863569827296492883485842974145076391654088154097803033982948898084192422150809385760511991169192044353228731864375715719064118394339415417054629392004621307042759799481522264617060523956256201137680272894311866260366238283858551565663520480629408383844349319586471282301251749494706061523663958609947049544255725056447964564549684815188261035801892684889942971676086592385285071073528462167439314005547455087297279161738865296114495425732286867689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (5,4,'-3478781676337858247983014311182511.567538638808357215203593479841446379226774481291286361639429856698999485760647422501864626078375852610019829111004807806660731243672830787729048847342063218718651165150612717759770504648306347926061960607388621011846314969634048226452709389995594961695723139571002939804473057725442880410434039783304583526414509590532906062732322732569475349107437896717416548237633532805602064623969799081086996320156575550896200848758685986331692388099427314008504506503745527468550106879602399030419569897808150076298414568875477195447656904373310322813412927463518325927626891046356679526447117311923853482118502868148386882363449163182892615259995945992014431502761210899772725227648729095696228388558331052524469604046072203605897109629560683446827492904111565278516043939137760721315953500281379039771826554155511347152'); INSERT INTO num_exp_div VALUES (5,4,'-.000000000000000122482510461124748279475400009367345900846466958806966807399903713411658400733717078392550780910604704603123670767210550800752620037863340961255721285160854785449315208955654408132775022766783343331151895973970395232686910362226184006990485313002943710214511418310741271074710741339586430026286272098156531835438969774325517509155992092194349661122678547097423264670055720422496527272118788005921590521726691666219504214087867030003203385360001614199656989667055583749577099440092378355805901262289841168751608673297446473709956390142112843400255748161809121986096092991616144443486023218404881798896685413932215981950393130292001833627899480153863300557853617312991880655905907971211246077450786084079040513198340644157868678782195341316027563717617074364438885981635394382733697473265872796207'); INSERT INTO num_exp_add VALUES (5,5,'-1305511260.86912143656097667105187670102899690968579124221579164162420806975946192322298144755910384776938712225011087241390006873062785578059026760203327501250049706526689818710354560323008828670011149765298051017265801991190008306172717341082925524420830693916101819757002096967047201422972812110849615680859082670783076645772990170896843113541983091562070596898134103833260687914713270783188725279639957354065711180111801123002700709263607616000614100832094145026813710081431112908410130665994676451253271560294574006261508508554207856812178219605043607074077914745225674338447810581824502012643860446309124220528435874'); INSERT INTO num_exp_sub VALUES (5,5,'0'); INSERT INTO num_exp_mul VALUES (5,5,'426089913064020811.057708378200224487694731586862745370027417544052374884336177893807736467646454486029424673621605232432043672119510371547153895504456723242262639262542904151307250842477327375961936454637964429999741717244285121019840463692418987118402683746281993192269229200465080358289645050337976214115902915692028162689089167194843185708212911364017271332623359100711545479273675423617018342297822477514128997410642005300368966199980354369928371655155437291469427189561877718971914040675572136507472590254222870537216617260612835805368361975725573009455402822669103118872235140158440342063571894152305875004532651814592458133460160514384171804043127771746596286988679698684698755896736275307574630777027620558428909546664763675431701332632828281070572045822129984625797185173815273651376003614106277727279230096226977335510'); INSERT INTO num_exp_div VALUES (5,5,'1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); INSERT INTO num_exp_add VALUES (5,6,'-652755630.38762364608541718463145771120672223443489913059334543712856431450577465795351472116052777583325262472505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (5,6,'-652755630.48149779047555948642041898982227467525089211162244620449564375525368726526946672639857607193613449752505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (5,6,'-30638438.151446159804025029882398388155309149089870990062944469684482366692824338098201222171115395923414887930224163525189097571163687285244255335505387733673499447610577050114902372990462064696637481657064525319516004273769831260452832960893174173254560250804003884280384718123289136453955482855362019158401218620018346500189769819687260476334734259702665316562988639223597110627626759216850014150105605927773639897638043177685498804811787888811168524202700283461266793154726325540776914500415140842975457394524215869103737379109516024460317825645645301237375972914247141703084877141866316168268901439172491577729880760950895760711857112463508064820414904611059588717092145484656103798852859978690742216940980929562068'); INSERT INTO num_exp_div VALUES (5,6,'-13907037655.047994416383638650569341223199042786813441967582376077478024677494832069402897226848055043557486983268019376307288565911231748501636517992289743940159005664424461285010295150828744259113760652210086696250085454819340987566229400805422509198052317518991183515696724846560872057916862620762789778660622787735923967096950195583369113574365386627110408307941105082873469072519133330718161987781080307947247163619814890462416622144825161521790673339279047700672881113718394727610096366361422482794458375587355933614201638489194194834709433413694420512869179976485096875057742460003147602405353823942488343056906912173170809084207937229591627643451380735179767199816663168139837088183577975769442341678933576388936845704303859241320794255052627716474860113993958556604381707826493168941926878481079724185426298004604'); INSERT INTO num_exp_add VALUES (5,7,'-818934540724601372.43456071828048833552593835051449845484289562110789582081210403487973096161149072377955192388469356112505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (5,7,'818934539419090111.56543928171951166447406164948550154515710437889210417918789596512026903838850927622044807611530643887494456379304996563468607210970486619898336249374975146736655090644822719838495585664994425117350974491367099004404995846913641329458537237789584653041949090121498951516476399288513593944575192159570458664608461677113504914551578443229008454218964701550932948083369656042643364608405637360180021322967144409944099438498649645368196191999692949583952927486593144959284443545794934667002661774373364219852712996869245745722896071593910890197478196462961042627387162830776094709087748993678069776845437889735782063'); INSERT INTO num_exp_mul VALUES (5,7,'534564131989234694540350103.27821462973515555648644772098605028371173048154132108733819196629002548296868548691993248746628993380136454426833349407578676005545111508293942736555269938962058196496152360848131645787941032968937794930046928523006455386861100809286408671908320322523368135203881520526880998279355848280412933152306299256343179622513731096363088094541514890135766460631462465021694553063366717467560655272004461368865264059368514271105464855575429914212085797297268595943955105608543373940035636033207568676745293499106348500559628723682588033431457023964317090780615020801564861497990103549650624438425421690193862533733474254'); INSERT INTO num_exp_div VALUES (5,7,'.000000000797079129642393611556079160915147221153735075943759104977169600937534508973732991117540626046659124172765761873705978811124901421049332579161931652390647472911517923131800238903184679028518657818755558526885018755394697157094867449047655737107085020874974955627907737126958129710597811740696534189608639914753884882702680512272194316887744972931453458445314561564591875764930680945589486999586667912816485821717403892703364322658245615895415781719033810595358092343690359557942948213374234065052300866661453767599465059289920067095083062096458980564265691295895672503728815182981118876144075942348853666085714846210822847053889733510154276933759200630639642310562242207518883342516103725757482864105340008709446643820864294556778969997115586027866760708448174502158738150605938364482719960251612464993'); INSERT INTO num_exp_add VALUES (5,8,'7844230593.20607652525116672615394735666141304947992676684520382624714879797087461877675155217754947572297228288498221620714146356962938009770486619898336249374975146736655090644822719838495585664994425117350974491367099004404995846913641329458537237789584653041949090121498951516476399288513593944575192159570458664608461677113504914551578443229008454218964701550932948083369656042643364608405637360180021322967144409944099438498649645368196191999692949583952927486593144959284443545794934667002661774373364219852712996869245745722896071593910890197478196462961042627387162830776094709087748993678069776845437889735782063'); INSERT INTO num_exp_sub VALUES (5,8,'-9149741854.07519796181214339720582405769040995916571800906099546787135686773033654199973299973665332349235940513509308862104153230025723587829513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (5,8,'-5546455599206321494.0676583421119904300307105296377723816472192007866147764761501865875232824814135783697976183493106885436876081315217834621720906478074798596116645640251460842350553806256223963023430631066024389364515688765194373161385579258482225808660340732705687558150699172147896486727530192499184101617379930846663835628510376484675411350654979679181852179924386290069790336316958202582966248703889464308649631486542724072047294216362186036638115240070658004553260251510288423749333873893917690832829128021808383128393431810674177390352413548658782609064839524756041501835115152819802758773711821322162752064589750295542985780512921839490040396053737870038534216948323935020460307350020911362024271167085905714873548388570602799432705061561572854498075600'); INSERT INTO num_exp_div VALUES (5,8,'-.076822018213756690975099471985461347542955923191183223634407380481978143225129486622351714276452369661632980197282261508936298649901018470846144321441236073683990324039849865750139470288565622579952182053792815638469841531577235191276257498209844422440366423136595067535337374223115507557306455001792362506235886189722508617024948653046102060677266555476719102193278190540414934812073355995577639986512222998268934000209944414236509139290657402937840986061987219441410741189615344050459067454369371094189930607834375561948483494321255500497786795636801854613881105643003358210407867114145806225724880370339074242480071595684502491827709175732777776915682786771730423733673667248186336046898260378049328204094804755195626798951644386924178161926128482002518979482630732440619051262620098544265763306253807191182'); INSERT INTO num_exp_add VALUES (5,9,'-597892150.08771044822540810796370552966707032464017958269847934730769542644402913723848026909285133109089452632480800168074607090893991283808726990171062867538012237270000932798704781608969096508450960185964292594677356241956277714380500188870696516251767979457838109804726539408115452577436052503866633026489282425086547752714324273565900641436632912781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (5,9,'-707619110.78141098833556856308817117136192658504561165951731229431651264331543278598450117846625251667849259592530287073315399782168794294250299770032264633712037469256688885911649778714039732161560189579333758422588445749233730591792217152212229008169062714458263709952275557558931748845536759606982982654369800245696528893058665897330942472105350178781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (5,9,'-35812445701642379.972368737320206275515144213236752803936806738624588812089615098329765811617509505790110909629109400553415312470540217508070421816878544125783329593128638405659896184248784794258084116406472768709113030915308410565617764394827427154923321461158387012978726512246146545834669665093228316853342805604075936530371665576147966721599968786161939347726656168798065647411457701453987215491345496003650288850096338695703984042549594979897253521041581573388369367579323607093487743440894765114619634001789457486407909224339065748496715380572175183589195611952939575073075140094901024063428239223964510824958346570603142906309198033196987949067156046076497974760641964978711558209708743776024313916111738542765749928287600981397080809041007714387564206594515733287925008053261840295560398311905155157989225181164097547541'); INSERT INTO num_exp_div VALUES (5,9,'-11.897816658873986795664687519069203701902563457968097729876034796143085813450454323128600602495745166997629078984618283588337379184733369491549230343315369634754204412939757136108898254582353378508832611703989221079986765793923635928759179573599208612516427628403686659479459867527627014558600521732194240404211484706621458983727740143568799713006127585168144158660566534382037451913967363675002134687952374080694449905223371627606557311710348820900963340884001770733452314715448053233208783321215998063958966729954113843581448912079950334969908657535514847005768455377990262943747367245613296497099716892292154137652893990339292671106003657659470243633112063075297194691349631518467702876183897580432003030164590920118726657290102377710611324297862045849839571689192181090062958059281673245670440852080202548743'); INSERT INTO num_exp_add VALUES (6,0,'.0469370721950711508944806393077762204079964905145503836835397203739563036579760026190241480514409364'); INSERT INTO num_exp_sub VALUES (6,0,'.0469370721950711508944806393077762204079964905145503836835397203739563036579760026190241480514409364'); INSERT INTO num_exp_mul VALUES (6,0,'0'); INSERT INTO num_exp_div VALUES (6,0,'NaN'); INSERT INTO num_exp_add VALUES (6,1,'85243.44233732197133191329295927531563604777955507322414928382967007765263923984471408038635831036097817458527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_sub VALUES (6,1,'-85243.34846317758118961150399799670008360696356209219504851646259063690472663252876207514831001425809630178527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (6,1,'4001.075404054519813215296429095020391062109905613738157927030437221793757373268325953178030040276107574363822832168160758728653712686313134828282109532831190239521843808940611025488601517574653932032236616573457735900045655665690517797280666732780030171712864961531623060353548802466577910774711998056232872212688464691036260746751992072745518373073825852119460094113694393273456369345499434994672730920070410547163082189385645712866100999708173472360864669110044660667614583576570496399103026286828660558854973376227247132815728164629722965145778698957093136175449225024685874279280018547740'); INSERT INTO num_exp_div VALUES (6,1,'.000000550624150700285432940805295709861455424264970126953321538967550091614148982212874391026630805836518138806917934859138493583812313778188030836027246840794439412443826640206464415527687555214009725107630387889854278497875708390050387195108441635824296563108288712340902423706104029452615686971019125750530034798026103476074158922893374911891438688457439945897348811702908216883650280617098402133628688982793791562476980709924382381505517834196446365877784931355599480881104446907801805570471686295270927836995181422963320376948188855989986414581755633425437161760674162177776773597848142496583128607548351599750592863590334617838124741567654525843413232313914310487355539260264225486180000012813397807525203822863232682089295055713257835007742845010741137213301116647610033909062369843750685396196342928455'); INSERT INTO num_exp_add VALUES (6,2,'-994877526002806872754342148749240.99659316232359475297606895243958507460511031229368344962653674268847910587702140353344168594152240599109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_sub VALUES (6,2,'994877526002806872754342148749241.09046730671373705476503023105513751542110329332278421699361618343639171319297340877148998204440427879109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_mul VALUES (6,2,'-46696638263247522384986521136500.479312417066793299922708112595886608370451213741279484136907754744903470430131032928908162742687359367826808123516519335458861613010646992354378739165872253762686683966945711430182491860196341344982195078000259063231136011430995647812149294224699587849791008794261026932467933475782780'); INSERT INTO num_exp_div VALUES (6,2,'-.000000000000000000000000000000000047178744084866106587600962473825168237820701199970144691815329658682341685812472535816245052671243808078367856957579485152424914481414614360809698177236664771558713606961423658442962083541733004775309314926918118528217478256885324362912426275407382550929085958089798861918760121727491366034496581249711153289495601712583077918760003840368008056353090552282274780428335438032908213783490070198414584291402513547386013689752310173492320159738977752795528725029134841933604057954874523842273790958618375118974623107241366036640538085329921129023905888674299774726871808862832797230915933851225308164365269753526489223540580759951230801125605963901491073619448437890841032149898629231552019804656219062534881074125995130202820302133432951999011667568746004715268323913437054078537'); INSERT INTO num_exp_add VALUES (6,3,'-60302029489319384367663884408085757480.1853341682137571584926062805631087054017160819890685789064777236456590745415460695320768374693076860837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (6,3,'60302029489319384367663884408085757480.2792083126038994602815675591786611462177090630181693462735571643935716818574980747701251335721895588837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (6,3,'-2830400711649493468815157129316992649.40542786074520931471973065281957756940496588853021620372179463538053123396140685749478530925306163968207226329985017644835203709485594362663495728106061878665324856417118064730721101615473194292620972173690618491026470353143141125614124440035267592258385099934706896692953497971326605145704135723011753705907329979207428661473172503098296622281647255008204864404416199384701720347319806375450632245634238172654086373193251877533131784268854289406126119630708578053354762596511353053106459297339360827562281168219966099848212'); INSERT INTO num_exp_div VALUES (6,3,'-.000000000000000000000000000000000000000778366376597400971124059102619954214055884926284646546105035591052258074563706355894551049631537984053410850060739107742208523938741961208742831871056600773325053133977559789796700130019975964192371715826863472981072974742704091801166438465082519558956925444635729210849210496466189037623555622901738570979273502405907969114110345815802999687171113749364073269902319653450479463404003706147915064100959774312307195946966281098140229199529866429134937742584938255441169541436021827079647129394362379406256722903991353136733939395366152312959281905058592776286736536360235356737359904478313225848562436632109470589310799000750518904145312512621838935796912993778920622238202744037977772169066929474233952081158212174549695244127987299282384885288897893503991509410567351494'); INSERT INTO num_exp_add VALUES (6,4,'5329378275943663322215245.29625473207137544719284446115519970394719946335145777492574745992986971075733570324679065009803281404581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (6,4,'-5329378275943663322215245.20238058768123314540388318253964726313120648232235700755866801918195710344138369800874235399515094124581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (6,4,'250145412892811547138949.592621291590152419206270097656346630226508074074623894951308487425470437268130465956063593951784820669318897182831355375451719125809800516979013437732298382708070979871283132689492336823087794373113039154669229889503700598930220858275174342776478898670277868700384853696009897221747924643343353942154528501454689084608965009561564638167714973711022212547096732831847202912862290958304510651828842182545311077713664465815992616213663619529378061133917572474298028065850515876361609671565914027186063801852554353160801534696062207299890867876199323530337336273950892723090754719547285920090419070001019943385293110663922226230169381423410428577990604776655422105400452217085311617728003688836185608912367677734364834577573255789160419371322775733777518997638403409000055707558465286469808848200141192627396502735'); INSERT INTO num_exp_div VALUES (6,4,'.000000000000000000000000008807232244507937251856465017967626593430084223212999583902527587737263981869382895220711835510154989851222501080395520249593128253795609198666884523792646863341248402687314509176781281863891589925961900674092953408613128961234166906173266411035009516545964362406728942021813644419154548354247112601793685146960840364604115937119024575638240439041250900118977183124605578660115160551830946251713350556181960983267689939549506518185340972020820080460565392359379680036788592213479105831301723237102710863182596413567756605711230290883888612188805367801369264231165178487334557824054205160222371548005742602736713668548450400926514169967213301919971189065307721110805424950794015852531342286935114651278691214233054575660712537044810163930633456573860895791198853393107188289695511873068'); INSERT INTO num_exp_add VALUES (6,5,'-652755630.38762364608541718463145771120672223443489913059334543712856431450577465795351472116052777583325262472505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (6,5,'652755630.48149779047555948642041898982227467525089211162244620449564375525368726526946672639857607193613449752505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (6,5,'-30638438.151446159804025029882398388155309149089870990062944469684482366692824338098201222171115395923414887930224163525189097571163687285244255335505387733673499447610577050114902372990462064696637481657064525319516004273769831260452832960893174173254560250804003884280384718123289136453955482855362019158401218620018346500189769819687260476334734259702665316562988639223597110627626759216850014150105605927773639897638043177685498804811787888811168524202700283461266793154726325540776914500415140842975457394524215869103737379109516024460317825645645301237375972914247141703084877141866316168268901439172491577729880760950895760711857112463508064820414904611059588717092145484656103798852859978690742216940980929562068'); INSERT INTO num_exp_div VALUES (6,5,'-.000000000071906039575366987930696117572143566208825430801491864851999044659045681114433294052065377679745375399878664822361548237094424148992770296383642432040129230180142339557437679166815114510467763288057917694948929009212876391059413439647163295629904270262780935228234994930653489111444964446097124407804311494588517082748514970905563707392765567625639455978464081409330528324962333492925267647686759704415549221137291475247571296491073010175087298752769122449499990102435819414671847617062560524758344361194566796343756743243766853291113852464023843527189221162680613675369708907935197867458588904367993736363321133720345058432019986643353417257503619558797249295232894674255060861358071309619524800424087896023710729815248847792174290644245138831518072176198607255346603270853333176255533974364728342822'); INSERT INTO num_exp_add VALUES (6,6,'.0938741443901423017889612786155524408159929810291007673670794407479126073159520052380482961028818728'); INSERT INTO num_exp_sub VALUES (6,6,'0'); INSERT INTO num_exp_mul VALUES (6,6,'.00220308874624532134736695825088747995945783791378828770826401323533973395137378460250799184832278118133622563295093909508983301127615815865216895482784469538070133388154961402881325731054433770884496'); INSERT INTO num_exp_div VALUES (6,6,'1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); INSERT INTO num_exp_add VALUES (6,7,'-818934540071845741.9530629278049288491055193606922237795920035094854496163164602796260436963420239973809758519485590636'); INSERT INTO num_exp_sub VALUES (6,7,'818934540071845742.0469370721950711508944806393077762204079964905145503836835397203739563036579760026190241480514409364'); INSERT INTO num_exp_mul VALUES (6,7,'-38438389630389612.0042045464692275627184627672063157323631169405883031379129843031477339360597564128205768842448328088'); INSERT INTO num_exp_div VALUES (6,7,'-.000000000000000000057314803440765029050667129936880528769333499793237773980613524885506515999851858649385968476426313207429914995755091541422893944525222307473169425244462149015717526718376299808423552027796204632286454853167559026787019718806449038446612978917236245943248168920696452018925986743620392955122431521581268518101342690974749463089739042586011924590503136498488946387508310209984849243014542648765897536338824721211252335866349509669538308454367849024503312249951727948786393404944555844863805495937835281927012430439403132382055464307180153473189842433614777883826783689904293115204700185380661601223693428304020047393499702811581067120117405280772944184877279069842269329959037186324135435468322336398566440055479142909170224780318371473684868152271947368867666706912563225912012901437076773416'); INSERT INTO num_exp_add VALUES (6,8,'8496986223.68757431572672621257436634648368772473081887846765003074279255322456188404621827857612554765910678041003765241409149793494330798800'); INSERT INTO num_exp_sub VALUES (6,8,'-8496986223.59370017133658391078540506786813528391482589743854926337571311247664927673026627333807725155622490761003765241409149793494330798800'); INSERT INTO num_exp_mul VALUES (6,8,'398823655.819545574205652791249227663407026876411660299394659390409794761643751582473390322547798567169668246138880832642141417531427935520467563318363116897177899262525720710134129529640376020947774470933902793259531840625444267816319963200'); INSERT INTO num_exp_div VALUES (6,8,'.000000000005523967081937952184172713994498918048454262874017009201501812494019618863622631634736130436187167745347383745890248619882896153083428308074678908731005176810208100004498415662458272149380846809398637385270265351808328466537502823071145089961996689711299405627596294988646826454676198092260759424935699382655736524042353938814268760468122584678267125994645166955751211397353140569987758938572953312303398024147927938612934833827734142292697389251052485981023756760420972614486278837214553818521196182883489483756785207650821722660455451660719560529693418375773124813290305501923899840247103166971466167032437598057958226806335324315214908788839919408525748236713611579486768218564733151121028172253396652755590051310396973181595992981076269789287489208817712754098019817792758730835341151711523474207'); INSERT INTO num_exp_add VALUES (6,9,'54863480.39378734225015137845671346015520435061071252892396685718794832880965812803098645730572474084523997120024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_sub VALUES (6,9,'-54863480.29991319786000907666775218153965190979471954789486608982086888806174552071503445206767644474235809840024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_mul VALUES (6,9,'2575131.137912978352131546639620215541477987701194164886305951830806120142596646541302305984776928560906754259789485960991272272782091464270104432109904222200473616116525297615725803495463468272171161659654385929185160689572943852767523792651123455283534072794326647404332228203001469884016996499768656263775233430922446983838511590562929268821678518640501686017030536100955531423152839988008496919169395159653034847677470665418765966542111749439412'); INSERT INTO num_exp_div VALUES (6,9,'.000000000855524875533453524582534418967571681572635027972658867593464437484123442242521660317156546196609749230372398872487667521984251509483676665788527375343148382604836976332389890799079878151841905152004537926201190193814594954194044560537664560344224646197027029681984683465852110060077865421064400958821808374370779297676624123638191407441015008434084079839721156870032377372497814037418047056438760664237367081226979226606227037631073946209105678283624370820396871058367779887709720661001099338250009251834581804647326512873792849059661525874160414378459696930831877643599421297749483849526695657467708603491876916749718079725746259119898269814551222336219537198318796277931946529242436502235147453584237994498566122973953203597470078105606906752099294162422474758048436539653041606499637623370030079916'); INSERT INTO num_exp_add VALUES (7,0,'-818934540071845742'); INSERT INTO num_exp_sub VALUES (7,0,'-818934540071845742'); INSERT INTO num_exp_mul VALUES (7,0,'0'); INSERT INTO num_exp_div VALUES (7,0,'NaN'); INSERT INTO num_exp_add VALUES (7,1,'-818934540071760498.60459975022373923760152136399214017262844141729040109985386964272131706381326192223266583769046276181472898406504104649192224392653722107164485675679551050629376558940966195135841284978096687306110481009743118940565957556492470398904849289222365256698601073536111216152709126800604695001949246634784573028721762079936564434050796321975774729383704426321489070979168993853338252728216162346796960170352897972568238870481118474064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_sub VALUES (7,1,'-818934540071930985.39540024977626076239847863600785982737155858270959890014613035727868293618673807776733416230953723818527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_mul VALUES (7,1,'-69808760806266041400340.70700818693892852138813934414383886494691670042143650609934777814995087699409404201920249076407981012095999320858479644760715204999741683528746097757549835956359129287002171391961763797857794730120426599135099619822532290339000466211195776337667123320942107370731349851576864242697412616810236323676004067839744992733887503405311090677026008324895177587064547630828026123718296429295638934384446325302964896473296829265805737112709269803814942537657996725913938408781715328945194948010970'); INSERT INTO num_exp_div VALUES (7,1,'-9607014551997.140858001442365669993007297071681832468350855627077185145567261170534005832165603932891201648027598773639089125980996652005412450490063683624648655909636499261774535015914730479401090227915382926027949990128880284298688443593909017437720828163877690126019616194376778317148693270900349151496295698078575648169637635898560612738481294674167553369445426793073304518646116539082953755973571046622684332425840412198776081251646424875405772676893185726872613804612566569794177506268399878105117763696990094108960076591684779180089885283939385808214239337829666227427148603057941899878123459708920227867371285837642561064461118016739395972994827327543594846953341750907541716807985738518071480209106185726125017342997283356926976052909493074301401955202616191210810331245427141945840542129607439703255628683506772979'); INSERT INTO num_exp_add VALUES (7,2,'-994877526002807691688882220594983.04353023451866590387054959174736129501310680280823383331007646306243540953499740615246583399296334239109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_sub VALUES (7,2,'994877526002806053819802076903499.04353023451866590387054959174736129501310680280823383331007646306243540953499740615246583399296334239109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_mul VALUES (7,2,'814739569184924399102711674444306584731316176345067.39834031417849342571224916231092924046722938910652929295271097903377854123984307101079073134405782275535446337229706620713104545454319555885847481531722101704765783025789147453570970090'); INSERT INTO num_exp_div VALUES (7,2,'.000000000000000823151110229758332661330617426417726331211894330147399760458555778324097596176117291103184653828305857999638466183347321835058943563347767579219763002258622507889760416640758842509635599414768344140175277742935564567127659688612699366182158030839083982896107176174766408199870924563237827899202849733606842856491701660599599211106794572237923985121475458446997860253437578966578617985764298513928307852082168209458400544457824307270777530312648199364084272310536024283945598340590403612752287693234647719354745060851129534452514828239800716088248915975054881011343555492596002595181046121935660176097475159074973635534016835214952415720717896518544064238656360099884889450237541254761746029507300068198731306211736696956568648033834554273602524147075895460874922913883751452403825099444642503437'); INSERT INTO num_exp_add VALUES (7,3,'-60302029489319384368482818948157603222.2322712404088283093870869198708849258097125725036189625900174440196153781995220721511009855207486224837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (7,3,'60302029489319384366844949868013911738.2322712404088283093870869198708849258097125725036189625900174440196153781995220721511009855207486224837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (7,3,'49383414785234649002982046297226894664526726187218771083.0993243619030008310875293647868815940421844461627295157812843657782639833900543200310573708100000958929315945039020410482966753145208427035917753919085618457760620513481628641658765820294863970581642745379331727722585319163262763708386199720411053619449096019862596221607526610103408936214184850115071874430846697061554769773328338028749631552202705583855831155461651414320570061181212214810086436100771547030013079997847086'); INSERT INTO num_exp_div VALUES (7,3,'.000000000000000000013580546907080371873577430837141172674171921610919544849037647398734065712983603204704663262116138799357430947986241590690589753181299773842880079777640016786921825609617596862828930939366173224366864448436461306602680780407912534492687474933386043505172346330210659476505435994582446405414027199938970759003336829722057241708213838318628292667946636226143164221380503228191376939596663443230082698085439531600756771639601022064620204571458766303985028143400866776954225590745596639602613498355332049777798367675438365442468743270334407716567057368347458892075084694158566383133325959042076573734408841629149903649365079563374278550978052491499304166424686842598833319515705663176855033865872333988551611996194856472662292344160194821687681312501127516922809221030420253714666026321243515830'); INSERT INTO num_exp_add VALUES (7,4,'5329377457009123250369503.24931765987630429629836382184742348353920297283690739124220773955591340709935970062776650204659187764581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (7,4,'-5329379094878203394060987.24931765987630429629836382184742348353920297283690739124220773955591340709935970062776650204659187764581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (7,4,'-4364411947278810125327066890819882483326918.05664098958260550284395870948992407314161088028674246708928421994893923699743452802989464864039994566042797942433140378990308345483670828497915478397481687305406460330009319949623844175096007381662809083363069100235985794575399268709260901964834244796150883807308976949196661411035264619638771824190014274817662519438658481432363824187693821267613212631153175155634316128036152465184903927860719447693468054624663668062006049759837326188252927823612718163916100588143128358998656306593393889422386501730237442526450419990376323903182669190482615734972147533221144682538647497701130447816148459762464395194383090936159579764712919396391813914821973715879062992249315474841639591907249142779103650773383644785606333916967894'); INSERT INTO num_exp_div VALUES (7,4,'-.000000153664179510102140733858340480800294287837601105047285453457000254577644933901525444082336054243749405512900867540483190494113677173628646221933766421338612376123824684592850465460156248403574333545090544920568230979754949827013129083778435107488003838746926270955224758508832133483591156567868631938590248213604979638895901933775098150684618378235712437137852195098700137765601802898366867034641606131280434771339920637353140131159441790904703083143627590062236537714415872864218260252838432414759890832271190606933534662897006726154587341385852258168335058931957995901987808602365467861573344491265289043037273815504867254228957776127752540924854546837197432384563153608878864912196453587628891285275067452280357349897203095502806923463147414086919014592380804424300739713935051357374227246098303140106'); INSERT INTO num_exp_add VALUES (7,5,'-818934540724601372.43456071828048833552593835051449845484289562110789582081210403487973096161149072377955192388469356112505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (7,5,'-818934539419090111.56543928171951166447406164948550154515710437889210417918789596512026903838850927622044807611530643887494456379304996563468607210970486619898336249374975146736655090644822719838495585664994425117350974491367099004404995846913641329458537237789584653041949090121498951516476399288513593944575192159570458664608461677113504914551578443229008454218964701550932948083369656042643364608405637360180021322967144409944099438498649645368196191999692949583952927486593144959284443545794934667002661774373364219852712996869245745722896071593910890197478196462961042627387162830776094709087748993678069776845437889735782063'); INSERT INTO num_exp_mul VALUES (7,5,'534564131989234694540350103.27821462973515555648644772098605028371173048154132108733819196629002548296868548691993248746628993380136454426833349407578676005545111508293942736555269938962058196496152360848131645787941032968937794930046928523006455386861100809286408671908320322523368135203881520526880998279355848280412933152306299256343179622513731096363088094541514890135766460631462465021694553063366717467560655272004461368865264059368514271105464855575429914212085797297268595943955105608543373940035636033207568676745293499106348500559628723682588033431457023964317090780615020801564861497990103549650624438425421690193862533733474254'); INSERT INTO num_exp_div VALUES (7,5,'1254580584.048971438599349046867230181719371038956756285986415773300837165755558702217197735811549684202279755101552533605390208155708695952004683670878589028717509749282693444655857296902117478518511492735290086040573521482737598395369632843374456793385511847676556826348943588519880411018079886373631771830925920986588708409208527042927229627786932908015502292313887561198156623702404977221789649731458241770690830680067801377815840764873662400590343236662968218256211697981048576328148435241545372543075051594952109757428031762469834781538302930957095080167901199455226976113347018972534334210416375400979738414416582588689496706548495076287263281908191770792203069614447622517839588243746755480572371988630084226963919158931419126724681617069720048557166545204944250492282054791996953359013543036918134163144772567093'); INSERT INTO num_exp_add VALUES (7,6,'-818934540071845741.9530629278049288491055193606922237795920035094854496163164602796260436963420239973809758519485590636'); INSERT INTO num_exp_sub VALUES (7,6,'-818934540071845742.0469370721950711508944806393077762204079964905145503836835397203739563036579760026190241480514409364'); INSERT INTO num_exp_mul VALUES (7,6,'-38438389630389612.0042045464692275627184627672063157323631169405883031379129843031477339360597564128205768842448328088'); INSERT INTO num_exp_div VALUES (7,6,'-17447499423661151023.558342555162228919125358089491573318627107322332520978657843895009110781773496490472817700487707134216424855867015781267287628022535529641238372370292374146871103236048507252055787621394728096799222976387108688980537900309311204203302960751747509648304056939321473462375648710590981564101023812800603438271190184064874290215309040519813024962909469701968804925443161094255632624090623433640078421818321246597728308302979223833487133268472455479442002005374793705431817866798804822885690193667521606781156962792120052947767160957903073698536973292205899421787948529970837601521657406211962967291912148632072929662185840265855612193255596825032457033402506154930851214421895488796227471490998190312007513478459049382774782886773158311656817014322925167278223360446454868236479549745612973293185989975394307678926'); INSERT INTO num_exp_add VALUES (7,7,'-1637869080143691484'); INSERT INTO num_exp_sub VALUES (7,7,'0'); INSERT INTO num_exp_mul VALUES (7,7,'670653780922685519356619170643530564'); INSERT INTO num_exp_div VALUES (7,7,'1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); INSERT INTO num_exp_add VALUES (7,8,'-818934531574859518.35936275646834493832011429282408849567717761204690035294074716714939441961175772404289860039233415598996234758590850206505669201200'); INSERT INTO num_exp_sub VALUES (7,8,'-818934548568831965.64063724353165506167988570717591150432282238795309964705925283285060558038824227595710139960766584401003765241409149793494330798800'); INSERT INTO num_exp_mul VALUES (7,8,'-6958475505053954666339703437.48985528725312694198056665033448258303533387675711770743843194274181580881296671866212320171337132096489224277825857521033238709600'); INSERT INTO num_exp_div VALUES (7,8,'-96379412.478435590945480884955616049873645089637121682284625533034225619945532704111492738646389632607594293500930307222576571876059094206480673293295865214240456906965855425738072430281475736130342229749511650392658808510082775031098547507966544723255869156056349218776847523349173551313282283869146710349521487706884633419341568648959204688757523312579312713453540395840470692533267158388401676533369105590789036132185107859069994833345453200014884023709597817280132465224778002071890368479648934317322270613208789859930618055792958996389145963056607200020526949699302565905917600478429628844015684879886549766473809801710003649193772354147104446894109928903223843036925147624639466770660174828940577089095480826473544099693433597812637069287644606693066736302793687011165899362920686114156254982709172925265118077531'); INSERT INTO num_exp_add VALUES (7,9,'-818934540016982261.65314972994491977243776717915257186979728396159058352649559139156429817562698954531329940720620096519975256547379603654362598494779213610069399116912987384006656023443527501447464682173445385303315267086044455246361273561294141518329233754041352632499787199926225490924591851865949646448441825186059741089695009429827829188117479084665641367'); INSERT INTO num_exp_sub VALUES (7,9,'-818934540126709222.34685027005508022756223282084742813020271603840941647350440860843570182437301045468670059279379903480024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_mul VALUES (7,9,'-44929599044588573810654775.83678007633232843418115790847152455559258007804727916986432256198687661496804050903769496933400455947645400628259699874770581538122521805603947464462448454681701547899144129061961394870320463199545502030106801911915987309444301341575451240764927967432593181449618816978119423290767783843864768557371257918447461479570164065303599994081990686'); INSERT INTO num_exp_div VALUES (7,9,'-14926769772.797708334489652004325241753714626257641081061212878627972973992233480868793527325656854681817156284203427388055525855608883067129036717726368707982450450575794623567027457808927082390474261155500697096284790656757163047499531247323702909360444831707029353441147768321257650234732286165724178549576948957405037843360446785505536809409054071975214796532504678683693402401018726571884721963641317944453797513145055081061680091585467186975354801535734149952115333241283186621720677488342266420359417174224757781125498130120775969091933838082305123652811689513300403051544682523761263183781206840940347226802620226164265210810994106136738030959199259066517106713585343004140573604437146025585149934286364795122716971496775012412420105368351774715982565252533025207453326002101655121126631180162560463548157187175671'); INSERT INTO num_exp_add VALUES (8,0,'8496986223.64063724353165506167988570717591150432282238795309964705925283285060558038824227595710139960766584401003765241409149793494330798800'); INSERT INTO num_exp_sub VALUES (8,0,'8496986223.64063724353165506167988570717591150432282238795309964705925283285060558038824227595710139960766584401003765241409149793494330798800'); INSERT INTO num_exp_mul VALUES (8,0,'0'); INSERT INTO num_exp_div VALUES (8,0,'NaN'); INSERT INTO num_exp_add VALUES (8,1,'8497071467.03603749330791582407836434318377133169438097066269854720538319012928851657498035372443556191720308219530866834905045144302106406146277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_sub VALUES (8,1,'8496900980.24523699375539429928140707116805167695126380524350074691312247557192264420150419818976723729812860582476663647913254442686555191453722107164485675679551050629376558940966195135841284978096687306110481009743118940565957556492470398904849289222365256698601073536111216152709126800604695001949246634784573028721762079936564434050796321975774729383704426321489070979168993853338252728216162346796960170352897972568238870481118474064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_mul VALUES (8,1,'724311956372274.0135050255361637906710330203036651743488213007179039756514944640108625580172737414192938789413338554327986697518463087452612658955180411327002900979574347739956600177846996063741787205122007268468674386396156638261992679442768654367111433834151087792255469957061758837789341439211010331332174981459471333376067541234901538285101103690622656631026001337239036711179989456674399137008584021283568040818388709554256523118702728176420022080138548890713013682480239784198421500241995499841675772793497485550923152267616622892846304530712344886979674416990935007952941652591352603797627920865960622077762568060903908151958000'); INSERT INTO num_exp_div VALUES (8,1,'99679.115123747637190903598543851248555278745675862923884476564848911494649941770503156134872464666625927195645517181131678518619856156844072856993813601495176097972982587061507650426363887871820112714099226501603733968262566093655417466145183587899155614471697804006772915054739361437054029183182533671508695646413074668188590846200362324428338974890534273352188276373478524543505805545661569395314989170104140776362043880099775594658817242753124957385625811310332354760117110779649164022618274859298031549851269619167173746259018497289174255201452265070501056913033329291819570027877856677145579673495987354805150868813877928857472561883332547900866904764950837506993759536410161752469488392566682723027340638271076406246129989851281210810196699482980833204884400423019400653089825859983062096326294783573417554749'); INSERT INTO num_exp_add VALUES (8,2,'-994877526002806872754333651763017.40289299098701084219066388457144979069028441485513418625082363021182982914675513019536443438529749838106171095037135009526312783302868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_sub VALUES (8,2,'994877526002806872754350645735464.68416747805032096555043529892327279933592919076133348036932929591304098992323968210956723360062918640113701577855434596514974380902868247857009494139535009572740621288230740389545481395'); INSERT INTO num_exp_mul VALUES (8,2,'-8453460632655529853033389979024265783461224.3195241893307807116624750282852146303290708492834695194274289713076935297734670940696121761483641291930931061232942894577813178566088927221374036301485916497770984757492912292002695944367308880163698595015497307574177176409203214324418237020500352652934909632442547242092296504047310806151851207329042221920888326000'); INSERT INTO num_exp_div VALUES (8,2,'-.000000000000000000000008540735921314463871578184793632135730756619558669911183806487803411545406462244216408739432325839683804021466133071768612386706692296158696852363349481716813410857655324486448455846562309041306880675446880859847445987588059144788756984750993583865748280824370754934966494724951583311563735533173023858438364336214213295786266815116844775733072416507474834701984381586060478606371028156925222726225495235702395502085206072985373035972506738983640539009567237336002073370431753469632428303255926718930619221521257726366850472572830063284204851204189447233044832163423057501488364913539948261528280564870049935369825245920984413480757133585498984374354957754078525161296201228031555280486615145365039415418251448980923331334883673792135893857917681235883506783408111446970710546686739582471'); INSERT INTO num_exp_add VALUES (8,3,'-60302029489319384367663884399588771256.5916339968771732477072012126949734214868901845505193155307646111690097978112797961939995859130827784737422228762767014427842766445950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (8,3,'60302029489319384367663884416582743703.8729084839404833710669726270467964301325349604567186096492702768702209585877643481082023851284144664938175277044596973126708926205950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (8,3,'-512385513828318260570283740065493064477880918352.732624553690077857674083796435724202494963885926573907185100543184828131859183999195040110586155435203949963570735841632689374488877298209082579317039061893012560130258753218955057387206477423088065663401594359617882154814262843273526859406265633827109554791772242178864873774889091687515990672487380368975556580539271333144212685871370972163560839446696514092637412587953506052848750866803569213269271165856310101244342151576488190595936869490659700946174362872797854591188391982770203203644172999264143929484089237665313698600170041324566984832357000400'); INSERT INTO num_exp_div VALUES (8,3,'-.000000000000000000000000000140907135225782279761112255989433531718277338909398600029580768021365259747075253760824424092983497958717844671162530550507041138147836569244869107757945370200122955794509365120853536859837243314494576053441804831018954867623755033888264275704547752628348151132333655667171970175829826792355986148522268067032057293494927558322394395160508723637192234110428953945018965078022622950949911124494740703606109543716688008516750321047603009424529696862953094999450658951089435460411028678817795100630449046993274191915359520936265372754315076684798942557329584282177053819106884196674660057281227248874819417305259132106690385871316407455034281900110779740008476645291647094776093567400422266906817555937149628005629880142615126571231411138926043531449659320501743591992888328328980526602'); INSERT INTO num_exp_add VALUES (8,4,'5329378275943671819201468.88995490340795935797824952902333498786202536079000703830146057240651898748760197658486790165425772165585380839129948178510273188565692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (8,4,'-5329378275943654825229021.60868041634464923461847811467151197921638058488380774418295490670530782671111742467066510243892603363577850356311648591521611590965692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (8,4,'45283653791262997781451381354094822.762732909505051438036873220502792213670540454778361182993875916509061144859281577740137081988678361247725064336120451090222456518107029158304937620179032477664627949959143233370320432203497828243297406462513350790251761540074946469824444452248386782451723637769289822576372357189700319768797708375563651655860093365309717823602754924352327588945034832436331911584742966378275504545736896430718939807674966738116698454215555860047859161126694019895490767779791933882712567492115664113775047192011252893773389940988533801360010782816196288710063568554147458866942816721046004257953642508395867837127678980002737669139369781058046396738606563716339660654364541530532834806205571191828994250708412638796240377704994928921528330863683630622922959130920715261879547446054261914770022377059156125037157979236658010950'); INSERT INTO num_exp_div VALUES (8,4,'.000000000000001594367257057971052149628499448029056279649281098852958322409409919964709324200796473211884339143791758566019217634542932882694487712398244322522748736692741288668885362384266615527166964187404128216235057387796054457728789109537338988453837993084016408244895452291151218602815057669592284587317035387004942691671916981967449109983992675125005085762403043329820872839739877674121174083273716295673230993049263574856197011389828478636779342320299895806297835595427859271617831720398457416685435560152182883615601663820189195644140652141180949257192740185075408019971747810015931542757445763460947106918998459997631117642552273815713467150465548031203738878873114842844016176922502916339025283749846225376341878386377192605865913018132981323065698049618379727531925408677611856682983907951667054819'); INSERT INTO num_exp_add VALUES (8,5,'7844230593.20607652525116672615394735666141304947992676684520382624714879797087461877675155217754947572297228288498221620714146356962938009770486619898336249374975146736655090644822719838495585664994425117350974491367099004404995846913641329458537237789584653041949090121498951516476399288513593944575192159570458664608461677113504914551578443229008454218964701550932948083369656042643364608405637360180021322967144409944099438498649645368196191999692949583952927486593144959284443545794934667002661774373364219852712996869245745722896071593910890197478196462961042627387162830776094709087748993678069776845437889735782063'); INSERT INTO num_exp_sub VALUES (8,5,'9149741854.07519796181214339720582405769040995916571800906099546787135686773033654199973299973665332349235940513509308862104153230025723587829513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (8,5,'-5546455599206321494.0676583421119904300307105296377723816472192007866147764761501865875232824814135783697976183493106885436876081315217834621720906478074798596116645640251460842350553806256223963023430631066024389364515688765194373161385579258482225808660340732705687558150699172147896486727530192499184101617379930846663835628510376484675411350654979679181852179924386290069790336316958202582966248703889464308649631486542724072047294216362186036638115240070658004553260251510288423749333873893917690832829128021808383128393431810674177390352413548658782609064839524756041501835115152819802758773711821322162752064589750295542985780512921839490040396053737870038534216948323935020460307350020911362024271167085905714873548388570602799432705061561572854498075600'); INSERT INTO num_exp_div VALUES (8,5,'-13.017101389051085341042057308965769356145255575582875626848796382322826525772114256699384710400140437710569924703769685567402446691691210934185000959063158239023412379691360587119206695513775971704926722817528818197919265145207032750407924774510773427697188520818450702875142190949766251178733262143962213111236591970766836685919581025629742334704854852196126735685421250263035895756028805974153787560164935038227108975229771590754808331856162035119882347418116049174638416621093907738608991987582465865527947015457540650512339263071898410531735438556948115098562123055444965056347091625748703503220861221718449714020622377233272042277814766996198081939221253025243417993701684007826177845003391944496774674489538520354606358872276671998045196738090133576377830721671972381371985771591052597345572374064920279182'); INSERT INTO num_exp_add VALUES (8,6,'8496986223.68757431572672621257436634648368772473081887846765003074279255322456188404621827857612554765910678041003765241409149793494330798800'); INSERT INTO num_exp_sub VALUES (8,6,'8496986223.59370017133658391078540506786813528391482589743854926337571311247664927673026627333807725155622490761003765241409149793494330798800'); INSERT INTO num_exp_mul VALUES (8,6,'398823655.819545574205652791249227663407026876411660299394659390409794761643751582473390322547798567169668246138880832642141417531427935520467563318363116897177899262525720710134129529640376020947774470933902793259531840625444267816319963200'); INSERT INTO num_exp_div VALUES (8,6,'181029319177.110996740664566780784253502559986936959009611748146099327460471609593148344991059106574612143724330935988823134137686051475120980257829276671900076859337187540608483895641504622910361858962883971613675309676443079313179200981488761707281247447120551917205792352229666049191991270809865110506639390610910481490688182068719005593641339338678014189749279508731647492051879768743158839680867283217578754666643688259810863605002821607490100820241093473083445658378988069593782353275713240897038366242558466047071334385431080003439842348547427066389352198560236731403235927478177780757802759046212921140424771887928786549573201311120885052685761195784207710933764480136690216943336587118385525047554334029388869436622866247240903231799829259264158812528305210833683370536416861544931420820452512390255774498188962903'); INSERT INTO num_exp_add VALUES (8,7,'-818934531574859518.35936275646834493832011429282408849567717761204690035294074716714939441961175772404289860039233415598996234758590850206505669201200'); INSERT INTO num_exp_sub VALUES (8,7,'818934548568831965.64063724353165506167988570717591150432282238795309964705925283285060558038824227595710139960766584401003765241409149793494330798800'); INSERT INTO num_exp_mul VALUES (8,7,'-6958475505053954666339703437.48985528725312694198056665033448258303533387675711770743843194274181580881296671866212320171337132096489224277825857521033238709600'); INSERT INTO num_exp_div VALUES (8,7,'-.000000010375659845651632013446652385870617923988120764298690164486716047614260682259722116360931978511176121353975789418625836899338225571166376573732227571704071000348895791547943896682585450808398324252224265156214259224488248639550967292466343168350213394398101712526534464002532408445204630441167137710565437434313424987517531891145368203998329086865151248833625645567863740298397742783405267970015165358620026813812552194344790169289440822038223606218360105618852154152168496637886434061050281055613760360200323363465925493033734895631921307644481639236601187225135325401868178006133838932915485272554505684060229409404902185944047523033315868230944723282246159741659387362889777495094736963530708159604929268812778894177095572578862150793098548829744006499229853198046828954650334595737117597239208825268'); INSERT INTO num_exp_add VALUES (8,8,'16993972447.28127448706331012335977141435182300864564477590619929411850566570121116077648455191420279921533168802007530482818299586988661597600'); INSERT INTO num_exp_sub VALUES (8,8,'0'); INSERT INTO num_exp_mul VALUES (8,8,'72198774884738777393.8687539247642452953425155400068591498151280875559609979248583367700231031634872342122563819478919600402159024059794279536786611373504966204744811722007869415559012475160471227957857756325962941799428857291371597146319816910515366298862558849452235442246081440000'); INSERT INTO num_exp_div VALUES (8,8,'1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); INSERT INTO num_exp_add VALUES (8,9,'8551849703.98748751358673528924211852802333963452553842636251612056366144128630740476125273064380199240146487881028508694029546139131732304020786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_sub VALUES (8,9,'8442122743.29378697347657483411765288632848337412010634954368317355484422441490375601523182127040080681386680920979021788788753447856929293579213610069399116912987384006656023443527501447464682173445385303315267086044455246361273561294141518329233754041352632499787199926225490924591851865949646448441825186059741089695009429827829188117479084665641367'); INSERT INTO num_exp_mul VALUES (8,9,'466174236688165594.9218054325256670866060556227711696100465581464881295978997280335378678072434776702952026828137140986670189756965420183565968027969700090735690246176791371115610886533930223141650377886909408268207750238603105232560663571044993507074695683027062426288270199495225881785499139012931143826099668999261931834700467395442768201666740663642498098541516326470052372008385656719236306238735524802875519713512894448940917708118676095378518264553310312628830009314653641136566040400'); INSERT INTO num_exp_div VALUES (8,9,'154.875085756903716715488911525453064308758123952566428258639786597308109810869086867746263482721081985848551254298524280231489145092826397833394044637104667137816928932471315095067524966582810436282901424423215992139000153713476369887383242289102867530775908269805285313842050961754114751975054515055089553180717444020378611767296609130477264722612784088270193199394531972594028420402254831778715196248487757266330454269044609134602570688339750190391651801546906342796660819535014295618246236706572780627362908121159003488810140236665846928586992082180006454824311789091323774002510945263351862712964422865623934112293184149374573706760114682326698881257123280119140924775171374360283137569618025005229268057970275164869735173660958715166148344076027212231446680947914004346760896298312286730627916684448923824769'); INSERT INTO num_exp_add VALUES (9,0,'54863480.34685027005508022756223282084742813020271603840941647350440860843570182437301045468670059279379903480024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_sub VALUES (9,0,'54863480.34685027005508022756223282084742813020271603840941647350440860843570182437301045468670059279379903480024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_mul VALUES (9,0,'0'); INSERT INTO num_exp_div VALUES (9,0,'NaN'); INSERT INTO num_exp_add VALUES (9,1,'54948723.74225051983134098996071145685528795757427462111901537365053896571438476055974853245403475510333627298551845046116291696445177112567064282766115207407461565363967417615506303416694032848457927390574251904212425813072768882213388082765916956736282110801611726537663292922699021333445658549608928179155685881583228490235606377831724593358583903616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_exp_sub VALUES (9,1,'54778236.95145002027881946516375418483956830283115745569981757335827825115701888818627237691936643048426179661497641859124500994829625897874508497095086558766563666622720535497438693688376602804651302002795213923698663694204683995198328880575615535181012624198813873609885725228117274934655048553507421448724831939026752650108735245933317237310133362383704426321489070979168993853338252728216162346796960170352897972568238870481118474064783391570102958474141459619245240874849766946530000977144965'); INSERT INTO num_exp_mul VALUES (9,1,'4676749348240.390309875431213992853550297086049749814750492488995108783145961719774217441193547534210468967573344456866203963659951312519988497979489304488948342258375915152429008993288817366720647491166024151209542534474867042837694499222928509320280684557676243780452100132238968233413333851595648146954975713386711764268506890884764704949969602122157394714663532141060559896359465918874990769222345665160127552795532197771168442486088776803398878354288847069602460071745966589164282641033852314335279121191855487126430176047553895892632834940595958394834437871886013513058514896870683979585091413977173250824451205330441299000850618134248917380244749589254309567551846327349592529960432446947239714236828401206843011440433362544797025114476612133622499094287321570559088587999417440664282418005102546343020409520421747216'); INSERT INTO num_exp_div VALUES (9,1,'643.609749344751131516972294140174556703217311736700045690413622699888869645595256683013323517984528456698303984909359393772036036540901870537096836621035845014213031549051156299974682317824766457362427063305495772666640279328909129870227828460705733995380145417663304348663705694070309475835826101153850359826502235923289787750107778906593010060115662191620280031872002110849782776325630424918493602259707267214006217268630948545349980430128422952869610116216278256812581821942763705098526140427280008360043829906543029486315209818099697988089748683904695870401517598840185535891464842870210715421728852789815860153472208176465166954851895457846723102438114697692610933532992841803219018495137378534010155991355251803548866919409031477821173935696065078362044927492034445482457329200246282082707380974745411383781'); INSERT INTO num_exp_add VALUES (9,2,'-994877526002806872754342093885760.69667996446358567630831677089993316481039076439881735980566785462673358516198695146576524119916430759085192883825888457383242076882081857926408611052522393579396644731758241837010163568445385303315267086044455246361273561294141518329233754041352632499787199926225490924591851865949646448441825186059741089695009429827829188117479084665641367'); INSERT INTO num_exp_sub VALUES (9,2,'994877526002806872754342203612721.39038050457374613143278241259478942521582284121765030681448507149813723390800786083916642678676237719134679789066681148658045087323654637787610377226547625566084597844703238942080799221554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_mul VALUES (9,2,'-54582443595378013373024060492546032003692.4875677735896411267274323339692558458420972958075073392126734000341372096298914875892612108329218081214550050039133117695428196702128258481789017059073444323729583900855712795086447886053552786449313809589992185978097430132940882612817775035217244553616977182049775786664446683332098226841743818600819221587510039430478859412452506872131851471967577741190323481953867845129745440745526578327709351120432530702446916035797432129052518980799424635406993848916727957825620638983706180841278402925286540375225365057191075559133035'); INSERT INTO num_exp_div VALUES (9,2,'-.000000000000000000000000055145964114074763360265614481666934002579974728749248345352023099030383962250681574081874554842623852433135871821620640200582985140388676650602814646133317791813938390695683843848260103199745295436998313216878337673674660966362155480524935736646623766057029148471463569162153009963312016563281545776175277904913263614668092319707343286073000287493274965714031678784835459999763925833141049057636632430975424499618419962303087175237320046300285962065818926167792812657620724550768858763098967149546312995222223400007044549870620849992226072041407997925405957501929449911416474388622107825120486594723448780503829317691081601820425151593487431389373265285594626753418140874747955925763163132984655078996173911578832035721963554569605730262976354029623260224710106409129114204296314733036'); INSERT INTO num_exp_add VALUES (9,3,'-60302029489319384367663884408030893999.8854209703537480818248540990234567956069965340942024890856088355839135538265116174644003927269495876835324407641642359213535695803871472434650475144516723617632059130297610134243891145006222068960999879308472500422640481972089756410157246974765071949782242392661524488959954348903412713930092273629207697480131360047867213863018127928853922173643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_sub VALUES (9,3,'60302029489319384367663884408140620960.5791215104639085369493197407183130560124286109130354360944260524553172025725325268378015783145476572840273098165721628341015996848028750420770651761919246816300854441592109844750954710317145008297946462099581451150385769713261452744310496166494545449824802407416426304041583975713483424241727236417259479541129474082301376239522310995725648773643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_exp_mul VALUES (9,3,'-3308379209762459471107480259839508279070920437.883503980178028214343751083865562028455061662673132221930429904398963590401793045470444301883103141901787466923883803951815572606105617157736442670792467625964359169270739534412932791178258858918086886061702512427989129732248215348301444245772127142869263635282888226326427510486246184233225114523636171202034558843515894542952126988613018789833835507734620046994907453602573865012044120483116345444810078666601100257620969379968264504287700045822481492526688635364586344704730579892342786173395802035361824932075736340405960099542224953439044947229246847140957298841482874444906129049023002897135347878048572628834749795298712449864571996898774444932083319581439741625832405434317985988163261591679157437224404970927012111196724239860528859217322132733404472897289'); INSERT INTO num_exp_div VALUES (9,3,'-.000000000000000000000000000000909811507365065002714756487495210579371808512079908127938523896001746219475805196061435010714649189975968123072269549018826343830061696154665503565341929634172463095299662727352635590451263034658630449260378893723785917860125051787451512267088404686342938118993621396641623525252649748977992770709930435013456855344203854749977414354164157192885125263071636468941596567220391082793700307461350484216679632552883058303710297475827456761138832914743429330069022439380297715971317819244718196187172770061156794130040674050533617155253444764036426045091327368023602807193742585178432544430741520636125146531502042579276206322507516332917325631822606079220413965396706334639331097621824106950192993127113903265025719013680733760540930122186345919977470628988674677630636632053583144327'); INSERT INTO num_exp_add VALUES (9,4,'5329378275943663377078725.59616792993138452386059664269485161374191901124632386474661634799161523147237015531446709484039091244606359050341194730653343894986479159670583937529516163204904273806158788218327396375034882788180783796976731912141525319602448709213495905899041406302673881364465504945113279286939663215197485367850132991968081639290297033476859158044889351836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_sub VALUES (9,4,'-5329378275943663267351764.90246738982122406873613100099999535333648693442749091773779913112021158272634924594106590925279284284556872145100402039378540884544906379809382171355490931218216320693213791113256760721925653394811317969065642404864072442190731745871963413981746671302248281216916486794296983018838956112081135739969615171358100498945955409711817327376172085836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_exp_mul VALUES (9,4,'292388240303165948041827159734686.255558469787242316676287235194652580157149226950109397295920730296960145548003120827363226435916209781396711693581454960342091452830648929118261388933297036933167543189308061917640517578583521401267417187854611829815212778183983326568586118831109538377828156118900313778053576483381085207892754728937946691892849474364477434665960112125254104966566712906532318984871145605839506991591027939136026602051635433295687547552796828217859648186757719639965988287173297286034098497871707197092627676226053609131138590878743560287292934815277894463305001278326023708395571840850120055316276256138004565442099731931051413153564744766098053176049414330146267604802971221161572130161432525297614616942172815141372973870720928125699420370428856022295499447755488148545048400795053604349570217878099721865670458104653570360'); INSERT INTO num_exp_div VALUES (9,4,'.000000000000000010294536718194523982241053267404812827031741197656209184880073175960433631103885281961037127283726462743623757855378209281373475473018922090781553213750339001555832360656399849031527008437303091226051008068950896796359518673740801770866360774945096397034708173365378527676779736929035450380795854046109380272505550244458858231227568118355064007614608452292270378691774826689216790090661497154742954386244856792006376222923780801296832612827123778915598893970651480451509706836620045721191411824060983487064555397842027454385628620582036592315345973096405447742002746762099231557054678593446667904250189208490698468539396733604833688133512716508825505666644390119877423938820483653319376926639295680552194966870285838815705038244628263602997511842285889300557188773128635554621378148419364876651'); INSERT INTO num_exp_add VALUES (9,5,'-597892150.08771044822540810796370552966707032464017958269847934730769542644402913723848026909285133109089452632480800168074607090893991283808726990171062867538012237270000932798704781608969096508450960185964292594677356241956277714380500188870696516251767979457838109804726539408115452577436052503866633026489282425086547752714324273565900641436632912781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_sub VALUES (9,5,'707619110.78141098833556856308817117136192658504561165951731229431651264331543278598450117846625251667849259592530287073315399782168794294250299770032264633712037469256688885911649778714039732161560189579333758422588445749233730591792217152212229008169062714458263709952275557558931748845536759606982982654369800245696528893058665897330942472105350178781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_exp_mul VALUES (9,5,'-35812445701642379.972368737320206275515144213236752803936806738624588812089615098329765811617509505790110909629109400553415312470540217508070421816878544125783329593128638405659896184248784794258084116406472768709113030915308410565617764394827427154923321461158387012978726512246146545834669665093228316853342805604075936530371665576147966721599968786161939347726656168798065647411457701453987215491345496003650288850096338695703984042549594979897253521041581573388369367579323607093487743440894765114619634001789457486407909224339065748496715380572175183589195611952939575073075140094901024063428239223964510824958346570603142906309198033196987949067156046076497974760641964978711558209708743776024313916111738542765749928287600981397080809041007714387564206594515733287925008053261840295560398311905155157989225181164097547541'); INSERT INTO num_exp_div VALUES (9,5,'-.084049034261605466896663277055600903951276881294745183935726262038673990196778002490449355450474227878560465916800470848046625257516764244432096856845087412397406701521972651300484716852035267197801389708234913163750232707469240634303111868882057393120649919262424619226282082184091177505826009374043368623853156698509808569378758387708910629731005691079770517679511879694426434724918004419953301426679939010592502325130576915399009756468717124460489039474155719834555522581553817856854607844133431854471292027873672356863673617090151801474016666978499651970627896504709551656249007718965259502928591648533670568214972768900993459927860068104745163979267716597907297073374689384723943955361288974065531322408839914599555769945298758102515352082822617428033648130099822033393662643586331479103933840387663729387'); INSERT INTO num_exp_add VALUES (9,6,'54863480.39378734225015137845671346015520435061071252892396685718794832880965812803098645730572474084523997120024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_sub VALUES (9,6,'54863480.29991319786000907666775218153965190979471954789486608982086888806174552071503445206767644474235809840024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_mul VALUES (9,6,'2575131.137912978352131546639620215541477987701194164886305951830806120142596646541302305984776928560906754259789485960991272272782091464270104432109904222200473616116525297615725803495463468272171161659654385929185160689572943852767523792651123455283534072794326647404332228203001469884016996499768656263775233430922446983838511590562929268821678518640501686017030536100955531423152839988008496919169395159653034847677470665418765966542111749439412'); INSERT INTO num_exp_div VALUES (9,6,'1168873084.346566233232746391559830634361431940000227460271861554316197556566224118756340501278103405856646766537018954185964066240457859194626558143313125824412559635129130086906976028635444060218797992547370132082916380788496584864016645155338102476357490305222392452114945853620686975383081427840791892729407194179236897452655907829255937027286698570784397487382242990326347080472574546312522326038419753951437799831430690304084087684303035538181812523230890783372773953961677974396907303758903934808035747944477277528267001070234880092255363221274303820343225415479126819937070570562654065195009839593938440374000473302075568746771126391307584779249330981594640387657042725725493800876630516005713789705652827210295338592985225924959199657729900181287069808881130884115897407246324220524401243575641227725030779990490'); INSERT INTO num_exp_add VALUES (9,7,'-818934540016982261.65314972994491977243776717915257186979728396159058352649559139156429817562698954531329940720620096519975256547379603654362598494779213610069399116912987384006656023443527501447464682173445385303315267086044455246361273561294141518329233754041352632499787199926225490924591851865949646448441825186059741089695009429827829188117479084665641367'); INSERT INTO num_exp_sub VALUES (9,7,'818934540126709222.34685027005508022756223282084742813020271603840941647350440860843570182437301045468670059279379903480024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_mul VALUES (9,7,'-44929599044588573810654775.83678007633232843418115790847152455559258007804727916986432256198687661496804050903769496933400455947645400628259699874770581538122521805603947464462448454681701547899144129061961394870320463199545502030106801911915987309444301341575451240764927967432593181449618816978119423290767783843864768557371257918447461479570164065303599994081990686'); INSERT INTO num_exp_div VALUES (9,7,'-.000000000066993731076524206362744068866774567920404984046399050881532938231826344009126898802592302273719505485084766150904380671495128604515800845609713368334606489445184535043833069145643553083555507533900955661105251251918425885537513359541698046533092111969478225528665278023069818968531644884466229545497943710817187632203193468836772459599856856811131193744272314519908999458320275710240994009061040198159739169960258978462113813370513611735006229733329565083659159456172425715216475781507996483885669437855000029758892126410922067202159414570164537031153818197618428471046051340835826664787585016361564969663413176434498159140395476980277574789931364078570781760777773379636490084338326576889857824344578398580499610233575273027387501809967324874264742269453420400624883982643066864175851881870402856698'); INSERT INTO num_exp_add VALUES (9,8,'8551849703.98748751358673528924211852802333963452553842636251612056366144128630740476125273064380199240146487881028508694029546139131732304020786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); INSERT INTO num_exp_sub VALUES (9,8,'-8442122743.29378697347657483411765288632848337412010634954368317355484422441490375601523182127040080681386680920979021788788753447856929293579213610069399116912987384006656023443527501447464682173445385303315267086044455246361273561294141518329233754041352632499787199926225490924591851865949646448441825186059741089695009429827829188117479084665641367'); INSERT INTO num_exp_mul VALUES (9,8,'466174236688165594.9218054325256670866060556227711696100465581464881295978997280335378678072434776702952026828137140986670189756965420183565968027969700090735690246176791371115610886533930223141650377886909408268207750238603105232560663571044993507074695683027062426288270199495225881785499139012931143826099668999261931834700467395442768201666740663642498098541516326470052372008385656719236306238735524802875519713512894448940917708118676095378518264553310312628830009314653641136566040400'); INSERT INTO num_exp_div VALUES (9,8,'.006456816440893715330247418029019114736889626790871612141686117271826070935285769018710680035004320626745647926106882508048159628931624522666638442625219959259156539178378186912871506893482633695438850964052285542425753626455183282159259999492971992739484319464700978750304962671213318202670228197968646486740006148091321740497272644910882302412140576608739962605210964504469426861972705740810533465451230811358870068391007718532021526225893542801514255726272411690175555142385382688220121052891017808391607717500701760375927811435030512071347521837090721052128992926357375527600337655573639413811262412492632491693179011503973930804928749370652038245414768103001067902012962988384812280453070895781287237746786414435546976395632454474312533482077585837153357017362048554313154580576238549196250793055676215164'); INSERT INTO num_exp_add VALUES (9,9,'109726960.69370054011016045512446564169485626040543207681883294700881721687140364874602090937340118558759806960049486905240792691274803010441572779861201766174025231986687953112944997105070635653109229393369465827911089507277452877411716963341532491917294735000425600147549018150816296268100707103116349627880517820609981140344341623765041830668717266'); INSERT INTO num_exp_sub VALUES (9,9,'0'); INSERT INTO num_exp_mul VALUES (9,9,'3010001475769225.8286280957637941018500905354415197182850820227163907782811814730309044010416886791014702373809932926301368137684091094408663914110947072451332976891128659038142954192986392936981664792370678656287232795203974766040821110221158579481177539669363513848425151485663431478439528936592701070340012569297177488556353760756495238304538439278682066056721729656193616571456456325016960870401748115848423105783116854283646624807603476682295234280408938557209608025246638166902335016025467565869375885610813662767004038102486303756741615124814580306266901273803721191779461890468156043551004644728343579032524687612403663816107770451694666844862368101122025340182510019516924578414085461628689'); INSERT INTO num_exp_div VALUES (9,9,'1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_exp_sqrt VALUES (0,'0'); INSERT INTO num_exp_sqrt VALUES (1,'291.964716019333021494947753821238960905461614737525349376826064492714634914263808902604580614735501799528494357560837535773816469841426747889103714048646989532842972129124080559131220979335403729022278994440514872845756198274805589586120535745968205107562348427941379641465378272611453955517402598409789621997041856848783989993820946766177453801729783316269310186191833995557234577548740940419224137195404391193633808203715191863638616433190672511651125299379882126530500870287424768024674231651229908224729856278167033444719242144302972892419034855417126978468296581589282861879645409909873113678361180607775255758820910366926076380306290306477790931129670172989289536405788838857428768869345763784112862591549008321546447442552533919976570125718481191724503352619626562352280522949665158335559389298720990302071'); INSERT INTO num_exp_sqrt VALUES (2,'31541679188064906.712574384704440356216787857626740375004266523720148374188511622980520374202725176835435173058936870163875556102907654264048353814040480579464700545975346621546520503928314632418705230212623378642743044255181848913683862360044189531298446109955034944189751302497670367665492719604026161836224535961347218522748523360100432275693829501972749859329753224444694962089604095212784768854310289429208671271394086829270986183171968944659703708706544668326267327938226750760690620258967209626420981505237183055363540806281098871221581265173394406715458619627534396065960117454160969749739483126059760636526242783235685190739315590041294766649891987044641492234243404608847939002062827210734973778130441825067858641461599799772535304379732674727995848518807202053316225824685704785148921785964036119338754973714515974054'); INSERT INTO num_exp_sqrt VALUES (3,'7765438138915239878.949520541017683429203286303188179443533225547096446554008374834292278237558244698868300666061834105683999048386497322007336816482648302911579331582895326423063492240235074387242190187374869842856897538718280497895072291181675294000739548676781615025944675912072664211455701112700937190832332966000160156597821149428032612782336278939437593991008833233156511435294360065004167893309428565243314846456225604669764879344135321428948841659419438769652686215993544390780212859309497190065178705035652106614050448518931820975038314187040226298661787490226917902356569717171481159691409131778764973037046501816919243659681416263730519167614043077472097520207347950292377914586524327206547377189493301153212000966249655331053184913579513686655963686155890934436604123384536027235444923674128269748280097789270784333442'); INSERT INTO num_exp_sqrt VALUES (4,'2308544622905.016172868282330339228589083058636874526727829838244942341440716909466939214393597311710652963849541394758298277969240038668406494621950956862959196896847352631445328917063551082418729435554972200530109505384839391233286173517804321019323644218483570886304028175359854335870835404627608254205407525763332087823548640923282031978903399118139052814618531713327991857575390136755426466065839913887477577516426991104516201265995293600539957187007068885368699949673989051443005684755994465547159213587471972139403333249259808344536605314911144950465968669770276463111776581675944967401948957460097365849699783091843609965345747287667911324039374314413430490112443463386381631812537639503425989372084906324702158112088898424705684574998783112519152403201231176840068666882123684602080460378627639651465436618032671756'); INSERT INTO num_exp_sqrt VALUES (5,'25549.082770905117529972076915050747181125832857399138345044265535151111965091602789684342996759657333588444489085160336703294705499665424408218434077722506748278242942379566431768762487954917389137120540138359870652558814224523699917122023018717544160579704907452934297025088008618627873220397030397424422097405152321366495319708580932627092620533785271831833326130796638935296720064431288560292191928489034307645738331451165431755179025359993690642194334018457793169983249853388987495489562746304107188105521296156525984787815685365255240654972150342496329030279439124533240114879332406941960563154881888172285475336782757262639979527682925214971861707635327995621436598536743180180978457735632181738067997521785965451385630326464388080990200265186437768409003553910194212076755448477164192901658547251079126833187'); INSERT INTO num_exp_sqrt VALUES (6,'.216649653115510782473161631235601739254284877523828136703593069337209747459679979369185882839688430004369697316986054374456779366220242645866798278985273820408495361607183119980716020227424205519727777568954933592987351750339481522149106749713967143685591960510946511796062486795368200503801097611436787402191532618456991115230272084771674098613479989808680789347124789253499967359190605681912854639520917409710307182238065185749856554472717209097115325999946728168357936779767099041518574001682560265549916593333117469681763348860131760281253987626822958726920016922608371657319505153308390495179319529587670415367205193280809809356733443291197315823747505896510820272670040485083775482983378341120809542502350385555577946098824446199419354197416933858522419312733314383889554606932774046771497129486979593226'); INSERT INTO num_exp_sqrt VALUES (7,'904950020.759072496304165474991957396337281699986101765045213964054286624338102141970514306010139529492299343393832200631760194440206005974547202512275476562767685193838576516154915404389465528270010938533075930081897392863141132529694804621418663424569202655893682412466871297412964570322984865326770090075582481194532433411398133265643849129084449161396724635797324126396071308557057830046688990212282866035593809633839882468628249964862932050189148498591642162462777480125024786829078066012617362076651920045684345679767223337287825546294839320770903419463644110383560050404456170063805115223954191445548226706113970164823214416171441655706141596091717118495955441099867737827763335880891937222647408575142200256804313345924443344596462585960919126827045197885802122062165934504665811115031150357820196176799560314653'); INSERT INTO num_exp_sqrt VALUES (8,'92179.098626752893864900181023972781406074846653380680747862421481598042923358730531575438403865501429843141967819802251116774924400485954931201776260931315313253827346015775662310076094882239170765060649024538403329505426563390044695320714825481746233901773893996663258170360232639353378395244461670781152793416950717050461856097473105730100523010642696332151571372764781034028324977128554099993021459338419164426784774496292405945103200724413639660488309795423335142455569853549710795692020963174011003447023610692365550245567840477105794884132665155376243735213346877116105595296043532605899184658904822980397411096930267453332143879534914237169761039374689145860503772331147367757318826885494994339695470190886515765452545019167989882527248872835783707554463866334705735781549392895480816605355996057201589681125'); INSERT INTO num_exp_sqrt VALUES (9,'7406.988615277484686670011157489572203134420118818648711986549881046321377798441006745317356200279801348355202517703531020643333388857073977704009782384103170022716610432579974132111487533733493986910583223121269323909760573942980360508642443245341392335557152177332615977623338526935953706604224108508582338123915133189529507760875123300397933931420500010248194253078118618381590347297853307090813639981736227771834732256867579490224181748450683295253634852775448770576585177080941820456051588076218688792321741398867304684922665590162004919486643750098085197190000638539994723704724550600891137853975703823903659121582583388450687255538838161486019214242094423895463814933532217776443473765708693285683261505695170847285063013324823850724236845500162436661946026097459146424122412596018946436589967013641971183281'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_exp_ln VALUES (0,'NaN'); INSERT INTO num_exp_ln VALUES (1,'11.353265918833698201334218522735144514838241118349715803442713722607336732214173255618762341321138898556011520430414052782971985419141860417968593746833898952016980791997105866598425597066404919489902082738711038276194174786383758877067916049129476352925010880025206629976454341252818402788928939407784629386362069592202090897264194883276572978998896242281239126931595483958092059051047739223830394259082355969005503976135238921488192773135287876801394308064862257453262299764712613486466254696464150007113953810688169396432889052881763511661127351872408811370081346456019961324265446884877073712053408327408917588393884214304220369626106333713688792094943405258431214313197283237071070354654837081449831786573831004911008790533179001070424813584405346221388686999574752038655226138085374176702005198770598232862'); INSERT INTO num_exp_ln VALUES (2,'75.980172429959420723484178622920965327708652620924912610122049843800380131746381968266727388919414524075492921510147435877107720844487333947572033626887969846858337336557672107987074468763307953130616555202495401302128216460637786993535376622372745654109623249396257174895352222213037880060756992073605135503615371392439827458529942230210514752764526895030759481226199720092008002458654297737883219558685499445394647863430593136350562417924068100891680398878483362058595716232013516337079804607378041880078724811071904523716775991447489914128580100888252698281559809224785596795038122963619830942475652745611551345360922016753939774272970008770647516790944335173711498988149783075646985898883858697162003144539047532603946093022417842140993960433780913606807466518632121884254341907122163281927271483110212890483'); INSERT INTO num_exp_ln VALUES (3,'86.992429107491709045555322727377654177072455841678650084144967727028762699430180506209786297136121512625728883607972513154010138109866327600596617277403558404624813332464431424791338402731178416819791932126837396086742033973404980654712734845137075562739300866280737071167943367603243180515859476717635339619107593771719314284984269343476343816253634799874584843436046260962736006310389088154751401911743739429257286834178656182340416539923956100441369280015412718483971113838923221170027312390404790743389872757674342133486652087007983701950040432125562287337697971646750563062524010514537132255605131615248097901911480464339325353279118429890601202554448469387179349495284716473293965884844451619766312048304583068386805927433174443889441171878078987788018564357316138422561213329104267180509029624308926098065'); INSERT INTO num_exp_ln VALUES (4,'56.935276817066740776567329017240462885579486075188456418197311631774373422196025180114152248099799048545382060930401786002025479108787121595516444894009593031141335985913019897883627990503003577804436730367402618412514152465206336556967419434371593632864308139215157721913158949066717186782560422199668568894551013785702491365073449320535603830475158258853167712460432995074161536886421366716995573365924430692151761737886552457036412140640821310927642146210426044265504978418405684030862182425702683702307323138985481047994648222224089112998195621687911787785594701557252468626097576375468916953563766801336922479861708649876362257086586679701715813254414915314296890025577780265459584203893089574567331742100451277992780400302806430264717887468808962517029442262560742822875484362427192693300423729233467613910'); INSERT INTO num_exp_ln VALUES (5,'20.296713391219923821414834924710998522858242536565236229645868008008504475111229451635162536658197320282791428572861452713483981402773630985812066048575864982038046409484905688236579134672910905547858248343712686247795669280482288748331949478864729205285910525962001251260319741279139167559906461672936902355959755164523720443059989357054368460911050707727029320725144824995614445423492687177126412520389766864793826362309254124276325522276592246655562770110024099522184080118637524912964002223613671995639705240767929562023556724031894855094820328152633412077228479168557819219970917880393852962560319397442566813746504969336443969816954424715197797253670026862362130664772772977978222813915593329422557592316429203293264572088112274848838446633519530653849595288125585730314673691986554304725866754516304420665'); INSERT INTO num_exp_ln VALUES (6,'-3.058947463851998053084898503420969773173569760507671013593014983772013099601022840164736581595033399273677583253456908293015637115395777673836877852797643436458673662566205707359569792482081945396989472318998080581824382006377064185813936544714612287417301161454496258176319380348780934551188852900784476213986897306897793456700682073399936398243222895442594762628402487110466705108765286617060826203345783502301472192906817785365563881556293576463515218574477264521950513789471494214626744754200844840310516235570475410854073969787604451971790833680742315518808178608136598148628107328076871698598743664423452623124027059698038466681488746505289551548778131621576387262707147068500249466398507704796800459013580425992071957391417767257856002976954566094297724379688683375704613872658653366052459242767328235849'); INSERT INTO num_exp_ln VALUES (7,'41.246780548917246608934265057073076900048579756649769602488660179351587788197892095257027979113051775079905924990472069951828742350559917110289416201523653941731339141666097617614477426376799479821365070373247490598890520285155435501242427296281987676879064510605563522117334502131946383957407685328562874307957108543536378261847119286989184256009392692140821396916222386573424618796707564187152459973446833193743614720624765332006827171872712331032607870580880807058576154429597725560836582655488602546786785520452359711161305828045237044625934404295366273012300148250900116489718279757540843657039519736455668388572899273464839528462223812926410544976290646668870192676914370659142463304861500879195867873346447316374869974900582948166687948531910220128160490935170837209017355954301127162240133341813847180541'); INSERT INTO num_exp_ln VALUES (8,'22.862977375646110045361670561177818139082238721442691850491173190000619222046296383571431877856442345505931635735363450488731186880557789439424987680284612480261693386095598289519783790826332183796775862215503493910816035128476952347072320869461206895223935484838130924268616681347949695029657753251443811448783435000569829291535036468240771401957519222523032235686030017496209956550934543164421459898155836108824017735809352580723262896259290484291175350770265895317482371895188221452083719817251845416195168686335127805092334984596224320638378502008767433534450949989322562311171685891891122105437154553106840103473941148230953978989145470651955269817951560544095229079088083494695756914405635176899994279484466773598435268700064279990885608144109747858515514066444373797446449729058958270758597627587968112958'); INSERT INTO num_exp_ln VALUES (9,'17.820358481980064387183481028572263407130633079314879566896470101569251997264841660326428805413719418277889123643557369421967068805165885825106611310020187894256310674762734896979157570968168599492401269694048046876387337971177513661006711375440365724346137980004810780215236524986274043416621637509807126148966029923572853117418545426960105154053049098579812135003711132897895016476695223444397389521434633067499404903493027304737402519428197015899833229473322655155458942323004249812974150129789653469524573801259946118454333405580647485894435301530550214095993989552176497867244278699359917247910082169086524111229983698975613609318418313798992088206507831757327320958918656453341769110558376097374227592021075267882222057385413453949580066342977546145482215220982989992069525148522710254796105001938615214263'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_exp_log10 VALUES (0,'NaN'); INSERT INTO num_exp_log10 VALUES (1,'4.930660740129727276654889314296515979425461685461970306647398411855044094312185293195497201658739777714943974003690119189101973212927970410047992001003936259467465542044528955416040460487922970233600641954269411521809500203864460110903973264337093883907933081597350982496469748131390809569321256206859934619579029279954574676601709408712255490686948453752571699579252140062805776361984468580258289509013081691778727372026090522694670379557247829136504595898935235926069699309392675806881162434168418505908116911054206058735257796918687777716036307205415038158583184624809880157060625643069601549803887864772092583549388533013233603450097615537162442973385137488450178790573546382354482351187412256794374383453695483855501587939419102008302408157959291557415763034668013452188944554607063362933134950906875499201'); INSERT INTO num_exp_log10 VALUES (2,'32.997769620388965086774969704518222090258389987679691893351902336370051104718852164011301929506188893338106627980171059175447833290713847317665944354651476245003161501753612545484635275306181777040447675475670149066399611203341262105766118892586541910243351018829302798733989560900125591073082441126709911019648451232244139674063434385451279378543163944005973452562993913383659295688375546058256196254319767218634546732685705517341998116744642480938405113447415486950667007645850519659606476727681944251201236366198374488204017630268083077471516734133869728427050843306716313813724061560369884508660845630727190444623729815564381063131729592825825486515070406390371638817503915214206586939112681762984038333298146999891250107667687034785493312416966635780188163871680959873288697497561452228182734430749066579749'); INSERT INTO num_exp_log10 VALUES (3,'37.780331928743475574895606142114739140772838801045013007323050327909196792739138159615327729728110344767302636436234256468332011934881494997184865617793179255006442447189720642997935223133982347184994174261506212652322213673745795726283311685835974151422721233207287206894148660531800622455957268888702309499182978182878524951883775154983702898237404558813230370364953160102391101897560104513279410610948028599674950811462114131673380477843456965645417025376374320207504913806546872166094337441573669261285052323206348035827948287081776955945081345131570610652073053464020209215624179904586956137079321655773178387441622685682721151900601340680061607114354850640946256225260430676099781727317540719923791064452012925902993317349390523278687089530234444415688602090547516647302454865526291471706301790881694022223'); INSERT INTO num_exp_log10 VALUES (4,'24.726676547286224970759328746582840552419566534667446425423046931401641497155587075591229106937829957279943690528061985864558314570189069764367933957499905044566413640017549478921384160584906257607957223101377816440084188042395098536074479064548620374152344954289432050971466476174493306432228880930006524504974367146536665170956555486181410864034862861231267121149652317599303804477688621597163730470970207231328339082779056152481480926452142005969020950341307977091850953883445808399574256295803245530993204179747743812544604144379381347499056545148243304041538981954204310612049423688645476667184129189153715486929216331980316967699254518020077226689317148303152585009031597809279387172427408557115400021035692880631275593381822805377317270568779655383061987766693697518921188619814204902583361096973421134004'); INSERT INTO num_exp_log10 VALUES (5,'8.814750626578650238811431417807018895270298639823442501111235973209197727215795256506525221092818797578008152140054383421240180435087611869193019443372556081555311825248667278358330916098378127100899126895012782320751838528480712942601038190627182482614147263228588284866661508052724762701223357327343090598060805245853527435948381893458352744679795853650453594546267600486696643924152372736774331080527157374379043696696647158270918245668579680394279565181670004245143555617589138267976417280970718829942998800499312890580011246294669585429723974582350357991472101919333996770115834067969654217063942059882195268353998096891812525364797586486311202350700339609637274043915687880562465121559531284337603363356183320193656553931871200575467929714875483123706358278876389849119105053294688326141759401230994901405'); INSERT INTO num_exp_log10 VALUES (6,'-1.328484003982869642690619298690906747763234110040562640557173509402512757735587333095924652711056556491908059708986413635120656426593745303715671199761364516107844087845783714418487426723538440387069985879601248897538855843115404484229652166941838283489828419407478748732927617251897244190697443966424660881366993754577233476597163021768156814527570512834684713730559883782625870597080940193303268818336816535968869931456641949301731046034660616615392129109391145214470757259042172416816936479713743188047425796931722546185493217275537303458837771965375448968719169174136287532752370175863826715450565025635651343928205805494319778539652563499901671319955144823432132740582617949774638538594081514904904341299199113721131520557004571803778698005652464301037962272085633628653321081368256925971558076970172779715'); INSERT INTO num_exp_log10 VALUES (7,'17.913249188669140643510654105014358282516966474257460687880559542190804665566625978925406311113121982595279826214959603627387555578965653325278444455875162277940655989601428868642914577248262147833499137348602966573601719040813549936948178463592211685237720748377879836890106515699728652218324794927458352954247096536337594789471529493944292143186953509162522579060020018226817623648563806559917579317916242706559131476179714031602207057714677845347616752450567251644277767418397621490301286115159509360375419599968738067461569666699939732107480135216621373057421990702923042287910730395998082514702629760389192370666675364405730936537832803383367187639209534697198515928978064543150195911463663617683085348965065679311986715357338675515370634753254774665197233934933271954463040729779956682570415317734489164385'); INSERT INTO num_exp_log10 VALUES (8,'9.929264914121995501917993119394933531225401243275938207624866270551448544301376913376130982251708700134720886862945040266148728213253651323129942781577143957084726727561987639140151337848818195806259935747329665025823709044567138449084349729747202164413995795609659711723455165142329822773177102845804114214340046404641970845707372809306219463962664551623665322610139794354769767829380018857313559373283673392337954610346290037758389035140213224696023751541663171574697035012610534455189013755134090933979479069288110010954211669067225249755249337768792642303351914884187159646984708862430789018895140670365476746734456807215043628059581947593694929159076346249490593187993386780521089745819640214783614157516171005086731241769146397577246387886107367648843380733370112546792442909347322732196805316614555689762'); INSERT INTO num_exp_log10 VALUES (9,'7.739283354261751283625223433456284905560931805428759681411970457812279544250432389511382263439324085689734710188041049046660480575958686859942980599595036769090747781359217248301544587434077376812293034848418204834388504169166350770257248896025815531248627658465029806509131631454856186387892627989218208026727504548130018922325585619738185507999433763118148418722504204066578294826264005398891049629199412773138457218976050467479292777172717500219850781664314597312411301296201533610562886229900497272268364496763758868455934979903774531992886483396489868888731578355541611359130188566524240259770918423445785338175040098706500034487703124623745259139247432324145633151895802637182446905097253961951018926565652497920605819785424451050191604602898777804133717341512568151920576684198443843944721398831404081859'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_exp_power_10_ln VALUES (0,'NaN'); INSERT INTO num_exp_power_10_ln VALUES (1,'225561990715.277245515991117670624124484084762557459065170589803293759247930753528436379932442146759103295277479258327642314622036941865221478746258727236601688778946696303277607709407496616423493315166963938393760548678730128692212077086588682984700837334554241405763691119669847463520746595280034536307041368063462023793177898200220207765205127584303464304601759554817607633012272490650155253979182893585119965271975927569080191838676053084168631217591768468344106219831174026139608715965691941366334940196517120885214887008671956523579678156919416435031020452971977153991139145404842034138317592877675821045409772456977018293365238179815614004574330200783530118851005077771478448804470170641452481992602803877112958872108069738434946694089025321283178188028224338756015337492913115267362635647236447601252924834642796058'); INSERT INTO num_exp_power_10_ln VALUES (2,'9553718264533556311125292459627965006385666643531070061102266984368939757379.536714147420215784125170401370065894858487440153494392538261078415409784085960333028254155527328359894197540839556987826344995348426293585457768226283066583722499658006242709930685932246087653832230889613022921575445199055131152661556678809191264086381976922223866204038615136758192929883317207903579770917317641181652055458721731297347443662717939116561947785705140374908203404860090658919334137955075887697259604047657534191202566335372150375993361370075961180728155127447781364264047857624746079509591666068708743260905728661917791822925979235918475633100283148558978385583805341715868143937062092264994833222352433299015979561976964779350640064096690062929265992966564232453102431600199173711947391200249130712039686700111791790265309426741120465259677894665532560198051256215915373145226284270408649736509'); INSERT INTO num_exp_power_10_ln VALUES (3,'982718444846268846508445482774217796844461660819285525931206164100817251856409365450682.362683768066405322653747385034480250394145008573806022660379219602846285813744865438912887625784087005970975437905783802114553690522787857272953842288090141945268495451006273685577260054069522075046955466204804067271437138871789034722069934693546671607506851844248427950939791205412350536883779850165603116191193657054604569586553874805856647223849267039531773072343908345333155562072887754900969504551717514980465801806565999410206735831440712124661645970935112535081991606671600328471264697018198676317466846450405861359235297846597981143547119390922405594115478086038680663368675222949247096131378724350715530605691796680604309063173515781378545860473572389718345696107553363715518601596249508215455106779522851210398208919496668879040223859884166805448827948087400426315425231119801173387715922086154065273'); INSERT INTO num_exp_power_10_ln VALUES (4,'861542720105376650266753999919217194383259935058507531116.774511336660822591851369622743235084609149542494189385785321912210129989390054947787009383210009523204976629456268332186620016067379702483800883493431423160815760933380418976582725913410929214462739708321325884209636272001805871036779154087677637129248122540412937033791526383240502286607736226090213753913654673523613612439527815137888202973659987501649474772884055648603290154867585312925699571949539600328906295652872654314913539778815035321695215634102441494403825526533235061083947035338872599854931230001361227174477274708230470794066733245241594719912710139298949856243576688344051439047966427547889756037265151798639614843866387316916203238068277912991427278268083231579195846744438643659745041780103653332041031419793815914447232121937821142169172566753399257291244398531365781832297786941359729799400'); INSERT INTO num_exp_power_10_ln VALUES (5,'198021976607570296508.271597639984889464620426933601643322058775615235389194561064983706229795978402690473201671702614911129095149240715527556855309177671128442458698638704394974473956869419481315262823632891676087912529523219333012290621046361106033860210270638559271706082115529424772192777643046125905852037759566224116373416253787241195450409652089019290072319861181399387753223422998872180810295299831487867222464355713552301775702554189470264147325049133532522718679336524769566984150923939420759804463781082299907043016120177416779442865059261387111806785876531152192378576258351599534512031062777609734092707165605364139201322351960602280089186180302246827234844736393745487324460438448807241887783263546165171099497316415863122023114646876909575845860402164818094500541234974716577550807551946414081410743197768993152975501'); INSERT INTO num_exp_power_10_ln VALUES (6,'.000873076977206566818052116526263730226812004454463281371489634779519089200224205946321120805055212090024554381349223642352209212670470260295303361873760972918129853308169576675500721645609379420329169271088810484607337679253503247351324049221970104335289487989027621978310506220905131150125321713385148268584530413680037620544212746920563790371941626294733473967065607791756894237438288480748407449237446113996117912144587258434808327522518688617394025018756570740098795745692805352377041347367240475846033282850136270250633825482156304826383360291164928049344226886150285595932088884965511963310715773499733217615863523253012606066583814112265708693122563204149232245895551314975524172504103194858904869273185785182598234060315036187756490539352752560361560286717869643902435677448962235275054804452967413005'); INSERT INTO num_exp_power_10_ln VALUES (7,'176514565873872717825163931126806100435750.096278384530154766967061948052237623936423931849868926020451465515367348890410352640552194499619062823622476972850692557798609619250753020363520533767813563613425606228355802781302735485038377521515850536680425059519814786118919994914180918228654298075183514200191737597656810036850772127169441661576862538643715648802139886576391427423689320082366572297580054381937437005879583216745596935643579262248665490169331304003204939561361718554509909313409421397022626924406091551900222555950699170864234411017062042057683304265485826061096835531732950909546314722726990314852356462874701181085379772134121978510387397276859318242238150439474660772561390798432890789762504242822787017140808209820627435991445529404692793744568204608385843245177656436105160780897472099970336514833257055017279707999437302548655364559'); INSERT INTO num_exp_power_10_ln VALUES (8,'72941951052009383458167.300747500436981484566111756088702608000390737594784514635592222758882092500858797317505303492923829092720870826490477962201959426813271424853341826896270963213736922458746003100613943600855942721319226948714369219316345322636075285343544788982588956431405042577296229122673590336976893594798942025893296105815818487227300314490440902574022885833779324177053242170024559675073866612316965636832258283516275906085642459351367507561963945012828379111856700009391438637054015804558386733558956649061672420804826896303889067785497738203077050774825608647969196321506624991188638449047860249367840775936911749905927108478444112230174584693363226143549933224252679398881354887872642908328737917862751077365602631600279486028043329404269490375935308156815477700961014566228692743960491745353377403533037122586797765130'); INSERT INTO num_exp_power_10_ln VALUES (9,'661239032819374816.097553651299556484820492272269662685578275493609248662925676004753503494252951243895572437264999063878330704584509915845096232798927524470286655554736724913758600775591269525423912692080421094644542553026831758426157681271572808657664918053119324646138457659418857926209701677786068580819823633713337632456905824562235373422309621872998037966404189020165296080436871220718574009921789858751384547836431858428729570977259373272041837411903005303672798845573379758630607982213326716018594073712340609488043353995410508475153538231445235003980586600882223782814368245305160648543466496726973755388826656879616734762068443462618454921858705377028522664844761719759342490380417060255776725333319537746890406213693117052223545525717132695297770810635066731941724108167146710297146989770382041617889670713111888375717'); COMMIT TRANSACTION; BEGIN TRANSACTION; INSERT INTO num_data VALUES (0, '0'); INSERT INTO num_data VALUES (1, '85243.39540024977626076239847863600785982737155858270959890014613035727868293618673807776733416230953723818527101593495895350807775607346277892835514324320448949370623441059033804864158715021903312693889518990256881059434042443507529601095150710777634743301398926463888783847290873199395304998050753365215426971278237920063435565949203678024225270616295573678510929020831006146661747271783837653203039829647102027431761129518881525935216608429897041525858540380754759125150233053469999022855035'); INSERT INTO num_data VALUES (2, '-994877526002806872754342148749241.04353023451866590387054959174736129501310680280823383331007646306243540953499740615246583399296334239109936336446284803020643582102868247857009494139535009572740621288230740389545481395'); INSERT INTO num_data VALUES (3, '-60302029489319384367663884408085757480.2322712404088283093870869198708849258097125725036189625900174440196153781995220721511009855207486224837798752903681993777275846325950111427710563453217985216966456785944859989497422927661683538629473170704026975786513125842675604577233871570629808699803522400038975396500769162308448069085909755023233588510630417065084295051270219462289785473643946404281422516357503746700705970360169619852905053433235726497292406142332833'); INSERT INTO num_data VALUES (4, '5329378275943663322215245.24931765987630429629836382184742348353920297283690739124220773955591340709935970062776650204659187764581615597720798385015942389765692769739983054442503547211560297249686289665792078548480268091496050883021187158502798880896590227542729659940394038802461081290690995869705131152889309663639310553909874081663091069118126221594338242710530718836025225507189149221049928936955230868771875644038572888630664890573507822342998964954667474300944699078658989010257103569231493090050659723450626338923049035040974032671138430612839043269997482582763267536489504794826476836323549796385028155416935072959933315468068930689064483178204550825728947252440604703474049780550458442808479096492346910001692358508618202898514895453589357'); INSERT INTO num_data VALUES (5, '-652755630.43456071828048833552593835051449845484289562110789582081210403487973096161149072377955192388469356112505543620695003436531392789029513380101663750625024853263344909355177280161504414335005574882649025508632900995595004153086358670541462762210415346958050909878501048483523600711486406055424807840429541335391538322886495085448421556770991545781035298449067051916630343957356635391594362639819978677032855590055900561501350354631803808000307050416047072513406855040715556454205065332997338225626635780147287003130754254277103928406089109802521803537038957372612837169223905290912251006321930223154562110264217937'); INSERT INTO num_data VALUES (6, '0.0469370721950711508944806393077762204079964905145503836835397203739563036579760026190241480514409364'); INSERT INTO num_data VALUES (7, '-818934540071845742'); INSERT INTO num_data VALUES (8, '8496986223.64063724353165506167988570717591150432282238795309964705925283285060558038824227595710139960766584401003765241409149793494330798800'); INSERT INTO num_data VALUES (9, '054863480.34685027005508022756223282084742813020271603840941647350440860843570182437301045468670059279379903480024743452620396345637401505220786389930600883087012615993343976556472498552535317826554614696684732913955544753638726438705858481670766245958647367500212800073774509075408148134050353551558174813940258910304990570172170811882520915334358633'); COMMIT TRANSACTION; -- ****************************** -- * Create indices for faster checks -- ****************************** CREATE UNIQUE INDEX num_exp_add_idx ON num_exp_add (id1, id2); CREATE UNIQUE INDEX num_exp_sub_idx ON num_exp_sub (id1, id2); CREATE UNIQUE INDEX num_exp_div_idx ON num_exp_div (id1, id2); CREATE UNIQUE INDEX num_exp_mul_idx ON num_exp_mul (id1, id2); CREATE UNIQUE INDEX num_exp_sqrt_idx ON num_exp_sqrt (id); CREATE UNIQUE INDEX num_exp_ln_idx ON num_exp_ln (id); CREATE UNIQUE INDEX num_exp_log10_idx ON num_exp_log10 (id); CREATE UNIQUE INDEX num_exp_power_10_ln_idx ON num_exp_power_10_ln (id); VACUUM ANALYZE num_exp_add; VACUUM ANALYZE num_exp_sub; VACUUM ANALYZE num_exp_div; VACUUM ANALYZE num_exp_mul; VACUUM ANALYZE num_exp_sqrt; VACUUM ANALYZE num_exp_ln; VACUUM ANALYZE num_exp_log10; VACUUM ANALYZE num_exp_power_10_ln; -- ****************************** -- * Now check the behaviour of the NUMERIC type -- ****************************** -- ****************************** -- * Addition check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, t1.val + t2.val FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, t2.expected FROM num_result t1, num_exp_add t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != t2.expected; DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, round(t1.val + t2.val, 10) FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, round(t2.expected, 10) as expected FROM num_result t1, num_exp_add t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != round(t2.expected, 10); -- ****************************** -- * Subtraction check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, t1.val - t2.val FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, t2.expected FROM num_result t1, num_exp_sub t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != t2.expected; DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, round(t1.val - t2.val, 40) FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, round(t2.expected, 40) FROM num_result t1, num_exp_sub t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != round(t2.expected, 40); -- ****************************** -- * Multiply check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, t1.val * t2.val FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, t2.expected FROM num_result t1, num_exp_mul t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != t2.expected; DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, round(t1.val * t2.val, 30) FROM num_data t1, num_data t2; SELECT t1.id1, t1.id2, t1.result, round(t2.expected, 30) as expected FROM num_result t1, num_exp_mul t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != round(t2.expected, 30); -- ****************************** -- * Division check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, t1.val / t2.val FROM num_data t1, num_data t2 WHERE t2.val != '0.0'; SELECT t1.id1, t1.id2, t1.result, t2.expected FROM num_result t1, num_exp_div t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != t2.expected; DELETE FROM num_result; INSERT INTO num_result SELECT t1.id, t2.id, round(t1.val / t2.val, 80) FROM num_data t1, num_data t2 WHERE t2.val != '0.0'; SELECT t1.id1, t1.id2, t1.result, round(t2.expected, 80) as expected FROM num_result t1, num_exp_div t2 WHERE t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.result != round(t2.expected, 80); -- ****************************** -- * Square root check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT id, 0, SQRT(ABS(val)) FROM num_data; SELECT t1.id1, t1.result, t2.expected FROM num_result t1, num_exp_sqrt t2 WHERE t1.id1 = t2.id AND t1.result != t2.expected; -- ****************************** -- * Natural logarithm check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT id, 0, LN(ABS(val)) FROM num_data WHERE val != '0.0'; SELECT t1.id1, t1.result, t2.expected FROM num_result t1, num_exp_ln t2 WHERE t1.id1 = t2.id AND t1.result != t2.expected; -- ****************************** -- * Logarithm base 10 check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT id, 0, LOG('10'::numeric, ABS(val)) FROM num_data WHERE val != '0.0'; SELECT t1.id1, t1.result, t2.expected FROM num_result t1, num_exp_log10 t2 WHERE t1.id1 = t2.id AND t1.result != t2.expected; -- ****************************** -- * POW(10, LN(value)) check -- ****************************** DELETE FROM num_result; INSERT INTO num_result SELECT id, 0, POW(numeric '10', LN(ABS(round(val,1000)))) FROM num_data WHERE val != '0.0'; SELECT t1.id1, t1.result, t2.expected FROM num_result t1, num_exp_power_10_ln t2 WHERE t1.id1 = t2.id AND t1.result != t2.expected; -- -- Test code path for raising to integer powers -- -- base less than 1 -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of POW(): -- -- for p in {-20..20} -- do -- b="0.084738" -- r=$(bc -ql <<< "scale=500 ; $b^$p" | head -n 1) -- echo "($b, $p, $r)," -- done WITH t(b, p, bc_result) AS (VALUES (0.084738, -20, 2744326694304960114888.7859130502035257), (0.084738, -19, 232548755422013710215.4459407000481464), (0.084738, -18, 19705716436950597776.2364581230406798), (0.084738, -17, 1669822999434319754.3627249884302211), (0.084738, -16, 141497461326065387.3451885900696001), (0.084738, -15, 11990211877848128.7928565907453178), (0.084738, -14, 1016026574105094.7376490817865767), (0.084738, -13, 86096059836517.5178789078924309), (0.084738, -12, 7295607918426.8214300228969888), (0.084738, -11, 618215223791.6519943372802450), (0.084738, -10, 52386321633.6570066961524534), (0.084738, -9, 4439112122.5928274334185666), (0.084738, -8, 376161483.0442710110530225), (0.084738, -7, 31875171.7502054369346110), (0.084738, -6, 2701038.3037689083149651), (0.084738, -5, 228880.5837847697527935), (0.084738, -4, 19394.8829087538193122), (0.084738, -3, 1643.4835879219811409), (0.084738, -2, 139.2655122733328379), (0.084738, -1, 11.8010809790176780), (0.084738, 0, 1), (0.084738, 1, .084738), (0.084738, 2, .007180528644), (0.084738, 3, .0006084636362353), (0.084738, 4, .0000515599916073), (0.084738, 5, .0000043690905688), (0.084738, 6, .0000003702279966), (0.084738, 7, .0000000313723800), (0.084738, 8, .0000000026584327), (0.084738, 9, .0000000002252703), (0.084738, 10, .0000000000190890), (0.084738, 11, .0000000000016176), (0.084738, 12, .0000000000001371), (0.084738, 13, .0000000000000116), (0.084738, 14, .0000000000000010), (0.084738, 15, .0000000000000001), (0.084738, 16, .0000000000000000), (0.084738, 17, .0000000000000000), (0.084738, 18, .0000000000000000), (0.084738, 19, .0000000000000000), (0.084738, 20, .0000000000000000)) SELECT b, p, bc_result, b^p AS power, b^p - bc_result AS diff FROM t; -- base greater than 1 -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of POW(): -- -- for p in {-20..20} -- do -- b="37.821637" -- r=$(bc -ql <<< "scale=500 ; $b^$p" | head -n 1) -- echo "($b, $p, $r)," -- done WITH t(b, p, bc_result) AS (VALUES (37.821637, -20, .0000000000000000), (37.821637, -19, .0000000000000000), (37.821637, -18, .0000000000000000), (37.821637, -17, .0000000000000000), (37.821637, -16, .0000000000000000), (37.821637, -15, .0000000000000000), (37.821637, -14, .0000000000000000), (37.821637, -13, .0000000000000000), (37.821637, -12, .0000000000000000), (37.821637, -11, .0000000000000000), (37.821637, -10, .0000000000000002), (37.821637, -9, .0000000000000063), (37.821637, -8, .0000000000002388), (37.821637, -7, .0000000000090327), (37.821637, -6, .0000000003416316), (37.821637, -5, .0000000129210673), (37.821637, -4, .0000004886959182), (37.821637, -3, .0000184832796213), (37.821637, -2, .0006990678924066), (37.821637, -1, .0264398920649574), (37.821637, 0, 1), (37.821637, 1, 37.821637), (37.821637, 2, 1430.476225359769), (37.821637, 3, 54102.9525326873775219), (37.821637, 4, 2046262.2313195326271135), (37.821637, 5, 77392987.3197773940323425), (37.821637, 6, 2927129472.7542235178972258), (37.821637, 7, 110708828370.5116321107718772), (37.821637, 8, 4187189119324.7924539711577286), (37.821637, 9, 158366346921451.9852944363360812), (37.821637, 10, 5989674486279224.5007355092228730), (37.821637, 11, 226539294168214309.7083246628376531), (37.821637, 12, 8568086950266418559.9938312759931069), (37.821637, 13, 324059074417413536066.1494087598581043), (37.821637, 14, 12256444679171401239980.3109258799733927), (37.821637, 15, 463558801566202198479885.2069857662592280), (37.821637, 16, 17532552720991931019508170.1002855156233684), (37.821637, 17, 663109844696719094948877928.0672523682648687), (37.821637, 18, 25079899837245684700124994552.6717306599041850), (37.821637, 19, 948562867640665366544581398598.1275771806665398), (37.821637, 20, 35876200451584291931921101974730.6901038166532866)) SELECT b, p, bc_result, b^p AS power, b^p - bc_result AS diff FROM t; -- -- Tests for raising to non-integer powers -- -- base less than 1 -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of POW(): -- -- for n in {-20..20} -- do -- b="0.06933247" -- p="$n.342987" -- r=$(bc -ql <<< "scale=500 ; e($p*l($b))" | head -n 1) -- echo "($b, $p, $r)," -- done WITH t(b, p, bc_result) AS (VALUES (0.06933247, -20.342987, 379149253615977128356318.39406340), (0.06933247, -19.342987, 26287354251852125772450.59436685), (0.06933247, -18.342987, 1822567200045909954554.65766042), (0.06933247, -17.342987, 126363085720167050546.86216560), (0.06933247, -16.342987, 8761064849800910427.02880469), (0.06933247, -15.342987, 607426265866876128.15466179), (0.06933247, -14.342987, 42114363355427213.14899924), (0.06933247, -13.342987, 2919892833909256.59283660), (0.06933247, -12.342987, 202443382310228.51544515), (0.06933247, -11.342987, 14035899730722.44924025), (0.06933247, -10.342987, 973143597003.32229028), (0.06933247, -9.342987, 67470449244.92493259), (0.06933247, -8.342987, 4677892898.16028054), (0.06933247, -7.342987, 324329869.02491071), (0.06933247, -6.342987, 22486590.914273551), (0.06933247, -5.342987, 1559050.8899661435), (0.06933247, -4.342987, 108092.84905705095), (0.06933247, -3.342987, 7494.3442144625131), (0.06933247, -2.342987, 519.60139541889576), (0.06933247, -1.342987, 36.025248159838727), (0.06933247, 0.342987, .40036522320023350), (0.06933247, 1.342987, .02775830982657349), (0.06933247, 2.342987, .001924552183301612), (0.06933247, 3.342987, .0001334339565121935), (0.06933247, 4.342987, .000009251305786862961), (0.06933247, 5.342987, .0000006414158809285026), (0.06933247, 6.342987, .00000004447094732199898), (0.06933247, 7.342987, .000000003083280621074075), (0.06933247, 8.342987, .0000000002137714611621997), (0.06933247, 9.342987, .00000000001482130341788437), (0.06933247, 10.342987, .000000000001027597574581366), (0.06933247, 11.342987, .00000000000007124587801173530), (0.06933247, 12.342987, .000000000000004939652699872298), (0.06933247, 13.342987, .0000000000000003424783226243151), (0.06933247, 14.342987, .00000000000000002374486802900065), (0.06933247, 15.342987, .000000000000000001646290350274646), (0.06933247, 16.342987, .0000000000000000001141413763217064), (0.06933247, 17.342987, .000000000000000000007913703549583420), (0.06933247, 18.342987, .0000000000000000000005486766139403860), (0.06933247, 19.342987, .00000000000000000000003804110487572339), (0.06933247, 20.342987, .000000000000000000000002637483762562946)) SELECT b, p, bc_result, b^p AS power, b^p - bc_result AS diff FROM t; -- base greater than 1 -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of POW(): -- -- for n in {-20..20} -- do -- b="27.234987" -- p="$n.230957" -- r=$(bc -ql <<< "scale=500 ; e($p*l($b))" | head -n 1) -- echo "($b, $p, $r)," -- done WITH t(b, p, bc_result) AS (VALUES (27.234987, -20.230957, .000000000000000000000000000009247064512095633), (27.234987, -19.230957, .0000000000000000000000000002518436817750859), (27.234987, -18.230957, .000000000000000000000000006858959399176602), (27.234987, -17.230957, .0000000000000000000000001868036700701026), (27.234987, -16.230957, .000000000000000000000005087595525911532), (27.234987, -15.230957, .0000000000000000000001385605980094587), (27.234987, -14.230957, .000000000000000000003773696085499835), (27.234987, -13.230957, .0000000000000000001027765638305389), (27.234987, -12.230957, .000000000000000002799118379829397), (27.234987, -11.230957, .00000000000000007623395268611469), (27.234987, -10.230957, .000000000000002076230710364949), (27.234987, -9.230957, .00000000000005654611640579014), (27.234987, -8.230957, .000000000001540032745212181), (27.234987, -7.230957, .00000000004194277179542807), (27.234987, -6.230957, .000000001142310844592450), (27.234987, -5.230957, .00000003111082100243440), (27.234987, -4.230957, .0000008473028055606278), (27.234987, -3.230957, .00002307628089450723), (27.234987, -2.230957, .0006284822101702527), (27.234987, -1.230957, .01711670482371810), (27.234987, 0.230957, 2.1451253063142300), (27.234987, 1.230957, 58.422459830839071), (27.234987, 2.230957, 1591.1349340009243), (27.234987, 3.230957, 43334.539242761031), (27.234987, 4.230957, 1180215.6129275865), (27.234987, 5.230957, 32143156.875279851), (27.234987, 6.230957, 875418459.63720737), (27.234987, 7.230957, 23842010367.779367), (27.234987, 8.230957, 649336842420.336290), (27.234987, 9.230957, 17684680461938.907402), (27.234987, 10.230957, 481642042480060.137900), (27.234987, 11.230957, 13117514765597885.614921), (27.234987, 12.230957, 357255344113366461.949871), (27.234987, 13.230957, 9729844652608062117.440722), (27.234987, 14.230957, 264992192625800087863.690528), (27.234987, 15.230957, 7217058921265161257566.469315), (27.234987, 16.230957, 196556505898890690402726.443417), (27.234987, 17.230957, 5353213882921711267539279.451015), (27.234987, 18.230957, 145794710509592328389185797.837767), (27.234987, 19.230957, 3970717045397510438979206144.696206), (27.234987, 20.230957, 108142427112079606637962972621.121293)) SELECT b, p, bc_result, b^p AS power, b^p - bc_result AS diff FROM t; -- Inputs close to overflow -- -- bc(1) results computed with a scale of 2700 and truncated to 4 decimal -- places. WITH t(b, p, bc_result) AS (VALUES (0.12, -2829.8369, 58463948950011752465280493160293790845494328939320966633018493248607815580903065923369555885857984675501574162389726507612128133630191173383130639968378879506624785786843501848666498440326970769604109017960864573408272864266102690849952650095786874354625921641729880352858506454246180842452983243549491658464046163869265572232996388827878976066830374513768599285647145439771472435206769249126377164951470622827631950210853282324510655982757098065657709137845327135766013147354253426364240746381620690117663724329288646510198895137275207992825719846135857839292915100523542874885080351683587865157015032404901182924720371819942957083390475846809517968191151435281268695782594904484795360890092607679215675240583291240729468370895035823777914792823688291214492607109455017754453939895630226174304357121900605689015734289765672740769194115142607443713769825894380064727556869268488695795705030158832909348803019429370973064732712469794182891757241046263341655894972953512257981661670321890336672832647028099324621932563236459127918144141230217523147304565594514812518826936144181257723061181656522095236928347413997136815409159361412494284201481609684892562646522086577634100783077813105675590737823924220663206479031113753135119759722725207724879578900186075841393115040465401462266086907464970054073340036852442184414587772177753008511913377364966775792477387717262694468450099866775550614257191941835797445874557362115814601886902749237439492398087966544817154173072811937702110580330775581851211123491341435883319798273456296794954514173820352334127081705706502510709179711510240917772628308487366740741280043704807717608366220401933596364641284631036907635403895053036499618723044314773148779735006542501244942039455169872946018271985844759209768927953340447524637670938413827595013338859796135512187473850161303598087634723542727044978083220970836296653305188470017342167913572166172051819741354902582606590658382067039498769674611071582171914886494269818475850690414812481252963932223686078322390396586222238852602472958831686564971334200490182175112490433364675164900946902818404704835106260174052265784055642968397240262737313737007322288203637798365320295080314524864099419556398713380156353062937736280885716820226469419928595465390700629307079710611273715705695938635644841913194091407807776191951797748706106000922803167645881087385311847268311361092838264814899353459146959869764278464187826798546290981492648723002412475976344071283321798061003719251864595518596639432393032991023409676558943539937377229130132816883146259468718344018277257037013406135980469482324577407154032999045733141275895.3432), (1.2, 32908.8896, 58463467728170833376633133695001863276259293590926929026251227859007891876739460057725441400966420577009060860805883032969522911803372870882799865787473726926215148161529632590083389287080925059682489116446754279752928005457087175157581627230586554364417068189211136840990661174760199073702207450133797324318403866058202372178813998850887986769280847189341565507156189065295823921162851958925352114220880236114784962150135485415106748467247897246441194126125699204912883449386043559785865023459356275014504597646990160571664166410683323036984805434677654413174177920726210827006973855410386789516533036723888687725436216478665958434776205940192130053647653715221076841771578099896259902368829351569726536927952661429685419815305418450230567773264738536471211804481206474781470237730069753206249915908804615495060673071058534441654604668770343616386612119048579369195201590008082689834456232255266932976831478404670192731621439902738547169253818323045451045749609624500171633897705543164388470746657118050314064066768449450440405619135824055131398727045420324382226572368236570500391463795989258779677208133531636928003546809249007993065200108076924439703799231711400266122025052209803513232429907231051873161206025860851056337427740362763618748092029386371493898291580557004812947013231371383576580415676519066503391905962989205397824064923920045371823949776899815750413244195402085917098964452866825666226141169411712884994564949174271056284898570445214367063763956186792886147126466387576513166370247576466566827375268334148320298849218878848928271566491769458471357076035396330179659440244425914213309776100351793665960978678576150833311810944729586040624059867137538839913141142139636023129691775489034134511666020819676247950267220131499463010350308195762769192775344260909521732256844149916046793599150786757764962585268686580124987490115873389726527572428003433405659445349155536369077209682951123806333170190998931670309088422483075609203671527331975811507450670132060984691061148836994322505371265263690017938762760088575875666254883673433331627055180154954694693433502522592907190906966067656027637884202418119121728966267936832338377284832958974299187166554160783467156478554899314000348357280306042140481751668215838656488457943830180819301102535170705017482946779698265096226184239631924271857062033454725540956591929965181603262502135610768915716020374362368495244256420143645126927013882334008435586481691725030031204304273292938132599127402133470745819213047706793887965197191137237066440328777206799072470374264316425913530947082957300047105685634407092811630672103242089966046839626911122.7149)) SELECT b, p, bc_result, b^p AS power, b^p - bc_result AS diff FROM t; -- -- Tests for EXP() -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of EXP(): -- -- for n in {-20..20} -- do -- x="$n.29837" -- r=$(bc -ql <<< "scale=500 ; e($x)" | head -n 1) -- echo "($x, $r)," -- done WITH t(x, bc_result) AS (VALUES (-20.29837, .000000001529431101152222), (-19.29837, .000000004157424770142192), (-18.29837, .00000001130105220586304), (-17.29837, .00000003071944485366452), (-16.29837, .00000008350410872606600), (-15.29837, .0000002269877013517336), (-14.29837, .0000006170165438681061), (-13.29837, .000001677224859055276), (-12.29837, .000004559169856609741), (-11.29837, .00001239310857408049), (-10.29837, .00003368796183504298), (-9.29837, .00009157337449401917), (-8.29837, .0002489222398577673), (-7.29837, .0006766408013046928), (-6.29837, .001839300394580514), (-5.29837, .004999736839665763), (-4.29837, .01359069379834070), (-3.29837, .03694333598818056), (-2.29837, .1004223988993283), (-1.29837, .2729763820983097), (0.29837, 1.3476603299656679), (1.29837, 3.6633205858807959), (2.29837, 9.9579377804197108), (3.29837, 27.068481317440698), (4.29837, 73.579760889182206), (5.29837, 200.01052696742555), (6.29837, 543.68498095607070), (7.29837, 1477.8890041389891), (8.29837, 4017.3188244304487), (9.29837, 10920.204759575742), (10.29837, 29684.194161006717), (11.29837, 80690.005580314652), (12.29837, 219338.17590722828), (13.29837, 596222.97785597218), (14.29837, 1620702.0864156289), (15.29837, 4405525.0308492653), (16.29837, 11975458.636179032), (17.29837, 32552671.598188404), (18.29837, 88487335.673150406), (19.29837, 240533516.60908059), (20.29837, 653837887.33381570)) SELECT x, bc_result, exp(x), exp(x)-bc_result AS diff FROM t; -- -- Tests for LN() -- -- input very small -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of LN(): -- -- for p in {1..40} -- do -- l=$(bc -ql <<< "scale=500 ; l(10^-$p)" | head -n 1) -- echo "('1.0e-$p', $l)," -- done WITH t(x, bc_result) AS (VALUES ('1.0e-1', -2.3025850929940457), ('1.0e-2', -4.6051701859880914), ('1.0e-3', -6.9077552789821371), ('1.0e-4', -9.2103403719761827), ('1.0e-5', -11.512925464970228), ('1.0e-6', -13.815510557964274), ('1.0e-7', -16.118095650958320), ('1.0e-8', -18.420680743952365), ('1.0e-9', -20.723265836946411), ('1.0e-10', -23.025850929940457), ('1.0e-11', -25.328436022934503), ('1.0e-12', -27.631021115928548), ('1.0e-13', -29.933606208922594), ('1.0e-14', -32.236191301916640), ('1.0e-15', -34.5387763949106853), ('1.0e-16', -36.84136148790473094), ('1.0e-17', -39.143946580898776628), ('1.0e-18', -41.4465316738928223123), ('1.0e-19', -43.74911676688686799634), ('1.0e-20', -46.051701859880913680360), ('1.0e-21', -48.3542869528749593643778), ('1.0e-22', -50.65687204586900504839581), ('1.0e-23', -52.959457138863050732413803), ('1.0e-24', -55.2620422318570964164317949), ('1.0e-25', -57.56462732485114210044978637), ('1.0e-26', -59.867212417845187784467777822), ('1.0e-27', -62.1697975108392334684857692765), ('1.0e-28', -64.47238260383327915250376073116), ('1.0e-29', -66.774967696827324836521752185847), ('1.0e-30', -69.0775527898213705205397436405309), ('1.0e-31', -71.38013788281541620455773509521529), ('1.0e-32', -73.682722975809461888575726549899655), ('1.0e-33', -75.9853080688035075725937180045840189), ('1.0e-34', -78.28789316179755325661170945926838306), ('1.0e-35', -80.590478254791598940629700913952747266), ('1.0e-36', -82.8930633477856446246476923686371114736), ('1.0e-37', -85.19564844077969030866568382332147568124), ('1.0e-38', -87.498233533773735992683675278005839888842), ('1.0e-39', -89.8008186267677816767016667326902040964430), ('1.0e-40', -92.10340371976182736071965818737456830404406)) SELECT x, bc_result, ln(x::numeric), ln(x::numeric)-bc_result AS diff FROM t; -- input very close to but smaller than 1 -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of LN(): -- -- for p in {1..40} -- do -- l=$(bc -ql <<< "scale=500 ; l(1-10^-$p)" | head -n 1) -- echo "('1.0e-$p', $l)," -- done WITH t(x, bc_result) AS (VALUES ('1.0e-1', -.10536051565782630), ('1.0e-2', -.010050335853501441), ('1.0e-3', -.0010005003335835335), ('1.0e-4', -.00010000500033335834), ('1.0e-5', -.000010000050000333336), ('1.0e-6', -.0000010000005000003333), ('1.0e-7', -.00000010000000500000033), ('1.0e-8', -.000000010000000050000000), ('1.0e-9', -.0000000010000000005000000), ('1.0e-10', -.00000000010000000000500000), ('1.0e-11', -.000000000010000000000050000), ('1.0e-12', -.0000000000010000000000005000), ('1.0e-13', -.00000000000010000000000000500), ('1.0e-14', -.000000000000010000000000000050), ('1.0e-15', -.0000000000000010000000000000005), ('1.0e-16', -.00000000000000010000000000000001), ('1.0e-17', -.000000000000000010000000000000000), ('1.0e-18', -.0000000000000000010000000000000000), ('1.0e-19', -.00000000000000000010000000000000000), ('1.0e-20', -.000000000000000000010000000000000000), ('1.0e-21', -.0000000000000000000010000000000000000), ('1.0e-22', -.00000000000000000000010000000000000000), ('1.0e-23', -.000000000000000000000010000000000000000), ('1.0e-24', -.0000000000000000000000010000000000000000), ('1.0e-25', -.00000000000000000000000010000000000000000), ('1.0e-26', -.000000000000000000000000010000000000000000), ('1.0e-27', -.0000000000000000000000000010000000000000000), ('1.0e-28', -.00000000000000000000000000010000000000000000), ('1.0e-29', -.000000000000000000000000000010000000000000000), ('1.0e-30', -.0000000000000000000000000000010000000000000000), ('1.0e-31', -.00000000000000000000000000000010000000000000000), ('1.0e-32', -.000000000000000000000000000000010000000000000000), ('1.0e-33', -.0000000000000000000000000000000010000000000000000), ('1.0e-34', -.00000000000000000000000000000000010000000000000000), ('1.0e-35', -.000000000000000000000000000000000010000000000000000), ('1.0e-36', -.0000000000000000000000000000000000010000000000000000), ('1.0e-37', -.00000000000000000000000000000000000010000000000000000), ('1.0e-38', -.000000000000000000000000000000000000010000000000000000), ('1.0e-39', -.0000000000000000000000000000000000000010000000000000000), ('1.0e-40', -.00000000000000000000000000000000000000010000000000000000)) SELECT '1-'||x, bc_result, ln(1.0-x::numeric), ln(1.0-x::numeric)-bc_result AS diff FROM t; -- input very close to but larger than 1 -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of LN(): -- -- for p in {1..40} -- do -- l=$(bc -ql <<< "scale=500 ; l(1+10^-$p)" | head -n 1) -- echo "('1.0e-$p', $l)," -- done WITH t(x, bc_result) AS (VALUES ('1.0e-1', .09531017980432486), ('1.0e-2', .009950330853168083), ('1.0e-3', .0009995003330835332), ('1.0e-4', .00009999500033330834), ('1.0e-5', .000009999950000333331), ('1.0e-6', .0000009999995000003333), ('1.0e-7', .00000009999999500000033), ('1.0e-8', .000000009999999950000000), ('1.0e-9', .0000000009999999995000000), ('1.0e-10', .00000000009999999999500000), ('1.0e-11', .000000000009999999999950000), ('1.0e-12', .0000000000009999999999995000), ('1.0e-13', .00000000000009999999999999500), ('1.0e-14', .000000000000009999999999999950), ('1.0e-15', .0000000000000009999999999999995), ('1.0e-16', .00000000000000010000000000000000), ('1.0e-17', .000000000000000010000000000000000), ('1.0e-18', .0000000000000000010000000000000000), ('1.0e-19', .00000000000000000010000000000000000), ('1.0e-20', .000000000000000000010000000000000000), ('1.0e-21', .0000000000000000000010000000000000000), ('1.0e-22', .00000000000000000000010000000000000000), ('1.0e-23', .000000000000000000000010000000000000000), ('1.0e-24', .0000000000000000000000010000000000000000), ('1.0e-25', .00000000000000000000000010000000000000000), ('1.0e-26', .000000000000000000000000010000000000000000), ('1.0e-27', .0000000000000000000000000010000000000000000), ('1.0e-28', .00000000000000000000000000010000000000000000), ('1.0e-29', .000000000000000000000000000010000000000000000), ('1.0e-30', .0000000000000000000000000000010000000000000000), ('1.0e-31', .00000000000000000000000000000010000000000000000), ('1.0e-32', .000000000000000000000000000000010000000000000000), ('1.0e-33', .0000000000000000000000000000000010000000000000000), ('1.0e-34', .00000000000000000000000000000000010000000000000000), ('1.0e-35', .000000000000000000000000000000000010000000000000000), ('1.0e-36', .0000000000000000000000000000000000010000000000000000), ('1.0e-37', .00000000000000000000000000000000000010000000000000000), ('1.0e-38', .000000000000000000000000000000000000010000000000000000), ('1.0e-39', .0000000000000000000000000000000000000010000000000000000), ('1.0e-40', .00000000000000000000000000000000000000010000000000000000)) SELECT '1+'||x, bc_result, ln(1.0+x::numeric), ln(1.0+x::numeric)-bc_result AS diff FROM t; -- input very large -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of LN(): -- -- for p in {1..40} -- do -- l=$(bc -ql <<< "scale=500 ; l(10^$p)" | head -n 1) -- echo "('1.0e$p', $l)," -- done WITH t(x, bc_result) AS (VALUES ('1.0e1', 2.3025850929940457), ('1.0e2', 4.6051701859880914), ('1.0e3', 6.9077552789821371), ('1.0e4', 9.2103403719761827), ('1.0e5', 11.512925464970228), ('1.0e6', 13.815510557964274), ('1.0e7', 16.118095650958320), ('1.0e8', 18.420680743952365), ('1.0e9', 20.723265836946411), ('1.0e10', 23.025850929940457), ('1.0e11', 25.328436022934503), ('1.0e12', 27.631021115928548), ('1.0e13', 29.933606208922594), ('1.0e14', 32.236191301916640), ('1.0e15', 34.538776394910685), ('1.0e16', 36.841361487904731), ('1.0e17', 39.143946580898777), ('1.0e18', 41.446531673892822), ('1.0e19', 43.749116766886868), ('1.0e20', 46.051701859880914), ('1.0e21', 48.354286952874959), ('1.0e22', 50.656872045869005), ('1.0e23', 52.959457138863051), ('1.0e24', 55.262042231857096), ('1.0e25', 57.564627324851142), ('1.0e26', 59.867212417845188), ('1.0e27', 62.169797510839233), ('1.0e28', 64.472382603833279), ('1.0e29', 66.774967696827325), ('1.0e30', 69.077552789821371), ('1.0e31', 71.380137882815416), ('1.0e32', 73.682722975809462), ('1.0e33', 75.985308068803508), ('1.0e34', 78.287893161797553), ('1.0e35', 80.590478254791599), ('1.0e36', 82.893063347785645), ('1.0e37', 85.195648440779690), ('1.0e38', 87.498233533773736), ('1.0e39', 89.800818626767782), ('1.0e40', 92.103403719761827)) SELECT x, bc_result, ln(x::numeric), ln(x::numeric)-bc_result AS diff FROM t; -- input huge -- -- bc(1) results computed with a scale of 1000 and truncated using the script -- below, and then rounded by hand to match the precision of LN(): -- -- for p in {1..10} -- do -- l=$(bc -ql <<< "scale=1000 ; l(10^${p}00)" | head -n 1) -- echo "('1.0e${p}00', $l)," -- done WITH t(x, bc_result) AS (VALUES ('1.0e100', 230.25850929940457), ('1.0e200', 460.51701859880914), ('1.0e300', 690.77552789821371), ('1.0e400', 921.03403719761827), ('1.0e500', 1151.2925464970228), ('1.0e600', 1381.5510557964274), ('1.0e700', 1611.8095650958320), ('1.0e800', 1842.0680743952365), ('1.0e900', 2072.3265836946411), ('1.0e1000', 2302.5850929940457)) SELECT x, bc_result, ln(x::numeric), ln(x::numeric)-bc_result AS diff FROM t; -- -- Tests for LOG() (base 10) -- -- input very small, exact result known WITH t(x) AS (SELECT '1e-'||n FROM generate_series(1, 100) g(n)) SELECT x, log(x::numeric) FROM t; -- input very small, non-exact results -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of LN(): -- -- for p in {1..50..7} -- do -- for d in {9..1..3} -- do -- l=$(bc -ql <<< "scale=500 ; l($d*10^-$p) / l(10)" | head -n 1) -- echo "('${d}.0e-$p', $l)," -- done -- done WITH t(x, bc_result) AS (VALUES ('9.0e-1', -.04575749056067513), ('6.0e-1', -.2218487496163564), ('3.0e-1', -.5228787452803376), ('9.0e-8', -7.045757490560675), ('6.0e-8', -7.221848749616356), ('3.0e-8', -7.522878745280338), ('9.0e-15', -14.0457574905606751), ('6.0e-15', -14.2218487496163564), ('3.0e-15', -14.5228787452803376), ('9.0e-22', -21.04575749056067512540994), ('6.0e-22', -21.22184874961635636749123), ('3.0e-22', -21.52287874528033756270497), ('9.0e-29', -28.045757490560675125409944193490), ('6.0e-29', -28.221848749616356367491233202020), ('3.0e-29', -28.522878745280337562704972096745), ('9.0e-36', -35.0457574905606751254099441934897693816), ('6.0e-36', -35.2218487496163563674912332020203916640), ('3.0e-36', -35.5228787452803375627049720967448846908), ('9.0e-43', -42.04575749056067512540994419348976938159974227), ('6.0e-43', -42.22184874961635636749123320202039166403168125), ('3.0e-43', -42.52287874528033756270497209674488469079987114), ('9.0e-50', -49.045757490560675125409944193489769381599742271618608), ('6.0e-50', -49.221848749616356367491233202020391664031681254347196), ('3.0e-50', -49.522878745280337562704972096744884690799871135809304)) SELECT x, bc_result, log(x::numeric), log(x::numeric)-bc_result AS diff FROM t; -- input very close to but smaller than 1 -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of LN(): -- -- for p in {1..40..7} -- do -- for d in {9..1..3} -- do -- l=$(bc -ql <<< "scale=500 ; l(1-$d*10^-$p) / l(10)" | head -n 1) -- echo "('${d}.0e-$p', $l)," -- done -- done WITH t(x, bc_result) AS (VALUES ('9.0e-1', -1.0000000000000000), ('6.0e-1', -.3979400086720376), ('3.0e-1', -.1549019599857432), ('9.0e-8', -.000000039086505130185422), ('6.0e-8', -.000000026057669695925208), ('3.0e-8', -.000000013028834652530076), ('9.0e-15', -.0000000000000039086503371292840), ('6.0e-15', -.0000000000000026057668914195188), ('3.0e-15', -.0000000000000013028834457097574), ('9.0e-22', -.00000000000000000000039086503371292664), ('6.0e-22', -.00000000000000000000026057668914195110), ('3.0e-22', -.00000000000000000000013028834457097555), ('9.0e-29', -.000000000000000000000000000039086503371292664), ('6.0e-29', -.000000000000000000000000000026057668914195110), ('3.0e-29', -.000000000000000000000000000013028834457097555), ('9.0e-36', -.0000000000000000000000000000000000039086503371292664), ('6.0e-36', -.0000000000000000000000000000000000026057668914195110), ('3.0e-36', -.0000000000000000000000000000000000013028834457097555)) SELECT '1-'||x, bc_result, log(1.0-x::numeric), log(1.0-x::numeric)-bc_result AS diff FROM t; -- input very close to but larger than 1 -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of LN(): -- -- for p in {1..40..7} -- do -- for d in {9..1..3} -- do -- l=$(bc -ql <<< "scale=500 ; l(1+$d*10^-$p) / l(10)" | head -n 1) -- echo "('${d}.0e-$p', $l)," -- done -- done WITH t(x, bc_result) AS (VALUES ('9.0e-1', .2787536009528290), ('6.0e-1', .2041199826559248), ('3.0e-1', .1139433523068368), ('9.0e-8', .000000039086501612400118), ('6.0e-8', .000000026057668132465074), ('3.0e-8', .000000013028834261665042), ('9.0e-15', .0000000000000039086503371292489), ('6.0e-15', .0000000000000026057668914195031), ('3.0e-15', .0000000000000013028834457097535), ('9.0e-22', .00000000000000000000039086503371292664), ('6.0e-22', .00000000000000000000026057668914195110), ('3.0e-22', .00000000000000000000013028834457097555), ('9.0e-29', .000000000000000000000000000039086503371292664), ('6.0e-29', .000000000000000000000000000026057668914195110), ('3.0e-29', .000000000000000000000000000013028834457097555), ('9.0e-36', .0000000000000000000000000000000000039086503371292664), ('6.0e-36', .0000000000000000000000000000000000026057668914195110), ('3.0e-36', .0000000000000000000000000000000000013028834457097555)) SELECT '1+'||x, bc_result, log(1.0+x::numeric), log(1.0+x::numeric)-bc_result AS diff FROM t; -- input very large, exact result known WITH t(x) AS (SELECT '1e'||n FROM generate_series(1, 100) g(n)) SELECT x, log(x::numeric) FROM t; -- input very large, non-exact results -- -- bc(1) results computed with a scale of 500 and truncated using the script -- below, and then rounded by hand to match the precision of LN(): -- -- for p in {10..50..7} -- do -- for d in {2..9..3} -- do -- l=$(bc -ql <<< "scale=500 ; l($d*10^$p) / l(10)" | head -n 1) -- echo "('${d}.0e$p', $l)," -- done -- done WITH t(x, bc_result) AS (VALUES ('2.0e10', 10.301029995663981), ('5.0e10', 10.698970004336019), ('8.0e10', 10.903089986991944), ('2.0e17', 17.301029995663981), ('5.0e17', 17.698970004336019), ('8.0e17', 17.903089986991944), ('2.0e24', 24.301029995663981), ('5.0e24', 24.698970004336019), ('8.0e24', 24.903089986991944), ('2.0e31', 31.301029995663981), ('5.0e31', 31.698970004336019), ('8.0e31', 31.903089986991944), ('2.0e38', 38.301029995663981), ('5.0e38', 38.698970004336019), ('8.0e38', 38.903089986991944), ('2.0e45', 45.30102999566398), ('5.0e45', 45.69897000433602), ('8.0e45', 45.90308998699194)) SELECT x, bc_result, log(x::numeric), log(x::numeric)-bc_result AS diff FROM t; pgFormatter-4.2/t/pg-test-files/sql/numerology.sql000066400000000000000000000041231361326045100223200ustar00rootroot00000000000000-- -- NUMEROLOGY -- Test various combinations of numeric types and functions. -- -- -- Test implicit type conversions -- This fails for Postgres v6.1 (and earlier?) -- so let's try explicit conversions for now - tgl 97/05/07 -- CREATE TABLE TEMP_FLOAT (f1 FLOAT8); INSERT INTO TEMP_FLOAT (f1) SELECT float8(f1) FROM INT4_TBL; INSERT INTO TEMP_FLOAT (f1) SELECT float8(f1) FROM INT2_TBL; SELECT '' AS ten, f1 FROM TEMP_FLOAT ORDER BY f1; -- int4 CREATE TABLE TEMP_INT4 (f1 INT4); INSERT INTO TEMP_INT4 (f1) SELECT int4(f1) FROM FLOAT8_TBL WHERE (f1 > -2147483647) AND (f1 < 2147483647); INSERT INTO TEMP_INT4 (f1) SELECT int4(f1) FROM INT2_TBL; SELECT '' AS nine, f1 FROM TEMP_INT4 ORDER BY f1; -- int2 CREATE TABLE TEMP_INT2 (f1 INT2); INSERT INTO TEMP_INT2 (f1) SELECT int2(f1) FROM FLOAT8_TBL WHERE (f1 >= -32767) AND (f1 <= 32767); INSERT INTO TEMP_INT2 (f1) SELECT int2(f1) FROM INT4_TBL WHERE (f1 >= -32767) AND (f1 <= 32767); SELECT '' AS five, f1 FROM TEMP_INT2 ORDER BY f1; -- -- Group-by combinations -- CREATE TABLE TEMP_GROUP (f1 INT4, f2 INT4, f3 FLOAT8); INSERT INTO TEMP_GROUP SELECT 1, (- i.f1), (- f.f1) FROM INT4_TBL i, FLOAT8_TBL f; INSERT INTO TEMP_GROUP SELECT 2, i.f1, f.f1 FROM INT4_TBL i, FLOAT8_TBL f; SELECT DISTINCT f1 AS two FROM TEMP_GROUP ORDER BY 1; SELECT f1 AS two, max(f3) AS max_float, min(f3) as min_float FROM TEMP_GROUP GROUP BY f1 ORDER BY two, max_float, min_float; -- GROUP BY a result column name is not legal per SQL92, but we accept it -- anyway (if the name is not the name of any column exposed by FROM). SELECT f1 AS two, max(f3) AS max_float, min(f3) AS min_float FROM TEMP_GROUP GROUP BY two ORDER BY two, max_float, min_float; SELECT f1 AS two, (max(f3) + 1) AS max_plus_1, (min(f3) - 1) AS min_minus_1 FROM TEMP_GROUP GROUP BY f1 ORDER BY two, min_minus_1; SELECT f1 AS two, max(f2) + min(f2) AS max_plus_min, min(f3) - 1 AS min_minus_1 FROM TEMP_GROUP GROUP BY f1 ORDER BY two, min_minus_1; DROP TABLE TEMP_INT2; DROP TABLE TEMP_INT4; DROP TABLE TEMP_FLOAT; DROP TABLE TEMP_GROUP; pgFormatter-4.2/t/pg-test-files/sql/object_address.sql000066400000000000000000000232361361326045100231010ustar00rootroot00000000000000-- -- Test for pg_get_object_address -- -- Clean up in case a prior regression run failed SET client_min_messages TO 'warning'; DROP ROLE IF EXISTS regress_addr_user; RESET client_min_messages; CREATE USER regress_addr_user; -- Test generic object addressing/identification functions CREATE SCHEMA addr_nsp; SET search_path TO 'addr_nsp'; CREATE FOREIGN DATA WRAPPER addr_fdw; CREATE SERVER addr_fserv FOREIGN DATA WRAPPER addr_fdw; CREATE TEXT SEARCH DICTIONARY addr_ts_dict (template=simple); CREATE TEXT SEARCH CONFIGURATION addr_ts_conf (copy=english); CREATE TEXT SEARCH TEMPLATE addr_ts_temp (lexize=dsimple_lexize); CREATE TEXT SEARCH PARSER addr_ts_prs (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); CREATE TABLE addr_nsp.gentable ( a serial primary key CONSTRAINT a_chk CHECK (a > 0), b text DEFAULT 'hello'); CREATE TABLE addr_nsp.parttable ( a int PRIMARY KEY ) PARTITION BY RANGE (a); CREATE VIEW addr_nsp.genview AS SELECT * from addr_nsp.gentable; CREATE MATERIALIZED VIEW addr_nsp.genmatview AS SELECT * FROM addr_nsp.gentable; CREATE TYPE addr_nsp.gencomptype AS (a int); CREATE TYPE addr_nsp.genenum AS ENUM ('one', 'two'); CREATE FOREIGN TABLE addr_nsp.genftable (a int) SERVER addr_fserv; CREATE AGGREGATE addr_nsp.genaggr(int4) (sfunc = int4pl, stype = int4); CREATE DOMAIN addr_nsp.gendomain AS int4 CONSTRAINT domconstr CHECK (value > 0); CREATE FUNCTION addr_nsp.trig() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN END; $$; CREATE TRIGGER t BEFORE INSERT ON addr_nsp.gentable FOR EACH ROW EXECUTE PROCEDURE addr_nsp.trig(); CREATE POLICY genpol ON addr_nsp.gentable; CREATE PROCEDURE addr_nsp.proc(int4) LANGUAGE SQL AS $$ $$; CREATE SERVER "integer" FOREIGN DATA WRAPPER addr_fdw; CREATE USER MAPPING FOR regress_addr_user SERVER "integer"; ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user IN SCHEMA public GRANT ALL ON TABLES TO regress_addr_user; ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user REVOKE DELETE ON TABLES FROM regress_addr_user; -- this transform would be quite unsafe to leave lying around, -- except that the SQL language pays no attention to transforms: CREATE TRANSFORM FOR int LANGUAGE SQL ( FROM SQL WITH FUNCTION prsd_lextype(internal), TO SQL WITH FUNCTION int4recv(internal)); CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable; CREATE SUBSCRIPTION addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE); CREATE STATISTICS addr_nsp.gentable_stat ON a, b FROM addr_nsp.gentable; -- test some error cases SELECT pg_get_object_address('stone', '{}', '{}'); SELECT pg_get_object_address('table', '{}', '{}'); SELECT pg_get_object_address('table', '{NULL}', '{}'); -- unrecognized object types DO $$ DECLARE objtype text; BEGIN FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'), ('toast table column'), ('view column'), ('materialized view column') LOOP BEGIN PERFORM pg_get_object_address(objtype, '{one}', '{}'); EXCEPTION WHEN invalid_parameter_value THEN RAISE WARNING 'error for %: %', objtype, sqlerrm; END; END LOOP; END; $$; -- miscellaneous other errors select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}'); select * from pg_get_object_address('operator of access method', '{btree,integer_ops,99}', '{int4,int4}'); select * from pg_get_object_address('function of access method', '{btree,integer_ops,1}', '{int4,bool}'); select * from pg_get_object_address('function of access method', '{btree,integer_ops,99}', '{int4,int4}'); DO $$ DECLARE objtype text; names text[]; args text[]; BEGIN FOR objtype IN VALUES ('table'), ('index'), ('sequence'), ('view'), ('materialized view'), ('foreign table'), ('table column'), ('foreign table column'), ('aggregate'), ('function'), ('procedure'), ('type'), ('cast'), ('table constraint'), ('domain constraint'), ('conversion'), ('default value'), ('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'), ('text search parser'), ('text search dictionary'), ('text search template'), ('text search configuration'), ('policy'), ('user mapping'), ('default acl'), ('transform'), ('operator of access method'), ('function of access method'), ('publication relation') LOOP FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}') LOOP FOR args IN VALUES ('{}'), ('{integer}') LOOP BEGIN PERFORM pg_get_object_address(objtype, names, args); EXCEPTION WHEN OTHERS THEN RAISE WARNING 'error for %,%,%: %', objtype, names, args, sqlerrm; END; END LOOP; END LOOP; END LOOP; END; $$; -- these object types cannot be qualified names SELECT pg_get_object_address('language', '{one}', '{}'); SELECT pg_get_object_address('language', '{one,two}', '{}'); SELECT pg_get_object_address('large object', '{123}', '{}'); SELECT pg_get_object_address('large object', '{123,456}', '{}'); SELECT pg_get_object_address('large object', '{blargh}', '{}'); SELECT pg_get_object_address('schema', '{one}', '{}'); SELECT pg_get_object_address('schema', '{one,two}', '{}'); SELECT pg_get_object_address('role', '{one}', '{}'); SELECT pg_get_object_address('role', '{one,two}', '{}'); SELECT pg_get_object_address('database', '{one}', '{}'); SELECT pg_get_object_address('database', '{one,two}', '{}'); SELECT pg_get_object_address('tablespace', '{one}', '{}'); SELECT pg_get_object_address('tablespace', '{one,two}', '{}'); SELECT pg_get_object_address('foreign-data wrapper', '{one}', '{}'); SELECT pg_get_object_address('foreign-data wrapper', '{one,two}', '{}'); SELECT pg_get_object_address('server', '{one}', '{}'); SELECT pg_get_object_address('server', '{one,two}', '{}'); SELECT pg_get_object_address('extension', '{one}', '{}'); SELECT pg_get_object_address('extension', '{one,two}', '{}'); SELECT pg_get_object_address('event trigger', '{one}', '{}'); SELECT pg_get_object_address('event trigger', '{one,two}', '{}'); SELECT pg_get_object_address('access method', '{one}', '{}'); SELECT pg_get_object_address('access method', '{one,two}', '{}'); SELECT pg_get_object_address('publication', '{one}', '{}'); SELECT pg_get_object_address('publication', '{one,two}', '{}'); SELECT pg_get_object_address('subscription', '{one}', '{}'); SELECT pg_get_object_address('subscription', '{one,two}', '{}'); -- test successful cases WITH objects (type, name, args) AS (VALUES ('table', '{addr_nsp, gentable}'::text[], '{}'::text[]), ('table', '{addr_nsp, parttable}'::text[], '{}'::text[]), ('index', '{addr_nsp, gentable_pkey}', '{}'), ('index', '{addr_nsp, parttable_pkey}', '{}'), ('sequence', '{addr_nsp, gentable_a_seq}', '{}'), -- toast table ('view', '{addr_nsp, genview}', '{}'), ('materialized view', '{addr_nsp, genmatview}', '{}'), ('foreign table', '{addr_nsp, genftable}', '{}'), ('table column', '{addr_nsp, gentable, b}', '{}'), ('foreign table column', '{addr_nsp, genftable, a}', '{}'), ('aggregate', '{addr_nsp, genaggr}', '{int4}'), ('function', '{pg_catalog, pg_identify_object}', '{pg_catalog.oid, pg_catalog.oid, int4}'), ('procedure', '{addr_nsp, proc}', '{int4}'), ('type', '{pg_catalog._int4}', '{}'), ('type', '{addr_nsp.gendomain}', '{}'), ('type', '{addr_nsp.gencomptype}', '{}'), ('type', '{addr_nsp.genenum}', '{}'), ('cast', '{int8}', '{int4}'), ('collation', '{default}', '{}'), ('table constraint', '{addr_nsp, gentable, a_chk}', '{}'), ('domain constraint', '{addr_nsp.gendomain}', '{domconstr}'), ('conversion', '{pg_catalog, ascii_to_mic}', '{}'), ('default value', '{addr_nsp, gentable, b}', '{}'), ('language', '{plpgsql}', '{}'), -- large object ('operator', '{+}', '{int4, int4}'), ('operator class', '{btree, int4_ops}', '{}'), ('operator family', '{btree, integer_ops}', '{}'), ('operator of access method', '{btree,integer_ops,1}', '{integer,integer}'), ('function of access method', '{btree,integer_ops,2}', '{integer,integer}'), ('rule', '{addr_nsp, genview, _RETURN}', '{}'), ('trigger', '{addr_nsp, gentable, t}', '{}'), ('schema', '{addr_nsp}', '{}'), ('text search parser', '{addr_ts_prs}', '{}'), ('text search dictionary', '{addr_ts_dict}', '{}'), ('text search template', '{addr_ts_temp}', '{}'), ('text search configuration', '{addr_ts_conf}', '{}'), ('role', '{regress_addr_user}', '{}'), -- database -- tablespace ('foreign-data wrapper', '{addr_fdw}', '{}'), ('server', '{addr_fserv}', '{}'), ('user mapping', '{regress_addr_user}', '{integer}'), ('default acl', '{regress_addr_user,public}', '{r}'), ('default acl', '{regress_addr_user}', '{r}'), -- extension -- event trigger ('policy', '{addr_nsp, gentable, genpol}', '{}'), ('transform', '{int}', '{sql}'), ('access method', '{btree}', '{}'), ('publication', '{addr_pub}', '{}'), ('publication relation', '{addr_nsp, gentable}', '{addr_pub}'), ('subscription', '{addr_sub}', '{}'), ('statistics object', '{addr_nsp, gentable_stat}', '{}') ) SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.objsubid)).*, -- test roundtrip through pg_identify_object_as_address ROW(pg_identify_object(addr1.classid, addr1.objid, addr1.objsubid)) = ROW(pg_identify_object(addr2.classid, addr2.objid, addr2.objsubid)) FROM objects, pg_get_object_address(type, name, args) addr1, pg_identify_object_as_address(classid, objid, objsubid) ioa(typ,nms,args), pg_get_object_address(typ, nms, ioa.args) as addr2 ORDER BY addr1.classid, addr1.objid, addr1.objsubid; --- --- Cleanup resources --- DROP FOREIGN DATA WRAPPER addr_fdw CASCADE; DROP PUBLICATION addr_pub; DROP SUBSCRIPTION addr_sub; DROP SCHEMA addr_nsp CASCADE; DROP OWNED BY regress_addr_user; DROP USER regress_addr_user; pgFormatter-4.2/t/pg-test-files/sql/oid.sql000066400000000000000000000024471361326045100207020ustar00rootroot00000000000000-- -- OID -- CREATE TABLE OID_TBL(f1 oid); INSERT INTO OID_TBL(f1) VALUES ('1234'); INSERT INTO OID_TBL(f1) VALUES ('1235'); INSERT INTO OID_TBL(f1) VALUES ('987'); INSERT INTO OID_TBL(f1) VALUES ('-1040'); INSERT INTO OID_TBL(f1) VALUES ('99999999'); INSERT INTO OID_TBL(f1) VALUES ('5 '); INSERT INTO OID_TBL(f1) VALUES (' 10 '); -- leading/trailing hard tab is also allowed INSERT INTO OID_TBL(f1) VALUES (' 15 '); -- bad inputs INSERT INTO OID_TBL(f1) VALUES (''); INSERT INTO OID_TBL(f1) VALUES (' '); INSERT INTO OID_TBL(f1) VALUES ('asdfasd'); INSERT INTO OID_TBL(f1) VALUES ('99asdfasd'); INSERT INTO OID_TBL(f1) VALUES ('5 d'); INSERT INTO OID_TBL(f1) VALUES (' 5d'); INSERT INTO OID_TBL(f1) VALUES ('5 5'); INSERT INTO OID_TBL(f1) VALUES (' - 500'); INSERT INTO OID_TBL(f1) VALUES ('32958209582039852935'); INSERT INTO OID_TBL(f1) VALUES ('-23582358720398502385'); SELECT '' AS six, * FROM OID_TBL; SELECT '' AS one, o.* FROM OID_TBL o WHERE o.f1 = 1234; SELECT '' AS five, o.* FROM OID_TBL o WHERE o.f1 <> '1234'; SELECT '' AS three, o.* FROM OID_TBL o WHERE o.f1 <= '1234'; SELECT '' AS two, o.* FROM OID_TBL o WHERE o.f1 < '1234'; SELECT '' AS four, o.* FROM OID_TBL o WHERE o.f1 >= '1234'; SELECT '' AS three, o.* FROM OID_TBL o WHERE o.f1 > '1234'; DROP TABLE OID_TBL; pgFormatter-4.2/t/pg-test-files/sql/oidjoins.sql000066400000000000000000000652641361326045100217530ustar00rootroot00000000000000-- -- This is created by pgsql/src/tools/findoidjoins/make_oidjoins_check -- SELECT ctid, aggfnoid FROM pg_catalog.pg_aggregate fk WHERE aggfnoid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggfnoid); SELECT ctid, aggtransfn FROM pg_catalog.pg_aggregate fk WHERE aggtransfn != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggtransfn); SELECT ctid, aggfinalfn FROM pg_catalog.pg_aggregate fk WHERE aggfinalfn != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggfinalfn); SELECT ctid, aggcombinefn FROM pg_catalog.pg_aggregate fk WHERE aggcombinefn != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggcombinefn); SELECT ctid, aggserialfn FROM pg_catalog.pg_aggregate fk WHERE aggserialfn != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggserialfn); SELECT ctid, aggdeserialfn FROM pg_catalog.pg_aggregate fk WHERE aggdeserialfn != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggdeserialfn); SELECT ctid, aggmtransfn FROM pg_catalog.pg_aggregate fk WHERE aggmtransfn != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggmtransfn); SELECT ctid, aggminvtransfn FROM pg_catalog.pg_aggregate fk WHERE aggminvtransfn != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggminvtransfn); SELECT ctid, aggmfinalfn FROM pg_catalog.pg_aggregate fk WHERE aggmfinalfn != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggmfinalfn); SELECT ctid, aggsortop FROM pg_catalog.pg_aggregate fk WHERE aggsortop != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.aggsortop); SELECT ctid, aggtranstype FROM pg_catalog.pg_aggregate fk WHERE aggtranstype != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.aggtranstype); SELECT ctid, aggmtranstype FROM pg_catalog.pg_aggregate fk WHERE aggmtranstype != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.aggmtranstype); SELECT ctid, amhandler FROM pg_catalog.pg_am fk WHERE amhandler != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amhandler); SELECT ctid, amopfamily FROM pg_catalog.pg_amop fk WHERE amopfamily != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_opfamily pk WHERE pk.oid = fk.amopfamily); SELECT ctid, amoplefttype FROM pg_catalog.pg_amop fk WHERE amoplefttype != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.amoplefttype); SELECT ctid, amoprighttype FROM pg_catalog.pg_amop fk WHERE amoprighttype != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.amoprighttype); SELECT ctid, amopopr FROM pg_catalog.pg_amop fk WHERE amopopr != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.amopopr); SELECT ctid, amopmethod FROM pg_catalog.pg_amop fk WHERE amopmethod != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_am pk WHERE pk.oid = fk.amopmethod); SELECT ctid, amopsortfamily FROM pg_catalog.pg_amop fk WHERE amopsortfamily != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_opfamily pk WHERE pk.oid = fk.amopsortfamily); SELECT ctid, amprocfamily FROM pg_catalog.pg_amproc fk WHERE amprocfamily != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_opfamily pk WHERE pk.oid = fk.amprocfamily); SELECT ctid, amproclefttype FROM pg_catalog.pg_amproc fk WHERE amproclefttype != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.amproclefttype); SELECT ctid, amprocrighttype FROM pg_catalog.pg_amproc fk WHERE amprocrighttype != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.amprocrighttype); SELECT ctid, amproc FROM pg_catalog.pg_amproc fk WHERE amproc != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amproc); SELECT ctid, adrelid FROM pg_catalog.pg_attrdef fk WHERE adrelid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.adrelid); SELECT ctid, attrelid FROM pg_catalog.pg_attribute fk WHERE attrelid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.attrelid); SELECT ctid, atttypid FROM pg_catalog.pg_attribute fk WHERE atttypid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.atttypid); SELECT ctid, attcollation FROM pg_catalog.pg_attribute fk WHERE attcollation != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_collation pk WHERE pk.oid = fk.attcollation); SELECT ctid, roleid FROM pg_catalog.pg_auth_members fk WHERE roleid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.roleid); SELECT ctid, member FROM pg_catalog.pg_auth_members fk WHERE member != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.member); SELECT ctid, grantor FROM pg_catalog.pg_auth_members fk WHERE grantor != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.grantor); SELECT ctid, castsource FROM pg_catalog.pg_cast fk WHERE castsource != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.castsource); SELECT ctid, casttarget FROM pg_catalog.pg_cast fk WHERE casttarget != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.casttarget); SELECT ctid, castfunc FROM pg_catalog.pg_cast fk WHERE castfunc != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.castfunc); SELECT ctid, relnamespace FROM pg_catalog.pg_class fk WHERE relnamespace != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.relnamespace); SELECT ctid, reltype FROM pg_catalog.pg_class fk WHERE reltype != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.reltype); SELECT ctid, reloftype FROM pg_catalog.pg_class fk WHERE reloftype != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.reloftype); SELECT ctid, relowner FROM pg_catalog.pg_class fk WHERE relowner != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.relowner); SELECT ctid, relam FROM pg_catalog.pg_class fk WHERE relam != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_am pk WHERE pk.oid = fk.relam); SELECT ctid, reltablespace FROM pg_catalog.pg_class fk WHERE reltablespace != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_tablespace pk WHERE pk.oid = fk.reltablespace); SELECT ctid, reltoastrelid FROM pg_catalog.pg_class fk WHERE reltoastrelid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.reltoastrelid); SELECT ctid, collnamespace FROM pg_catalog.pg_collation fk WHERE collnamespace != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.collnamespace); SELECT ctid, collowner FROM pg_catalog.pg_collation fk WHERE collowner != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.collowner); SELECT ctid, connamespace FROM pg_catalog.pg_constraint fk WHERE connamespace != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.connamespace); SELECT ctid, conrelid FROM pg_catalog.pg_constraint fk WHERE conrelid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.conrelid); SELECT ctid, contypid FROM pg_catalog.pg_constraint fk WHERE contypid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.contypid); SELECT ctid, conindid FROM pg_catalog.pg_constraint fk WHERE conindid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.conindid); SELECT ctid, conparentid FROM pg_catalog.pg_constraint fk WHERE conparentid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_constraint pk WHERE pk.oid = fk.conparentid); SELECT ctid, confrelid FROM pg_catalog.pg_constraint fk WHERE confrelid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.confrelid); SELECT ctid, connamespace FROM pg_catalog.pg_conversion fk WHERE connamespace != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.connamespace); SELECT ctid, conowner FROM pg_catalog.pg_conversion fk WHERE conowner != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.conowner); SELECT ctid, conproc FROM pg_catalog.pg_conversion fk WHERE conproc != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.conproc); SELECT ctid, datdba FROM pg_catalog.pg_database fk WHERE datdba != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.datdba); SELECT ctid, dattablespace FROM pg_catalog.pg_database fk WHERE dattablespace != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_tablespace pk WHERE pk.oid = fk.dattablespace); SELECT ctid, setdatabase FROM pg_catalog.pg_db_role_setting fk WHERE setdatabase != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_database pk WHERE pk.oid = fk.setdatabase); SELECT ctid, classid FROM pg_catalog.pg_depend fk WHERE classid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.classid); SELECT ctid, refclassid FROM pg_catalog.pg_depend fk WHERE refclassid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.refclassid); SELECT ctid, classoid FROM pg_catalog.pg_description fk WHERE classoid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.classoid); SELECT ctid, enumtypid FROM pg_catalog.pg_enum fk WHERE enumtypid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.enumtypid); SELECT ctid, extowner FROM pg_catalog.pg_extension fk WHERE extowner != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.extowner); SELECT ctid, extnamespace FROM pg_catalog.pg_extension fk WHERE extnamespace != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.extnamespace); SELECT ctid, fdwowner FROM pg_catalog.pg_foreign_data_wrapper fk WHERE fdwowner != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.fdwowner); SELECT ctid, srvowner FROM pg_catalog.pg_foreign_server fk WHERE srvowner != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.srvowner); SELECT ctid, srvfdw FROM pg_catalog.pg_foreign_server fk WHERE srvfdw != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_foreign_data_wrapper pk WHERE pk.oid = fk.srvfdw); SELECT ctid, indexrelid FROM pg_catalog.pg_index fk WHERE indexrelid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.indexrelid); SELECT ctid, indrelid FROM pg_catalog.pg_index fk WHERE indrelid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.indrelid); SELECT ctid, inhrelid FROM pg_catalog.pg_inherits fk WHERE inhrelid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.inhrelid); SELECT ctid, inhparent FROM pg_catalog.pg_inherits fk WHERE inhparent != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.inhparent); SELECT ctid, classoid FROM pg_catalog.pg_init_privs fk WHERE classoid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.classoid); SELECT ctid, lanowner FROM pg_catalog.pg_language fk WHERE lanowner != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.lanowner); SELECT ctid, lanplcallfoid FROM pg_catalog.pg_language fk WHERE lanplcallfoid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.lanplcallfoid); SELECT ctid, laninline FROM pg_catalog.pg_language fk WHERE laninline != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.laninline); SELECT ctid, lanvalidator FROM pg_catalog.pg_language fk WHERE lanvalidator != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.lanvalidator); SELECT ctid, loid FROM pg_catalog.pg_largeobject fk WHERE loid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_largeobject_metadata pk WHERE pk.oid = fk.loid); SELECT ctid, lomowner FROM pg_catalog.pg_largeobject_metadata fk WHERE lomowner != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.lomowner); SELECT ctid, nspowner FROM pg_catalog.pg_namespace fk WHERE nspowner != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.nspowner); SELECT ctid, opcmethod FROM pg_catalog.pg_opclass fk WHERE opcmethod != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_am pk WHERE pk.oid = fk.opcmethod); SELECT ctid, opcnamespace FROM pg_catalog.pg_opclass fk WHERE opcnamespace != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.opcnamespace); SELECT ctid, opcowner FROM pg_catalog.pg_opclass fk WHERE opcowner != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.opcowner); SELECT ctid, opcfamily FROM pg_catalog.pg_opclass fk WHERE opcfamily != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_opfamily pk WHERE pk.oid = fk.opcfamily); SELECT ctid, opcintype FROM pg_catalog.pg_opclass fk WHERE opcintype != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.opcintype); SELECT ctid, opckeytype FROM pg_catalog.pg_opclass fk WHERE opckeytype != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.opckeytype); SELECT ctid, oprnamespace FROM pg_catalog.pg_operator fk WHERE oprnamespace != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.oprnamespace); SELECT ctid, oprowner FROM pg_catalog.pg_operator fk WHERE oprowner != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.oprowner); SELECT ctid, oprleft FROM pg_catalog.pg_operator fk WHERE oprleft != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.oprleft); SELECT ctid, oprright FROM pg_catalog.pg_operator fk WHERE oprright != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.oprright); SELECT ctid, oprresult FROM pg_catalog.pg_operator fk WHERE oprresult != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.oprresult); SELECT ctid, oprcom FROM pg_catalog.pg_operator fk WHERE oprcom != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.oprcom); SELECT ctid, oprnegate FROM pg_catalog.pg_operator fk WHERE oprnegate != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.oprnegate); SELECT ctid, oprcode FROM pg_catalog.pg_operator fk WHERE oprcode != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.oprcode); SELECT ctid, oprrest FROM pg_catalog.pg_operator fk WHERE oprrest != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.oprrest); SELECT ctid, oprjoin FROM pg_catalog.pg_operator fk WHERE oprjoin != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.oprjoin); SELECT ctid, opfmethod FROM pg_catalog.pg_opfamily fk WHERE opfmethod != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_am pk WHERE pk.oid = fk.opfmethod); SELECT ctid, opfnamespace FROM pg_catalog.pg_opfamily fk WHERE opfnamespace != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.opfnamespace); SELECT ctid, opfowner FROM pg_catalog.pg_opfamily fk WHERE opfowner != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.opfowner); SELECT ctid, partrelid FROM pg_catalog.pg_partitioned_table fk WHERE partrelid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.partrelid); SELECT ctid, partdefid FROM pg_catalog.pg_partitioned_table fk WHERE partdefid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.partdefid); SELECT ctid, polrelid FROM pg_catalog.pg_policy fk WHERE polrelid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.polrelid); SELECT ctid, pronamespace FROM pg_catalog.pg_proc fk WHERE pronamespace != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.pronamespace); SELECT ctid, proowner FROM pg_catalog.pg_proc fk WHERE proowner != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.proowner); SELECT ctid, prolang FROM pg_catalog.pg_proc fk WHERE prolang != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_language pk WHERE pk.oid = fk.prolang); SELECT ctid, provariadic FROM pg_catalog.pg_proc fk WHERE provariadic != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.provariadic); SELECT ctid, prosupport FROM pg_catalog.pg_proc fk WHERE prosupport != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prosupport); SELECT ctid, prorettype FROM pg_catalog.pg_proc fk WHERE prorettype != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.prorettype); SELECT ctid, rngtypid FROM pg_catalog.pg_range fk WHERE rngtypid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.rngtypid); SELECT ctid, rngsubtype FROM pg_catalog.pg_range fk WHERE rngsubtype != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.rngsubtype); SELECT ctid, rngcollation FROM pg_catalog.pg_range fk WHERE rngcollation != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_collation pk WHERE pk.oid = fk.rngcollation); SELECT ctid, rngsubopc FROM pg_catalog.pg_range fk WHERE rngsubopc != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_opclass pk WHERE pk.oid = fk.rngsubopc); SELECT ctid, rngcanonical FROM pg_catalog.pg_range fk WHERE rngcanonical != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.rngcanonical); SELECT ctid, rngsubdiff FROM pg_catalog.pg_range fk WHERE rngsubdiff != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.rngsubdiff); SELECT ctid, ev_class FROM pg_catalog.pg_rewrite fk WHERE ev_class != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.ev_class); SELECT ctid, seqrelid FROM pg_catalog.pg_sequence fk WHERE seqrelid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.seqrelid); SELECT ctid, seqtypid FROM pg_catalog.pg_sequence fk WHERE seqtypid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.seqtypid); SELECT ctid, refclassid FROM pg_catalog.pg_shdepend fk WHERE refclassid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.refclassid); SELECT ctid, classoid FROM pg_catalog.pg_shdescription fk WHERE classoid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.classoid); SELECT ctid, starelid FROM pg_catalog.pg_statistic fk WHERE starelid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.starelid); SELECT ctid, staop1 FROM pg_catalog.pg_statistic fk WHERE staop1 != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.staop1); SELECT ctid, staop2 FROM pg_catalog.pg_statistic fk WHERE staop2 != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.staop2); SELECT ctid, staop3 FROM pg_catalog.pg_statistic fk WHERE staop3 != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.staop3); SELECT ctid, staop4 FROM pg_catalog.pg_statistic fk WHERE staop4 != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.staop4); SELECT ctid, staop5 FROM pg_catalog.pg_statistic fk WHERE staop5 != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.staop5); SELECT ctid, stxrelid FROM pg_catalog.pg_statistic_ext fk WHERE stxrelid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.stxrelid); SELECT ctid, stxnamespace FROM pg_catalog.pg_statistic_ext fk WHERE stxnamespace != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.stxnamespace); SELECT ctid, stxowner FROM pg_catalog.pg_statistic_ext fk WHERE stxowner != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.stxowner); SELECT ctid, spcowner FROM pg_catalog.pg_tablespace fk WHERE spcowner != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.spcowner); SELECT ctid, trftype FROM pg_catalog.pg_transform fk WHERE trftype != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.trftype); SELECT ctid, trflang FROM pg_catalog.pg_transform fk WHERE trflang != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_language pk WHERE pk.oid = fk.trflang); SELECT ctid, trffromsql FROM pg_catalog.pg_transform fk WHERE trffromsql != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.trffromsql); SELECT ctid, trftosql FROM pg_catalog.pg_transform fk WHERE trftosql != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.trftosql); SELECT ctid, tgrelid FROM pg_catalog.pg_trigger fk WHERE tgrelid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.tgrelid); SELECT ctid, tgfoid FROM pg_catalog.pg_trigger fk WHERE tgfoid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.tgfoid); SELECT ctid, tgconstrrelid FROM pg_catalog.pg_trigger fk WHERE tgconstrrelid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.tgconstrrelid); SELECT ctid, tgconstrindid FROM pg_catalog.pg_trigger fk WHERE tgconstrindid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.tgconstrindid); SELECT ctid, tgconstraint FROM pg_catalog.pg_trigger fk WHERE tgconstraint != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_constraint pk WHERE pk.oid = fk.tgconstraint); SELECT ctid, cfgnamespace FROM pg_catalog.pg_ts_config fk WHERE cfgnamespace != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.cfgnamespace); SELECT ctid, cfgowner FROM pg_catalog.pg_ts_config fk WHERE cfgowner != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.cfgowner); SELECT ctid, cfgparser FROM pg_catalog.pg_ts_config fk WHERE cfgparser != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_ts_parser pk WHERE pk.oid = fk.cfgparser); SELECT ctid, mapcfg FROM pg_catalog.pg_ts_config_map fk WHERE mapcfg != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_ts_config pk WHERE pk.oid = fk.mapcfg); SELECT ctid, mapdict FROM pg_catalog.pg_ts_config_map fk WHERE mapdict != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_ts_dict pk WHERE pk.oid = fk.mapdict); SELECT ctid, dictnamespace FROM pg_catalog.pg_ts_dict fk WHERE dictnamespace != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.dictnamespace); SELECT ctid, dictowner FROM pg_catalog.pg_ts_dict fk WHERE dictowner != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.dictowner); SELECT ctid, dicttemplate FROM pg_catalog.pg_ts_dict fk WHERE dicttemplate != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_ts_template pk WHERE pk.oid = fk.dicttemplate); SELECT ctid, prsnamespace FROM pg_catalog.pg_ts_parser fk WHERE prsnamespace != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.prsnamespace); SELECT ctid, prsstart FROM pg_catalog.pg_ts_parser fk WHERE prsstart != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prsstart); SELECT ctid, prstoken FROM pg_catalog.pg_ts_parser fk WHERE prstoken != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prstoken); SELECT ctid, prsend FROM pg_catalog.pg_ts_parser fk WHERE prsend != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prsend); SELECT ctid, prsheadline FROM pg_catalog.pg_ts_parser fk WHERE prsheadline != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prsheadline); SELECT ctid, prslextype FROM pg_catalog.pg_ts_parser fk WHERE prslextype != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prslextype); SELECT ctid, tmplnamespace FROM pg_catalog.pg_ts_template fk WHERE tmplnamespace != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.tmplnamespace); SELECT ctid, tmplinit FROM pg_catalog.pg_ts_template fk WHERE tmplinit != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.tmplinit); SELECT ctid, tmpllexize FROM pg_catalog.pg_ts_template fk WHERE tmpllexize != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.tmpllexize); SELECT ctid, typnamespace FROM pg_catalog.pg_type fk WHERE typnamespace != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.typnamespace); SELECT ctid, typowner FROM pg_catalog.pg_type fk WHERE typowner != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.typowner); SELECT ctid, typrelid FROM pg_catalog.pg_type fk WHERE typrelid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.typrelid); SELECT ctid, typelem FROM pg_catalog.pg_type fk WHERE typelem != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.typelem); SELECT ctid, typarray FROM pg_catalog.pg_type fk WHERE typarray != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.typarray); SELECT ctid, typinput FROM pg_catalog.pg_type fk WHERE typinput != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typinput); SELECT ctid, typoutput FROM pg_catalog.pg_type fk WHERE typoutput != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typoutput); SELECT ctid, typreceive FROM pg_catalog.pg_type fk WHERE typreceive != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typreceive); SELECT ctid, typsend FROM pg_catalog.pg_type fk WHERE typsend != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typsend); SELECT ctid, typmodin FROM pg_catalog.pg_type fk WHERE typmodin != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typmodin); SELECT ctid, typmodout FROM pg_catalog.pg_type fk WHERE typmodout != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typmodout); SELECT ctid, typanalyze FROM pg_catalog.pg_type fk WHERE typanalyze != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typanalyze); SELECT ctid, typbasetype FROM pg_catalog.pg_type fk WHERE typbasetype != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.typbasetype); SELECT ctid, typcollation FROM pg_catalog.pg_type fk WHERE typcollation != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_collation pk WHERE pk.oid = fk.typcollation); SELECT ctid, conpfeqop FROM (SELECT ctid, unnest(conpfeqop) AS conpfeqop FROM pg_catalog.pg_constraint) fk WHERE conpfeqop != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.conpfeqop); SELECT ctid, conppeqop FROM (SELECT ctid, unnest(conppeqop) AS conppeqop FROM pg_catalog.pg_constraint) fk WHERE conppeqop != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.conppeqop); SELECT ctid, conffeqop FROM (SELECT ctid, unnest(conffeqop) AS conffeqop FROM pg_catalog.pg_constraint) fk WHERE conffeqop != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.conffeqop); SELECT ctid, conexclop FROM (SELECT ctid, unnest(conexclop) AS conexclop FROM pg_catalog.pg_constraint) fk WHERE conexclop != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.conexclop); SELECT ctid, proallargtypes FROM (SELECT ctid, unnest(proallargtypes) AS proallargtypes FROM pg_catalog.pg_proc) fk WHERE proallargtypes != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.proallargtypes); pgFormatter-4.2/t/pg-test-files/sql/opr_sanity.sql000066400000000000000000001506461361326045100223230ustar00rootroot00000000000000-- -- OPR_SANITY -- Sanity checks for common errors in making operator/procedure system tables: -- pg_operator, pg_proc, pg_cast, pg_conversion, pg_aggregate, pg_am, -- pg_amop, pg_amproc, pg_opclass, pg_opfamily, pg_index. -- -- Every test failure in this file should be closely inspected. -- The description of the failing test should be read carefully before -- adjusting the expected output. In most cases, the queries should -- not find *any* matching entries. -- -- NB: we assume the oidjoins test will have caught any dangling links, -- that is OID or REGPROC fields that are not zero and do not match some -- row in the linked-to table. However, if we want to enforce that a link -- field can't be 0, we have to check it here. -- -- NB: run this test earlier than the create_operator test, because -- that test creates some bogus operators... -- Helper functions to deal with cases where binary-coercible matches are -- allowed. -- This should match IsBinaryCoercible() in parse_coerce.c. -- It doesn't currently know about some cases, notably domains, anyelement, -- anynonarray, anyenum, or record, but it doesn't need to (yet). create function binary_coercible(oid, oid) returns bool as $$ begin if $1 = $2 then return true; end if; if EXISTS(select 1 from pg_catalog.pg_cast where castsource = $1 and casttarget = $2 and castmethod = 'b' and castcontext = 'i') then return true; end if; if $2 = 'pg_catalog.any'::pg_catalog.regtype then return true; end if; if $2 = 'pg_catalog.anyarray'::pg_catalog.regtype then if EXISTS(select 1 from pg_catalog.pg_type where oid = $1 and typelem != 0 and typlen = -1) then return true; end if; end if; if $2 = 'pg_catalog.anyrange'::pg_catalog.regtype then if (select typtype from pg_catalog.pg_type where oid = $1) = 'r' then return true; end if; end if; return false; end $$ language plpgsql strict stable; -- This one ignores castcontext, so it will allow cases where an explicit -- (but still binary) cast would be required to convert the input type. -- We don't currently use this for any tests in this file, but it is a -- reasonable alternative definition for some scenarios. create function explicitly_binary_coercible(oid, oid) returns bool as $$ begin if $1 = $2 then return true; end if; if EXISTS(select 1 from pg_catalog.pg_cast where castsource = $1 and casttarget = $2 and castmethod = 'b') then return true; end if; if $2 = 'pg_catalog.any'::pg_catalog.regtype then return true; end if; if $2 = 'pg_catalog.anyarray'::pg_catalog.regtype then if EXISTS(select 1 from pg_catalog.pg_type where oid = $1 and typelem != 0 and typlen = -1) then return true; end if; end if; if $2 = 'pg_catalog.anyrange'::pg_catalog.regtype then if (select typtype from pg_catalog.pg_type where oid = $1) = 'r' then return true; end if; end if; return false; end $$ language plpgsql strict stable; -- **************** pg_proc **************** -- Look for illegal values in pg_proc fields. SELECT p1.oid, p1.proname FROM pg_proc as p1 WHERE p1.prolang = 0 OR p1.prorettype = 0 OR p1.pronargs < 0 OR p1.pronargdefaults < 0 OR p1.pronargdefaults > p1.pronargs OR array_lower(p1.proargtypes, 1) != 0 OR array_upper(p1.proargtypes, 1) != p1.pronargs-1 OR 0::oid = ANY (p1.proargtypes) OR procost <= 0 OR CASE WHEN proretset THEN prorows <= 0 ELSE prorows != 0 END OR prokind NOT IN ('f', 'a', 'w', 'p') OR provolatile NOT IN ('i', 's', 'v') OR proparallel NOT IN ('s', 'r', 'u'); -- prosrc should never be null or empty SELECT p1.oid, p1.proname FROM pg_proc as p1 WHERE prosrc IS NULL OR prosrc = '' OR prosrc = '-'; -- proretset should only be set for normal functions SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE proretset AND prokind != 'f'; -- currently, no built-in functions should be SECURITY DEFINER; -- this might change in future, but there will probably never be many. SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE prosecdef ORDER BY 1; -- pronargdefaults should be 0 iff proargdefaults is null SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE (pronargdefaults <> 0) != (proargdefaults IS NOT NULL); -- probin should be non-empty for C functions, null everywhere else SELECT p1.oid, p1.proname FROM pg_proc as p1 WHERE prolang = 13 AND (probin IS NULL OR probin = '' OR probin = '-'); SELECT p1.oid, p1.proname FROM pg_proc as p1 WHERE prolang != 13 AND probin IS NOT NULL; -- Look for conflicting proc definitions (same names and input datatypes). -- (This test should be dead code now that we have the unique index -- pg_proc_proname_args_nsp_index, but I'll leave it in anyway.) SELECT p1.oid, p1.proname, p2.oid, p2.proname FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.proname = p2.proname AND p1.pronargs = p2.pronargs AND p1.proargtypes = p2.proargtypes; -- Considering only built-in procs (prolang = 12), look for multiple uses -- of the same internal function (ie, matching prosrc fields). It's OK to -- have several entries with different pronames for the same internal function, -- but conflicts in the number of arguments and other critical items should -- be complained of. (We don't check data types here; see next query.) -- Note: ignore aggregate functions here, since they all point to the same -- dummy built-in function. SELECT p1.oid, p1.proname, p2.oid, p2.proname FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid < p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND (p1.prokind != 'a' OR p2.prokind != 'a') AND (p1.prolang != p2.prolang OR p1.prokind != p2.prokind OR p1.prosecdef != p2.prosecdef OR p1.proleakproof != p2.proleakproof OR p1.proisstrict != p2.proisstrict OR p1.proretset != p2.proretset OR p1.provolatile != p2.provolatile OR p1.pronargs != p2.pronargs); -- Look for uses of different type OIDs in the argument/result type fields -- for different aliases of the same built-in function. -- This indicates that the types are being presumed to be binary-equivalent, -- or that the built-in function is prepared to deal with different types. -- That's not wrong, necessarily, but we make lists of all the types being -- so treated. Note that the expected output of this part of the test will -- need to be modified whenever new pairs of types are made binary-equivalent, -- or when new polymorphic built-in functions are added! -- Note: ignore aggregate functions here, since they all point to the same -- dummy built-in function. Likewise, ignore range constructor functions. SELECT DISTINCT p1.prorettype, p2.prorettype FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND p1.prokind != 'a' AND p2.prokind != 'a' AND p1.prosrc NOT LIKE E'range\\_constructor_' AND p2.prosrc NOT LIKE E'range\\_constructor_' AND (p1.prorettype < p2.prorettype) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0] FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND p1.prokind != 'a' AND p2.prokind != 'a' AND p1.prosrc NOT LIKE E'range\\_constructor_' AND p2.prosrc NOT LIKE E'range\\_constructor_' AND (p1.proargtypes[0] < p2.proargtypes[0]) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1] FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND p1.prokind != 'a' AND p2.prokind != 'a' AND p1.prosrc NOT LIKE E'range\\_constructor_' AND p2.prosrc NOT LIKE E'range\\_constructor_' AND (p1.proargtypes[1] < p2.proargtypes[1]) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2] FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND p1.prokind != 'a' AND p2.prokind != 'a' AND (p1.proargtypes[2] < p2.proargtypes[2]) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[3], p2.proargtypes[3] FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND p1.prokind != 'a' AND p2.prokind != 'a' AND (p1.proargtypes[3] < p2.proargtypes[3]) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[4], p2.proargtypes[4] FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND p1.prokind != 'a' AND p2.prokind != 'a' AND (p1.proargtypes[4] < p2.proargtypes[4]) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[5], p2.proargtypes[5] FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND p1.prokind != 'a' AND p2.prokind != 'a' AND (p1.proargtypes[5] < p2.proargtypes[5]) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[6], p2.proargtypes[6] FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND p1.prokind != 'a' AND p2.prokind != 'a' AND (p1.proargtypes[6] < p2.proargtypes[6]) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[7], p2.proargtypes[7] FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND p1.prokind != 'a' AND p2.prokind != 'a' AND (p1.proargtypes[7] < p2.proargtypes[7]) ORDER BY 1, 2; -- Look for functions that return type "internal" and do not have any -- "internal" argument. Such a function would be a security hole since -- it might be used to call an internal function from an SQL command. -- As of 7.3 this query should find only internal_in, which is safe because -- it always throws an error when called. SELECT p1.oid, p1.proname FROM pg_proc as p1 WHERE p1.prorettype = 'internal'::regtype AND NOT 'internal'::regtype = ANY (p1.proargtypes); -- Look for functions that return a polymorphic type and do not have any -- polymorphic argument. Calls of such functions would be unresolvable -- at parse time. As of 9.6 this query should find only some input functions -- and GiST support functions associated with these pseudotypes. SELECT p1.oid, p1.proname FROM pg_proc as p1 WHERE p1.prorettype IN ('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype, 'anyenum'::regtype, 'anyrange'::regtype) AND NOT ('anyelement'::regtype = ANY (p1.proargtypes) OR 'anyarray'::regtype = ANY (p1.proargtypes) OR 'anynonarray'::regtype = ANY (p1.proargtypes) OR 'anyenum'::regtype = ANY (p1.proargtypes) OR 'anyrange'::regtype = ANY (p1.proargtypes)) ORDER BY 2; -- Look for functions that accept cstring and are neither datatype input -- functions nor encoding conversion functions. It's almost never a good -- idea to use cstring input for a function meant to be called from SQL; -- text should be used instead, because cstring lacks suitable casts. -- As of 9.6 this query should find only cstring_out and cstring_send. -- However, we must manually exclude shell_in, which might or might not be -- rejected by the EXISTS clause depending on whether there are currently -- any shell types. SELECT p1.oid, p1.proname FROM pg_proc as p1 WHERE 'cstring'::regtype = ANY (p1.proargtypes) AND NOT EXISTS(SELECT 1 FROM pg_type WHERE typinput = p1.oid) AND NOT EXISTS(SELECT 1 FROM pg_conversion WHERE conproc = p1.oid) AND p1.oid != 'shell_in(cstring)'::regprocedure ORDER BY 1; -- Likewise, look for functions that return cstring and aren't datatype output -- functions nor typmod output functions. -- As of 9.6 this query should find only cstring_in and cstring_recv. -- However, we must manually exclude shell_out. SELECT p1.oid, p1.proname FROM pg_proc as p1 WHERE p1.prorettype = 'cstring'::regtype AND NOT EXISTS(SELECT 1 FROM pg_type WHERE typoutput = p1.oid) AND NOT EXISTS(SELECT 1 FROM pg_type WHERE typmodout = p1.oid) AND p1.oid != 'shell_out(opaque)'::regprocedure ORDER BY 1; -- Check for length inconsistencies between the various argument-info arrays. SELECT p1.oid, p1.proname FROM pg_proc as p1 WHERE proallargtypes IS NOT NULL AND array_length(proallargtypes,1) < array_length(proargtypes,1); SELECT p1.oid, p1.proname FROM pg_proc as p1 WHERE proargmodes IS NOT NULL AND array_length(proargmodes,1) < array_length(proargtypes,1); SELECT p1.oid, p1.proname FROM pg_proc as p1 WHERE proargnames IS NOT NULL AND array_length(proargnames,1) < array_length(proargtypes,1); SELECT p1.oid, p1.proname FROM pg_proc as p1 WHERE proallargtypes IS NOT NULL AND proargmodes IS NOT NULL AND array_length(proallargtypes,1) <> array_length(proargmodes,1); SELECT p1.oid, p1.proname FROM pg_proc as p1 WHERE proallargtypes IS NOT NULL AND proargnames IS NOT NULL AND array_length(proallargtypes,1) <> array_length(proargnames,1); SELECT p1.oid, p1.proname FROM pg_proc as p1 WHERE proargmodes IS NOT NULL AND proargnames IS NOT NULL AND array_length(proargmodes,1) <> array_length(proargnames,1); -- Check that proallargtypes matches proargtypes SELECT p1.oid, p1.proname, p1.proargtypes, p1.proallargtypes, p1.proargmodes FROM pg_proc as p1 WHERE proallargtypes IS NOT NULL AND ARRAY(SELECT unnest(proargtypes)) <> ARRAY(SELECT proallargtypes[i] FROM generate_series(1, array_length(proallargtypes, 1)) g(i) WHERE proargmodes IS NULL OR proargmodes[i] IN ('i', 'b', 'v')); -- Check for prosupport functions with the wrong signature SELECT p1.oid, p1.proname, p2.oid, p2.proname FROM pg_proc AS p1, pg_proc AS p2 WHERE p2.oid = p1.prosupport AND (p2.prorettype != 'internal'::regtype OR p2.proretset OR p2.pronargs != 1 OR p2.proargtypes[0] != 'internal'::regtype); -- Insist that all built-in pg_proc entries have descriptions SELECT p1.oid, p1.proname FROM pg_proc as p1 LEFT JOIN pg_description as d ON p1.tableoid = d.classoid and p1.oid = d.objoid and d.objsubid = 0 WHERE d.classoid IS NULL AND p1.oid <= 9999; -- List of built-in leakproof functions -- -- Leakproof functions should only be added after carefully -- scrutinizing all possibly executed codepaths for possible -- information leaks. Don't add functions here unless you know what a -- leakproof function is. If unsure, don't mark it as such. -- temporarily disable fancy output, so catalog changes create less diff noise \a\t SELECT p1.oid::regprocedure FROM pg_proc p1 JOIN pg_namespace pn ON pronamespace = pn.oid WHERE nspname = 'pg_catalog' AND proleakproof ORDER BY 1; -- restore normal output mode \a\t -- List of functions used by libpq's fe-lobj.c -- -- If the output of this query changes, you probably broke libpq. -- lo_initialize() assumes that there will be at most one match for -- each listed name. select proname, oid from pg_catalog.pg_proc where proname in ( 'lo_open', 'lo_close', 'lo_creat', 'lo_create', 'lo_unlink', 'lo_lseek', 'lo_lseek64', 'lo_tell', 'lo_tell64', 'lo_truncate', 'lo_truncate64', 'loread', 'lowrite') and pronamespace = (select oid from pg_catalog.pg_namespace where nspname = 'pg_catalog') order by 1; -- Check that all immutable functions are marked parallel safe SELECT p1.oid, p1.proname FROM pg_proc AS p1 WHERE provolatile = 'i' AND proparallel = 'u'; -- **************** pg_cast **************** -- Catch bogus values in pg_cast columns (other than cases detected by -- oidjoins test). SELECT * FROM pg_cast c WHERE castsource = 0 OR casttarget = 0 OR castcontext NOT IN ('e', 'a', 'i') OR castmethod NOT IN ('f', 'b' ,'i'); -- Check that castfunc is nonzero only for cast methods that need a function, -- and zero otherwise SELECT * FROM pg_cast c WHERE (castmethod = 'f' AND castfunc = 0) OR (castmethod IN ('b', 'i') AND castfunc <> 0); -- Look for casts to/from the same type that aren't length coercion functions. -- (We assume they are length coercions if they take multiple arguments.) -- Such entries are not necessarily harmful, but they are useless. SELECT * FROM pg_cast c WHERE castsource = casttarget AND castfunc = 0; SELECT c.* FROM pg_cast c, pg_proc p WHERE c.castfunc = p.oid AND p.pronargs < 2 AND castsource = casttarget; -- Look for cast functions that don't have the right signature. The -- argument and result types in pg_proc must be the same as, or binary -- compatible with, what it says in pg_cast. -- As a special case, we allow casts from CHAR(n) that use functions -- declared to take TEXT. This does not pass the binary-coercibility test -- because CHAR(n)-to-TEXT normally invokes rtrim(). However, the results -- are the same, so long as the function is one that ignores trailing blanks. SELECT c.* FROM pg_cast c, pg_proc p WHERE c.castfunc = p.oid AND (p.pronargs < 1 OR p.pronargs > 3 OR NOT (binary_coercible(c.castsource, p.proargtypes[0]) OR (c.castsource = 'character'::regtype AND p.proargtypes[0] = 'text'::regtype)) OR NOT binary_coercible(p.prorettype, c.casttarget)); SELECT c.* FROM pg_cast c, pg_proc p WHERE c.castfunc = p.oid AND ((p.pronargs > 1 AND p.proargtypes[1] != 'int4'::regtype) OR (p.pronargs > 2 AND p.proargtypes[2] != 'bool'::regtype)); -- Look for binary compatible casts that do not have the reverse -- direction registered as well, or where the reverse direction is not -- also binary compatible. This is legal, but usually not intended. -- As of 7.4, this finds the casts from text and varchar to bpchar, because -- those are binary-compatible while the reverse way goes through rtrim(). -- As of 8.2, this finds the cast from cidr to inet, because that is a -- trivial binary coercion while the other way goes through inet_to_cidr(). -- As of 8.3, this finds the casts from xml to text, varchar, and bpchar, -- because those are binary-compatible while the reverse goes through -- texttoxml(), which does an XML syntax check. -- As of 9.1, this finds the cast from pg_node_tree to text, which we -- intentionally do not provide a reverse pathway for. SELECT castsource::regtype, casttarget::regtype, castfunc, castcontext FROM pg_cast c WHERE c.castmethod = 'b' AND NOT EXISTS (SELECT 1 FROM pg_cast k WHERE k.castmethod = 'b' AND k.castsource = c.casttarget AND k.casttarget = c.castsource); -- **************** pg_conversion **************** -- Look for illegal values in pg_conversion fields. SELECT p1.oid, p1.conname FROM pg_conversion as p1 WHERE p1.conproc = 0 OR pg_encoding_to_char(conforencoding) = '' OR pg_encoding_to_char(contoencoding) = ''; -- Look for conprocs that don't have the expected signature. SELECT p.oid, p.proname, c.oid, c.conname FROM pg_proc p, pg_conversion c WHERE p.oid = c.conproc AND (p.prorettype != 'void'::regtype OR p.proretset OR p.pronargs != 5 OR p.proargtypes[0] != 'int4'::regtype OR p.proargtypes[1] != 'int4'::regtype OR p.proargtypes[2] != 'cstring'::regtype OR p.proargtypes[3] != 'internal'::regtype OR p.proargtypes[4] != 'int4'::regtype); -- Check for conprocs that don't perform the specific conversion that -- pg_conversion alleges they do, by trying to invoke each conversion -- on some simple ASCII data. (The conproc should throw an error if -- it doesn't accept the encodings that are passed to it.) -- Unfortunately, we can't test non-default conprocs this way, because -- there is no way to ask convert() to invoke them, and we cannot call -- them directly from SQL. But there are no non-default built-in -- conversions anyway. -- (Similarly, this doesn't cope with any search path issues.) SELECT p1.oid, p1.conname FROM pg_conversion as p1 WHERE condefault AND convert('ABC'::bytea, pg_encoding_to_char(conforencoding), pg_encoding_to_char(contoencoding)) != 'ABC'; -- **************** pg_operator **************** -- Look for illegal values in pg_operator fields. SELECT p1.oid, p1.oprname FROM pg_operator as p1 WHERE (p1.oprkind != 'b' AND p1.oprkind != 'l' AND p1.oprkind != 'r') OR p1.oprresult = 0 OR p1.oprcode = 0; -- Look for missing or unwanted operand types SELECT p1.oid, p1.oprname FROM pg_operator as p1 WHERE (p1.oprleft = 0 and p1.oprkind != 'l') OR (p1.oprleft != 0 and p1.oprkind = 'l') OR (p1.oprright = 0 and p1.oprkind != 'r') OR (p1.oprright != 0 and p1.oprkind = 'r'); -- Look for conflicting operator definitions (same names and input datatypes). SELECT p1.oid, p1.oprcode, p2.oid, p2.oprcode FROM pg_operator AS p1, pg_operator AS p2 WHERE p1.oid != p2.oid AND p1.oprname = p2.oprname AND p1.oprkind = p2.oprkind AND p1.oprleft = p2.oprleft AND p1.oprright = p2.oprright; -- Look for commutative operators that don't commute. -- DEFINITIONAL NOTE: If A.oprcom = B, then x A y has the same result as y B x. -- We expect that B will always say that B.oprcom = A as well; that's not -- inherently essential, but it would be inefficient not to mark it so. SELECT p1.oid, p1.oprcode, p2.oid, p2.oprcode FROM pg_operator AS p1, pg_operator AS p2 WHERE p1.oprcom = p2.oid AND (p1.oprkind != 'b' OR p1.oprleft != p2.oprright OR p1.oprright != p2.oprleft OR p1.oprresult != p2.oprresult OR p1.oid != p2.oprcom); -- Look for negatory operators that don't agree. -- DEFINITIONAL NOTE: If A.oprnegate = B, then both A and B must yield -- boolean results, and (x A y) == ! (x B y), or the equivalent for -- single-operand operators. -- We expect that B will always say that B.oprnegate = A as well; that's not -- inherently essential, but it would be inefficient not to mark it so. -- Also, A and B had better not be the same operator. SELECT p1.oid, p1.oprcode, p2.oid, p2.oprcode FROM pg_operator AS p1, pg_operator AS p2 WHERE p1.oprnegate = p2.oid AND (p1.oprkind != p2.oprkind OR p1.oprleft != p2.oprleft OR p1.oprright != p2.oprright OR p1.oprresult != 'bool'::regtype OR p2.oprresult != 'bool'::regtype OR p1.oid != p2.oprnegate OR p1.oid = p2.oid); -- Make a list of the names of operators that are claimed to be commutator -- pairs. This list will grow over time, but before accepting a new entry -- make sure you didn't link the wrong operators. SELECT DISTINCT o1.oprname AS op1, o2.oprname AS op2 FROM pg_operator o1, pg_operator o2 WHERE o1.oprcom = o2.oid AND o1.oprname <= o2.oprname ORDER BY 1, 2; -- Likewise for negator pairs. SELECT DISTINCT o1.oprname AS op1, o2.oprname AS op2 FROM pg_operator o1, pg_operator o2 WHERE o1.oprnegate = o2.oid AND o1.oprname <= o2.oprname ORDER BY 1, 2; -- A mergejoinable or hashjoinable operator must be binary, must return -- boolean, and must have a commutator (itself, unless it's a cross-type -- operator). SELECT p1.oid, p1.oprname FROM pg_operator AS p1 WHERE (p1.oprcanmerge OR p1.oprcanhash) AND NOT (p1.oprkind = 'b' AND p1.oprresult = 'bool'::regtype AND p1.oprcom != 0); -- What's more, the commutator had better be mergejoinable/hashjoinable too. SELECT p1.oid, p1.oprname, p2.oid, p2.oprname FROM pg_operator AS p1, pg_operator AS p2 WHERE p1.oprcom = p2.oid AND (p1.oprcanmerge != p2.oprcanmerge OR p1.oprcanhash != p2.oprcanhash); -- Mergejoinable operators should appear as equality members of btree index -- opfamilies. SELECT p1.oid, p1.oprname FROM pg_operator AS p1 WHERE p1.oprcanmerge AND NOT EXISTS (SELECT 1 FROM pg_amop WHERE amopmethod = (SELECT oid FROM pg_am WHERE amname = 'btree') AND amopopr = p1.oid AND amopstrategy = 3); -- And the converse. SELECT p1.oid, p1.oprname, p.amopfamily FROM pg_operator AS p1, pg_amop p WHERE amopopr = p1.oid AND amopmethod = (SELECT oid FROM pg_am WHERE amname = 'btree') AND amopstrategy = 3 AND NOT p1.oprcanmerge; -- Hashable operators should appear as members of hash index opfamilies. SELECT p1.oid, p1.oprname FROM pg_operator AS p1 WHERE p1.oprcanhash AND NOT EXISTS (SELECT 1 FROM pg_amop WHERE amopmethod = (SELECT oid FROM pg_am WHERE amname = 'hash') AND amopopr = p1.oid AND amopstrategy = 1); -- And the converse. SELECT p1.oid, p1.oprname, p.amopfamily FROM pg_operator AS p1, pg_amop p WHERE amopopr = p1.oid AND amopmethod = (SELECT oid FROM pg_am WHERE amname = 'hash') AND NOT p1.oprcanhash; -- Check that each operator defined in pg_operator matches its oprcode entry -- in pg_proc. Easiest to do this separately for each oprkind. SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprcode = p2.oid AND p1.oprkind = 'b' AND (p2.pronargs != 2 OR NOT binary_coercible(p2.prorettype, p1.oprresult) OR NOT binary_coercible(p1.oprleft, p2.proargtypes[0]) OR NOT binary_coercible(p1.oprright, p2.proargtypes[1])); SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprcode = p2.oid AND p1.oprkind = 'l' AND (p2.pronargs != 1 OR NOT binary_coercible(p2.prorettype, p1.oprresult) OR NOT binary_coercible(p1.oprright, p2.proargtypes[0]) OR p1.oprleft != 0); SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprcode = p2.oid AND p1.oprkind = 'r' AND (p2.pronargs != 1 OR NOT binary_coercible(p2.prorettype, p1.oprresult) OR NOT binary_coercible(p1.oprleft, p2.proargtypes[0]) OR p1.oprright != 0); -- If the operator is mergejoinable or hashjoinable, its underlying function -- should not be volatile. SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprcode = p2.oid AND (p1.oprcanmerge OR p1.oprcanhash) AND p2.provolatile = 'v'; -- If oprrest is set, the operator must return boolean, -- and it must link to a proc with the right signature -- to be a restriction selectivity estimator. -- The proc signature we want is: float8 proc(internal, oid, internal, int4) SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprrest = p2.oid AND (p1.oprresult != 'bool'::regtype OR p2.prorettype != 'float8'::regtype OR p2.proretset OR p2.pronargs != 4 OR p2.proargtypes[0] != 'internal'::regtype OR p2.proargtypes[1] != 'oid'::regtype OR p2.proargtypes[2] != 'internal'::regtype OR p2.proargtypes[3] != 'int4'::regtype); -- If oprjoin is set, the operator must be a binary boolean op, -- and it must link to a proc with the right signature -- to be a join selectivity estimator. -- The proc signature we want is: float8 proc(internal, oid, internal, int2, internal) -- (Note: the old signature with only 4 args is still allowed, but no core -- estimator should be using it.) SELECT p1.oid, p1.oprname, p2.oid, p2.proname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprjoin = p2.oid AND (p1.oprkind != 'b' OR p1.oprresult != 'bool'::regtype OR p2.prorettype != 'float8'::regtype OR p2.proretset OR p2.pronargs != 5 OR p2.proargtypes[0] != 'internal'::regtype OR p2.proargtypes[1] != 'oid'::regtype OR p2.proargtypes[2] != 'internal'::regtype OR p2.proargtypes[3] != 'int2'::regtype OR p2.proargtypes[4] != 'internal'::regtype); -- Insist that all built-in pg_operator entries have descriptions SELECT p1.oid, p1.oprname FROM pg_operator as p1 LEFT JOIN pg_description as d ON p1.tableoid = d.classoid and p1.oid = d.objoid and d.objsubid = 0 WHERE d.classoid IS NULL AND p1.oid <= 9999; -- Check that operators' underlying functions have suitable comments, -- namely 'implementation of XXX operator'. (Note: it's not necessary to -- put such comments into pg_proc.dat; initdb will generate them as needed.) -- In some cases involving legacy names for operators, there are multiple -- operators referencing the same pg_proc entry, so ignore operators whose -- comments say they are deprecated. -- We also have a few functions that are both operator support and meant to -- be called directly; those should have comments matching their operator. WITH funcdescs AS ( SELECT p.oid as p_oid, proname, o.oid as o_oid, pd.description as prodesc, 'implementation of ' || oprname || ' operator' as expecteddesc, od.description as oprdesc FROM pg_proc p JOIN pg_operator o ON oprcode = p.oid LEFT JOIN pg_description pd ON (pd.objoid = p.oid and pd.classoid = p.tableoid and pd.objsubid = 0) LEFT JOIN pg_description od ON (od.objoid = o.oid and od.classoid = o.tableoid and od.objsubid = 0) WHERE o.oid <= 9999 ) SELECT * FROM funcdescs WHERE prodesc IS DISTINCT FROM expecteddesc AND oprdesc NOT LIKE 'deprecated%' AND prodesc IS DISTINCT FROM oprdesc; -- Show all the operator-implementation functions that have their own -- comments. This should happen only in cases where the function and -- operator syntaxes are both documented at the user level. -- This should be a pretty short list; it's mostly legacy cases. WITH funcdescs AS ( SELECT p.oid as p_oid, proname, o.oid as o_oid, pd.description as prodesc, 'implementation of ' || oprname || ' operator' as expecteddesc, od.description as oprdesc FROM pg_proc p JOIN pg_operator o ON oprcode = p.oid LEFT JOIN pg_description pd ON (pd.objoid = p.oid and pd.classoid = p.tableoid and pd.objsubid = 0) LEFT JOIN pg_description od ON (od.objoid = o.oid and od.classoid = o.tableoid and od.objsubid = 0) WHERE o.oid <= 9999 ) SELECT p_oid, proname, prodesc FROM funcdescs WHERE prodesc IS DISTINCT FROM expecteddesc AND oprdesc NOT LIKE 'deprecated%' ORDER BY 1; -- Operators that are commutator pairs should have identical volatility -- and leakproofness markings on their implementation functions. SELECT o1.oid, o1.oprcode, o2.oid, o2.oprcode FROM pg_operator AS o1, pg_operator AS o2, pg_proc AS p1, pg_proc AS p2 WHERE o1.oprcom = o2.oid AND p1.oid = o1.oprcode AND p2.oid = o2.oprcode AND (p1.provolatile != p2.provolatile OR p1.proleakproof != p2.proleakproof); -- Likewise for negator pairs. SELECT o1.oid, o1.oprcode, o2.oid, o2.oprcode FROM pg_operator AS o1, pg_operator AS o2, pg_proc AS p1, pg_proc AS p2 WHERE o1.oprnegate = o2.oid AND p1.oid = o1.oprcode AND p2.oid = o2.oprcode AND (p1.provolatile != p2.provolatile OR p1.proleakproof != p2.proleakproof); -- Btree comparison operators' functions should have the same volatility -- and leakproofness markings as the associated comparison support function. -- As of Postgres 12, the only exceptions are that textual equality functions -- are marked leakproof but textual comparison/inequality functions are not. SELECT pp.oid::regprocedure as proc, pp.provolatile as vp, pp.proleakproof as lp, po.oid::regprocedure as opr, po.provolatile as vo, po.proleakproof as lo FROM pg_proc pp, pg_proc po, pg_operator o, pg_amproc ap, pg_amop ao WHERE pp.oid = ap.amproc AND po.oid = o.oprcode AND o.oid = ao.amopopr AND ao.amopmethod = (SELECT oid FROM pg_am WHERE amname = 'btree') AND ao.amopfamily = ap.amprocfamily AND ao.amoplefttype = ap.amproclefttype AND ao.amoprighttype = ap.amprocrighttype AND ap.amprocnum = 1 AND (pp.provolatile != po.provolatile OR pp.proleakproof != po.proleakproof) ORDER BY 1; -- **************** pg_aggregate **************** -- Look for illegal values in pg_aggregate fields. SELECT ctid, aggfnoid::oid FROM pg_aggregate as p1 WHERE aggfnoid = 0 OR aggtransfn = 0 OR aggkind NOT IN ('n', 'o', 'h') OR aggnumdirectargs < 0 OR (aggkind = 'n' AND aggnumdirectargs > 0) OR aggfinalmodify NOT IN ('r', 's', 'w') OR aggmfinalmodify NOT IN ('r', 's', 'w') OR aggtranstype = 0 OR aggtransspace < 0 OR aggmtransspace < 0; -- Make sure the matching pg_proc entry is sensible, too. SELECT a.aggfnoid::oid, p.proname FROM pg_aggregate as a, pg_proc as p WHERE a.aggfnoid = p.oid AND (p.prokind != 'a' OR p.proretset OR p.pronargs < a.aggnumdirectargs); -- Make sure there are no prokind = PROKIND_AGGREGATE pg_proc entries without matches. SELECT oid, proname FROM pg_proc as p WHERE p.prokind = 'a' AND NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid); -- If there is no finalfn then the output type must be the transtype. SELECT a.aggfnoid::oid, p.proname FROM pg_aggregate as a, pg_proc as p WHERE a.aggfnoid = p.oid AND a.aggfinalfn = 0 AND p.prorettype != a.aggtranstype; -- Cross-check transfn against its entry in pg_proc. SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr WHERE a.aggfnoid = p.oid AND a.aggtransfn = ptr.oid AND (ptr.proretset OR NOT (ptr.pronargs = CASE WHEN a.aggkind = 'n' THEN p.pronargs + 1 ELSE greatest(p.pronargs - a.aggnumdirectargs, 1) + 1 END) OR NOT binary_coercible(ptr.prorettype, a.aggtranstype) OR NOT binary_coercible(a.aggtranstype, ptr.proargtypes[0]) OR (p.pronargs > 0 AND NOT binary_coercible(p.proargtypes[0], ptr.proargtypes[1])) OR (p.pronargs > 1 AND NOT binary_coercible(p.proargtypes[1], ptr.proargtypes[2])) OR (p.pronargs > 2 AND NOT binary_coercible(p.proargtypes[2], ptr.proargtypes[3])) -- we could carry the check further, but 3 args is enough for now OR (p.pronargs > 3) ); -- Cross-check finalfn (if present) against its entry in pg_proc. SELECT a.aggfnoid::oid, p.proname, pfn.oid, pfn.proname FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS pfn WHERE a.aggfnoid = p.oid AND a.aggfinalfn = pfn.oid AND (pfn.proretset OR NOT binary_coercible(pfn.prorettype, p.prorettype) OR NOT binary_coercible(a.aggtranstype, pfn.proargtypes[0]) OR CASE WHEN a.aggfinalextra THEN pfn.pronargs != p.pronargs + 1 ELSE pfn.pronargs != a.aggnumdirectargs + 1 END OR (pfn.pronargs > 1 AND NOT binary_coercible(p.proargtypes[0], pfn.proargtypes[1])) OR (pfn.pronargs > 2 AND NOT binary_coercible(p.proargtypes[1], pfn.proargtypes[2])) OR (pfn.pronargs > 3 AND NOT binary_coercible(p.proargtypes[2], pfn.proargtypes[3])) -- we could carry the check further, but 4 args is enough for now OR (pfn.pronargs > 4) ); -- If transfn is strict then either initval should be non-NULL, or -- input type should match transtype so that the first non-null input -- can be assigned as the state value. SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr WHERE a.aggfnoid = p.oid AND a.aggtransfn = ptr.oid AND ptr.proisstrict AND a.agginitval IS NULL AND NOT binary_coercible(p.proargtypes[0], a.aggtranstype); -- Check for inconsistent specifications of moving-aggregate columns. SELECT ctid, aggfnoid::oid FROM pg_aggregate as p1 WHERE aggmtranstype != 0 AND (aggmtransfn = 0 OR aggminvtransfn = 0); SELECT ctid, aggfnoid::oid FROM pg_aggregate as p1 WHERE aggmtranstype = 0 AND (aggmtransfn != 0 OR aggminvtransfn != 0 OR aggmfinalfn != 0 OR aggmtransspace != 0 OR aggminitval IS NOT NULL); -- If there is no mfinalfn then the output type must be the mtranstype. SELECT a.aggfnoid::oid, p.proname FROM pg_aggregate as a, pg_proc as p WHERE a.aggfnoid = p.oid AND a.aggmtransfn != 0 AND a.aggmfinalfn = 0 AND p.prorettype != a.aggmtranstype; -- Cross-check mtransfn (if present) against its entry in pg_proc. SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr WHERE a.aggfnoid = p.oid AND a.aggmtransfn = ptr.oid AND (ptr.proretset OR NOT (ptr.pronargs = CASE WHEN a.aggkind = 'n' THEN p.pronargs + 1 ELSE greatest(p.pronargs - a.aggnumdirectargs, 1) + 1 END) OR NOT binary_coercible(ptr.prorettype, a.aggmtranstype) OR NOT binary_coercible(a.aggmtranstype, ptr.proargtypes[0]) OR (p.pronargs > 0 AND NOT binary_coercible(p.proargtypes[0], ptr.proargtypes[1])) OR (p.pronargs > 1 AND NOT binary_coercible(p.proargtypes[1], ptr.proargtypes[2])) OR (p.pronargs > 2 AND NOT binary_coercible(p.proargtypes[2], ptr.proargtypes[3])) -- we could carry the check further, but 3 args is enough for now OR (p.pronargs > 3) ); -- Cross-check minvtransfn (if present) against its entry in pg_proc. SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr WHERE a.aggfnoid = p.oid AND a.aggminvtransfn = ptr.oid AND (ptr.proretset OR NOT (ptr.pronargs = CASE WHEN a.aggkind = 'n' THEN p.pronargs + 1 ELSE greatest(p.pronargs - a.aggnumdirectargs, 1) + 1 END) OR NOT binary_coercible(ptr.prorettype, a.aggmtranstype) OR NOT binary_coercible(a.aggmtranstype, ptr.proargtypes[0]) OR (p.pronargs > 0 AND NOT binary_coercible(p.proargtypes[0], ptr.proargtypes[1])) OR (p.pronargs > 1 AND NOT binary_coercible(p.proargtypes[1], ptr.proargtypes[2])) OR (p.pronargs > 2 AND NOT binary_coercible(p.proargtypes[2], ptr.proargtypes[3])) -- we could carry the check further, but 3 args is enough for now OR (p.pronargs > 3) ); -- Cross-check mfinalfn (if present) against its entry in pg_proc. SELECT a.aggfnoid::oid, p.proname, pfn.oid, pfn.proname FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS pfn WHERE a.aggfnoid = p.oid AND a.aggmfinalfn = pfn.oid AND (pfn.proretset OR NOT binary_coercible(pfn.prorettype, p.prorettype) OR NOT binary_coercible(a.aggmtranstype, pfn.proargtypes[0]) OR CASE WHEN a.aggmfinalextra THEN pfn.pronargs != p.pronargs + 1 ELSE pfn.pronargs != a.aggnumdirectargs + 1 END OR (pfn.pronargs > 1 AND NOT binary_coercible(p.proargtypes[0], pfn.proargtypes[1])) OR (pfn.pronargs > 2 AND NOT binary_coercible(p.proargtypes[1], pfn.proargtypes[2])) OR (pfn.pronargs > 3 AND NOT binary_coercible(p.proargtypes[2], pfn.proargtypes[3])) -- we could carry the check further, but 4 args is enough for now OR (pfn.pronargs > 4) ); -- If mtransfn is strict then either minitval should be non-NULL, or -- input type should match mtranstype so that the first non-null input -- can be assigned as the state value. SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr WHERE a.aggfnoid = p.oid AND a.aggmtransfn = ptr.oid AND ptr.proisstrict AND a.aggminitval IS NULL AND NOT binary_coercible(p.proargtypes[0], a.aggmtranstype); -- mtransfn and minvtransfn should have same strictness setting. SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname, iptr.oid, iptr.proname FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr, pg_proc AS iptr WHERE a.aggfnoid = p.oid AND a.aggmtransfn = ptr.oid AND a.aggminvtransfn = iptr.oid AND ptr.proisstrict != iptr.proisstrict; -- Check that all combine functions have signature -- combine(transtype, transtype) returns transtype SELECT a.aggfnoid, p.proname FROM pg_aggregate as a, pg_proc as p WHERE a.aggcombinefn = p.oid AND (p.pronargs != 2 OR p.prorettype != p.proargtypes[0] OR p.prorettype != p.proargtypes[1] OR NOT binary_coercible(a.aggtranstype, p.proargtypes[0])); -- Check that no combine function for an INTERNAL transtype is strict. SELECT a.aggfnoid, p.proname FROM pg_aggregate as a, pg_proc as p WHERE a.aggcombinefn = p.oid AND a.aggtranstype = 'internal'::regtype AND p.proisstrict; -- serialize/deserialize functions should be specified only for aggregates -- with transtype internal and a combine function, and we should have both -- or neither of them. SELECT aggfnoid, aggtranstype, aggserialfn, aggdeserialfn FROM pg_aggregate WHERE (aggserialfn != 0 OR aggdeserialfn != 0) AND (aggtranstype != 'internal'::regtype OR aggcombinefn = 0 OR aggserialfn = 0 OR aggdeserialfn = 0); -- Check that all serialization functions have signature -- serialize(internal) returns bytea -- Also insist that they be strict; it's wasteful to run them on NULLs. SELECT a.aggfnoid, p.proname FROM pg_aggregate as a, pg_proc as p WHERE a.aggserialfn = p.oid AND (p.prorettype != 'bytea'::regtype OR p.pronargs != 1 OR p.proargtypes[0] != 'internal'::regtype OR NOT p.proisstrict); -- Check that all deserialization functions have signature -- deserialize(bytea, internal) returns internal -- Also insist that they be strict; it's wasteful to run them on NULLs. SELECT a.aggfnoid, p.proname FROM pg_aggregate as a, pg_proc as p WHERE a.aggdeserialfn = p.oid AND (p.prorettype != 'internal'::regtype OR p.pronargs != 2 OR p.proargtypes[0] != 'bytea'::regtype OR p.proargtypes[1] != 'internal'::regtype OR NOT p.proisstrict); -- Check that aggregates which have the same transition function also have -- the same combine, serialization, and deserialization functions. -- While that isn't strictly necessary, it's fishy if they don't. SELECT a.aggfnoid, a.aggcombinefn, a.aggserialfn, a.aggdeserialfn, b.aggfnoid, b.aggcombinefn, b.aggserialfn, b.aggdeserialfn FROM pg_aggregate a, pg_aggregate b WHERE a.aggfnoid < b.aggfnoid AND a.aggtransfn = b.aggtransfn AND (a.aggcombinefn != b.aggcombinefn OR a.aggserialfn != b.aggserialfn OR a.aggdeserialfn != b.aggdeserialfn); -- Cross-check aggsortop (if present) against pg_operator. -- We expect to find entries for bool_and, bool_or, every, max, and min. SELECT DISTINCT proname, oprname FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid ORDER BY 1, 2; -- Check datatypes match SELECT a.aggfnoid::oid, o.oid FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid AND (oprkind != 'b' OR oprresult != 'boolean'::regtype OR oprleft != p.proargtypes[0] OR oprright != p.proargtypes[0]); -- Check operator is a suitable btree opfamily member SELECT a.aggfnoid::oid, o.oid FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid AND NOT EXISTS(SELECT 1 FROM pg_amop WHERE amopmethod = (SELECT oid FROM pg_am WHERE amname = 'btree') AND amopopr = o.oid AND amoplefttype = o.oprleft AND amoprighttype = o.oprright); -- Check correspondence of btree strategies and names SELECT DISTINCT proname, oprname, amopstrategy FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p, pg_amop as ao WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid AND amopopr = o.oid AND amopmethod = (SELECT oid FROM pg_am WHERE amname = 'btree') ORDER BY 1, 2; -- Check that there are not aggregates with the same name and different -- numbers of arguments. While not technically wrong, we have a project policy -- to avoid this because it opens the door for confusion in connection with -- ORDER BY: novices frequently put the ORDER BY in the wrong place. -- See the fate of the single-argument form of string_agg() for history. -- (Note: we don't forbid users from creating such aggregates; the policy is -- just to think twice before creating built-in aggregates like this.) -- The only aggregates that should show up here are count(x) and count(*). SELECT p1.oid::regprocedure, p2.oid::regprocedure FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid < p2.oid AND p1.proname = p2.proname AND p1.prokind = 'a' AND p2.prokind = 'a' AND array_dims(p1.proargtypes) != array_dims(p2.proargtypes) ORDER BY 1; -- For the same reason, built-in aggregates with default arguments are no good. SELECT oid, proname FROM pg_proc AS p WHERE prokind = 'a' AND proargdefaults IS NOT NULL; -- For the same reason, we avoid creating built-in variadic aggregates, except -- that variadic ordered-set aggregates are OK (since they have special syntax -- that is not subject to the misplaced ORDER BY issue). SELECT p.oid, proname FROM pg_proc AS p JOIN pg_aggregate AS a ON a.aggfnoid = p.oid WHERE prokind = 'a' AND provariadic != 0 AND a.aggkind = 'n'; -- **************** pg_opfamily **************** -- Look for illegal values in pg_opfamily fields SELECT p1.oid FROM pg_opfamily as p1 WHERE p1.opfmethod = 0 OR p1.opfnamespace = 0; -- Look for opfamilies having no opclasses. While most validation of -- opfamilies is now handled by AM-specific amvalidate functions, that's -- driven from pg_opclass entries below, so an empty opfamily would not -- get noticed. SELECT oid, opfname FROM pg_opfamily f WHERE NOT EXISTS (SELECT 1 FROM pg_opclass WHERE opcfamily = f.oid); -- **************** pg_opclass **************** -- Look for illegal values in pg_opclass fields SELECT p1.oid FROM pg_opclass AS p1 WHERE p1.opcmethod = 0 OR p1.opcnamespace = 0 OR p1.opcfamily = 0 OR p1.opcintype = 0; -- opcmethod must match owning opfamily's opfmethod SELECT p1.oid, p2.oid FROM pg_opclass AS p1, pg_opfamily AS p2 WHERE p1.opcfamily = p2.oid AND p1.opcmethod != p2.opfmethod; -- There should not be multiple entries in pg_opclass with opcdefault true -- and the same opcmethod/opcintype combination. SELECT p1.oid, p2.oid FROM pg_opclass AS p1, pg_opclass AS p2 WHERE p1.oid != p2.oid AND p1.opcmethod = p2.opcmethod AND p1.opcintype = p2.opcintype AND p1.opcdefault AND p2.opcdefault; -- Ask access methods to validate opclasses -- (this replaces a lot of SQL-level checks that used to be done in this file) SELECT oid, opcname FROM pg_opclass WHERE NOT amvalidate(oid); -- **************** pg_am **************** -- Look for illegal values in pg_am fields SELECT p1.oid, p1.amname FROM pg_am AS p1 WHERE p1.amhandler = 0; -- Check for index amhandler functions with the wrong signature SELECT p1.oid, p1.amname, p2.oid, p2.proname FROM pg_am AS p1, pg_proc AS p2 WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset OR p2.pronargs != 1 OR p2.proargtypes[0] != 'internal'::regtype); -- Check for table amhandler functions with the wrong signature SELECT p1.oid, p1.amname, p2.oid, p2.proname FROM pg_am AS p1, pg_proc AS p2 WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND (p2.prorettype != 'table_am_handler'::regtype OR p2.proretset OR p2.pronargs != 1 OR p2.proargtypes[0] != 'internal'::regtype); -- **************** pg_amop **************** -- Look for illegal values in pg_amop fields SELECT p1.amopfamily, p1.amopstrategy FROM pg_amop as p1 WHERE p1.amopfamily = 0 OR p1.amoplefttype = 0 OR p1.amoprighttype = 0 OR p1.amopopr = 0 OR p1.amopmethod = 0 OR p1.amopstrategy < 1; SELECT p1.amopfamily, p1.amopstrategy FROM pg_amop as p1 WHERE NOT ((p1.amoppurpose = 's' AND p1.amopsortfamily = 0) OR (p1.amoppurpose = 'o' AND p1.amopsortfamily <> 0)); -- amopmethod must match owning opfamily's opfmethod SELECT p1.oid, p2.oid FROM pg_amop AS p1, pg_opfamily AS p2 WHERE p1.amopfamily = p2.oid AND p1.amopmethod != p2.opfmethod; -- Make a list of all the distinct operator names being used in particular -- strategy slots. This is a bit hokey, since the list might need to change -- in future releases, but it's an effective way of spotting mistakes such as -- swapping two operators within a family. SELECT DISTINCT amopmethod, amopstrategy, oprname FROM pg_amop p1 LEFT JOIN pg_operator p2 ON amopopr = p2.oid ORDER BY 1, 2, 3; -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing -- to insist on for all standard datatypes. SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.oprname FROM pg_amop AS p1, pg_operator AS p2 WHERE p1.amopopr = p2.oid AND p1.amoppurpose = 's' AND (p2.oprrest = 0 OR p2.oprjoin = 0); -- Check that each opclass in an opfamily has associated operators, that is -- ones whose oprleft matches opcintype (possibly by coercion). SELECT p1.opcname, p1.opcfamily FROM pg_opclass AS p1 WHERE NOT EXISTS(SELECT 1 FROM pg_amop AS p2 WHERE p2.amopfamily = p1.opcfamily AND binary_coercible(p1.opcintype, p2.amoplefttype)); -- Check that each operator listed in pg_amop has an associated opclass, -- that is one whose opcintype matches oprleft (possibly by coercion). -- Otherwise the operator is useless because it cannot be matched to an index. -- (In principle it could be useful to list such operators in multiple-datatype -- btree opfamilies, but in practice you'd expect there to be an opclass for -- every datatype the family knows about.) SELECT p1.amopfamily, p1.amopstrategy, p1.amopopr FROM pg_amop AS p1 WHERE NOT EXISTS(SELECT 1 FROM pg_opclass AS p2 WHERE p2.opcfamily = p1.amopfamily AND binary_coercible(p2.opcintype, p1.amoplefttype)); -- Operators that are primary members of opclasses must be immutable (else -- it suggests that the index ordering isn't fixed). Operators that are -- cross-type members need only be stable, since they are just shorthands -- for index probe queries. SELECT p1.amopfamily, p1.amopopr, p2.oprname, p3.prosrc FROM pg_amop AS p1, pg_operator AS p2, pg_proc AS p3 WHERE p1.amopopr = p2.oid AND p2.oprcode = p3.oid AND p1.amoplefttype = p1.amoprighttype AND p3.provolatile != 'i'; SELECT p1.amopfamily, p1.amopopr, p2.oprname, p3.prosrc FROM pg_amop AS p1, pg_operator AS p2, pg_proc AS p3 WHERE p1.amopopr = p2.oid AND p2.oprcode = p3.oid AND p1.amoplefttype != p1.amoprighttype AND p3.provolatile = 'v'; -- **************** pg_amproc **************** -- Look for illegal values in pg_amproc fields SELECT p1.amprocfamily, p1.amprocnum FROM pg_amproc as p1 WHERE p1.amprocfamily = 0 OR p1.amproclefttype = 0 OR p1.amprocrighttype = 0 OR p1.amprocnum < 1 OR p1.amproc = 0; -- Support routines that are primary members of opfamilies must be immutable -- (else it suggests that the index ordering isn't fixed). But cross-type -- members need only be stable, since they are just shorthands -- for index probe queries. SELECT p1.amprocfamily, p1.amproc, p2.prosrc FROM pg_amproc AS p1, pg_proc AS p2 WHERE p1.amproc = p2.oid AND p1.amproclefttype = p1.amprocrighttype AND p2.provolatile != 'i'; SELECT p1.amprocfamily, p1.amproc, p2.prosrc FROM pg_amproc AS p1, pg_proc AS p2 WHERE p1.amproc = p2.oid AND p1.amproclefttype != p1.amprocrighttype AND p2.provolatile = 'v'; -- **************** pg_index **************** -- Look for illegal values in pg_index fields. SELECT p1.indexrelid, p1.indrelid FROM pg_index as p1 WHERE p1.indexrelid = 0 OR p1.indrelid = 0 OR p1.indnatts <= 0 OR p1.indnatts > 32; -- oidvector and int2vector fields should be of length indnatts. SELECT p1.indexrelid, p1.indrelid FROM pg_index as p1 WHERE array_lower(indkey, 1) != 0 OR array_upper(indkey, 1) != indnatts-1 OR array_lower(indclass, 1) != 0 OR array_upper(indclass, 1) != indnatts-1 OR array_lower(indcollation, 1) != 0 OR array_upper(indcollation, 1) != indnatts-1 OR array_lower(indoption, 1) != 0 OR array_upper(indoption, 1) != indnatts-1; -- Check that opclasses and collations match the underlying columns. -- (As written, this test ignores expression indexes.) SELECT indexrelid::regclass, indrelid::regclass, attname, atttypid::regtype, opcname FROM (SELECT indexrelid, indrelid, unnest(indkey) as ikey, unnest(indclass) as iclass, unnest(indcollation) as icoll FROM pg_index) ss, pg_attribute a, pg_opclass opc WHERE a.attrelid = indrelid AND a.attnum = ikey AND opc.oid = iclass AND (NOT binary_coercible(atttypid, opcintype) OR icoll != attcollation); -- For system catalogs, be even tighter: nearly all indexes should be -- exact type matches not binary-coercible matches. At this writing -- the only exception is an OID index on a regproc column. SELECT indexrelid::regclass, indrelid::regclass, attname, atttypid::regtype, opcname FROM (SELECT indexrelid, indrelid, unnest(indkey) as ikey, unnest(indclass) as iclass, unnest(indcollation) as icoll FROM pg_index WHERE indrelid < 16384) ss, pg_attribute a, pg_opclass opc WHERE a.attrelid = indrelid AND a.attnum = ikey AND opc.oid = iclass AND (opcintype != atttypid OR icoll != attcollation) ORDER BY 1; -- Check for system catalogs with collation-sensitive ordering. This is not -- a representational error in pg_index, but simply wrong catalog design. -- It's bad because we expect to be able to clone template0 and assign the -- copy a different database collation. It would especially not work for -- shared catalogs. SELECT relname, attname, attcollation FROM pg_class c, pg_attribute a WHERE c.oid = attrelid AND c.oid < 16384 AND c.relkind != 'v' AND -- we don't care about columns in views attcollation != 0 AND attcollation != (SELECT oid FROM pg_collation WHERE collname = 'C'); -- Double-check that collation-sensitive indexes have "C" collation, too. SELECT indexrelid::regclass, indrelid::regclass, iclass, icoll FROM (SELECT indexrelid, indrelid, unnest(indclass) as iclass, unnest(indcollation) as icoll FROM pg_index WHERE indrelid < 16384) ss WHERE icoll != 0 AND icoll != (SELECT oid FROM pg_collation WHERE collname = 'C'); pgFormatter-4.2/t/pg-test-files/sql/partition_aggregate.sql000066400000000000000000000412701361326045100241430ustar00rootroot00000000000000-- -- PARTITION_AGGREGATE -- Test partitionwise aggregation on partitioned tables -- -- Enable partitionwise aggregate, which by default is disabled. SET enable_partitionwise_aggregate TO true; -- Enable partitionwise join, which by default is disabled. SET enable_partitionwise_join TO true; -- Disable parallel plans. SET max_parallel_workers_per_gather TO 0; -- -- Tests for list partitioned tables. -- CREATE TABLE pagg_tab (a int, b int, c text, d int) PARTITION BY LIST(c); CREATE TABLE pagg_tab_p1 PARTITION OF pagg_tab FOR VALUES IN ('0000', '0001', '0002', '0003'); CREATE TABLE pagg_tab_p2 PARTITION OF pagg_tab FOR VALUES IN ('0004', '0005', '0006', '0007'); CREATE TABLE pagg_tab_p3 PARTITION OF pagg_tab FOR VALUES IN ('0008', '0009', '0010', '0011'); INSERT INTO pagg_tab SELECT i % 20, i % 30, to_char(i % 12, 'FM0000'), i % 30 FROM generate_series(0, 2999) i; ANALYZE pagg_tab; -- When GROUP BY clause matches; full aggregation is performed for each partition. EXPLAIN (COSTS OFF) SELECT c, sum(a), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY c HAVING avg(d) < 15 ORDER BY 1, 2, 3; SELECT c, sum(a), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY c HAVING avg(d) < 15 ORDER BY 1, 2, 3; -- When GROUP BY clause does not match; partial aggregation is performed for each partition. EXPLAIN (COSTS OFF) SELECT a, sum(b), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY a HAVING avg(d) < 15 ORDER BY 1, 2, 3; SELECT a, sum(b), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY a HAVING avg(d) < 15 ORDER BY 1, 2, 3; -- Check with multiple columns in GROUP BY EXPLAIN (COSTS OFF) SELECT a, c, count(*) FROM pagg_tab GROUP BY a, c; -- Check with multiple columns in GROUP BY, order in GROUP BY is reversed EXPLAIN (COSTS OFF) SELECT a, c, count(*) FROM pagg_tab GROUP BY c, a; -- Check with multiple columns in GROUP BY, order in target-list is reversed EXPLAIN (COSTS OFF) SELECT c, a, count(*) FROM pagg_tab GROUP BY a, c; -- Test when input relation for grouping is dummy EXPLAIN (COSTS OFF) SELECT c, sum(a) FROM pagg_tab WHERE 1 = 2 GROUP BY c; SELECT c, sum(a) FROM pagg_tab WHERE 1 = 2 GROUP BY c; EXPLAIN (COSTS OFF) SELECT c, sum(a) FROM pagg_tab WHERE c = 'x' GROUP BY c; SELECT c, sum(a) FROM pagg_tab WHERE c = 'x' GROUP BY c; -- Test GroupAggregate paths by disabling hash aggregates. SET enable_hashagg TO false; -- When GROUP BY clause matches full aggregation is performed for each partition. EXPLAIN (COSTS OFF) SELECT c, sum(a), avg(b), count(*) FROM pagg_tab GROUP BY 1 HAVING avg(d) < 15 ORDER BY 1, 2, 3; SELECT c, sum(a), avg(b), count(*) FROM pagg_tab GROUP BY 1 HAVING avg(d) < 15 ORDER BY 1, 2, 3; -- When GROUP BY clause does not match; partial aggregation is performed for each partition. EXPLAIN (COSTS OFF) SELECT a, sum(b), avg(b), count(*) FROM pagg_tab GROUP BY 1 HAVING avg(d) < 15 ORDER BY 1, 2, 3; SELECT a, sum(b), avg(b), count(*) FROM pagg_tab GROUP BY 1 HAVING avg(d) < 15 ORDER BY 1, 2, 3; -- Test partitionwise grouping without any aggregates EXPLAIN (COSTS OFF) SELECT c FROM pagg_tab GROUP BY c ORDER BY 1; SELECT c FROM pagg_tab GROUP BY c ORDER BY 1; EXPLAIN (COSTS OFF) SELECT a FROM pagg_tab WHERE a < 3 GROUP BY a ORDER BY 1; SELECT a FROM pagg_tab WHERE a < 3 GROUP BY a ORDER BY 1; RESET enable_hashagg; -- ROLLUP, partitionwise aggregation does not apply EXPLAIN (COSTS OFF) SELECT c, sum(a) FROM pagg_tab GROUP BY rollup(c) ORDER BY 1, 2; -- ORDERED SET within the aggregate. -- Full aggregation; since all the rows that belong to the same group come -- from the same partition, having an ORDER BY within the aggregate doesn't -- make any difference. EXPLAIN (COSTS OFF) SELECT c, sum(b order by a) FROM pagg_tab GROUP BY c ORDER BY 1, 2; -- Since GROUP BY clause does not match with PARTITION KEY; we need to do -- partial aggregation. However, ORDERED SET are not partial safe and thus -- partitionwise aggregation plan is not generated. EXPLAIN (COSTS OFF) SELECT a, sum(b order by a) FROM pagg_tab GROUP BY a ORDER BY 1, 2; -- JOIN query CREATE TABLE pagg_tab1(x int, y int) PARTITION BY RANGE(x); CREATE TABLE pagg_tab1_p1 PARTITION OF pagg_tab1 FOR VALUES FROM (0) TO (10); CREATE TABLE pagg_tab1_p2 PARTITION OF pagg_tab1 FOR VALUES FROM (10) TO (20); CREATE TABLE pagg_tab1_p3 PARTITION OF pagg_tab1 FOR VALUES FROM (20) TO (30); CREATE TABLE pagg_tab2(x int, y int) PARTITION BY RANGE(y); CREATE TABLE pagg_tab2_p1 PARTITION OF pagg_tab2 FOR VALUES FROM (0) TO (10); CREATE TABLE pagg_tab2_p2 PARTITION OF pagg_tab2 FOR VALUES FROM (10) TO (20); CREATE TABLE pagg_tab2_p3 PARTITION OF pagg_tab2 FOR VALUES FROM (20) TO (30); INSERT INTO pagg_tab1 SELECT i % 30, i % 20 FROM generate_series(0, 299, 2) i; INSERT INTO pagg_tab2 SELECT i % 20, i % 30 FROM generate_series(0, 299, 3) i; ANALYZE pagg_tab1; ANALYZE pagg_tab2; -- When GROUP BY clause matches; full aggregation is performed for each partition. EXPLAIN (COSTS OFF) SELECT t1.x, sum(t1.y), count(*) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.x ORDER BY 1, 2, 3; SELECT t1.x, sum(t1.y), count(*) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.x ORDER BY 1, 2, 3; -- Check with whole-row reference; partitionwise aggregation does not apply EXPLAIN (COSTS OFF) SELECT t1.x, sum(t1.y), count(t1) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.x ORDER BY 1, 2, 3; SELECT t1.x, sum(t1.y), count(t1) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.x ORDER BY 1, 2, 3; -- GROUP BY having other matching key EXPLAIN (COSTS OFF) SELECT t2.y, sum(t1.y), count(*) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t2.y ORDER BY 1, 2, 3; -- When GROUP BY clause does not match; partial aggregation is performed for each partition. -- Also test GroupAggregate paths by disabling hash aggregates. SET enable_hashagg TO false; EXPLAIN (COSTS OFF) SELECT t1.y, sum(t1.x), count(*) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.y HAVING avg(t1.x) > 10 ORDER BY 1, 2, 3; SELECT t1.y, sum(t1.x), count(*) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.y HAVING avg(t1.x) > 10 ORDER BY 1, 2, 3; RESET enable_hashagg; -- Check with LEFT/RIGHT/FULL OUTER JOINs which produces NULL values for -- aggregation -- LEFT JOIN, should produce partial partitionwise aggregation plan as -- GROUP BY is on nullable column EXPLAIN (COSTS OFF) SELECT b.y, sum(a.y) FROM pagg_tab1 a LEFT JOIN pagg_tab2 b ON a.x = b.y GROUP BY b.y ORDER BY 1 NULLS LAST; SELECT b.y, sum(a.y) FROM pagg_tab1 a LEFT JOIN pagg_tab2 b ON a.x = b.y GROUP BY b.y ORDER BY 1 NULLS LAST; -- RIGHT JOIN, should produce full partitionwise aggregation plan as -- GROUP BY is on non-nullable column EXPLAIN (COSTS OFF) SELECT b.y, sum(a.y) FROM pagg_tab1 a RIGHT JOIN pagg_tab2 b ON a.x = b.y GROUP BY b.y ORDER BY 1 NULLS LAST; SELECT b.y, sum(a.y) FROM pagg_tab1 a RIGHT JOIN pagg_tab2 b ON a.x = b.y GROUP BY b.y ORDER BY 1 NULLS LAST; -- FULL JOIN, should produce partial partitionwise aggregation plan as -- GROUP BY is on nullable column EXPLAIN (COSTS OFF) SELECT a.x, sum(b.x) FROM pagg_tab1 a FULL OUTER JOIN pagg_tab2 b ON a.x = b.y GROUP BY a.x ORDER BY 1 NULLS LAST; SELECT a.x, sum(b.x) FROM pagg_tab1 a FULL OUTER JOIN pagg_tab2 b ON a.x = b.y GROUP BY a.x ORDER BY 1 NULLS LAST; -- LEFT JOIN, with dummy relation on right side, ideally -- should produce full partitionwise aggregation plan as GROUP BY is on -- non-nullable columns. -- But right now we are unable to do partitionwise join in this case. EXPLAIN (COSTS OFF) SELECT a.x, b.y, count(*) FROM (SELECT * FROM pagg_tab1 WHERE x < 20) a LEFT JOIN (SELECT * FROM pagg_tab2 WHERE y > 10) b ON a.x = b.y WHERE a.x > 5 or b.y < 20 GROUP BY a.x, b.y ORDER BY 1, 2; SELECT a.x, b.y, count(*) FROM (SELECT * FROM pagg_tab1 WHERE x < 20) a LEFT JOIN (SELECT * FROM pagg_tab2 WHERE y > 10) b ON a.x = b.y WHERE a.x > 5 or b.y < 20 GROUP BY a.x, b.y ORDER BY 1, 2; -- FULL JOIN, with dummy relations on both sides, ideally -- should produce partial partitionwise aggregation plan as GROUP BY is on -- nullable columns. -- But right now we are unable to do partitionwise join in this case. EXPLAIN (COSTS OFF) SELECT a.x, b.y, count(*) FROM (SELECT * FROM pagg_tab1 WHERE x < 20) a FULL JOIN (SELECT * FROM pagg_tab2 WHERE y > 10) b ON a.x = b.y WHERE a.x > 5 or b.y < 20 GROUP BY a.x, b.y ORDER BY 1, 2; SELECT a.x, b.y, count(*) FROM (SELECT * FROM pagg_tab1 WHERE x < 20) a FULL JOIN (SELECT * FROM pagg_tab2 WHERE y > 10) b ON a.x = b.y WHERE a.x > 5 or b.y < 20 GROUP BY a.x, b.y ORDER BY 1, 2; -- Empty join relation because of empty outer side, no partitionwise agg plan EXPLAIN (COSTS OFF) SELECT a.x, a.y, count(*) FROM (SELECT * FROM pagg_tab1 WHERE x = 1 AND x = 2) a LEFT JOIN pagg_tab2 b ON a.x = b.y GROUP BY a.x, a.y ORDER BY 1, 2; SELECT a.x, a.y, count(*) FROM (SELECT * FROM pagg_tab1 WHERE x = 1 AND x = 2) a LEFT JOIN pagg_tab2 b ON a.x = b.y GROUP BY a.x, a.y ORDER BY 1, 2; -- Partition by multiple columns CREATE TABLE pagg_tab_m (a int, b int, c int) PARTITION BY RANGE(a, ((a+b)/2)); CREATE TABLE pagg_tab_m_p1 PARTITION OF pagg_tab_m FOR VALUES FROM (0, 0) TO (10, 10); CREATE TABLE pagg_tab_m_p2 PARTITION OF pagg_tab_m FOR VALUES FROM (10, 10) TO (20, 20); CREATE TABLE pagg_tab_m_p3 PARTITION OF pagg_tab_m FOR VALUES FROM (20, 20) TO (30, 30); INSERT INTO pagg_tab_m SELECT i % 30, i % 40, i % 50 FROM generate_series(0, 2999) i; ANALYZE pagg_tab_m; -- Partial aggregation as GROUP BY clause does not match with PARTITION KEY EXPLAIN (COSTS OFF) SELECT a, sum(b), avg(c), count(*) FROM pagg_tab_m GROUP BY a HAVING avg(c) < 22 ORDER BY 1, 2, 3; SELECT a, sum(b), avg(c), count(*) FROM pagg_tab_m GROUP BY a HAVING avg(c) < 22 ORDER BY 1, 2, 3; -- Full aggregation as GROUP BY clause matches with PARTITION KEY EXPLAIN (COSTS OFF) SELECT a, sum(b), avg(c), count(*) FROM pagg_tab_m GROUP BY a, (a+b)/2 HAVING sum(b) < 50 ORDER BY 1, 2, 3; SELECT a, sum(b), avg(c), count(*) FROM pagg_tab_m GROUP BY a, (a+b)/2 HAVING sum(b) < 50 ORDER BY 1, 2, 3; -- Full aggregation as PARTITION KEY is part of GROUP BY clause EXPLAIN (COSTS OFF) SELECT a, c, sum(b), avg(c), count(*) FROM pagg_tab_m GROUP BY (a+b)/2, 2, 1 HAVING sum(b) = 50 AND avg(c) > 25 ORDER BY 1, 2, 3; SELECT a, c, sum(b), avg(c), count(*) FROM pagg_tab_m GROUP BY (a+b)/2, 2, 1 HAVING sum(b) = 50 AND avg(c) > 25 ORDER BY 1, 2, 3; -- Test with multi-level partitioning scheme CREATE TABLE pagg_tab_ml (a int, b int, c text) PARTITION BY RANGE(a); CREATE TABLE pagg_tab_ml_p1 PARTITION OF pagg_tab_ml FOR VALUES FROM (0) TO (10); CREATE TABLE pagg_tab_ml_p2 PARTITION OF pagg_tab_ml FOR VALUES FROM (10) TO (20) PARTITION BY LIST (c); CREATE TABLE pagg_tab_ml_p2_s1 PARTITION OF pagg_tab_ml_p2 FOR VALUES IN ('0000', '0001'); CREATE TABLE pagg_tab_ml_p2_s2 PARTITION OF pagg_tab_ml_p2 FOR VALUES IN ('0002', '0003'); -- This level of partitioning has different column positions than the parent CREATE TABLE pagg_tab_ml_p3(b int, c text, a int) PARTITION BY RANGE (b); CREATE TABLE pagg_tab_ml_p3_s1(c text, a int, b int); CREATE TABLE pagg_tab_ml_p3_s2 PARTITION OF pagg_tab_ml_p3 FOR VALUES FROM (5) TO (10); ALTER TABLE pagg_tab_ml_p3 ATTACH PARTITION pagg_tab_ml_p3_s1 FOR VALUES FROM (0) TO (5); ALTER TABLE pagg_tab_ml ATTACH PARTITION pagg_tab_ml_p3 FOR VALUES FROM (20) TO (30); INSERT INTO pagg_tab_ml SELECT i % 30, i % 10, to_char(i % 4, 'FM0000') FROM generate_series(0, 29999) i; ANALYZE pagg_tab_ml; -- For Parallel Append SET max_parallel_workers_per_gather TO 2; -- Full aggregation at level 1 as GROUP BY clause matches with PARTITION KEY -- for level 1 only. For subpartitions, GROUP BY clause does not match with -- PARTITION KEY, but still we do not see a partial aggregation as array_agg() -- is not partial agg safe. EXPLAIN (COSTS OFF) SELECT a, sum(b), array_agg(distinct c), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3 ORDER BY 1, 2, 3; SELECT a, sum(b), array_agg(distinct c), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3 ORDER BY 1, 2, 3; -- Without ORDER BY clause, to test Gather at top-most path EXPLAIN (COSTS OFF) SELECT a, sum(b), array_agg(distinct c), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3; -- Full aggregation at level 1 as GROUP BY clause matches with PARTITION KEY -- for level 1 only. For subpartitions, GROUP BY clause does not match with -- PARTITION KEY, thus we will have a partial aggregation for them. EXPLAIN (COSTS OFF) SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3 ORDER BY 1, 2, 3; SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3 ORDER BY 1, 2, 3; -- Partial aggregation at all levels as GROUP BY clause does not match with -- PARTITION KEY EXPLAIN (COSTS OFF) SELECT b, sum(a), count(*) FROM pagg_tab_ml GROUP BY b ORDER BY 1, 2, 3; SELECT b, sum(a), count(*) FROM pagg_tab_ml GROUP BY b HAVING avg(a) < 15 ORDER BY 1, 2, 3; -- Full aggregation at all levels as GROUP BY clause matches with PARTITION KEY EXPLAIN (COSTS OFF) SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a, b, c HAVING avg(b) > 7 ORDER BY 1, 2, 3; SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a, b, c HAVING avg(b) > 7 ORDER BY 1, 2, 3; -- Parallelism within partitionwise aggregates SET min_parallel_table_scan_size TO '8kB'; SET parallel_setup_cost TO 0; -- Full aggregation at level 1 as GROUP BY clause matches with PARTITION KEY -- for level 1 only. For subpartitions, GROUP BY clause does not match with -- PARTITION KEY, thus we will have a partial aggregation for them. EXPLAIN (COSTS OFF) SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3 ORDER BY 1, 2, 3; SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a HAVING avg(b) < 3 ORDER BY 1, 2, 3; -- Partial aggregation at all levels as GROUP BY clause does not match with -- PARTITION KEY EXPLAIN (COSTS OFF) SELECT b, sum(a), count(*) FROM pagg_tab_ml GROUP BY b ORDER BY 1, 2, 3; SELECT b, sum(a), count(*) FROM pagg_tab_ml GROUP BY b HAVING avg(a) < 15 ORDER BY 1, 2, 3; -- Full aggregation at all levels as GROUP BY clause matches with PARTITION KEY EXPLAIN (COSTS OFF) SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a, b, c HAVING avg(b) > 7 ORDER BY 1, 2, 3; SELECT a, sum(b), count(*) FROM pagg_tab_ml GROUP BY a, b, c HAVING avg(b) > 7 ORDER BY 1, 2, 3; -- Parallelism within partitionwise aggregates (single level) -- Add few parallel setup cost, so that we will see a plan which gathers -- partially created paths even for full aggregation and sticks a single Gather -- followed by finalization step. -- Without this, the cost of doing partial aggregation + Gather + finalization -- for each partition and then Append over it turns out to be same and this -- wins as we add it first. This parallel_setup_cost plays a vital role in -- costing such plans. SET parallel_setup_cost TO 10; CREATE TABLE pagg_tab_para(x int, y int) PARTITION BY RANGE(x); CREATE TABLE pagg_tab_para_p1 PARTITION OF pagg_tab_para FOR VALUES FROM (0) TO (10); CREATE TABLE pagg_tab_para_p2 PARTITION OF pagg_tab_para FOR VALUES FROM (10) TO (20); CREATE TABLE pagg_tab_para_p3 PARTITION OF pagg_tab_para FOR VALUES FROM (20) TO (30); INSERT INTO pagg_tab_para SELECT i % 30, i % 20 FROM generate_series(0, 29999) i; ANALYZE pagg_tab_para; -- When GROUP BY clause matches; full aggregation is performed for each partition. EXPLAIN (COSTS OFF) SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; -- When GROUP BY clause does not match; partial aggregation is performed for each partition. EXPLAIN (COSTS OFF) SELECT y, sum(x), avg(x), count(*) FROM pagg_tab_para GROUP BY y HAVING avg(x) < 12 ORDER BY 1, 2, 3; SELECT y, sum(x), avg(x), count(*) FROM pagg_tab_para GROUP BY y HAVING avg(x) < 12 ORDER BY 1, 2, 3; -- Test when parent can produce parallel paths but not any (or some) of its children ALTER TABLE pagg_tab_para_p1 SET (parallel_workers = 0); ALTER TABLE pagg_tab_para_p3 SET (parallel_workers = 0); ANALYZE pagg_tab_para; EXPLAIN (COSTS OFF) SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; ALTER TABLE pagg_tab_para_p2 SET (parallel_workers = 0); ANALYZE pagg_tab_para; EXPLAIN (COSTS OFF) SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; -- Reset parallelism parameters to get partitionwise aggregation plan. RESET min_parallel_table_scan_size; RESET parallel_setup_cost; EXPLAIN (COSTS OFF) SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; SELECT x, sum(y), avg(y), count(*) FROM pagg_tab_para GROUP BY x HAVING avg(y) < 7 ORDER BY 1, 2, 3; pgFormatter-4.2/t/pg-test-files/sql/partition_info.sql000066400000000000000000000127631361326045100231550ustar00rootroot00000000000000-- -- Tests for functions providing information about partitions -- SELECT * FROM pg_partition_tree(NULL); SELECT * FROM pg_partition_tree(0); SELECT * FROM pg_partition_ancestors(NULL); SELECT * FROM pg_partition_ancestors(0); SELECT pg_partition_root(NULL); SELECT pg_partition_root(0); -- Test table partition trees CREATE TABLE ptif_test (a int, b int) PARTITION BY range (a); CREATE TABLE ptif_test0 PARTITION OF ptif_test FOR VALUES FROM (minvalue) TO (0) PARTITION BY list (b); CREATE TABLE ptif_test01 PARTITION OF ptif_test0 FOR VALUES IN (1); CREATE TABLE ptif_test1 PARTITION OF ptif_test FOR VALUES FROM (0) TO (100) PARTITION BY list (b); CREATE TABLE ptif_test11 PARTITION OF ptif_test1 FOR VALUES IN (1); CREATE TABLE ptif_test2 PARTITION OF ptif_test FOR VALUES FROM (100) TO (200); -- This partitioned table should remain with no partitions. CREATE TABLE ptif_test3 PARTITION OF ptif_test FOR VALUES FROM (200) TO (maxvalue) PARTITION BY list (b); -- Test pg_partition_root for tables SELECT pg_partition_root('ptif_test'); SELECT pg_partition_root('ptif_test0'); SELECT pg_partition_root('ptif_test01'); SELECT pg_partition_root('ptif_test3'); -- Test index partition tree CREATE INDEX ptif_test_index ON ONLY ptif_test (a); CREATE INDEX ptif_test0_index ON ONLY ptif_test0 (a); ALTER INDEX ptif_test_index ATTACH PARTITION ptif_test0_index; CREATE INDEX ptif_test01_index ON ptif_test01 (a); ALTER INDEX ptif_test0_index ATTACH PARTITION ptif_test01_index; CREATE INDEX ptif_test1_index ON ONLY ptif_test1 (a); ALTER INDEX ptif_test_index ATTACH PARTITION ptif_test1_index; CREATE INDEX ptif_test11_index ON ptif_test11 (a); ALTER INDEX ptif_test1_index ATTACH PARTITION ptif_test11_index; CREATE INDEX ptif_test2_index ON ptif_test2 (a); ALTER INDEX ptif_test_index ATTACH PARTITION ptif_test2_index; CREATE INDEX ptif_test3_index ON ptif_test3 (a); ALTER INDEX ptif_test_index ATTACH PARTITION ptif_test3_index; -- Test pg_partition_root for indexes SELECT pg_partition_root('ptif_test_index'); SELECT pg_partition_root('ptif_test0_index'); SELECT pg_partition_root('ptif_test01_index'); SELECT pg_partition_root('ptif_test3_index'); -- List all tables members of the tree SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree('ptif_test'); -- List tables from an intermediate level SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree('ptif_test0') p JOIN pg_class c ON (p.relid = c.oid); -- List from leaf table SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree('ptif_test01') p JOIN pg_class c ON (p.relid = c.oid); -- List from partitioned table with no partitions SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree('ptif_test3') p JOIN pg_class c ON (p.relid = c.oid); -- List all ancestors of root and leaf tables SELECT * FROM pg_partition_ancestors('ptif_test01'); SELECT * FROM pg_partition_ancestors('ptif_test'); -- List all members using pg_partition_root with leaf table reference SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree(pg_partition_root('ptif_test01')) p JOIN pg_class c ON (p.relid = c.oid); -- List all indexes members of the tree SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree('ptif_test_index'); -- List indexes from an intermediate level SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree('ptif_test0_index') p JOIN pg_class c ON (p.relid = c.oid); -- List from leaf index SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree('ptif_test01_index') p JOIN pg_class c ON (p.relid = c.oid); -- List from partitioned index with no partitions SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree('ptif_test3_index') p JOIN pg_class c ON (p.relid = c.oid); -- List all members using pg_partition_root with leaf index reference SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree(pg_partition_root('ptif_test01_index')) p JOIN pg_class c ON (p.relid = c.oid); -- List all ancestors of root and leaf indexes SELECT * FROM pg_partition_ancestors('ptif_test01_index'); SELECT * FROM pg_partition_ancestors('ptif_test_index'); DROP TABLE ptif_test; -- Table that is not part of any partition tree is not listed. CREATE TABLE ptif_normal_table(a int); SELECT relid, parentrelid, level, isleaf FROM pg_partition_tree('ptif_normal_table'); SELECT * FROM pg_partition_ancestors('ptif_normal_table'); SELECT pg_partition_root('ptif_normal_table'); DROP TABLE ptif_normal_table; -- Various partitioning-related functions return empty/NULL if passed relations -- of types that cannot be part of a partition tree; for example, views, -- materialized views, legacy inheritance children or parents, etc. CREATE VIEW ptif_test_view AS SELECT 1; CREATE MATERIALIZED VIEW ptif_test_matview AS SELECT 1; CREATE TABLE ptif_li_parent (); CREATE TABLE ptif_li_child () INHERITS (ptif_li_parent); SELECT * FROM pg_partition_tree('ptif_test_view'); SELECT * FROM pg_partition_tree('ptif_test_matview'); SELECT * FROM pg_partition_tree('ptif_li_parent'); SELECT * FROM pg_partition_tree('ptif_li_child'); SELECT * FROM pg_partition_ancestors('ptif_test_view'); SELECT * FROM pg_partition_ancestors('ptif_test_matview'); SELECT * FROM pg_partition_ancestors('ptif_li_parent'); SELECT * FROM pg_partition_ancestors('ptif_li_child'); SELECT pg_partition_root('ptif_test_view'); SELECT pg_partition_root('ptif_test_matview'); SELECT pg_partition_root('ptif_li_parent'); SELECT pg_partition_root('ptif_li_child'); DROP VIEW ptif_test_view; DROP MATERIALIZED VIEW ptif_test_matview; DROP TABLE ptif_li_parent, ptif_li_child; pgFormatter-4.2/t/pg-test-files/sql/partition_join.sql000066400000000000000000000611231361326045100231530ustar00rootroot00000000000000-- -- PARTITION_JOIN -- Test partitionwise join between partitioned tables -- -- Enable partitionwise join, which by default is disabled. SET enable_partitionwise_join to true; -- -- partitioned by a single column -- CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a); CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250); CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600); CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500); INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 2 = 0; CREATE INDEX iprt1_p1_a on prt1_p1(a); CREATE INDEX iprt1_p2_a on prt1_p2(a); CREATE INDEX iprt1_p3_a on prt1_p3(a); ANALYZE prt1; CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b); CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250); CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500); CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600); INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 3 = 0; CREATE INDEX iprt2_p1_b on prt2_p1(b); CREATE INDEX iprt2_p2_b on prt2_p2(b); CREATE INDEX iprt2_p3_b on prt2_p3(b); ANALYZE prt2; -- inner join EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; -- left outer join, with whole-row reference; partitionwise join does not apply EXPLAIN (COSTS OFF) SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; -- right outer join EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b; -- full outer join, with placeholder vars EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.a OR t2.phv = t2.b ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.a OR t2.phv = t2.b ORDER BY t1.a, t2.b; -- Join with pruned partitions from joining relations EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.b = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.b = 0 ORDER BY t1.a, t2.b; -- Currently we can't do partitioned join if nullable-side partitions are pruned EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; -- Currently we can't do partitioned join if nullable-side partitions are pruned EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b; -- Semi-join EXPLAIN (COSTS OFF) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a; SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a; -- Anti-join with aggregates EXPLAIN (COSTS OFF) SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b); SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b); -- lateral reference EXPLAIN (COSTS OFF) SELECT * FROM prt1 t1 LEFT JOIN LATERAL (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss ON t1.a = ss.t2a WHERE t1.b = 0 ORDER BY t1.a; SELECT * FROM prt1 t1 LEFT JOIN LATERAL (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss ON t1.a = ss.t2a WHERE t1.b = 0 ORDER BY t1.a; EXPLAIN (COSTS OFF) SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL (SELECT t2.a AS t2a, t3.a AS t3a, t2.b t2b, t2.c t2c, least(t1.a,t2.a,t3.b) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss ON t1.c = ss.t2c WHERE (t1.b + coalesce(ss.t2b, 0)) = 0 ORDER BY t1.a; SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL (SELECT t2.a AS t2a, t3.a AS t3a, t2.b t2b, t2.c t2c, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss ON t1.c = ss.t2c WHERE (t1.b + coalesce(ss.t2b, 0)) = 0 ORDER BY t1.a; -- -- partitioned by expression -- CREATE TABLE prt1_e (a int, b int, c int) PARTITION BY RANGE(((a + b)/2)); CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES FROM (0) TO (250); CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES FROM (250) TO (500); CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES FROM (500) TO (600); INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i; CREATE INDEX iprt1_e_p1_ab2 on prt1_e_p1(((a+b)/2)); CREATE INDEX iprt1_e_p2_ab2 on prt1_e_p2(((a+b)/2)); CREATE INDEX iprt1_e_p3_ab2 on prt1_e_p3(((a+b)/2)); ANALYZE prt1_e; CREATE TABLE prt2_e (a int, b int, c int) PARTITION BY RANGE(((b + a)/2)); CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES FROM (0) TO (250); CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES FROM (250) TO (500); CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES FROM (500) TO (600); INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i; ANALYZE prt2_e; EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.c = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.c = 0 ORDER BY t1.a, t2.b; -- -- N-way join -- EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.b = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.b = 0 ORDER BY t1.a, t2.b; EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; -- Cases with non-nullable expressions in subquery results; -- make sure these go to null as expected EXPLAIN (COSTS OFF) SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.c = 0) t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.a = t1.phv OR t2.b = t2.phv OR (t3.a + t3.b)/2 = t3.phv ORDER BY t1.a, t2.b, t3.a + t3.b; SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.c = 0) t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.a = t1.phv OR t2.b = t2.phv OR (t3.a + t3.b)/2 = t3.phv ORDER BY t1.a, t2.b, t3.a + t3.b; -- Semi-join EXPLAIN (COSTS OFF) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.a = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.b = 0 ORDER BY t1.a; SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.a = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.b = 0 ORDER BY t1.a; EXPLAIN (COSTS OFF) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; -- test merge joins SET enable_hashjoin TO off; SET enable_nestloop TO off; EXPLAIN (COSTS OFF) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; -- MergeAppend on nullable column -- This should generate a partitionwise join, but currently fails to EXPLAIN (COSTS OFF) SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; -- merge join when expression with whole-row reference needs to be sorted; -- partitionwise join does not apply EXPLAIN (COSTS OFF) SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2.b ORDER BY t1.a; SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2.b ORDER BY t1.a; RESET enable_hashjoin; RESET enable_nestloop; -- -- partitioned by multiple columns -- CREATE TABLE prt1_m (a int, b int, c int) PARTITION BY RANGE(a, ((a + b)/2)); CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES FROM (0, 0) TO (250, 250); CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES FROM (250, 250) TO (500, 500); CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES FROM (500, 500) TO (600, 600); INSERT INTO prt1_m SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i; ANALYZE prt1_m; CREATE TABLE prt2_m (a int, b int, c int) PARTITION BY RANGE(((b + a)/2), b); CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES FROM (0, 0) TO (250, 250); CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES FROM (250, 250) TO (500, 500); CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES FROM (500, 500) TO (600, 600); INSERT INTO prt2_m SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i; ANALYZE prt2_m; EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b; -- -- tests for list partitioned tables. -- CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c); CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0003', '0004', '0010'); CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0001', '0005', '0002', '0009'); CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0006', '0007', '0008', '0011'); INSERT INTO plt1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; ANALYZE plt1; CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c); CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0000', '0003', '0004', '0010'); CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0001', '0005', '0002', '0009'); CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0006', '0007', '0008', '0011'); INSERT INTO plt2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i; ANALYZE plt2; -- -- list partitioned by expression -- CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A')); CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0000', '0003', '0004', '0010'); CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0001', '0005', '0002', '0009'); CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0006', '0007', '0008', '0011'); INSERT INTO plt1_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; ANALYZE plt1_e; -- test partition matching with N-way join EXPLAIN (COSTS OFF) SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; -- joins where one of the relations is proven empty EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a = 1 AND t1.a = 2; EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 LEFT JOIN prt2 t2 ON t1.a = t2.b; EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b, prt1 t3 WHERE t2.b = t3.a; EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b; -- -- tests for hash partitioned tables. -- CREATE TABLE pht1 (a int, b int, c text) PARTITION BY HASH(c); CREATE TABLE pht1_p1 PARTITION OF pht1 FOR VALUES WITH (MODULUS 3, REMAINDER 0); CREATE TABLE pht1_p2 PARTITION OF pht1 FOR VALUES WITH (MODULUS 3, REMAINDER 1); CREATE TABLE pht1_p3 PARTITION OF pht1 FOR VALUES WITH (MODULUS 3, REMAINDER 2); INSERT INTO pht1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; ANALYZE pht1; CREATE TABLE pht2 (a int, b int, c text) PARTITION BY HASH(c); CREATE TABLE pht2_p1 PARTITION OF pht2 FOR VALUES WITH (MODULUS 3, REMAINDER 0); CREATE TABLE pht2_p2 PARTITION OF pht2 FOR VALUES WITH (MODULUS 3, REMAINDER 1); CREATE TABLE pht2_p3 PARTITION OF pht2 FOR VALUES WITH (MODULUS 3, REMAINDER 2); INSERT INTO pht2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i; ANALYZE pht2; -- -- hash partitioned by expression -- CREATE TABLE pht1_e (a int, b int, c text) PARTITION BY HASH(ltrim(c, 'A')); CREATE TABLE pht1_e_p1 PARTITION OF pht1_e FOR VALUES WITH (MODULUS 3, REMAINDER 0); CREATE TABLE pht1_e_p2 PARTITION OF pht1_e FOR VALUES WITH (MODULUS 3, REMAINDER 1); CREATE TABLE pht1_e_p3 PARTITION OF pht1_e FOR VALUES WITH (MODULUS 3, REMAINDER 2); INSERT INTO pht1_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 299, 2) i; ANALYZE pht1_e; -- test partition matching with N-way join EXPLAIN (COSTS OFF) SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM pht1 t1, pht2 t2, pht1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM pht1 t1, pht2 t2, pht1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; -- test default partition behavior for range ALTER TABLE prt1 DETACH PARTITION prt1_p3; ALTER TABLE prt1 ATTACH PARTITION prt1_p3 DEFAULT; ANALYZE prt1; ALTER TABLE prt2 DETACH PARTITION prt2_p3; ALTER TABLE prt2 ATTACH PARTITION prt2_p3 DEFAULT; ANALYZE prt2; EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; -- test default partition behavior for list ALTER TABLE plt1 DETACH PARTITION plt1_p3; ALTER TABLE plt1 ATTACH PARTITION plt1_p3 DEFAULT; ANALYZE plt1; ALTER TABLE plt2 DETACH PARTITION plt2_p3; ALTER TABLE plt2 ATTACH PARTITION plt2_p3 DEFAULT; ANALYZE plt2; EXPLAIN (COSTS OFF) SELECT avg(t1.a), avg(t2.b), t1.c, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.a % 25 = 0 GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; -- -- multiple levels of partitioning -- CREATE TABLE prt1_l (a int, b int, c varchar) PARTITION BY RANGE(a); CREATE TABLE prt1_l_p1 PARTITION OF prt1_l FOR VALUES FROM (0) TO (250); CREATE TABLE prt1_l_p2 PARTITION OF prt1_l FOR VALUES FROM (250) TO (500) PARTITION BY LIST (c); CREATE TABLE prt1_l_p2_p1 PARTITION OF prt1_l_p2 FOR VALUES IN ('0000', '0001'); CREATE TABLE prt1_l_p2_p2 PARTITION OF prt1_l_p2 FOR VALUES IN ('0002', '0003'); CREATE TABLE prt1_l_p3 PARTITION OF prt1_l FOR VALUES FROM (500) TO (600) PARTITION BY RANGE (b); CREATE TABLE prt1_l_p3_p1 PARTITION OF prt1_l_p3 FOR VALUES FROM (0) TO (13); CREATE TABLE prt1_l_p3_p2 PARTITION OF prt1_l_p3 FOR VALUES FROM (13) TO (25); INSERT INTO prt1_l SELECT i, i % 25, to_char(i % 4, 'FM0000') FROM generate_series(0, 599, 2) i; ANALYZE prt1_l; CREATE TABLE prt2_l (a int, b int, c varchar) PARTITION BY RANGE(b); CREATE TABLE prt2_l_p1 PARTITION OF prt2_l FOR VALUES FROM (0) TO (250); CREATE TABLE prt2_l_p2 PARTITION OF prt2_l FOR VALUES FROM (250) TO (500) PARTITION BY LIST (c); CREATE TABLE prt2_l_p2_p1 PARTITION OF prt2_l_p2 FOR VALUES IN ('0000', '0001'); CREATE TABLE prt2_l_p2_p2 PARTITION OF prt2_l_p2 FOR VALUES IN ('0002', '0003'); CREATE TABLE prt2_l_p3 PARTITION OF prt2_l FOR VALUES FROM (500) TO (600) PARTITION BY RANGE (a); CREATE TABLE prt2_l_p3_p1 PARTITION OF prt2_l_p3 FOR VALUES FROM (0) TO (13); CREATE TABLE prt2_l_p3_p2 PARTITION OF prt2_l_p3 FOR VALUES FROM (13) TO (25); INSERT INTO prt2_l SELECT i % 25, i, to_char(i % 4, 'FM0000') FROM generate_series(0, 599, 3) i; ANALYZE prt2_l; -- inner join, qual covering only top-level partitions EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; -- left join EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t1.b = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t1.b = 0 ORDER BY t1.a, t2.b; -- right join EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.a = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.a = 0 ORDER BY t1.a, t2.b; -- full join EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.b = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.a = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.b = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.a = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b; -- lateral partitionwise join EXPLAIN (COSTS OFF) SELECT * FROM prt1_l t1 LEFT JOIN LATERAL (SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.b AS t3b, least(t1.a,t2.a,t3.b) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.c = t3.c)) ss ON t1.a = ss.t2a AND t1.c = ss.t2c WHERE t1.b = 0 ORDER BY t1.a; SELECT * FROM prt1_l t1 LEFT JOIN LATERAL (SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.b AS t3b, least(t1.a,t2.a,t3.b) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.c = t3.c)) ss ON t1.a = ss.t2a AND t1.c = ss.t2c WHERE t1.b = 0 ORDER BY t1.a; -- join with one side empty EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c; -- Test case to verify proper handling of subqueries in a partitioned delete. -- The weird-looking lateral join is just there to force creation of a -- nestloop parameter within the subquery, which exposes the problem if the -- planner fails to make multiple copies of the subquery as appropriate. EXPLAIN (COSTS OFF) DELETE FROM prt1_l WHERE EXISTS ( SELECT 1 FROM int4_tbl, LATERAL (SELECT int4_tbl.f1 FROM int8_tbl LIMIT 2) ss WHERE prt1_l.c IS NULL); -- -- negative testcases -- CREATE TABLE prt1_n (a int, b int, c varchar) PARTITION BY RANGE(c); CREATE TABLE prt1_n_p1 PARTITION OF prt1_n FOR VALUES FROM ('0000') TO ('0250'); CREATE TABLE prt1_n_p2 PARTITION OF prt1_n FOR VALUES FROM ('0250') TO ('0500'); INSERT INTO prt1_n SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 499, 2) i; ANALYZE prt1_n; CREATE TABLE prt2_n (a int, b int, c text) PARTITION BY LIST(c); CREATE TABLE prt2_n_p1 PARTITION OF prt2_n FOR VALUES IN ('0000', '0003', '0004', '0010', '0006', '0007'); CREATE TABLE prt2_n_p2 PARTITION OF prt2_n FOR VALUES IN ('0001', '0005', '0002', '0009', '0008', '0011'); INSERT INTO prt2_n SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; ANALYZE prt2_n; CREATE TABLE prt3_n (a int, b int, c text) PARTITION BY LIST(c); CREATE TABLE prt3_n_p1 PARTITION OF prt3_n FOR VALUES IN ('0000', '0004', '0006', '0007'); CREATE TABLE prt3_n_p2 PARTITION OF prt3_n FOR VALUES IN ('0001', '0002', '0008', '0010'); CREATE TABLE prt3_n_p3 PARTITION OF prt3_n FOR VALUES IN ('0003', '0005', '0009', '0011'); INSERT INTO prt2_n SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; ANALYZE prt3_n; CREATE TABLE prt4_n (a int, b int, c text) PARTITION BY RANGE(a); CREATE TABLE prt4_n_p1 PARTITION OF prt4_n FOR VALUES FROM (0) TO (300); CREATE TABLE prt4_n_p2 PARTITION OF prt4_n FOR VALUES FROM (300) TO (500); CREATE TABLE prt4_n_p3 PARTITION OF prt4_n FOR VALUES FROM (500) TO (600); INSERT INTO prt4_n SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i; ANALYZE prt4_n; -- partitionwise join can not be applied if the partition ranges differ EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2 WHERE t1.a = t2.a; EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2, prt2 t3 WHERE t1.a = t2.a and t1.a = t3.b; -- partitionwise join can not be applied if there are no equi-join conditions -- between partition keys EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON (t1.a < t2.b); -- equi-join with join condition on partial keys does not qualify for -- partitionwise join EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2; -- equi-join between out-of-order partition key columns does not qualify for -- partitionwise join EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = t2.b; -- equi-join between non-key columns does not qualify for partitionwise join EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.c = t2.c; -- partitionwise join can not be applied between tables with different -- partition lists EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 LEFT JOIN prt2_n t2 ON (t1.c = t2.c); EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 JOIN prt2_n t2 ON (t1.c = t2.c) JOIN plt1 t3 ON (t1.c = t3.c); -- partitionwise join can not be applied for a join between list and range -- partitioned table EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 FULL JOIN prt1 t2 ON (t1.c = t2.c); -- partitionwise join can not be applied if only one of joining table has -- default partition ALTER TABLE prt2 DETACH PARTITION prt2_p3; ALTER TABLE prt2 ATTACH PARTITION prt2_p3 FOR VALUES FROM (500) TO (600); ANALYZE prt2; EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; pgFormatter-4.2/t/pg-test-files/sql/partition_prune.sql000066400000000000000000001226431361326045100233520ustar00rootroot00000000000000-- -- Test partitioning planner code -- create table lp (a char) partition by list (a); create table lp_default partition of lp default; create table lp_ef partition of lp for values in ('e', 'f'); create table lp_ad partition of lp for values in ('a', 'd'); create table lp_bc partition of lp for values in ('b', 'c'); create table lp_g partition of lp for values in ('g'); create table lp_null partition of lp for values in (null); explain (costs off) select * from lp; explain (costs off) select * from lp where a > 'a' and a < 'd'; explain (costs off) select * from lp where a > 'a' and a <= 'd'; explain (costs off) select * from lp where a = 'a'; explain (costs off) select * from lp where 'a' = a; /* commuted */ explain (costs off) select * from lp where a is not null; explain (costs off) select * from lp where a is null; explain (costs off) select * from lp where a = 'a' or a = 'c'; explain (costs off) select * from lp where a is not null and (a = 'a' or a = 'c'); explain (costs off) select * from lp where a <> 'g'; explain (costs off) select * from lp where a <> 'a' and a <> 'd'; explain (costs off) select * from lp where a not in ('a', 'd'); -- collation matches the partitioning collation, pruning works create table coll_pruning (a text collate "C") partition by list (a); create table coll_pruning_a partition of coll_pruning for values in ('a'); create table coll_pruning_b partition of coll_pruning for values in ('b'); create table coll_pruning_def partition of coll_pruning default; explain (costs off) select * from coll_pruning where a collate "C" = 'a' collate "C"; -- collation doesn't match the partitioning collation, no pruning occurs explain (costs off) select * from coll_pruning where a collate "POSIX" = 'a' collate "POSIX"; create table rlp (a int, b varchar) partition by range (a); create table rlp_default partition of rlp default partition by list (a); create table rlp_default_default partition of rlp_default default; create table rlp_default_10 partition of rlp_default for values in (10); create table rlp_default_30 partition of rlp_default for values in (30); create table rlp_default_null partition of rlp_default for values in (null); create table rlp1 partition of rlp for values from (minvalue) to (1); create table rlp2 partition of rlp for values from (1) to (10); create table rlp3 (b varchar, a int) partition by list (b varchar_ops); create table rlp3_default partition of rlp3 default; create table rlp3abcd partition of rlp3 for values in ('ab', 'cd'); create table rlp3efgh partition of rlp3 for values in ('ef', 'gh'); create table rlp3nullxy partition of rlp3 for values in (null, 'xy'); alter table rlp attach partition rlp3 for values from (15) to (20); create table rlp4 partition of rlp for values from (20) to (30) partition by range (a); create table rlp4_default partition of rlp4 default; create table rlp4_1 partition of rlp4 for values from (20) to (25); create table rlp4_2 partition of rlp4 for values from (25) to (29); create table rlp5 partition of rlp for values from (31) to (maxvalue) partition by range (a); create table rlp5_default partition of rlp5 default; create table rlp5_1 partition of rlp5 for values from (31) to (40); explain (costs off) select * from rlp where a < 1; explain (costs off) select * from rlp where 1 > a; /* commuted */ explain (costs off) select * from rlp where a <= 1; explain (costs off) select * from rlp where a = 1; explain (costs off) select * from rlp where a = 1::bigint; /* same as above */ explain (costs off) select * from rlp where a = 1::numeric; /* no pruning */ explain (costs off) select * from rlp where a <= 10; explain (costs off) select * from rlp where a > 10; explain (costs off) select * from rlp where a < 15; explain (costs off) select * from rlp where a <= 15; explain (costs off) select * from rlp where a > 15 and b = 'ab'; explain (costs off) select * from rlp where a = 16; explain (costs off) select * from rlp where a = 16 and b in ('not', 'in', 'here'); explain (costs off) select * from rlp where a = 16 and b < 'ab'; explain (costs off) select * from rlp where a = 16 and b <= 'ab'; explain (costs off) select * from rlp where a = 16 and b is null; explain (costs off) select * from rlp where a = 16 and b is not null; explain (costs off) select * from rlp where a is null; explain (costs off) select * from rlp where a is not null; explain (costs off) select * from rlp where a > 30; explain (costs off) select * from rlp where a = 30; /* only default is scanned */ explain (costs off) select * from rlp where a <= 31; explain (costs off) select * from rlp where a = 1 or a = 7; explain (costs off) select * from rlp where a = 1 or b = 'ab'; explain (costs off) select * from rlp where a > 20 and a < 27; explain (costs off) select * from rlp where a = 29; explain (costs off) select * from rlp where a >= 29; -- redundant clauses are eliminated explain (costs off) select * from rlp where a > 1 and a = 10; /* only default */ explain (costs off) select * from rlp where a > 1 and a >=15; /* rlp3 onwards, including default */ explain (costs off) select * from rlp where a = 1 and a = 3; /* empty */ explain (costs off) select * from rlp where (a = 1 and a = 3) or (a > 1 and a = 15); -- multi-column keys create table mc3p (a int, b int, c int) partition by range (a, abs(b), c); create table mc3p_default partition of mc3p default; create table mc3p0 partition of mc3p for values from (minvalue, minvalue, minvalue) to (1, 1, 1); create table mc3p1 partition of mc3p for values from (1, 1, 1) to (10, 5, 10); create table mc3p2 partition of mc3p for values from (10, 5, 10) to (10, 10, 10); create table mc3p3 partition of mc3p for values from (10, 10, 10) to (10, 10, 20); create table mc3p4 partition of mc3p for values from (10, 10, 20) to (10, maxvalue, maxvalue); create table mc3p5 partition of mc3p for values from (11, 1, 1) to (20, 10, 10); create table mc3p6 partition of mc3p for values from (20, 10, 10) to (20, 20, 20); create table mc3p7 partition of mc3p for values from (20, 20, 20) to (maxvalue, maxvalue, maxvalue); explain (costs off) select * from mc3p where a = 1; explain (costs off) select * from mc3p where a = 1 and abs(b) < 1; explain (costs off) select * from mc3p where a = 1 and abs(b) = 1; explain (costs off) select * from mc3p where a = 1 and abs(b) = 1 and c < 8; explain (costs off) select * from mc3p where a = 10 and abs(b) between 5 and 35; explain (costs off) select * from mc3p where a > 10; explain (costs off) select * from mc3p where a >= 10; explain (costs off) select * from mc3p where a < 10; explain (costs off) select * from mc3p where a <= 10 and abs(b) < 10; explain (costs off) select * from mc3p where a = 11 and abs(b) = 0; explain (costs off) select * from mc3p where a = 20 and abs(b) = 10 and c = 100; explain (costs off) select * from mc3p where a > 20; explain (costs off) select * from mc3p where a >= 20; explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1 and c = 1) or (a = 10 and abs(b) = 5 and c = 10) or (a > 11 and a < 20); explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1 and c = 1) or (a = 10 and abs(b) = 5 and c = 10) or (a > 11 and a < 20) or a < 1; explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1 and c = 1) or (a = 10 and abs(b) = 5 and c = 10) or (a > 11 and a < 20) or a < 1 or a = 1; explain (costs off) select * from mc3p where a = 1 or abs(b) = 1 or c = 1; explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1) or (a = 10 and abs(b) = 10); explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1) or (a = 10 and abs(b) = 9); -- a simpler multi-column keys case create table mc2p (a int, b int) partition by range (a, b); create table mc2p_default partition of mc2p default; create table mc2p0 partition of mc2p for values from (minvalue, minvalue) to (1, minvalue); create table mc2p1 partition of mc2p for values from (1, minvalue) to (1, 1); create table mc2p2 partition of mc2p for values from (1, 1) to (2, minvalue); create table mc2p3 partition of mc2p for values from (2, minvalue) to (2, 1); create table mc2p4 partition of mc2p for values from (2, 1) to (2, maxvalue); create table mc2p5 partition of mc2p for values from (2, maxvalue) to (maxvalue, maxvalue); explain (costs off) select * from mc2p where a < 2; explain (costs off) select * from mc2p where a = 2 and b < 1; explain (costs off) select * from mc2p where a > 1; explain (costs off) select * from mc2p where a = 1 and b > 1; -- all partitions but the default one should be pruned explain (costs off) select * from mc2p where a = 1 and b is null; explain (costs off) select * from mc2p where a is null and b is null; explain (costs off) select * from mc2p where a is null and b = 1; explain (costs off) select * from mc2p where a is null; explain (costs off) select * from mc2p where b is null; -- boolean partitioning create table boolpart (a bool) partition by list (a); create table boolpart_default partition of boolpart default; create table boolpart_t partition of boolpart for values in ('true'); create table boolpart_f partition of boolpart for values in ('false'); explain (costs off) select * from boolpart where a in (true, false); explain (costs off) select * from boolpart where a = false; explain (costs off) select * from boolpart where not a = false; explain (costs off) select * from boolpart where a is true or a is not true; explain (costs off) select * from boolpart where a is not true; explain (costs off) select * from boolpart where a is not true and a is not false; explain (costs off) select * from boolpart where a is unknown; explain (costs off) select * from boolpart where a is not unknown; -- test scalar-to-array operators create table coercepart (a varchar) partition by list (a); create table coercepart_ab partition of coercepart for values in ('ab'); create table coercepart_bc partition of coercepart for values in ('bc'); create table coercepart_cd partition of coercepart for values in ('cd'); explain (costs off) select * from coercepart where a in ('ab', to_char(125, '999')); explain (costs off) select * from coercepart where a ~ any ('{ab}'); explain (costs off) select * from coercepart where a !~ all ('{ab}'); explain (costs off) select * from coercepart where a ~ any ('{ab,bc}'); explain (costs off) select * from coercepart where a !~ all ('{ab,bc}'); drop table coercepart; CREATE TABLE part (a INT, b INT) PARTITION BY LIST (a); CREATE TABLE part_p1 PARTITION OF part FOR VALUES IN (-2,-1,0,1,2); CREATE TABLE part_p2 PARTITION OF part DEFAULT PARTITION BY RANGE(a); CREATE TABLE part_p2_p1 PARTITION OF part_p2 DEFAULT; INSERT INTO part VALUES (-1,-1), (1,1), (2,NULL), (NULL,-2),(NULL,NULL); EXPLAIN (COSTS OFF) SELECT tableoid::regclass as part, a, b FROM part WHERE a IS NULL ORDER BY 1, 2, 3; -- -- some more cases -- -- -- pruning for partitioned table appearing inside a sub-query -- -- pruning won't work for mc3p, because some keys are Params explain (costs off) select * from mc2p t1, lateral (select count(*) from mc3p t2 where t2.a = t1.b and abs(t2.b) = 1 and t2.c = 1) s where t1.a = 1; -- pruning should work fine, because values for a prefix of keys (a, b) are -- available explain (costs off) select * from mc2p t1, lateral (select count(*) from mc3p t2 where t2.c = t1.b and abs(t2.b) = 1 and t2.a = 1) s where t1.a = 1; -- also here, because values for all keys are provided explain (costs off) select * from mc2p t1, lateral (select count(*) from mc3p t2 where t2.a = 1 and abs(t2.b) = 1 and t2.c = 1) s where t1.a = 1; -- -- pruning with clauses containing <> operator -- -- doesn't prune range partitions create table rp (a int) partition by range (a); create table rp0 partition of rp for values from (minvalue) to (1); create table rp1 partition of rp for values from (1) to (2); create table rp2 partition of rp for values from (2) to (maxvalue); explain (costs off) select * from rp where a <> 1; explain (costs off) select * from rp where a <> 1 and a <> 2; -- null partition should be eliminated due to strict <> clause. explain (costs off) select * from lp where a <> 'a'; -- ensure we detect contradictions in clauses; a can't be NULL and NOT NULL. explain (costs off) select * from lp where a <> 'a' and a is null; explain (costs off) select * from lp where (a <> 'a' and a <> 'd') or a is null; -- check that it also works for a partitioned table that's not root, -- which in this case are partitions of rlp that are themselves -- list-partitioned on b explain (costs off) select * from rlp where a = 15 and b <> 'ab' and b <> 'cd' and b <> 'xy' and b is not null; -- -- different collations for different keys with same expression -- create table coll_pruning_multi (a text) partition by range (substr(a, 1) collate "POSIX", substr(a, 1) collate "C"); create table coll_pruning_multi1 partition of coll_pruning_multi for values from ('a', 'a') to ('a', 'e'); create table coll_pruning_multi2 partition of coll_pruning_multi for values from ('a', 'e') to ('a', 'z'); create table coll_pruning_multi3 partition of coll_pruning_multi for values from ('b', 'a') to ('b', 'e'); -- no pruning, because no value for the leading key explain (costs off) select * from coll_pruning_multi where substr(a, 1) = 'e' collate "C"; -- pruning, with a value provided for the leading key explain (costs off) select * from coll_pruning_multi where substr(a, 1) = 'a' collate "POSIX"; -- pruning, with values provided for both keys explain (costs off) select * from coll_pruning_multi where substr(a, 1) = 'e' collate "C" and substr(a, 1) = 'a' collate "POSIX"; -- -- LIKE operators don't prune -- create table like_op_noprune (a text) partition by list (a); create table like_op_noprune1 partition of like_op_noprune for values in ('ABC'); create table like_op_noprune2 partition of like_op_noprune for values in ('BCD'); explain (costs off) select * from like_op_noprune where a like '%BC'; -- -- tests wherein clause value requires a cross-type comparison function -- create table lparted_by_int2 (a smallint) partition by list (a); create table lparted_by_int2_1 partition of lparted_by_int2 for values in (1); create table lparted_by_int2_16384 partition of lparted_by_int2 for values in (16384); explain (costs off) select * from lparted_by_int2 where a = 100000000000000; create table rparted_by_int2 (a smallint) partition by range (a); create table rparted_by_int2_1 partition of rparted_by_int2 for values from (1) to (10); create table rparted_by_int2_16384 partition of rparted_by_int2 for values from (10) to (16384); -- all partitions pruned explain (costs off) select * from rparted_by_int2 where a > 100000000000000; create table rparted_by_int2_maxvalue partition of rparted_by_int2 for values from (16384) to (maxvalue); -- all partitions but rparted_by_int2_maxvalue pruned explain (costs off) select * from rparted_by_int2 where a > 100000000000000; drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart, rp, coll_pruning_multi, like_op_noprune, lparted_by_int2, rparted_by_int2; -- -- Test Partition pruning for HASH partitioning -- -- Use hand-rolled hash functions and operator classes to get predictable -- result on different matchines. See the definitions of -- part_part_test_int4_ops and part_test_text_ops in insert.sql. -- create table hp (a int, b text) partition by hash (a part_test_int4_ops, b part_test_text_ops); create table hp0 partition of hp for values with (modulus 4, remainder 0); create table hp3 partition of hp for values with (modulus 4, remainder 3); create table hp1 partition of hp for values with (modulus 4, remainder 1); create table hp2 partition of hp for values with (modulus 4, remainder 2); insert into hp values (null, null); insert into hp values (1, null); insert into hp values (1, 'xxx'); insert into hp values (null, 'xxx'); insert into hp values (2, 'xxx'); insert into hp values (1, 'abcde'); select tableoid::regclass, * from hp order by 1; -- partial keys won't prune, nor would non-equality conditions explain (costs off) select * from hp where a = 1; explain (costs off) select * from hp where b = 'xxx'; explain (costs off) select * from hp where a is null; explain (costs off) select * from hp where b is null; explain (costs off) select * from hp where a < 1 and b = 'xxx'; explain (costs off) select * from hp where a <> 1 and b = 'yyy'; explain (costs off) select * from hp where a <> 1 and b <> 'xxx'; -- pruning should work if either a value or a IS NULL clause is provided for -- each of the keys explain (costs off) select * from hp where a is null and b is null; explain (costs off) select * from hp where a = 1 and b is null; explain (costs off) select * from hp where a = 1 and b = 'xxx'; explain (costs off) select * from hp where a is null and b = 'xxx'; explain (costs off) select * from hp where a = 2 and b = 'xxx'; explain (costs off) select * from hp where a = 1 and b = 'abcde'; explain (costs off) select * from hp where (a = 1 and b = 'abcde') or (a = 2 and b = 'xxx') or (a is null and b is null); drop table hp; -- -- Test runtime partition pruning -- create table ab (a int not null, b int not null) partition by list (a); create table ab_a2 partition of ab for values in(2) partition by list (b); create table ab_a2_b1 partition of ab_a2 for values in (1); create table ab_a2_b2 partition of ab_a2 for values in (2); create table ab_a2_b3 partition of ab_a2 for values in (3); create table ab_a1 partition of ab for values in(1) partition by list (b); create table ab_a1_b1 partition of ab_a1 for values in (1); create table ab_a1_b2 partition of ab_a1 for values in (2); create table ab_a1_b3 partition of ab_a1 for values in (3); create table ab_a3 partition of ab for values in(3) partition by list (b); create table ab_a3_b1 partition of ab_a3 for values in (1); create table ab_a3_b2 partition of ab_a3 for values in (2); create table ab_a3_b3 partition of ab_a3 for values in (3); -- Disallow index only scans as concurrent transactions may stop visibility -- bits being set causing "Heap Fetches" to be unstable in the EXPLAIN ANALYZE -- output. set enable_indexonlyscan = off; prepare ab_q1 (int, int, int) as select * from ab where a between $1 and $2 and b <= $3; -- Execute query 5 times to allow choose_custom_plan -- to start considering a generic plan. execute ab_q1 (1, 8, 3); execute ab_q1 (1, 8, 3); execute ab_q1 (1, 8, 3); execute ab_q1 (1, 8, 3); execute ab_q1 (1, 8, 3); explain (analyze, costs off, summary off, timing off) execute ab_q1 (2, 2, 3); explain (analyze, costs off, summary off, timing off) execute ab_q1 (1, 2, 3); deallocate ab_q1; -- Runtime pruning after optimizer pruning prepare ab_q1 (int, int) as select a from ab where a between $1 and $2 and b < 3; -- Execute query 5 times to allow choose_custom_plan -- to start considering a generic plan. execute ab_q1 (1, 8); execute ab_q1 (1, 8); execute ab_q1 (1, 8); execute ab_q1 (1, 8); execute ab_q1 (1, 8); explain (analyze, costs off, summary off, timing off) execute ab_q1 (2, 2); explain (analyze, costs off, summary off, timing off) execute ab_q1 (2, 4); -- Ensure a mix of PARAM_EXTERN and PARAM_EXEC Params work together at -- different levels of partitioning. prepare ab_q2 (int, int) as select a from ab where a between $1 and $2 and b < (select 3); execute ab_q2 (1, 8); execute ab_q2 (1, 8); execute ab_q2 (1, 8); execute ab_q2 (1, 8); execute ab_q2 (1, 8); explain (analyze, costs off, summary off, timing off) execute ab_q2 (2, 2); -- As above, but swap the PARAM_EXEC Param to the first partition level prepare ab_q3 (int, int) as select a from ab where b between $1 and $2 and a < (select 3); execute ab_q3 (1, 8); execute ab_q3 (1, 8); execute ab_q3 (1, 8); execute ab_q3 (1, 8); execute ab_q3 (1, 8); explain (analyze, costs off, summary off, timing off) execute ab_q3 (2, 2); -- Test a backwards Append scan create table list_part (a int) partition by list (a); create table list_part1 partition of list_part for values in (1); create table list_part2 partition of list_part for values in (2); create table list_part3 partition of list_part for values in (3); create table list_part4 partition of list_part for values in (4); insert into list_part select generate_series(1,4); begin; -- Don't select an actual value out of the table as the order of the Append's -- subnodes may not be stable. declare cur SCROLL CURSOR for select 1 from list_part where a > (select 1) and a < (select 4); -- move beyond the final row move 3 from cur; -- Ensure we get two rows. fetch backward all from cur; commit; begin; -- Test run-time pruning using stable functions create function list_part_fn(int) returns int as $$ begin return $1; end;$$ language plpgsql stable; -- Ensure pruning works using a stable function containing no Vars explain (analyze, costs off, summary off, timing off) select * from list_part where a = list_part_fn(1); -- Ensure pruning does not take place when the function has a Var parameter explain (analyze, costs off, summary off, timing off) select * from list_part where a = list_part_fn(a); -- Ensure pruning does not take place when the expression contains a Var. explain (analyze, costs off, summary off, timing off) select * from list_part where a = list_part_fn(1) + a; rollback; drop table list_part; -- Parallel append -- Suppress the number of loops each parallel node runs for. This is because -- more than one worker may run the same parallel node if timing conditions -- are just right, which destabilizes the test. create function explain_parallel_append(text) returns setof text language plpgsql as $$ declare ln text; begin for ln in execute format('explain (analyze, costs off, summary off, timing off) %s', $1) loop if ln like '%Parallel%' then ln := regexp_replace(ln, 'loops=\d*', 'loops=N'); end if; return next ln; end loop; end; $$; prepare ab_q4 (int, int) as select avg(a) from ab where a between $1 and $2 and b < 4; -- Encourage use of parallel plans set parallel_setup_cost = 0; set parallel_tuple_cost = 0; set min_parallel_table_scan_size = 0; set max_parallel_workers_per_gather = 2; -- Execute query 5 times to allow choose_custom_plan -- to start considering a generic plan. execute ab_q4 (1, 8); execute ab_q4 (1, 8); execute ab_q4 (1, 8); execute ab_q4 (1, 8); execute ab_q4 (1, 8); select explain_parallel_append('execute ab_q4 (2, 2)'); -- Test run-time pruning with IN lists. prepare ab_q5 (int, int, int) as select avg(a) from ab where a in($1,$2,$3) and b < 4; -- Execute query 5 times to allow choose_custom_plan -- to start considering a generic plan. execute ab_q5 (1, 2, 3); execute ab_q5 (1, 2, 3); execute ab_q5 (1, 2, 3); execute ab_q5 (1, 2, 3); execute ab_q5 (1, 2, 3); select explain_parallel_append('execute ab_q5 (1, 1, 1)'); select explain_parallel_append('execute ab_q5 (2, 3, 3)'); -- Try some params whose values do not belong to any partition. -- We'll still get a single subplan in this case, but it should not be scanned. select explain_parallel_append('execute ab_q5 (33, 44, 55)'); -- Test Parallel Append with PARAM_EXEC Params select explain_parallel_append('select count(*) from ab where (a = (select 1) or a = (select 3)) and b = 2'); -- Test pruning during parallel nested loop query create table lprt_a (a int not null); -- Insert some values we won't find in ab insert into lprt_a select 0 from generate_series(1,100); -- and insert some values that we should find. insert into lprt_a values(1),(1); analyze lprt_a; create index ab_a2_b1_a_idx on ab_a2_b1 (a); create index ab_a2_b2_a_idx on ab_a2_b2 (a); create index ab_a2_b3_a_idx on ab_a2_b3 (a); create index ab_a1_b1_a_idx on ab_a1_b1 (a); create index ab_a1_b2_a_idx on ab_a1_b2 (a); create index ab_a1_b3_a_idx on ab_a1_b3 (a); create index ab_a3_b1_a_idx on ab_a3_b1 (a); create index ab_a3_b2_a_idx on ab_a3_b2 (a); create index ab_a3_b3_a_idx on ab_a3_b3 (a); set enable_hashjoin = 0; set enable_mergejoin = 0; select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(0, 0, 1)'); -- Ensure the same partitions are pruned when we make the nested loop -- parameter an Expr rather than a plain Param. select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a + 0 where a.a in(0, 0, 1)'); insert into lprt_a values(3),(3); select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 3)'); select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 0)'); delete from lprt_a where a = 1; select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 0)'); reset enable_hashjoin; reset enable_mergejoin; reset parallel_setup_cost; reset parallel_tuple_cost; reset min_parallel_table_scan_size; reset max_parallel_workers_per_gather; -- Test run-time partition pruning with an initplan explain (analyze, costs off, summary off, timing off) select * from ab where a = (select max(a) from lprt_a) and b = (select max(a)-1 from lprt_a); -- Test run-time partition pruning with UNION ALL parents explain (analyze, costs off, summary off, timing off) select * from (select * from ab where a = 1 union all select * from ab) ab where b = (select 1); -- A case containing a UNION ALL with a non-partitioned child. explain (analyze, costs off, summary off, timing off) select * from (select * from ab where a = 1 union all (values(10,5)) union all select * from ab) ab where b = (select 1); -- Another UNION ALL test, but containing a mix of exec init and exec run-time pruning. create table xy_1 (x int, y int); insert into xy_1 values(100,-10); set enable_bitmapscan = 0; set enable_indexscan = 0; set plan_cache_mode = 'force_generic_plan'; prepare ab_q6 as select * from ( select tableoid::regclass,a,b from ab union all select tableoid::regclass,x,y from xy_1 union all select tableoid::regclass,a,b from ab ) ab where a = $1 and b = (select -10); -- Ensure the xy_1 subplan is not pruned. explain (analyze, costs off, summary off, timing off) execute ab_q6(1); -- Ensure we see just the xy_1 row. execute ab_q6(100); reset enable_bitmapscan; reset enable_indexscan; reset plan_cache_mode; deallocate ab_q1; deallocate ab_q2; deallocate ab_q3; deallocate ab_q4; deallocate ab_q5; deallocate ab_q6; -- UPDATE on a partition subtree has been seen to have problems. insert into ab values (1,2); explain (analyze, costs off, summary off, timing off) update ab_a1 set b = 3 from ab where ab.a = 1 and ab.a = ab_a1.a; table ab; -- Test UPDATE where source relation has run-time pruning enabled truncate ab; insert into ab values (1, 1), (1, 2), (1, 3), (2, 1); explain (analyze, costs off, summary off, timing off) update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 1); select tableoid::regclass, * from ab; drop table ab, lprt_a; -- Join create table tbl1(col1 int); insert into tbl1 values (501), (505); -- Basic table create table tprt (col1 int) partition by range (col1); create table tprt_1 partition of tprt for values from (1) to (501); create table tprt_2 partition of tprt for values from (501) to (1001); create table tprt_3 partition of tprt for values from (1001) to (2001); create table tprt_4 partition of tprt for values from (2001) to (3001); create table tprt_5 partition of tprt for values from (3001) to (4001); create table tprt_6 partition of tprt for values from (4001) to (5001); create index tprt1_idx on tprt_1 (col1); create index tprt2_idx on tprt_2 (col1); create index tprt3_idx on tprt_3 (col1); create index tprt4_idx on tprt_4 (col1); create index tprt5_idx on tprt_5 (col1); create index tprt6_idx on tprt_6 (col1); insert into tprt values (10), (20), (501), (502), (505), (1001), (4500); set enable_hashjoin = off; set enable_mergejoin = off; explain (analyze, costs off, summary off, timing off) select * from tbl1 join tprt on tbl1.col1 > tprt.col1; explain (analyze, costs off, summary off, timing off) select * from tbl1 join tprt on tbl1.col1 = tprt.col1; select tbl1.col1, tprt.col1 from tbl1 inner join tprt on tbl1.col1 > tprt.col1 order by tbl1.col1, tprt.col1; select tbl1.col1, tprt.col1 from tbl1 inner join tprt on tbl1.col1 = tprt.col1 order by tbl1.col1, tprt.col1; -- Multiple partitions insert into tbl1 values (1001), (1010), (1011); explain (analyze, costs off, summary off, timing off) select * from tbl1 inner join tprt on tbl1.col1 > tprt.col1; explain (analyze, costs off, summary off, timing off) select * from tbl1 inner join tprt on tbl1.col1 = tprt.col1; select tbl1.col1, tprt.col1 from tbl1 inner join tprt on tbl1.col1 > tprt.col1 order by tbl1.col1, tprt.col1; select tbl1.col1, tprt.col1 from tbl1 inner join tprt on tbl1.col1 = tprt.col1 order by tbl1.col1, tprt.col1; -- Last partition delete from tbl1; insert into tbl1 values (4400); explain (analyze, costs off, summary off, timing off) select * from tbl1 join tprt on tbl1.col1 < tprt.col1; select tbl1.col1, tprt.col1 from tbl1 inner join tprt on tbl1.col1 < tprt.col1 order by tbl1.col1, tprt.col1; -- No matching partition delete from tbl1; insert into tbl1 values (10000); explain (analyze, costs off, summary off, timing off) select * from tbl1 join tprt on tbl1.col1 = tprt.col1; select tbl1.col1, tprt.col1 from tbl1 inner join tprt on tbl1.col1 = tprt.col1 order by tbl1.col1, tprt.col1; drop table tbl1, tprt; -- Test with columns defined in varying orders between each level create table part_abc (a int not null, b int not null, c int not null) partition by list (a); create table part_bac (b int not null, a int not null, c int not null) partition by list (b); create table part_cab (c int not null, a int not null, b int not null) partition by list (c); create table part_abc_p1 (a int not null, b int not null, c int not null); alter table part_abc attach partition part_bac for values in(1); alter table part_bac attach partition part_cab for values in(2); alter table part_cab attach partition part_abc_p1 for values in(3); prepare part_abc_q1 (int, int, int) as select * from part_abc where a = $1 and b = $2 and c = $3; -- Execute query 5 times to allow choose_custom_plan -- to start considering a generic plan. execute part_abc_q1 (1, 2, 3); execute part_abc_q1 (1, 2, 3); execute part_abc_q1 (1, 2, 3); execute part_abc_q1 (1, 2, 3); execute part_abc_q1 (1, 2, 3); -- Single partition should be scanned. explain (analyze, costs off, summary off, timing off) execute part_abc_q1 (1, 2, 3); deallocate part_abc_q1; drop table part_abc; -- Ensure that an Append node properly handles a sub-partitioned table -- matching without any of its leaf partitions matching the clause. create table listp (a int, b int) partition by list (a); create table listp_1 partition of listp for values in(1) partition by list (b); create table listp_1_1 partition of listp_1 for values in(1); create table listp_2 partition of listp for values in(2) partition by list (b); create table listp_2_1 partition of listp_2 for values in(2); select * from listp where b = 1; -- Ensure that an Append node properly can handle selection of all first level -- partitions before finally detecting the correct set of 2nd level partitions -- which match the given parameter. prepare q1 (int,int) as select * from listp where b in ($1,$2); execute q1 (1,2); execute q1 (1,2); execute q1 (1,2); execute q1 (1,2); execute q1 (1,2); explain (analyze, costs off, summary off, timing off) execute q1 (1,1); explain (analyze, costs off, summary off, timing off) execute q1 (2,2); -- Try with no matching partitions. One subplan should remain in this case, -- but it shouldn't be executed. explain (analyze, costs off, summary off, timing off) execute q1 (0,0); deallocate q1; -- Test more complex cases where a not-equal condition further eliminates partitions. prepare q1 (int,int,int,int) as select * from listp where b in($1,$2) and $3 <> b and $4 <> b; execute q1 (1,2,3,4); execute q1 (1,2,3,4); execute q1 (1,2,3,4); execute q1 (1,2,3,4); execute q1 (1,2,3,4); -- Both partitions allowed by IN clause, but one disallowed by <> clause explain (analyze, costs off, summary off, timing off) execute q1 (1,2,2,0); -- Both partitions allowed by IN clause, then both excluded again by <> clauses. -- One subplan will remain in this case, but it should not be executed. explain (analyze, costs off, summary off, timing off) execute q1 (1,2,2,1); -- Ensure Params that evaluate to NULL properly prune away all partitions explain (analyze, costs off, summary off, timing off) select * from listp where a = (select null::int); drop table listp; -- Ensure runtime pruning works with initplans params with boolean types create table boolvalues (value bool not null); insert into boolvalues values('t'),('f'); create table boolp (a bool) partition by list (a); create table boolp_t partition of boolp for values in('t'); create table boolp_f partition of boolp for values in('f'); explain (analyze, costs off, summary off, timing off) select * from boolp where a = (select value from boolvalues where value); explain (analyze, costs off, summary off, timing off) select * from boolp where a = (select value from boolvalues where not value); drop table boolp; -- -- Test run-time pruning of MergeAppend subnodes -- set enable_seqscan = off; set enable_sort = off; create table ma_test (a int, b int) partition by range (a); create table ma_test_p1 partition of ma_test for values from (0) to (10); create table ma_test_p2 partition of ma_test for values from (10) to (20); create table ma_test_p3 partition of ma_test for values from (20) to (30); insert into ma_test select x,x from generate_series(0,29) t(x); create index on ma_test (b); analyze ma_test; prepare mt_q1 (int) as select a from ma_test where a >= $1 and a % 10 = 5 order by b; -- Execute query 5 times to allow choose_custom_plan -- to start considering a generic plan. execute mt_q1(0); execute mt_q1(0); execute mt_q1(0); execute mt_q1(0); execute mt_q1(0); explain (analyze, costs off, summary off, timing off) execute mt_q1(15); execute mt_q1(15); explain (analyze, costs off, summary off, timing off) execute mt_q1(25); execute mt_q1(25); -- Ensure MergeAppend behaves correctly when no subplans match explain (analyze, costs off, summary off, timing off) execute mt_q1(35); execute mt_q1(35); deallocate mt_q1; -- ensure initplan params properly prune partitions explain (analyze, costs off, summary off, timing off) select * from ma_test where a >= (select min(b) from ma_test_p2) order by b; reset enable_seqscan; reset enable_sort; drop table ma_test; reset enable_indexonlyscan; -- -- check that pruning works properly when the partition key is of a -- pseudotype -- -- array type list partition key create table pp_arrpart (a int[]) partition by list (a); create table pp_arrpart1 partition of pp_arrpart for values in ('{1}'); create table pp_arrpart2 partition of pp_arrpart for values in ('{2, 3}', '{4, 5}'); explain (costs off) select * from pp_arrpart where a = '{1}'; explain (costs off) select * from pp_arrpart where a = '{1, 2}'; explain (costs off) select * from pp_arrpart where a in ('{4, 5}', '{1}'); explain (costs off) update pp_arrpart set a = a where a = '{1}'; explain (costs off) delete from pp_arrpart where a = '{1}'; drop table pp_arrpart; -- array type hash partition key create table pph_arrpart (a int[]) partition by hash (a); create table pph_arrpart1 partition of pph_arrpart for values with (modulus 2, remainder 0); create table pph_arrpart2 partition of pph_arrpart for values with (modulus 2, remainder 1); insert into pph_arrpart values ('{1}'), ('{1, 2}'), ('{4, 5}'); select tableoid::regclass, * from pph_arrpart order by 1; explain (costs off) select * from pph_arrpart where a = '{1}'; explain (costs off) select * from pph_arrpart where a = '{1, 2}'; explain (costs off) select * from pph_arrpart where a in ('{4, 5}', '{1}'); drop table pph_arrpart; -- enum type list partition key create type pp_colors as enum ('green', 'blue', 'black'); create table pp_enumpart (a pp_colors) partition by list (a); create table pp_enumpart_green partition of pp_enumpart for values in ('green'); create table pp_enumpart_blue partition of pp_enumpart for values in ('blue'); explain (costs off) select * from pp_enumpart where a = 'blue'; explain (costs off) select * from pp_enumpart where a = 'black'; drop table pp_enumpart; drop type pp_colors; -- record type as partition key create type pp_rectype as (a int, b int); create table pp_recpart (a pp_rectype) partition by list (a); create table pp_recpart_11 partition of pp_recpart for values in ('(1,1)'); create table pp_recpart_23 partition of pp_recpart for values in ('(2,3)'); explain (costs off) select * from pp_recpart where a = '(1,1)'::pp_rectype; explain (costs off) select * from pp_recpart where a = '(1,2)'::pp_rectype; drop table pp_recpart; drop type pp_rectype; -- range type partition key create table pp_intrangepart (a int4range) partition by list (a); create table pp_intrangepart12 partition of pp_intrangepart for values in ('[1,2]'); create table pp_intrangepart2inf partition of pp_intrangepart for values in ('[2,)'); explain (costs off) select * from pp_intrangepart where a = '[1,2]'::int4range; explain (costs off) select * from pp_intrangepart where a = '(1,2)'::int4range; drop table pp_intrangepart; -- -- Ensure the enable_partition_prune GUC properly disables partition pruning. -- create table pp_lp (a int, value int) partition by list (a); create table pp_lp1 partition of pp_lp for values in(1); create table pp_lp2 partition of pp_lp for values in(2); explain (costs off) select * from pp_lp where a = 1; explain (costs off) update pp_lp set value = 10 where a = 1; explain (costs off) delete from pp_lp where a = 1; set enable_partition_pruning = off; set constraint_exclusion = 'partition'; -- this should not affect the result. explain (costs off) select * from pp_lp where a = 1; explain (costs off) update pp_lp set value = 10 where a = 1; explain (costs off) delete from pp_lp where a = 1; set constraint_exclusion = 'off'; -- this should not affect the result. explain (costs off) select * from pp_lp where a = 1; explain (costs off) update pp_lp set value = 10 where a = 1; explain (costs off) delete from pp_lp where a = 1; drop table pp_lp; -- Ensure enable_partition_prune does not affect non-partitioned tables. create table inh_lp (a int, value int); create table inh_lp1 (a int, value int, check(a = 1)) inherits (inh_lp); create table inh_lp2 (a int, value int, check(a = 2)) inherits (inh_lp); set constraint_exclusion = 'partition'; -- inh_lp2 should be removed in the following 3 cases. explain (costs off) select * from inh_lp where a = 1; explain (costs off) update inh_lp set value = 10 where a = 1; explain (costs off) delete from inh_lp where a = 1; -- Ensure we don't exclude normal relations when we only expect to exclude -- inheritance children explain (costs off) update inh_lp1 set value = 10 where a = 2; drop table inh_lp cascade; reset enable_partition_pruning; reset constraint_exclusion; -- Check pruning for a partition tree containing only temporary relations create temp table pp_temp_parent (a int) partition by list (a); create temp table pp_temp_part_1 partition of pp_temp_parent for values in (1); create temp table pp_temp_part_def partition of pp_temp_parent default; explain (costs off) select * from pp_temp_parent where true; explain (costs off) select * from pp_temp_parent where a = 2; drop table pp_temp_parent; -- Stress run-time partition pruning a bit more, per bug reports create temp table p (a int, b int, c int) partition by list (a); create temp table p1 partition of p for values in (1); create temp table p2 partition of p for values in (2); create temp table q (a int, b int, c int) partition by list (a); create temp table q1 partition of q for values in (1) partition by list (b); create temp table q11 partition of q1 for values in (1) partition by list (c); create temp table q111 partition of q11 for values in (1); create temp table q2 partition of q for values in (2) partition by list (b); create temp table q21 partition of q2 for values in (1); create temp table q22 partition of q2 for values in (2); insert into q22 values (2, 2, 3); explain (costs off) select * from ( select * from p union all select * from q1 union all select 1, 1, 1 ) s(a, b, c) where s.a = 1 and s.b = 1 and s.c = (select 1); select * from ( select * from p union all select * from q1 union all select 1, 1, 1 ) s(a, b, c) where s.a = 1 and s.b = 1 and s.c = (select 1); prepare q (int, int) as select * from ( select * from p union all select * from q1 union all select 1, 1, 1 ) s(a, b, c) where s.a = $1 and s.b = $2 and s.c = (select 1); set plan_cache_mode to force_generic_plan; explain (costs off) execute q (1, 1); execute q (1, 1); reset plan_cache_mode; drop table p, q; -- Ensure run-time pruning works correctly when we match a partitioned table -- on the first level but find no matching partitions on the second level. create table listp (a int, b int) partition by list (a); create table listp1 partition of listp for values in(1); create table listp2 partition of listp for values in(2) partition by list(b); create table listp2_10 partition of listp2 for values in (10); explain (analyze, costs off, summary off, timing off) select * from listp where a = (select 2) and b <> 10; -- -- check that a partition directly accessed in a query is excluded with -- constraint_exclusion = on -- -- turn off partition pruning, so that it doesn't interfere set enable_partition_pruning to off; -- setting constraint_exclusion to 'partition' disables exclusion set constraint_exclusion to 'partition'; explain (costs off) select * from listp1 where a = 2; explain (costs off) update listp1 set a = 1 where a = 2; -- constraint exclusion enabled set constraint_exclusion to 'on'; explain (costs off) select * from listp1 where a = 2; explain (costs off) update listp1 set a = 1 where a = 2; reset constraint_exclusion; reset enable_partition_pruning; drop table listp; pgFormatter-4.2/t/pg-test-files/sql/password.sql000066400000000000000000000073561361326045100217750ustar00rootroot00000000000000-- -- Tests for password verifiers -- -- Tests for GUC password_encryption SET password_encryption = 'novalue'; -- error SET password_encryption = true; -- ok SET password_encryption = 'md5'; -- ok SET password_encryption = 'scram-sha-256'; -- ok -- consistency of password entries SET password_encryption = 'md5'; CREATE ROLE regress_passwd1 PASSWORD 'role_pwd1'; SET password_encryption = 'on'; CREATE ROLE regress_passwd2 PASSWORD 'role_pwd2'; SET password_encryption = 'scram-sha-256'; CREATE ROLE regress_passwd3 PASSWORD 'role_pwd3'; CREATE ROLE regress_passwd4 PASSWORD NULL; -- check list of created entries -- -- The scram verifier will look something like: -- SCRAM-SHA-256$4096:E4HxLGtnRzsYwg==$6YtlR4t69SguDiwFvbVgVZtuz6gpJQQqUMZ7IQJK5yI=:ps75jrHeYU4lXCcXI4O8oIdJ3eO8o2jirjruw9phBTo= -- -- Since the salt is random, the exact value stored will be different on every test -- run. Use a regular expression to mask the changing parts. SELECT rolname, regexp_replace(rolpassword, '(SCRAM-SHA-256)\$(\d+):([a-zA-Z0-9+/=]+)\$([a-zA-Z0-9+=/]+):([a-zA-Z0-9+/=]+)', '\1$\2:$:') as rolpassword_masked FROM pg_authid WHERE rolname LIKE 'regress_passwd%' ORDER BY rolname, rolpassword; -- Rename a role ALTER ROLE regress_passwd2 RENAME TO regress_passwd2_new; -- md5 entry should have been removed SELECT rolname, rolpassword FROM pg_authid WHERE rolname LIKE 'regress_passwd2_new' ORDER BY rolname, rolpassword; ALTER ROLE regress_passwd2_new RENAME TO regress_passwd2; -- Change passwords with ALTER USER. With plaintext or already-encrypted -- passwords. SET password_encryption = 'md5'; -- encrypt with MD5 ALTER ROLE regress_passwd2 PASSWORD 'foo'; -- already encrypted, use as they are ALTER ROLE regress_passwd1 PASSWORD 'md5cd3578025fe2c3d7ed1b9a9b26238b70'; ALTER ROLE regress_passwd3 PASSWORD 'SCRAM-SHA-256$4096:VLK4RMaQLCvNtQ==$6YtlR4t69SguDiwFvbVgVZtuz6gpJQQqUMZ7IQJK5yI=:ps75jrHeYU4lXCcXI4O8oIdJ3eO8o2jirjruw9phBTo='; SET password_encryption = 'scram-sha-256'; -- create SCRAM verifier ALTER ROLE regress_passwd4 PASSWORD 'foo'; -- already encrypted with MD5, use as it is CREATE ROLE regress_passwd5 PASSWORD 'md5e73a4b11df52a6068f8b39f90be36023'; -- This looks like a valid SCRAM-SHA-256 verifier, but it is not -- so it should be hashed with SCRAM-SHA-256. CREATE ROLE regress_passwd6 PASSWORD 'SCRAM-SHA-256$1234'; -- These may look like valid MD5 verifiers, but they are not, so they -- should be hashed with SCRAM-SHA-256. -- trailing garbage at the end CREATE ROLE regress_passwd7 PASSWORD 'md5012345678901234567890123456789zz'; -- invalid length CREATE ROLE regress_passwd8 PASSWORD 'md501234567890123456789012345678901zz'; SELECT rolname, regexp_replace(rolpassword, '(SCRAM-SHA-256)\$(\d+):([a-zA-Z0-9+/=]+)\$([a-zA-Z0-9+=/]+):([a-zA-Z0-9+/=]+)', '\1$\2:$:') as rolpassword_masked FROM pg_authid WHERE rolname LIKE 'regress_passwd%' ORDER BY rolname, rolpassword; -- An empty password is not allowed, in any form CREATE ROLE regress_passwd_empty PASSWORD ''; ALTER ROLE regress_passwd_empty PASSWORD 'md585939a5ce845f1a1b620742e3c659e0a'; ALTER ROLE regress_passwd_empty PASSWORD 'SCRAM-SHA-256$4096:hpFyHTUsSWcR7O9P$LgZFIt6Oqdo27ZFKbZ2nV+vtnYM995pDh9ca6WSi120=:qVV5NeluNfUPkwm7Vqat25RjSPLkGeoZBQs6wVv+um4='; SELECT rolpassword FROM pg_authid WHERE rolname='regress_passwd_empty'; DROP ROLE regress_passwd1; DROP ROLE regress_passwd2; DROP ROLE regress_passwd3; DROP ROLE regress_passwd4; DROP ROLE regress_passwd5; DROP ROLE regress_passwd6; DROP ROLE regress_passwd7; DROP ROLE regress_passwd8; DROP ROLE regress_passwd_empty; -- all entries should have been removed SELECT rolname, rolpassword FROM pg_authid WHERE rolname LIKE 'regress_passwd%' ORDER BY rolname, rolpassword; pgFormatter-4.2/t/pg-test-files/sql/path.sql000066400000000000000000000020501361326045100210510ustar00rootroot00000000000000-- -- PATH -- --DROP TABLE PATH_TBL; CREATE TABLE PATH_TBL (f1 path); INSERT INTO PATH_TBL VALUES ('[(1,2),(3,4)]'); INSERT INTO PATH_TBL VALUES (' ( ( 1 , 2 ) , ( 3 , 4 ) ) '); INSERT INTO PATH_TBL VALUES ('[ (0,0),(3,0),(4,5),(1,6) ]'); INSERT INTO PATH_TBL VALUES ('((1,2) ,(3,4 ))'); INSERT INTO PATH_TBL VALUES ('1,2 ,3,4 '); INSERT INTO PATH_TBL VALUES (' [1,2,3, 4] '); INSERT INTO PATH_TBL VALUES ('((10,20))'); -- Only one point INSERT INTO PATH_TBL VALUES ('[ 11,12,13,14 ]'); INSERT INTO PATH_TBL VALUES ('( 11,12,13,14) '); -- bad values for parser testing INSERT INTO PATH_TBL VALUES ('[]'); INSERT INTO PATH_TBL VALUES ('[(,2),(3,4)]'); INSERT INTO PATH_TBL VALUES ('[(1,2),(3,4)'); INSERT INTO PATH_TBL VALUES ('(1,2,3,4'); INSERT INTO PATH_TBL VALUES ('(1,2),(3,4)]'); SELECT '' AS count, f1 AS open_path FROM PATH_TBL WHERE isopen(f1); SELECT '' AS count, f1 AS closed_path FROM PATH_TBL WHERE isclosed(f1); SELECT '' AS count, pclose(f1) AS closed_path FROM PATH_TBL; SELECT '' AS count, popen(f1) AS open_path FROM PATH_TBL; pgFormatter-4.2/t/pg-test-files/sql/pg_lsn.sql000066400000000000000000000021741361326045100214060ustar00rootroot00000000000000-- -- PG_LSN -- CREATE TABLE PG_LSN_TBL (f1 pg_lsn); -- Largest and smallest input INSERT INTO PG_LSN_TBL VALUES ('0/0'); INSERT INTO PG_LSN_TBL VALUES ('FFFFFFFF/FFFFFFFF'); -- Incorrect input INSERT INTO PG_LSN_TBL VALUES ('G/0'); INSERT INTO PG_LSN_TBL VALUES ('-1/0'); INSERT INTO PG_LSN_TBL VALUES (' 0/12345678'); INSERT INTO PG_LSN_TBL VALUES ('ABCD/'); INSERT INTO PG_LSN_TBL VALUES ('/ABCD'); DROP TABLE PG_LSN_TBL; -- Operators SELECT '0/16AE7F8' = '0/16AE7F8'::pg_lsn; SELECT '0/16AE7F8'::pg_lsn != '0/16AE7F7'; SELECT '0/16AE7F7' < '0/16AE7F8'::pg_lsn; SELECT '0/16AE7F8' > pg_lsn '0/16AE7F7'; SELECT '0/16AE7F7'::pg_lsn - '0/16AE7F8'::pg_lsn; SELECT '0/16AE7F8'::pg_lsn - '0/16AE7F7'::pg_lsn; -- Check btree and hash opclasses EXPLAIN (COSTS OFF) SELECT DISTINCT (i || '/' || j)::pg_lsn f FROM generate_series(1, 10) i, generate_series(1, 10) j, generate_series(1, 5) k WHERE i <= 10 AND j > 0 AND j <= 10 ORDER BY f; SELECT DISTINCT (i || '/' || j)::pg_lsn f FROM generate_series(1, 10) i, generate_series(1, 10) j, generate_series(1, 5) k WHERE i <= 10 AND j > 0 AND j <= 10 ORDER BY f; pgFormatter-4.2/t/pg-test-files/sql/plancache.sql000066400000000000000000000121731361326045100220420ustar00rootroot00000000000000-- -- Tests to exercise the plan caching/invalidation mechanism -- CREATE TEMP TABLE pcachetest AS SELECT * FROM int8_tbl; -- create and use a cached plan PREPARE prepstmt AS SELECT * FROM pcachetest; EXECUTE prepstmt; -- and one with parameters PREPARE prepstmt2(bigint) AS SELECT * FROM pcachetest WHERE q1 = $1; EXECUTE prepstmt2(123); -- invalidate the plans and see what happens DROP TABLE pcachetest; EXECUTE prepstmt; EXECUTE prepstmt2(123); -- recreate the temp table (this demonstrates that the raw plan is -- purely textual and doesn't depend on OIDs, for instance) CREATE TEMP TABLE pcachetest AS SELECT * FROM int8_tbl ORDER BY 2; EXECUTE prepstmt; EXECUTE prepstmt2(123); -- prepared statements should prevent change in output tupdesc, -- since clients probably aren't expecting that to change on the fly ALTER TABLE pcachetest ADD COLUMN q3 bigint; EXECUTE prepstmt; EXECUTE prepstmt2(123); -- but we're nice guys and will let you undo your mistake ALTER TABLE pcachetest DROP COLUMN q3; EXECUTE prepstmt; EXECUTE prepstmt2(123); -- Try it with a view, which isn't directly used in the resulting plan -- but should trigger invalidation anyway CREATE TEMP VIEW pcacheview AS SELECT * FROM pcachetest; PREPARE vprep AS SELECT * FROM pcacheview; EXECUTE vprep; CREATE OR REPLACE TEMP VIEW pcacheview AS SELECT q1, q2/2 AS q2 FROM pcachetest; EXECUTE vprep; -- Check basic SPI plan invalidation create function cache_test(int) returns int as $$ declare total int; begin create temp table t1(f1 int); insert into t1 values($1); insert into t1 values(11); insert into t1 values(12); insert into t1 values(13); select sum(f1) into total from t1; drop table t1; return total; end $$ language plpgsql; select cache_test(1); select cache_test(2); select cache_test(3); -- Check invalidation of plpgsql "simple expression" create temp view v1 as select 2+2 as f1; create function cache_test_2() returns int as $$ begin return f1 from v1; end$$ language plpgsql; select cache_test_2(); create or replace temp view v1 as select 2+2+4 as f1; select cache_test_2(); create or replace temp view v1 as select 2+2+4+(select max(unique1) from tenk1) as f1; select cache_test_2(); --- Check that change of search_path is honored when re-using cached plan create schema s1 create table abc (f1 int); create schema s2 create table abc (f1 int); insert into s1.abc values(123); insert into s2.abc values(456); set search_path = s1; prepare p1 as select f1 from abc; execute p1; set search_path = s2; select f1 from abc; execute p1; alter table s1.abc add column f2 float8; -- force replan execute p1; drop schema s1 cascade; drop schema s2 cascade; reset search_path; -- Check that invalidation deals with regclass constants create temp sequence seq; prepare p2 as select nextval('seq'); execute p2; drop sequence seq; create temp sequence seq; execute p2; -- Check DDL via SPI, immediately followed by SPI plan re-use -- (bug in original coding) create function cachebug() returns void as $$ declare r int; begin drop table if exists temptable cascade; create temp table temptable as select * from generate_series(1,3) as f1; create temp view vv as select * from temptable; for r in select * from vv loop raise notice '%', r; end loop; end$$ language plpgsql; select cachebug(); select cachebug(); -- Check that addition or removal of any partition is correctly dealt with by -- default partition table when it is being used in prepared statement. create table pc_list_parted (a int) partition by list(a); create table pc_list_part_null partition of pc_list_parted for values in (null); create table pc_list_part_1 partition of pc_list_parted for values in (1); create table pc_list_part_def partition of pc_list_parted default; prepare pstmt_def_insert (int) as insert into pc_list_part_def values($1); -- should fail execute pstmt_def_insert(null); execute pstmt_def_insert(1); create table pc_list_part_2 partition of pc_list_parted for values in (2); execute pstmt_def_insert(2); alter table pc_list_parted detach partition pc_list_part_null; -- should be ok execute pstmt_def_insert(null); drop table pc_list_part_1; -- should be ok execute pstmt_def_insert(1); drop table pc_list_parted, pc_list_part_null; deallocate pstmt_def_insert; -- Test plan_cache_mode create table test_mode (a int); insert into test_mode select 1 from generate_series(1,1000) union all select 2; create index on test_mode (a); analyze test_mode; prepare test_mode_pp (int) as select count(*) from test_mode where a = $1; -- up to 5 executions, custom plan is used explain (costs off) execute test_mode_pp(2); -- force generic plan set plan_cache_mode to force_generic_plan; explain (costs off) execute test_mode_pp(2); -- get to generic plan by 5 executions set plan_cache_mode to auto; execute test_mode_pp(1); -- 1x execute test_mode_pp(1); -- 2x execute test_mode_pp(1); -- 3x execute test_mode_pp(1); -- 4x execute test_mode_pp(1); -- 5x -- we should now get a really bad plan explain (costs off) execute test_mode_pp(2); -- but we can force a custom plan set plan_cache_mode to force_custom_plan; explain (costs off) execute test_mode_pp(2); drop table test_mode; pgFormatter-4.2/t/pg-test-files/sql/plpgsql.sql000066400000000000000000003437451361326045100216220ustar00rootroot00000000000000-- -- PLPGSQL -- -- Scenario: -- -- A building with a modern TP cable installation where any -- of the wall connectors can be used to plug in phones, -- ethernet interfaces or local office hubs. The backside -- of the wall connectors is wired to one of several patch- -- fields in the building. -- -- In the patchfields, there are hubs and all the slots -- representing the wall connectors. In addition there are -- slots that can represent a phone line from the central -- phone system. -- -- Triggers ensure consistency of the patching information. -- -- Functions are used to build up powerful views that let -- you look behind the wall when looking at a patchfield -- or into a room. -- create table Room ( roomno char(8), comment text ); create unique index Room_rno on Room using btree (roomno bpchar_ops); create table WSlot ( slotname char(20), roomno char(8), slotlink char(20), backlink char(20) ); create unique index WSlot_name on WSlot using btree (slotname bpchar_ops); create table PField ( name text, comment text ); create unique index PField_name on PField using btree (name text_ops); create table PSlot ( slotname char(20), pfname text, slotlink char(20), backlink char(20) ); create unique index PSlot_name on PSlot using btree (slotname bpchar_ops); create table PLine ( slotname char(20), phonenumber char(20), comment text, backlink char(20) ); create unique index PLine_name on PLine using btree (slotname bpchar_ops); create table Hub ( name char(14), comment text, nslots integer ); create unique index Hub_name on Hub using btree (name bpchar_ops); create table HSlot ( slotname char(20), hubname char(14), slotno integer, slotlink char(20) ); create unique index HSlot_name on HSlot using btree (slotname bpchar_ops); create index HSlot_hubname on HSlot using btree (hubname bpchar_ops); create table System ( name text, comment text ); create unique index System_name on System using btree (name text_ops); create table IFace ( slotname char(20), sysname text, ifname text, slotlink char(20) ); create unique index IFace_name on IFace using btree (slotname bpchar_ops); create table PHone ( slotname char(20), comment text, slotlink char(20) ); create unique index PHone_name on PHone using btree (slotname bpchar_ops); -- ************************************************************ -- * -- * Trigger procedures and functions for the patchfield -- * test of PL/pgSQL -- * -- ************************************************************ -- ************************************************************ -- * AFTER UPDATE on Room -- * - If room no changes let wall slots follow -- ************************************************************ create function tg_room_au() returns trigger as ' begin if new.roomno != old.roomno then update WSlot set roomno = new.roomno where roomno = old.roomno; end if; return new; end; ' language plpgsql; create trigger tg_room_au after update on Room for each row execute procedure tg_room_au(); -- ************************************************************ -- * AFTER DELETE on Room -- * - delete wall slots in this room -- ************************************************************ create function tg_room_ad() returns trigger as ' begin delete from WSlot where roomno = old.roomno; return old; end; ' language plpgsql; create trigger tg_room_ad after delete on Room for each row execute procedure tg_room_ad(); -- ************************************************************ -- * BEFORE INSERT or UPDATE on WSlot -- * - Check that room exists -- ************************************************************ create function tg_wslot_biu() returns trigger as $$ begin if count(*) = 0 from Room where roomno = new.roomno then raise exception 'Room % does not exist', new.roomno; end if; return new; end; $$ language plpgsql; create trigger tg_wslot_biu before insert or update on WSlot for each row execute procedure tg_wslot_biu(); -- ************************************************************ -- * AFTER UPDATE on PField -- * - Let PSlots of this field follow -- ************************************************************ create function tg_pfield_au() returns trigger as ' begin if new.name != old.name then update PSlot set pfname = new.name where pfname = old.name; end if; return new; end; ' language plpgsql; create trigger tg_pfield_au after update on PField for each row execute procedure tg_pfield_au(); -- ************************************************************ -- * AFTER DELETE on PField -- * - Remove all slots of this patchfield -- ************************************************************ create function tg_pfield_ad() returns trigger as ' begin delete from PSlot where pfname = old.name; return old; end; ' language plpgsql; create trigger tg_pfield_ad after delete on PField for each row execute procedure tg_pfield_ad(); -- ************************************************************ -- * BEFORE INSERT or UPDATE on PSlot -- * - Ensure that our patchfield does exist -- ************************************************************ create function tg_pslot_biu() returns trigger as $proc$ declare pfrec record; ps alias for new; begin select into pfrec * from PField where name = ps.pfname; if not found then raise exception $$Patchfield "%" does not exist$$, ps.pfname; end if; return ps; end; $proc$ language plpgsql; create trigger tg_pslot_biu before insert or update on PSlot for each row execute procedure tg_pslot_biu(); -- ************************************************************ -- * AFTER UPDATE on System -- * - If system name changes let interfaces follow -- ************************************************************ create function tg_system_au() returns trigger as ' begin if new.name != old.name then update IFace set sysname = new.name where sysname = old.name; end if; return new; end; ' language plpgsql; create trigger tg_system_au after update on System for each row execute procedure tg_system_au(); -- ************************************************************ -- * BEFORE INSERT or UPDATE on IFace -- * - set the slotname to IF.sysname.ifname -- ************************************************************ create function tg_iface_biu() returns trigger as $$ declare sname text; sysrec record; begin select into sysrec * from system where name = new.sysname; if not found then raise exception $q$system "%" does not exist$q$, new.sysname; end if; sname := 'IF.' || new.sysname; sname := sname || '.'; sname := sname || new.ifname; if length(sname) > 20 then raise exception 'IFace slotname "%" too long (20 char max)', sname; end if; new.slotname := sname; return new; end; $$ language plpgsql; create trigger tg_iface_biu before insert or update on IFace for each row execute procedure tg_iface_biu(); -- ************************************************************ -- * AFTER INSERT or UPDATE or DELETE on Hub -- * - insert/delete/rename slots as required -- ************************************************************ create function tg_hub_a() returns trigger as ' declare hname text; dummy integer; begin if tg_op = ''INSERT'' then dummy := tg_hub_adjustslots(new.name, 0, new.nslots); return new; end if; if tg_op = ''UPDATE'' then if new.name != old.name then update HSlot set hubname = new.name where hubname = old.name; end if; dummy := tg_hub_adjustslots(new.name, old.nslots, new.nslots); return new; end if; if tg_op = ''DELETE'' then dummy := tg_hub_adjustslots(old.name, old.nslots, 0); return old; end if; end; ' language plpgsql; create trigger tg_hub_a after insert or update or delete on Hub for each row execute procedure tg_hub_a(); -- ************************************************************ -- * Support function to add/remove slots of Hub -- ************************************************************ create function tg_hub_adjustslots(hname bpchar, oldnslots integer, newnslots integer) returns integer as ' begin if newnslots = oldnslots then return 0; end if; if newnslots < oldnslots then delete from HSlot where hubname = hname and slotno > newnslots; return 0; end if; for i in oldnslots + 1 .. newnslots loop insert into HSlot (slotname, hubname, slotno, slotlink) values (''HS.dummy'', hname, i, ''''); end loop; return 0; end ' language plpgsql; -- Test comments COMMENT ON FUNCTION tg_hub_adjustslots_wrong(bpchar, integer, integer) IS 'function with args'; COMMENT ON FUNCTION tg_hub_adjustslots(bpchar, integer, integer) IS 'function with args'; COMMENT ON FUNCTION tg_hub_adjustslots(bpchar, integer, integer) IS NULL; -- ************************************************************ -- * BEFORE INSERT or UPDATE on HSlot -- * - prevent from manual manipulation -- * - set the slotname to HS.hubname.slotno -- ************************************************************ create function tg_hslot_biu() returns trigger as ' declare sname text; xname HSlot.slotname%TYPE; hubrec record; begin select into hubrec * from Hub where name = new.hubname; if not found then raise exception ''no manual manipulation of HSlot''; end if; if new.slotno < 1 or new.slotno > hubrec.nslots then raise exception ''no manual manipulation of HSlot''; end if; if tg_op = ''UPDATE'' and new.hubname != old.hubname then if count(*) > 0 from Hub where name = old.hubname then raise exception ''no manual manipulation of HSlot''; end if; end if; sname := ''HS.'' || trim(new.hubname); sname := sname || ''.''; sname := sname || new.slotno::text; if length(sname) > 20 then raise exception ''HSlot slotname "%" too long (20 char max)'', sname; end if; new.slotname := sname; return new; end; ' language plpgsql; create trigger tg_hslot_biu before insert or update on HSlot for each row execute procedure tg_hslot_biu(); -- ************************************************************ -- * BEFORE DELETE on HSlot -- * - prevent from manual manipulation -- ************************************************************ create function tg_hslot_bd() returns trigger as ' declare hubrec record; begin select into hubrec * from Hub where name = old.hubname; if not found then return old; end if; if old.slotno > hubrec.nslots then return old; end if; raise exception ''no manual manipulation of HSlot''; end; ' language plpgsql; create trigger tg_hslot_bd before delete on HSlot for each row execute procedure tg_hslot_bd(); -- ************************************************************ -- * BEFORE INSERT on all slots -- * - Check name prefix -- ************************************************************ create function tg_chkslotname() returns trigger as ' begin if substr(new.slotname, 1, 2) != tg_argv[0] then raise exception ''slotname must begin with %'', tg_argv[0]; end if; return new; end; ' language plpgsql; create trigger tg_chkslotname before insert on PSlot for each row execute procedure tg_chkslotname('PS'); create trigger tg_chkslotname before insert on WSlot for each row execute procedure tg_chkslotname('WS'); create trigger tg_chkslotname before insert on PLine for each row execute procedure tg_chkslotname('PL'); create trigger tg_chkslotname before insert on IFace for each row execute procedure tg_chkslotname('IF'); create trigger tg_chkslotname before insert on PHone for each row execute procedure tg_chkslotname('PH'); -- ************************************************************ -- * BEFORE INSERT or UPDATE on all slots with slotlink -- * - Set slotlink to empty string if NULL value given -- ************************************************************ create function tg_chkslotlink() returns trigger as ' begin if new.slotlink isnull then new.slotlink := ''''; end if; return new; end; ' language plpgsql; create trigger tg_chkslotlink before insert or update on PSlot for each row execute procedure tg_chkslotlink(); create trigger tg_chkslotlink before insert or update on WSlot for each row execute procedure tg_chkslotlink(); create trigger tg_chkslotlink before insert or update on IFace for each row execute procedure tg_chkslotlink(); create trigger tg_chkslotlink before insert or update on HSlot for each row execute procedure tg_chkslotlink(); create trigger tg_chkslotlink before insert or update on PHone for each row execute procedure tg_chkslotlink(); -- ************************************************************ -- * BEFORE INSERT or UPDATE on all slots with backlink -- * - Set backlink to empty string if NULL value given -- ************************************************************ create function tg_chkbacklink() returns trigger as ' begin if new.backlink isnull then new.backlink := ''''; end if; return new; end; ' language plpgsql; create trigger tg_chkbacklink before insert or update on PSlot for each row execute procedure tg_chkbacklink(); create trigger tg_chkbacklink before insert or update on WSlot for each row execute procedure tg_chkbacklink(); create trigger tg_chkbacklink before insert or update on PLine for each row execute procedure tg_chkbacklink(); -- ************************************************************ -- * BEFORE UPDATE on PSlot -- * - do delete/insert instead of update if name changes -- ************************************************************ create function tg_pslot_bu() returns trigger as ' begin if new.slotname != old.slotname then delete from PSlot where slotname = old.slotname; insert into PSlot ( slotname, pfname, slotlink, backlink ) values ( new.slotname, new.pfname, new.slotlink, new.backlink ); return null; end if; return new; end; ' language plpgsql; create trigger tg_pslot_bu before update on PSlot for each row execute procedure tg_pslot_bu(); -- ************************************************************ -- * BEFORE UPDATE on WSlot -- * - do delete/insert instead of update if name changes -- ************************************************************ create function tg_wslot_bu() returns trigger as ' begin if new.slotname != old.slotname then delete from WSlot where slotname = old.slotname; insert into WSlot ( slotname, roomno, slotlink, backlink ) values ( new.slotname, new.roomno, new.slotlink, new.backlink ); return null; end if; return new; end; ' language plpgsql; create trigger tg_wslot_bu before update on WSlot for each row execute procedure tg_Wslot_bu(); -- ************************************************************ -- * BEFORE UPDATE on PLine -- * - do delete/insert instead of update if name changes -- ************************************************************ create function tg_pline_bu() returns trigger as ' begin if new.slotname != old.slotname then delete from PLine where slotname = old.slotname; insert into PLine ( slotname, phonenumber, comment, backlink ) values ( new.slotname, new.phonenumber, new.comment, new.backlink ); return null; end if; return new; end; ' language plpgsql; create trigger tg_pline_bu before update on PLine for each row execute procedure tg_pline_bu(); -- ************************************************************ -- * BEFORE UPDATE on IFace -- * - do delete/insert instead of update if name changes -- ************************************************************ create function tg_iface_bu() returns trigger as ' begin if new.slotname != old.slotname then delete from IFace where slotname = old.slotname; insert into IFace ( slotname, sysname, ifname, slotlink ) values ( new.slotname, new.sysname, new.ifname, new.slotlink ); return null; end if; return new; end; ' language plpgsql; create trigger tg_iface_bu before update on IFace for each row execute procedure tg_iface_bu(); -- ************************************************************ -- * BEFORE UPDATE on HSlot -- * - do delete/insert instead of update if name changes -- ************************************************************ create function tg_hslot_bu() returns trigger as ' begin if new.slotname != old.slotname or new.hubname != old.hubname then delete from HSlot where slotname = old.slotname; insert into HSlot ( slotname, hubname, slotno, slotlink ) values ( new.slotname, new.hubname, new.slotno, new.slotlink ); return null; end if; return new; end; ' language plpgsql; create trigger tg_hslot_bu before update on HSlot for each row execute procedure tg_hslot_bu(); -- ************************************************************ -- * BEFORE UPDATE on PHone -- * - do delete/insert instead of update if name changes -- ************************************************************ create function tg_phone_bu() returns trigger as ' begin if new.slotname != old.slotname then delete from PHone where slotname = old.slotname; insert into PHone ( slotname, comment, slotlink ) values ( new.slotname, new.comment, new.slotlink ); return null; end if; return new; end; ' language plpgsql; create trigger tg_phone_bu before update on PHone for each row execute procedure tg_phone_bu(); -- ************************************************************ -- * AFTER INSERT or UPDATE or DELETE on slot with backlink -- * - Ensure that the opponent correctly points back to us -- ************************************************************ create function tg_backlink_a() returns trigger as ' declare dummy integer; begin if tg_op = ''INSERT'' then if new.backlink != '''' then dummy := tg_backlink_set(new.backlink, new.slotname); end if; return new; end if; if tg_op = ''UPDATE'' then if new.backlink != old.backlink then if old.backlink != '''' then dummy := tg_backlink_unset(old.backlink, old.slotname); end if; if new.backlink != '''' then dummy := tg_backlink_set(new.backlink, new.slotname); end if; else if new.slotname != old.slotname and new.backlink != '''' then dummy := tg_slotlink_set(new.backlink, new.slotname); end if; end if; return new; end if; if tg_op = ''DELETE'' then if old.backlink != '''' then dummy := tg_backlink_unset(old.backlink, old.slotname); end if; return old; end if; end; ' language plpgsql; create trigger tg_backlink_a after insert or update or delete on PSlot for each row execute procedure tg_backlink_a('PS'); create trigger tg_backlink_a after insert or update or delete on WSlot for each row execute procedure tg_backlink_a('WS'); create trigger tg_backlink_a after insert or update or delete on PLine for each row execute procedure tg_backlink_a('PL'); -- ************************************************************ -- * Support function to set the opponents backlink field -- * if it does not already point to the requested slot -- ************************************************************ create function tg_backlink_set(myname bpchar, blname bpchar) returns integer as ' declare mytype char(2); link char(4); rec record; begin mytype := substr(myname, 1, 2); link := mytype || substr(blname, 1, 2); if link = ''PLPL'' then raise exception ''backlink between two phone lines does not make sense''; end if; if link in (''PLWS'', ''WSPL'') then raise exception ''direct link of phone line to wall slot not permitted''; end if; if mytype = ''PS'' then select into rec * from PSlot where slotname = myname; if not found then raise exception ''% does not exist'', myname; end if; if rec.backlink != blname then update PSlot set backlink = blname where slotname = myname; end if; return 0; end if; if mytype = ''WS'' then select into rec * from WSlot where slotname = myname; if not found then raise exception ''% does not exist'', myname; end if; if rec.backlink != blname then update WSlot set backlink = blname where slotname = myname; end if; return 0; end if; if mytype = ''PL'' then select into rec * from PLine where slotname = myname; if not found then raise exception ''% does not exist'', myname; end if; if rec.backlink != blname then update PLine set backlink = blname where slotname = myname; end if; return 0; end if; raise exception ''illegal backlink beginning with %'', mytype; end; ' language plpgsql; -- ************************************************************ -- * Support function to clear out the backlink field if -- * it still points to specific slot -- ************************************************************ create function tg_backlink_unset(bpchar, bpchar) returns integer as ' declare myname alias for $1; blname alias for $2; mytype char(2); rec record; begin mytype := substr(myname, 1, 2); if mytype = ''PS'' then select into rec * from PSlot where slotname = myname; if not found then return 0; end if; if rec.backlink = blname then update PSlot set backlink = '''' where slotname = myname; end if; return 0; end if; if mytype = ''WS'' then select into rec * from WSlot where slotname = myname; if not found then return 0; end if; if rec.backlink = blname then update WSlot set backlink = '''' where slotname = myname; end if; return 0; end if; if mytype = ''PL'' then select into rec * from PLine where slotname = myname; if not found then return 0; end if; if rec.backlink = blname then update PLine set backlink = '''' where slotname = myname; end if; return 0; end if; end ' language plpgsql; -- ************************************************************ -- * AFTER INSERT or UPDATE or DELETE on slot with slotlink -- * - Ensure that the opponent correctly points back to us -- ************************************************************ create function tg_slotlink_a() returns trigger as ' declare dummy integer; begin if tg_op = ''INSERT'' then if new.slotlink != '''' then dummy := tg_slotlink_set(new.slotlink, new.slotname); end if; return new; end if; if tg_op = ''UPDATE'' then if new.slotlink != old.slotlink then if old.slotlink != '''' then dummy := tg_slotlink_unset(old.slotlink, old.slotname); end if; if new.slotlink != '''' then dummy := tg_slotlink_set(new.slotlink, new.slotname); end if; else if new.slotname != old.slotname and new.slotlink != '''' then dummy := tg_slotlink_set(new.slotlink, new.slotname); end if; end if; return new; end if; if tg_op = ''DELETE'' then if old.slotlink != '''' then dummy := tg_slotlink_unset(old.slotlink, old.slotname); end if; return old; end if; end; ' language plpgsql; create trigger tg_slotlink_a after insert or update or delete on PSlot for each row execute procedure tg_slotlink_a('PS'); create trigger tg_slotlink_a after insert or update or delete on WSlot for each row execute procedure tg_slotlink_a('WS'); create trigger tg_slotlink_a after insert or update or delete on IFace for each row execute procedure tg_slotlink_a('IF'); create trigger tg_slotlink_a after insert or update or delete on HSlot for each row execute procedure tg_slotlink_a('HS'); create trigger tg_slotlink_a after insert or update or delete on PHone for each row execute procedure tg_slotlink_a('PH'); -- ************************************************************ -- * Support function to set the opponents slotlink field -- * if it does not already point to the requested slot -- ************************************************************ create function tg_slotlink_set(bpchar, bpchar) returns integer as ' declare myname alias for $1; blname alias for $2; mytype char(2); link char(4); rec record; begin mytype := substr(myname, 1, 2); link := mytype || substr(blname, 1, 2); if link = ''PHPH'' then raise exception ''slotlink between two phones does not make sense''; end if; if link in (''PHHS'', ''HSPH'') then raise exception ''link of phone to hub does not make sense''; end if; if link in (''PHIF'', ''IFPH'') then raise exception ''link of phone to hub does not make sense''; end if; if link in (''PSWS'', ''WSPS'') then raise exception ''slotlink from patchslot to wallslot not permitted''; end if; if mytype = ''PS'' then select into rec * from PSlot where slotname = myname; if not found then raise exception ''% does not exist'', myname; end if; if rec.slotlink != blname then update PSlot set slotlink = blname where slotname = myname; end if; return 0; end if; if mytype = ''WS'' then select into rec * from WSlot where slotname = myname; if not found then raise exception ''% does not exist'', myname; end if; if rec.slotlink != blname then update WSlot set slotlink = blname where slotname = myname; end if; return 0; end if; if mytype = ''IF'' then select into rec * from IFace where slotname = myname; if not found then raise exception ''% does not exist'', myname; end if; if rec.slotlink != blname then update IFace set slotlink = blname where slotname = myname; end if; return 0; end if; if mytype = ''HS'' then select into rec * from HSlot where slotname = myname; if not found then raise exception ''% does not exist'', myname; end if; if rec.slotlink != blname then update HSlot set slotlink = blname where slotname = myname; end if; return 0; end if; if mytype = ''PH'' then select into rec * from PHone where slotname = myname; if not found then raise exception ''% does not exist'', myname; end if; if rec.slotlink != blname then update PHone set slotlink = blname where slotname = myname; end if; return 0; end if; raise exception ''illegal slotlink beginning with %'', mytype; end; ' language plpgsql; -- ************************************************************ -- * Support function to clear out the slotlink field if -- * it still points to specific slot -- ************************************************************ create function tg_slotlink_unset(bpchar, bpchar) returns integer as ' declare myname alias for $1; blname alias for $2; mytype char(2); rec record; begin mytype := substr(myname, 1, 2); if mytype = ''PS'' then select into rec * from PSlot where slotname = myname; if not found then return 0; end if; if rec.slotlink = blname then update PSlot set slotlink = '''' where slotname = myname; end if; return 0; end if; if mytype = ''WS'' then select into rec * from WSlot where slotname = myname; if not found then return 0; end if; if rec.slotlink = blname then update WSlot set slotlink = '''' where slotname = myname; end if; return 0; end if; if mytype = ''IF'' then select into rec * from IFace where slotname = myname; if not found then return 0; end if; if rec.slotlink = blname then update IFace set slotlink = '''' where slotname = myname; end if; return 0; end if; if mytype = ''HS'' then select into rec * from HSlot where slotname = myname; if not found then return 0; end if; if rec.slotlink = blname then update HSlot set slotlink = '''' where slotname = myname; end if; return 0; end if; if mytype = ''PH'' then select into rec * from PHone where slotname = myname; if not found then return 0; end if; if rec.slotlink = blname then update PHone set slotlink = '''' where slotname = myname; end if; return 0; end if; end; ' language plpgsql; -- ************************************************************ -- * Describe the backside of a patchfield slot -- ************************************************************ create function pslot_backlink_view(bpchar) returns text as ' <> declare rec record; bltype char(2); retval text; begin select into rec * from PSlot where slotname = $1; if not found then return ''''; end if; if rec.backlink = '''' then return ''-''; end if; bltype := substr(rec.backlink, 1, 2); if bltype = ''PL'' then declare rec record; begin select into rec * from PLine where slotname = "outer".rec.backlink; retval := ''Phone line '' || trim(rec.phonenumber); if rec.comment != '''' then retval := retval || '' (''; retval := retval || rec.comment; retval := retval || '')''; end if; return retval; end; end if; if bltype = ''WS'' then select into rec * from WSlot where slotname = rec.backlink; retval := trim(rec.slotname) || '' in room ''; retval := retval || trim(rec.roomno); retval := retval || '' -> ''; return retval || wslot_slotlink_view(rec.slotname); end if; return rec.backlink; end; ' language plpgsql; -- ************************************************************ -- * Describe the front of a patchfield slot -- ************************************************************ create function pslot_slotlink_view(bpchar) returns text as ' declare psrec record; sltype char(2); retval text; begin select into psrec * from PSlot where slotname = $1; if not found then return ''''; end if; if psrec.slotlink = '''' then return ''-''; end if; sltype := substr(psrec.slotlink, 1, 2); if sltype = ''PS'' then retval := trim(psrec.slotlink) || '' -> ''; return retval || pslot_backlink_view(psrec.slotlink); end if; if sltype = ''HS'' then retval := comment from Hub H, HSlot HS where HS.slotname = psrec.slotlink and H.name = HS.hubname; retval := retval || '' slot ''; retval := retval || slotno::text from HSlot where slotname = psrec.slotlink; return retval; end if; return psrec.slotlink; end; ' language plpgsql; -- ************************************************************ -- * Describe the front of a wall connector slot -- ************************************************************ create function wslot_slotlink_view(bpchar) returns text as ' declare rec record; sltype char(2); retval text; begin select into rec * from WSlot where slotname = $1; if not found then return ''''; end if; if rec.slotlink = '''' then return ''-''; end if; sltype := substr(rec.slotlink, 1, 2); if sltype = ''PH'' then select into rec * from PHone where slotname = rec.slotlink; retval := ''Phone '' || trim(rec.slotname); if rec.comment != '''' then retval := retval || '' (''; retval := retval || rec.comment; retval := retval || '')''; end if; return retval; end if; if sltype = ''IF'' then declare syrow System%RowType; ifrow IFace%ROWTYPE; begin select into ifrow * from IFace where slotname = rec.slotlink; select into syrow * from System where name = ifrow.sysname; retval := syrow.name || '' IF ''; retval := retval || ifrow.ifname; if syrow.comment != '''' then retval := retval || '' (''; retval := retval || syrow.comment; retval := retval || '')''; end if; return retval; end; end if; return rec.slotlink; end; ' language plpgsql; -- ************************************************************ -- * View of a patchfield describing backside and patches -- ************************************************************ create view Pfield_v1 as select PF.pfname, PF.slotname, pslot_backlink_view(PF.slotname) as backside, pslot_slotlink_view(PF.slotname) as patch from PSlot PF; -- -- First we build the house - so we create the rooms -- insert into Room values ('001', 'Entrance'); insert into Room values ('002', 'Office'); insert into Room values ('003', 'Office'); insert into Room values ('004', 'Technical'); insert into Room values ('101', 'Office'); insert into Room values ('102', 'Conference'); insert into Room values ('103', 'Restroom'); insert into Room values ('104', 'Technical'); insert into Room values ('105', 'Office'); insert into Room values ('106', 'Office'); -- -- Second we install the wall connectors -- insert into WSlot values ('WS.001.1a', '001', '', ''); insert into WSlot values ('WS.001.1b', '001', '', ''); insert into WSlot values ('WS.001.2a', '001', '', ''); insert into WSlot values ('WS.001.2b', '001', '', ''); insert into WSlot values ('WS.001.3a', '001', '', ''); insert into WSlot values ('WS.001.3b', '001', '', ''); insert into WSlot values ('WS.002.1a', '002', '', ''); insert into WSlot values ('WS.002.1b', '002', '', ''); insert into WSlot values ('WS.002.2a', '002', '', ''); insert into WSlot values ('WS.002.2b', '002', '', ''); insert into WSlot values ('WS.002.3a', '002', '', ''); insert into WSlot values ('WS.002.3b', '002', '', ''); insert into WSlot values ('WS.003.1a', '003', '', ''); insert into WSlot values ('WS.003.1b', '003', '', ''); insert into WSlot values ('WS.003.2a', '003', '', ''); insert into WSlot values ('WS.003.2b', '003', '', ''); insert into WSlot values ('WS.003.3a', '003', '', ''); insert into WSlot values ('WS.003.3b', '003', '', ''); insert into WSlot values ('WS.101.1a', '101', '', ''); insert into WSlot values ('WS.101.1b', '101', '', ''); insert into WSlot values ('WS.101.2a', '101', '', ''); insert into WSlot values ('WS.101.2b', '101', '', ''); insert into WSlot values ('WS.101.3a', '101', '', ''); insert into WSlot values ('WS.101.3b', '101', '', ''); insert into WSlot values ('WS.102.1a', '102', '', ''); insert into WSlot values ('WS.102.1b', '102', '', ''); insert into WSlot values ('WS.102.2a', '102', '', ''); insert into WSlot values ('WS.102.2b', '102', '', ''); insert into WSlot values ('WS.102.3a', '102', '', ''); insert into WSlot values ('WS.102.3b', '102', '', ''); insert into WSlot values ('WS.105.1a', '105', '', ''); insert into WSlot values ('WS.105.1b', '105', '', ''); insert into WSlot values ('WS.105.2a', '105', '', ''); insert into WSlot values ('WS.105.2b', '105', '', ''); insert into WSlot values ('WS.105.3a', '105', '', ''); insert into WSlot values ('WS.105.3b', '105', '', ''); insert into WSlot values ('WS.106.1a', '106', '', ''); insert into WSlot values ('WS.106.1b', '106', '', ''); insert into WSlot values ('WS.106.2a', '106', '', ''); insert into WSlot values ('WS.106.2b', '106', '', ''); insert into WSlot values ('WS.106.3a', '106', '', ''); insert into WSlot values ('WS.106.3b', '106', '', ''); -- -- Now create the patch fields and their slots -- insert into PField values ('PF0_1', 'Wallslots basement'); -- -- The cables for these will be made later, so they are unconnected for now -- insert into PSlot values ('PS.base.a1', 'PF0_1', '', ''); insert into PSlot values ('PS.base.a2', 'PF0_1', '', ''); insert into PSlot values ('PS.base.a3', 'PF0_1', '', ''); insert into PSlot values ('PS.base.a4', 'PF0_1', '', ''); insert into PSlot values ('PS.base.a5', 'PF0_1', '', ''); insert into PSlot values ('PS.base.a6', 'PF0_1', '', ''); -- -- These are already wired to the wall connectors -- insert into PSlot values ('PS.base.b1', 'PF0_1', '', 'WS.002.1a'); insert into PSlot values ('PS.base.b2', 'PF0_1', '', 'WS.002.1b'); insert into PSlot values ('PS.base.b3', 'PF0_1', '', 'WS.002.2a'); insert into PSlot values ('PS.base.b4', 'PF0_1', '', 'WS.002.2b'); insert into PSlot values ('PS.base.b5', 'PF0_1', '', 'WS.002.3a'); insert into PSlot values ('PS.base.b6', 'PF0_1', '', 'WS.002.3b'); insert into PSlot values ('PS.base.c1', 'PF0_1', '', 'WS.003.1a'); insert into PSlot values ('PS.base.c2', 'PF0_1', '', 'WS.003.1b'); insert into PSlot values ('PS.base.c3', 'PF0_1', '', 'WS.003.2a'); insert into PSlot values ('PS.base.c4', 'PF0_1', '', 'WS.003.2b'); insert into PSlot values ('PS.base.c5', 'PF0_1', '', 'WS.003.3a'); insert into PSlot values ('PS.base.c6', 'PF0_1', '', 'WS.003.3b'); -- -- This patchfield will be renamed later into PF0_2 - so its -- slots references in pfname should follow -- insert into PField values ('PF0_X', 'Phonelines basement'); insert into PSlot values ('PS.base.ta1', 'PF0_X', '', ''); insert into PSlot values ('PS.base.ta2', 'PF0_X', '', ''); insert into PSlot values ('PS.base.ta3', 'PF0_X', '', ''); insert into PSlot values ('PS.base.ta4', 'PF0_X', '', ''); insert into PSlot values ('PS.base.ta5', 'PF0_X', '', ''); insert into PSlot values ('PS.base.ta6', 'PF0_X', '', ''); insert into PSlot values ('PS.base.tb1', 'PF0_X', '', ''); insert into PSlot values ('PS.base.tb2', 'PF0_X', '', ''); insert into PSlot values ('PS.base.tb3', 'PF0_X', '', ''); insert into PSlot values ('PS.base.tb4', 'PF0_X', '', ''); insert into PSlot values ('PS.base.tb5', 'PF0_X', '', ''); insert into PSlot values ('PS.base.tb6', 'PF0_X', '', ''); insert into PField values ('PF1_1', 'Wallslots first floor'); insert into PSlot values ('PS.first.a1', 'PF1_1', '', 'WS.101.1a'); insert into PSlot values ('PS.first.a2', 'PF1_1', '', 'WS.101.1b'); insert into PSlot values ('PS.first.a3', 'PF1_1', '', 'WS.101.2a'); insert into PSlot values ('PS.first.a4', 'PF1_1', '', 'WS.101.2b'); insert into PSlot values ('PS.first.a5', 'PF1_1', '', 'WS.101.3a'); insert into PSlot values ('PS.first.a6', 'PF1_1', '', 'WS.101.3b'); insert into PSlot values ('PS.first.b1', 'PF1_1', '', 'WS.102.1a'); insert into PSlot values ('PS.first.b2', 'PF1_1', '', 'WS.102.1b'); insert into PSlot values ('PS.first.b3', 'PF1_1', '', 'WS.102.2a'); insert into PSlot values ('PS.first.b4', 'PF1_1', '', 'WS.102.2b'); insert into PSlot values ('PS.first.b5', 'PF1_1', '', 'WS.102.3a'); insert into PSlot values ('PS.first.b6', 'PF1_1', '', 'WS.102.3b'); insert into PSlot values ('PS.first.c1', 'PF1_1', '', 'WS.105.1a'); insert into PSlot values ('PS.first.c2', 'PF1_1', '', 'WS.105.1b'); insert into PSlot values ('PS.first.c3', 'PF1_1', '', 'WS.105.2a'); insert into PSlot values ('PS.first.c4', 'PF1_1', '', 'WS.105.2b'); insert into PSlot values ('PS.first.c5', 'PF1_1', '', 'WS.105.3a'); insert into PSlot values ('PS.first.c6', 'PF1_1', '', 'WS.105.3b'); insert into PSlot values ('PS.first.d1', 'PF1_1', '', 'WS.106.1a'); insert into PSlot values ('PS.first.d2', 'PF1_1', '', 'WS.106.1b'); insert into PSlot values ('PS.first.d3', 'PF1_1', '', 'WS.106.2a'); insert into PSlot values ('PS.first.d4', 'PF1_1', '', 'WS.106.2b'); insert into PSlot values ('PS.first.d5', 'PF1_1', '', 'WS.106.3a'); insert into PSlot values ('PS.first.d6', 'PF1_1', '', 'WS.106.3b'); -- -- Now we wire the wall connectors 1a-2a in room 001 to the -- patchfield. In the second update we make an error, and -- correct it after -- update PSlot set backlink = 'WS.001.1a' where slotname = 'PS.base.a1'; update PSlot set backlink = 'WS.001.1b' where slotname = 'PS.base.a3'; select * from WSlot where roomno = '001' order by slotname; select * from PSlot where slotname ~ 'PS.base.a' order by slotname; update PSlot set backlink = 'WS.001.2a' where slotname = 'PS.base.a3'; select * from WSlot where roomno = '001' order by slotname; select * from PSlot where slotname ~ 'PS.base.a' order by slotname; update PSlot set backlink = 'WS.001.1b' where slotname = 'PS.base.a2'; select * from WSlot where roomno = '001' order by slotname; select * from PSlot where slotname ~ 'PS.base.a' order by slotname; -- -- Same procedure for 2b-3b but this time updating the WSlot instead -- of the PSlot. Due to the triggers the result is the same: -- WSlot and corresponding PSlot point to each other. -- update WSlot set backlink = 'PS.base.a4' where slotname = 'WS.001.2b'; update WSlot set backlink = 'PS.base.a6' where slotname = 'WS.001.3a'; select * from WSlot where roomno = '001' order by slotname; select * from PSlot where slotname ~ 'PS.base.a' order by slotname; update WSlot set backlink = 'PS.base.a6' where slotname = 'WS.001.3b'; select * from WSlot where roomno = '001' order by slotname; select * from PSlot where slotname ~ 'PS.base.a' order by slotname; update WSlot set backlink = 'PS.base.a5' where slotname = 'WS.001.3a'; select * from WSlot where roomno = '001' order by slotname; select * from PSlot where slotname ~ 'PS.base.a' order by slotname; insert into PField values ('PF1_2', 'Phonelines first floor'); insert into PSlot values ('PS.first.ta1', 'PF1_2', '', ''); insert into PSlot values ('PS.first.ta2', 'PF1_2', '', ''); insert into PSlot values ('PS.first.ta3', 'PF1_2', '', ''); insert into PSlot values ('PS.first.ta4', 'PF1_2', '', ''); insert into PSlot values ('PS.first.ta5', 'PF1_2', '', ''); insert into PSlot values ('PS.first.ta6', 'PF1_2', '', ''); insert into PSlot values ('PS.first.tb1', 'PF1_2', '', ''); insert into PSlot values ('PS.first.tb2', 'PF1_2', '', ''); insert into PSlot values ('PS.first.tb3', 'PF1_2', '', ''); insert into PSlot values ('PS.first.tb4', 'PF1_2', '', ''); insert into PSlot values ('PS.first.tb5', 'PF1_2', '', ''); insert into PSlot values ('PS.first.tb6', 'PF1_2', '', ''); -- -- Fix the wrong name for patchfield PF0_2 -- update PField set name = 'PF0_2' where name = 'PF0_X'; select * from PSlot order by slotname; select * from WSlot order by slotname; -- -- Install the central phone system and create the phone numbers. -- They are wired on insert to the patchfields. Again the -- triggers automatically tell the PSlots to update their -- backlink field. -- insert into PLine values ('PL.001', '-0', 'Central call', 'PS.base.ta1'); insert into PLine values ('PL.002', '-101', '', 'PS.base.ta2'); insert into PLine values ('PL.003', '-102', '', 'PS.base.ta3'); insert into PLine values ('PL.004', '-103', '', 'PS.base.ta5'); insert into PLine values ('PL.005', '-104', '', 'PS.base.ta6'); insert into PLine values ('PL.006', '-106', '', 'PS.base.tb2'); insert into PLine values ('PL.007', '-108', '', 'PS.base.tb3'); insert into PLine values ('PL.008', '-109', '', 'PS.base.tb4'); insert into PLine values ('PL.009', '-121', '', 'PS.base.tb5'); insert into PLine values ('PL.010', '-122', '', 'PS.base.tb6'); insert into PLine values ('PL.015', '-134', '', 'PS.first.ta1'); insert into PLine values ('PL.016', '-137', '', 'PS.first.ta3'); insert into PLine values ('PL.017', '-139', '', 'PS.first.ta4'); insert into PLine values ('PL.018', '-362', '', 'PS.first.tb1'); insert into PLine values ('PL.019', '-363', '', 'PS.first.tb2'); insert into PLine values ('PL.020', '-364', '', 'PS.first.tb3'); insert into PLine values ('PL.021', '-365', '', 'PS.first.tb5'); insert into PLine values ('PL.022', '-367', '', 'PS.first.tb6'); insert into PLine values ('PL.028', '-501', 'Fax entrance', 'PS.base.ta2'); insert into PLine values ('PL.029', '-502', 'Fax first floor', 'PS.first.ta1'); -- -- Buy some phones, plug them into the wall and patch the -- phone lines to the corresponding patchfield slots. -- insert into PHone values ('PH.hc001', 'Hicom standard', 'WS.001.1a'); update PSlot set slotlink = 'PS.base.ta1' where slotname = 'PS.base.a1'; insert into PHone values ('PH.hc002', 'Hicom standard', 'WS.002.1a'); update PSlot set slotlink = 'PS.base.ta5' where slotname = 'PS.base.b1'; insert into PHone values ('PH.hc003', 'Hicom standard', 'WS.002.2a'); update PSlot set slotlink = 'PS.base.tb2' where slotname = 'PS.base.b3'; insert into PHone values ('PH.fax001', 'Canon fax', 'WS.001.2a'); update PSlot set slotlink = 'PS.base.ta2' where slotname = 'PS.base.a3'; -- -- Install a hub at one of the patchfields, plug a computers -- ethernet interface into the wall and patch it to the hub. -- insert into Hub values ('base.hub1', 'Patchfield PF0_1 hub', 16); insert into System values ('orion', 'PC'); insert into IFace values ('IF', 'orion', 'eth0', 'WS.002.1b'); update PSlot set slotlink = 'HS.base.hub1.1' where slotname = 'PS.base.b2'; -- -- Now we take a look at the patchfield -- select * from PField_v1 where pfname = 'PF0_1' order by slotname; select * from PField_v1 where pfname = 'PF0_2' order by slotname; -- -- Finally we want errors -- insert into PField values ('PF1_1', 'should fail due to unique index'); update PSlot set backlink = 'WS.not.there' where slotname = 'PS.base.a1'; update PSlot set backlink = 'XX.illegal' where slotname = 'PS.base.a1'; update PSlot set slotlink = 'PS.not.there' where slotname = 'PS.base.a1'; update PSlot set slotlink = 'XX.illegal' where slotname = 'PS.base.a1'; insert into HSlot values ('HS', 'base.hub1', 1, ''); insert into HSlot values ('HS', 'base.hub1', 20, ''); delete from HSlot; insert into IFace values ('IF', 'notthere', 'eth0', ''); insert into IFace values ('IF', 'orion', 'ethernet_interface_name_too_long', ''); -- -- The following tests are unrelated to the scenario outlined above; -- they merely exercise specific parts of PL/pgSQL -- -- -- Test recursion, per bug report 7-Sep-01 -- CREATE FUNCTION recursion_test(int,int) RETURNS text AS ' DECLARE rslt text; BEGIN IF $1 <= 0 THEN rslt = CAST($2 AS TEXT); ELSE rslt = CAST($1 AS TEXT) || '','' || recursion_test($1 - 1, $2); END IF; RETURN rslt; END;' LANGUAGE plpgsql; SELECT recursion_test(4,3); -- -- Test the FOUND magic variable -- CREATE TABLE found_test_tbl (a int); create function test_found() returns boolean as ' declare begin insert into found_test_tbl values (1); if FOUND then insert into found_test_tbl values (2); end if; update found_test_tbl set a = 100 where a = 1; if FOUND then insert into found_test_tbl values (3); end if; delete from found_test_tbl where a = 9999; -- matches no rows if not FOUND then insert into found_test_tbl values (4); end if; for i in 1 .. 10 loop -- no need to do anything end loop; if FOUND then insert into found_test_tbl values (5); end if; -- never executes the loop for i in 2 .. 1 loop -- no need to do anything end loop; if not FOUND then insert into found_test_tbl values (6); end if; return true; end;' language plpgsql; select test_found(); select * from found_test_tbl; -- -- Test set-returning functions for PL/pgSQL -- create function test_table_func_rec() returns setof found_test_tbl as ' DECLARE rec RECORD; BEGIN FOR rec IN select * from found_test_tbl LOOP RETURN NEXT rec; END LOOP; RETURN; END;' language plpgsql; select * from test_table_func_rec(); create function test_table_func_row() returns setof found_test_tbl as ' DECLARE row found_test_tbl%ROWTYPE; BEGIN FOR row IN select * from found_test_tbl LOOP RETURN NEXT row; END LOOP; RETURN; END;' language plpgsql; select * from test_table_func_row(); create function test_ret_set_scalar(int,int) returns setof int as ' DECLARE i int; BEGIN FOR i IN $1 .. $2 LOOP RETURN NEXT i + 1; END LOOP; RETURN; END;' language plpgsql; select * from test_ret_set_scalar(1,10); create function test_ret_set_rec_dyn(int) returns setof record as ' DECLARE retval RECORD; BEGIN IF $1 > 10 THEN SELECT INTO retval 5, 10, 15; RETURN NEXT retval; RETURN NEXT retval; ELSE SELECT INTO retval 50, 5::numeric, ''xxx''::text; RETURN NEXT retval; RETURN NEXT retval; END IF; RETURN; END;' language plpgsql; SELECT * FROM test_ret_set_rec_dyn(1500) AS (a int, b int, c int); SELECT * FROM test_ret_set_rec_dyn(5) AS (a int, b numeric, c text); create function test_ret_rec_dyn(int) returns record as ' DECLARE retval RECORD; BEGIN IF $1 > 10 THEN SELECT INTO retval 5, 10, 15; RETURN retval; ELSE SELECT INTO retval 50, 5::numeric, ''xxx''::text; RETURN retval; END IF; END;' language plpgsql; SELECT * FROM test_ret_rec_dyn(1500) AS (a int, b int, c int); SELECT * FROM test_ret_rec_dyn(5) AS (a int, b numeric, c text); -- -- Test handling of OUT parameters, including polymorphic cases. -- Note that RETURN is optional with OUT params; we try both ways. -- -- wrong way to do it: create function f1(in i int, out j int) returns int as $$ begin return i+1; end$$ language plpgsql; create function f1(in i int, out j int) as $$ begin j := i+1; return; end$$ language plpgsql; select f1(42); select * from f1(42); create or replace function f1(inout i int) as $$ begin i := i+1; end$$ language plpgsql; select f1(42); select * from f1(42); drop function f1(int); create function f1(in i int, out j int) returns setof int as $$ begin j := i+1; return next; j := i+2; return next; return; end$$ language plpgsql; select * from f1(42); drop function f1(int); create function f1(in i int, out j int, out k text) as $$ begin j := i; j := j+1; k := 'foo'; end$$ language plpgsql; select f1(42); select * from f1(42); drop function f1(int); create function f1(in i int, out j int, out k text) returns setof record as $$ begin j := i+1; k := 'foo'; return next; j := j+1; k := 'foot'; return next; end$$ language plpgsql; select * from f1(42); drop function f1(int); create function duplic(in i anyelement, out j anyelement, out k anyarray) as $$ begin j := i; k := array[j,j]; return; end$$ language plpgsql; select * from duplic(42); select * from duplic('foo'::text); drop function duplic(anyelement); -- -- test PERFORM -- create table perform_test ( a INT, b INT ); create function perform_simple_func(int) returns boolean as ' BEGIN IF $1 < 20 THEN INSERT INTO perform_test VALUES ($1, $1 + 10); RETURN TRUE; ELSE RETURN FALSE; END IF; END;' language plpgsql; create function perform_test_func() returns void as ' BEGIN IF FOUND then INSERT INTO perform_test VALUES (100, 100); END IF; PERFORM perform_simple_func(5); IF FOUND then INSERT INTO perform_test VALUES (100, 100); END IF; PERFORM perform_simple_func(50); IF FOUND then INSERT INTO perform_test VALUES (100, 100); END IF; RETURN; END;' language plpgsql; SELECT perform_test_func(); SELECT * FROM perform_test; drop table perform_test; -- -- Test proper snapshot handling in simple expressions -- create temp table users(login text, id serial); create function sp_id_user(a_login text) returns int as $$ declare x int; begin select into x id from users where login = a_login; if found then return x; end if; return 0; end$$ language plpgsql stable; insert into users values('user1'); select sp_id_user('user1'); select sp_id_user('userx'); create function sp_add_user(a_login text) returns int as $$ declare my_id_user int; begin my_id_user = sp_id_user( a_login ); IF my_id_user > 0 THEN RETURN -1; -- error code for existing user END IF; INSERT INTO users ( login ) VALUES ( a_login ); my_id_user = sp_id_user( a_login ); IF my_id_user = 0 THEN RETURN -2; -- error code for insertion failure END IF; RETURN my_id_user; end$$ language plpgsql; select sp_add_user('user1'); select sp_add_user('user2'); select sp_add_user('user2'); select sp_add_user('user3'); select sp_add_user('user3'); drop function sp_add_user(text); drop function sp_id_user(text); -- -- tests for refcursors -- create table rc_test (a int, b int); create function return_unnamed_refcursor() returns refcursor as $$ declare rc refcursor; begin open rc for select a from rc_test; return rc; end $$ language plpgsql; create function use_refcursor(rc refcursor) returns int as $$ declare rc refcursor; x record; begin rc := return_unnamed_refcursor(); fetch next from rc into x; return x.a; end $$ language plpgsql; select use_refcursor(return_unnamed_refcursor()); create function return_refcursor(rc refcursor) returns refcursor as $$ begin open rc for select a from rc_test; return rc; end $$ language plpgsql; create function refcursor_test1(refcursor) returns refcursor as $$ begin perform return_refcursor($1); return $1; end $$ language plpgsql; begin; select refcursor_test1('test1'); fetch next in test1; select refcursor_test1('test2'); fetch all from test2; commit; -- should fail fetch next from test1; create function refcursor_test2(int, int) returns boolean as $$ declare c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2; nonsense record; begin open c1($1, $2); fetch c1 into nonsense; close c1; if found then return true; else return false; end if; end $$ language plpgsql; select refcursor_test2(20000, 20000) as "Should be false", refcursor_test2(20, 20) as "Should be true"; -- -- tests for cursors with named parameter arguments -- create function namedparmcursor_test1(int, int) returns boolean as $$ declare c1 cursor (param1 int, param12 int) for select * from rc_test where a > param1 and b > param12; nonsense record; begin open c1(param12 := $2, param1 := $1); fetch c1 into nonsense; close c1; if found then return true; else return false; end if; end $$ language plpgsql; select namedparmcursor_test1(20000, 20000) as "Should be false", namedparmcursor_test1(20, 20) as "Should be true"; -- mixing named and positional argument notations create function namedparmcursor_test2(int, int) returns boolean as $$ declare c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2; nonsense record; begin open c1(param1 := $1, $2); fetch c1 into nonsense; close c1; if found then return true; else return false; end if; end $$ language plpgsql; select namedparmcursor_test2(20, 20); -- mixing named and positional: param2 is given twice, once in named notation -- and second time in positional notation. Should throw an error at parse time create function namedparmcursor_test3() returns void as $$ declare c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2; begin open c1(param2 := 20, 21); end $$ language plpgsql; -- mixing named and positional: same as previous test, but param1 is duplicated create function namedparmcursor_test4() returns void as $$ declare c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2; begin open c1(20, param1 := 21); end $$ language plpgsql; -- duplicate named parameter, should throw an error at parse time create function namedparmcursor_test5() returns void as $$ declare c1 cursor (p1 int, p2 int) for select * from tenk1 where thousand = p1 and tenthous = p2; begin open c1 (p2 := 77, p2 := 42); end $$ language plpgsql; -- not enough parameters, should throw an error at parse time create function namedparmcursor_test6() returns void as $$ declare c1 cursor (p1 int, p2 int) for select * from tenk1 where thousand = p1 and tenthous = p2; begin open c1 (p2 := 77); end $$ language plpgsql; -- division by zero runtime error, the context given in the error message -- should be sensible create function namedparmcursor_test7() returns void as $$ declare c1 cursor (p1 int, p2 int) for select * from tenk1 where thousand = p1 and tenthous = p2; begin open c1 (p2 := 77, p1 := 42/0); end $$ language plpgsql; select namedparmcursor_test7(); -- check that line comments work correctly within the argument list (there -- is some special handling of this case in the code: the newline after the -- comment must be preserved when the argument-evaluating query is -- constructed, otherwise the comment effectively comments out the next -- argument, too) create function namedparmcursor_test8() returns int4 as $$ declare c1 cursor (p1 int, p2 int) for select count(*) from tenk1 where thousand = p1 and tenthous = p2; n int4; begin open c1 (77 -- test , 42); fetch c1 into n; return n; end $$ language plpgsql; select namedparmcursor_test8(); -- cursor parameter name can match plpgsql variable or unreserved keyword create function namedparmcursor_test9(p1 int) returns int4 as $$ declare c1 cursor (p1 int, p2 int, debug int) for select count(*) from tenk1 where thousand = p1 and tenthous = p2 and four = debug; p2 int4 := 1006; n int4; begin open c1 (p1 := p1, p2 := p2, debug := 2); fetch c1 into n; return n; end $$ language plpgsql; select namedparmcursor_test9(6); -- -- tests for "raise" processing -- create function raise_test1(int) returns int as $$ begin raise notice 'This message has too many parameters!', $1; return $1; end; $$ language plpgsql; create function raise_test2(int) returns int as $$ begin raise notice 'This message has too few parameters: %, %, %', $1, $1; return $1; end; $$ language plpgsql; create function raise_test3(int) returns int as $$ begin raise notice 'This message has no parameters (despite having %% signs in it)!'; return $1; end; $$ language plpgsql; select raise_test3(1); -- Test re-RAISE inside a nested exception block. This case is allowed -- by Oracle's PL/SQL but was handled differently by PG before 9.1. CREATE FUNCTION reraise_test() RETURNS void AS $$ BEGIN BEGIN RAISE syntax_error; EXCEPTION WHEN syntax_error THEN BEGIN raise notice 'exception % thrown in inner block, reraising', sqlerrm; RAISE; EXCEPTION WHEN OTHERS THEN raise notice 'RIGHT - exception % caught in inner block', sqlerrm; END; END; EXCEPTION WHEN OTHERS THEN raise notice 'WRONG - exception % caught in outer block', sqlerrm; END; $$ LANGUAGE plpgsql; SELECT reraise_test(); -- -- reject function definitions that contain malformed SQL queries at -- compile-time, where possible -- create function bad_sql1() returns int as $$ declare a int; begin a := 5; Johnny Yuma; a := 10; return a; end$$ language plpgsql; create function bad_sql2() returns int as $$ declare r record; begin for r in select I fought the law, the law won LOOP raise notice 'in loop'; end loop; return 5; end;$$ language plpgsql; -- a RETURN expression is mandatory, except for void-returning -- functions, where it is not allowed create function missing_return_expr() returns int as $$ begin return ; end;$$ language plpgsql; create function void_return_expr() returns void as $$ begin return 5; end;$$ language plpgsql; -- VOID functions are allowed to omit RETURN create function void_return_expr() returns void as $$ begin perform 2+2; end;$$ language plpgsql; select void_return_expr(); -- but ordinary functions are not create function missing_return_expr() returns int as $$ begin perform 2+2; end;$$ language plpgsql; select missing_return_expr(); drop function void_return_expr(); drop function missing_return_expr(); -- -- EXECUTE ... INTO test -- create table eifoo (i integer, y integer); create type eitype as (i integer, y integer); create or replace function execute_into_test(varchar) returns record as $$ declare _r record; _rt eifoo%rowtype; _v eitype; i int; j int; k int; begin execute 'insert into '||$1||' values(10,15)'; execute 'select (row).* from (select row(10,1)::eifoo) s' into _r; raise notice '% %', _r.i, _r.y; execute 'select * from '||$1||' limit 1' into _rt; raise notice '% %', _rt.i, _rt.y; execute 'select *, 20 from '||$1||' limit 1' into i, j, k; raise notice '% % %', i, j, k; execute 'select 1,2' into _v; return _v; end; $$ language plpgsql; select execute_into_test('eifoo'); drop table eifoo cascade; drop type eitype cascade; -- -- SQLSTATE and SQLERRM test -- create function excpt_test1() returns void as $$ begin raise notice '% %', sqlstate, sqlerrm; end; $$ language plpgsql; -- should fail: SQLSTATE and SQLERRM are only in defined EXCEPTION -- blocks select excpt_test1(); create function excpt_test2() returns void as $$ begin begin begin raise notice '% %', sqlstate, sqlerrm; end; end; end; $$ language plpgsql; -- should fail select excpt_test2(); create function excpt_test3() returns void as $$ begin begin raise exception 'user exception'; exception when others then raise notice 'caught exception % %', sqlstate, sqlerrm; begin raise notice '% %', sqlstate, sqlerrm; perform 10/0; exception when substring_error then -- this exception handler shouldn't be invoked raise notice 'unexpected exception: % %', sqlstate, sqlerrm; when division_by_zero then raise notice 'caught exception % %', sqlstate, sqlerrm; end; raise notice '% %', sqlstate, sqlerrm; end; end; $$ language plpgsql; select excpt_test3(); create function excpt_test4() returns text as $$ begin begin perform 1/0; exception when others then return sqlerrm; end; end; $$ language plpgsql; select excpt_test4(); drop function excpt_test1(); drop function excpt_test2(); drop function excpt_test3(); drop function excpt_test4(); -- parameters of raise stmt can be expressions create function raise_exprs() returns void as $$ declare a integer[] = '{10,20,30}'; c varchar = 'xyz'; i integer; begin i := 2; raise notice '%; %; %; %; %; %', a, a[i], c, (select c || 'abc'), row(10,'aaa',NULL,30), NULL; end;$$ language plpgsql; select raise_exprs(); drop function raise_exprs(); -- regression test: verify that multiple uses of same plpgsql datum within -- a SQL command all get mapped to the same $n parameter. The return value -- of the SELECT is not important, we only care that it doesn't fail with -- a complaint about an ungrouped column reference. create function multi_datum_use(p1 int) returns bool as $$ declare x int; y int; begin select into x,y unique1/p1, unique1/$1 from tenk1 group by unique1/p1; return x = y; end$$ language plpgsql; select multi_datum_use(42); -- -- Test STRICT limiter in both planned and EXECUTE invocations. -- Note that a data-modifying query is quasi strict (disallow multi rows) -- by default in the planned case, but not in EXECUTE. -- create temp table foo (f1 int, f2 int); insert into foo values (1,2), (3,4); create or replace function stricttest() returns void as $$ declare x record; begin -- should work insert into foo values(5,6) returning * into x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; select stricttest(); create or replace function stricttest() returns void as $$ declare x record; begin -- should fail due to implicit strict insert into foo values(7,8),(9,10) returning * into x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; select stricttest(); create or replace function stricttest() returns void as $$ declare x record; begin -- should work execute 'insert into foo values(5,6) returning *' into x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; select stricttest(); create or replace function stricttest() returns void as $$ declare x record; begin -- this should work since EXECUTE isn't as picky execute 'insert into foo values(7,8),(9,10) returning *' into x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; select stricttest(); select * from foo; create or replace function stricttest() returns void as $$ declare x record; begin -- should work select * from foo where f1 = 3 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; select stricttest(); create or replace function stricttest() returns void as $$ declare x record; begin -- should fail, no rows select * from foo where f1 = 0 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; select stricttest(); create or replace function stricttest() returns void as $$ declare x record; begin -- should fail, too many rows select * from foo where f1 > 3 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; select stricttest(); create or replace function stricttest() returns void as $$ declare x record; begin -- should work execute 'select * from foo where f1 = 3' into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; select stricttest(); create or replace function stricttest() returns void as $$ declare x record; begin -- should fail, no rows execute 'select * from foo where f1 = 0' into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; select stricttest(); create or replace function stricttest() returns void as $$ declare x record; begin -- should fail, too many rows execute 'select * from foo where f1 > 3' into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; select stricttest(); drop function stricttest(); -- test printing parameters after failure due to STRICT set plpgsql.print_strict_params to true; create or replace function stricttest() returns void as $$ declare x record; p1 int := 2; p3 text := 'foo'; begin -- no rows select * from foo where f1 = p1 and f1::text = p3 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; select stricttest(); create or replace function stricttest() returns void as $$ declare x record; p1 int := 2; p3 text := 'foo'; begin -- too many rows select * from foo where f1 > p1 or f1::text = p3 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; select stricttest(); create or replace function stricttest() returns void as $$ declare x record; begin -- too many rows, no params select * from foo where f1 > 3 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; select stricttest(); create or replace function stricttest() returns void as $$ declare x record; begin -- no rows execute 'select * from foo where f1 = $1 or f1::text = $2' using 0, 'foo' into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; select stricttest(); create or replace function stricttest() returns void as $$ declare x record; begin -- too many rows execute 'select * from foo where f1 > $1' using 1 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; select stricttest(); create or replace function stricttest() returns void as $$ declare x record; begin -- too many rows, no parameters execute 'select * from foo where f1 > 3' into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; select stricttest(); create or replace function stricttest() returns void as $$ -- override the global #print_strict_params off declare x record; p1 int := 2; p3 text := 'foo'; begin -- too many rows select * from foo where f1 > p1 or f1::text = p3 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; select stricttest(); reset plpgsql.print_strict_params; create or replace function stricttest() returns void as $$ -- override the global #print_strict_params on declare x record; p1 int := 2; p3 text := 'foo'; begin -- too many rows select * from foo where f1 > p1 or f1::text = p3 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; select stricttest(); -- test warnings and errors set plpgsql.extra_warnings to 'all'; set plpgsql.extra_warnings to 'none'; set plpgsql.extra_errors to 'all'; set plpgsql.extra_errors to 'none'; -- test warnings when shadowing a variable set plpgsql.extra_warnings to 'shadowed_variables'; -- simple shadowing of input and output parameters create or replace function shadowtest(in1 int) returns table (out1 int) as $$ declare in1 int; out1 int; begin end $$ language plpgsql; select shadowtest(1); set plpgsql.extra_warnings to 'shadowed_variables'; select shadowtest(1); create or replace function shadowtest(in1 int) returns table (out1 int) as $$ declare in1 int; out1 int; begin end $$ language plpgsql; select shadowtest(1); drop function shadowtest(int); -- shadowing in a second DECLARE block create or replace function shadowtest() returns void as $$ declare f1 int; begin declare f1 int; begin end; end$$ language plpgsql; drop function shadowtest(); -- several levels of shadowing create or replace function shadowtest(in1 int) returns void as $$ declare in1 int; begin declare in1 int; begin end; end$$ language plpgsql; drop function shadowtest(int); -- shadowing in cursor definitions create or replace function shadowtest() returns void as $$ declare f1 int; c1 cursor (f1 int) for select 1; begin end$$ language plpgsql; drop function shadowtest(); -- test errors when shadowing a variable set plpgsql.extra_errors to 'shadowed_variables'; create or replace function shadowtest(f1 int) returns boolean as $$ declare f1 int; begin return 1; end $$ language plpgsql; select shadowtest(1); reset plpgsql.extra_errors; reset plpgsql.extra_warnings; create or replace function shadowtest(f1 int) returns boolean as $$ declare f1 int; begin return 1; end $$ language plpgsql; select shadowtest(1); -- runtime extra checks set plpgsql.extra_warnings to 'too_many_rows'; do $$ declare x int; begin select v from generate_series(1,2) g(v) into x; end; $$; set plpgsql.extra_errors to 'too_many_rows'; do $$ declare x int; begin select v from generate_series(1,2) g(v) into x; end; $$; reset plpgsql.extra_errors; reset plpgsql.extra_warnings; set plpgsql.extra_warnings to 'strict_multi_assignment'; do $$ declare x int; y int; begin select 1 into x, y; select 1,2 into x, y; select 1,2,3 into x, y; end $$; set plpgsql.extra_errors to 'strict_multi_assignment'; do $$ declare x int; y int; begin select 1 into x, y; select 1,2 into x, y; select 1,2,3 into x, y; end $$; create table test_01(a int, b int, c int); alter table test_01 drop column a; -- the check is active only when source table is not empty insert into test_01 values(10,20); do $$ declare x int; y int; begin select * from test_01 into x, y; -- should be ok raise notice 'ok'; select * from test_01 into x; -- should to fail end; $$; do $$ declare t test_01; begin select 1, 2 into t; -- should be ok raise notice 'ok'; select 1, 2, 3 into t; -- should fail; end; $$; do $$ declare t test_01; begin select 1 into t; -- should fail; end; $$; drop table test_01; reset plpgsql.extra_errors; reset plpgsql.extra_warnings; -- test scrollable cursor support create function sc_test() returns setof integer as $$ declare c scroll cursor for select f1 from int4_tbl; x integer; begin open c; fetch last from c into x; while found loop return next x; fetch prior from c into x; end loop; close c; end; $$ language plpgsql; select * from sc_test(); create or replace function sc_test() returns setof integer as $$ declare c no scroll cursor for select f1 from int4_tbl; x integer; begin open c; fetch last from c into x; while found loop return next x; fetch prior from c into x; end loop; close c; end; $$ language plpgsql; select * from sc_test(); -- fails because of NO SCROLL specification create or replace function sc_test() returns setof integer as $$ declare c refcursor; x integer; begin open c scroll for select f1 from int4_tbl; fetch last from c into x; while found loop return next x; fetch prior from c into x; end loop; close c; end; $$ language plpgsql; select * from sc_test(); create or replace function sc_test() returns setof integer as $$ declare c refcursor; x integer; begin open c scroll for execute 'select f1 from int4_tbl'; fetch last from c into x; while found loop return next x; fetch relative -2 from c into x; end loop; close c; end; $$ language plpgsql; select * from sc_test(); create or replace function sc_test() returns setof integer as $$ declare c refcursor; x integer; begin open c scroll for execute 'select f1 from int4_tbl'; fetch last from c into x; while found loop return next x; move backward 2 from c; fetch relative -1 from c into x; end loop; close c; end; $$ language plpgsql; select * from sc_test(); create or replace function sc_test() returns setof integer as $$ declare c cursor for select * from generate_series(1, 10); x integer; begin open c; loop move relative 2 in c; if not found then exit; end if; fetch next from c into x; if found then return next x; end if; end loop; close c; end; $$ language plpgsql; select * from sc_test(); create or replace function sc_test() returns setof integer as $$ declare c cursor for select * from generate_series(1, 10); x integer; begin open c; move forward all in c; fetch backward from c into x; if found then return next x; end if; close c; end; $$ language plpgsql; select * from sc_test(); drop function sc_test(); -- test qualified variable names create function pl_qual_names (param1 int) returns void as $$ <> declare param1 int := 1; begin <> declare param1 int := 2; begin raise notice 'param1 = %', param1; raise notice 'pl_qual_names.param1 = %', pl_qual_names.param1; raise notice 'outerblock.param1 = %', outerblock.param1; raise notice 'innerblock.param1 = %', innerblock.param1; end; end; $$ language plpgsql; select pl_qual_names(42); drop function pl_qual_names(int); -- tests for RETURN QUERY create function ret_query1(out int, out int) returns setof record as $$ begin $1 := -1; $2 := -2; return next; return query select x + 1, x * 10 from generate_series(0, 10) s (x); return next; end; $$ language plpgsql; select * from ret_query1(); create type record_type as (x text, y int, z boolean); create or replace function ret_query2(lim int) returns setof record_type as $$ begin return query select md5(s.x::text), s.x, s.x > 0 from generate_series(-8, lim) s (x) where s.x % 2 = 0; end; $$ language plpgsql; select * from ret_query2(8); -- test EXECUTE USING create function exc_using(int, text) returns int as $$ declare i int; begin for i in execute 'select * from generate_series(1,$1)' using $1+1 loop raise notice '%', i; end loop; execute 'select $2 + $2*3 + length($1)' into i using $2,$1; return i; end $$ language plpgsql; select exc_using(5, 'foobar'); drop function exc_using(int, text); create or replace function exc_using(int) returns void as $$ declare c refcursor; i int; begin open c for execute 'select * from generate_series(1,$1)' using $1+1; loop fetch c into i; exit when not found; raise notice '%', i; end loop; close c; return; end; $$ language plpgsql; select exc_using(5); drop function exc_using(int); -- test FOR-over-cursor create or replace function forc01() returns void as $$ declare c cursor(r1 integer, r2 integer) for select * from generate_series(r1,r2) i; c2 cursor for select * from generate_series(41,43) i; begin for r in c(5,7) loop raise notice '% from %', r.i, c; end loop; -- again, to test if cursor was closed properly for r in c(9,10) loop raise notice '% from %', r.i, c; end loop; -- and test a parameterless cursor for r in c2 loop raise notice '% from %', r.i, c2; end loop; -- and try it with a hand-assigned name raise notice 'after loop, c2 = %', c2; c2 := 'special_name'; for r in c2 loop raise notice '% from %', r.i, c2; end loop; raise notice 'after loop, c2 = %', c2; -- and try it with a generated name -- (which we can't show in the output because it's variable) c2 := null; for r in c2 loop raise notice '%', r.i; end loop; raise notice 'after loop, c2 = %', c2; return; end; $$ language plpgsql; select forc01(); -- try updating the cursor's current row create temp table forc_test as select n as i, n as j from generate_series(1,10) n; create or replace function forc01() returns void as $$ declare c cursor for select * from forc_test; begin for r in c loop raise notice '%, %', r.i, r.j; update forc_test set i = i * 100, j = r.j * 2 where current of c; end loop; end; $$ language plpgsql; select forc01(); select * from forc_test; -- same, with a cursor whose portal name doesn't match variable name create or replace function forc01() returns void as $$ declare c refcursor := 'fooled_ya'; r record; begin open c for select * from forc_test; loop fetch c into r; exit when not found; raise notice '%, %', r.i, r.j; update forc_test set i = i * 100, j = r.j * 2 where current of c; end loop; end; $$ language plpgsql; select forc01(); select * from forc_test; drop function forc01(); -- fail because cursor has no query bound to it create or replace function forc_bad() returns void as $$ declare c refcursor; begin for r in c loop raise notice '%', r.i; end loop; end; $$ language plpgsql; -- test RETURN QUERY EXECUTE create or replace function return_dquery() returns setof int as $$ begin return query execute 'select * from (values(10),(20)) f'; return query execute 'select * from (values($1),($2)) f' using 40,50; end; $$ language plpgsql; select * from return_dquery(); drop function return_dquery(); -- test RETURN QUERY with dropped columns create table tabwithcols(a int, b int, c int, d int); insert into tabwithcols values(10,20,30,40),(50,60,70,80); create or replace function returnqueryf() returns setof tabwithcols as $$ begin return query select * from tabwithcols; return query execute 'select * from tabwithcols'; end; $$ language plpgsql; select * from returnqueryf(); alter table tabwithcols drop column b; select * from returnqueryf(); alter table tabwithcols drop column d; select * from returnqueryf(); alter table tabwithcols add column d int; select * from returnqueryf(); drop function returnqueryf(); drop table tabwithcols; -- -- Tests for composite-type results -- create type compostype as (x int, y varchar); -- test: use of variable of composite type in return statement create or replace function compos() returns compostype as $$ declare v compostype; begin v := (1, 'hello'); return v; end; $$ language plpgsql; select compos(); -- test: use of variable of record type in return statement create or replace function compos() returns compostype as $$ declare v record; begin v := (1, 'hello'::varchar); return v; end; $$ language plpgsql; select compos(); -- test: use of row expr in return statement create or replace function compos() returns compostype as $$ begin return (1, 'hello'::varchar); end; $$ language plpgsql; select compos(); -- this does not work currently (no implicit casting) create or replace function compos() returns compostype as $$ begin return (1, 'hello'); end; $$ language plpgsql; select compos(); -- ... but this does create or replace function compos() returns compostype as $$ begin return (1, 'hello')::compostype; end; $$ language plpgsql; select compos(); drop function compos(); -- test: return a row expr as record. create or replace function composrec() returns record as $$ declare v record; begin v := (1, 'hello'); return v; end; $$ language plpgsql; select composrec(); -- test: return row expr in return statement. create or replace function composrec() returns record as $$ begin return (1, 'hello'); end; $$ language plpgsql; select composrec(); drop function composrec(); -- test: row expr in RETURN NEXT statement. create or replace function compos() returns setof compostype as $$ begin for i in 1..3 loop return next (1, 'hello'::varchar); end loop; return next null::compostype; return next (2, 'goodbye')::compostype; end; $$ language plpgsql; select * from compos(); drop function compos(); -- test: use invalid expr in return statement. create or replace function compos() returns compostype as $$ begin return 1 + 1; end; $$ language plpgsql; select compos(); -- RETURN variable is a different code path ... create or replace function compos() returns compostype as $$ declare x int := 42; begin return x; end; $$ language plpgsql; select * from compos(); drop function compos(); -- test: invalid use of composite variable in scalar-returning function create or replace function compos() returns int as $$ declare v compostype; begin v := (1, 'hello'); return v; end; $$ language plpgsql; select compos(); -- test: invalid use of composite expression in scalar-returning function create or replace function compos() returns int as $$ begin return (1, 'hello')::compostype; end; $$ language plpgsql; select compos(); drop function compos(); drop type compostype; -- -- Tests for 8.4's new RAISE features -- create or replace function raise_test() returns void as $$ begin raise notice '% % %', 1, 2, 3 using errcode = '55001', detail = 'some detail info', hint = 'some hint'; raise '% % %', 1, 2, 3 using errcode = 'division_by_zero', detail = 'some detail info'; end; $$ language plpgsql; select raise_test(); -- Since we can't actually see the thrown SQLSTATE in default psql output, -- test it like this; this also tests re-RAISE create or replace function raise_test() returns void as $$ begin raise 'check me' using errcode = 'division_by_zero', detail = 'some detail info'; exception when others then raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm; raise; end; $$ language plpgsql; select raise_test(); create or replace function raise_test() returns void as $$ begin raise 'check me' using errcode = '1234F', detail = 'some detail info'; exception when others then raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm; raise; end; $$ language plpgsql; select raise_test(); -- SQLSTATE specification in WHEN create or replace function raise_test() returns void as $$ begin raise 'check me' using errcode = '1234F', detail = 'some detail info'; exception when sqlstate '1234F' then raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm; raise; end; $$ language plpgsql; select raise_test(); create or replace function raise_test() returns void as $$ begin raise division_by_zero using detail = 'some detail info'; exception when others then raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm; raise; end; $$ language plpgsql; select raise_test(); create or replace function raise_test() returns void as $$ begin raise division_by_zero; end; $$ language plpgsql; select raise_test(); create or replace function raise_test() returns void as $$ begin raise sqlstate '1234F'; end; $$ language plpgsql; select raise_test(); create or replace function raise_test() returns void as $$ begin raise division_by_zero using message = 'custom' || ' message'; end; $$ language plpgsql; select raise_test(); create or replace function raise_test() returns void as $$ begin raise using message = 'custom' || ' message', errcode = '22012'; end; $$ language plpgsql; select raise_test(); -- conflict on message create or replace function raise_test() returns void as $$ begin raise notice 'some message' using message = 'custom' || ' message', errcode = '22012'; end; $$ language plpgsql; select raise_test(); -- conflict on errcode create or replace function raise_test() returns void as $$ begin raise division_by_zero using message = 'custom' || ' message', errcode = '22012'; end; $$ language plpgsql; select raise_test(); -- nothing to re-RAISE create or replace function raise_test() returns void as $$ begin raise; end; $$ language plpgsql; select raise_test(); -- test access to exception data create function zero_divide() returns int as $$ declare v int := 0; begin return 10 / v; end; $$ language plpgsql; create or replace function raise_test() returns void as $$ begin raise exception 'custom exception' using detail = 'some detail of custom exception', hint = 'some hint related to custom exception'; end; $$ language plpgsql; create function stacked_diagnostics_test() returns void as $$ declare _sqlstate text; _message text; _context text; begin perform zero_divide(); exception when others then get stacked diagnostics _sqlstate = returned_sqlstate, _message = message_text, _context = pg_exception_context; raise notice 'sqlstate: %, message: %, context: [%]', _sqlstate, _message, replace(_context, E'\n', ' <- '); end; $$ language plpgsql; select stacked_diagnostics_test(); create or replace function stacked_diagnostics_test() returns void as $$ declare _detail text; _hint text; _message text; begin perform raise_test(); exception when others then get stacked diagnostics _message = message_text, _detail = pg_exception_detail, _hint = pg_exception_hint; raise notice 'message: %, detail: %, hint: %', _message, _detail, _hint; end; $$ language plpgsql; select stacked_diagnostics_test(); -- fail, cannot use stacked diagnostics statement outside handler create or replace function stacked_diagnostics_test() returns void as $$ declare _detail text; _hint text; _message text; begin get stacked diagnostics _message = message_text, _detail = pg_exception_detail, _hint = pg_exception_hint; raise notice 'message: %, detail: %, hint: %', _message, _detail, _hint; end; $$ language plpgsql; select stacked_diagnostics_test(); drop function zero_divide(); drop function stacked_diagnostics_test(); -- check cases where implicit SQLSTATE variable could be confused with -- SQLSTATE as a keyword, cf bug #5524 create or replace function raise_test() returns void as $$ begin perform 1/0; exception when sqlstate '22012' then raise notice using message = sqlstate; raise sqlstate '22012' using message = 'substitute message'; end; $$ language plpgsql; select raise_test(); drop function raise_test(); -- test passing column_name, constraint_name, datatype_name, table_name -- and schema_name error fields create or replace function stacked_diagnostics_test() returns void as $$ declare _column_name text; _constraint_name text; _datatype_name text; _table_name text; _schema_name text; begin raise exception using column = '>>some column name<<', constraint = '>>some constraint name<<', datatype = '>>some datatype name<<', table = '>>some table name<<', schema = '>>some schema name<<'; exception when others then get stacked diagnostics _column_name = column_name, _constraint_name = constraint_name, _datatype_name = pg_datatype_name, _table_name = table_name, _schema_name = schema_name; raise notice 'column %, constraint %, type %, table %, schema %', _column_name, _constraint_name, _datatype_name, _table_name, _schema_name; end; $$ language plpgsql; select stacked_diagnostics_test(); drop function stacked_diagnostics_test(); -- test variadic functions create or replace function vari(variadic int[]) returns void as $$ begin for i in array_lower($1,1)..array_upper($1,1) loop raise notice '%', $1[i]; end loop; end; $$ language plpgsql; select vari(1,2,3,4,5); select vari(3,4,5); select vari(variadic array[5,6,7]); drop function vari(int[]); -- coercion test create or replace function pleast(variadic numeric[]) returns numeric as $$ declare aux numeric = $1[array_lower($1,1)]; begin for i in array_lower($1,1)+1..array_upper($1,1) loop if $1[i] < aux then aux := $1[i]; end if; end loop; return aux; end; $$ language plpgsql immutable strict; select pleast(10,1,2,3,-16); select pleast(10.2,2.2,-1.1); select pleast(10.2,10, -20); select pleast(10,20, -1.0); -- in case of conflict, non-variadic version is preferred create or replace function pleast(numeric) returns numeric as $$ begin raise notice 'non-variadic function called'; return $1; end; $$ language plpgsql immutable strict; select pleast(10); drop function pleast(numeric[]); drop function pleast(numeric); -- test table functions create function tftest(int) returns table(a int, b int) as $$ begin return query select $1, $1+i from generate_series(1,5) g(i); end; $$ language plpgsql immutable strict; select * from tftest(10); create or replace function tftest(a1 int) returns table(a int, b int) as $$ begin a := a1; b := a1 + 1; return next; a := a1 * 10; b := a1 * 10 + 1; return next; end; $$ language plpgsql immutable strict; select * from tftest(10); drop function tftest(int); create or replace function rttest() returns setof int as $$ declare rc int; rca int[]; begin return query values(10),(20); get diagnostics rc = row_count; raise notice '% %', found, rc; return query select * from (values(10),(20)) f(a) where false; get diagnostics rc = row_count; raise notice '% %', found, rc; return query execute 'values(10),(20)'; -- just for fun, let's use array elements as targets get diagnostics rca[1] = row_count; raise notice '% %', found, rca[1]; return query execute 'select * from (values(10),(20)) f(a) where false'; get diagnostics rca[2] = row_count; raise notice '% %', found, rca[2]; end; $$ language plpgsql; select * from rttest(); drop function rttest(); -- Test for proper cleanup at subtransaction exit. This example -- exposed a bug in PG 8.2. CREATE FUNCTION leaker_1(fail BOOL) RETURNS INTEGER AS $$ DECLARE v_var INTEGER; BEGIN BEGIN v_var := (leaker_2(fail)).error_code; EXCEPTION WHEN others THEN RETURN 0; END; RETURN 1; END; $$ LANGUAGE plpgsql; CREATE FUNCTION leaker_2(fail BOOL, OUT error_code INTEGER, OUT new_id INTEGER) RETURNS RECORD AS $$ BEGIN IF fail THEN RAISE EXCEPTION 'fail ...'; END IF; error_code := 1; new_id := 1; RETURN; END; $$ LANGUAGE plpgsql; SELECT * FROM leaker_1(false); SELECT * FROM leaker_1(true); DROP FUNCTION leaker_1(bool); DROP FUNCTION leaker_2(bool); -- Test for appropriate cleanup of non-simple expression evaluations -- (bug in all versions prior to August 2010) CREATE FUNCTION nonsimple_expr_test() RETURNS text[] AS $$ DECLARE arr text[]; lr text; i integer; BEGIN arr := array[array['foo','bar'], array['baz', 'quux']]; lr := 'fool'; i := 1; -- use sub-SELECTs to make expressions non-simple arr[(SELECT i)][(SELECT i+1)] := (SELECT lr); RETURN arr; END; $$ LANGUAGE plpgsql; SELECT nonsimple_expr_test(); DROP FUNCTION nonsimple_expr_test(); CREATE FUNCTION nonsimple_expr_test() RETURNS integer AS $$ declare i integer NOT NULL := 0; begin begin i := (SELECT NULL::integer); -- should throw error exception WHEN OTHERS THEN i := (SELECT 1::integer); end; return i; end; $$ LANGUAGE plpgsql; SELECT nonsimple_expr_test(); DROP FUNCTION nonsimple_expr_test(); -- -- Test cases involving recursion and error recovery in simple expressions -- (bugs in all versions before October 2010). The problems are most -- easily exposed by mutual recursion between plpgsql and sql functions. -- create function recurse(float8) returns float8 as $$ begin if ($1 > 0) then return sql_recurse($1 - 1); else return $1; end if; end; $$ language plpgsql; -- "limit" is to prevent this from being inlined create function sql_recurse(float8) returns float8 as $$ select recurse($1) limit 1; $$ language sql; select recurse(10); create function error1(text) returns text language sql as $$ SELECT relname::text FROM pg_class c WHERE c.oid = $1::regclass $$; create function error2(p_name_table text) returns text language plpgsql as $$ begin return error1(p_name_table); end$$; BEGIN; create table public.stuffs (stuff text); SAVEPOINT a; select error2('nonexistent.stuffs'); ROLLBACK TO a; select error2('public.stuffs'); rollback; drop function error2(p_name_table text); drop function error1(text); -- Test for proper handling of cast-expression caching create function sql_to_date(integer) returns date as $$ select $1::text::date $$ language sql immutable strict; create cast (integer as date) with function sql_to_date(integer) as assignment; create function cast_invoker(integer) returns date as $$ begin return $1; end$$ language plpgsql; select cast_invoker(20150717); select cast_invoker(20150718); -- second call crashed in pre-release 9.5 begin; select cast_invoker(20150717); select cast_invoker(20150718); savepoint s1; select cast_invoker(20150718); select cast_invoker(-1); -- fails rollback to savepoint s1; select cast_invoker(20150719); select cast_invoker(20150720); commit; drop function cast_invoker(integer); drop function sql_to_date(integer) cascade; -- Test handling of cast cache inside DO blocks -- (to check the original crash case, this must be a cast not previously -- used in this session) begin; do $$ declare x text[]; begin x := '{1.23, 4.56}'::numeric[]; end $$; do $$ declare x text[]; begin x := '{1.23, 4.56}'::numeric[]; end $$; end; -- Test for consistent reporting of error context create function fail() returns int language plpgsql as $$ begin return 1/0; end $$; select fail(); select fail(); drop function fail(); -- Test handling of string literals. set standard_conforming_strings = off; create or replace function strtest() returns text as $$ begin raise notice 'foo\\bar\041baz'; return 'foo\\bar\041baz'; end $$ language plpgsql; select strtest(); create or replace function strtest() returns text as $$ begin raise notice E'foo\\bar\041baz'; return E'foo\\bar\041baz'; end $$ language plpgsql; select strtest(); set standard_conforming_strings = on; create or replace function strtest() returns text as $$ begin raise notice 'foo\\bar\041baz\'; return 'foo\\bar\041baz\'; end $$ language plpgsql; select strtest(); create or replace function strtest() returns text as $$ begin raise notice E'foo\\bar\041baz'; return E'foo\\bar\041baz'; end $$ language plpgsql; select strtest(); drop function strtest(); -- Test anonymous code blocks. DO $$ DECLARE r record; BEGIN FOR r IN SELECT rtrim(roomno) AS roomno, comment FROM Room ORDER BY roomno LOOP RAISE NOTICE '%, %', r.roomno, r.comment; END LOOP; END$$; -- these are to check syntax error reporting DO LANGUAGE plpgsql $$begin return 1; end$$; DO $$ DECLARE r record; BEGIN FOR r IN SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomno LOOP RAISE NOTICE '%, %', r.roomno, r.comment; END LOOP; END$$; -- Check handling of errors thrown from/into anonymous code blocks. do $outer$ begin for i in 1..10 loop begin execute $ex$ do $$ declare x int = 0; begin x := 1 / x; end; $$; $ex$; exception when division_by_zero then raise notice 'caught division by zero'; end; end loop; end; $outer$; -- Check variable scoping -- a var is not available in its own or prior -- default expressions. create function scope_test() returns int as $$ declare x int := 42; begin declare y int := x + 1; x int := x + 2; begin return x * 100 + y; end; end; $$ language plpgsql; select scope_test(); drop function scope_test(); -- Check handling of conflicts between plpgsql vars and table columns. set plpgsql.variable_conflict = error; create function conflict_test() returns setof int8_tbl as $$ declare r record; q1 bigint := 42; begin for r in select q1,q2 from int8_tbl loop return next r; end loop; end; $$ language plpgsql; select * from conflict_test(); create or replace function conflict_test() returns setof int8_tbl as $$ #variable_conflict use_variable declare r record; q1 bigint := 42; begin for r in select q1,q2 from int8_tbl loop return next r; end loop; end; $$ language plpgsql; select * from conflict_test(); create or replace function conflict_test() returns setof int8_tbl as $$ #variable_conflict use_column declare r record; q1 bigint := 42; begin for r in select q1,q2 from int8_tbl loop return next r; end loop; end; $$ language plpgsql; select * from conflict_test(); drop function conflict_test(); -- Check that an unreserved keyword can be used as a variable name create function unreserved_test() returns int as $$ declare forward int := 21; begin forward := forward * 2; return forward; end $$ language plpgsql; select unreserved_test(); create or replace function unreserved_test() returns int as $$ declare return int := 42; begin return := return + 1; return return; end $$ language plpgsql; select unreserved_test(); create or replace function unreserved_test() returns int as $$ declare comment int := 21; begin comment := comment * 2; comment on function unreserved_test() is 'this is a test'; return comment; end $$ language plpgsql; select unreserved_test(); select obj_description('unreserved_test()'::regprocedure, 'pg_proc'); drop function unreserved_test(); -- -- Test FOREACH over arrays -- create function foreach_test(anyarray) returns void as $$ declare x int; begin foreach x in array $1 loop raise notice '%', x; end loop; end; $$ language plpgsql; select foreach_test(ARRAY[1,2,3,4]); select foreach_test(ARRAY[[1,2],[3,4]]); create or replace function foreach_test(anyarray) returns void as $$ declare x int; begin foreach x slice 1 in array $1 loop raise notice '%', x; end loop; end; $$ language plpgsql; -- should fail select foreach_test(ARRAY[1,2,3,4]); select foreach_test(ARRAY[[1,2],[3,4]]); create or replace function foreach_test(anyarray) returns void as $$ declare x int[]; begin foreach x slice 1 in array $1 loop raise notice '%', x; end loop; end; $$ language plpgsql; select foreach_test(ARRAY[1,2,3,4]); select foreach_test(ARRAY[[1,2],[3,4]]); -- higher level of slicing create or replace function foreach_test(anyarray) returns void as $$ declare x int[]; begin foreach x slice 2 in array $1 loop raise notice '%', x; end loop; end; $$ language plpgsql; -- should fail select foreach_test(ARRAY[1,2,3,4]); -- ok select foreach_test(ARRAY[[1,2],[3,4]]); select foreach_test(ARRAY[[[1,2]],[[3,4]]]); create type xy_tuple AS (x int, y int); -- iteration over array of records create or replace function foreach_test(anyarray) returns void as $$ declare r record; begin foreach r in array $1 loop raise notice '%', r; end loop; end; $$ language plpgsql; select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]); select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]); create or replace function foreach_test(anyarray) returns void as $$ declare x int; y int; begin foreach x, y in array $1 loop raise notice 'x = %, y = %', x, y; end loop; end; $$ language plpgsql; select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]); select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]); -- slicing over array of composite types create or replace function foreach_test(anyarray) returns void as $$ declare x xy_tuple[]; begin foreach x slice 1 in array $1 loop raise notice '%', x; end loop; end; $$ language plpgsql; select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]); select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]); drop function foreach_test(anyarray); drop type xy_tuple; -- -- Assorted tests for array subscript assignment -- create temp table rtype (id int, ar text[]); create function arrayassign1() returns text[] language plpgsql as $$ declare r record; begin r := row(12, '{foo,bar,baz}')::rtype; r.ar[2] := 'replace'; return r.ar; end$$; select arrayassign1(); select arrayassign1(); -- try again to exercise internal caching create domain orderedarray as int[2] constraint sorted check (value[1] < value[2]); select '{1,2}'::orderedarray; select '{2,1}'::orderedarray; -- fail create function testoa(x1 int, x2 int, x3 int) returns orderedarray language plpgsql as $$ declare res orderedarray; begin res := array[x1, x2]; res[2] := x3; return res; end$$; select testoa(1,2,3); select testoa(1,2,3); -- try again to exercise internal caching select testoa(2,1,3); -- fail at initial assign select testoa(1,2,1); -- fail at update drop function arrayassign1(); drop function testoa(x1 int, x2 int, x3 int); -- -- Test handling of expanded arrays -- create function returns_rw_array(int) returns int[] language plpgsql as $$ declare r int[]; begin r := array[$1, $1]; return r; end; $$ stable; create function consumes_rw_array(int[]) returns int language plpgsql as $$ begin return $1[1]; end; $$ stable; select consumes_rw_array(returns_rw_array(42)); -- bug #14174 explain (verbose, costs off) select i, a from (select returns_rw_array(1) as a offset 0) ss, lateral consumes_rw_array(a) i; select i, a from (select returns_rw_array(1) as a offset 0) ss, lateral consumes_rw_array(a) i; explain (verbose, costs off) select consumes_rw_array(a), a from returns_rw_array(1) a; select consumes_rw_array(a), a from returns_rw_array(1) a; explain (verbose, costs off) select consumes_rw_array(a), a from (values (returns_rw_array(1)), (returns_rw_array(2))) v(a); select consumes_rw_array(a), a from (values (returns_rw_array(1)), (returns_rw_array(2))) v(a); do $$ declare a int[] := array[1,2]; begin a := a || 3; raise notice 'a = %', a; end$$; -- -- Test access to call stack -- create function inner_func(int) returns int as $$ declare _context text; begin get diagnostics _context = pg_context; raise notice '***%***', _context; -- lets do it again, just for fun.. get diagnostics _context = pg_context; raise notice '***%***', _context; raise notice 'lets make sure we didnt break anything'; return 2 * $1; end; $$ language plpgsql; create or replace function outer_func(int) returns int as $$ declare myresult int; begin raise notice 'calling down into inner_func()'; myresult := inner_func($1); raise notice 'inner_func() done'; return myresult; end; $$ language plpgsql; create or replace function outer_outer_func(int) returns int as $$ declare myresult int; begin raise notice 'calling down into outer_func()'; myresult := outer_func($1); raise notice 'outer_func() done'; return myresult; end; $$ language plpgsql; select outer_outer_func(10); -- repeated call should to work select outer_outer_func(20); drop function outer_outer_func(int); drop function outer_func(int); drop function inner_func(int); -- access to call stack from exception create function inner_func(int) returns int as $$ declare _context text; sx int := 5; begin begin perform sx / 0; exception when division_by_zero then get diagnostics _context = pg_context; raise notice '***%***', _context; end; -- lets do it again, just for fun.. get diagnostics _context = pg_context; raise notice '***%***', _context; raise notice 'lets make sure we didnt break anything'; return 2 * $1; end; $$ language plpgsql; create or replace function outer_func(int) returns int as $$ declare myresult int; begin raise notice 'calling down into inner_func()'; myresult := inner_func($1); raise notice 'inner_func() done'; return myresult; end; $$ language plpgsql; create or replace function outer_outer_func(int) returns int as $$ declare myresult int; begin raise notice 'calling down into outer_func()'; myresult := outer_func($1); raise notice 'outer_func() done'; return myresult; end; $$ language plpgsql; select outer_outer_func(10); -- repeated call should to work select outer_outer_func(20); drop function outer_outer_func(int); drop function outer_func(int); drop function inner_func(int); -- -- Test ASSERT -- do $$ begin assert 1=1; -- should succeed end; $$; do $$ begin assert 1=0; -- should fail end; $$; do $$ begin assert NULL; -- should fail end; $$; -- check controlling GUC set plpgsql.check_asserts = off; do $$ begin assert 1=0; -- won't be tested end; $$; reset plpgsql.check_asserts; -- test custom message do $$ declare var text := 'some value'; begin assert 1=0, format('assertion failed, var = "%s"', var); end; $$; -- ensure assertions are not trapped by 'others' do $$ begin assert 1=0, 'unhandled assertion'; exception when others then null; -- do nothing end; $$; -- Test use of plpgsql in a domain check constraint (cf. bug #14414) create function plpgsql_domain_check(val int) returns boolean as $$ begin return val > 0; end $$ language plpgsql immutable; create domain plpgsql_domain as integer check(plpgsql_domain_check(value)); do $$ declare v_test plpgsql_domain; begin v_test := 1; end; $$; do $$ declare v_test plpgsql_domain := 1; begin v_test := 0; -- fail end; $$; -- Test handling of expanded array passed to a domain constraint (bug #14472) create function plpgsql_arr_domain_check(val int[]) returns boolean as $$ begin return val[1] > 0; end $$ language plpgsql immutable; create domain plpgsql_arr_domain as int[] check(plpgsql_arr_domain_check(value)); do $$ declare v_test plpgsql_arr_domain; begin v_test := array[1]; v_test := v_test || 2; end; $$; do $$ declare v_test plpgsql_arr_domain := array[1]; begin v_test := 0 || v_test; -- fail end; $$; -- -- test usage of transition tables in AFTER triggers -- CREATE TABLE transition_table_base (id int PRIMARY KEY, val text); CREATE FUNCTION transition_table_base_ins_func() RETURNS trigger LANGUAGE plpgsql AS $$ DECLARE t text; l text; BEGIN t = ''; FOR l IN EXECUTE $q$ EXPLAIN (TIMING off, COSTS off, VERBOSE on) SELECT * FROM newtable $q$ LOOP t = t || l || E'\n'; END LOOP; RAISE INFO '%', t; RETURN new; END; $$; CREATE TRIGGER transition_table_base_ins_trig AFTER INSERT ON transition_table_base REFERENCING OLD TABLE AS oldtable NEW TABLE AS newtable FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_base_ins_func(); CREATE TRIGGER transition_table_base_ins_trig AFTER INSERT ON transition_table_base REFERENCING NEW TABLE AS newtable FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_base_ins_func(); INSERT INTO transition_table_base VALUES (1, 'One'), (2, 'Two'); INSERT INTO transition_table_base VALUES (3, 'Three'), (4, 'Four'); CREATE OR REPLACE FUNCTION transition_table_base_upd_func() RETURNS trigger LANGUAGE plpgsql AS $$ DECLARE t text; l text; BEGIN t = ''; FOR l IN EXECUTE $q$ EXPLAIN (TIMING off, COSTS off, VERBOSE on) SELECT * FROM oldtable ot FULL JOIN newtable nt USING (id) $q$ LOOP t = t || l || E'\n'; END LOOP; RAISE INFO '%', t; RETURN new; END; $$; CREATE TRIGGER transition_table_base_upd_trig AFTER UPDATE ON transition_table_base REFERENCING OLD TABLE AS oldtable NEW TABLE AS newtable FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_base_upd_func(); UPDATE transition_table_base SET val = '*' || val || '*' WHERE id BETWEEN 2 AND 3; CREATE TABLE transition_table_level1 ( level1_no serial NOT NULL , level1_node_name varchar(255), PRIMARY KEY (level1_no) ) WITHOUT OIDS; CREATE TABLE transition_table_level2 ( level2_no serial NOT NULL , parent_no int NOT NULL, level1_node_name varchar(255), PRIMARY KEY (level2_no) ) WITHOUT OIDS; CREATE TABLE transition_table_status ( level int NOT NULL, node_no int NOT NULL, status int, PRIMARY KEY (level, node_no) ) WITHOUT OIDS; CREATE FUNCTION transition_table_level1_ri_parent_del_func() RETURNS TRIGGER LANGUAGE plpgsql AS $$ DECLARE n bigint; BEGIN PERFORM FROM p JOIN transition_table_level2 c ON c.parent_no = p.level1_no; IF FOUND THEN RAISE EXCEPTION 'RI error'; END IF; RETURN NULL; END; $$; CREATE TRIGGER transition_table_level1_ri_parent_del_trigger AFTER DELETE ON transition_table_level1 REFERENCING OLD TABLE AS p FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_level1_ri_parent_del_func(); CREATE FUNCTION transition_table_level1_ri_parent_upd_func() RETURNS TRIGGER LANGUAGE plpgsql AS $$ DECLARE x int; BEGIN WITH p AS (SELECT level1_no, sum(delta) cnt FROM (SELECT level1_no, 1 AS delta FROM i UNION ALL SELECT level1_no, -1 AS delta FROM d) w GROUP BY level1_no HAVING sum(delta) < 0) SELECT level1_no FROM p JOIN transition_table_level2 c ON c.parent_no = p.level1_no INTO x; IF FOUND THEN RAISE EXCEPTION 'RI error'; END IF; RETURN NULL; END; $$; CREATE TRIGGER transition_table_level1_ri_parent_upd_trigger AFTER UPDATE ON transition_table_level1 REFERENCING OLD TABLE AS d NEW TABLE AS i FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_level1_ri_parent_upd_func(); CREATE FUNCTION transition_table_level2_ri_child_insupd_func() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN PERFORM FROM i LEFT JOIN transition_table_level1 p ON p.level1_no IS NOT NULL AND p.level1_no = i.parent_no WHERE p.level1_no IS NULL; IF FOUND THEN RAISE EXCEPTION 'RI error'; END IF; RETURN NULL; END; $$; CREATE TRIGGER transition_table_level2_ri_child_ins_trigger AFTER INSERT ON transition_table_level2 REFERENCING NEW TABLE AS i FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_level2_ri_child_insupd_func(); CREATE TRIGGER transition_table_level2_ri_child_upd_trigger AFTER UPDATE ON transition_table_level2 REFERENCING NEW TABLE AS i FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_level2_ri_child_insupd_func(); -- create initial test data INSERT INTO transition_table_level1 (level1_no) SELECT generate_series(1,200); ANALYZE transition_table_level1; INSERT INTO transition_table_level2 (level2_no, parent_no) SELECT level2_no, level2_no / 50 + 1 AS parent_no FROM generate_series(1,9999) level2_no; ANALYZE transition_table_level2; INSERT INTO transition_table_status (level, node_no, status) SELECT 1, level1_no, 0 FROM transition_table_level1; INSERT INTO transition_table_status (level, node_no, status) SELECT 2, level2_no, 0 FROM transition_table_level2; ANALYZE transition_table_status; INSERT INTO transition_table_level1(level1_no) SELECT generate_series(201,1000); ANALYZE transition_table_level1; -- behave reasonably if someone tries to modify a transition table CREATE FUNCTION transition_table_level2_bad_usage_func() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN INSERT INTO dx VALUES (1000000, 1000000, 'x'); RETURN NULL; END; $$; CREATE TRIGGER transition_table_level2_bad_usage_trigger AFTER DELETE ON transition_table_level2 REFERENCING OLD TABLE AS dx FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_level2_bad_usage_func(); DELETE FROM transition_table_level2 WHERE level2_no BETWEEN 301 AND 305; DROP TRIGGER transition_table_level2_bad_usage_trigger ON transition_table_level2; -- attempt modifications which would break RI (should all fail) DELETE FROM transition_table_level1 WHERE level1_no = 25; UPDATE transition_table_level1 SET level1_no = -1 WHERE level1_no = 30; INSERT INTO transition_table_level2 (level2_no, parent_no) VALUES (10000, 10000); UPDATE transition_table_level2 SET parent_no = 2000 WHERE level2_no = 40; -- attempt modifications which would not break RI (should all succeed) DELETE FROM transition_table_level1 WHERE level1_no BETWEEN 201 AND 1000; DELETE FROM transition_table_level1 WHERE level1_no BETWEEN 100000000 AND 100000010; SELECT count(*) FROM transition_table_level1; DELETE FROM transition_table_level2 WHERE level2_no BETWEEN 211 AND 220; SELECT count(*) FROM transition_table_level2; CREATE TABLE alter_table_under_transition_tables ( id int PRIMARY KEY, name text ); CREATE FUNCTION alter_table_under_transition_tables_upd_func() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RAISE WARNING 'old table = %, new table = %', (SELECT string_agg(id || '=' || name, ',') FROM d), (SELECT string_agg(id || '=' || name, ',') FROM i); RAISE NOTICE 'one = %', (SELECT 1 FROM alter_table_under_transition_tables LIMIT 1); RETURN NULL; END; $$; -- should fail, TRUNCATE is not compatible with transition tables CREATE TRIGGER alter_table_under_transition_tables_upd_trigger AFTER TRUNCATE OR UPDATE ON alter_table_under_transition_tables REFERENCING OLD TABLE AS d NEW TABLE AS i FOR EACH STATEMENT EXECUTE PROCEDURE alter_table_under_transition_tables_upd_func(); -- should work CREATE TRIGGER alter_table_under_transition_tables_upd_trigger AFTER UPDATE ON alter_table_under_transition_tables REFERENCING OLD TABLE AS d NEW TABLE AS i FOR EACH STATEMENT EXECUTE PROCEDURE alter_table_under_transition_tables_upd_func(); INSERT INTO alter_table_under_transition_tables VALUES (1, '1'), (2, '2'), (3, '3'); UPDATE alter_table_under_transition_tables SET name = name || name; -- now change 'name' to an integer to see what happens... ALTER TABLE alter_table_under_transition_tables ALTER COLUMN name TYPE int USING name::integer; UPDATE alter_table_under_transition_tables SET name = (name::text || name::text)::integer; -- now drop column 'name' ALTER TABLE alter_table_under_transition_tables DROP column name; UPDATE alter_table_under_transition_tables SET id = id; -- -- Test multiple reference to a transition table -- CREATE TABLE multi_test (i int); INSERT INTO multi_test VALUES (1); CREATE OR REPLACE FUNCTION multi_test_trig() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN RAISE NOTICE 'count = %', (SELECT COUNT(*) FROM new_test); RAISE NOTICE 'count union = %', (SELECT COUNT(*) FROM (SELECT * FROM new_test UNION ALL SELECT * FROM new_test) ss); RETURN NULL; END$$; CREATE TRIGGER my_trigger AFTER UPDATE ON multi_test REFERENCING NEW TABLE AS new_test OLD TABLE as old_test FOR EACH STATEMENT EXECUTE PROCEDURE multi_test_trig(); UPDATE multi_test SET i = i; DROP TABLE multi_test; DROP FUNCTION multi_test_trig(); -- -- Check type parsing and record fetching from partitioned tables -- CREATE TABLE partitioned_table (a int, b text) PARTITION BY LIST (a); CREATE TABLE pt_part1 PARTITION OF partitioned_table FOR VALUES IN (1); CREATE TABLE pt_part2 PARTITION OF partitioned_table FOR VALUES IN (2); INSERT INTO partitioned_table VALUES (1, 'Row 1'); INSERT INTO partitioned_table VALUES (2, 'Row 2'); CREATE OR REPLACE FUNCTION get_from_partitioned_table(partitioned_table.a%type) RETURNS partitioned_table AS $$ DECLARE a_val partitioned_table.a%TYPE; result partitioned_table%ROWTYPE; BEGIN a_val := $1; SELECT * INTO result FROM partitioned_table WHERE a = a_val; RETURN result; END; $$ LANGUAGE plpgsql; SELECT * FROM get_from_partitioned_table(1) AS t; CREATE OR REPLACE FUNCTION list_partitioned_table() RETURNS SETOF partitioned_table.a%TYPE AS $$ DECLARE row partitioned_table%ROWTYPE; a_val partitioned_table.a%TYPE; BEGIN FOR row IN SELECT * FROM partitioned_table ORDER BY a LOOP a_val := row.a; RETURN NEXT a_val; END LOOP; RETURN; END; $$ LANGUAGE plpgsql; SELECT * FROM list_partitioned_table() AS t; -- -- Check argument name is used instead of $n in error message -- CREATE FUNCTION fx(x WSlot) RETURNS void AS $$ BEGIN GET DIAGNOSTICS x = ROW_COUNT; RETURN; END; $$ LANGUAGE plpgsql; pgFormatter-4.2/t/pg-test-files/sql/point.sql000066400000000000000000000074261361326045100212620ustar00rootroot00000000000000-- -- POINT -- -- avoid bit-exact output here because operations may not be bit-exact. SET extra_float_digits = 0; CREATE TABLE POINT_TBL(f1 point); INSERT INTO POINT_TBL(f1) VALUES ('(0.0,0.0)'); INSERT INTO POINT_TBL(f1) VALUES ('(-10.0,0.0)'); INSERT INTO POINT_TBL(f1) VALUES ('(-3.0,4.0)'); INSERT INTO POINT_TBL(f1) VALUES ('(5.1, 34.5)'); INSERT INTO POINT_TBL(f1) VALUES ('(-5.0,-12.0)'); INSERT INTO POINT_TBL(f1) VALUES ('(1e-300,-1e-300)'); -- To underflow INSERT INTO POINT_TBL(f1) VALUES ('(1e+300,Inf)'); -- To overflow INSERT INTO POINT_TBL(f1) VALUES (' ( Nan , NaN ) '); -- bad format points INSERT INTO POINT_TBL(f1) VALUES ('asdfasdf'); INSERT INTO POINT_TBL(f1) VALUES ('10.0,10.0'); INSERT INTO POINT_TBL(f1) VALUES ('(10.0 10.0)'); INSERT INTO POINT_TBL(f1) VALUES ('(10.0, 10.0) x'); INSERT INTO POINT_TBL(f1) VALUES ('(10.0,10.0'); INSERT INTO POINT_TBL(f1) VALUES ('(10.0, 1e+500)'); -- Out of range SELECT '' AS six, * FROM POINT_TBL; -- left of SELECT '' AS three, p.* FROM POINT_TBL p WHERE p.f1 << '(0.0, 0.0)'; -- right of SELECT '' AS three, p.* FROM POINT_TBL p WHERE '(0.0,0.0)' >> p.f1; -- above SELECT '' AS one, p.* FROM POINT_TBL p WHERE '(0.0,0.0)' >^ p.f1; -- below SELECT '' AS one, p.* FROM POINT_TBL p WHERE p.f1 <^ '(0.0, 0.0)'; -- equal SELECT '' AS one, p.* FROM POINT_TBL p WHERE p.f1 ~= '(5.1, 34.5)'; -- point in box SELECT '' AS three, p.* FROM POINT_TBL p WHERE p.f1 <@ box '(0,0,100,100)'; SELECT '' AS three, p.* FROM POINT_TBL p WHERE box '(0,0,100,100)' @> p.f1; SELECT '' AS three, p.* FROM POINT_TBL p WHERE not p.f1 <@ box '(0,0,100,100)'; SELECT '' AS two, p.* FROM POINT_TBL p WHERE p.f1 <@ path '[(0,0),(-10,0),(-10,10)]'; SELECT '' AS three, p.* FROM POINT_TBL p WHERE not box '(0,0,100,100)' @> p.f1; SELECT '' AS six, p.f1, p.f1 <-> point '(0,0)' AS dist FROM POINT_TBL p ORDER BY dist; SELECT '' AS thirtysix, p1.f1 AS point1, p2.f1 AS point2, p1.f1 <-> p2.f1 AS dist FROM POINT_TBL p1, POINT_TBL p2 ORDER BY dist, p1.f1[0], p2.f1[0]; SELECT '' AS thirty, p1.f1 AS point1, p2.f1 AS point2 FROM POINT_TBL p1, POINT_TBL p2 WHERE (p1.f1 <-> p2.f1) > 3; -- put distance result into output to allow sorting with GEQ optimizer - tgl 97/05/10 SELECT '' AS fifteen, p1.f1 AS point1, p2.f1 AS point2, (p1.f1 <-> p2.f1) AS distance FROM POINT_TBL p1, POINT_TBL p2 WHERE (p1.f1 <-> p2.f1) > 3 and p1.f1 << p2.f1 ORDER BY distance, p1.f1[0], p2.f1[0]; -- put distance result into output to allow sorting with GEQ optimizer - tgl 97/05/10 SELECT '' AS three, p1.f1 AS point1, p2.f1 AS point2, (p1.f1 <-> p2.f1) AS distance FROM POINT_TBL p1, POINT_TBL p2 WHERE (p1.f1 <-> p2.f1) > 3 and p1.f1 << p2.f1 and p1.f1 >^ p2.f1 ORDER BY distance; -- Test that GiST indexes provide same behavior as sequential scan CREATE TEMP TABLE point_gist_tbl(f1 point); INSERT INTO point_gist_tbl SELECT '(0,0)' FROM generate_series(0,1000); CREATE INDEX point_gist_tbl_index ON point_gist_tbl USING gist (f1); INSERT INTO point_gist_tbl VALUES ('(0.0000009,0.0000009)'); SET enable_seqscan TO true; SET enable_indexscan TO false; SET enable_bitmapscan TO false; SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000009,0.0000009)'::point; SELECT COUNT(*) FROM point_gist_tbl WHERE f1 <@ '(0.0000009,0.0000009),(0.0000009,0.0000009)'::box; SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000018,0.0000018)'::point; SET enable_seqscan TO false; SET enable_indexscan TO true; SET enable_bitmapscan TO true; SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000009,0.0000009)'::point; SELECT COUNT(*) FROM point_gist_tbl WHERE f1 <@ '(0.0000009,0.0000009),(0.0000009,0.0000009)'::box; SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000018,0.0000018)'::point; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; pgFormatter-4.2/t/pg-test-files/sql/polygon.sql000066400000000000000000000122271361326045100216130ustar00rootroot00000000000000-- -- POLYGON -- -- polygon logic -- CREATE TABLE POLYGON_TBL(f1 polygon); INSERT INTO POLYGON_TBL(f1) VALUES ('(2.0,0.0),(2.0,4.0),(0.0,0.0)'); INSERT INTO POLYGON_TBL(f1) VALUES ('(3.0,1.0),(3.0,3.0),(1.0,0.0)'); INSERT INTO POLYGON_TBL(f1) VALUES ('(1,2),(3,4),(5,6),(7,8)'); INSERT INTO POLYGON_TBL(f1) VALUES ('(7,8),(5,6),(3,4),(1,2)'); -- Reverse INSERT INTO POLYGON_TBL(f1) VALUES ('(1,2),(7,8),(5,6),(3,-4)'); -- degenerate polygons INSERT INTO POLYGON_TBL(f1) VALUES ('(0.0,0.0)'); INSERT INTO POLYGON_TBL(f1) VALUES ('(0.0,1.0),(0.0,1.0)'); -- bad polygon input strings INSERT INTO POLYGON_TBL(f1) VALUES ('0.0'); INSERT INTO POLYGON_TBL(f1) VALUES ('(0.0 0.0'); INSERT INTO POLYGON_TBL(f1) VALUES ('(0,1,2)'); INSERT INTO POLYGON_TBL(f1) VALUES ('(0,1,2,3'); INSERT INTO POLYGON_TBL(f1) VALUES ('asdf'); SELECT '' AS four, * FROM POLYGON_TBL; -- -- Test the SP-GiST index -- CREATE TABLE quad_poly_tbl (id int, p polygon); INSERT INTO quad_poly_tbl SELECT (x - 1) * 100 + y, polygon(circle(point(x * 10, y * 10), 1 + (x + y) % 10)) FROM generate_series(1, 100) x, generate_series(1, 100) y; INSERT INTO quad_poly_tbl SELECT i, polygon '((200, 300),(210, 310),(230, 290))' FROM generate_series(10001, 11000) AS i; INSERT INTO quad_poly_tbl VALUES (11001, NULL), (11002, NULL), (11003, NULL); CREATE INDEX quad_poly_tbl_idx ON quad_poly_tbl USING spgist(p); -- get reference results for ORDER BY distance from seq scan SET enable_seqscan = ON; SET enable_indexscan = OFF; SET enable_bitmapscan = OFF; CREATE TEMP TABLE quad_poly_tbl_ord_seq2 AS SELECT rank() OVER (ORDER BY p <-> point '123,456') n, p <-> point '123,456' dist, id FROM quad_poly_tbl WHERE p <@ polygon '((300,300),(400,600),(600,500),(700,200))'; -- check results results from index scan SET enable_seqscan = OFF; SET enable_indexscan = OFF; SET enable_bitmapscan = ON; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_poly_tbl WHERE p << polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p << polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_poly_tbl WHERE p &< polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p &< polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_poly_tbl WHERE p && polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p && polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_poly_tbl WHERE p &> polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p &> polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_poly_tbl WHERE p >> polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p >> polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_poly_tbl WHERE p <<| polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p <<| polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_poly_tbl WHERE p &<| polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p &<| polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_poly_tbl WHERE p |&> polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p |&> polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_poly_tbl WHERE p |>> polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p |>> polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_poly_tbl WHERE p <@ polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT count(*) FROM quad_poly_tbl WHERE p <@ polygon '((300,300),(400,600),(600,500),(700,200))'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_poly_tbl WHERE p @> polygon '((340,550),(343,552),(341,553))'; SELECT count(*) FROM quad_poly_tbl WHERE p @> polygon '((340,550),(343,552),(341,553))'; EXPLAIN (COSTS OFF) SELECT count(*) FROM quad_poly_tbl WHERE p ~= polygon '((200, 300),(210, 310),(230, 290))'; SELECT count(*) FROM quad_poly_tbl WHERE p ~= polygon '((200, 300),(210, 310),(230, 290))'; -- test ORDER BY distance SET enable_indexscan = ON; SET enable_bitmapscan = OFF; EXPLAIN (COSTS OFF) SELECT rank() OVER (ORDER BY p <-> point '123,456') n, p <-> point '123,456' dist, id FROM quad_poly_tbl WHERE p <@ polygon '((300,300),(400,600),(600,500),(700,200))'; CREATE TEMP TABLE quad_poly_tbl_ord_idx2 AS SELECT rank() OVER (ORDER BY p <-> point '123,456') n, p <-> point '123,456' dist, id FROM quad_poly_tbl WHERE p <@ polygon '((300,300),(400,600),(600,500),(700,200))'; SELECT * FROM quad_poly_tbl_ord_seq2 seq FULL JOIN quad_poly_tbl_ord_idx2 idx ON seq.n = idx.n AND seq.id = idx.id AND (seq.dist = idx.dist OR seq.dist IS NULL AND idx.dist IS NULL) WHERE seq.id IS NULL OR idx.id IS NULL; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; pgFormatter-4.2/t/pg-test-files/sql/polymorphism.sql000066400000000000000000000661111361326045100226670ustar00rootroot00000000000000-- Currently this tests polymorphic aggregates and indirectly does some -- testing of polymorphic SQL functions. It ought to be extended. -- Tests for other features related to function-calling have snuck in, too. -- Legend: ----------- -- A = type is ANY -- P = type is polymorphic -- N = type is non-polymorphic -- B = aggregate base type -- S = aggregate state type -- R = aggregate return type -- 1 = arg1 of a function -- 2 = arg2 of a function -- ag = aggregate -- tf = trans (state) function -- ff = final function -- rt = return type of a function -- -> = implies -- => = allowed -- !> = not allowed -- E = exists -- NE = not-exists -- -- Possible states: -- ---------------- -- B = (A || P || N) -- when (B = A) -> (tf2 = NE) -- S = (P || N) -- ff = (E || NE) -- tf1 = (P || N) -- tf2 = (NE || P || N) -- R = (P || N) -- create functions for use as tf and ff with the needed combinations of -- argument polymorphism, but within the constraints of valid aggregate -- functions, i.e. tf arg1 and tf return type must match -- polymorphic single arg transfn CREATE FUNCTION stfp(anyarray) RETURNS anyarray AS 'select $1' LANGUAGE SQL; -- non-polymorphic single arg transfn CREATE FUNCTION stfnp(int[]) RETURNS int[] AS 'select $1' LANGUAGE SQL; -- dual polymorphic transfn CREATE FUNCTION tfp(anyarray,anyelement) RETURNS anyarray AS 'select $1 || $2' LANGUAGE SQL; -- dual non-polymorphic transfn CREATE FUNCTION tfnp(int[],int) RETURNS int[] AS 'select $1 || $2' LANGUAGE SQL; -- arg1 only polymorphic transfn CREATE FUNCTION tf1p(anyarray,int) RETURNS anyarray AS 'select $1' LANGUAGE SQL; -- arg2 only polymorphic transfn CREATE FUNCTION tf2p(int[],anyelement) RETURNS int[] AS 'select $1' LANGUAGE SQL; -- multi-arg polymorphic CREATE FUNCTION sum3(anyelement,anyelement,anyelement) returns anyelement AS 'select $1+$2+$3' language sql strict; -- finalfn polymorphic CREATE FUNCTION ffp(anyarray) RETURNS anyarray AS 'select $1' LANGUAGE SQL; -- finalfn non-polymorphic CREATE FUNCTION ffnp(int[]) returns int[] as 'select $1' LANGUAGE SQL; -- Try to cover all the possible states: -- -- Note: in Cases 1 & 2, we are trying to return P. Therefore, if the transfn -- is stfnp, tfnp, or tf2p, we must use ffp as finalfn, because stfnp, tfnp, -- and tf2p do not return P. Conversely, in Cases 3 & 4, we are trying to -- return N. Therefore, if the transfn is stfp, tfp, or tf1p, we must use ffnp -- as finalfn, because stfp, tfp, and tf1p do not return N. -- -- Case1 (R = P) && (B = A) -- ------------------------ -- S tf1 -- ------- -- N N -- should CREATE CREATE AGGREGATE myaggp01a(*) (SFUNC = stfnp, STYPE = int4[], FINALFUNC = ffp, INITCOND = '{}'); -- P N -- should ERROR: stfnp(anyarray) not matched by stfnp(int[]) CREATE AGGREGATE myaggp02a(*) (SFUNC = stfnp, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); -- N P -- should CREATE CREATE AGGREGATE myaggp03a(*) (SFUNC = stfp, STYPE = int4[], FINALFUNC = ffp, INITCOND = '{}'); CREATE AGGREGATE myaggp03b(*) (SFUNC = stfp, STYPE = int4[], INITCOND = '{}'); -- P P -- should ERROR: we have no way to resolve S CREATE AGGREGATE myaggp04a(*) (SFUNC = stfp, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); CREATE AGGREGATE myaggp04b(*) (SFUNC = stfp, STYPE = anyarray, INITCOND = '{}'); -- Case2 (R = P) && ((B = P) || (B = N)) -- ------------------------------------- -- S tf1 B tf2 -- ----------------------- -- N N N N -- should CREATE CREATE AGGREGATE myaggp05a(BASETYPE = int, SFUNC = tfnp, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}'); -- N N N P -- should CREATE CREATE AGGREGATE myaggp06a(BASETYPE = int, SFUNC = tf2p, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}'); -- N N P N -- should ERROR: tfnp(int[], anyelement) not matched by tfnp(int[], int) CREATE AGGREGATE myaggp07a(BASETYPE = anyelement, SFUNC = tfnp, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}'); -- N N P P -- should CREATE CREATE AGGREGATE myaggp08a(BASETYPE = anyelement, SFUNC = tf2p, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}'); -- N P N N -- should CREATE CREATE AGGREGATE myaggp09a(BASETYPE = int, SFUNC = tf1p, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}'); CREATE AGGREGATE myaggp09b(BASETYPE = int, SFUNC = tf1p, STYPE = int[], INITCOND = '{}'); -- N P N P -- should CREATE CREATE AGGREGATE myaggp10a(BASETYPE = int, SFUNC = tfp, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}'); CREATE AGGREGATE myaggp10b(BASETYPE = int, SFUNC = tfp, STYPE = int[], INITCOND = '{}'); -- N P P N -- should ERROR: tf1p(int[],anyelement) not matched by tf1p(anyarray,int) CREATE AGGREGATE myaggp11a(BASETYPE = anyelement, SFUNC = tf1p, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}'); CREATE AGGREGATE myaggp11b(BASETYPE = anyelement, SFUNC = tf1p, STYPE = int[], INITCOND = '{}'); -- N P P P -- should ERROR: tfp(int[],anyelement) not matched by tfp(anyarray,anyelement) CREATE AGGREGATE myaggp12a(BASETYPE = anyelement, SFUNC = tfp, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}'); CREATE AGGREGATE myaggp12b(BASETYPE = anyelement, SFUNC = tfp, STYPE = int[], INITCOND = '{}'); -- P N N N -- should ERROR: tfnp(anyarray, int) not matched by tfnp(int[],int) CREATE AGGREGATE myaggp13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); -- P N N P -- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement) CREATE AGGREGATE myaggp14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); -- P N P N -- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int) CREATE AGGREGATE myaggp15a(BASETYPE = anyelement, SFUNC = tfnp, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); -- P N P P -- should ERROR: tf2p(anyarray, anyelement) not matched by tf2p(int[],anyelement) CREATE AGGREGATE myaggp16a(BASETYPE = anyelement, SFUNC = tf2p, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); -- P P N N -- should ERROR: we have no way to resolve S CREATE AGGREGATE myaggp17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); CREATE AGGREGATE myaggp17b(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray, INITCOND = '{}'); -- P P N P -- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement) CREATE AGGREGATE myaggp18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); CREATE AGGREGATE myaggp18b(BASETYPE = int, SFUNC = tfp, STYPE = anyarray, INITCOND = '{}'); -- P P P N -- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int) CREATE AGGREGATE myaggp19a(BASETYPE = anyelement, SFUNC = tf1p, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); CREATE AGGREGATE myaggp19b(BASETYPE = anyelement, SFUNC = tf1p, STYPE = anyarray, INITCOND = '{}'); -- P P P P -- should CREATE CREATE AGGREGATE myaggp20a(BASETYPE = anyelement, SFUNC = tfp, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); CREATE AGGREGATE myaggp20b(BASETYPE = anyelement, SFUNC = tfp, STYPE = anyarray, INITCOND = '{}'); -- Case3 (R = N) && (B = A) -- ------------------------ -- S tf1 -- ------- -- N N -- should CREATE CREATE AGGREGATE myaggn01a(*) (SFUNC = stfnp, STYPE = int4[], FINALFUNC = ffnp, INITCOND = '{}'); CREATE AGGREGATE myaggn01b(*) (SFUNC = stfnp, STYPE = int4[], INITCOND = '{}'); -- P N -- should ERROR: stfnp(anyarray) not matched by stfnp(int[]) CREATE AGGREGATE myaggn02a(*) (SFUNC = stfnp, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); CREATE AGGREGATE myaggn02b(*) (SFUNC = stfnp, STYPE = anyarray, INITCOND = '{}'); -- N P -- should CREATE CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[], FINALFUNC = ffnp, INITCOND = '{}'); -- P P -- should ERROR: ffnp(anyarray) not matched by ffnp(int[]) CREATE AGGREGATE myaggn04a(*) (SFUNC = stfp, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); -- Case4 (R = N) && ((B = P) || (B = N)) -- ------------------------------------- -- S tf1 B tf2 -- ----------------------- -- N N N N -- should CREATE CREATE AGGREGATE myaggn05a(BASETYPE = int, SFUNC = tfnp, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}'); CREATE AGGREGATE myaggn05b(BASETYPE = int, SFUNC = tfnp, STYPE = int[], INITCOND = '{}'); -- N N N P -- should CREATE CREATE AGGREGATE myaggn06a(BASETYPE = int, SFUNC = tf2p, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}'); CREATE AGGREGATE myaggn06b(BASETYPE = int, SFUNC = tf2p, STYPE = int[], INITCOND = '{}'); -- N N P N -- should ERROR: tfnp(int[], anyelement) not matched by tfnp(int[], int) CREATE AGGREGATE myaggn07a(BASETYPE = anyelement, SFUNC = tfnp, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}'); CREATE AGGREGATE myaggn07b(BASETYPE = anyelement, SFUNC = tfnp, STYPE = int[], INITCOND = '{}'); -- N N P P -- should CREATE CREATE AGGREGATE myaggn08a(BASETYPE = anyelement, SFUNC = tf2p, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}'); CREATE AGGREGATE myaggn08b(BASETYPE = anyelement, SFUNC = tf2p, STYPE = int[], INITCOND = '{}'); -- N P N N -- should CREATE CREATE AGGREGATE myaggn09a(BASETYPE = int, SFUNC = tf1p, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}'); -- N P N P -- should CREATE CREATE AGGREGATE myaggn10a(BASETYPE = int, SFUNC = tfp, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}'); -- N P P N -- should ERROR: tf1p(int[],anyelement) not matched by tf1p(anyarray,int) CREATE AGGREGATE myaggn11a(BASETYPE = anyelement, SFUNC = tf1p, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}'); -- N P P P -- should ERROR: tfp(int[],anyelement) not matched by tfp(anyarray,anyelement) CREATE AGGREGATE myaggn12a(BASETYPE = anyelement, SFUNC = tfp, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}'); -- P N N N -- should ERROR: tfnp(anyarray, int) not matched by tfnp(int[],int) CREATE AGGREGATE myaggn13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); CREATE AGGREGATE myaggn13b(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray, INITCOND = '{}'); -- P N N P -- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement) CREATE AGGREGATE myaggn14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); CREATE AGGREGATE myaggn14b(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray, INITCOND = '{}'); -- P N P N -- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int) CREATE AGGREGATE myaggn15a(BASETYPE = anyelement, SFUNC = tfnp, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); CREATE AGGREGATE myaggn15b(BASETYPE = anyelement, SFUNC = tfnp, STYPE = anyarray, INITCOND = '{}'); -- P N P P -- should ERROR: tf2p(anyarray, anyelement) not matched by tf2p(int[],anyelement) CREATE AGGREGATE myaggn16a(BASETYPE = anyelement, SFUNC = tf2p, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); CREATE AGGREGATE myaggn16b(BASETYPE = anyelement, SFUNC = tf2p, STYPE = anyarray, INITCOND = '{}'); -- P P N N -- should ERROR: ffnp(anyarray) not matched by ffnp(int[]) CREATE AGGREGATE myaggn17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); -- P P N P -- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement) CREATE AGGREGATE myaggn18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); -- P P P N -- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int) CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); -- P P P P -- should ERROR: ffnp(anyarray) not matched by ffnp(int[]) CREATE AGGREGATE myaggn20a(BASETYPE = anyelement, SFUNC = tfp, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); -- multi-arg polymorphic CREATE AGGREGATE mysum2(anyelement,anyelement) (SFUNC = sum3, STYPE = anyelement, INITCOND = '0'); -- create test data for polymorphic aggregates create temp table t(f1 int, f2 int[], f3 text); insert into t values(1,array[1],'a'); insert into t values(1,array[11],'b'); insert into t values(1,array[111],'c'); insert into t values(2,array[2],'a'); insert into t values(2,array[22],'b'); insert into t values(2,array[222],'c'); insert into t values(3,array[3],'a'); insert into t values(3,array[3],'b'); -- test the successfully created polymorphic aggregates select f3, myaggp01a(*) from t group by f3 order by f3; select f3, myaggp03a(*) from t group by f3 order by f3; select f3, myaggp03b(*) from t group by f3 order by f3; select f3, myaggp05a(f1) from t group by f3 order by f3; select f3, myaggp06a(f1) from t group by f3 order by f3; select f3, myaggp08a(f1) from t group by f3 order by f3; select f3, myaggp09a(f1) from t group by f3 order by f3; select f3, myaggp09b(f1) from t group by f3 order by f3; select f3, myaggp10a(f1) from t group by f3 order by f3; select f3, myaggp10b(f1) from t group by f3 order by f3; select f3, myaggp20a(f1) from t group by f3 order by f3; select f3, myaggp20b(f1) from t group by f3 order by f3; select f3, myaggn01a(*) from t group by f3 order by f3; select f3, myaggn01b(*) from t group by f3 order by f3; select f3, myaggn03a(*) from t group by f3 order by f3; select f3, myaggn05a(f1) from t group by f3 order by f3; select f3, myaggn05b(f1) from t group by f3 order by f3; select f3, myaggn06a(f1) from t group by f3 order by f3; select f3, myaggn06b(f1) from t group by f3 order by f3; select f3, myaggn08a(f1) from t group by f3 order by f3; select f3, myaggn08b(f1) from t group by f3 order by f3; select f3, myaggn09a(f1) from t group by f3 order by f3; select f3, myaggn10a(f1) from t group by f3 order by f3; select mysum2(f1, f1 + 1) from t; -- test inlining of polymorphic SQL functions create function bleat(int) returns int as $$ begin raise notice 'bleat %', $1; return $1; end$$ language plpgsql; create function sql_if(bool, anyelement, anyelement) returns anyelement as $$ select case when $1 then $2 else $3 end $$ language sql; -- Note this would fail with integer overflow, never mind wrong bleat() output, -- if the CASE expression were not successfully inlined select f1, sql_if(f1 > 0, bleat(f1), bleat(f1 + 1)) from int4_tbl; select q2, sql_if(q2 > 0, q2, q2 + 1) from int8_tbl; -- another sort of polymorphic aggregate CREATE AGGREGATE array_cat_accum (anyarray) ( sfunc = array_cat, stype = anyarray, initcond = '{}' ); SELECT array_cat_accum(i) FROM (VALUES (ARRAY[1,2]), (ARRAY[3,4])) as t(i); SELECT array_cat_accum(i) FROM (VALUES (ARRAY[row(1,2),row(3,4)]), (ARRAY[row(5,6),row(7,8)])) as t(i); -- another kind of polymorphic aggregate create function add_group(grp anyarray, ad anyelement, size integer) returns anyarray as $$ begin if grp is null then return array[ad]; end if; if array_upper(grp, 1) < size then return grp || ad; end if; return grp; end; $$ language plpgsql immutable; create aggregate build_group(anyelement, integer) ( SFUNC = add_group, STYPE = anyarray ); select build_group(q1,3) from int8_tbl; -- this should fail because stype isn't compatible with arg create aggregate build_group(int8, integer) ( SFUNC = add_group, STYPE = int2[] ); -- but we can make a non-poly agg from a poly sfunc if types are OK create aggregate build_group(int8, integer) ( SFUNC = add_group, STYPE = int8[] ); -- check proper resolution of data types for polymorphic transfn/finalfn create function first_el(anyarray) returns anyelement as 'select $1[1]' language sql strict immutable; create aggregate first_el_agg_f8(float8) ( SFUNC = array_append, STYPE = float8[], FINALFUNC = first_el ); create aggregate first_el_agg_any(anyelement) ( SFUNC = array_append, STYPE = anyarray, FINALFUNC = first_el ); select first_el_agg_f8(x::float8) from generate_series(1,10) x; select first_el_agg_any(x) from generate_series(1,10) x; select first_el_agg_f8(x::float8) over(order by x) from generate_series(1,10) x; select first_el_agg_any(x) over(order by x) from generate_series(1,10) x; -- check that we can apply functions taking ANYARRAY to pg_stats select distinct array_ndims(histogram_bounds) from pg_stats where histogram_bounds is not null; -- such functions must protect themselves if varying element type isn't OK -- (WHERE clause here is to avoid possibly getting a collation error instead) select max(histogram_bounds) from pg_stats where tablename = 'pg_am'; -- test variadic polymorphic functions create function myleast(variadic anyarray) returns anyelement as $$ select min($1[i]) from generate_subscripts($1,1) g(i) $$ language sql immutable strict; select myleast(10, 1, 20, 33); select myleast(1.1, 0.22, 0.55); select myleast('z'::text); select myleast(); -- fail -- test with variadic call parameter select myleast(variadic array[1,2,3,4,-1]); select myleast(variadic array[1.1, -5.5]); --test with empty variadic call parameter select myleast(variadic array[]::int[]); -- an example with some ordinary arguments too create function concat(text, variadic anyarray) returns text as $$ select array_to_string($2, $1); $$ language sql immutable strict; select concat('%', 1, 2, 3, 4, 5); select concat('|', 'a'::text, 'b', 'c'); select concat('|', variadic array[1,2,33]); select concat('|', variadic array[]::int[]); drop function concat(text, anyarray); -- mix variadic with anyelement create function formarray(anyelement, variadic anyarray) returns anyarray as $$ select array_prepend($1, $2); $$ language sql immutable strict; select formarray(1,2,3,4,5); select formarray(1.1, variadic array[1.2,55.5]); select formarray(1.1, array[1.2,55.5]); -- fail without variadic select formarray(1, 'x'::text); -- fail, type mismatch select formarray(1, variadic array['x'::text]); -- fail, type mismatch drop function formarray(anyelement, variadic anyarray); -- test pg_typeof() function select pg_typeof(null); -- unknown select pg_typeof(0); -- integer select pg_typeof(0.0); -- numeric select pg_typeof(1+1 = 2); -- boolean select pg_typeof('x'); -- unknown select pg_typeof('' || ''); -- text select pg_typeof(pg_typeof(0)); -- regtype select pg_typeof(array[1.2,55.5]); -- numeric[] select pg_typeof(myleast(10, 1, 20, 33)); -- polymorphic input -- test functions with default parameters -- test basic functionality create function dfunc(a int = 1, int = 2) returns int as $$ select $1 + $2; $$ language sql; select dfunc(); select dfunc(10); select dfunc(10, 20); select dfunc(10, 20, 30); -- fail drop function dfunc(); -- fail drop function dfunc(int); -- fail drop function dfunc(int, int); -- ok -- fail: defaults must be at end of argument list create function dfunc(a int = 1, b int) returns int as $$ select $1 + $2; $$ language sql; -- however, this should work: create function dfunc(a int = 1, out sum int, b int = 2) as $$ select $1 + $2; $$ language sql; select dfunc(); -- verify it lists properly \df dfunc drop function dfunc(int, int); -- check implicit coercion create function dfunc(a int DEFAULT 1.0, int DEFAULT '-1') returns int as $$ select $1 + $2; $$ language sql; select dfunc(); create function dfunc(a text DEFAULT 'Hello', b text DEFAULT 'World') returns text as $$ select $1 || ', ' || $2; $$ language sql; select dfunc(); -- fail: which dfunc should be called? int or text select dfunc('Hi'); -- ok select dfunc('Hi', 'City'); -- ok select dfunc(0); -- ok select dfunc(10, 20); -- ok drop function dfunc(int, int); drop function dfunc(text, text); create function dfunc(int = 1, int = 2) returns int as $$ select 2; $$ language sql; create function dfunc(int = 1, int = 2, int = 3, int = 4) returns int as $$ select 4; $$ language sql; -- Now, dfunc(nargs = 2) and dfunc(nargs = 4) are ambiguous when called -- with 0 to 2 arguments. select dfunc(); -- fail select dfunc(1); -- fail select dfunc(1, 2); -- fail select dfunc(1, 2, 3); -- ok select dfunc(1, 2, 3, 4); -- ok drop function dfunc(int, int); drop function dfunc(int, int, int, int); -- default values are not allowed for output parameters create function dfunc(out int = 20) returns int as $$ select 1; $$ language sql; -- polymorphic parameter test create function dfunc(anyelement = 'World'::text) returns text as $$ select 'Hello, ' || $1::text; $$ language sql; select dfunc(); select dfunc(0); select dfunc(to_date('20081215','YYYYMMDD')); select dfunc('City'::text); drop function dfunc(anyelement); -- check defaults for variadics create function dfunc(a variadic int[]) returns int as $$ select array_upper($1, 1) $$ language sql; select dfunc(); -- fail select dfunc(10); select dfunc(10,20); create or replace function dfunc(a variadic int[] default array[]::int[]) returns int as $$ select array_upper($1, 1) $$ language sql; select dfunc(); -- now ok select dfunc(10); select dfunc(10,20); -- can't remove the default once it exists create or replace function dfunc(a variadic int[]) returns int as $$ select array_upper($1, 1) $$ language sql; \df dfunc drop function dfunc(a variadic int[]); -- Ambiguity should be reported only if there's not a better match available create function dfunc(int = 1, int = 2, int = 3) returns int as $$ select 3; $$ language sql; create function dfunc(int = 1, int = 2) returns int as $$ select 2; $$ language sql; create function dfunc(text) returns text as $$ select $1; $$ language sql; -- dfunc(narg=2) and dfunc(narg=3) are ambiguous select dfunc(1); -- fail -- but this works since the ambiguous functions aren't preferred anyway select dfunc('Hi'); drop function dfunc(int, int, int); drop function dfunc(int, int); drop function dfunc(text); -- -- Tests for named- and mixed-notation function calling -- create function dfunc(a int, b int, c int = 0, d int = 0) returns table (a int, b int, c int, d int) as $$ select $1, $2, $3, $4; $$ language sql; select (dfunc(10,20,30)).*; select (dfunc(a := 10, b := 20, c := 30)).*; select * from dfunc(a := 10, b := 20); select * from dfunc(b := 10, a := 20); select * from dfunc(0); -- fail select * from dfunc(1,2); select * from dfunc(1,2,c := 3); select * from dfunc(1,2,d := 3); select * from dfunc(x := 20, b := 10, x := 30); -- fail, duplicate name select * from dfunc(10, b := 20, 30); -- fail, named args must be last select * from dfunc(x := 10, b := 20, c := 30); -- fail, unknown param select * from dfunc(10, 10, a := 20); -- fail, a overlaps positional parameter select * from dfunc(1,c := 2,d := 3); -- fail, no value for b drop function dfunc(int, int, int, int); -- test with different parameter types create function dfunc(a varchar, b numeric, c date = current_date) returns table (a varchar, b numeric, c date) as $$ select $1, $2, $3; $$ language sql; select (dfunc('Hello World', 20, '2009-07-25'::date)).*; select * from dfunc('Hello World', 20, '2009-07-25'::date); select * from dfunc(c := '2009-07-25'::date, a := 'Hello World', b := 20); select * from dfunc('Hello World', b := 20, c := '2009-07-25'::date); select * from dfunc('Hello World', c := '2009-07-25'::date, b := 20); select * from dfunc('Hello World', c := 20, b := '2009-07-25'::date); -- fail drop function dfunc(varchar, numeric, date); -- test out parameters with named params create function dfunc(a varchar = 'def a', out _a varchar, c numeric = NULL, out _c numeric) returns record as $$ select $1, $2; $$ language sql; select (dfunc()).*; select * from dfunc(); select * from dfunc('Hello', 100); select * from dfunc(a := 'Hello', c := 100); select * from dfunc(c := 100, a := 'Hello'); select * from dfunc('Hello'); select * from dfunc('Hello', c := 100); select * from dfunc(c := 100); -- fail, can no longer change an input parameter's name create or replace function dfunc(a varchar = 'def a', out _a varchar, x numeric = NULL, out _c numeric) returns record as $$ select $1, $2; $$ language sql; create or replace function dfunc(a varchar = 'def a', out _a varchar, numeric = NULL, out _c numeric) returns record as $$ select $1, $2; $$ language sql; drop function dfunc(varchar, numeric); --fail, named parameters are not unique create function testpolym(a int, a int) returns int as $$ select 1;$$ language sql; create function testpolym(int, out a int, out a int) returns int as $$ select 1;$$ language sql; create function testpolym(out a int, inout a int) returns int as $$ select 1;$$ language sql; create function testpolym(a int, inout a int) returns int as $$ select 1;$$ language sql; -- valid create function testpolym(a int, out a int) returns int as $$ select $1;$$ language sql; select testpolym(37); drop function testpolym(int); create function testpolym(a int) returns table(a int) as $$ select $1;$$ language sql; select * from testpolym(37); drop function testpolym(int); -- test polymorphic params and defaults create function dfunc(a anyelement, b anyelement = null, flag bool = true) returns anyelement as $$ select case when $3 then $1 else $2 end; $$ language sql; select dfunc(1,2); select dfunc('a'::text, 'b'); -- positional notation with default select dfunc(a := 1, b := 2); select dfunc(a := 'a'::text, b := 'b'); select dfunc(a := 'a'::text, b := 'b', flag := false); -- named notation select dfunc(b := 'b'::text, a := 'a'); -- named notation with default select dfunc(a := 'a'::text, flag := true); -- named notation with default select dfunc(a := 'a'::text, flag := false); -- named notation with default select dfunc(b := 'b'::text, a := 'a', flag := true); -- named notation select dfunc('a'::text, 'b', false); -- full positional notation select dfunc('a'::text, 'b', flag := false); -- mixed notation select dfunc('a'::text, 'b', true); -- full positional notation select dfunc('a'::text, 'b', flag := true); -- mixed notation -- ansi/sql syntax select dfunc(a => 1, b => 2); select dfunc(a => 'a'::text, b => 'b'); select dfunc(a => 'a'::text, b => 'b', flag => false); -- named notation select dfunc(b => 'b'::text, a => 'a'); -- named notation with default select dfunc(a => 'a'::text, flag => true); -- named notation with default select dfunc(a => 'a'::text, flag => false); -- named notation with default select dfunc(b => 'b'::text, a => 'a', flag => true); -- named notation select dfunc('a'::text, 'b', false); -- full positional notation select dfunc('a'::text, 'b', flag => false); -- mixed notation select dfunc('a'::text, 'b', true); -- full positional notation select dfunc('a'::text, 'b', flag => true); -- mixed notation -- this tests lexer edge cases around => select dfunc(a =>-1); select dfunc(a =>+1); select dfunc(a =>/**/1); select dfunc(a =>--comment to be removed by psql 1); -- need DO to protect the -- from psql do $$ declare r integer; begin select dfunc(a=>-- comment 1) into r; raise info 'r = %', r; end; $$; -- check reverse-listing of named-arg calls CREATE VIEW dfview AS SELECT q1, q2, dfunc(q1,q2, flag := q1>q2) as c3, dfunc(q1, flag := q1= 1000 AND oid < 3000 ORDER BY oid; RESET client_min_messages; -- test proper begins here CREATE USER regress_priv_user1; CREATE USER regress_priv_user2; CREATE USER regress_priv_user3; CREATE USER regress_priv_user4; CREATE USER regress_priv_user5; CREATE USER regress_priv_user5; -- duplicate CREATE GROUP regress_priv_group1; CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2; ALTER GROUP regress_priv_group1 ADD USER regress_priv_user4; ALTER GROUP regress_priv_group2 ADD USER regress_priv_user2; -- duplicate ALTER GROUP regress_priv_group2 DROP USER regress_priv_user2; GRANT regress_priv_group2 TO regress_priv_user4 WITH ADMIN OPTION; -- test owner privileges SET SESSION AUTHORIZATION regress_priv_user1; SELECT session_user, current_user; CREATE TABLE atest1 ( a int, b text ); SELECT * FROM atest1; INSERT INTO atest1 VALUES (1, 'one'); DELETE FROM atest1; UPDATE atest1 SET a = 1 WHERE b = 'blech'; TRUNCATE atest1; BEGIN; LOCK atest1 IN ACCESS EXCLUSIVE MODE; COMMIT; REVOKE ALL ON atest1 FROM PUBLIC; SELECT * FROM atest1; GRANT ALL ON atest1 TO regress_priv_user2; GRANT SELECT ON atest1 TO regress_priv_user3, regress_priv_user4; SELECT * FROM atest1; CREATE TABLE atest2 (col1 varchar(10), col2 boolean); GRANT SELECT ON atest2 TO regress_priv_user2; GRANT UPDATE ON atest2 TO regress_priv_user3; GRANT INSERT ON atest2 TO regress_priv_user4; GRANT TRUNCATE ON atest2 TO regress_priv_user5; SET SESSION AUTHORIZATION regress_priv_user2; SELECT session_user, current_user; -- try various combinations of queries on atest1 and atest2 SELECT * FROM atest1; -- ok SELECT * FROM atest2; -- ok INSERT INTO atest1 VALUES (2, 'two'); -- ok INSERT INTO atest2 VALUES ('foo', true); -- fail INSERT INTO atest1 SELECT 1, b FROM atest1; -- ok UPDATE atest1 SET a = 1 WHERE a = 2; -- ok UPDATE atest2 SET col2 = NOT col2; -- fail SELECT * FROM atest1 FOR UPDATE; -- ok SELECT * FROM atest2 FOR UPDATE; -- fail DELETE FROM atest2; -- fail TRUNCATE atest2; -- fail BEGIN; LOCK atest2 IN ACCESS EXCLUSIVE MODE; -- fail COMMIT; GRANT ALL ON atest1 TO PUBLIC; -- fail -- checks in subquery, both ok SELECT * FROM atest1 WHERE ( b IN ( SELECT col1 FROM atest2 ) ); SELECT * FROM atest2 WHERE ( col1 IN ( SELECT b FROM atest1 ) ); SET SESSION AUTHORIZATION regress_priv_user3; SELECT session_user, current_user; SELECT * FROM atest1; -- ok SELECT * FROM atest2; -- fail INSERT INTO atest1 VALUES (2, 'two'); -- fail INSERT INTO atest2 VALUES ('foo', true); -- fail INSERT INTO atest1 SELECT 1, b FROM atest1; -- fail UPDATE atest1 SET a = 1 WHERE a = 2; -- fail UPDATE atest2 SET col2 = NULL; -- ok UPDATE atest2 SET col2 = NOT col2; -- fails; requires SELECT on atest2 UPDATE atest2 SET col2 = true FROM atest1 WHERE atest1.a = 5; -- ok SELECT * FROM atest1 FOR UPDATE; -- fail SELECT * FROM atest2 FOR UPDATE; -- fail DELETE FROM atest2; -- fail TRUNCATE atest2; -- fail BEGIN; LOCK atest2 IN ACCESS EXCLUSIVE MODE; -- ok COMMIT; -- checks in subquery, both fail SELECT * FROM atest1 WHERE ( b IN ( SELECT col1 FROM atest2 ) ); SELECT * FROM atest2 WHERE ( col1 IN ( SELECT b FROM atest1 ) ); SET SESSION AUTHORIZATION regress_priv_user4; SELECT * FROM atest1; -- ok -- test leaky-function protections in selfuncs -- regress_priv_user1 will own a table and provide a view for it. SET SESSION AUTHORIZATION regress_priv_user1; CREATE TABLE atest12 as SELECT x AS a, 10001 - x AS b FROM generate_series(1,10000) x; CREATE INDEX ON atest12 (a); CREATE INDEX ON atest12 (abs(a)); VACUUM ANALYZE atest12; CREATE FUNCTION leak(integer,integer) RETURNS boolean AS $$begin return $1 < $2; end$$ LANGUAGE plpgsql immutable; CREATE OPERATOR <<< (procedure = leak, leftarg = integer, rightarg = integer, restrict = scalarltsel); -- view with leaky operator CREATE VIEW atest12v AS SELECT * FROM atest12 WHERE b <<< 5; GRANT SELECT ON atest12v TO PUBLIC; -- This plan should use nestloop, knowing that few rows will be selected. EXPLAIN (COSTS OFF) SELECT * FROM atest12v x, atest12v y WHERE x.a = y.b; -- And this one. EXPLAIN (COSTS OFF) SELECT * FROM atest12 x, atest12 y WHERE x.a = y.b and abs(y.a) <<< 5; -- Check if regress_priv_user2 can break security. SET SESSION AUTHORIZATION regress_priv_user2; CREATE FUNCTION leak2(integer,integer) RETURNS boolean AS $$begin raise notice 'leak % %', $1, $2; return $1 > $2; end$$ LANGUAGE plpgsql immutable; CREATE OPERATOR >>> (procedure = leak2, leftarg = integer, rightarg = integer, restrict = scalargtsel); -- This should not show any "leak" notices before failing. EXPLAIN (COSTS OFF) SELECT * FROM atest12 WHERE a >>> 0; -- This plan should use hashjoin, as it will expect many rows to be selected. EXPLAIN (COSTS OFF) SELECT * FROM atest12v x, atest12v y WHERE x.a = y.b; -- Now regress_priv_user1 grants sufficient access to regress_priv_user2. SET SESSION AUTHORIZATION regress_priv_user1; GRANT SELECT (a, b) ON atest12 TO PUBLIC; SET SESSION AUTHORIZATION regress_priv_user2; -- Now regress_priv_user2 will also get a good row estimate. EXPLAIN (COSTS OFF) SELECT * FROM atest12v x, atest12v y WHERE x.a = y.b; -- But not for this, due to lack of table-wide permissions needed -- to make use of the expression index's statistics. EXPLAIN (COSTS OFF) SELECT * FROM atest12 x, atest12 y WHERE x.a = y.b and abs(y.a) <<< 5; -- clean up (regress_priv_user1's objects are all dropped later) DROP FUNCTION leak2(integer, integer) CASCADE; -- groups SET SESSION AUTHORIZATION regress_priv_user3; CREATE TABLE atest3 (one int, two int, three int); GRANT DELETE ON atest3 TO GROUP regress_priv_group2; SET SESSION AUTHORIZATION regress_priv_user1; SELECT * FROM atest3; -- fail DELETE FROM atest3; -- ok -- views SET SESSION AUTHORIZATION regress_priv_user3; CREATE VIEW atestv1 AS SELECT * FROM atest1; -- ok /* The next *should* fail, but it's not implemented that way yet. */ CREATE VIEW atestv2 AS SELECT * FROM atest2; CREATE VIEW atestv3 AS SELECT * FROM atest3; -- ok /* Empty view is a corner case that failed in 9.2. */ CREATE VIEW atestv0 AS SELECT 0 as x WHERE false; -- ok SELECT * FROM atestv1; -- ok SELECT * FROM atestv2; -- fail GRANT SELECT ON atestv1, atestv3 TO regress_priv_user4; GRANT SELECT ON atestv2 TO regress_priv_user2; SET SESSION AUTHORIZATION regress_priv_user4; SELECT * FROM atestv1; -- ok SELECT * FROM atestv2; -- fail SELECT * FROM atestv3; -- ok SELECT * FROM atestv0; -- fail -- Appendrels excluded by constraints failed to check permissions in 8.4-9.2. select * from ((select a.q1 as x from int8_tbl a offset 0) union all (select b.q2 as x from int8_tbl b offset 0)) ss where false; set constraint_exclusion = on; select * from ((select a.q1 as x, random() from int8_tbl a where q1 > 0) union all (select b.q2 as x, random() from int8_tbl b where q2 > 0)) ss where x < 0; reset constraint_exclusion; CREATE VIEW atestv4 AS SELECT * FROM atestv3; -- nested view SELECT * FROM atestv4; -- ok GRANT SELECT ON atestv4 TO regress_priv_user2; SET SESSION AUTHORIZATION regress_priv_user2; -- Two complex cases: SELECT * FROM atestv3; -- fail SELECT * FROM atestv4; -- ok (even though regress_priv_user2 cannot access underlying atestv3) SELECT * FROM atest2; -- ok SELECT * FROM atestv2; -- fail (even though regress_priv_user2 can access underlying atest2) -- Test column level permissions SET SESSION AUTHORIZATION regress_priv_user1; CREATE TABLE atest5 (one int, two int unique, three int, four int unique); CREATE TABLE atest6 (one int, two int, blue int); GRANT SELECT (one), INSERT (two), UPDATE (three) ON atest5 TO regress_priv_user4; GRANT ALL (one) ON atest5 TO regress_priv_user3; INSERT INTO atest5 VALUES (1,2,3); SET SESSION AUTHORIZATION regress_priv_user4; SELECT * FROM atest5; -- fail SELECT one FROM atest5; -- ok SELECT two FROM atest5; -- fail SELECT atest5 FROM atest5; -- fail SELECT 1 FROM atest5; -- ok SELECT 1 FROM atest5 a JOIN atest5 b USING (one); -- ok SELECT 1 FROM atest5 a JOIN atest5 b USING (two); -- fail SELECT 1 FROM atest5 a NATURAL JOIN atest5 b; -- fail SELECT (j.*) IS NULL FROM (atest5 a JOIN atest5 b USING (one)) j; -- fail SELECT 1 FROM atest5 WHERE two = 2; -- fail SELECT * FROM atest1, atest5; -- fail SELECT atest1.* FROM atest1, atest5; -- ok SELECT atest1.*,atest5.one FROM atest1, atest5; -- ok SELECT atest1.*,atest5.one FROM atest1 JOIN atest5 ON (atest1.a = atest5.two); -- fail SELECT atest1.*,atest5.one FROM atest1 JOIN atest5 ON (atest1.a = atest5.one); -- ok SELECT one, two FROM atest5; -- fail SET SESSION AUTHORIZATION regress_priv_user1; GRANT SELECT (one,two) ON atest6 TO regress_priv_user4; SET SESSION AUTHORIZATION regress_priv_user4; SELECT one, two FROM atest5 NATURAL JOIN atest6; -- fail still SET SESSION AUTHORIZATION regress_priv_user1; GRANT SELECT (two) ON atest5 TO regress_priv_user4; SET SESSION AUTHORIZATION regress_priv_user4; SELECT one, two FROM atest5 NATURAL JOIN atest6; -- ok now -- test column-level privileges for INSERT and UPDATE INSERT INTO atest5 (two) VALUES (3); -- ok INSERT INTO atest5 (three) VALUES (4); -- fail INSERT INTO atest5 VALUES (5,5,5); -- fail UPDATE atest5 SET three = 10; -- ok UPDATE atest5 SET one = 8; -- fail UPDATE atest5 SET three = 5, one = 2; -- fail -- Check that column level privs are enforced in RETURNING -- Ok. INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) DO UPDATE set three = 10; -- Error. No SELECT on column three. INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) DO UPDATE set three = 10 RETURNING atest5.three; -- Ok. May SELECT on column "one": INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) DO UPDATE set three = 10 RETURNING atest5.one; -- Check that column level privileges are enforced for EXCLUDED -- Ok. we may select one INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) DO UPDATE set three = EXCLUDED.one; -- Error. No select rights on three INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) DO UPDATE set three = EXCLUDED.three; INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) DO UPDATE set one = 8; -- fails (due to UPDATE) INSERT INTO atest5(three) VALUES (4) ON CONFLICT (two) DO UPDATE set three = 10; -- fails (due to INSERT) -- Check that the columns in the inference require select privileges INSERT INTO atest5(four) VALUES (4); -- fail SET SESSION AUTHORIZATION regress_priv_user1; GRANT INSERT (four) ON atest5 TO regress_priv_user4; SET SESSION AUTHORIZATION regress_priv_user4; INSERT INTO atest5(four) VALUES (4) ON CONFLICT (four) DO UPDATE set three = 3; -- fails (due to SELECT) INSERT INTO atest5(four) VALUES (4) ON CONFLICT ON CONSTRAINT atest5_four_key DO UPDATE set three = 3; -- fails (due to SELECT) INSERT INTO atest5(four) VALUES (4); -- ok SET SESSION AUTHORIZATION regress_priv_user1; GRANT SELECT (four) ON atest5 TO regress_priv_user4; SET SESSION AUTHORIZATION regress_priv_user4; INSERT INTO atest5(four) VALUES (4) ON CONFLICT (four) DO UPDATE set three = 3; -- ok INSERT INTO atest5(four) VALUES (4) ON CONFLICT ON CONSTRAINT atest5_four_key DO UPDATE set three = 3; -- ok SET SESSION AUTHORIZATION regress_priv_user1; REVOKE ALL (one) ON atest5 FROM regress_priv_user4; GRANT SELECT (one,two,blue) ON atest6 TO regress_priv_user4; SET SESSION AUTHORIZATION regress_priv_user4; SELECT one FROM atest5; -- fail UPDATE atest5 SET one = 1; -- fail SELECT atest6 FROM atest6; -- ok -- check error reporting with column privs SET SESSION AUTHORIZATION regress_priv_user1; CREATE TABLE t1 (c1 int, c2 int, c3 int check (c3 < 5), primary key (c1, c2)); GRANT SELECT (c1) ON t1 TO regress_priv_user2; GRANT INSERT (c1, c2, c3) ON t1 TO regress_priv_user2; GRANT UPDATE (c1, c2, c3) ON t1 TO regress_priv_user2; -- seed data INSERT INTO t1 VALUES (1, 1, 1); INSERT INTO t1 VALUES (1, 2, 1); INSERT INTO t1 VALUES (2, 1, 2); INSERT INTO t1 VALUES (2, 2, 2); INSERT INTO t1 VALUES (3, 1, 3); SET SESSION AUTHORIZATION regress_priv_user2; INSERT INTO t1 (c1, c2) VALUES (1, 1); -- fail, but row not shown UPDATE t1 SET c2 = 1; -- fail, but row not shown INSERT INTO t1 (c1, c2) VALUES (null, null); -- fail, but see columns being inserted INSERT INTO t1 (c3) VALUES (null); -- fail, but see columns being inserted or have SELECT INSERT INTO t1 (c1) VALUES (5); -- fail, but see columns being inserted or have SELECT UPDATE t1 SET c3 = 10; -- fail, but see columns with SELECT rights, or being modified SET SESSION AUTHORIZATION regress_priv_user1; DROP TABLE t1; -- test column-level privileges when involved with DELETE SET SESSION AUTHORIZATION regress_priv_user1; ALTER TABLE atest6 ADD COLUMN three integer; GRANT DELETE ON atest5 TO regress_priv_user3; GRANT SELECT (two) ON atest5 TO regress_priv_user3; REVOKE ALL (one) ON atest5 FROM regress_priv_user3; GRANT SELECT (one) ON atest5 TO regress_priv_user4; SET SESSION AUTHORIZATION regress_priv_user4; SELECT atest6 FROM atest6; -- fail SELECT one FROM atest5 NATURAL JOIN atest6; -- fail SET SESSION AUTHORIZATION regress_priv_user1; ALTER TABLE atest6 DROP COLUMN three; SET SESSION AUTHORIZATION regress_priv_user4; SELECT atest6 FROM atest6; -- ok SELECT one FROM atest5 NATURAL JOIN atest6; -- ok SET SESSION AUTHORIZATION regress_priv_user1; ALTER TABLE atest6 DROP COLUMN two; REVOKE SELECT (one,blue) ON atest6 FROM regress_priv_user4; SET SESSION AUTHORIZATION regress_priv_user4; SELECT * FROM atest6; -- fail SELECT 1 FROM atest6; -- fail SET SESSION AUTHORIZATION regress_priv_user3; DELETE FROM atest5 WHERE one = 1; -- fail DELETE FROM atest5 WHERE two = 2; -- ok -- check inheritance cases SET SESSION AUTHORIZATION regress_priv_user1; CREATE TABLE atestp1 (f1 int, f2 int); CREATE TABLE atestp2 (fx int, fy int); CREATE TABLE atestc (fz int) INHERITS (atestp1, atestp2); GRANT SELECT(fx,fy,tableoid) ON atestp2 TO regress_priv_user2; GRANT SELECT(fx) ON atestc TO regress_priv_user2; SET SESSION AUTHORIZATION regress_priv_user2; SELECT fx FROM atestp2; -- ok SELECT fy FROM atestp2; -- ok SELECT atestp2 FROM atestp2; -- ok SELECT tableoid FROM atestp2; -- ok SELECT fy FROM atestc; -- fail SET SESSION AUTHORIZATION regress_priv_user1; GRANT SELECT(fy,tableoid) ON atestc TO regress_priv_user2; SET SESSION AUTHORIZATION regress_priv_user2; SELECT fx FROM atestp2; -- still ok SELECT fy FROM atestp2; -- ok SELECT atestp2 FROM atestp2; -- ok SELECT tableoid FROM atestp2; -- ok -- privileges on functions, languages -- switch to superuser \c - REVOKE ALL PRIVILEGES ON LANGUAGE sql FROM PUBLIC; GRANT USAGE ON LANGUAGE sql TO regress_priv_user1; -- ok GRANT USAGE ON LANGUAGE c TO PUBLIC; -- fail SET SESSION AUTHORIZATION regress_priv_user1; GRANT USAGE ON LANGUAGE sql TO regress_priv_user2; -- fail CREATE FUNCTION priv_testfunc1(int) RETURNS int AS 'select 2 * $1;' LANGUAGE sql; CREATE FUNCTION priv_testfunc2(int) RETURNS int AS 'select 3 * $1;' LANGUAGE sql; CREATE AGGREGATE priv_testagg1(int) (sfunc = int4pl, stype = int4); CREATE PROCEDURE priv_testproc1(int) AS 'select $1;' LANGUAGE sql; REVOKE ALL ON FUNCTION priv_testfunc1(int), priv_testfunc2(int), priv_testagg1(int) FROM PUBLIC; GRANT EXECUTE ON FUNCTION priv_testfunc1(int), priv_testfunc2(int), priv_testagg1(int) TO regress_priv_user2; REVOKE ALL ON FUNCTION priv_testproc1(int) FROM PUBLIC; -- fail, not a function REVOKE ALL ON PROCEDURE priv_testproc1(int) FROM PUBLIC; GRANT EXECUTE ON PROCEDURE priv_testproc1(int) TO regress_priv_user2; GRANT USAGE ON FUNCTION priv_testfunc1(int) TO regress_priv_user3; -- semantic error GRANT USAGE ON FUNCTION priv_testagg1(int) TO regress_priv_user3; -- semantic error GRANT USAGE ON PROCEDURE priv_testproc1(int) TO regress_priv_user3; -- semantic error GRANT ALL PRIVILEGES ON FUNCTION priv_testfunc1(int) TO regress_priv_user4; GRANT ALL PRIVILEGES ON FUNCTION priv_testfunc_nosuch(int) TO regress_priv_user4; GRANT ALL PRIVILEGES ON FUNCTION priv_testagg1(int) TO regress_priv_user4; GRANT ALL PRIVILEGES ON PROCEDURE priv_testproc1(int) TO regress_priv_user4; CREATE FUNCTION priv_testfunc4(boolean) RETURNS text AS 'select col1 from atest2 where col2 = $1;' LANGUAGE sql SECURITY DEFINER; GRANT EXECUTE ON FUNCTION priv_testfunc4(boolean) TO regress_priv_user3; SET SESSION AUTHORIZATION regress_priv_user2; SELECT priv_testfunc1(5), priv_testfunc2(5); -- ok CREATE FUNCTION priv_testfunc3(int) RETURNS int AS 'select 2 * $1;' LANGUAGE sql; -- fail SELECT priv_testagg1(x) FROM (VALUES (1), (2), (3)) _(x); -- ok CALL priv_testproc1(6); -- ok SET SESSION AUTHORIZATION regress_priv_user3; SELECT priv_testfunc1(5); -- fail SELECT priv_testagg1(x) FROM (VALUES (1), (2), (3)) _(x); -- fail CALL priv_testproc1(6); -- fail SELECT col1 FROM atest2 WHERE col2 = true; -- fail SELECT priv_testfunc4(true); -- ok SET SESSION AUTHORIZATION regress_priv_user4; SELECT priv_testfunc1(5); -- ok SELECT priv_testagg1(x) FROM (VALUES (1), (2), (3)) _(x); -- ok CALL priv_testproc1(6); -- ok DROP FUNCTION priv_testfunc1(int); -- fail DROP AGGREGATE priv_testagg1(int); -- fail DROP PROCEDURE priv_testproc1(int); -- fail \c - DROP FUNCTION priv_testfunc1(int); -- ok -- restore to sanity GRANT ALL PRIVILEGES ON LANGUAGE sql TO PUBLIC; -- verify privilege checks on array-element coercions BEGIN; SELECT '{1}'::int4[]::int8[]; REVOKE ALL ON FUNCTION int8(integer) FROM PUBLIC; SELECT '{1}'::int4[]::int8[]; --superuser, succeed SET SESSION AUTHORIZATION regress_priv_user4; SELECT '{1}'::int4[]::int8[]; --other user, fail ROLLBACK; -- privileges on types -- switch to superuser \c - CREATE TYPE priv_testtype1 AS (a int, b text); REVOKE USAGE ON TYPE priv_testtype1 FROM PUBLIC; GRANT USAGE ON TYPE priv_testtype1 TO regress_priv_user2; GRANT USAGE ON TYPE _priv_testtype1 TO regress_priv_user2; -- fail GRANT USAGE ON DOMAIN priv_testtype1 TO regress_priv_user2; -- fail CREATE DOMAIN priv_testdomain1 AS int; REVOKE USAGE on DOMAIN priv_testdomain1 FROM PUBLIC; GRANT USAGE ON DOMAIN priv_testdomain1 TO regress_priv_user2; GRANT USAGE ON TYPE priv_testdomain1 TO regress_priv_user2; -- ok SET SESSION AUTHORIZATION regress_priv_user1; -- commands that should fail CREATE AGGREGATE priv_testagg1a(priv_testdomain1) (sfunc = int4_sum, stype = bigint); CREATE DOMAIN priv_testdomain2a AS priv_testdomain1; CREATE DOMAIN priv_testdomain3a AS int; CREATE FUNCTION castfunc(int) RETURNS priv_testdomain3a AS $$ SELECT $1::priv_testdomain3a $$ LANGUAGE SQL; CREATE CAST (priv_testdomain1 AS priv_testdomain3a) WITH FUNCTION castfunc(int); DROP FUNCTION castfunc(int) CASCADE; DROP DOMAIN priv_testdomain3a; CREATE FUNCTION priv_testfunc5a(a priv_testdomain1) RETURNS int LANGUAGE SQL AS $$ SELECT $1 $$; CREATE FUNCTION priv_testfunc6a(b int) RETURNS priv_testdomain1 LANGUAGE SQL AS $$ SELECT $1::priv_testdomain1 $$; CREATE OPERATOR !+! (PROCEDURE = int4pl, LEFTARG = priv_testdomain1, RIGHTARG = priv_testdomain1); CREATE TABLE test5a (a int, b priv_testdomain1); CREATE TABLE test6a OF priv_testtype1; CREATE TABLE test10a (a int[], b priv_testtype1[]); CREATE TABLE test9a (a int, b int); ALTER TABLE test9a ADD COLUMN c priv_testdomain1; ALTER TABLE test9a ALTER COLUMN b TYPE priv_testdomain1; CREATE TYPE test7a AS (a int, b priv_testdomain1); CREATE TYPE test8a AS (a int, b int); ALTER TYPE test8a ADD ATTRIBUTE c priv_testdomain1; ALTER TYPE test8a ALTER ATTRIBUTE b TYPE priv_testdomain1; CREATE TABLE test11a AS (SELECT 1::priv_testdomain1 AS a); REVOKE ALL ON TYPE priv_testtype1 FROM PUBLIC; SET SESSION AUTHORIZATION regress_priv_user2; -- commands that should succeed CREATE AGGREGATE priv_testagg1b(priv_testdomain1) (sfunc = int4_sum, stype = bigint); CREATE DOMAIN priv_testdomain2b AS priv_testdomain1; CREATE DOMAIN priv_testdomain3b AS int; CREATE FUNCTION castfunc(int) RETURNS priv_testdomain3b AS $$ SELECT $1::priv_testdomain3b $$ LANGUAGE SQL; CREATE CAST (priv_testdomain1 AS priv_testdomain3b) WITH FUNCTION castfunc(int); CREATE FUNCTION priv_testfunc5b(a priv_testdomain1) RETURNS int LANGUAGE SQL AS $$ SELECT $1 $$; CREATE FUNCTION priv_testfunc6b(b int) RETURNS priv_testdomain1 LANGUAGE SQL AS $$ SELECT $1::priv_testdomain1 $$; CREATE OPERATOR !! (PROCEDURE = priv_testfunc5b, RIGHTARG = priv_testdomain1); CREATE TABLE test5b (a int, b priv_testdomain1); CREATE TABLE test6b OF priv_testtype1; CREATE TABLE test10b (a int[], b priv_testtype1[]); CREATE TABLE test9b (a int, b int); ALTER TABLE test9b ADD COLUMN c priv_testdomain1; ALTER TABLE test9b ALTER COLUMN b TYPE priv_testdomain1; CREATE TYPE test7b AS (a int, b priv_testdomain1); CREATE TYPE test8b AS (a int, b int); ALTER TYPE test8b ADD ATTRIBUTE c priv_testdomain1; ALTER TYPE test8b ALTER ATTRIBUTE b TYPE priv_testdomain1; CREATE TABLE test11b AS (SELECT 1::priv_testdomain1 AS a); REVOKE ALL ON TYPE priv_testtype1 FROM PUBLIC; \c - DROP AGGREGATE priv_testagg1b(priv_testdomain1); DROP DOMAIN priv_testdomain2b; DROP OPERATOR !! (NONE, priv_testdomain1); DROP FUNCTION priv_testfunc5b(a priv_testdomain1); DROP FUNCTION priv_testfunc6b(b int); DROP TABLE test5b; DROP TABLE test6b; DROP TABLE test9b; DROP TABLE test10b; DROP TYPE test7b; DROP TYPE test8b; DROP CAST (priv_testdomain1 AS priv_testdomain3b); DROP FUNCTION castfunc(int) CASCADE; DROP DOMAIN priv_testdomain3b; DROP TABLE test11b; DROP TYPE priv_testtype1; -- ok DROP DOMAIN priv_testdomain1; -- ok -- truncate SET SESSION AUTHORIZATION regress_priv_user5; TRUNCATE atest2; -- ok TRUNCATE atest3; -- fail -- has_table_privilege function -- bad-input checks select has_table_privilege(NULL,'pg_authid','select'); select has_table_privilege('pg_shad','select'); select has_table_privilege('nosuchuser','pg_authid','select'); select has_table_privilege('pg_authid','sel'); select has_table_privilege(-999999,'pg_authid','update'); select has_table_privilege(1,'select'); -- superuser \c - select has_table_privilege(current_user,'pg_authid','select'); select has_table_privilege(current_user,'pg_authid','insert'); select has_table_privilege(t2.oid,'pg_authid','update') from (select oid from pg_roles where rolname = current_user) as t2; select has_table_privilege(t2.oid,'pg_authid','delete') from (select oid from pg_roles where rolname = current_user) as t2; -- 'rule' privilege no longer exists, but for backwards compatibility -- has_table_privilege still recognizes the keyword and says FALSE select has_table_privilege(current_user,t1.oid,'rule') from (select oid from pg_class where relname = 'pg_authid') as t1; select has_table_privilege(current_user,t1.oid,'references') from (select oid from pg_class where relname = 'pg_authid') as t1; select has_table_privilege(t2.oid,t1.oid,'select') from (select oid from pg_class where relname = 'pg_authid') as t1, (select oid from pg_roles where rolname = current_user) as t2; select has_table_privilege(t2.oid,t1.oid,'insert') from (select oid from pg_class where relname = 'pg_authid') as t1, (select oid from pg_roles where rolname = current_user) as t2; select has_table_privilege('pg_authid','update'); select has_table_privilege('pg_authid','delete'); select has_table_privilege('pg_authid','truncate'); select has_table_privilege(t1.oid,'select') from (select oid from pg_class where relname = 'pg_authid') as t1; select has_table_privilege(t1.oid,'trigger') from (select oid from pg_class where relname = 'pg_authid') as t1; -- non-superuser SET SESSION AUTHORIZATION regress_priv_user3; select has_table_privilege(current_user,'pg_class','select'); select has_table_privilege(current_user,'pg_class','insert'); select has_table_privilege(t2.oid,'pg_class','update') from (select oid from pg_roles where rolname = current_user) as t2; select has_table_privilege(t2.oid,'pg_class','delete') from (select oid from pg_roles where rolname = current_user) as t2; select has_table_privilege(current_user,t1.oid,'references') from (select oid from pg_class where relname = 'pg_class') as t1; select has_table_privilege(t2.oid,t1.oid,'select') from (select oid from pg_class where relname = 'pg_class') as t1, (select oid from pg_roles where rolname = current_user) as t2; select has_table_privilege(t2.oid,t1.oid,'insert') from (select oid from pg_class where relname = 'pg_class') as t1, (select oid from pg_roles where rolname = current_user) as t2; select has_table_privilege('pg_class','update'); select has_table_privilege('pg_class','delete'); select has_table_privilege('pg_class','truncate'); select has_table_privilege(t1.oid,'select') from (select oid from pg_class where relname = 'pg_class') as t1; select has_table_privilege(t1.oid,'trigger') from (select oid from pg_class where relname = 'pg_class') as t1; select has_table_privilege(current_user,'atest1','select'); select has_table_privilege(current_user,'atest1','insert'); select has_table_privilege(t2.oid,'atest1','update') from (select oid from pg_roles where rolname = current_user) as t2; select has_table_privilege(t2.oid,'atest1','delete') from (select oid from pg_roles where rolname = current_user) as t2; select has_table_privilege(current_user,t1.oid,'references') from (select oid from pg_class where relname = 'atest1') as t1; select has_table_privilege(t2.oid,t1.oid,'select') from (select oid from pg_class where relname = 'atest1') as t1, (select oid from pg_roles where rolname = current_user) as t2; select has_table_privilege(t2.oid,t1.oid,'insert') from (select oid from pg_class where relname = 'atest1') as t1, (select oid from pg_roles where rolname = current_user) as t2; select has_table_privilege('atest1','update'); select has_table_privilege('atest1','delete'); select has_table_privilege('atest1','truncate'); select has_table_privilege(t1.oid,'select') from (select oid from pg_class where relname = 'atest1') as t1; select has_table_privilege(t1.oid,'trigger') from (select oid from pg_class where relname = 'atest1') as t1; -- has_column_privilege function -- bad-input checks (as non-super-user) select has_column_privilege('pg_authid',NULL,'select'); select has_column_privilege('pg_authid','nosuchcol','select'); select has_column_privilege(9999,'nosuchcol','select'); select has_column_privilege(9999,99::int2,'select'); select has_column_privilege('pg_authid',99::int2,'select'); select has_column_privilege(9999,99::int2,'select'); create temp table mytable(f1 int, f2 int, f3 int); alter table mytable drop column f2; select has_column_privilege('mytable','f2','select'); select has_column_privilege('mytable','........pg.dropped.2........','select'); select has_column_privilege('mytable',2::int2,'select'); revoke select on table mytable from regress_priv_user3; select has_column_privilege('mytable',2::int2,'select'); drop table mytable; -- Grant options SET SESSION AUTHORIZATION regress_priv_user1; CREATE TABLE atest4 (a int); GRANT SELECT ON atest4 TO regress_priv_user2 WITH GRANT OPTION; GRANT UPDATE ON atest4 TO regress_priv_user2; GRANT SELECT ON atest4 TO GROUP regress_priv_group1 WITH GRANT OPTION; SET SESSION AUTHORIZATION regress_priv_user2; GRANT SELECT ON atest4 TO regress_priv_user3; GRANT UPDATE ON atest4 TO regress_priv_user3; -- fail SET SESSION AUTHORIZATION regress_priv_user1; REVOKE SELECT ON atest4 FROM regress_priv_user3; -- does nothing SELECT has_table_privilege('regress_priv_user3', 'atest4', 'SELECT'); -- true REVOKE SELECT ON atest4 FROM regress_priv_user2; -- fail REVOKE GRANT OPTION FOR SELECT ON atest4 FROM regress_priv_user2 CASCADE; -- ok SELECT has_table_privilege('regress_priv_user2', 'atest4', 'SELECT'); -- true SELECT has_table_privilege('regress_priv_user3', 'atest4', 'SELECT'); -- false SELECT has_table_privilege('regress_priv_user1', 'atest4', 'SELECT WITH GRANT OPTION'); -- true -- Admin options SET SESSION AUTHORIZATION regress_priv_user4; CREATE FUNCTION dogrant_ok() RETURNS void LANGUAGE sql SECURITY DEFINER AS 'GRANT regress_priv_group2 TO regress_priv_user5'; GRANT regress_priv_group2 TO regress_priv_user5; -- ok: had ADMIN OPTION SET ROLE regress_priv_group2; GRANT regress_priv_group2 TO regress_priv_user5; -- fails: SET ROLE suspended privilege SET SESSION AUTHORIZATION regress_priv_user1; GRANT regress_priv_group2 TO regress_priv_user5; -- fails: no ADMIN OPTION SELECT dogrant_ok(); -- ok: SECURITY DEFINER conveys ADMIN SET ROLE regress_priv_group2; GRANT regress_priv_group2 TO regress_priv_user5; -- fails: SET ROLE did not help SET SESSION AUTHORIZATION regress_priv_group2; GRANT regress_priv_group2 TO regress_priv_user5; -- ok: a role can self-admin CREATE FUNCTION dogrant_fails() RETURNS void LANGUAGE sql SECURITY DEFINER AS 'GRANT regress_priv_group2 TO regress_priv_user5'; SELECT dogrant_fails(); -- fails: no self-admin in SECURITY DEFINER DROP FUNCTION dogrant_fails(); SET SESSION AUTHORIZATION regress_priv_user4; DROP FUNCTION dogrant_ok(); REVOKE regress_priv_group2 FROM regress_priv_user5; -- has_sequence_privilege tests \c - CREATE SEQUENCE x_seq; GRANT USAGE on x_seq to regress_priv_user2; SELECT has_sequence_privilege('regress_priv_user1', 'atest1', 'SELECT'); SELECT has_sequence_privilege('regress_priv_user1', 'x_seq', 'INSERT'); SELECT has_sequence_privilege('regress_priv_user1', 'x_seq', 'SELECT'); SET SESSION AUTHORIZATION regress_priv_user2; SELECT has_sequence_privilege('x_seq', 'USAGE'); -- largeobject privilege tests \c - SET SESSION AUTHORIZATION regress_priv_user1; SELECT lo_create(1001); SELECT lo_create(1002); SELECT lo_create(1003); SELECT lo_create(1004); SELECT lo_create(1005); GRANT ALL ON LARGE OBJECT 1001 TO PUBLIC; GRANT SELECT ON LARGE OBJECT 1003 TO regress_priv_user2; GRANT SELECT,UPDATE ON LARGE OBJECT 1004 TO regress_priv_user2; GRANT ALL ON LARGE OBJECT 1005 TO regress_priv_user2; GRANT SELECT ON LARGE OBJECT 1005 TO regress_priv_user2 WITH GRANT OPTION; GRANT SELECT, INSERT ON LARGE OBJECT 1001 TO PUBLIC; -- to be failed GRANT SELECT, UPDATE ON LARGE OBJECT 1001 TO nosuchuser; -- to be failed GRANT SELECT, UPDATE ON LARGE OBJECT 999 TO PUBLIC; -- to be failed \c - SET SESSION AUTHORIZATION regress_priv_user2; SELECT lo_create(2001); SELECT lo_create(2002); SELECT loread(lo_open(1001, x'20000'::int), 32); -- allowed, for now SELECT lowrite(lo_open(1001, x'40000'::int), 'abcd'); -- fail, wrong mode SELECT loread(lo_open(1001, x'40000'::int), 32); SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied SELECT loread(lo_open(1003, x'40000'::int), 32); SELECT loread(lo_open(1004, x'40000'::int), 32); SELECT lowrite(lo_open(1001, x'20000'::int), 'abcd'); SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied SELECT lowrite(lo_open(1003, x'20000'::int), 'abcd'); -- to be denied SELECT lowrite(lo_open(1004, x'20000'::int), 'abcd'); GRANT SELECT ON LARGE OBJECT 1005 TO regress_priv_user3; GRANT UPDATE ON LARGE OBJECT 1006 TO regress_priv_user3; -- to be denied REVOKE ALL ON LARGE OBJECT 2001, 2002 FROM PUBLIC; GRANT ALL ON LARGE OBJECT 2001 TO regress_priv_user3; SELECT lo_unlink(1001); -- to be denied SELECT lo_unlink(2002); \c - -- confirm ACL setting SELECT oid, pg_get_userbyid(lomowner) ownername, lomacl FROM pg_largeobject_metadata WHERE oid >= 1000 AND oid < 3000 ORDER BY oid; SET SESSION AUTHORIZATION regress_priv_user3; SELECT loread(lo_open(1001, x'40000'::int), 32); SELECT loread(lo_open(1003, x'40000'::int), 32); -- to be denied SELECT loread(lo_open(1005, x'40000'::int), 32); SELECT lo_truncate(lo_open(1005, x'20000'::int), 10); -- to be denied SELECT lo_truncate(lo_open(2001, x'20000'::int), 10); -- compatibility mode in largeobject permission \c - SET lo_compat_privileges = false; -- default setting SET SESSION AUTHORIZATION regress_priv_user4; SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); -- to be denied SELECT lo_put(1002, 1, 'abcd'); -- to be denied SELECT lo_unlink(1002); -- to be denied SELECT lo_export(1001, '/dev/null'); -- to be denied SELECT lo_import('/dev/null'); -- to be denied SELECT lo_import('/dev/null', 2003); -- to be denied \c - SET lo_compat_privileges = true; -- compatibility mode SET SESSION AUTHORIZATION regress_priv_user4; SELECT loread(lo_open(1002, x'40000'::int), 32); SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); SELECT lo_unlink(1002); SELECT lo_export(1001, '/dev/null'); -- to be denied -- don't allow unpriv users to access pg_largeobject contents \c - SELECT * FROM pg_largeobject LIMIT 0; SET SESSION AUTHORIZATION regress_priv_user1; SELECT * FROM pg_largeobject LIMIT 0; -- to be denied -- test default ACLs \c - CREATE SCHEMA testns; GRANT ALL ON SCHEMA testns TO regress_priv_user1; CREATE TABLE testns.acltest1 (x int); SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'SELECT'); -- no SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'INSERT'); -- no ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT SELECT ON TABLES TO public; SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'SELECT'); -- no SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'INSERT'); -- no DROP TABLE testns.acltest1; CREATE TABLE testns.acltest1 (x int); SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'SELECT'); -- yes SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'INSERT'); -- no ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT INSERT ON TABLES TO regress_priv_user1; DROP TABLE testns.acltest1; CREATE TABLE testns.acltest1 (x int); SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'SELECT'); -- yes SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'INSERT'); -- yes ALTER DEFAULT PRIVILEGES IN SCHEMA testns REVOKE INSERT ON TABLES FROM regress_priv_user1; DROP TABLE testns.acltest1; CREATE TABLE testns.acltest1 (x int); SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'SELECT'); -- yes SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'INSERT'); -- no ALTER DEFAULT PRIVILEGES FOR ROLE regress_priv_user1 REVOKE EXECUTE ON FUNCTIONS FROM public; ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT USAGE ON SCHEMAS TO regress_priv_user2; -- error -- -- Testing blanket default grants is very hazardous since it might change -- the privileges attached to objects created by concurrent regression tests. -- To avoid that, be sure to revoke the privileges again before committing. -- BEGIN; ALTER DEFAULT PRIVILEGES GRANT USAGE ON SCHEMAS TO regress_priv_user2; CREATE SCHEMA testns2; SELECT has_schema_privilege('regress_priv_user2', 'testns2', 'USAGE'); -- yes SELECT has_schema_privilege('regress_priv_user2', 'testns2', 'CREATE'); -- no ALTER DEFAULT PRIVILEGES REVOKE USAGE ON SCHEMAS FROM regress_priv_user2; CREATE SCHEMA testns3; SELECT has_schema_privilege('regress_priv_user2', 'testns3', 'USAGE'); -- no SELECT has_schema_privilege('regress_priv_user2', 'testns3', 'CREATE'); -- no ALTER DEFAULT PRIVILEGES GRANT ALL ON SCHEMAS TO regress_priv_user2; CREATE SCHEMA testns4; SELECT has_schema_privilege('regress_priv_user2', 'testns4', 'USAGE'); -- yes SELECT has_schema_privilege('regress_priv_user2', 'testns4', 'CREATE'); -- yes ALTER DEFAULT PRIVILEGES REVOKE ALL ON SCHEMAS FROM regress_priv_user2; COMMIT; CREATE SCHEMA testns5; SELECT has_schema_privilege('regress_priv_user2', 'testns5', 'USAGE'); -- no SELECT has_schema_privilege('regress_priv_user2', 'testns5', 'CREATE'); -- no SET ROLE regress_priv_user1; CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql; CREATE AGGREGATE testns.agg1(int) (sfunc = int4pl, stype = int4); CREATE PROCEDURE testns.bar() AS 'select 1' LANGUAGE sql; SELECT has_function_privilege('regress_priv_user2', 'testns.foo()', 'EXECUTE'); -- no SELECT has_function_privilege('regress_priv_user2', 'testns.agg1(int)', 'EXECUTE'); -- no SELECT has_function_privilege('regress_priv_user2', 'testns.bar()', 'EXECUTE'); -- no ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT EXECUTE ON ROUTINES to public; DROP FUNCTION testns.foo(); CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql; DROP AGGREGATE testns.agg1(int); CREATE AGGREGATE testns.agg1(int) (sfunc = int4pl, stype = int4); DROP PROCEDURE testns.bar(); CREATE PROCEDURE testns.bar() AS 'select 1' LANGUAGE sql; SELECT has_function_privilege('regress_priv_user2', 'testns.foo()', 'EXECUTE'); -- yes SELECT has_function_privilege('regress_priv_user2', 'testns.agg1(int)', 'EXECUTE'); -- yes SELECT has_function_privilege('regress_priv_user2', 'testns.bar()', 'EXECUTE'); -- yes (counts as function here) DROP FUNCTION testns.foo(); DROP AGGREGATE testns.agg1(int); DROP PROCEDURE testns.bar(); ALTER DEFAULT PRIVILEGES FOR ROLE regress_priv_user1 REVOKE USAGE ON TYPES FROM public; CREATE DOMAIN testns.priv_testdomain1 AS int; SELECT has_type_privilege('regress_priv_user2', 'testns.priv_testdomain1', 'USAGE'); -- no ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT USAGE ON TYPES to public; DROP DOMAIN testns.priv_testdomain1; CREATE DOMAIN testns.priv_testdomain1 AS int; SELECT has_type_privilege('regress_priv_user2', 'testns.priv_testdomain1', 'USAGE'); -- yes DROP DOMAIN testns.priv_testdomain1; RESET ROLE; SELECT count(*) FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid WHERE nspname = 'testns'; DROP SCHEMA testns CASCADE; DROP SCHEMA testns2 CASCADE; DROP SCHEMA testns3 CASCADE; DROP SCHEMA testns4 CASCADE; DROP SCHEMA testns5 CASCADE; SELECT d.* -- check that entries went away FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid WHERE nspname IS NULL AND defaclnamespace != 0; -- Grant on all objects of given type in a schema \c - CREATE SCHEMA testns; CREATE TABLE testns.t1 (f1 int); CREATE TABLE testns.t2 (f1 int); SELECT has_table_privilege('regress_priv_user1', 'testns.t1', 'SELECT'); -- false GRANT ALL ON ALL TABLES IN SCHEMA testns TO regress_priv_user1; SELECT has_table_privilege('regress_priv_user1', 'testns.t1', 'SELECT'); -- true SELECT has_table_privilege('regress_priv_user1', 'testns.t2', 'SELECT'); -- true REVOKE ALL ON ALL TABLES IN SCHEMA testns FROM regress_priv_user1; SELECT has_table_privilege('regress_priv_user1', 'testns.t1', 'SELECT'); -- false SELECT has_table_privilege('regress_priv_user1', 'testns.t2', 'SELECT'); -- false CREATE FUNCTION testns.priv_testfunc(int) RETURNS int AS 'select 3 * $1;' LANGUAGE sql; CREATE AGGREGATE testns.priv_testagg(int) (sfunc = int4pl, stype = int4); CREATE PROCEDURE testns.priv_testproc(int) AS 'select 3' LANGUAGE sql; SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testfunc(int)', 'EXECUTE'); -- true by default SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testagg(int)', 'EXECUTE'); -- true by default SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testproc(int)', 'EXECUTE'); -- true by default REVOKE ALL ON ALL FUNCTIONS IN SCHEMA testns FROM PUBLIC; SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testfunc(int)', 'EXECUTE'); -- false SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testagg(int)', 'EXECUTE'); -- false SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testproc(int)', 'EXECUTE'); -- still true, not a function REVOKE ALL ON ALL PROCEDURES IN SCHEMA testns FROM PUBLIC; SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testproc(int)', 'EXECUTE'); -- now false GRANT ALL ON ALL ROUTINES IN SCHEMA testns TO PUBLIC; SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testfunc(int)', 'EXECUTE'); -- true SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testagg(int)', 'EXECUTE'); -- true SELECT has_function_privilege('regress_priv_user1', 'testns.priv_testproc(int)', 'EXECUTE'); -- true DROP SCHEMA testns CASCADE; -- Change owner of the schema & and rename of new schema owner \c - CREATE ROLE regress_schemauser1 superuser login; CREATE ROLE regress_schemauser2 superuser login; SET SESSION ROLE regress_schemauser1; CREATE SCHEMA testns; SELECT nspname, rolname FROM pg_namespace, pg_roles WHERE pg_namespace.nspname = 'testns' AND pg_namespace.nspowner = pg_roles.oid; ALTER SCHEMA testns OWNER TO regress_schemauser2; ALTER ROLE regress_schemauser2 RENAME TO regress_schemauser_renamed; SELECT nspname, rolname FROM pg_namespace, pg_roles WHERE pg_namespace.nspname = 'testns' AND pg_namespace.nspowner = pg_roles.oid; set session role regress_schemauser_renamed; DROP SCHEMA testns CASCADE; -- clean up \c - DROP ROLE regress_schemauser1; DROP ROLE regress_schemauser_renamed; -- test that dependent privileges are revoked (or not) properly \c - set session role regress_priv_user1; create table dep_priv_test (a int); grant select on dep_priv_test to regress_priv_user2 with grant option; grant select on dep_priv_test to regress_priv_user3 with grant option; set session role regress_priv_user2; grant select on dep_priv_test to regress_priv_user4 with grant option; set session role regress_priv_user3; grant select on dep_priv_test to regress_priv_user4 with grant option; set session role regress_priv_user4; grant select on dep_priv_test to regress_priv_user5; \dp dep_priv_test set session role regress_priv_user2; revoke select on dep_priv_test from regress_priv_user4 cascade; \dp dep_priv_test set session role regress_priv_user3; revoke select on dep_priv_test from regress_priv_user4 cascade; \dp dep_priv_test set session role regress_priv_user1; drop table dep_priv_test; -- clean up \c drop sequence x_seq; DROP AGGREGATE priv_testagg1(int); DROP FUNCTION priv_testfunc2(int); DROP FUNCTION priv_testfunc4(boolean); DROP PROCEDURE priv_testproc1(int); DROP VIEW atestv0; DROP VIEW atestv1; DROP VIEW atestv2; -- this should cascade to drop atestv4 DROP VIEW atestv3 CASCADE; -- this should complain "does not exist" DROP VIEW atestv4; DROP TABLE atest1; DROP TABLE atest2; DROP TABLE atest3; DROP TABLE atest4; DROP TABLE atest5; DROP TABLE atest6; DROP TABLE atestc; DROP TABLE atestp1; DROP TABLE atestp2; SELECT lo_unlink(oid) FROM pg_largeobject_metadata WHERE oid >= 1000 AND oid < 3000 ORDER BY oid; DROP GROUP regress_priv_group1; DROP GROUP regress_priv_group2; -- these are needed to clean up permissions REVOKE USAGE ON LANGUAGE sql FROM regress_priv_user1; DROP OWNED BY regress_priv_user1; DROP USER regress_priv_user1; DROP USER regress_priv_user2; DROP USER regress_priv_user3; DROP USER regress_priv_user4; DROP USER regress_priv_user5; DROP USER regress_priv_user6; -- permissions with LOCK TABLE CREATE USER regress_locktable_user; CREATE TABLE lock_table (a int); -- LOCK TABLE and SELECT permission GRANT SELECT ON lock_table TO regress_locktable_user; SET SESSION AUTHORIZATION regress_locktable_user; BEGIN; LOCK TABLE lock_table IN ROW EXCLUSIVE MODE; -- should fail ROLLBACK; BEGIN; LOCK TABLE lock_table IN ACCESS SHARE MODE; -- should pass COMMIT; BEGIN; LOCK TABLE lock_table IN ACCESS EXCLUSIVE MODE; -- should fail ROLLBACK; \c REVOKE SELECT ON lock_table FROM regress_locktable_user; -- LOCK TABLE and INSERT permission GRANT INSERT ON lock_table TO regress_locktable_user; SET SESSION AUTHORIZATION regress_locktable_user; BEGIN; LOCK TABLE lock_table IN ROW EXCLUSIVE MODE; -- should pass COMMIT; BEGIN; LOCK TABLE lock_table IN ACCESS SHARE MODE; -- should fail ROLLBACK; BEGIN; LOCK TABLE lock_table IN ACCESS EXCLUSIVE MODE; -- should fail ROLLBACK; \c REVOKE INSERT ON lock_table FROM regress_locktable_user; -- LOCK TABLE and UPDATE permission GRANT UPDATE ON lock_table TO regress_locktable_user; SET SESSION AUTHORIZATION regress_locktable_user; BEGIN; LOCK TABLE lock_table IN ROW EXCLUSIVE MODE; -- should pass COMMIT; BEGIN; LOCK TABLE lock_table IN ACCESS SHARE MODE; -- should fail ROLLBACK; BEGIN; LOCK TABLE lock_table IN ACCESS EXCLUSIVE MODE; -- should pass COMMIT; \c REVOKE UPDATE ON lock_table FROM regress_locktable_user; -- LOCK TABLE and DELETE permission GRANT DELETE ON lock_table TO regress_locktable_user; SET SESSION AUTHORIZATION regress_locktable_user; BEGIN; LOCK TABLE lock_table IN ROW EXCLUSIVE MODE; -- should pass COMMIT; BEGIN; LOCK TABLE lock_table IN ACCESS SHARE MODE; -- should fail ROLLBACK; BEGIN; LOCK TABLE lock_table IN ACCESS EXCLUSIVE MODE; -- should pass COMMIT; \c REVOKE DELETE ON lock_table FROM regress_locktable_user; -- LOCK TABLE and TRUNCATE permission GRANT TRUNCATE ON lock_table TO regress_locktable_user; SET SESSION AUTHORIZATION regress_locktable_user; BEGIN; LOCK TABLE lock_table IN ROW EXCLUSIVE MODE; -- should pass COMMIT; BEGIN; LOCK TABLE lock_table IN ACCESS SHARE MODE; -- should fail ROLLBACK; BEGIN; LOCK TABLE lock_table IN ACCESS EXCLUSIVE MODE; -- should pass COMMIT; \c REVOKE TRUNCATE ON lock_table FROM regress_locktable_user; -- clean up DROP TABLE lock_table; DROP USER regress_locktable_user; pgFormatter-4.2/t/pg-test-files/sql/psql.sql000066400000000000000000000513571361326045100211120ustar00rootroot00000000000000-- -- Tests for psql features that aren't closely connected to any -- specific server features -- -- \set -- fail: invalid name \set invalid/name foo -- fail: invalid value for special variable \set AUTOCOMMIT foo \set FETCH_COUNT foo -- check handling of built-in boolean variable \echo :ON_ERROR_ROLLBACK \set ON_ERROR_ROLLBACK \echo :ON_ERROR_ROLLBACK \set ON_ERROR_ROLLBACK foo \echo :ON_ERROR_ROLLBACK \set ON_ERROR_ROLLBACK on \echo :ON_ERROR_ROLLBACK \unset ON_ERROR_ROLLBACK \echo :ON_ERROR_ROLLBACK -- \g and \gx SELECT 1 as one, 2 as two \g \gx SELECT 3 as three, 4 as four \gx \g -- \gx should work in FETCH_COUNT mode too \set FETCH_COUNT 1 SELECT 1 as one, 2 as two \g \gx SELECT 3 as three, 4 as four \gx \g \unset FETCH_COUNT -- \gset select 10 as test01, 20 as test02, 'Hello' as test03 \gset pref01_ \echo :pref01_test01 :pref01_test02 :pref01_test03 -- should fail: bad variable name select 10 as "bad name" \gset -- multiple backslash commands in one line select 1 as x, 2 as y \gset pref01_ \\ \echo :pref01_x select 3 as x, 4 as y \gset pref01_ \echo :pref01_x \echo :pref01_y select 5 as x, 6 as y \gset pref01_ \\ \g \echo :pref01_x :pref01_y select 7 as x, 8 as y \g \gset pref01_ \echo :pref01_x :pref01_y -- NULL should unset the variable \set var2 xyz select 1 as var1, NULL as var2, 3 as var3 \gset \echo :var1 :var2 :var3 -- \gset requires just one tuple select 10 as test01, 20 as test02 from generate_series(1,3) \gset select 10 as test01, 20 as test02 from generate_series(1,0) \gset -- \gset should work in FETCH_COUNT mode too \set FETCH_COUNT 1 select 1 as x, 2 as y \gset pref01_ \\ \echo :pref01_x select 3 as x, 4 as y \gset pref01_ \echo :pref01_x \echo :pref01_y select 10 as test01, 20 as test02 from generate_series(1,3) \gset select 10 as test01, 20 as test02 from generate_series(1,0) \gset \unset FETCH_COUNT -- \gdesc SELECT NULL AS zero, 1 AS one, 2.0 AS two, 'three' AS three, $1 AS four, sin($2) as five, 'foo'::varchar(4) as six, CURRENT_DATE AS now \gdesc -- should work with tuple-returning utilities, such as EXECUTE PREPARE test AS SELECT 1 AS first, 2 AS second; EXECUTE test \gdesc EXPLAIN EXECUTE test \gdesc -- should fail cleanly - syntax error SELECT 1 + \gdesc -- check behavior with empty results SELECT \gdesc CREATE TABLE bububu(a int) \gdesc -- subject command should not have executed TABLE bububu; -- fail -- query buffer should remain unchanged SELECT 1 AS x, 'Hello', 2 AS y, true AS "dirty\name" \gdesc \g -- all on one line SELECT 3 AS x, 'Hello', 4 AS y, true AS "dirty\name" \gdesc \g -- \gexec create temporary table gexec_test(a int, b text, c date, d float); select format('create index on gexec_test(%I)', attname) from pg_attribute where attrelid = 'gexec_test'::regclass and attnum > 0 order by attnum \gexec -- \gexec should work in FETCH_COUNT mode too -- (though the fetch limit applies to the executed queries not the meta query) \set FETCH_COUNT 1 select 'select 1 as ones', 'select x.y, x.y*2 as double from generate_series(1,4) as x(y)' union all select 'drop table gexec_test', NULL union all select 'drop table gexec_test', 'select ''2000-01-01''::date as party_over' \gexec \unset FETCH_COUNT -- show all pset options \pset -- test multi-line headers, wrapping, and newline indicators -- in aligned, unaligned, and wrapped formats prepare q as select array_to_string(array_agg(repeat('x',2*n)),E'\n') as "ab c", array_to_string(array_agg(repeat('y',20-2*n)),E'\n') as "a bc" from generate_series(1,10) as n(n) group by n>1 order by n>1; \pset linestyle ascii \pset expanded off \pset columns 40 \pset border 0 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset border 1 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset border 2 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset expanded on \pset columns 20 \pset border 0 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset border 1 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset border 2 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset linestyle old-ascii \pset expanded off \pset columns 40 \pset border 0 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset border 1 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset border 2 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset expanded on \pset columns 20 \pset border 0 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset border 1 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset border 2 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; deallocate q; -- test single-line header and data prepare q as select repeat('x',2*n) as "0123456789abcdef", repeat('y',20-2*n) as "0123456789" from generate_series(1,10) as n; \pset linestyle ascii \pset expanded off \pset columns 40 \pset border 0 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset border 1 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset border 2 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset expanded on \pset columns 30 \pset border 0 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset border 1 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset border 2 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset expanded on \pset columns 20 \pset border 0 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset border 1 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset border 2 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset linestyle old-ascii \pset expanded off \pset columns 40 \pset border 0 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset border 1 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset border 2 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset expanded on \pset border 0 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset border 1 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; \pset border 2 \pset format unaligned execute q; \pset format aligned execute q; \pset format wrapped execute q; deallocate q; \pset linestyle ascii \pset border 1 -- support table for output-format tests (useful to create a footer) create table psql_serial_tab (id serial); -- test header/footer/tuples_only behavior in aligned/unaligned/wrapped cases \pset format aligned \pset expanded off \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset expanded on \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false -- empty table is a special case for this format select 1 where false; \pset format unaligned \pset expanded off \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset expanded on \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset format wrapped \pset expanded off \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset expanded on \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false -- check conditional tableam display -- Create a heap2 table am handler with heapam handler CREATE ACCESS METHOD heap_psql TYPE TABLE HANDLER heap_tableam_handler; CREATE TABLE tbl_heap_psql(f1 int, f2 char(100)) using heap_psql; CREATE TABLE tbl_heap(f1 int, f2 char(100)) using heap; \d+ tbl_heap_psql \d+ tbl_heap \set HIDE_TABLEAM off \d+ tbl_heap_psql \d+ tbl_heap \set HIDE_TABLEAM on DROP TABLE tbl_heap, tbl_heap_psql; DROP ACCESS METHOD heap_psql; -- test numericlocale (as best we can without control of psql's locale) \pset format aligned \pset expanded off \pset numericlocale true select n, -n as m, n * 111 as x, '1e90'::float8 as f from generate_series(0,3) n; \pset numericlocale false -- test asciidoc output format \pset format asciidoc \pset border 1 \pset expanded off \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset expanded on \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false prepare q as select 'some|text' as "a|title", ' ' as "empty ", n as int from generate_series(1,2) as n; \pset expanded off \pset border 0 execute q; \pset border 1 execute q; \pset border 2 execute q; \pset expanded on \pset border 0 execute q; \pset border 1 execute q; \pset border 2 execute q; deallocate q; -- test csv output format \pset format csv \pset border 1 \pset expanded off \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset expanded on \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false prepare q as select 'some"text' as "a""title", E' \n' as "junk", ' ' as "empty", n as int from generate_series(1,2) as n; \pset expanded off execute q; \pset expanded on execute q; deallocate q; -- special cases \pset expanded off select 'comma,comma' as comma, 'semi;semi' as semi; \pset csv_fieldsep ';' select 'comma,comma' as comma, 'semi;semi' as semi; select '\.' as data; \pset csv_fieldsep '.' select '\' as d1, '' as d2; -- illegal csv separators \pset csv_fieldsep '' \pset csv_fieldsep '\0' \pset csv_fieldsep '\n' \pset csv_fieldsep '\r' \pset csv_fieldsep '"' \pset csv_fieldsep ',,' \pset csv_fieldsep ',' -- test html output format \pset format html \pset border 1 \pset expanded off \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset expanded on \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false prepare q as select 'some"text' as "a&title", E' \n' as "junk", ' ' as "empty", n as int from generate_series(1,2) as n; \pset expanded off \pset border 0 execute q; \pset border 1 execute q; \pset tableattr foobar execute q; \pset tableattr \pset expanded on \pset border 0 execute q; \pset border 1 execute q; \pset tableattr foobar execute q; \pset tableattr deallocate q; -- test latex output format \pset format latex \pset border 1 \pset expanded off \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset expanded on \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false prepare q as select 'some\more_text' as "a$title", E' #%&^~|\n{bar}' as "junk", ' ' as "empty", n as int from generate_series(1,2) as n; \pset expanded off \pset border 0 execute q; \pset border 1 execute q; \pset border 2 execute q; \pset border 3 execute q; \pset expanded on \pset border 0 execute q; \pset border 1 execute q; \pset border 2 execute q; \pset border 3 execute q; deallocate q; -- test latex-longtable output format \pset format latex-longtable \pset border 1 \pset expanded off \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset expanded on \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false prepare q as select 'some\more_text' as "a$title", E' #%&^~|\n{bar}' as "junk", ' ' as "empty", n as int from generate_series(1,2) as n; \pset expanded off \pset border 0 execute q; \pset border 1 execute q; \pset border 2 execute q; \pset border 3 execute q; \pset tableattr lr execute q; \pset tableattr \pset expanded on \pset border 0 execute q; \pset border 1 execute q; \pset border 2 execute q; \pset border 3 execute q; \pset tableattr lr execute q; \pset tableattr deallocate q; -- test troff-ms output format \pset format troff-ms \pset border 1 \pset expanded off \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false \pset expanded on \d psql_serial_tab_id_seq \pset tuples_only true \df exp \pset tuples_only false prepare q as select 'some\text' as "a\title", E' \n' as "junk", ' ' as "empty", n as int from generate_series(1,2) as n; \pset expanded off \pset border 0 execute q; \pset border 1 execute q; \pset border 2 execute q; \pset expanded on \pset border 0 execute q; \pset border 1 execute q; \pset border 2 execute q; deallocate q; -- check ambiguous format requests \pset format a \pset format l -- clean up after output format tests drop table psql_serial_tab; \pset format aligned \pset expanded off \pset border 1 -- tests for \if ... \endif \if true select 'okay'; select 'still okay'; \else not okay; still not okay \endif -- at this point query buffer should still have last valid line \g -- \if should work okay on part of a query select \if true 42 \else (bogus \endif forty_two; select \if false \\ (bogus \else \\ 42 \endif \\ forty_two; -- test a large nested if using a variety of true-equivalents \if true \if 1 \if yes \if on \echo 'all true' \else \echo 'should not print #1-1' \endif \else \echo 'should not print #1-2' \endif \else \echo 'should not print #1-3' \endif \else \echo 'should not print #1-4' \endif -- test a variety of false-equivalents in an if/elif/else structure \if false \echo 'should not print #2-1' \elif 0 \echo 'should not print #2-2' \elif no \echo 'should not print #2-3' \elif off \echo 'should not print #2-4' \else \echo 'all false' \endif -- test simple true-then-else \if true \echo 'first thing true' \else \echo 'should not print #3-1' \endif -- test simple false-true-else \if false \echo 'should not print #4-1' \elif true \echo 'second thing true' \else \echo 'should not print #5-1' \endif -- invalid boolean expressions are false \if invalid boolean expression \echo 'will not print #6-1' \else \echo 'will print anyway #6-2' \endif -- test un-matched endif \endif -- test un-matched else \else -- test un-matched elif \elif -- test double-else error \if true \else \else \endif -- test elif out-of-order \if false \else \elif \endif -- test if-endif matching in a false branch \if false \if false \echo 'should not print #7-1' \else \echo 'should not print #7-2' \endif \echo 'should not print #7-3' \else \echo 'should print #7-4' \endif -- show that vars and backticks are not expanded when ignoring extra args \set foo bar \echo :foo :'foo' :"foo" \pset fieldsep | `nosuchcommand` :foo :'foo' :"foo" -- show that vars and backticks are not expanded and commands are ignored -- when in a false if-branch \set try_to_quit '\\q' \if false :try_to_quit \echo `nosuchcommand` :foo :'foo' :"foo" \pset fieldsep | `nosuchcommand` :foo :'foo' :"foo" \a \C arg1 \c arg1 arg2 arg3 arg4 \cd arg1 \conninfo \copy arg1 arg2 arg3 arg4 arg5 arg6 \copyright \dt arg1 \e arg1 arg2 \ef whole_line \ev whole_line \echo arg1 arg2 arg3 arg4 arg5 \echo arg1 \encoding arg1 \errverbose \g arg1 \gx arg1 \gexec \h \html \i arg1 \ir arg1 \l arg1 \lo arg1 arg2 \o arg1 \p \password arg1 \prompt arg1 arg2 \pset arg1 arg2 \q \reset \s arg1 \set arg1 arg2 arg3 arg4 arg5 arg6 arg7 \setenv arg1 arg2 \sf whole_line \sv whole_line \t arg1 \T arg1 \timing arg1 \unset arg1 \w arg1 \watch arg1 \x arg1 -- \else here is eaten as part of OT_FILEPIPE argument \w |/no/such/file \else -- \endif here is eaten as part of whole-line argument \! whole_line \endif \else \echo 'should print #8-1' \endif -- :{?...} defined variable test \set i 1 \if :{?i} \echo '#9-1 ok, variable i is defined' \else \echo 'should not print #9-2' \endif \if :{?no_such_variable} \echo 'should not print #10-1' \else \echo '#10-2 ok, variable no_such_variable is not defined' \endif SELECT :{?i} AS i_is_defined; SELECT NOT :{?no_such_var} AS no_such_var_is_not_defined; -- SHOW_CONTEXT \set SHOW_CONTEXT never do $$ begin raise notice 'foo'; raise exception 'bar'; end $$; \set SHOW_CONTEXT errors do $$ begin raise notice 'foo'; raise exception 'bar'; end $$; \set SHOW_CONTEXT always do $$ begin raise notice 'foo'; raise exception 'bar'; end $$; -- test printing and clearing the query buffer SELECT 1; \p SELECT 2 \r \p SELECT 3 \p UNION SELECT 4 \p UNION SELECT 5 ORDER BY 1; \r \p -- tests for special result variables -- working query, 2 rows selected SELECT 1 AS stuff UNION SELECT 2; \echo 'error:' :ERROR \echo 'error code:' :SQLSTATE \echo 'number of rows:' :ROW_COUNT -- syntax error SELECT 1 UNION; \echo 'error:' :ERROR \echo 'error code:' :SQLSTATE \echo 'number of rows:' :ROW_COUNT \echo 'last error message:' :LAST_ERROR_MESSAGE \echo 'last error code:' :LAST_ERROR_SQLSTATE -- empty query ; \echo 'error:' :ERROR \echo 'error code:' :SQLSTATE \echo 'number of rows:' :ROW_COUNT -- must have kept previous values \echo 'last error message:' :LAST_ERROR_MESSAGE \echo 'last error code:' :LAST_ERROR_SQLSTATE -- other query error DROP TABLE this_table_does_not_exist; \echo 'error:' :ERROR \echo 'error code:' :SQLSTATE \echo 'number of rows:' :ROW_COUNT \echo 'last error message:' :LAST_ERROR_MESSAGE \echo 'last error code:' :LAST_ERROR_SQLSTATE -- nondefault verbosity error settings (except verbose, which is too unstable) \set VERBOSITY terse SELECT 1 UNION; \echo 'error:' :ERROR \echo 'error code:' :SQLSTATE \echo 'last error message:' :LAST_ERROR_MESSAGE \set VERBOSITY sqlstate SELECT 1/0; \echo 'error:' :ERROR \echo 'error code:' :SQLSTATE \echo 'last error message:' :LAST_ERROR_MESSAGE \set VERBOSITY default -- working \gdesc SELECT 3 AS three, 4 AS four \gdesc \echo 'error:' :ERROR \echo 'error code:' :SQLSTATE \echo 'number of rows:' :ROW_COUNT -- \gdesc with an error SELECT 4 AS \gdesc \echo 'error:' :ERROR \echo 'error code:' :SQLSTATE \echo 'number of rows:' :ROW_COUNT \echo 'last error message:' :LAST_ERROR_MESSAGE \echo 'last error code:' :LAST_ERROR_SQLSTATE -- check row count for a cursor-fetched query \set FETCH_COUNT 10 select unique2 from tenk1 order by unique2 limit 19; \echo 'error:' :ERROR \echo 'error code:' :SQLSTATE \echo 'number of rows:' :ROW_COUNT -- cursor-fetched query with an error after the first group select 1/(15-unique2) from tenk1 order by unique2 limit 19; \echo 'error:' :ERROR \echo 'error code:' :SQLSTATE \echo 'number of rows:' :ROW_COUNT \echo 'last error message:' :LAST_ERROR_MESSAGE \echo 'last error code:' :LAST_ERROR_SQLSTATE \unset FETCH_COUNT create schema testpart; create role testrole_partitioning; alter schema testpart owner to testrole_partitioning; set role to testrole_partitioning; -- run test inside own schema and hide other partitions set search_path to testpart; create table testtable_apple(logdate date); create table testtable_orange(logdate date); create index testtable_apple_index on testtable_apple(logdate); create index testtable_orange_index on testtable_orange(logdate); create table testpart_apple(logdate date) partition by range(logdate); create table testpart_orange(logdate date) partition by range(logdate); create index testpart_apple_index on testpart_apple(logdate); create index testpart_orange_index on testpart_orange(logdate); -- only partition related object should be displayed \dP test*apple* \dPt test*apple* \dPi test*apple* drop table testtable_apple; drop table testtable_orange; drop table testpart_apple; drop table testpart_orange; create table parent_tab (id int) partition by range (id); create index parent_index on parent_tab (id); create table child_0_10 partition of parent_tab for values from (0) to (10); create table child_10_20 partition of parent_tab for values from (10) to (20); create table child_20_30 partition of parent_tab for values from (20) to (30); insert into parent_tab values (generate_series(0,29)); create table child_30_40 partition of parent_tab for values from (30) to (40) partition by range(id); create table child_30_35 partition of child_30_40 for values from (30) to (35); create table child_35_40 partition of child_30_40 for values from (35) to (40); insert into parent_tab values (generate_series(30,39)); \dPt \dPi \dP testpart.* \dP \dPtn \dPin \dPn \dPn testpart.* drop table parent_tab cascade; drop schema testpart; set search_path to default; set role to default; drop role testrole_partitioning; pgFormatter-4.2/t/pg-test-files/sql/psql_crosstab.sql000066400000000000000000000071461361326045100230070ustar00rootroot00000000000000-- -- \crosstabview -- CREATE TABLE ctv_data (v, h, c, i, d) AS VALUES ('v1','h2','foo', 3, '2015-04-01'::date), ('v2','h1','bar', 3, '2015-01-02'), ('v1','h0','baz', NULL, '2015-07-12'), ('v0','h4','qux', 4, '2015-07-15'), ('v0','h4','dbl', -3, '2014-12-15'), ('v0',NULL,'qux', 5, '2014-07-15'), ('v1','h2','quux',7, '2015-04-04'); -- make plans more stable ANALYZE ctv_data; -- running \crosstabview after query uses query in buffer SELECT v, EXTRACT(year FROM d), count(*) FROM ctv_data GROUP BY 1, 2 ORDER BY 1, 2; -- basic usage with 3 columns \crosstabview -- ordered months in horizontal header, quoted column name SELECT v, to_char(d, 'Mon') AS "month name", EXTRACT(month FROM d) AS num, count(*) FROM ctv_data GROUP BY 1,2,3 ORDER BY 1 \crosstabview v "month name" 4 num -- ordered months in vertical header, ordered years in horizontal header SELECT EXTRACT(year FROM d) AS year, to_char(d,'Mon') AS """month"" name", EXTRACT(month FROM d) AS month, format('sum=%s avg=%s', sum(i), avg(i)::numeric(2,1)) FROM ctv_data GROUP BY EXTRACT(year FROM d), to_char(d,'Mon'), EXTRACT(month FROM d) ORDER BY month \crosstabview """month"" name" year format year -- combine contents vertically into the same cell (V/H duplicates) SELECT v, h, string_agg(c, E'\n') FROM ctv_data GROUP BY v, h ORDER BY 1,2,3 \crosstabview 1 2 3 -- horizontal ASC order from window function SELECT v,h, string_agg(c, E'\n') AS c, row_number() OVER(ORDER BY h) AS r FROM ctv_data GROUP BY v, h ORDER BY 1,3,2 \crosstabview v h c r -- horizontal DESC order from window function SELECT v, h, string_agg(c, E'\n') AS c, row_number() OVER(ORDER BY h DESC) AS r FROM ctv_data GROUP BY v, h ORDER BY 1,3,2 \crosstabview v h c r -- horizontal ASC order from window function, NULLs pushed rightmost SELECT v,h, string_agg(c, E'\n') AS c, row_number() OVER(ORDER BY h NULLS LAST) AS r FROM ctv_data GROUP BY v, h ORDER BY 1,3,2 \crosstabview v h c r -- only null, no column name, 2 columns: error SELECT null,null \crosstabview -- only null, no column name, 3 columns: works SELECT null,null,null \crosstabview -- null display \pset null '#null#' SELECT v,h, string_agg(i::text, E'\n') AS i FROM ctv_data GROUP BY v, h ORDER BY h,v \crosstabview v h i \pset null '' -- refer to columns by position SELECT v,h,string_agg(i::text, E'\n'), string_agg(c, E'\n') FROM ctv_data GROUP BY v, h ORDER BY h,v \crosstabview 2 1 4 -- refer to columns by positions and names mixed SELECT v,h, string_agg(i::text, E'\n') AS i, string_agg(c, E'\n') AS c FROM ctv_data GROUP BY v, h ORDER BY h,v \crosstabview 1 "h" 4 -- refer to columns by quoted names, check downcasing of unquoted name SELECT 1 as "22", 2 as b, 3 as "Foo" \crosstabview "22" B "Foo" -- error: bad column name SELECT v,h,c,i FROM ctv_data \crosstabview v h j -- error: need to quote name SELECT 1 as "22", 2 as b, 3 as "Foo" \crosstabview 1 2 Foo -- error: need to not quote name SELECT 1 as "22", 2 as b, 3 as "Foo" \crosstabview 1 "B" "Foo" -- error: bad column number SELECT v,h,i,c FROM ctv_data \crosstabview 2 1 5 -- error: same H and V columns SELECT v,h,i,c FROM ctv_data \crosstabview 2 h 4 -- error: too many columns SELECT a,a,1 FROM generate_series(1,3000) AS a \crosstabview -- error: only one column SELECT 1 \crosstabview DROP TABLE ctv_data; -- check error reporting (bug #14476) CREATE TABLE ctv_data (x int, y int, v text); INSERT INTO ctv_data SELECT 1, x, '*' || x FROM generate_series(1,10) x; SELECT * FROM ctv_data \crosstabview INSERT INTO ctv_data VALUES (1, 10, '*'); -- duplicate data to cause error SELECT * FROM ctv_data \crosstabview DROP TABLE ctv_data; pgFormatter-4.2/t/pg-test-files/sql/publication.sql000066400000000000000000000106751361326045100224420ustar00rootroot00000000000000-- -- PUBLICATION -- CREATE ROLE regress_publication_user LOGIN SUPERUSER; CREATE ROLE regress_publication_user2; CREATE ROLE regress_publication_user_dummy LOGIN NOSUPERUSER; SET SESSION AUTHORIZATION 'regress_publication_user'; CREATE PUBLICATION testpub_default; COMMENT ON PUBLICATION testpub_default IS 'test publication'; SELECT obj_description(p.oid, 'pg_publication') FROM pg_publication p; CREATE PUBLICATION testpib_ins_trunct WITH (publish = insert); ALTER PUBLICATION testpub_default SET (publish = update); -- error cases CREATE PUBLICATION testpub_xxx WITH (foo); CREATE PUBLICATION testpub_xxx WITH (publish = 'cluster, vacuum'); \dRp ALTER PUBLICATION testpub_default SET (publish = 'insert, update, delete'); \dRp --- adding tables CREATE SCHEMA pub_test; CREATE TABLE testpub_tbl1 (id serial primary key, data text); CREATE TABLE pub_test.testpub_nopk (foo int, bar int); CREATE VIEW testpub_view AS SELECT 1; CREATE TABLE testpub_parted (a int) PARTITION BY LIST (a); CREATE PUBLICATION testpub_foralltables FOR ALL TABLES WITH (publish = 'insert'); ALTER PUBLICATION testpub_foralltables SET (publish = 'insert, update'); CREATE TABLE testpub_tbl2 (id serial primary key, data text); -- fail - can't add to for all tables publication ALTER PUBLICATION testpub_foralltables ADD TABLE testpub_tbl2; -- fail - can't drop from all tables publication ALTER PUBLICATION testpub_foralltables DROP TABLE testpub_tbl2; -- fail - can't add to for all tables publication ALTER PUBLICATION testpub_foralltables SET TABLE pub_test.testpub_nopk; SELECT pubname, puballtables FROM pg_publication WHERE pubname = 'testpub_foralltables'; \d+ testpub_tbl2 \dRp+ testpub_foralltables DROP TABLE testpub_tbl2; DROP PUBLICATION testpub_foralltables; CREATE TABLE testpub_tbl3 (a int); CREATE TABLE testpub_tbl3a (b text) INHERITS (testpub_tbl3); CREATE PUBLICATION testpub3 FOR TABLE testpub_tbl3; CREATE PUBLICATION testpub4 FOR TABLE ONLY testpub_tbl3; \dRp+ testpub3 \dRp+ testpub4 DROP TABLE testpub_tbl3, testpub_tbl3a; DROP PUBLICATION testpub3, testpub4; -- fail - view CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_view; CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_tbl1, pub_test.testpub_nopk; -- fail - already added ALTER PUBLICATION testpub_fortbl ADD TABLE testpub_tbl1; -- fail - already added CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_tbl1; \dRp+ testpub_fortbl -- fail - view ALTER PUBLICATION testpub_default ADD TABLE testpub_view; -- fail - partitioned table ALTER PUBLICATION testpub_fortbl ADD TABLE testpub_parted; ALTER PUBLICATION testpub_default ADD TABLE testpub_tbl1; ALTER PUBLICATION testpub_default SET TABLE testpub_tbl1; ALTER PUBLICATION testpub_default ADD TABLE pub_test.testpub_nopk; ALTER PUBLICATION testpib_ins_trunct ADD TABLE pub_test.testpub_nopk, testpub_tbl1; \d+ pub_test.testpub_nopk \d+ testpub_tbl1 \dRp+ testpub_default ALTER PUBLICATION testpub_default DROP TABLE testpub_tbl1, pub_test.testpub_nopk; -- fail - nonexistent ALTER PUBLICATION testpub_default DROP TABLE pub_test.testpub_nopk; \d+ testpub_tbl1 -- permissions SET ROLE regress_publication_user2; CREATE PUBLICATION testpub2; -- fail SET ROLE regress_publication_user; GRANT CREATE ON DATABASE regression TO regress_publication_user2; SET ROLE regress_publication_user2; CREATE PUBLICATION testpub2; -- ok ALTER PUBLICATION testpub2 ADD TABLE testpub_tbl1; -- fail SET ROLE regress_publication_user; GRANT regress_publication_user TO regress_publication_user2; SET ROLE regress_publication_user2; ALTER PUBLICATION testpub2 ADD TABLE testpub_tbl1; -- ok DROP PUBLICATION testpub2; SET ROLE regress_publication_user; REVOKE CREATE ON DATABASE regression FROM regress_publication_user2; DROP TABLE testpub_parted; DROP VIEW testpub_view; DROP TABLE testpub_tbl1; \dRp+ testpub_default -- fail - must be owner of publication SET ROLE regress_publication_user_dummy; ALTER PUBLICATION testpub_default RENAME TO testpub_dummy; RESET ROLE; ALTER PUBLICATION testpub_default RENAME TO testpub_foo; \dRp testpub_foo -- rename back to keep the rest simple ALTER PUBLICATION testpub_foo RENAME TO testpub_default; ALTER PUBLICATION testpub_default OWNER TO regress_publication_user2; \dRp testpub_default DROP PUBLICATION testpub_default; DROP PUBLICATION testpib_ins_trunct; DROP PUBLICATION testpub_fortbl; DROP SCHEMA pub_test CASCADE; RESET SESSION AUTHORIZATION; DROP ROLE regress_publication_user, regress_publication_user2; DROP ROLE regress_publication_user_dummy; pgFormatter-4.2/t/pg-test-files/sql/random.sql000066400000000000000000000021511361326045100213770ustar00rootroot00000000000000-- -- RANDOM -- Test the random function -- -- count the number of tuples originally, should be 1000 SELECT count(*) FROM onek; -- pick three random rows, they shouldn't match (SELECT unique1 AS random FROM onek ORDER BY random() LIMIT 1) INTERSECT (SELECT unique1 AS random FROM onek ORDER BY random() LIMIT 1) INTERSECT (SELECT unique1 AS random FROM onek ORDER BY random() LIMIT 1); -- count roughly 1/10 of the tuples SELECT count(*) AS random INTO RANDOM_TBL FROM onek WHERE random() < 1.0/10; -- select again, the count should be different INSERT INTO RANDOM_TBL (random) SELECT count(*) FROM onek WHERE random() < 1.0/10; -- select again, the count should be different INSERT INTO RANDOM_TBL (random) SELECT count(*) FROM onek WHERE random() < 1.0/10; -- select again, the count should be different INSERT INTO RANDOM_TBL (random) SELECT count(*) FROM onek WHERE random() < 1.0/10; -- now test that they are different counts SELECT random, count(random) FROM RANDOM_TBL GROUP BY random HAVING count(random) > 3; SELECT AVG(random) FROM RANDOM_TBL HAVING AVG(random) NOT BETWEEN 80 AND 120; pgFormatter-4.2/t/pg-test-files/sql/rangefuncs.sql000066400000000000000000000657501361326045100222700ustar00rootroot00000000000000CREATE TABLE rngfunc2(rngfuncid int, f2 int); INSERT INTO rngfunc2 VALUES(1, 11); INSERT INTO rngfunc2 VALUES(2, 22); INSERT INTO rngfunc2 VALUES(1, 111); CREATE FUNCTION rngfunct(int) returns setof rngfunc2 as 'SELECT * FROM rngfunc2 WHERE rngfuncid = $1 ORDER BY f2;' LANGUAGE SQL; -- function with ORDINALITY select * from rngfunct(1) with ordinality as z(a,b,ord); select * from rngfunct(1) with ordinality as z(a,b,ord) where b > 100; -- ordinal 2, not 1 -- ordinality vs. column names and types select a,b,ord from rngfunct(1) with ordinality as z(a,b,ord); select a,ord from unnest(array['a','b']) with ordinality as z(a,ord); select * from unnest(array['a','b']) with ordinality as z(a,ord); select a,ord from unnest(array[1.0::float8]) with ordinality as z(a,ord); select * from unnest(array[1.0::float8]) with ordinality as z(a,ord); select row_to_json(s.*) from generate_series(11,14) with ordinality s; -- ordinality vs. views create temporary view vw_ord as select * from (values (1)) v(n) join rngfunct(1) with ordinality as z(a,b,ord) on (n=ord); select * from vw_ord; select definition from pg_views where viewname='vw_ord'; drop view vw_ord; -- multiple functions select * from rows from(rngfunct(1),rngfunct(2)) with ordinality as z(a,b,c,d,ord); create temporary view vw_ord as select * from (values (1)) v(n) join rows from(rngfunct(1),rngfunct(2)) with ordinality as z(a,b,c,d,ord) on (n=ord); select * from vw_ord; select definition from pg_views where viewname='vw_ord'; drop view vw_ord; -- expansions of unnest() select * from unnest(array[10,20],array['foo','bar'],array[1.0]); select * from unnest(array[10,20],array['foo','bar'],array[1.0]) with ordinality as z(a,b,c,ord); select * from rows from(unnest(array[10,20],array['foo','bar'],array[1.0])) with ordinality as z(a,b,c,ord); select * from rows from(unnest(array[10,20],array['foo','bar']), generate_series(101,102)) with ordinality as z(a,b,c,ord); create temporary view vw_ord as select * from unnest(array[10,20],array['foo','bar'],array[1.0]) as z(a,b,c); select * from vw_ord; select definition from pg_views where viewname='vw_ord'; drop view vw_ord; create temporary view vw_ord as select * from rows from(unnest(array[10,20],array['foo','bar'],array[1.0])) as z(a,b,c); select * from vw_ord; select definition from pg_views where viewname='vw_ord'; drop view vw_ord; create temporary view vw_ord as select * from rows from(unnest(array[10,20],array['foo','bar']), generate_series(1,2)) as z(a,b,c); select * from vw_ord; select definition from pg_views where viewname='vw_ord'; drop view vw_ord; -- ordinality and multiple functions vs. rewind and reverse scan begin; declare rf_cur scroll cursor for select * from rows from(generate_series(1,5),generate_series(1,2)) with ordinality as g(i,j,o); fetch all from rf_cur; fetch backward all from rf_cur; fetch all from rf_cur; fetch next from rf_cur; fetch next from rf_cur; fetch prior from rf_cur; fetch absolute 1 from rf_cur; fetch next from rf_cur; fetch next from rf_cur; fetch next from rf_cur; fetch prior from rf_cur; fetch prior from rf_cur; fetch prior from rf_cur; commit; -- function with implicit LATERAL select * from rngfunc2, rngfunct(rngfunc2.rngfuncid) z where rngfunc2.f2 = z.f2; -- function with implicit LATERAL and explicit ORDINALITY select * from rngfunc2, rngfunct(rngfunc2.rngfuncid) with ordinality as z(rngfuncid,f2,ord) where rngfunc2.f2 = z.f2; -- function in subselect select * from rngfunc2 where f2 in (select f2 from rngfunct(rngfunc2.rngfuncid) z where z.rngfuncid = rngfunc2.rngfuncid) ORDER BY 1,2; -- function in subselect select * from rngfunc2 where f2 in (select f2 from rngfunct(1) z where z.rngfuncid = rngfunc2.rngfuncid) ORDER BY 1,2; -- function in subselect select * from rngfunc2 where f2 in (select f2 from rngfunct(rngfunc2.rngfuncid) z where z.rngfuncid = 1) ORDER BY 1,2; -- nested functions select rngfunct.rngfuncid, rngfunct.f2 from rngfunct(sin(pi()/2)::int) ORDER BY 1,2; CREATE TABLE rngfunc (rngfuncid int, rngfuncsubid int, rngfuncname text, primary key(rngfuncid,rngfuncsubid)); INSERT INTO rngfunc VALUES(1,1,'Joe'); INSERT INTO rngfunc VALUES(1,2,'Ed'); INSERT INTO rngfunc VALUES(2,1,'Mary'); -- sql, proretset = f, prorettype = b CREATE FUNCTION getrngfunc1(int) RETURNS int AS 'SELECT $1;' LANGUAGE SQL; SELECT * FROM getrngfunc1(1) AS t1; SELECT * FROM getrngfunc1(1) WITH ORDINALITY AS t1(v,o); CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc1(1); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc1(1) WITH ORDINALITY as t1(v,o); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; -- sql, proretset = t, prorettype = b CREATE FUNCTION getrngfunc2(int) RETURNS setof int AS 'SELECT rngfuncid FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; SELECT * FROM getrngfunc2(1) AS t1; SELECT * FROM getrngfunc2(1) WITH ORDINALITY AS t1(v,o); CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc2(1); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc2(1) WITH ORDINALITY AS t1(v,o); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; -- sql, proretset = t, prorettype = b CREATE FUNCTION getrngfunc3(int) RETURNS setof text AS 'SELECT rngfuncname FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; SELECT * FROM getrngfunc3(1) AS t1; SELECT * FROM getrngfunc3(1) WITH ORDINALITY AS t1(v,o); CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc3(1); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc3(1) WITH ORDINALITY AS t1(v,o); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; -- sql, proretset = f, prorettype = c CREATE FUNCTION getrngfunc4(int) RETURNS rngfunc AS 'SELECT * FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; SELECT * FROM getrngfunc4(1) AS t1; SELECT * FROM getrngfunc4(1) WITH ORDINALITY AS t1(a,b,c,o); CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc4(1); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc4(1) WITH ORDINALITY AS t1(a,b,c,o); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; -- sql, proretset = t, prorettype = c CREATE FUNCTION getrngfunc5(int) RETURNS setof rngfunc AS 'SELECT * FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; SELECT * FROM getrngfunc5(1) AS t1; SELECT * FROM getrngfunc5(1) WITH ORDINALITY AS t1(a,b,c,o); CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc5(1); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc5(1) WITH ORDINALITY AS t1(a,b,c,o); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; -- sql, proretset = f, prorettype = record CREATE FUNCTION getrngfunc6(int) RETURNS RECORD AS 'SELECT * FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; SELECT * FROM getrngfunc6(1) AS t1(rngfuncid int, rngfuncsubid int, rngfuncname text); SELECT * FROM ROWS FROM( getrngfunc6(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text) ) WITH ORDINALITY; CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc6(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; CREATE VIEW vw_getrngfunc AS SELECT * FROM ROWS FROM( getrngfunc6(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text) ) WITH ORDINALITY; SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; -- sql, proretset = t, prorettype = record CREATE FUNCTION getrngfunc7(int) RETURNS setof record AS 'SELECT * FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; SELECT * FROM getrngfunc7(1) AS t1(rngfuncid int, rngfuncsubid int, rngfuncname text); SELECT * FROM ROWS FROM( getrngfunc7(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text) ) WITH ORDINALITY; CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc7(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; CREATE VIEW vw_getrngfunc AS SELECT * FROM ROWS FROM( getrngfunc7(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text) ) WITH ORDINALITY; SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; -- plpgsql, proretset = f, prorettype = b CREATE FUNCTION getrngfunc8(int) RETURNS int AS 'DECLARE rngfuncint int; BEGIN SELECT rngfuncid into rngfuncint FROM rngfunc WHERE rngfuncid = $1; RETURN rngfuncint; END;' LANGUAGE plpgsql; SELECT * FROM getrngfunc8(1) AS t1; SELECT * FROM getrngfunc8(1) WITH ORDINALITY AS t1(v,o); CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc8(1); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc8(1) WITH ORDINALITY AS t1(v,o); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; -- plpgsql, proretset = f, prorettype = c CREATE FUNCTION getrngfunc9(int) RETURNS rngfunc AS 'DECLARE rngfunctup rngfunc%ROWTYPE; BEGIN SELECT * into rngfunctup FROM rngfunc WHERE rngfuncid = $1; RETURN rngfunctup; END;' LANGUAGE plpgsql; SELECT * FROM getrngfunc9(1) AS t1; SELECT * FROM getrngfunc9(1) WITH ORDINALITY AS t1(a,b,c,o); CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc9(1); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc9(1) WITH ORDINALITY AS t1(a,b,c,o); SELECT * FROM vw_getrngfunc; DROP VIEW vw_getrngfunc; -- mix 'n match kinds, to exercise expandRTE and related logic select * from rows from(getrngfunc1(1),getrngfunc2(1),getrngfunc3(1),getrngfunc4(1),getrngfunc5(1), getrngfunc6(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text), getrngfunc7(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text), getrngfunc8(1),getrngfunc9(1)) with ordinality as t1(a,b,c,d,e,f,g,h,i,j,k,l,m,o,p,q,r,s,t,u); select * from rows from(getrngfunc9(1),getrngfunc8(1), getrngfunc7(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text), getrngfunc6(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text), getrngfunc5(1),getrngfunc4(1),getrngfunc3(1),getrngfunc2(1),getrngfunc1(1)) with ordinality as t1(a,b,c,d,e,f,g,h,i,j,k,l,m,o,p,q,r,s,t,u); create temporary view vw_rngfunc as select * from rows from(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text), getrngfunc1(1)) with ordinality as t1(a,b,c,d,e,f,g,n); select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); drop view vw_rngfunc; DROP FUNCTION getrngfunc1(int); DROP FUNCTION getrngfunc2(int); DROP FUNCTION getrngfunc3(int); DROP FUNCTION getrngfunc4(int); DROP FUNCTION getrngfunc5(int); DROP FUNCTION getrngfunc6(int); DROP FUNCTION getrngfunc7(int); DROP FUNCTION getrngfunc8(int); DROP FUNCTION getrngfunc9(int); DROP FUNCTION rngfunct(int); DROP TABLE rngfunc2; DROP TABLE rngfunc; -- Rescan tests -- CREATE TEMPORARY SEQUENCE rngfunc_rescan_seq1; CREATE TEMPORARY SEQUENCE rngfunc_rescan_seq2; CREATE TYPE rngfunc_rescan_t AS (i integer, s bigint); CREATE FUNCTION rngfunc_sql(int,int) RETURNS setof rngfunc_rescan_t AS 'SELECT i, nextval(''rngfunc_rescan_seq1'') FROM generate_series($1,$2) i;' LANGUAGE SQL; -- plpgsql functions use materialize mode CREATE FUNCTION rngfunc_mat(int,int) RETURNS setof rngfunc_rescan_t AS 'begin for i in $1..$2 loop return next (i, nextval(''rngfunc_rescan_seq2'')); end loop; end;' LANGUAGE plpgsql; --invokes ExecReScanFunctionScan - all these cases should materialize the function only once -- LEFT JOIN on a condition that the planner can't prove to be true is used to ensure the function -- is on the inner path of a nestloop join SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN rngfunc_sql(11,13) ON (r+i)<100; SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN rngfunc_sql(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100; SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN rngfunc_mat(11,13) ON (r+i)<100; SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN rngfunc_mat(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100; SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN ROWS FROM( rngfunc_sql(11,13), rngfunc_mat(11,13) ) WITH ORDINALITY AS f(i1,s1,i2,s2,o) ON (r+i1+i2)<100; SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN generate_series(11,13) f(i) ON (r+i)<100; SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN generate_series(11,13) WITH ORDINALITY AS f(i,o) ON (r+i)<100; SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN unnest(array[10,20,30]) f(i) ON (r+i)<100; SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN unnest(array[10,20,30]) WITH ORDINALITY AS f(i,o) ON (r+i)<100; --invokes ExecReScanFunctionScan with chgParam != NULL (using implied LATERAL) SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_sql(10+r,13); SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_sql(10+r,13) WITH ORDINALITY AS f(i,s,o); SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_sql(11,10+r); SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_sql(11,10+r) WITH ORDINALITY AS f(i,s,o); SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), rngfunc_sql(r1,r2); SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), rngfunc_sql(r1,r2) WITH ORDINALITY AS f(i,s,o); SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_mat(10+r,13); SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_mat(10+r,13) WITH ORDINALITY AS f(i,s,o); SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_mat(11,10+r); SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_mat(11,10+r) WITH ORDINALITY AS f(i,s,o); SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), rngfunc_mat(r1,r2); SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), rngfunc_mat(r1,r2) WITH ORDINALITY AS f(i,s,o); -- selective rescan of multiple functions: SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( rngfunc_sql(11,11), rngfunc_mat(10+r,13) ); SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( rngfunc_sql(10+r,13), rngfunc_mat(11,11) ); SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( rngfunc_sql(10+r,13), rngfunc_mat(10+r,13) ); SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); SELECT * FROM generate_series(1,2) r1, generate_series(r1,3) r2, ROWS FROM( rngfunc_sql(10+r1,13), rngfunc_mat(10+r2,13) ); SELECT * FROM (VALUES (1),(2),(3)) v(r), generate_series(10+r,20-r) f(i); SELECT * FROM (VALUES (1),(2),(3)) v(r), generate_series(10+r,20-r) WITH ORDINALITY AS f(i,o); SELECT * FROM (VALUES (1),(2),(3)) v(r), unnest(array[r*10,r*20,r*30]) f(i); SELECT * FROM (VALUES (1),(2),(3)) v(r), unnest(array[r*10,r*20,r*30]) WITH ORDINALITY AS f(i,o); -- deep nesting SELECT * FROM (VALUES (1),(2),(3)) v1(r1), LATERAL (SELECT r1, * FROM (VALUES (10),(20),(30)) v2(r2) LEFT JOIN generate_series(21,23) f(i) ON ((r2+i)<100) OFFSET 0) s1; SELECT * FROM (VALUES (1),(2),(3)) v1(r1), LATERAL (SELECT r1, * FROM (VALUES (10),(20),(30)) v2(r2) LEFT JOIN generate_series(20+r1,23) f(i) ON ((r2+i)<100) OFFSET 0) s1; SELECT * FROM (VALUES (1),(2),(3)) v1(r1), LATERAL (SELECT r1, * FROM (VALUES (10),(20),(30)) v2(r2) LEFT JOIN generate_series(r2,r2+3) f(i) ON ((r2+i)<100) OFFSET 0) s1; SELECT * FROM (VALUES (1),(2),(3)) v1(r1), LATERAL (SELECT r1, * FROM (VALUES (10),(20),(30)) v2(r2) LEFT JOIN generate_series(r1,2+r2/5) f(i) ON ((r2+i)<100) OFFSET 0) s1; -- check handling of FULL JOIN with multiple lateral references (bug #15741) SELECT * FROM (VALUES (1),(2)) v1(r1) LEFT JOIN LATERAL ( SELECT * FROM generate_series(1, v1.r1) AS gs1 LEFT JOIN LATERAL ( SELECT * FROM generate_series(1, gs1) AS gs2 LEFT JOIN generate_series(1, gs2) AS gs3 ON TRUE ) AS ss1 ON TRUE FULL JOIN generate_series(1, v1.r1) AS gs4 ON FALSE ) AS ss0 ON TRUE; DROP FUNCTION rngfunc_sql(int,int); DROP FUNCTION rngfunc_mat(int,int); DROP SEQUENCE rngfunc_rescan_seq1; DROP SEQUENCE rngfunc_rescan_seq2; -- -- Test cases involving OUT parameters -- CREATE FUNCTION rngfunc(in f1 int, out f2 int) AS 'select $1+1' LANGUAGE sql; SELECT rngfunc(42); SELECT * FROM rngfunc(42); SELECT * FROM rngfunc(42) AS p(x); -- explicit spec of return type is OK CREATE OR REPLACE FUNCTION rngfunc(in f1 int, out f2 int) RETURNS int AS 'select $1+1' LANGUAGE sql; -- error, wrong result type CREATE OR REPLACE FUNCTION rngfunc(in f1 int, out f2 int) RETURNS float AS 'select $1+1' LANGUAGE sql; -- with multiple OUT params you must get a RECORD result CREATE OR REPLACE FUNCTION rngfunc(in f1 int, out f2 int, out f3 text) RETURNS int AS 'select $1+1' LANGUAGE sql; CREATE OR REPLACE FUNCTION rngfunc(in f1 int, out f2 int, out f3 text) RETURNS record AS 'select $1+1' LANGUAGE sql; CREATE OR REPLACE FUNCTION rngfuncr(in f1 int, out f2 int, out text) AS $$select $1-1, $1::text || 'z'$$ LANGUAGE sql; SELECT f1, rngfuncr(f1) FROM int4_tbl; SELECT * FROM rngfuncr(42); SELECT * FROM rngfuncr(42) AS p(a,b); CREATE OR REPLACE FUNCTION rngfuncb(in f1 int, inout f2 int, out text) AS $$select $2-1, $1::text || 'z'$$ LANGUAGE sql; SELECT f1, rngfuncb(f1, f1/2) FROM int4_tbl; SELECT * FROM rngfuncb(42, 99); SELECT * FROM rngfuncb(42, 99) AS p(a,b); -- Can reference function with or without OUT params for DROP, etc DROP FUNCTION rngfunc(int); DROP FUNCTION rngfuncr(in f2 int, out f1 int, out text); DROP FUNCTION rngfuncb(in f1 int, inout f2 int); -- -- For my next trick, polymorphic OUT parameters -- CREATE FUNCTION dup (f1 anyelement, f2 out anyelement, f3 out anyarray) AS 'select $1, array[$1,$1]' LANGUAGE sql; SELECT dup(22); SELECT dup('xyz'); -- fails SELECT dup('xyz'::text); SELECT * FROM dup('xyz'::text); -- fails, as we are attempting to rename first argument CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray) AS 'select $1, array[$1,$1]' LANGUAGE sql; DROP FUNCTION dup(anyelement); -- equivalent behavior, though different name exposed for input arg CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray) AS 'select $1, array[$1,$1]' LANGUAGE sql; SELECT dup(22); DROP FUNCTION dup(anyelement); -- fails, no way to deduce outputs CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray) AS 'select $1, array[$1,$1]' LANGUAGE sql; -- -- table functions -- CREATE OR REPLACE FUNCTION rngfunc() RETURNS TABLE(a int) AS $$ SELECT a FROM generate_series(1,5) a(a) $$ LANGUAGE sql; SELECT * FROM rngfunc(); DROP FUNCTION rngfunc(); CREATE OR REPLACE FUNCTION rngfunc(int) RETURNS TABLE(a int, b int) AS $$ SELECT a, b FROM generate_series(1,$1) a(a), generate_series(1,$1) b(b) $$ LANGUAGE sql; SELECT * FROM rngfunc(3); DROP FUNCTION rngfunc(int); -- case that causes change of typmod knowledge during inlining CREATE OR REPLACE FUNCTION rngfunc() RETURNS TABLE(a varchar(5)) AS $$ SELECT 'hello'::varchar(5) $$ LANGUAGE sql STABLE; SELECT * FROM rngfunc() GROUP BY 1; DROP FUNCTION rngfunc(); -- -- some tests on SQL functions with RETURNING -- create temp table tt(f1 serial, data text); create function insert_tt(text) returns int as $$ insert into tt(data) values($1) returning f1 $$ language sql; select insert_tt('foo'); select insert_tt('bar'); select * from tt; -- insert will execute to completion even if function needs just 1 row create or replace function insert_tt(text) returns int as $$ insert into tt(data) values($1),($1||$1) returning f1 $$ language sql; select insert_tt('fool'); select * from tt; -- setof does what's expected create or replace function insert_tt2(text,text) returns setof int as $$ insert into tt(data) values($1),($2) returning f1 $$ language sql; select insert_tt2('foolish','barrish'); select * from insert_tt2('baz','quux'); select * from tt; -- limit doesn't prevent execution to completion select insert_tt2('foolish','barrish') limit 1; select * from tt; -- triggers will fire, too create function noticetrigger() returns trigger as $$ begin raise notice 'noticetrigger % %', new.f1, new.data; return null; end $$ language plpgsql; create trigger tnoticetrigger after insert on tt for each row execute procedure noticetrigger(); select insert_tt2('foolme','barme') limit 1; select * from tt; -- and rules work create temp table tt_log(f1 int, data text); create rule insert_tt_rule as on insert to tt do also insert into tt_log values(new.*); select insert_tt2('foollog','barlog') limit 1; select * from tt; -- note that nextval() gets executed a second time in the rule expansion, -- which is expected. select * from tt_log; -- test case for a whole-row-variable bug create function rngfunc1(n integer, out a text, out b text) returns setof record language sql as $$ select 'foo ' || i, 'bar ' || i from generate_series(1,$1) i $$; set work_mem='64kB'; select t.a, t, t.a from rngfunc1(10000) t limit 1; reset work_mem; select t.a, t, t.a from rngfunc1(10000) t limit 1; drop function rngfunc1(n integer); -- test use of SQL functions returning record -- this is supported in some cases where the query doesn't specify -- the actual record type ... create function array_to_set(anyarray) returns setof record as $$ select i AS "index", $1[i] AS "value" from generate_subscripts($1, 1) i $$ language sql strict immutable; select array_to_set(array['one', 'two']); select * from array_to_set(array['one', 'two']) as t(f1 int,f2 text); select * from array_to_set(array['one', 'two']); -- fail create temp table rngfunc(f1 int8, f2 int8); create function testrngfunc() returns record as $$ insert into rngfunc values (1,2) returning *; $$ language sql; select testrngfunc(); select * from testrngfunc() as t(f1 int8,f2 int8); select * from testrngfunc(); -- fail drop function testrngfunc(); create function testrngfunc() returns setof record as $$ insert into rngfunc values (1,2), (3,4) returning *; $$ language sql; select testrngfunc(); select * from testrngfunc() as t(f1 int8,f2 int8); select * from testrngfunc(); -- fail drop function testrngfunc(); -- -- Check some cases involving added/dropped columns in a rowtype result -- create temp table users (userid text, seq int, email text, todrop bool, moredrop int, enabled bool); insert into users values ('id',1,'email',true,11,true); insert into users values ('id2',2,'email2',true,12,true); alter table users drop column todrop; create or replace function get_first_user() returns users as $$ SELECT * FROM users ORDER BY userid LIMIT 1; $$ language sql stable; SELECT get_first_user(); SELECT * FROM get_first_user(); create or replace function get_users() returns setof users as $$ SELECT * FROM users ORDER BY userid; $$ language sql stable; SELECT get_users(); SELECT * FROM get_users(); SELECT * FROM get_users() WITH ORDINALITY; -- make sure ordinality copes -- multiple functions vs. dropped columns SELECT * FROM ROWS FROM(generate_series(10,11), get_users()) WITH ORDINALITY; SELECT * FROM ROWS FROM(get_users(), generate_series(10,11)) WITH ORDINALITY; -- check that we can cope with post-parsing changes in rowtypes create temp view usersview as SELECT * FROM ROWS FROM(get_users(), generate_series(10,11)) WITH ORDINALITY; select * from usersview; alter table users add column junk text; select * from usersview; begin; alter table users drop column moredrop; select * from usersview; -- expect clean failure rollback; alter table users alter column seq type numeric; select * from usersview; -- expect clean failure drop view usersview; drop function get_first_user(); drop function get_users(); drop table users; -- this won't get inlined because of type coercion, but it shouldn't fail create or replace function rngfuncbar() returns setof text as $$ select 'foo'::varchar union all select 'bar'::varchar ; $$ language sql stable; select rngfuncbar(); select * from rngfuncbar(); drop function rngfuncbar(); -- check handling of a SQL function with multiple OUT params (bug #5777) create or replace function rngfuncbar(out integer, out numeric) as $$ select (1, 2.1) $$ language sql; select * from rngfuncbar(); create or replace function rngfuncbar(out integer, out numeric) as $$ select (1, 2) $$ language sql; select * from rngfuncbar(); -- fail create or replace function rngfuncbar(out integer, out numeric) as $$ select (1, 2.1, 3) $$ language sql; select * from rngfuncbar(); -- fail drop function rngfuncbar(); -- check whole-row-Var handling in nested lateral functions (bug #11703) create function extractq2(t int8_tbl) returns int8 as $$ select t.q2 $$ language sql immutable; explain (verbose, costs off) select x from int8_tbl, extractq2(int8_tbl) f(x); select x from int8_tbl, extractq2(int8_tbl) f(x); create function extractq2_2(t int8_tbl) returns table(ret1 int8) as $$ select extractq2(t) offset 0 $$ language sql immutable; explain (verbose, costs off) select x from int8_tbl, extractq2_2(int8_tbl) f(x); select x from int8_tbl, extractq2_2(int8_tbl) f(x); -- without the "offset 0", this function gets optimized quite differently create function extractq2_2_opt(t int8_tbl) returns table(ret1 int8) as $$ select extractq2(t) $$ language sql immutable; explain (verbose, costs off) select x from int8_tbl, extractq2_2_opt(int8_tbl) f(x); select x from int8_tbl, extractq2_2_opt(int8_tbl) f(x); -- check handling of nulls in SRF results (bug #7808) create type rngfunc2 as (a integer, b text); select *, row_to_json(u) from unnest(array[(1,'foo')::rngfunc2, null::rngfunc2]) u; select *, row_to_json(u) from unnest(array[null::rngfunc2, null::rngfunc2]) u; select *, row_to_json(u) from unnest(array[null::rngfunc2, (1,'foo')::rngfunc2, null::rngfunc2]) u; select *, row_to_json(u) from unnest(array[]::rngfunc2[]) u; drop type rngfunc2; pgFormatter-4.2/t/pg-test-files/sql/rangetypes.sql000066400000000000000000000456701361326045100223150ustar00rootroot00000000000000-- Tests for range data types. create type textrange as range (subtype=text, collation="C"); -- -- test input parser -- -- negative tests; should fail select ''::textrange; select '-[a,z)'::textrange; select '[a,z) - '::textrange; select '(",a)'::textrange; select '(,,a)'::textrange; select '(),a)'::textrange; select '(a,))'::textrange; select '(],a)'::textrange; select '(a,])'::textrange; select '[z,a]'::textrange; -- should succeed select ' empty '::textrange; select ' ( empty, empty ) '::textrange; select ' ( " a " " a ", " z " " z " ) '::textrange; select '(,z)'::textrange; select '(a,)'::textrange; select '[,z]'::textrange; select '[a,]'::textrange; select '(,)'::textrange; select '[ , ]'::textrange; select '["",""]'::textrange; select '[",",","]'::textrange; select '["\\","\\"]'::textrange; select '(\\,a)'::textrange; select '((,z)'::textrange; select '([,z)'::textrange; select '(!,()'::textrange; select '(!,[)'::textrange; select '[a,a]'::textrange; -- these are allowed but normalize to empty: select '[a,a)'::textrange; select '(a,a]'::textrange; select '(a,a)'::textrange; -- -- create some test data and test the operators -- CREATE TABLE numrange_test (nr NUMRANGE); create index numrange_test_btree on numrange_test(nr); INSERT INTO numrange_test VALUES('[,)'); INSERT INTO numrange_test VALUES('[3,]'); INSERT INTO numrange_test VALUES('[, 5)'); INSERT INTO numrange_test VALUES(numrange(1.1, 2.2)); INSERT INTO numrange_test VALUES('empty'); INSERT INTO numrange_test VALUES(numrange(1.7, 1.7, '[]')); SELECT nr, isempty(nr), lower(nr), upper(nr) FROM numrange_test; SELECT nr, lower_inc(nr), lower_inf(nr), upper_inc(nr), upper_inf(nr) FROM numrange_test; SELECT * FROM numrange_test WHERE range_contains(nr, numrange(1.9,1.91)); SELECT * FROM numrange_test WHERE nr @> numrange(1.0,10000.1); SELECT * FROM numrange_test WHERE range_contained_by(numrange(-1e7,-10000.1), nr); SELECT * FROM numrange_test WHERE 1.9 <@ nr; select * from numrange_test where nr = 'empty'; select * from numrange_test where nr = '(1.1, 2.2)'; select * from numrange_test where nr = '[1.1, 2.2)'; select * from numrange_test where nr < 'empty'; select * from numrange_test where nr < numrange(-1000.0, -1000.0,'[]'); select * from numrange_test where nr < numrange(0.0, 1.0,'[]'); select * from numrange_test where nr < numrange(1000.0, 1001.0,'[]'); select * from numrange_test where nr <= 'empty'; select * from numrange_test where nr >= 'empty'; select * from numrange_test where nr > 'empty'; select * from numrange_test where nr > numrange(-1001.0, -1000.0,'[]'); select * from numrange_test where nr > numrange(0.0, 1.0,'[]'); select * from numrange_test where nr > numrange(1000.0, 1000.0,'[]'); select numrange(2.0, 1.0); select numrange(2.0, 3.0) -|- numrange(3.0, 4.0); select range_adjacent(numrange(2.0, 3.0), numrange(3.1, 4.0)); select range_adjacent(numrange(2.0, 3.0), numrange(3.1, null)); select numrange(2.0, 3.0, '[]') -|- numrange(3.0, 4.0, '()'); select numrange(1.0, 2.0) -|- numrange(2.0, 3.0,'[]'); select range_adjacent(numrange(2.0, 3.0, '(]'), numrange(1.0, 2.0, '(]')); select numrange(1.1, 3.3) <@ numrange(0.1,10.1); select numrange(0.1, 10.1) <@ numrange(1.1,3.3); select numrange(1.1, 2.2) - numrange(2.0, 3.0); select numrange(1.1, 2.2) - numrange(2.2, 3.0); select numrange(1.1, 2.2,'[]') - numrange(2.0, 3.0); select range_minus(numrange(10.1,12.2,'[]'), numrange(110.0,120.2,'(]')); select range_minus(numrange(10.1,12.2,'[]'), numrange(0.0,120.2,'(]')); select numrange(4.5, 5.5, '[]') && numrange(5.5, 6.5); select numrange(1.0, 2.0) << numrange(3.0, 4.0); select numrange(1.0, 3.0,'[]') << numrange(3.0, 4.0,'[]'); select numrange(1.0, 3.0,'()') << numrange(3.0, 4.0,'()'); select numrange(1.0, 2.0) >> numrange(3.0, 4.0); select numrange(3.0, 70.0) &< numrange(6.6, 100.0); select numrange(1.1, 2.2) < numrange(1.0, 200.2); select numrange(1.1, 2.2) < numrange(1.1, 1.2); select numrange(1.0, 2.0) + numrange(2.0, 3.0); select numrange(1.0, 2.0) + numrange(1.5, 3.0); select numrange(1.0, 2.0) + numrange(2.5, 3.0); -- should fail select range_merge(numrange(1.0, 2.0), numrange(2.0, 3.0)); select range_merge(numrange(1.0, 2.0), numrange(1.5, 3.0)); select range_merge(numrange(1.0, 2.0), numrange(2.5, 3.0)); -- shouldn't fail select numrange(1.0, 2.0) * numrange(2.0, 3.0); select numrange(1.0, 2.0) * numrange(1.5, 3.0); select numrange(1.0, 2.0) * numrange(2.5, 3.0); create table numrange_test2(nr numrange); create index numrange_test2_hash_idx on numrange_test2 (nr); INSERT INTO numrange_test2 VALUES('[, 5)'); INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2)); INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2)); INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2,'()')); INSERT INTO numrange_test2 VALUES('empty'); select * from numrange_test2 where nr = 'empty'::numrange; select * from numrange_test2 where nr = numrange(1.1, 2.2); select * from numrange_test2 where nr = numrange(1.1, 2.3); set enable_nestloop=t; set enable_hashjoin=f; set enable_mergejoin=f; select * from numrange_test natural join numrange_test2 order by nr; set enable_nestloop=f; set enable_hashjoin=t; set enable_mergejoin=f; select * from numrange_test natural join numrange_test2 order by nr; set enable_nestloop=f; set enable_hashjoin=f; set enable_mergejoin=t; select * from numrange_test natural join numrange_test2 order by nr; set enable_nestloop to default; set enable_hashjoin to default; set enable_mergejoin to default; DROP TABLE numrange_test; DROP TABLE numrange_test2; -- test canonical form for int4range select int4range(1, 10, '[]'); select int4range(1, 10, '[)'); select int4range(1, 10, '(]'); select int4range(1, 10, '()'); select int4range(1, 2, '()'); -- test canonical form for daterange select daterange('2000-01-10'::date, '2000-01-20'::date, '[]'); select daterange('2000-01-10'::date, '2000-01-20'::date, '[)'); select daterange('2000-01-10'::date, '2000-01-20'::date, '(]'); select daterange('2000-01-10'::date, '2000-01-20'::date, '()'); select daterange('2000-01-10'::date, '2000-01-11'::date, '()'); select daterange('2000-01-10'::date, '2000-01-11'::date, '(]'); -- test GiST index that's been built incrementally create table test_range_gist(ir int4range); create index test_range_gist_idx on test_range_gist using gist (ir); insert into test_range_gist select int4range(g, g+10) from generate_series(1,2000) g; insert into test_range_gist select 'empty'::int4range from generate_series(1,500) g; insert into test_range_gist select int4range(g, g+10000) from generate_series(1,1000) g; insert into test_range_gist select 'empty'::int4range from generate_series(1,500) g; insert into test_range_gist select int4range(NULL,g*10,'(]') from generate_series(1,100) g; insert into test_range_gist select int4range(g*10,NULL,'(]') from generate_series(1,100) g; insert into test_range_gist select int4range(g, g+10) from generate_series(1,2000) g; -- first, verify non-indexed results SET enable_seqscan = t; SET enable_indexscan = f; SET enable_bitmapscan = f; select count(*) from test_range_gist where ir @> 'empty'::int4range; select count(*) from test_range_gist where ir = int4range(10,20); select count(*) from test_range_gist where ir @> 10; select count(*) from test_range_gist where ir @> int4range(10,20); select count(*) from test_range_gist where ir && int4range(10,20); select count(*) from test_range_gist where ir <@ int4range(10,50); select count(*) from test_range_gist where ir << int4range(100,500); select count(*) from test_range_gist where ir >> int4range(100,500); select count(*) from test_range_gist where ir &< int4range(100,500); select count(*) from test_range_gist where ir &> int4range(100,500); select count(*) from test_range_gist where ir -|- int4range(100,500); -- now check same queries using index SET enable_seqscan = f; SET enable_indexscan = t; SET enable_bitmapscan = f; select count(*) from test_range_gist where ir @> 'empty'::int4range; select count(*) from test_range_gist where ir = int4range(10,20); select count(*) from test_range_gist where ir @> 10; select count(*) from test_range_gist where ir @> int4range(10,20); select count(*) from test_range_gist where ir && int4range(10,20); select count(*) from test_range_gist where ir <@ int4range(10,50); select count(*) from test_range_gist where ir << int4range(100,500); select count(*) from test_range_gist where ir >> int4range(100,500); select count(*) from test_range_gist where ir &< int4range(100,500); select count(*) from test_range_gist where ir &> int4range(100,500); select count(*) from test_range_gist where ir -|- int4range(100,500); -- now check same queries using a bulk-loaded index drop index test_range_gist_idx; create index test_range_gist_idx on test_range_gist using gist (ir); select count(*) from test_range_gist where ir @> 'empty'::int4range; select count(*) from test_range_gist where ir = int4range(10,20); select count(*) from test_range_gist where ir @> 10; select count(*) from test_range_gist where ir @> int4range(10,20); select count(*) from test_range_gist where ir && int4range(10,20); select count(*) from test_range_gist where ir <@ int4range(10,50); select count(*) from test_range_gist where ir << int4range(100,500); select count(*) from test_range_gist where ir >> int4range(100,500); select count(*) from test_range_gist where ir &< int4range(100,500); select count(*) from test_range_gist where ir &> int4range(100,500); select count(*) from test_range_gist where ir -|- int4range(100,500); -- test SP-GiST index that's been built incrementally create table test_range_spgist(ir int4range); create index test_range_spgist_idx on test_range_spgist using spgist (ir); insert into test_range_spgist select int4range(g, g+10) from generate_series(1,2000) g; insert into test_range_spgist select 'empty'::int4range from generate_series(1,500) g; insert into test_range_spgist select int4range(g, g+10000) from generate_series(1,1000) g; insert into test_range_spgist select 'empty'::int4range from generate_series(1,500) g; insert into test_range_spgist select int4range(NULL,g*10,'(]') from generate_series(1,100) g; insert into test_range_spgist select int4range(g*10,NULL,'(]') from generate_series(1,100) g; insert into test_range_spgist select int4range(g, g+10) from generate_series(1,2000) g; -- first, verify non-indexed results SET enable_seqscan = t; SET enable_indexscan = f; SET enable_bitmapscan = f; select count(*) from test_range_spgist where ir @> 'empty'::int4range; select count(*) from test_range_spgist where ir = int4range(10,20); select count(*) from test_range_spgist where ir @> 10; select count(*) from test_range_spgist where ir @> int4range(10,20); select count(*) from test_range_spgist where ir && int4range(10,20); select count(*) from test_range_spgist where ir <@ int4range(10,50); select count(*) from test_range_spgist where ir << int4range(100,500); select count(*) from test_range_spgist where ir >> int4range(100,500); select count(*) from test_range_spgist where ir &< int4range(100,500); select count(*) from test_range_spgist where ir &> int4range(100,500); select count(*) from test_range_spgist where ir -|- int4range(100,500); -- now check same queries using index SET enable_seqscan = f; SET enable_indexscan = t; SET enable_bitmapscan = f; select count(*) from test_range_spgist where ir @> 'empty'::int4range; select count(*) from test_range_spgist where ir = int4range(10,20); select count(*) from test_range_spgist where ir @> 10; select count(*) from test_range_spgist where ir @> int4range(10,20); select count(*) from test_range_spgist where ir && int4range(10,20); select count(*) from test_range_spgist where ir <@ int4range(10,50); select count(*) from test_range_spgist where ir << int4range(100,500); select count(*) from test_range_spgist where ir >> int4range(100,500); select count(*) from test_range_spgist where ir &< int4range(100,500); select count(*) from test_range_spgist where ir &> int4range(100,500); select count(*) from test_range_spgist where ir -|- int4range(100,500); -- now check same queries using a bulk-loaded index drop index test_range_spgist_idx; create index test_range_spgist_idx on test_range_spgist using spgist (ir); select count(*) from test_range_spgist where ir @> 'empty'::int4range; select count(*) from test_range_spgist where ir = int4range(10,20); select count(*) from test_range_spgist where ir @> 10; select count(*) from test_range_spgist where ir @> int4range(10,20); select count(*) from test_range_spgist where ir && int4range(10,20); select count(*) from test_range_spgist where ir <@ int4range(10,50); select count(*) from test_range_spgist where ir << int4range(100,500); select count(*) from test_range_spgist where ir >> int4range(100,500); select count(*) from test_range_spgist where ir &< int4range(100,500); select count(*) from test_range_spgist where ir &> int4range(100,500); select count(*) from test_range_spgist where ir -|- int4range(100,500); -- test index-only scans explain (costs off) select ir from test_range_spgist where ir -|- int4range(10,20) order by ir; select ir from test_range_spgist where ir -|- int4range(10,20) order by ir; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; -- test elem <@ range operator create table test_range_elem(i int4); create index test_range_elem_idx on test_range_elem (i); insert into test_range_elem select i from generate_series(1,100) i; select count(*) from test_range_elem where i <@ int4range(10,50); drop table test_range_elem; -- -- Btree_gist is not included by default, so to test exclusion -- constraints with range types, use singleton int ranges for the "=" -- portion of the constraint. -- create table test_range_excl( room int4range, speaker int4range, during tsrange, exclude using gist (room with =, during with &&), exclude using gist (speaker with =, during with &&) ); insert into test_range_excl values(int4range(123, 123, '[]'), int4range(1, 1, '[]'), '[2010-01-02 10:00, 2010-01-02 11:00)'); insert into test_range_excl values(int4range(123, 123, '[]'), int4range(2, 2, '[]'), '[2010-01-02 11:00, 2010-01-02 12:00)'); insert into test_range_excl values(int4range(123, 123, '[]'), int4range(3, 3, '[]'), '[2010-01-02 10:10, 2010-01-02 11:00)'); insert into test_range_excl values(int4range(124, 124, '[]'), int4range(3, 3, '[]'), '[2010-01-02 10:10, 2010-01-02 11:10)'); insert into test_range_excl values(int4range(125, 125, '[]'), int4range(1, 1, '[]'), '[2010-01-02 10:10, 2010-01-02 11:00)'); -- test bigint ranges select int8range(10000000000::int8, 20000000000::int8,'(]'); -- test tstz ranges set timezone to '-08'; select '[2010-01-01 01:00:00 -05, 2010-01-01 02:00:00 -08)'::tstzrange; -- should fail select '[2010-01-01 01:00:00 -08, 2010-01-01 02:00:00 -05)'::tstzrange; set timezone to default; -- -- Test user-defined range of floats -- --should fail create type float8range as range (subtype=float8, subtype_diff=float4mi); --should succeed create type float8range as range (subtype=float8, subtype_diff=float8mi); select '[123.001, 5.e9)'::float8range @> 888.882::float8; create table float8range_test(f8r float8range, i int); insert into float8range_test values(float8range(-100.00007, '1.111113e9'), 42); select * from float8range_test; drop table float8range_test; -- -- Test range types over domains -- create domain mydomain as int4; create type mydomainrange as range(subtype=mydomain); select '[4,50)'::mydomainrange @> 7::mydomain; drop domain mydomain; -- fail drop domain mydomain cascade; -- -- Test domains over range types -- create domain restrictedrange as int4range check (upper(value) < 10); select '[4,5)'::restrictedrange @> 7; select '[4,50)'::restrictedrange @> 7; -- should fail drop domain restrictedrange; -- -- Test multiple range types over the same subtype -- create type textrange1 as range(subtype=text, collation="C"); create type textrange2 as range(subtype=text, collation="C"); select textrange1('a','Z') @> 'b'::text; select textrange2('a','z') @> 'b'::text; drop type textrange1; drop type textrange2; -- -- Test polymorphic type system -- create function anyarray_anyrange_func(a anyarray, r anyrange) returns anyelement as 'select $1[1] + lower($2);' language sql; select anyarray_anyrange_func(ARRAY[1,2], int4range(10,20)); -- should fail select anyarray_anyrange_func(ARRAY[1,2], numrange(10,20)); drop function anyarray_anyrange_func(anyarray, anyrange); -- should fail create function bogus_func(anyelement) returns anyrange as 'select int4range(1,10)' language sql; -- should fail create function bogus_func(int) returns anyrange as 'select int4range(1,10)' language sql; create function range_add_bounds(anyrange) returns anyelement as 'select lower($1) + upper($1)' language sql; select range_add_bounds(int4range(1, 17)); select range_add_bounds(numrange(1.0001, 123.123)); create function rangetypes_sql(q anyrange, b anyarray, out c anyelement) as $$ select upper($1) + $2[1] $$ language sql; select rangetypes_sql(int4range(1,10), ARRAY[2,20]); select rangetypes_sql(numrange(1,10), ARRAY[2,20]); -- match failure -- -- Arrays of ranges -- select ARRAY[numrange(1.1, 1.2), numrange(12.3, 155.5)]; create table i8r_array (f1 int, f2 int8range[]); insert into i8r_array values (42, array[int8range(1,10), int8range(2,20)]); select * from i8r_array; drop table i8r_array; -- -- Ranges of arrays -- create type arrayrange as range (subtype=int4[]); select arrayrange(ARRAY[1,2], ARRAY[2,1]); select arrayrange(ARRAY[2,1], ARRAY[1,2]); -- fail select array[1,1] <@ arrayrange(array[1,2], array[2,1]); select array[1,3] <@ arrayrange(array[1,2], array[2,1]); -- -- Ranges of composites -- create type two_ints as (a int, b int); create type two_ints_range as range (subtype = two_ints); -- with force_parallel_mode on, this exercises tqueue.c's range remapping select *, row_to_json(upper(t)) as u from (values (two_ints_range(row(1,2), row(3,4))), (two_ints_range(row(5,6), row(7,8)))) v(t); drop type two_ints cascade; -- -- Check behavior when subtype lacks a hash function -- create type cashrange as range (subtype = money); set enable_sort = off; -- try to make it pick a hash setop implementation select '(2,5)'::cashrange except select '(5,6)'::cashrange; reset enable_sort; -- -- OUT/INOUT/TABLE functions -- create function outparam_succeed(i anyrange, out r anyrange, out t text) as $$ select $1, 'foo'::text $$ language sql; select * from outparam_succeed(int4range(1,2)); create function inoutparam_succeed(out i anyelement, inout r anyrange) as $$ select upper($1), $1 $$ language sql; select * from inoutparam_succeed(int4range(1,2)); create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange) as $$ select $1, $2 $$ language sql; select * from table_succeed(123, int4range(1,11)); -- should fail create function outparam_fail(i anyelement, out r anyrange, out t text) as $$ select '[1,10]', 'foo' $$ language sql; --should fail create function inoutparam_fail(inout i anyelement, out r anyrange) as $$ select $1, '[1,10]' $$ language sql; --should fail create function table_fail(i anyelement) returns table(i anyelement, r anyrange) as $$ select $1, '[1,10]' $$ language sql; pgFormatter-4.2/t/pg-test-files/sql/regex.linux.utf8.sql000066400000000000000000000033041361326045100232550ustar00rootroot00000000000000/* * This test is for Linux/glibc systems and others that implement proper * locale classification of Unicode characters with high code values. * It must be run in a database with UTF8 encoding and a Unicode-aware locale. */ SET client_encoding TO UTF8; -- -- Test the "high colormap" logic with single characters and ranges that -- exceed the MAX_SIMPLE_CHR cutoff, here assumed to be less than U+2000. -- -- trivial cases: SELECT 'aⓐ' ~ U&'a\24D0' AS t; SELECT 'aⓐ' ~ U&'a\24D1' AS f; SELECT 'aⓕ' ~ 'a[ⓐ-ⓩ]' AS t; SELECT 'aⒻ' ~ 'a[ⓐ-ⓩ]' AS f; -- cases requiring splitting of ranges: SELECT 'aⓕⓕ' ~ 'aⓕ[ⓐ-ⓩ]' AS t; SELECT 'aⓕⓐ' ~ 'aⓕ[ⓐ-ⓩ]' AS t; SELECT 'aⓐⓕ' ~ 'aⓕ[ⓐ-ⓩ]' AS f; SELECT 'aⓕⓕ' ~ 'a[ⓐ-ⓩ]ⓕ' AS t; SELECT 'aⓕⓐ' ~ 'a[ⓐ-ⓩ]ⓕ' AS f; SELECT 'aⓐⓕ' ~ 'a[ⓐ-ⓩ]ⓕ' AS t; SELECT 'aⒶⓜ' ~ 'a[Ⓐ-ⓜ][ⓜ-ⓩ]' AS t; SELECT 'aⓜⓜ' ~ 'a[Ⓐ-ⓜ][ⓜ-ⓩ]' AS t; SELECT 'aⓜⓩ' ~ 'a[Ⓐ-ⓜ][ⓜ-ⓩ]' AS t; SELECT 'aⓩⓩ' ~ 'a[Ⓐ-ⓜ][ⓜ-ⓩ]' AS f; SELECT 'aⓜ⓪' ~ 'a[Ⓐ-ⓜ][ⓜ-ⓩ]' AS f; SELECT 'a0' ~ 'a[a-ⓩ]' AS f; SELECT 'aq' ~ 'a[a-ⓩ]' AS t; SELECT 'aⓜ' ~ 'a[a-ⓩ]' AS t; SELECT 'a⓪' ~ 'a[a-ⓩ]' AS f; -- Locale-dependent character classes SELECT 'aⒶⓜ⓪' ~ '[[:alpha:]][[:alpha:]][[:alpha:]][[:graph:]]' AS t; SELECT 'aⒶⓜ⓪' ~ '[[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]]' AS f; -- Locale-dependent character classes with high ranges SELECT 'aⒶⓜ⓪' ~ '[a-z][[:alpha:]][ⓐ-ⓩ][[:graph:]]' AS t; SELECT 'aⓜⒶ⓪' ~ '[a-z][[:alpha:]][ⓐ-ⓩ][[:graph:]]' AS f; SELECT 'aⓜⒶ⓪' ~ '[a-z][ⓐ-ⓩ][[:alpha:]][[:graph:]]' AS t; SELECT 'aⒶⓜ⓪' ~ '[a-z][ⓐ-ⓩ][[:alpha:]][[:graph:]]' AS f; pgFormatter-4.2/t/pg-test-files/sql/regex.sql000066400000000000000000000114641361326045100212400ustar00rootroot00000000000000-- -- Regular expression tests -- -- Don't want to have to double backslashes in regexes set standard_conforming_strings = on; -- Test simple quantified backrefs select 'bbbbb' ~ '^([bc])\1*$' as t; select 'ccc' ~ '^([bc])\1*$' as t; select 'xxx' ~ '^([bc])\1*$' as f; select 'bbc' ~ '^([bc])\1*$' as f; select 'b' ~ '^([bc])\1*$' as t; -- Test quantified backref within a larger expression select 'abc abc abc' ~ '^(\w+)( \1)+$' as t; select 'abc abd abc' ~ '^(\w+)( \1)+$' as f; select 'abc abc abd' ~ '^(\w+)( \1)+$' as f; select 'abc abc abc' ~ '^(.+)( \1)+$' as t; select 'abc abd abc' ~ '^(.+)( \1)+$' as f; select 'abc abc abd' ~ '^(.+)( \1)+$' as f; -- Test some cases that crashed in 9.2beta1 due to pmatch[] array overrun select substring('asd TO foo' from ' TO (([a-z0-9._]+|"([^"]+|"")+")+)'); select substring('a' from '((a))+'); select substring('a' from '((a)+)'); -- Test regexp_match() select regexp_match('abc', ''); select regexp_match('abc', 'bc'); select regexp_match('abc', 'd') is null; select regexp_match('abc', '(B)(c)', 'i'); select regexp_match('abc', 'Bd', 'ig'); -- error -- Test lookahead constraints select regexp_matches('ab', 'a(?=b)b*'); select regexp_matches('a', 'a(?=b)b*'); select regexp_matches('abc', 'a(?=b)b*(?=c)c*'); select regexp_matches('ab', 'a(?=b)b*(?=c)c*'); select regexp_matches('ab', 'a(?!b)b*'); select regexp_matches('a', 'a(?!b)b*'); select regexp_matches('b', '(?=b)b'); select regexp_matches('a', '(?=b)b'); -- Test lookbehind constraints select regexp_matches('abb', '(?<=a)b*'); select regexp_matches('a', 'a(?<=a)b*'); select regexp_matches('abc', 'a(?<=a)b*(?<=b)c*'); select regexp_matches('ab', 'a(?<=a)b*(?<=b)c*'); select regexp_matches('ab', 'a*(? 0; SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass); ALTER TABLE reloptions_test RESET (vacuum_truncate); SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; INSERT INTO reloptions_test VALUES (1, NULL), (NULL, NULL); VACUUM reloptions_test; SELECT pg_relation_size('reloptions_test') = 0; -- Test toast.* options DROP TABLE reloptions_test; CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23); SELECT reltoastrelid as toast_oid FROM pg_class WHERE oid = 'reloptions_test'::regclass \gset SELECT reloptions FROM pg_class WHERE oid = :toast_oid; ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24); SELECT reloptions FROM pg_class WHERE oid = :toast_oid; ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay); SELECT reloptions FROM pg_class WHERE oid = :toast_oid; -- Fail on non-existent options in toast namespace CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42); -- Mix TOAST & heap DROP TABLE reloptions_test; CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40); SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass; SELECT reloptions FROM pg_class WHERE oid = ( SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass); -- -- CREATE INDEX, ALTER INDEX for btrees -- CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30); SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass; -- Fail when option and namespace do not exist CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2); CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_ns.fillfactor=2); -- Check allowed ranges CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1); CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130); -- Check ALTER ALTER INDEX reloptions_test_idx SET (fillfactor=40); SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass; -- Check ALTER on empty reloption list CREATE INDEX reloptions_test_idx3 ON reloptions_test (s); ALTER INDEX reloptions_test_idx3 SET (fillfactor=40); SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass; pgFormatter-4.2/t/pg-test-files/sql/replica_identity.sql000066400000000000000000000073061361326045100234560ustar00rootroot00000000000000CREATE TABLE test_replica_identity ( id serial primary key, keya text not null, keyb text not null, nonkey text, CONSTRAINT test_replica_identity_unique_defer UNIQUE (keya, keyb) DEFERRABLE, CONSTRAINT test_replica_identity_unique_nondefer UNIQUE (keya, keyb) ) ; CREATE TABLE test_replica_identity_othertable (id serial primary key); CREATE INDEX test_replica_identity_keyab ON test_replica_identity (keya, keyb); CREATE UNIQUE INDEX test_replica_identity_keyab_key ON test_replica_identity (keya, keyb); CREATE UNIQUE INDEX test_replica_identity_nonkey ON test_replica_identity (keya, nonkey); CREATE INDEX test_replica_identity_hash ON test_replica_identity USING hash (nonkey); CREATE UNIQUE INDEX test_replica_identity_expr ON test_replica_identity (keya, keyb, (3)); CREATE UNIQUE INDEX test_replica_identity_partial ON test_replica_identity (keya, keyb) WHERE keyb != '3'; -- default is 'd'/DEFAULT for user created tables SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; -- but 'none' for system tables SELECT relreplident FROM pg_class WHERE oid = 'pg_class'::regclass; SELECT relreplident FROM pg_class WHERE oid = 'pg_constraint'::regclass; ---- -- Make sure we detect ineligible indexes ---- -- fail, not unique ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_keyab; -- fail, not a candidate key, nullable column ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_nonkey; -- fail, hash indexes cannot do uniqueness ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_hash; -- fail, expression index ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_expr; -- fail, partial index ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_partial; -- fail, not our index ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_othertable_pkey; -- fail, deferrable ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_unique_defer; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; ---- -- Make sure index cases succeed ---- -- succeed, primary key ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_pkey; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; \d test_replica_identity -- succeed, nondeferrable unique constraint over nonnullable cols ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_unique_nondefer; -- succeed unique index over nonnullable cols ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_keyab_key; ALTER TABLE test_replica_identity REPLICA IDENTITY USING INDEX test_replica_identity_keyab_key; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; \d test_replica_identity SELECT count(*) FROM pg_index WHERE indrelid = 'test_replica_identity'::regclass AND indisreplident; ---- -- Make sure non index cases work ---- ALTER TABLE test_replica_identity REPLICA IDENTITY DEFAULT; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; SELECT count(*) FROM pg_index WHERE indrelid = 'test_replica_identity'::regclass AND indisreplident; ALTER TABLE test_replica_identity REPLICA IDENTITY FULL; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; \d+ test_replica_identity ALTER TABLE test_replica_identity REPLICA IDENTITY NOTHING; SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; DROP TABLE test_replica_identity; DROP TABLE test_replica_identity_othertable; pgFormatter-4.2/t/pg-test-files/sql/returning.sql000066400000000000000000000074751361326045100221520ustar00rootroot00000000000000-- -- Test INSERT/UPDATE/DELETE RETURNING -- -- Simple cases CREATE TEMP TABLE foo (f1 serial, f2 text, f3 int default 42); INSERT INTO foo (f2,f3) VALUES ('test', DEFAULT), ('More', 11), (upper('more'), 7+9) RETURNING *, f1+f3 AS sum; SELECT * FROM foo; UPDATE foo SET f2 = lower(f2), f3 = DEFAULT RETURNING foo.*, f1+f3 AS sum13; SELECT * FROM foo; DELETE FROM foo WHERE f1 > 2 RETURNING f3, f2, f1, least(f1,f3); SELECT * FROM foo; -- Subplans and initplans in the RETURNING list INSERT INTO foo SELECT f1+10, f2, f3+99 FROM foo RETURNING *, f1+112 IN (SELECT q1 FROM int8_tbl) AS subplan, EXISTS(SELECT * FROM int4_tbl) AS initplan; UPDATE foo SET f3 = f3 * 2 WHERE f1 > 10 RETURNING *, f1+112 IN (SELECT q1 FROM int8_tbl) AS subplan, EXISTS(SELECT * FROM int4_tbl) AS initplan; DELETE FROM foo WHERE f1 > 10 RETURNING *, f1+112 IN (SELECT q1 FROM int8_tbl) AS subplan, EXISTS(SELECT * FROM int4_tbl) AS initplan; -- Joins UPDATE foo SET f3 = f3*2 FROM int4_tbl i WHERE foo.f1 + 123455 = i.f1 RETURNING foo.*, i.f1 as "i.f1"; SELECT * FROM foo; DELETE FROM foo USING int4_tbl i WHERE foo.f1 + 123455 = i.f1 RETURNING foo.*, i.f1 as "i.f1"; SELECT * FROM foo; -- Check inheritance cases CREATE TEMP TABLE foochild (fc int) INHERITS (foo); INSERT INTO foochild VALUES(123,'child',999,-123); ALTER TABLE foo ADD COLUMN f4 int8 DEFAULT 99; SELECT * FROM foo; SELECT * FROM foochild; UPDATE foo SET f4 = f4 + f3 WHERE f4 = 99 RETURNING *; SELECT * FROM foo; SELECT * FROM foochild; UPDATE foo SET f3 = f3*2 FROM int8_tbl i WHERE foo.f1 = i.q2 RETURNING *; SELECT * FROM foo; SELECT * FROM foochild; DELETE FROM foo USING int8_tbl i WHERE foo.f1 = i.q2 RETURNING *; SELECT * FROM foo; SELECT * FROM foochild; DROP TABLE foochild; -- Rules and views CREATE TEMP VIEW voo AS SELECT f1, f2 FROM foo; CREATE RULE voo_i AS ON INSERT TO voo DO INSTEAD INSERT INTO foo VALUES(new.*, 57); INSERT INTO voo VALUES(11,'zit'); -- fails: INSERT INTO voo VALUES(12,'zoo') RETURNING *, f1*2; -- fails, incompatible list: CREATE OR REPLACE RULE voo_i AS ON INSERT TO voo DO INSTEAD INSERT INTO foo VALUES(new.*, 57) RETURNING *; CREATE OR REPLACE RULE voo_i AS ON INSERT TO voo DO INSTEAD INSERT INTO foo VALUES(new.*, 57) RETURNING f1, f2; -- should still work INSERT INTO voo VALUES(13,'zit2'); -- works now INSERT INTO voo VALUES(14,'zoo2') RETURNING *; SELECT * FROM foo; SELECT * FROM voo; CREATE OR REPLACE RULE voo_u AS ON UPDATE TO voo DO INSTEAD UPDATE foo SET f1 = new.f1, f2 = new.f2 WHERE f1 = old.f1 RETURNING f1, f2; update voo set f1 = f1 + 1 where f2 = 'zoo2'; update voo set f1 = f1 + 1 where f2 = 'zoo2' RETURNING *, f1*2; SELECT * FROM foo; SELECT * FROM voo; CREATE OR REPLACE RULE voo_d AS ON DELETE TO voo DO INSTEAD DELETE FROM foo WHERE f1 = old.f1 RETURNING f1, f2; DELETE FROM foo WHERE f1 = 13; DELETE FROM foo WHERE f2 = 'zit' RETURNING *; SELECT * FROM foo; SELECT * FROM voo; -- Try a join case CREATE TEMP TABLE joinme (f2j text, other int); INSERT INTO joinme VALUES('more', 12345); INSERT INTO joinme VALUES('zoo2', 54321); INSERT INTO joinme VALUES('other', 0); CREATE TEMP VIEW joinview AS SELECT foo.*, other FROM foo JOIN joinme ON (f2 = f2j); SELECT * FROM joinview; CREATE RULE joinview_u AS ON UPDATE TO joinview DO INSTEAD UPDATE foo SET f1 = new.f1, f3 = new.f3 FROM joinme WHERE f2 = f2j AND f2 = old.f2 RETURNING foo.*, other; UPDATE joinview SET f1 = f1 + 1 WHERE f3 = 57 RETURNING *, other + 1; SELECT * FROM joinview; SELECT * FROM foo; SELECT * FROM voo; -- Check aliased target relation INSERT INTO foo AS bar DEFAULT VALUES RETURNING *; -- ok INSERT INTO foo AS bar DEFAULT VALUES RETURNING foo.*; -- fails, wrong name INSERT INTO foo AS bar DEFAULT VALUES RETURNING bar.*; -- ok INSERT INTO foo AS bar DEFAULT VALUES RETURNING bar.f3; -- ok pgFormatter-4.2/t/pg-test-files/sql/roleattributes.sql000066400000000000000000000217071361326045100231770ustar00rootroot00000000000000-- default for superuser is false CREATE ROLE regress_test_def_superuser; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_def_superuser'; CREATE ROLE regress_test_superuser WITH SUPERUSER; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_superuser'; ALTER ROLE regress_test_superuser WITH NOSUPERUSER; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_superuser'; ALTER ROLE regress_test_superuser WITH SUPERUSER; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_superuser'; -- default for inherit is true CREATE ROLE regress_test_def_inherit; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_def_inherit'; CREATE ROLE regress_test_inherit WITH NOINHERIT; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_inherit'; ALTER ROLE regress_test_inherit WITH INHERIT; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_inherit'; ALTER ROLE regress_test_inherit WITH NOINHERIT; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_inherit'; -- default for create role is false CREATE ROLE regress_test_def_createrole; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_def_createrole'; CREATE ROLE regress_test_createrole WITH CREATEROLE; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_createrole'; ALTER ROLE regress_test_createrole WITH NOCREATEROLE; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_createrole'; ALTER ROLE regress_test_createrole WITH CREATEROLE; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_createrole'; -- default for create database is false CREATE ROLE regress_test_def_createdb; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_def_createdb'; CREATE ROLE regress_test_createdb WITH CREATEDB; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_createdb'; ALTER ROLE regress_test_createdb WITH NOCREATEDB; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_createdb'; ALTER ROLE regress_test_createdb WITH CREATEDB; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_createdb'; -- default for can login is false for role CREATE ROLE regress_test_def_role_canlogin; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_def_role_canlogin'; CREATE ROLE regress_test_role_canlogin WITH LOGIN; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_role_canlogin'; ALTER ROLE regress_test_role_canlogin WITH NOLOGIN; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_role_canlogin'; ALTER ROLE regress_test_role_canlogin WITH LOGIN; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_role_canlogin'; -- default for can login is true for user CREATE USER regress_test_def_user_canlogin; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_def_user_canlogin'; CREATE USER regress_test_user_canlogin WITH NOLOGIN; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_user_canlogin'; ALTER USER regress_test_user_canlogin WITH LOGIN; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_user_canlogin'; ALTER USER regress_test_user_canlogin WITH NOLOGIN; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_user_canlogin'; -- default for replication is false CREATE ROLE regress_test_def_replication; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_def_replication'; CREATE ROLE regress_test_replication WITH REPLICATION; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_replication'; ALTER ROLE regress_test_replication WITH NOREPLICATION; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_replication'; ALTER ROLE regress_test_replication WITH REPLICATION; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_replication'; -- default for bypassrls is false CREATE ROLE regress_test_def_bypassrls; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_def_bypassrls'; CREATE ROLE regress_test_bypassrls WITH BYPASSRLS; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_bypassrls'; ALTER ROLE regress_test_bypassrls WITH NOBYPASSRLS; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_bypassrls'; ALTER ROLE regress_test_bypassrls WITH BYPASSRLS; SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname = 'regress_test_bypassrls'; -- clean up roles DROP ROLE regress_test_def_superuser; DROP ROLE regress_test_superuser; DROP ROLE regress_test_def_inherit; DROP ROLE regress_test_inherit; DROP ROLE regress_test_def_createrole; DROP ROLE regress_test_createrole; DROP ROLE regress_test_def_createdb; DROP ROLE regress_test_createdb; DROP ROLE regress_test_def_role_canlogin; DROP ROLE regress_test_role_canlogin; DROP USER regress_test_def_user_canlogin; DROP USER regress_test_user_canlogin; DROP ROLE regress_test_def_replication; DROP ROLE regress_test_replication; DROP ROLE regress_test_def_bypassrls; DROP ROLE regress_test_bypassrls; pgFormatter-4.2/t/pg-test-files/sql/rolenames.sql000066400000000000000000000451201361326045100221070ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION chkrolattr() RETURNS TABLE ("role" name, rolekeyword text, canlogin bool, replication bool) AS $$ SELECT r.rolname, v.keyword, r.rolcanlogin, r.rolreplication FROM pg_roles r JOIN (VALUES(CURRENT_USER, 'current_user'), (SESSION_USER, 'session_user'), ('current_user', '-'), ('session_user', '-'), ('Public', '-'), ('None', '-')) AS v(uname, keyword) ON (r.rolname = v.uname) ORDER BY 1; $$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION chksetconfig() RETURNS TABLE (db name, "role" name, rolkeyword text, setconfig text[]) AS $$ SELECT COALESCE(d.datname, 'ALL'), COALESCE(r.rolname, 'ALL'), COALESCE(v.keyword, '-'), s.setconfig FROM pg_db_role_setting s LEFT JOIN pg_roles r ON (r.oid = s.setrole) LEFT JOIN pg_database d ON (d.oid = s.setdatabase) LEFT JOIN (VALUES(CURRENT_USER, 'current_user'), (SESSION_USER, 'session_user')) AS v(uname, keyword) ON (r.rolname = v.uname) WHERE (r.rolname) IN ('Public', 'current_user', 'regress_testrol1', 'regress_testrol2') ORDER BY 1, 2; $$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION chkumapping() RETURNS TABLE (umname name, umserver name, umoptions text[]) AS $$ SELECT r.rolname, s.srvname, m.umoptions FROM pg_user_mapping m LEFT JOIN pg_roles r ON (r.oid = m.umuser) JOIN pg_foreign_server s ON (s.oid = m.umserver) ORDER BY 2; $$ LANGUAGE SQL; CREATE ROLE "Public"; CREATE ROLE "None"; CREATE ROLE "current_user"; CREATE ROLE "session_user"; CREATE ROLE "user"; CREATE ROLE current_user; -- error CREATE ROLE current_role; -- error CREATE ROLE session_user; -- error CREATE ROLE user; -- error CREATE ROLE all; -- error CREATE ROLE public; -- error CREATE ROLE "public"; -- error CREATE ROLE none; -- error CREATE ROLE "none"; -- error CREATE ROLE pg_abc; -- error CREATE ROLE "pg_abc"; -- error CREATE ROLE pg_abcdef; -- error CREATE ROLE "pg_abcdef"; -- error CREATE ROLE regress_testrol0 SUPERUSER LOGIN; CREATE ROLE regress_testrolx SUPERUSER LOGIN; CREATE ROLE regress_testrol2 SUPERUSER; CREATE ROLE regress_testrol1 SUPERUSER LOGIN IN ROLE regress_testrol2; \c - SET SESSION AUTHORIZATION regress_testrol1; SET ROLE regress_testrol2; -- ALTER ROLE BEGIN; SELECT * FROM chkrolattr(); ALTER ROLE CURRENT_USER WITH REPLICATION; SELECT * FROM chkrolattr(); ALTER ROLE "current_user" WITH REPLICATION; SELECT * FROM chkrolattr(); ALTER ROLE SESSION_USER WITH REPLICATION; SELECT * FROM chkrolattr(); ALTER ROLE "session_user" WITH REPLICATION; SELECT * FROM chkrolattr(); ALTER USER "Public" WITH REPLICATION; ALTER USER "None" WITH REPLICATION; SELECT * FROM chkrolattr(); ALTER USER regress_testrol1 WITH NOREPLICATION; ALTER USER regress_testrol2 WITH NOREPLICATION; SELECT * FROM chkrolattr(); ROLLBACK; ALTER ROLE USER WITH LOGIN; -- error ALTER ROLE CURRENT_ROLE WITH LOGIN; --error ALTER ROLE ALL WITH REPLICATION; -- error ALTER ROLE SESSION_ROLE WITH NOREPLICATION; -- error ALTER ROLE PUBLIC WITH NOREPLICATION; -- error ALTER ROLE "public" WITH NOREPLICATION; -- error ALTER ROLE NONE WITH NOREPLICATION; -- error ALTER ROLE "none" WITH NOREPLICATION; -- error ALTER ROLE nonexistent WITH NOREPLICATION; -- error -- ALTER USER BEGIN; SELECT * FROM chkrolattr(); ALTER USER CURRENT_USER WITH REPLICATION; SELECT * FROM chkrolattr(); ALTER USER "current_user" WITH REPLICATION; SELECT * FROM chkrolattr(); ALTER USER SESSION_USER WITH REPLICATION; SELECT * FROM chkrolattr(); ALTER USER "session_user" WITH REPLICATION; SELECT * FROM chkrolattr(); ALTER USER "Public" WITH REPLICATION; ALTER USER "None" WITH REPLICATION; SELECT * FROM chkrolattr(); ALTER USER regress_testrol1 WITH NOREPLICATION; ALTER USER regress_testrol2 WITH NOREPLICATION; SELECT * FROM chkrolattr(); ROLLBACK; ALTER USER USER WITH LOGIN; -- error ALTER USER CURRENT_ROLE WITH LOGIN; -- error ALTER USER ALL WITH REPLICATION; -- error ALTER USER SESSION_ROLE WITH NOREPLICATION; -- error ALTER USER PUBLIC WITH NOREPLICATION; -- error ALTER USER "public" WITH NOREPLICATION; -- error ALTER USER NONE WITH NOREPLICATION; -- error ALTER USER "none" WITH NOREPLICATION; -- error ALTER USER nonexistent WITH NOREPLICATION; -- error -- ALTER ROLE SET/RESET SELECT * FROM chksetconfig(); ALTER ROLE CURRENT_USER SET application_name to 'FOO'; ALTER ROLE SESSION_USER SET application_name to 'BAR'; ALTER ROLE "current_user" SET application_name to 'FOOFOO'; ALTER ROLE "Public" SET application_name to 'BARBAR'; ALTER ROLE ALL SET application_name to 'SLAP'; SELECT * FROM chksetconfig(); ALTER ROLE regress_testrol1 SET application_name to 'SLAM'; SELECT * FROM chksetconfig(); ALTER ROLE CURRENT_USER RESET application_name; ALTER ROLE SESSION_USER RESET application_name; ALTER ROLE "current_user" RESET application_name; ALTER ROLE "Public" RESET application_name; ALTER ROLE ALL RESET application_name; SELECT * FROM chksetconfig(); ALTER ROLE CURRENT_ROLE SET application_name to 'BAZ'; -- error ALTER ROLE USER SET application_name to 'BOOM'; -- error ALTER ROLE PUBLIC SET application_name to 'BOMB'; -- error ALTER ROLE nonexistent SET application_name to 'BOMB'; -- error -- ALTER USER SET/RESET SELECT * FROM chksetconfig(); ALTER USER CURRENT_USER SET application_name to 'FOO'; ALTER USER SESSION_USER SET application_name to 'BAR'; ALTER USER "current_user" SET application_name to 'FOOFOO'; ALTER USER "Public" SET application_name to 'BARBAR'; ALTER USER ALL SET application_name to 'SLAP'; SELECT * FROM chksetconfig(); ALTER USER regress_testrol1 SET application_name to 'SLAM'; SELECT * FROM chksetconfig(); ALTER USER CURRENT_USER RESET application_name; ALTER USER SESSION_USER RESET application_name; ALTER USER "current_user" RESET application_name; ALTER USER "Public" RESET application_name; ALTER USER ALL RESET application_name; SELECT * FROM chksetconfig(); ALTER USER CURRENT_USER SET application_name to 'BAZ'; -- error ALTER USER USER SET application_name to 'BOOM'; -- error ALTER USER PUBLIC SET application_name to 'BOMB'; -- error ALTER USER NONE SET application_name to 'BOMB'; -- error ALTER USER nonexistent SET application_name to 'BOMB'; -- error -- CREATE SCHEMA CREATE SCHEMA newschema1 AUTHORIZATION CURRENT_USER; CREATE SCHEMA newschema2 AUTHORIZATION "current_user"; CREATE SCHEMA newschema3 AUTHORIZATION SESSION_USER; CREATE SCHEMA newschema4 AUTHORIZATION regress_testrolx; CREATE SCHEMA newschema5 AUTHORIZATION "Public"; CREATE SCHEMA newschema6 AUTHORIZATION USER; -- error CREATE SCHEMA newschema6 AUTHORIZATION CURRENT_ROLE; -- error CREATE SCHEMA newschema6 AUTHORIZATION PUBLIC; -- error CREATE SCHEMA newschema6 AUTHORIZATION "public"; -- error CREATE SCHEMA newschema6 AUTHORIZATION NONE; -- error CREATE SCHEMA newschema6 AUTHORIZATION nonexistent; -- error SELECT n.nspname, r.rolname FROM pg_namespace n JOIN pg_roles r ON (r.oid = n.nspowner) WHERE n.nspname LIKE 'newschema_' ORDER BY 1; CREATE SCHEMA IF NOT EXISTS newschema1 AUTHORIZATION CURRENT_USER; CREATE SCHEMA IF NOT EXISTS newschema2 AUTHORIZATION "current_user"; CREATE SCHEMA IF NOT EXISTS newschema3 AUTHORIZATION SESSION_USER; CREATE SCHEMA IF NOT EXISTS newschema4 AUTHORIZATION regress_testrolx; CREATE SCHEMA IF NOT EXISTS newschema5 AUTHORIZATION "Public"; CREATE SCHEMA IF NOT EXISTS newschema6 AUTHORIZATION USER; -- error CREATE SCHEMA IF NOT EXISTS newschema6 AUTHORIZATION CURRENT_ROLE; -- error CREATE SCHEMA IF NOT EXISTS newschema6 AUTHORIZATION PUBLIC; -- error CREATE SCHEMA IF NOT EXISTS newschema6 AUTHORIZATION "public"; -- error CREATE SCHEMA IF NOT EXISTS newschema6 AUTHORIZATION NONE; -- error CREATE SCHEMA IF NOT EXISTS newschema6 AUTHORIZATION nonexistent; -- error SELECT n.nspname, r.rolname FROM pg_namespace n JOIN pg_roles r ON (r.oid = n.nspowner) WHERE n.nspname LIKE 'newschema_' ORDER BY 1; -- ALTER TABLE OWNER TO \c - SET SESSION AUTHORIZATION regress_testrol0; CREATE TABLE testtab1 (a int); CREATE TABLE testtab2 (a int); CREATE TABLE testtab3 (a int); CREATE TABLE testtab4 (a int); CREATE TABLE testtab5 (a int); CREATE TABLE testtab6 (a int); \c - SET SESSION AUTHORIZATION regress_testrol1; SET ROLE regress_testrol2; ALTER TABLE testtab1 OWNER TO CURRENT_USER; ALTER TABLE testtab2 OWNER TO "current_user"; ALTER TABLE testtab3 OWNER TO SESSION_USER; ALTER TABLE testtab4 OWNER TO regress_testrolx; ALTER TABLE testtab5 OWNER TO "Public"; ALTER TABLE testtab6 OWNER TO CURRENT_ROLE; -- error ALTER TABLE testtab6 OWNER TO USER; --error ALTER TABLE testtab6 OWNER TO PUBLIC; -- error ALTER TABLE testtab6 OWNER TO "public"; -- error ALTER TABLE testtab6 OWNER TO nonexistent; -- error SELECT c.relname, r.rolname FROM pg_class c JOIN pg_roles r ON (r.oid = c.relowner) WHERE relname LIKE 'testtab_' ORDER BY 1; -- ALTER TABLE, VIEW, MATERIALIZED VIEW, FOREIGN TABLE, SEQUENCE are -- changed their owner in the same way. -- ALTER AGGREGATE \c - SET SESSION AUTHORIZATION regress_testrol0; CREATE AGGREGATE testagg1(int2) (SFUNC = int2_sum, STYPE = int8); CREATE AGGREGATE testagg2(int2) (SFUNC = int2_sum, STYPE = int8); CREATE AGGREGATE testagg3(int2) (SFUNC = int2_sum, STYPE = int8); CREATE AGGREGATE testagg4(int2) (SFUNC = int2_sum, STYPE = int8); CREATE AGGREGATE testagg5(int2) (SFUNC = int2_sum, STYPE = int8); CREATE AGGREGATE testagg5(int2) (SFUNC = int2_sum, STYPE = int8); CREATE AGGREGATE testagg6(int2) (SFUNC = int2_sum, STYPE = int8); CREATE AGGREGATE testagg7(int2) (SFUNC = int2_sum, STYPE = int8); CREATE AGGREGATE testagg8(int2) (SFUNC = int2_sum, STYPE = int8); CREATE AGGREGATE testagg9(int2) (SFUNC = int2_sum, STYPE = int8); \c - SET SESSION AUTHORIZATION regress_testrol1; SET ROLE regress_testrol2; ALTER AGGREGATE testagg1(int2) OWNER TO CURRENT_USER; ALTER AGGREGATE testagg2(int2) OWNER TO "current_user"; ALTER AGGREGATE testagg3(int2) OWNER TO SESSION_USER; ALTER AGGREGATE testagg4(int2) OWNER TO regress_testrolx; ALTER AGGREGATE testagg5(int2) OWNER TO "Public"; ALTER AGGREGATE testagg5(int2) OWNER TO CURRENT_ROLE; -- error ALTER AGGREGATE testagg5(int2) OWNER TO USER; -- error ALTER AGGREGATE testagg5(int2) OWNER TO PUBLIC; -- error ALTER AGGREGATE testagg5(int2) OWNER TO "public"; -- error ALTER AGGREGATE testagg5(int2) OWNER TO nonexistent; -- error SELECT p.proname, r.rolname FROM pg_proc p JOIN pg_roles r ON (r.oid = p.proowner) WHERE proname LIKE 'testagg_' ORDER BY 1; -- CREATE USER MAPPING CREATE FOREIGN DATA WRAPPER test_wrapper; CREATE SERVER sv1 FOREIGN DATA WRAPPER test_wrapper; CREATE SERVER sv2 FOREIGN DATA WRAPPER test_wrapper; CREATE SERVER sv3 FOREIGN DATA WRAPPER test_wrapper; CREATE SERVER sv4 FOREIGN DATA WRAPPER test_wrapper; CREATE SERVER sv5 FOREIGN DATA WRAPPER test_wrapper; CREATE SERVER sv6 FOREIGN DATA WRAPPER test_wrapper; CREATE SERVER sv7 FOREIGN DATA WRAPPER test_wrapper; CREATE SERVER sv8 FOREIGN DATA WRAPPER test_wrapper; CREATE SERVER sv9 FOREIGN DATA WRAPPER test_wrapper; CREATE USER MAPPING FOR CURRENT_USER SERVER sv1 OPTIONS (user 'CURRENT_USER'); CREATE USER MAPPING FOR "current_user" SERVER sv2 OPTIONS (user '"current_user"'); CREATE USER MAPPING FOR USER SERVER sv3 OPTIONS (user 'USER'); CREATE USER MAPPING FOR "user" SERVER sv4 OPTIONS (user '"USER"'); CREATE USER MAPPING FOR SESSION_USER SERVER sv5 OPTIONS (user 'SESSION_USER'); CREATE USER MAPPING FOR PUBLIC SERVER sv6 OPTIONS (user 'PUBLIC'); CREATE USER MAPPING FOR "Public" SERVER sv7 OPTIONS (user '"Public"'); CREATE USER MAPPING FOR regress_testrolx SERVER sv8 OPTIONS (user 'regress_testrolx'); CREATE USER MAPPING FOR CURRENT_ROLE SERVER sv9 OPTIONS (user 'CURRENT_ROLE'); -- error CREATE USER MAPPING FOR nonexistent SERVER sv9 OPTIONS (user 'nonexistent'); -- error; SELECT * FROM chkumapping(); -- ALTER USER MAPPING ALTER USER MAPPING FOR CURRENT_USER SERVER sv1 OPTIONS (SET user 'CURRENT_USER_alt'); ALTER USER MAPPING FOR "current_user" SERVER sv2 OPTIONS (SET user '"current_user"_alt'); ALTER USER MAPPING FOR USER SERVER sv3 OPTIONS (SET user 'USER_alt'); ALTER USER MAPPING FOR "user" SERVER sv4 OPTIONS (SET user '"user"_alt'); ALTER USER MAPPING FOR SESSION_USER SERVER sv5 OPTIONS (SET user 'SESSION_USER_alt'); ALTER USER MAPPING FOR PUBLIC SERVER sv6 OPTIONS (SET user 'public_alt'); ALTER USER MAPPING FOR "Public" SERVER sv7 OPTIONS (SET user '"Public"_alt'); ALTER USER MAPPING FOR regress_testrolx SERVER sv8 OPTIONS (SET user 'regress_testrolx_alt'); ALTER USER MAPPING FOR CURRENT_ROLE SERVER sv9 OPTIONS (SET user 'CURRENT_ROLE_alt'); ALTER USER MAPPING FOR nonexistent SERVER sv9 OPTIONS (SET user 'nonexistent_alt'); -- error SELECT * FROM chkumapping(); -- DROP USER MAPPING DROP USER MAPPING FOR CURRENT_USER SERVER sv1; DROP USER MAPPING FOR "current_user" SERVER sv2; DROP USER MAPPING FOR USER SERVER sv3; DROP USER MAPPING FOR "user" SERVER sv4; DROP USER MAPPING FOR SESSION_USER SERVER sv5; DROP USER MAPPING FOR PUBLIC SERVER sv6; DROP USER MAPPING FOR "Public" SERVER sv7; DROP USER MAPPING FOR regress_testrolx SERVER sv8; DROP USER MAPPING FOR CURRENT_ROLE SERVER sv9; -- error DROP USER MAPPING FOR nonexistent SERVER sv; -- error SELECT * FROM chkumapping(); CREATE USER MAPPING FOR CURRENT_USER SERVER sv1 OPTIONS (user 'CURRENT_USER'); CREATE USER MAPPING FOR "current_user" SERVER sv2 OPTIONS (user '"current_user"'); CREATE USER MAPPING FOR USER SERVER sv3 OPTIONS (user 'USER'); CREATE USER MAPPING FOR "user" SERVER sv4 OPTIONS (user '"USER"'); CREATE USER MAPPING FOR SESSION_USER SERVER sv5 OPTIONS (user 'SESSION_USER'); CREATE USER MAPPING FOR PUBLIC SERVER sv6 OPTIONS (user 'PUBLIC'); CREATE USER MAPPING FOR "Public" SERVER sv7 OPTIONS (user '"Public"'); CREATE USER MAPPING FOR regress_testrolx SERVER sv8 OPTIONS (user 'regress_testrolx'); SELECT * FROM chkumapping(); -- DROP USER MAPPING IF EXISTS DROP USER MAPPING IF EXISTS FOR CURRENT_USER SERVER sv1; SELECT * FROM chkumapping(); DROP USER MAPPING IF EXISTS FOR "current_user" SERVER sv2; SELECT * FROM chkumapping(); DROP USER MAPPING IF EXISTS FOR USER SERVER sv3; SELECT * FROM chkumapping(); DROP USER MAPPING IF EXISTS FOR "user" SERVER sv4; SELECT * FROM chkumapping(); DROP USER MAPPING IF EXISTS FOR SESSION_USER SERVER sv5; SELECT * FROM chkumapping(); DROP USER MAPPING IF EXISTS FOR PUBLIC SERVER sv6; SELECT * FROM chkumapping(); DROP USER MAPPING IF EXISTS FOR "Public" SERVER sv7; SELECT * FROM chkumapping(); DROP USER MAPPING IF EXISTS FOR regress_testrolx SERVER sv8; SELECT * FROM chkumapping(); DROP USER MAPPING IF EXISTS FOR CURRENT_ROLE SERVER sv9; --error DROP USER MAPPING IF EXISTS FOR nonexistent SERVER sv9; -- error -- GRANT/REVOKE GRANT regress_testrol0 TO pg_signal_backend; -- success SET ROLE pg_signal_backend; --success RESET ROLE; CREATE SCHEMA test_roles_schema AUTHORIZATION pg_signal_backend; --success SET ROLE regress_testrol2; UPDATE pg_proc SET proacl = null WHERE proname LIKE 'testagg_'; SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_'; REVOKE ALL PRIVILEGES ON FUNCTION testagg1(int2) FROM PUBLIC; REVOKE ALL PRIVILEGES ON FUNCTION testagg2(int2) FROM PUBLIC; REVOKE ALL PRIVILEGES ON FUNCTION testagg3(int2) FROM PUBLIC; REVOKE ALL PRIVILEGES ON FUNCTION testagg4(int2) FROM PUBLIC; REVOKE ALL PRIVILEGES ON FUNCTION testagg5(int2) FROM PUBLIC; REVOKE ALL PRIVILEGES ON FUNCTION testagg6(int2) FROM PUBLIC; REVOKE ALL PRIVILEGES ON FUNCTION testagg7(int2) FROM PUBLIC; REVOKE ALL PRIVILEGES ON FUNCTION testagg8(int2) FROM PUBLIC; GRANT ALL PRIVILEGES ON FUNCTION testagg1(int2) TO PUBLIC; GRANT ALL PRIVILEGES ON FUNCTION testagg2(int2) TO CURRENT_USER; GRANT ALL PRIVILEGES ON FUNCTION testagg3(int2) TO "current_user"; GRANT ALL PRIVILEGES ON FUNCTION testagg4(int2) TO SESSION_USER; GRANT ALL PRIVILEGES ON FUNCTION testagg5(int2) TO "Public"; GRANT ALL PRIVILEGES ON FUNCTION testagg6(int2) TO regress_testrolx; GRANT ALL PRIVILEGES ON FUNCTION testagg7(int2) TO "public"; GRANT ALL PRIVILEGES ON FUNCTION testagg8(int2) TO current_user, public, regress_testrolx; SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_'; GRANT ALL PRIVILEGES ON FUNCTION testagg9(int2) TO CURRENT_ROLE; --error GRANT ALL PRIVILEGES ON FUNCTION testagg9(int2) TO USER; --error GRANT ALL PRIVILEGES ON FUNCTION testagg9(int2) TO NONE; --error GRANT ALL PRIVILEGES ON FUNCTION testagg9(int2) TO "none"; --error SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_'; REVOKE ALL PRIVILEGES ON FUNCTION testagg1(int2) FROM PUBLIC; REVOKE ALL PRIVILEGES ON FUNCTION testagg2(int2) FROM CURRENT_USER; REVOKE ALL PRIVILEGES ON FUNCTION testagg3(int2) FROM "current_user"; REVOKE ALL PRIVILEGES ON FUNCTION testagg4(int2) FROM SESSION_USER; REVOKE ALL PRIVILEGES ON FUNCTION testagg5(int2) FROM "Public"; REVOKE ALL PRIVILEGES ON FUNCTION testagg6(int2) FROM regress_testrolx; REVOKE ALL PRIVILEGES ON FUNCTION testagg7(int2) FROM "public"; REVOKE ALL PRIVILEGES ON FUNCTION testagg8(int2) FROM current_user, public, regress_testrolx; SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_'; REVOKE ALL PRIVILEGES ON FUNCTION testagg9(int2) FROM CURRENT_ROLE; --error REVOKE ALL PRIVILEGES ON FUNCTION testagg9(int2) FROM USER; --error REVOKE ALL PRIVILEGES ON FUNCTION testagg9(int2) FROM NONE; --error REVOKE ALL PRIVILEGES ON FUNCTION testagg9(int2) FROM "none"; --error SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_'; -- DEFAULT MONITORING ROLES CREATE ROLE regress_role_haspriv; CREATE ROLE regress_role_nopriv; -- pg_read_all_stats GRANT pg_read_all_stats TO regress_role_haspriv; SET SESSION AUTHORIZATION regress_role_haspriv; -- returns true with role member of pg_read_all_stats SELECT COUNT(*) = 0 AS haspriv FROM pg_stat_activity WHERE query = ''; SET SESSION AUTHORIZATION regress_role_nopriv; -- returns false with role not member of pg_read_all_stats SELECT COUNT(*) = 0 AS haspriv FROM pg_stat_activity WHERE query = ''; RESET SESSION AUTHORIZATION; REVOKE pg_read_all_stats FROM regress_role_haspriv; -- pg_read_all_settings GRANT pg_read_all_settings TO regress_role_haspriv; BEGIN; -- A GUC using GUC_SUPERUSER_ONLY is useful for negative tests. SET LOCAL session_preload_libraries TO 'path-to-preload-libraries'; SET SESSION AUTHORIZATION regress_role_haspriv; -- passes with role member of pg_read_all_settings SHOW session_preload_libraries; SET SESSION AUTHORIZATION regress_role_nopriv; -- fails with role not member of pg_read_all_settings SHOW session_preload_libraries; RESET SESSION AUTHORIZATION; ROLLBACK; REVOKE pg_read_all_settings FROM regress_role_haspriv; -- clean up \c DROP SCHEMA test_roles_schema; DROP OWNED BY regress_testrol0, "Public", "current_user", regress_testrol1, regress_testrol2, regress_testrolx CASCADE; DROP ROLE regress_testrol0, regress_testrol1, regress_testrol2, regress_testrolx; DROP ROLE "Public", "None", "current_user", "session_user", "user"; DROP ROLE regress_role_haspriv, regress_role_nopriv; pgFormatter-4.2/t/pg-test-files/sql/rowsecurity.sql000066400000000000000000001701431361326045100225250ustar00rootroot00000000000000-- -- Test of Row-level security feature -- -- Clean up in case a prior regression run failed -- Suppress NOTICE messages when users/groups don't exist SET client_min_messages TO 'warning'; DROP USER IF EXISTS regress_rls_alice; DROP USER IF EXISTS regress_rls_bob; DROP USER IF EXISTS regress_rls_carol; DROP USER IF EXISTS regress_rls_dave; DROP USER IF EXISTS regress_rls_exempt_user; DROP ROLE IF EXISTS regress_rls_group1; DROP ROLE IF EXISTS regress_rls_group2; DROP SCHEMA IF EXISTS regress_rls_schema CASCADE; RESET client_min_messages; -- initial setup CREATE USER regress_rls_alice NOLOGIN; CREATE USER regress_rls_bob NOLOGIN; CREATE USER regress_rls_carol NOLOGIN; CREATE USER regress_rls_dave NOLOGIN; CREATE USER regress_rls_exempt_user BYPASSRLS NOLOGIN; CREATE ROLE regress_rls_group1 NOLOGIN; CREATE ROLE regress_rls_group2 NOLOGIN; GRANT regress_rls_group1 TO regress_rls_bob; GRANT regress_rls_group2 TO regress_rls_carol; CREATE SCHEMA regress_rls_schema; GRANT ALL ON SCHEMA regress_rls_schema to public; SET search_path = regress_rls_schema; -- setup of malicious function CREATE OR REPLACE FUNCTION f_leak(text) RETURNS bool COST 0.0000001 LANGUAGE plpgsql AS 'BEGIN RAISE NOTICE ''f_leak => %'', $1; RETURN true; END'; GRANT EXECUTE ON FUNCTION f_leak(text) TO public; -- BASIC Row-Level Security Scenario SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE uaccount ( pguser name primary key, seclv int ); GRANT SELECT ON uaccount TO public; INSERT INTO uaccount VALUES ('regress_rls_alice', 99), ('regress_rls_bob', 1), ('regress_rls_carol', 2), ('regress_rls_dave', 3); CREATE TABLE category ( cid int primary key, cname text ); GRANT ALL ON category TO public; INSERT INTO category VALUES (11, 'novel'), (22, 'science fiction'), (33, 'technology'), (44, 'manga'); CREATE TABLE document ( did int primary key, cid int references category(cid), dlevel int not null, dauthor name, dtitle text ); GRANT ALL ON document TO public; INSERT INTO document VALUES ( 1, 11, 1, 'regress_rls_bob', 'my first novel'), ( 2, 11, 2, 'regress_rls_bob', 'my second novel'), ( 3, 22, 2, 'regress_rls_bob', 'my science fiction'), ( 4, 44, 1, 'regress_rls_bob', 'my first manga'), ( 5, 44, 2, 'regress_rls_bob', 'my second manga'), ( 6, 22, 1, 'regress_rls_carol', 'great science fiction'), ( 7, 33, 2, 'regress_rls_carol', 'great technology book'), ( 8, 44, 1, 'regress_rls_carol', 'great manga'), ( 9, 22, 1, 'regress_rls_dave', 'awesome science fiction'), (10, 33, 2, 'regress_rls_dave', 'awesome technology book'); ALTER TABLE document ENABLE ROW LEVEL SECURITY; -- user's security level must be higher than or equal to document's CREATE POLICY p1 ON document AS PERMISSIVE USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user)); -- try to create a policy of bogus type CREATE POLICY p1 ON document AS UGLY USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user)); -- but Dave isn't allowed to anything at cid 50 or above -- this is to make sure that we sort the policies by name first -- when applying WITH CHECK, a later INSERT by Dave should fail due -- to p1r first CREATE POLICY p2r ON document AS RESTRICTIVE TO regress_rls_dave USING (cid <> 44 AND cid < 50); -- and Dave isn't allowed to see manga documents CREATE POLICY p1r ON document AS RESTRICTIVE TO regress_rls_dave USING (cid <> 44); \dp \d document SELECT * FROM pg_policies WHERE schemaname = 'regress_rls_schema' AND tablename = 'document' ORDER BY policyname; -- viewpoint from regress_rls_bob SET SESSION AUTHORIZATION regress_rls_bob; SET row_security TO ON; SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did; SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER BY did; -- try a sampled version SELECT * FROM document TABLESAMPLE BERNOULLI(50) REPEATABLE(0) WHERE f_leak(dtitle) ORDER BY did; -- viewpoint from regress_rls_carol SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did; SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER BY did; -- try a sampled version SELECT * FROM document TABLESAMPLE BERNOULLI(50) REPEATABLE(0) WHERE f_leak(dtitle) ORDER BY did; EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle); EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle); -- viewpoint from regress_rls_dave SET SESSION AUTHORIZATION regress_rls_dave; SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did; SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER BY did; EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle); EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle); -- 44 would technically fail for both p2r and p1r, but we should get an error -- back from p1r for this because it sorts first INSERT INTO document VALUES (100, 44, 1, 'regress_rls_dave', 'testing sorting of policies'); -- fail -- Just to see a p2r error INSERT INTO document VALUES (100, 55, 1, 'regress_rls_dave', 'testing sorting of policies'); -- fail -- only owner can change policies ALTER POLICY p1 ON document USING (true); --fail DROP POLICY p1 ON document; --fail SET SESSION AUTHORIZATION regress_rls_alice; ALTER POLICY p1 ON document USING (dauthor = current_user); -- viewpoint from regress_rls_bob again SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did; SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER by did; -- viewpoint from rls_regres_carol again SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did; SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER by did; EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle); EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle); -- interaction of FK/PK constraints SET SESSION AUTHORIZATION regress_rls_alice; CREATE POLICY p2 ON category USING (CASE WHEN current_user = 'regress_rls_bob' THEN cid IN (11, 33) WHEN current_user = 'regress_rls_carol' THEN cid IN (22, 44) ELSE false END); ALTER TABLE category ENABLE ROW LEVEL SECURITY; -- cannot delete PK referenced by invisible FK SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM document d FULL OUTER JOIN category c on d.cid = c.cid ORDER BY d.did, c.cid; DELETE FROM category WHERE cid = 33; -- fails with FK violation -- can insert FK referencing invisible PK SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM document d FULL OUTER JOIN category c on d.cid = c.cid ORDER BY d.did, c.cid; INSERT INTO document VALUES (11, 33, 1, current_user, 'hoge'); -- UNIQUE or PRIMARY KEY constraint violation DOES reveal presence of row SET SESSION AUTHORIZATION regress_rls_bob; INSERT INTO document VALUES (8, 44, 1, 'regress_rls_bob', 'my third manga'); -- Must fail with unique violation, revealing presence of did we can't see SELECT * FROM document WHERE did = 8; -- and confirm we can't see it -- RLS policies are checked before constraints INSERT INTO document VALUES (8, 44, 1, 'regress_rls_carol', 'my third manga'); -- Should fail with RLS check violation, not duplicate key violation UPDATE document SET did = 8, dauthor = 'regress_rls_carol' WHERE did = 5; -- Should fail with RLS check violation, not duplicate key violation -- database superuser does bypass RLS policy when enabled RESET SESSION AUTHORIZATION; SET row_security TO ON; SELECT * FROM document; SELECT * FROM category; -- database superuser does bypass RLS policy when disabled RESET SESSION AUTHORIZATION; SET row_security TO OFF; SELECT * FROM document; SELECT * FROM category; -- database non-superuser with bypass privilege can bypass RLS policy when disabled SET SESSION AUTHORIZATION regress_rls_exempt_user; SET row_security TO OFF; SELECT * FROM document; SELECT * FROM category; -- RLS policy does not apply to table owner when RLS enabled. SET SESSION AUTHORIZATION regress_rls_alice; SET row_security TO ON; SELECT * FROM document; SELECT * FROM category; -- RLS policy does not apply to table owner when RLS disabled. SET SESSION AUTHORIZATION regress_rls_alice; SET row_security TO OFF; SELECT * FROM document; SELECT * FROM category; -- -- Table inheritance and RLS policy -- SET SESSION AUTHORIZATION regress_rls_alice; SET row_security TO ON; CREATE TABLE t1 (id int not null primary key, a int, junk1 text, b text); ALTER TABLE t1 DROP COLUMN junk1; -- just a disturbing factor GRANT ALL ON t1 TO public; CREATE TABLE t2 (c float) INHERITS (t1); GRANT ALL ON t2 TO public; CREATE TABLE t3 (id int not null primary key, c text, b text, a int); ALTER TABLE t3 INHERIT t1; GRANT ALL ON t3 TO public; CREATE POLICY p1 ON t1 FOR ALL TO PUBLIC USING (a % 2 = 0); -- be even number CREATE POLICY p2 ON t2 FOR ALL TO PUBLIC USING (a % 2 = 1); -- be odd number ALTER TABLE t1 ENABLE ROW LEVEL SECURITY; ALTER TABLE t2 ENABLE ROW LEVEL SECURITY; SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM t1; EXPLAIN (COSTS OFF) SELECT * FROM t1; SELECT * FROM t1 WHERE f_leak(b); EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b); -- reference to system column SELECT tableoid::regclass, * FROM t1; EXPLAIN (COSTS OFF) SELECT *, t1 FROM t1; -- reference to whole-row reference SELECT *, t1 FROM t1; EXPLAIN (COSTS OFF) SELECT *, t1 FROM t1; -- for share/update lock SELECT * FROM t1 FOR SHARE; EXPLAIN (COSTS OFF) SELECT * FROM t1 FOR SHARE; SELECT * FROM t1 WHERE f_leak(b) FOR SHARE; EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b) FOR SHARE; -- union all query SELECT a, b, tableoid::regclass FROM t2 UNION ALL SELECT a, b, tableoid::regclass FROM t3; EXPLAIN (COSTS OFF) SELECT a, b, tableoid::regclass FROM t2 UNION ALL SELECT a, b, tableoid::regclass FROM t3; -- superuser is allowed to bypass RLS checks RESET SESSION AUTHORIZATION; SET row_security TO OFF; SELECT * FROM t1 WHERE f_leak(b); EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b); -- non-superuser with bypass privilege can bypass RLS policy when disabled SET SESSION AUTHORIZATION regress_rls_exempt_user; SET row_security TO OFF; SELECT * FROM t1 WHERE f_leak(b); EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b); -- -- Partitioned Tables -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE part_document ( did int, cid int, dlevel int not null, dauthor name, dtitle text ) PARTITION BY RANGE (cid); GRANT ALL ON part_document TO public; -- Create partitions for document categories CREATE TABLE part_document_fiction PARTITION OF part_document FOR VALUES FROM (11) to (12); CREATE TABLE part_document_satire PARTITION OF part_document FOR VALUES FROM (55) to (56); CREATE TABLE part_document_nonfiction PARTITION OF part_document FOR VALUES FROM (99) to (100); GRANT ALL ON part_document_fiction TO public; GRANT ALL ON part_document_satire TO public; GRANT ALL ON part_document_nonfiction TO public; INSERT INTO part_document VALUES ( 1, 11, 1, 'regress_rls_bob', 'my first novel'), ( 2, 11, 2, 'regress_rls_bob', 'my second novel'), ( 3, 99, 2, 'regress_rls_bob', 'my science textbook'), ( 4, 55, 1, 'regress_rls_bob', 'my first satire'), ( 5, 99, 2, 'regress_rls_bob', 'my history book'), ( 6, 11, 1, 'regress_rls_carol', 'great science fiction'), ( 7, 99, 2, 'regress_rls_carol', 'great technology book'), ( 8, 55, 2, 'regress_rls_carol', 'great satire'), ( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'), (10, 99, 2, 'regress_rls_dave', 'awesome technology book'); ALTER TABLE part_document ENABLE ROW LEVEL SECURITY; -- Create policy on parent -- user's security level must be higher than or equal to document's CREATE POLICY pp1 ON part_document AS PERMISSIVE USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user)); -- Dave is only allowed to see cid < 55 CREATE POLICY pp1r ON part_document AS RESTRICTIVE TO regress_rls_dave USING (cid < 55); \d+ part_document SELECT * FROM pg_policies WHERE schemaname = 'regress_rls_schema' AND tablename like '%part_document%' ORDER BY policyname; -- viewpoint from regress_rls_bob SET SESSION AUTHORIZATION regress_rls_bob; SET row_security TO ON; SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did; EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); -- viewpoint from regress_rls_carol SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did; EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); -- viewpoint from regress_rls_dave SET SESSION AUTHORIZATION regress_rls_dave; SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did; EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); -- pp1 ERROR INSERT INTO part_document VALUES (100, 11, 5, 'regress_rls_dave', 'testing pp1'); -- fail -- pp1r ERROR INSERT INTO part_document VALUES (100, 99, 1, 'regress_rls_dave', 'testing pp1r'); -- fail -- Show that RLS policy does not apply for direct inserts to children -- This should fail with RLS POLICY pp1r violation. INSERT INTO part_document VALUES (100, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- fail -- But this should succeed. INSERT INTO part_document_satire VALUES (100, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- success -- We still cannot see the row using the parent SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did; -- But we can if we look directly SELECT * FROM part_document_satire WHERE f_leak(dtitle) ORDER BY did; -- Turn on RLS and create policy on child to show RLS is checked before constraints SET SESSION AUTHORIZATION regress_rls_alice; ALTER TABLE part_document_satire ENABLE ROW LEVEL SECURITY; CREATE POLICY pp3 ON part_document_satire AS RESTRICTIVE USING (cid < 55); -- This should fail with RLS violation now. SET SESSION AUTHORIZATION regress_rls_dave; INSERT INTO part_document_satire VALUES (101, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- fail -- And now we cannot see directly into the partition either, due to RLS SELECT * FROM part_document_satire WHERE f_leak(dtitle) ORDER BY did; -- The parent looks same as before -- viewpoint from regress_rls_dave SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did; EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); -- viewpoint from regress_rls_carol SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did; EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); -- only owner can change policies ALTER POLICY pp1 ON part_document USING (true); --fail DROP POLICY pp1 ON part_document; --fail SET SESSION AUTHORIZATION regress_rls_alice; ALTER POLICY pp1 ON part_document USING (dauthor = current_user); -- viewpoint from regress_rls_bob again SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did; -- viewpoint from rls_regres_carol again SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did; EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle); -- database superuser does bypass RLS policy when enabled RESET SESSION AUTHORIZATION; SET row_security TO ON; SELECT * FROM part_document ORDER BY did; SELECT * FROM part_document_satire ORDER by did; -- database non-superuser with bypass privilege can bypass RLS policy when disabled SET SESSION AUTHORIZATION regress_rls_exempt_user; SET row_security TO OFF; SELECT * FROM part_document ORDER BY did; SELECT * FROM part_document_satire ORDER by did; -- RLS policy does not apply to table owner when RLS enabled. SET SESSION AUTHORIZATION regress_rls_alice; SET row_security TO ON; SELECT * FROM part_document ORDER by did; SELECT * FROM part_document_satire ORDER by did; -- When RLS disabled, other users get ERROR. SET SESSION AUTHORIZATION regress_rls_dave; SET row_security TO OFF; SELECT * FROM part_document ORDER by did; SELECT * FROM part_document_satire ORDER by did; -- Check behavior with a policy that uses a SubPlan not an InitPlan. SET SESSION AUTHORIZATION regress_rls_alice; SET row_security TO ON; CREATE POLICY pp3 ON part_document AS RESTRICTIVE USING ((SELECT dlevel <= seclv FROM uaccount WHERE pguser = current_user)); SET SESSION AUTHORIZATION regress_rls_carol; INSERT INTO part_document VALUES (100, 11, 5, 'regress_rls_carol', 'testing pp3'); -- fail ----- Dependencies ----- SET SESSION AUTHORIZATION regress_rls_alice; SET row_security TO ON; CREATE TABLE dependee (x integer, y integer); CREATE TABLE dependent (x integer, y integer); CREATE POLICY d1 ON dependent FOR ALL TO PUBLIC USING (x = (SELECT d.x FROM dependee d WHERE d.y = y)); DROP TABLE dependee; -- Should fail without CASCADE due to dependency on row security qual? DROP TABLE dependee CASCADE; EXPLAIN (COSTS OFF) SELECT * FROM dependent; -- After drop, should be unqualified ----- RECURSION ---- -- -- Simple recursion -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE rec1 (x integer, y integer); CREATE POLICY r1 ON rec1 USING (x = (SELECT r.x FROM rec1 r WHERE y = r.y)); ALTER TABLE rec1 ENABLE ROW LEVEL SECURITY; SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM rec1; -- fail, direct recursion -- -- Mutual recursion -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE rec2 (a integer, b integer); ALTER POLICY r1 ON rec1 USING (x = (SELECT a FROM rec2 WHERE b = y)); CREATE POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1 WHERE y = b)); ALTER TABLE rec2 ENABLE ROW LEVEL SECURITY; SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM rec1; -- fail, mutual recursion -- -- Mutual recursion via views -- SET SESSION AUTHORIZATION regress_rls_bob; CREATE VIEW rec1v AS SELECT * FROM rec1; CREATE VIEW rec2v AS SELECT * FROM rec2; SET SESSION AUTHORIZATION regress_rls_alice; ALTER POLICY r1 ON rec1 USING (x = (SELECT a FROM rec2v WHERE b = y)); ALTER POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1v WHERE y = b)); SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM rec1; -- fail, mutual recursion via views -- -- Mutual recursion via .s.b views -- SET SESSION AUTHORIZATION regress_rls_bob; DROP VIEW rec1v, rec2v CASCADE; CREATE VIEW rec1v WITH (security_barrier) AS SELECT * FROM rec1; CREATE VIEW rec2v WITH (security_barrier) AS SELECT * FROM rec2; SET SESSION AUTHORIZATION regress_rls_alice; CREATE POLICY r1 ON rec1 USING (x = (SELECT a FROM rec2v WHERE b = y)); CREATE POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1v WHERE y = b)); SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM rec1; -- fail, mutual recursion via s.b. views -- -- recursive RLS and VIEWs in policy -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE s1 (a int, b text); INSERT INTO s1 (SELECT x, md5(x::text) FROM generate_series(-10,10) x); CREATE TABLE s2 (x int, y text); INSERT INTO s2 (SELECT x, md5(x::text) FROM generate_series(-6,6) x); GRANT SELECT ON s1, s2 TO regress_rls_bob; CREATE POLICY p1 ON s1 USING (a in (select x from s2 where y like '%2f%')); CREATE POLICY p2 ON s2 USING (x in (select a from s1 where b like '%22%')); CREATE POLICY p3 ON s1 FOR INSERT WITH CHECK (a = (SELECT a FROM s1)); ALTER TABLE s1 ENABLE ROW LEVEL SECURITY; ALTER TABLE s2 ENABLE ROW LEVEL SECURITY; SET SESSION AUTHORIZATION regress_rls_bob; CREATE VIEW v2 AS SELECT * FROM s2 WHERE y like '%af%'; SELECT * FROM s1 WHERE f_leak(b); -- fail (infinite recursion) INSERT INTO s1 VALUES (1, 'foo'); -- fail (infinite recursion) SET SESSION AUTHORIZATION regress_rls_alice; DROP POLICY p3 on s1; ALTER POLICY p2 ON s2 USING (x % 2 = 0); SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM s1 WHERE f_leak(b); -- OK EXPLAIN (COSTS OFF) SELECT * FROM only s1 WHERE f_leak(b); SET SESSION AUTHORIZATION regress_rls_alice; ALTER POLICY p1 ON s1 USING (a in (select x from v2)); -- using VIEW in RLS policy SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM s1 WHERE f_leak(b); -- OK EXPLAIN (COSTS OFF) SELECT * FROM s1 WHERE f_leak(b); SELECT (SELECT x FROM s1 LIMIT 1) xx, * FROM s2 WHERE y like '%28%'; EXPLAIN (COSTS OFF) SELECT (SELECT x FROM s1 LIMIT 1) xx, * FROM s2 WHERE y like '%28%'; SET SESSION AUTHORIZATION regress_rls_alice; ALTER POLICY p2 ON s2 USING (x in (select a from s1 where b like '%d2%')); SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM s1 WHERE f_leak(b); -- fail (infinite recursion via view) -- prepared statement with regress_rls_alice privilege PREPARE p1(int) AS SELECT * FROM t1 WHERE a <= $1; EXECUTE p1(2); EXPLAIN (COSTS OFF) EXECUTE p1(2); -- superuser is allowed to bypass RLS checks RESET SESSION AUTHORIZATION; SET row_security TO OFF; SELECT * FROM t1 WHERE f_leak(b); EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b); -- plan cache should be invalidated EXECUTE p1(2); EXPLAIN (COSTS OFF) EXECUTE p1(2); PREPARE p2(int) AS SELECT * FROM t1 WHERE a = $1; EXECUTE p2(2); EXPLAIN (COSTS OFF) EXECUTE p2(2); -- also, case when privilege switch from superuser SET SESSION AUTHORIZATION regress_rls_bob; SET row_security TO ON; EXECUTE p2(2); EXPLAIN (COSTS OFF) EXECUTE p2(2); -- -- UPDATE / DELETE and Row-level security -- SET SESSION AUTHORIZATION regress_rls_bob; EXPLAIN (COSTS OFF) UPDATE t1 SET b = b || b WHERE f_leak(b); UPDATE t1 SET b = b || b WHERE f_leak(b); EXPLAIN (COSTS OFF) UPDATE only t1 SET b = b || '_updt' WHERE f_leak(b); UPDATE only t1 SET b = b || '_updt' WHERE f_leak(b); -- returning clause with system column UPDATE only t1 SET b = b WHERE f_leak(b) RETURNING tableoid::regclass, *, t1; UPDATE t1 SET b = b WHERE f_leak(b) RETURNING *; UPDATE t1 SET b = b WHERE f_leak(b) RETURNING tableoid::regclass, *, t1; -- updates with from clause EXPLAIN (COSTS OFF) UPDATE t2 SET b=t2.b FROM t3 WHERE t2.a = 3 and t3.a = 2 AND f_leak(t2.b) AND f_leak(t3.b); UPDATE t2 SET b=t2.b FROM t3 WHERE t2.a = 3 and t3.a = 2 AND f_leak(t2.b) AND f_leak(t3.b); EXPLAIN (COSTS OFF) UPDATE t1 SET b=t1.b FROM t2 WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b); UPDATE t1 SET b=t1.b FROM t2 WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b); EXPLAIN (COSTS OFF) UPDATE t2 SET b=t2.b FROM t1 WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b); UPDATE t2 SET b=t2.b FROM t1 WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b); -- updates with from clause self join EXPLAIN (COSTS OFF) UPDATE t2 t2_1 SET b = t2_2.b FROM t2 t2_2 WHERE t2_1.a = 3 AND t2_2.a = t2_1.a AND t2_2.b = t2_1.b AND f_leak(t2_1.b) AND f_leak(t2_2.b) RETURNING *, t2_1, t2_2; UPDATE t2 t2_1 SET b = t2_2.b FROM t2 t2_2 WHERE t2_1.a = 3 AND t2_2.a = t2_1.a AND t2_2.b = t2_1.b AND f_leak(t2_1.b) AND f_leak(t2_2.b) RETURNING *, t2_1, t2_2; EXPLAIN (COSTS OFF) UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2 WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2; UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2 WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2; RESET SESSION AUTHORIZATION; SET row_security TO OFF; SELECT * FROM t1 ORDER BY a,b; SET SESSION AUTHORIZATION regress_rls_bob; SET row_security TO ON; EXPLAIN (COSTS OFF) DELETE FROM only t1 WHERE f_leak(b); EXPLAIN (COSTS OFF) DELETE FROM t1 WHERE f_leak(b); DELETE FROM only t1 WHERE f_leak(b) RETURNING tableoid::regclass, *, t1; DELETE FROM t1 WHERE f_leak(b) RETURNING tableoid::regclass, *, t1; -- -- S.b. view on top of Row-level security -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE b1 (a int, b text); INSERT INTO b1 (SELECT x, md5(x::text) FROM generate_series(-10,10) x); CREATE POLICY p1 ON b1 USING (a % 2 = 0); ALTER TABLE b1 ENABLE ROW LEVEL SECURITY; GRANT ALL ON b1 TO regress_rls_bob; SET SESSION AUTHORIZATION regress_rls_bob; CREATE VIEW bv1 WITH (security_barrier) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION; GRANT ALL ON bv1 TO regress_rls_carol; SET SESSION AUTHORIZATION regress_rls_carol; EXPLAIN (COSTS OFF) SELECT * FROM bv1 WHERE f_leak(b); SELECT * FROM bv1 WHERE f_leak(b); INSERT INTO bv1 VALUES (-1, 'xxx'); -- should fail view WCO INSERT INTO bv1 VALUES (11, 'xxx'); -- should fail RLS check INSERT INTO bv1 VALUES (12, 'xxx'); -- ok EXPLAIN (COSTS OFF) UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak(b); UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak(b); EXPLAIN (COSTS OFF) DELETE FROM bv1 WHERE a = 6 AND f_leak(b); DELETE FROM bv1 WHERE a = 6 AND f_leak(b); SET SESSION AUTHORIZATION regress_rls_alice; SELECT * FROM b1; -- -- INSERT ... ON CONFLICT DO UPDATE and Row-level security -- SET SESSION AUTHORIZATION regress_rls_alice; DROP POLICY p1 ON document; DROP POLICY p1r ON document; CREATE POLICY p1 ON document FOR SELECT USING (true); CREATE POLICY p2 ON document FOR INSERT WITH CHECK (dauthor = current_user); CREATE POLICY p3 ON document FOR UPDATE USING (cid = (SELECT cid from category WHERE cname = 'novel')) WITH CHECK (dauthor = current_user); SET SESSION AUTHORIZATION regress_rls_bob; -- Exists... SELECT * FROM document WHERE did = 2; -- ...so violates actual WITH CHECK OPTION within UPDATE (not INSERT, since -- alternative UPDATE path happens to be taken): INSERT INTO document VALUES (2, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_carol', 'my first novel') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, dauthor = EXCLUDED.dauthor; -- Violates USING qual for UPDATE policy p3. -- -- UPDATE path is taken, but UPDATE fails purely because *existing* row to be -- updated is not a "novel"/cid 11 (row is not leaked, even though we have -- SELECT privileges sufficient to see the row in this instance): INSERT INTO document VALUES (33, 22, 1, 'regress_rls_bob', 'okay science fiction'); -- preparation for next statement INSERT INTO document VALUES (33, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'Some novel, replaces sci-fi') -- takes UPDATE path ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle; -- Fine (we UPDATE, since INSERT WCOs and UPDATE security barrier quals + WCOs -- not violated): INSERT INTO document VALUES (2, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'my first novel') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *; -- Fine (we INSERT, so "cid = 33" ("technology") isn't evaluated): INSERT INTO document VALUES (78, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'some technology novel') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *; -- Fine (same query, but we UPDATE, so "cid = 33", ("technology") is not the -- case in respect of *existing* tuple): INSERT INTO document VALUES (78, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'some technology novel') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *; -- Same query a third time, but now fails due to existing tuple finally not -- passing quals: INSERT INTO document VALUES (78, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'some technology novel') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *; -- Don't fail just because INSERT doesn't satisfy WITH CHECK option that -- originated as a barrier/USING() qual from the UPDATE. Note that the UPDATE -- path *isn't* taken, and so UPDATE-related policy does not apply: INSERT INTO document VALUES (79, (SELECT cid from category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'technology book, can only insert') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *; -- But this time, the same statement fails, because the UPDATE path is taken, -- and updating the row just inserted falls afoul of security barrier qual -- (enforced as WCO) -- what we might have updated target tuple to is -- irrelevant, in fact. INSERT INTO document VALUES (79, (SELECT cid from category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'technology book, can only insert') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *; -- Test default USING qual enforced as WCO SET SESSION AUTHORIZATION regress_rls_alice; DROP POLICY p1 ON document; DROP POLICY p2 ON document; DROP POLICY p3 ON document; CREATE POLICY p3_with_default ON document FOR UPDATE USING (cid = (SELECT cid from category WHERE cname = 'novel')); SET SESSION AUTHORIZATION regress_rls_bob; -- Just because WCO-style enforcement of USING quals occurs with -- existing/target tuple does not mean that the implementation can be allowed -- to fail to also enforce this qual against the final tuple appended to -- relation (since in the absence of an explicit WCO, this is also interpreted -- as an UPDATE/ALL WCO in general). -- -- UPDATE path is taken here (fails due to existing tuple). Note that this is -- not reported as a "USING expression", because it's an RLS UPDATE check that originated as -- a USING qual for the purposes of RLS in general, as opposed to an explicit -- USING qual that is ordinarily a security barrier. We leave it up to the -- UPDATE to make this fail: INSERT INTO document VALUES (79, (SELECT cid from category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'technology book, can only insert') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *; -- UPDATE path is taken here. Existing tuple passes, since its cid -- corresponds to "novel", but default USING qual is enforced against -- post-UPDATE tuple too (as always when updating with a policy that lacks an -- explicit WCO), and so this fails: INSERT INTO document VALUES (2, (SELECT cid from category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'my first novel') ON CONFLICT (did) DO UPDATE SET cid = EXCLUDED.cid, dtitle = EXCLUDED.dtitle RETURNING *; SET SESSION AUTHORIZATION regress_rls_alice; DROP POLICY p3_with_default ON document; -- -- Test ALL policies with ON CONFLICT DO UPDATE (much the same as existing UPDATE -- tests) -- CREATE POLICY p3_with_all ON document FOR ALL USING (cid = (SELECT cid from category WHERE cname = 'novel')) WITH CHECK (dauthor = current_user); SET SESSION AUTHORIZATION regress_rls_bob; -- Fails, since ALL WCO is enforced in insert path: INSERT INTO document VALUES (80, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_carol', 'my first novel') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33; -- Fails, since ALL policy USING qual is enforced (existing, target tuple is in -- violation, since it has the "manga" cid): INSERT INTO document VALUES (4, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'my first novel') ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle; -- Fails, since ALL WCO are enforced: INSERT INTO document VALUES (1, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'my first novel') ON CONFLICT (did) DO UPDATE SET dauthor = 'regress_rls_carol'; -- -- ROLE/GROUP -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE z1 (a int, b text); CREATE TABLE z2 (a int, b text); GRANT SELECT ON z1,z2 TO regress_rls_group1, regress_rls_group2, regress_rls_bob, regress_rls_carol; INSERT INTO z1 VALUES (1, 'aba'), (2, 'bbb'), (3, 'ccc'), (4, 'dad'); CREATE POLICY p1 ON z1 TO regress_rls_group1 USING (a % 2 = 0); CREATE POLICY p2 ON z1 TO regress_rls_group2 USING (a % 2 = 1); ALTER TABLE z1 ENABLE ROW LEVEL SECURITY; SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM z1 WHERE f_leak(b); EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b); PREPARE plancache_test AS SELECT * FROM z1 WHERE f_leak(b); EXPLAIN (COSTS OFF) EXECUTE plancache_test; PREPARE plancache_test2 AS WITH q AS MATERIALIZED (SELECT * FROM z1 WHERE f_leak(b)) SELECT * FROM q,z2; EXPLAIN (COSTS OFF) EXECUTE plancache_test2; PREPARE plancache_test3 AS WITH q AS MATERIALIZED (SELECT * FROM z2) SELECT * FROM q,z1 WHERE f_leak(z1.b); EXPLAIN (COSTS OFF) EXECUTE plancache_test3; SET ROLE regress_rls_group1; SELECT * FROM z1 WHERE f_leak(b); EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b); EXPLAIN (COSTS OFF) EXECUTE plancache_test; EXPLAIN (COSTS OFF) EXECUTE plancache_test2; EXPLAIN (COSTS OFF) EXECUTE plancache_test3; SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM z1 WHERE f_leak(b); EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b); EXPLAIN (COSTS OFF) EXECUTE plancache_test; EXPLAIN (COSTS OFF) EXECUTE plancache_test2; EXPLAIN (COSTS OFF) EXECUTE plancache_test3; SET ROLE regress_rls_group2; SELECT * FROM z1 WHERE f_leak(b); EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b); EXPLAIN (COSTS OFF) EXECUTE plancache_test; EXPLAIN (COSTS OFF) EXECUTE plancache_test2; EXPLAIN (COSTS OFF) EXECUTE plancache_test3; -- -- Views should follow policy for view owner. -- -- View and Table owner are the same. SET SESSION AUTHORIZATION regress_rls_alice; CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b); GRANT SELECT ON rls_view TO regress_rls_bob; -- Query as role that is not owner of view or table. Should return all records. SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM rls_view; EXPLAIN (COSTS OFF) SELECT * FROM rls_view; -- Query as view/table owner. Should return all records. SET SESSION AUTHORIZATION regress_rls_alice; SELECT * FROM rls_view; EXPLAIN (COSTS OFF) SELECT * FROM rls_view; DROP VIEW rls_view; -- View and Table owners are different. SET SESSION AUTHORIZATION regress_rls_bob; CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b); GRANT SELECT ON rls_view TO regress_rls_alice; -- Query as role that is not owner of view but is owner of table. -- Should return records based on view owner policies. SET SESSION AUTHORIZATION regress_rls_alice; SELECT * FROM rls_view; EXPLAIN (COSTS OFF) SELECT * FROM rls_view; -- Query as role that is not owner of table but is owner of view. -- Should return records based on view owner policies. SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM rls_view; EXPLAIN (COSTS OFF) SELECT * FROM rls_view; -- Query as role that is not the owner of the table or view without permissions. SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM rls_view; --fail - permission denied. EXPLAIN (COSTS OFF) SELECT * FROM rls_view; --fail - permission denied. -- Query as role that is not the owner of the table or view with permissions. SET SESSION AUTHORIZATION regress_rls_bob; GRANT SELECT ON rls_view TO regress_rls_carol; SELECT * FROM rls_view; EXPLAIN (COSTS OFF) SELECT * FROM rls_view; SET SESSION AUTHORIZATION regress_rls_bob; DROP VIEW rls_view; -- -- Command specific -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE x1 (a int, b text, c text); GRANT ALL ON x1 TO PUBLIC; INSERT INTO x1 VALUES (1, 'abc', 'regress_rls_bob'), (2, 'bcd', 'regress_rls_bob'), (3, 'cde', 'regress_rls_carol'), (4, 'def', 'regress_rls_carol'), (5, 'efg', 'regress_rls_bob'), (6, 'fgh', 'regress_rls_bob'), (7, 'fgh', 'regress_rls_carol'), (8, 'fgh', 'regress_rls_carol'); CREATE POLICY p0 ON x1 FOR ALL USING (c = current_user); CREATE POLICY p1 ON x1 FOR SELECT USING (a % 2 = 0); CREATE POLICY p2 ON x1 FOR INSERT WITH CHECK (a % 2 = 1); CREATE POLICY p3 ON x1 FOR UPDATE USING (a % 2 = 0); CREATE POLICY p4 ON x1 FOR DELETE USING (a < 8); ALTER TABLE x1 ENABLE ROW LEVEL SECURITY; SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM x1 WHERE f_leak(b) ORDER BY a ASC; UPDATE x1 SET b = b || '_updt' WHERE f_leak(b) RETURNING *; SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM x1 WHERE f_leak(b) ORDER BY a ASC; UPDATE x1 SET b = b || '_updt' WHERE f_leak(b) RETURNING *; DELETE FROM x1 WHERE f_leak(b) RETURNING *; -- -- Duplicate Policy Names -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE y1 (a int, b text); CREATE TABLE y2 (a int, b text); GRANT ALL ON y1, y2 TO regress_rls_bob; CREATE POLICY p1 ON y1 FOR ALL USING (a % 2 = 0); CREATE POLICY p2 ON y1 FOR SELECT USING (a > 2); CREATE POLICY p1 ON y1 FOR SELECT USING (a % 2 = 1); --fail CREATE POLICY p1 ON y2 FOR ALL USING (a % 2 = 0); --OK ALTER TABLE y1 ENABLE ROW LEVEL SECURITY; ALTER TABLE y2 ENABLE ROW LEVEL SECURITY; -- -- Expression structure with SBV -- -- Create view as table owner. RLS should NOT be applied. SET SESSION AUTHORIZATION regress_rls_alice; CREATE VIEW rls_sbv WITH (security_barrier) AS SELECT * FROM y1 WHERE f_leak(b); EXPLAIN (COSTS OFF) SELECT * FROM rls_sbv WHERE (a = 1); DROP VIEW rls_sbv; -- Create view as role that does not own table. RLS should be applied. SET SESSION AUTHORIZATION regress_rls_bob; CREATE VIEW rls_sbv WITH (security_barrier) AS SELECT * FROM y1 WHERE f_leak(b); EXPLAIN (COSTS OFF) SELECT * FROM rls_sbv WHERE (a = 1); DROP VIEW rls_sbv; -- -- Expression structure -- SET SESSION AUTHORIZATION regress_rls_alice; INSERT INTO y2 (SELECT x, md5(x::text) FROM generate_series(0,20) x); CREATE POLICY p2 ON y2 USING (a % 3 = 0); CREATE POLICY p3 ON y2 USING (a % 4 = 0); SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM y2 WHERE f_leak(b); EXPLAIN (COSTS OFF) SELECT * FROM y2 WHERE f_leak(b); -- -- Qual push-down of leaky functions, when not referring to table -- SELECT * FROM y2 WHERE f_leak('abc'); EXPLAIN (COSTS OFF) SELECT * FROM y2 WHERE f_leak('abc'); CREATE TABLE test_qual_pushdown ( abc text ); INSERT INTO test_qual_pushdown VALUES ('abc'),('def'); SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak(abc); EXPLAIN (COSTS OFF) SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak(abc); SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak(b); EXPLAIN (COSTS OFF) SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak(b); DROP TABLE test_qual_pushdown; -- -- Plancache invalidate on user change. -- RESET SESSION AUTHORIZATION; DROP TABLE t1 CASCADE; CREATE TABLE t1 (a integer); GRANT SELECT ON t1 TO regress_rls_bob, regress_rls_carol; CREATE POLICY p1 ON t1 TO regress_rls_bob USING ((a % 2) = 0); CREATE POLICY p2 ON t1 TO regress_rls_carol USING ((a % 4) = 0); ALTER TABLE t1 ENABLE ROW LEVEL SECURITY; -- Prepare as regress_rls_bob SET ROLE regress_rls_bob; PREPARE role_inval AS SELECT * FROM t1; -- Check plan EXPLAIN (COSTS OFF) EXECUTE role_inval; -- Change to regress_rls_carol SET ROLE regress_rls_carol; -- Check plan- should be different EXPLAIN (COSTS OFF) EXECUTE role_inval; -- Change back to regress_rls_bob SET ROLE regress_rls_bob; -- Check plan- should be back to original EXPLAIN (COSTS OFF) EXECUTE role_inval; -- -- CTE and RLS -- RESET SESSION AUTHORIZATION; DROP TABLE t1 CASCADE; CREATE TABLE t1 (a integer, b text); CREATE POLICY p1 ON t1 USING (a % 2 = 0); ALTER TABLE t1 ENABLE ROW LEVEL SECURITY; GRANT ALL ON t1 TO regress_rls_bob; INSERT INTO t1 (SELECT x, md5(x::text) FROM generate_series(0,20) x); SET SESSION AUTHORIZATION regress_rls_bob; WITH cte1 AS MATERIALIZED (SELECT * FROM t1 WHERE f_leak(b)) SELECT * FROM cte1; EXPLAIN (COSTS OFF) WITH cte1 AS MATERIALIZED (SELECT * FROM t1 WHERE f_leak(b)) SELECT * FROM cte1; WITH cte1 AS (UPDATE t1 SET a = a + 1 RETURNING *) SELECT * FROM cte1; --fail WITH cte1 AS (UPDATE t1 SET a = a RETURNING *) SELECT * FROM cte1; --ok WITH cte1 AS (INSERT INTO t1 VALUES (21, 'Fail') RETURNING *) SELECT * FROM cte1; --fail WITH cte1 AS (INSERT INTO t1 VALUES (20, 'Success') RETURNING *) SELECT * FROM cte1; --ok -- -- Rename Policy -- RESET SESSION AUTHORIZATION; ALTER POLICY p1 ON t1 RENAME TO p1; --fail SELECT polname, relname FROM pg_policy pol JOIN pg_class pc ON (pc.oid = pol.polrelid) WHERE relname = 't1'; ALTER POLICY p1 ON t1 RENAME TO p2; --ok SELECT polname, relname FROM pg_policy pol JOIN pg_class pc ON (pc.oid = pol.polrelid) WHERE relname = 't1'; -- -- Check INSERT SELECT -- SET SESSION AUTHORIZATION regress_rls_bob; CREATE TABLE t2 (a integer, b text); INSERT INTO t2 (SELECT * FROM t1); EXPLAIN (COSTS OFF) INSERT INTO t2 (SELECT * FROM t1); SELECT * FROM t2; EXPLAIN (COSTS OFF) SELECT * FROM t2; CREATE TABLE t3 AS SELECT * FROM t1; SELECT * FROM t3; SELECT * INTO t4 FROM t1; SELECT * FROM t4; -- -- RLS with JOIN -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE blog (id integer, author text, post text); CREATE TABLE comment (blog_id integer, message text); GRANT ALL ON blog, comment TO regress_rls_bob; CREATE POLICY blog_1 ON blog USING (id % 2 = 0); ALTER TABLE blog ENABLE ROW LEVEL SECURITY; INSERT INTO blog VALUES (1, 'alice', 'blog #1'), (2, 'bob', 'blog #1'), (3, 'alice', 'blog #2'), (4, 'alice', 'blog #3'), (5, 'john', 'blog #1'); INSERT INTO comment VALUES (1, 'cool blog'), (1, 'fun blog'), (3, 'crazy blog'), (5, 'what?'), (4, 'insane!'), (2, 'who did it?'); SET SESSION AUTHORIZATION regress_rls_bob; -- Check RLS JOIN with Non-RLS. SELECT id, author, message FROM blog JOIN comment ON id = blog_id; -- Check Non-RLS JOIN with RLS. SELECT id, author, message FROM comment JOIN blog ON id = blog_id; SET SESSION AUTHORIZATION regress_rls_alice; CREATE POLICY comment_1 ON comment USING (blog_id < 4); ALTER TABLE comment ENABLE ROW LEVEL SECURITY; SET SESSION AUTHORIZATION regress_rls_bob; -- Check RLS JOIN RLS SELECT id, author, message FROM blog JOIN comment ON id = blog_id; SELECT id, author, message FROM comment JOIN blog ON id = blog_id; SET SESSION AUTHORIZATION regress_rls_alice; DROP TABLE blog, comment; -- -- Default Deny Policy -- RESET SESSION AUTHORIZATION; DROP POLICY p2 ON t1; ALTER TABLE t1 OWNER TO regress_rls_alice; -- Check that default deny does not apply to superuser. RESET SESSION AUTHORIZATION; SELECT * FROM t1; EXPLAIN (COSTS OFF) SELECT * FROM t1; -- Check that default deny does not apply to table owner. SET SESSION AUTHORIZATION regress_rls_alice; SELECT * FROM t1; EXPLAIN (COSTS OFF) SELECT * FROM t1; -- Check that default deny applies to non-owner/non-superuser when RLS on. SET SESSION AUTHORIZATION regress_rls_bob; SET row_security TO ON; SELECT * FROM t1; EXPLAIN (COSTS OFF) SELECT * FROM t1; SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM t1; EXPLAIN (COSTS OFF) SELECT * FROM t1; -- -- COPY TO/FROM -- RESET SESSION AUTHORIZATION; DROP TABLE copy_t CASCADE; CREATE TABLE copy_t (a integer, b text); CREATE POLICY p1 ON copy_t USING (a % 2 = 0); ALTER TABLE copy_t ENABLE ROW LEVEL SECURITY; GRANT ALL ON copy_t TO regress_rls_bob, regress_rls_exempt_user; INSERT INTO copy_t (SELECT x, md5(x::text) FROM generate_series(0,10) x); -- Check COPY TO as Superuser/owner. RESET SESSION AUTHORIZATION; SET row_security TO OFF; COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; SET row_security TO ON; COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; -- Check COPY TO as user with permissions. SET SESSION AUTHORIZATION regress_rls_bob; SET row_security TO OFF; COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS SET row_security TO ON; COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok -- Check COPY TO as user with permissions and BYPASSRLS SET SESSION AUTHORIZATION regress_rls_exempt_user; SET row_security TO OFF; COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok SET row_security TO ON; COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok -- Check COPY TO as user without permissions. SET row_security TO OFF; SET SESSION AUTHORIZATION regress_rls_carol; SET row_security TO OFF; COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS SET row_security TO ON; COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - permission denied -- Check COPY relation TO; keep it just one row to avoid reordering issues RESET SESSION AUTHORIZATION; SET row_security TO ON; CREATE TABLE copy_rel_to (a integer, b text); CREATE POLICY p1 ON copy_rel_to USING (a % 2 = 0); ALTER TABLE copy_rel_to ENABLE ROW LEVEL SECURITY; GRANT ALL ON copy_rel_to TO regress_rls_bob, regress_rls_exempt_user; INSERT INTO copy_rel_to VALUES (1, md5('1')); -- Check COPY TO as Superuser/owner. RESET SESSION AUTHORIZATION; SET row_security TO OFF; COPY copy_rel_to TO STDOUT WITH DELIMITER ','; SET row_security TO ON; COPY copy_rel_to TO STDOUT WITH DELIMITER ','; -- Check COPY TO as user with permissions. SET SESSION AUTHORIZATION regress_rls_bob; SET row_security TO OFF; COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS SET row_security TO ON; COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok -- Check COPY TO as user with permissions and BYPASSRLS SET SESSION AUTHORIZATION regress_rls_exempt_user; SET row_security TO OFF; COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok SET row_security TO ON; COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok -- Check COPY TO as user without permissions. SET row_security TO OFF; SET SESSION AUTHORIZATION regress_rls_carol; SET row_security TO OFF; COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied SET row_security TO ON; COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied -- Check COPY FROM as Superuser/owner. RESET SESSION AUTHORIZATION; SET row_security TO OFF; SET row_security TO ON; -- Check COPY FROM as user with permissions. SET SESSION AUTHORIZATION regress_rls_bob; SET row_security TO OFF; COPY copy_t FROM STDIN; --fail - would be affected by RLS. SET row_security TO ON; COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS. -- Check COPY FROM as user with permissions and BYPASSRLS SET SESSION AUTHORIZATION regress_rls_exempt_user; SET row_security TO ON; -- Check COPY FROM as user without permissions. SET SESSION AUTHORIZATION regress_rls_carol; SET row_security TO OFF; COPY copy_t FROM STDIN; --fail - permission denied. SET row_security TO ON; COPY copy_t FROM STDIN; --fail - permission denied. RESET SESSION AUTHORIZATION; DROP TABLE copy_t; DROP TABLE copy_rel_to CASCADE; -- Check WHERE CURRENT OF SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE current_check (currentid int, payload text, rlsuser text); GRANT ALL ON current_check TO PUBLIC; INSERT INTO current_check VALUES (1, 'abc', 'regress_rls_bob'), (2, 'bcd', 'regress_rls_bob'), (3, 'cde', 'regress_rls_bob'), (4, 'def', 'regress_rls_bob'); CREATE POLICY p1 ON current_check FOR SELECT USING (currentid % 2 = 0); CREATE POLICY p2 ON current_check FOR DELETE USING (currentid = 4 AND rlsuser = current_user); CREATE POLICY p3 ON current_check FOR UPDATE USING (currentid = 4) WITH CHECK (rlsuser = current_user); ALTER TABLE current_check ENABLE ROW LEVEL SECURITY; SET SESSION AUTHORIZATION regress_rls_bob; -- Can SELECT even rows SELECT * FROM current_check; -- Cannot UPDATE row 2 UPDATE current_check SET payload = payload || '_new' WHERE currentid = 2 RETURNING *; BEGIN; DECLARE current_check_cursor SCROLL CURSOR FOR SELECT * FROM current_check; -- Returns rows that can be seen according to SELECT policy, like plain SELECT -- above (even rows) FETCH ABSOLUTE 1 FROM current_check_cursor; -- Still cannot UPDATE row 2 through cursor UPDATE current_check SET payload = payload || '_new' WHERE CURRENT OF current_check_cursor RETURNING *; -- Can update row 4 through cursor, which is the next visible row FETCH RELATIVE 1 FROM current_check_cursor; UPDATE current_check SET payload = payload || '_new' WHERE CURRENT OF current_check_cursor RETURNING *; SELECT * FROM current_check; -- Plan should be a subquery TID scan EXPLAIN (COSTS OFF) UPDATE current_check SET payload = payload WHERE CURRENT OF current_check_cursor; -- Similarly can only delete row 4 FETCH ABSOLUTE 1 FROM current_check_cursor; DELETE FROM current_check WHERE CURRENT OF current_check_cursor RETURNING *; FETCH RELATIVE 1 FROM current_check_cursor; DELETE FROM current_check WHERE CURRENT OF current_check_cursor RETURNING *; SELECT * FROM current_check; COMMIT; -- -- check pg_stats view filtering -- SET row_security TO ON; SET SESSION AUTHORIZATION regress_rls_alice; ANALYZE current_check; -- Stats visible SELECT row_security_active('current_check'); SELECT attname, most_common_vals FROM pg_stats WHERE tablename = 'current_check' ORDER BY 1; SET SESSION AUTHORIZATION regress_rls_bob; -- Stats not visible SELECT row_security_active('current_check'); SELECT attname, most_common_vals FROM pg_stats WHERE tablename = 'current_check' ORDER BY 1; -- -- Collation support -- BEGIN; CREATE TABLE coll_t (c) AS VALUES ('bar'::text); CREATE POLICY coll_p ON coll_t USING (c < ('foo'::text COLLATE "C")); ALTER TABLE coll_t ENABLE ROW LEVEL SECURITY; GRANT SELECT ON coll_t TO regress_rls_alice; SELECT (string_to_array(polqual, ':'))[7] AS inputcollid FROM pg_policy WHERE polrelid = 'coll_t'::regclass; SET SESSION AUTHORIZATION regress_rls_alice; SELECT * FROM coll_t; ROLLBACK; -- -- Shared Object Dependencies -- RESET SESSION AUTHORIZATION; BEGIN; CREATE ROLE regress_rls_eve; CREATE ROLE regress_rls_frank; CREATE TABLE tbl1 (c) AS VALUES ('bar'::text); GRANT SELECT ON TABLE tbl1 TO regress_rls_eve; CREATE POLICY P ON tbl1 TO regress_rls_eve, regress_rls_frank USING (true); SELECT refclassid::regclass, deptype FROM pg_depend WHERE classid = 'pg_policy'::regclass AND refobjid = 'tbl1'::regclass; SELECT refclassid::regclass, deptype FROM pg_shdepend WHERE classid = 'pg_policy'::regclass AND refobjid IN ('regress_rls_eve'::regrole, 'regress_rls_frank'::regrole); SAVEPOINT q; DROP ROLE regress_rls_eve; --fails due to dependency on POLICY p ROLLBACK TO q; ALTER POLICY p ON tbl1 TO regress_rls_frank USING (true); SAVEPOINT q; DROP ROLE regress_rls_eve; --fails due to dependency on GRANT SELECT ROLLBACK TO q; REVOKE ALL ON TABLE tbl1 FROM regress_rls_eve; SAVEPOINT q; DROP ROLE regress_rls_eve; --succeeds ROLLBACK TO q; SAVEPOINT q; DROP ROLE regress_rls_frank; --fails due to dependency on POLICY p ROLLBACK TO q; DROP POLICY p ON tbl1; SAVEPOINT q; DROP ROLE regress_rls_frank; -- succeeds ROLLBACK TO q; ROLLBACK; -- cleanup -- -- Converting table to view -- BEGIN; CREATE TABLE t (c int); CREATE POLICY p ON t USING (c % 2 = 1); ALTER TABLE t ENABLE ROW LEVEL SECURITY; SAVEPOINT q; CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD SELECT * FROM generate_series(1,5) t0(c); -- fails due to row level security enabled ROLLBACK TO q; ALTER TABLE t DISABLE ROW LEVEL SECURITY; SAVEPOINT q; CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD SELECT * FROM generate_series(1,5) t0(c); -- fails due to policy p on t ROLLBACK TO q; DROP POLICY p ON t; CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD SELECT * FROM generate_series(1,5) t0(c); -- succeeds ROLLBACK; -- -- Policy expression handling -- BEGIN; CREATE TABLE t (c) AS VALUES ('bar'::text); CREATE POLICY p ON t USING (max(c)); -- fails: aggregate functions are not allowed in policy expressions ROLLBACK; -- -- Non-target relations are only subject to SELECT policies -- SET SESSION AUTHORIZATION regress_rls_alice; CREATE TABLE r1 (a int); CREATE TABLE r2 (a int); INSERT INTO r1 VALUES (10), (20); INSERT INTO r2 VALUES (10), (20); GRANT ALL ON r1, r2 TO regress_rls_bob; CREATE POLICY p1 ON r1 USING (true); ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; CREATE POLICY p1 ON r2 FOR SELECT USING (true); CREATE POLICY p2 ON r2 FOR INSERT WITH CHECK (false); CREATE POLICY p3 ON r2 FOR UPDATE USING (false); CREATE POLICY p4 ON r2 FOR DELETE USING (false); ALTER TABLE r2 ENABLE ROW LEVEL SECURITY; SET SESSION AUTHORIZATION regress_rls_bob; SELECT * FROM r1; SELECT * FROM r2; -- r2 is read-only INSERT INTO r2 VALUES (2); -- Not allowed UPDATE r2 SET a = 2 RETURNING *; -- Updates nothing DELETE FROM r2 RETURNING *; -- Deletes nothing -- r2 can be used as a non-target relation in DML INSERT INTO r1 SELECT a + 1 FROM r2 RETURNING *; -- OK UPDATE r1 SET a = r2.a + 2 FROM r2 WHERE r1.a = r2.a RETURNING *; -- OK DELETE FROM r1 USING r2 WHERE r1.a = r2.a + 2 RETURNING *; -- OK SELECT * FROM r1; SELECT * FROM r2; SET SESSION AUTHORIZATION regress_rls_alice; DROP TABLE r1; DROP TABLE r2; -- -- FORCE ROW LEVEL SECURITY applies RLS to owners too -- SET SESSION AUTHORIZATION regress_rls_alice; SET row_security = on; CREATE TABLE r1 (a int); INSERT INTO r1 VALUES (10), (20); CREATE POLICY p1 ON r1 USING (false); ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; ALTER TABLE r1 FORCE ROW LEVEL SECURITY; -- No error, but no rows TABLE r1; -- RLS error INSERT INTO r1 VALUES (1); -- No error (unable to see any rows to update) UPDATE r1 SET a = 1; TABLE r1; -- No error (unable to see any rows to delete) DELETE FROM r1; TABLE r1; SET row_security = off; -- these all fail, would be affected by RLS TABLE r1; UPDATE r1 SET a = 1; DELETE FROM r1; DROP TABLE r1; -- -- FORCE ROW LEVEL SECURITY does not break RI -- SET SESSION AUTHORIZATION regress_rls_alice; SET row_security = on; CREATE TABLE r1 (a int PRIMARY KEY); CREATE TABLE r2 (a int REFERENCES r1); INSERT INTO r1 VALUES (10), (20); INSERT INTO r2 VALUES (10), (20); -- Create policies on r2 which prevent the -- owner from seeing any rows, but RI should -- still see them. CREATE POLICY p1 ON r2 USING (false); ALTER TABLE r2 ENABLE ROW LEVEL SECURITY; ALTER TABLE r2 FORCE ROW LEVEL SECURITY; -- Errors due to rows in r2 DELETE FROM r1; -- Reset r2 to no-RLS DROP POLICY p1 ON r2; ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY; ALTER TABLE r2 DISABLE ROW LEVEL SECURITY; -- clean out r2 for INSERT test below DELETE FROM r2; -- Change r1 to not allow rows to be seen CREATE POLICY p1 ON r1 USING (false); ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; ALTER TABLE r1 FORCE ROW LEVEL SECURITY; -- No rows seen TABLE r1; -- No error, RI still sees that row exists in r1 INSERT INTO r2 VALUES (10); DROP TABLE r2; DROP TABLE r1; -- Ensure cascaded DELETE works CREATE TABLE r1 (a int PRIMARY KEY); CREATE TABLE r2 (a int REFERENCES r1 ON DELETE CASCADE); INSERT INTO r1 VALUES (10), (20); INSERT INTO r2 VALUES (10), (20); -- Create policies on r2 which prevent the -- owner from seeing any rows, but RI should -- still see them. CREATE POLICY p1 ON r2 USING (false); ALTER TABLE r2 ENABLE ROW LEVEL SECURITY; ALTER TABLE r2 FORCE ROW LEVEL SECURITY; -- Deletes all records from both DELETE FROM r1; -- Remove FORCE from r2 ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY; -- As owner, we now bypass RLS -- verify no rows in r2 now TABLE r2; DROP TABLE r2; DROP TABLE r1; -- Ensure cascaded UPDATE works CREATE TABLE r1 (a int PRIMARY KEY); CREATE TABLE r2 (a int REFERENCES r1 ON UPDATE CASCADE); INSERT INTO r1 VALUES (10), (20); INSERT INTO r2 VALUES (10), (20); -- Create policies on r2 which prevent the -- owner from seeing any rows, but RI should -- still see them. CREATE POLICY p1 ON r2 USING (false); ALTER TABLE r2 ENABLE ROW LEVEL SECURITY; ALTER TABLE r2 FORCE ROW LEVEL SECURITY; -- Updates records in both UPDATE r1 SET a = a+5; -- Remove FORCE from r2 ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY; -- As owner, we now bypass RLS -- verify records in r2 updated TABLE r2; DROP TABLE r2; DROP TABLE r1; -- -- Test INSERT+RETURNING applies SELECT policies as -- WithCheckOptions (meaning an error is thrown) -- SET SESSION AUTHORIZATION regress_rls_alice; SET row_security = on; CREATE TABLE r1 (a int); CREATE POLICY p1 ON r1 FOR SELECT USING (false); CREATE POLICY p2 ON r1 FOR INSERT WITH CHECK (true); ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; ALTER TABLE r1 FORCE ROW LEVEL SECURITY; -- Works fine INSERT INTO r1 VALUES (10), (20); -- No error, but no rows TABLE r1; SET row_security = off; -- fail, would be affected by RLS TABLE r1; SET row_security = on; -- Error INSERT INTO r1 VALUES (10), (20) RETURNING *; DROP TABLE r1; -- -- Test UPDATE+RETURNING applies SELECT policies as -- WithCheckOptions (meaning an error is thrown) -- SET SESSION AUTHORIZATION regress_rls_alice; SET row_security = on; CREATE TABLE r1 (a int PRIMARY KEY); CREATE POLICY p1 ON r1 FOR SELECT USING (a < 20); CREATE POLICY p2 ON r1 FOR UPDATE USING (a < 20) WITH CHECK (true); CREATE POLICY p3 ON r1 FOR INSERT WITH CHECK (true); INSERT INTO r1 VALUES (10); ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; ALTER TABLE r1 FORCE ROW LEVEL SECURITY; -- Works fine UPDATE r1 SET a = 30; -- Show updated rows ALTER TABLE r1 NO FORCE ROW LEVEL SECURITY; TABLE r1; -- reset value in r1 for test with RETURNING UPDATE r1 SET a = 10; -- Verify row reset TABLE r1; ALTER TABLE r1 FORCE ROW LEVEL SECURITY; -- Error UPDATE r1 SET a = 30 RETURNING *; -- UPDATE path of INSERT ... ON CONFLICT DO UPDATE should also error out INSERT INTO r1 VALUES (10) ON CONFLICT (a) DO UPDATE SET a = 30 RETURNING *; -- Should still error out without RETURNING (use of arbiter always requires -- SELECT permissions) INSERT INTO r1 VALUES (10) ON CONFLICT (a) DO UPDATE SET a = 30; INSERT INTO r1 VALUES (10) ON CONFLICT ON CONSTRAINT r1_pkey DO UPDATE SET a = 30; DROP TABLE r1; -- Check dependency handling RESET SESSION AUTHORIZATION; CREATE TABLE dep1 (c1 int); CREATE TABLE dep2 (c1 int); CREATE POLICY dep_p1 ON dep1 TO regress_rls_bob USING (c1 > (select max(dep2.c1) from dep2)); ALTER POLICY dep_p1 ON dep1 TO regress_rls_bob,regress_rls_carol; -- Should return one SELECT count(*) = 1 FROM pg_depend WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1') AND refobjid = (SELECT oid FROM pg_class WHERE relname = 'dep2'); ALTER POLICY dep_p1 ON dep1 USING (true); -- Should return one SELECT count(*) = 1 FROM pg_shdepend WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1') AND refobjid = (SELECT oid FROM pg_authid WHERE rolname = 'regress_rls_bob'); -- Should return one SELECT count(*) = 1 FROM pg_shdepend WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1') AND refobjid = (SELECT oid FROM pg_authid WHERE rolname = 'regress_rls_carol'); -- Should return zero SELECT count(*) = 0 FROM pg_depend WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1') AND refobjid = (SELECT oid FROM pg_class WHERE relname = 'dep2'); -- DROP OWNED BY testing RESET SESSION AUTHORIZATION; CREATE ROLE regress_rls_dob_role1; CREATE ROLE regress_rls_dob_role2; CREATE TABLE dob_t1 (c1 int); CREATE TABLE dob_t2 (c1 int) PARTITION BY RANGE (c1); CREATE POLICY p1 ON dob_t1 TO regress_rls_dob_role1 USING (true); DROP OWNED BY regress_rls_dob_role1; DROP POLICY p1 ON dob_t1; -- should fail, already gone CREATE POLICY p1 ON dob_t1 TO regress_rls_dob_role1,regress_rls_dob_role2 USING (true); DROP OWNED BY regress_rls_dob_role1; DROP POLICY p1 ON dob_t1; -- should succeed CREATE POLICY p1 ON dob_t2 TO regress_rls_dob_role1,regress_rls_dob_role2 USING (true); DROP OWNED BY regress_rls_dob_role1; DROP POLICY p1 ON dob_t2; -- should succeed DROP USER regress_rls_dob_role1; DROP USER regress_rls_dob_role2; -- Bug #15708: view + table with RLS should check policies as view owner CREATE TABLE ref_tbl (a int); INSERT INTO ref_tbl VALUES (1); CREATE TABLE rls_tbl (a int); INSERT INTO rls_tbl VALUES (10); ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY; CREATE POLICY p1 ON rls_tbl USING (EXISTS (SELECT 1 FROM ref_tbl)); GRANT SELECT ON ref_tbl TO regress_rls_bob; GRANT SELECT ON rls_tbl TO regress_rls_bob; CREATE VIEW rls_view AS SELECT * FROM rls_tbl; ALTER VIEW rls_view OWNER TO regress_rls_bob; GRANT SELECT ON rls_view TO regress_rls_alice; SET SESSION AUTHORIZATION regress_rls_alice; SELECT * FROM ref_tbl; -- Permission denied SELECT * FROM rls_tbl; -- Permission denied SELECT * FROM rls_view; -- OK RESET SESSION AUTHORIZATION; DROP VIEW rls_view; DROP TABLE rls_tbl; DROP TABLE ref_tbl; -- -- Clean up objects -- RESET SESSION AUTHORIZATION; DROP SCHEMA regress_rls_schema CASCADE; DROP USER regress_rls_alice; DROP USER regress_rls_bob; DROP USER regress_rls_carol; DROP USER regress_rls_dave; DROP USER regress_rls_exempt_user; DROP ROLE regress_rls_group1; DROP ROLE regress_rls_group2; -- Arrange to have a few policies left over, for testing -- pg_dump/pg_restore CREATE SCHEMA regress_rls_schema; CREATE TABLE rls_tbl (c1 int); ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY; CREATE POLICY p1 ON rls_tbl USING (c1 > 5); CREATE POLICY p2 ON rls_tbl FOR SELECT USING (c1 <= 3); CREATE POLICY p3 ON rls_tbl FOR UPDATE USING (c1 <= 3) WITH CHECK (c1 > 5); CREATE POLICY p4 ON rls_tbl FOR DELETE USING (c1 <= 3); CREATE TABLE rls_tbl_force (c1 int); ALTER TABLE rls_tbl_force ENABLE ROW LEVEL SECURITY; ALTER TABLE rls_tbl_force FORCE ROW LEVEL SECURITY; CREATE POLICY p1 ON rls_tbl_force USING (c1 = 5) WITH CHECK (c1 < 5); CREATE POLICY p2 ON rls_tbl_force FOR SELECT USING (c1 = 8); CREATE POLICY p3 ON rls_tbl_force FOR UPDATE USING (c1 = 8) WITH CHECK (c1 >= 5); CREATE POLICY p4 ON rls_tbl_force FOR DELETE USING (c1 = 8); pgFormatter-4.2/t/pg-test-files/sql/rowtypes.sql000066400000000000000000000374501361326045100220250ustar00rootroot00000000000000-- -- ROWTYPES -- -- Make both a standalone composite type and a table rowtype create type complex as (r float8, i float8); create temp table fullname (first text, last text); -- Nested composite create type quad as (c1 complex, c2 complex); -- Some simple tests of I/O conversions and row construction select (1.1,2.2)::complex, row((3.3,4.4),(5.5,null))::quad; select row('Joe', 'Blow')::fullname, '(Joe,Blow)'::fullname; select '(Joe,von Blow)'::fullname, '(Joe,d''Blow)'::fullname; select '(Joe,"von""Blow")'::fullname, E'(Joe,d\\\\Blow)'::fullname; select '(Joe,"Blow,Jr")'::fullname; select '(Joe,)'::fullname; -- ok, null 2nd column select '(Joe)'::fullname; -- bad select '(Joe,,)'::fullname; -- bad select '[]'::fullname; -- bad select ' (Joe,Blow) '::fullname; -- ok, extra whitespace select '(Joe,Blow) /'::fullname; -- bad create temp table quadtable(f1 int, q quad); insert into quadtable values (1, ((3.3,4.4),(5.5,6.6))); insert into quadtable values (2, ((null,4.4),(5.5,6.6))); select * from quadtable; select f1, q.c1 from quadtable; -- fails, q is a table reference select f1, (q).c1, (qq.q).c1.i from quadtable qq; create temp table people (fn fullname, bd date); insert into people values ('(Joe,Blow)', '1984-01-10'); select * from people; -- at the moment this will not work due to ALTER TABLE inadequacy: alter table fullname add column suffix text default ''; -- but this should work: alter table fullname add column suffix text default null; select * from people; -- test insertion/updating of subfields update people set fn.suffix = 'Jr'; select * from people; insert into quadtable (f1, q.c1.r, q.c2.i) values(44,55,66); select * from quadtable; -- The object here is to ensure that toasted references inside -- composite values don't cause problems. The large f1 value will -- be toasted inside pp, it must still work after being copied to people. create temp table pp (f1 text); insert into pp values (repeat('abcdefghijkl', 100000)); insert into people select ('Jim', f1, null)::fullname, current_date from pp; select (fn).first, substr((fn).last, 1, 20), length((fn).last) from people; -- Test row comparison semantics. Prior to PG 8.2 we did this in a totally -- non-spec-compliant way. select ROW(1,2) < ROW(1,3) as true; select ROW(1,2) < ROW(1,1) as false; select ROW(1,2) < ROW(1,NULL) as null; select ROW(1,2,3) < ROW(1,3,NULL) as true; -- the NULL is not examined select ROW(11,'ABC') < ROW(11,'DEF') as true; select ROW(11,'ABC') > ROW(11,'DEF') as false; select ROW(12,'ABC') > ROW(11,'DEF') as true; -- = and <> have different NULL-behavior than < etc select ROW(1,2,3) < ROW(1,NULL,4) as null; select ROW(1,2,3) = ROW(1,NULL,4) as false; select ROW(1,2,3) <> ROW(1,NULL,4) as true; -- We allow operators beyond the six standard ones, if they have btree -- operator classes. select ROW('ABC','DEF') ~<=~ ROW('DEF','ABC') as true; select ROW('ABC','DEF') ~>=~ ROW('DEF','ABC') as false; select ROW('ABC','DEF') ~~ ROW('DEF','ABC') as fail; -- Comparisons of ROW() expressions can cope with some type mismatches select ROW(1,2) = ROW(1,2::int8); select ROW(1,2) in (ROW(3,4), ROW(1,2)); select ROW(1,2) in (ROW(3,4), ROW(1,2::int8)); -- Check row comparison with a subselect select unique1, unique2 from tenk1 where (unique1, unique2) < any (select ten, ten from tenk1 where hundred < 3) and unique1 <= 20 order by 1; -- Also check row comparison with an indexable condition explain (costs off) select thousand, tenthous from tenk1 where (thousand, tenthous) >= (997, 5000) order by thousand, tenthous; select thousand, tenthous from tenk1 where (thousand, tenthous) >= (997, 5000) order by thousand, tenthous; explain (costs off) select thousand, tenthous, four from tenk1 where (thousand, tenthous, four) > (998, 5000, 3) order by thousand, tenthous; select thousand, tenthous, four from tenk1 where (thousand, tenthous, four) > (998, 5000, 3) order by thousand, tenthous; explain (costs off) select thousand, tenthous from tenk1 where (998, 5000) < (thousand, tenthous) order by thousand, tenthous; select thousand, tenthous from tenk1 where (998, 5000) < (thousand, tenthous) order by thousand, tenthous; explain (costs off) select thousand, hundred from tenk1 where (998, 5000) < (thousand, hundred) order by thousand, hundred; select thousand, hundred from tenk1 where (998, 5000) < (thousand, hundred) order by thousand, hundred; -- Test case for bug #14010: indexed row comparisons fail with nulls create temp table test_table (a text, b text); insert into test_table values ('a', 'b'); insert into test_table select 'a', null from generate_series(1,1000); insert into test_table values ('b', 'a'); create index on test_table (a,b); set enable_sort = off; explain (costs off) select a,b from test_table where (a,b) > ('a','a') order by a,b; select a,b from test_table where (a,b) > ('a','a') order by a,b; reset enable_sort; -- Check row comparisons with IN select * from int8_tbl i8 where i8 in (row(123,456)); -- fail, type mismatch explain (costs off) select * from int8_tbl i8 where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)'); select * from int8_tbl i8 where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)'); -- Check some corner cases involving empty rowtypes select ROW(); select ROW() IS NULL; select ROW() = ROW(); -- Check ability to create arrays of anonymous rowtypes select array[ row(1,2), row(3,4), row(5,6) ]; -- Check ability to compare an anonymous row to elements of an array select row(1,1.1) = any (array[ row(7,7.7), row(1,1.1), row(0,0.0) ]); select row(1,1.1) = any (array[ row(7,7.7), row(1,1.0), row(0,0.0) ]); -- Check behavior with a non-comparable rowtype create type cantcompare as (p point, r float8); create temp table cc (f1 cantcompare); insert into cc values('("(1,2)",3)'); insert into cc values('("(4,5)",6)'); select * from cc order by f1; -- fail, but should complain about cantcompare -- -- Tests for record_{eq,cmp} -- create type testtype1 as (a int, b int); -- all true select row(1, 2)::testtype1 < row(1, 3)::testtype1; select row(1, 2)::testtype1 <= row(1, 3)::testtype1; select row(1, 2)::testtype1 = row(1, 2)::testtype1; select row(1, 2)::testtype1 <> row(1, 3)::testtype1; select row(1, 3)::testtype1 >= row(1, 2)::testtype1; select row(1, 3)::testtype1 > row(1, 2)::testtype1; -- all false select row(1, -2)::testtype1 < row(1, -3)::testtype1; select row(1, -2)::testtype1 <= row(1, -3)::testtype1; select row(1, -2)::testtype1 = row(1, -3)::testtype1; select row(1, -2)::testtype1 <> row(1, -2)::testtype1; select row(1, -3)::testtype1 >= row(1, -2)::testtype1; select row(1, -3)::testtype1 > row(1, -2)::testtype1; -- true, but see *< below select row(1, -2)::testtype1 < row(1, 3)::testtype1; -- mismatches create type testtype3 as (a int, b text); select row(1, 2)::testtype1 < row(1, 'abc')::testtype3; select row(1, 2)::testtype1 <> row(1, 'abc')::testtype3; create type testtype5 as (a int); select row(1, 2)::testtype1 < row(1)::testtype5; select row(1, 2)::testtype1 <> row(1)::testtype5; -- non-comparable types create type testtype6 as (a int, b point); select row(1, '(1,2)')::testtype6 < row(1, '(1,3)')::testtype6; select row(1, '(1,2)')::testtype6 <> row(1, '(1,3)')::testtype6; drop type testtype1, testtype3, testtype5, testtype6; -- -- Tests for record_image_{eq,cmp} -- create type testtype1 as (a int, b int); -- all true select row(1, 2)::testtype1 *< row(1, 3)::testtype1; select row(1, 2)::testtype1 *<= row(1, 3)::testtype1; select row(1, 2)::testtype1 *= row(1, 2)::testtype1; select row(1, 2)::testtype1 *<> row(1, 3)::testtype1; select row(1, 3)::testtype1 *>= row(1, 2)::testtype1; select row(1, 3)::testtype1 *> row(1, 2)::testtype1; -- all false select row(1, -2)::testtype1 *< row(1, -3)::testtype1; select row(1, -2)::testtype1 *<= row(1, -3)::testtype1; select row(1, -2)::testtype1 *= row(1, -3)::testtype1; select row(1, -2)::testtype1 *<> row(1, -2)::testtype1; select row(1, -3)::testtype1 *>= row(1, -2)::testtype1; select row(1, -3)::testtype1 *> row(1, -2)::testtype1; -- This returns the "wrong" order because record_image_cmp works on -- unsigned datums without knowing about the actual data type. select row(1, -2)::testtype1 *< row(1, 3)::testtype1; -- other types create type testtype2 as (a smallint, b bool); -- byval different sizes select row(1, true)::testtype2 *< row(2, true)::testtype2; select row(-2, true)::testtype2 *< row(-1, true)::testtype2; select row(0, false)::testtype2 *< row(0, true)::testtype2; select row(0, false)::testtype2 *<> row(0, true)::testtype2; create type testtype3 as (a int, b text); -- variable length select row(1, 'abc')::testtype3 *< row(1, 'abd')::testtype3; select row(1, 'abc')::testtype3 *< row(1, 'abcd')::testtype3; select row(1, 'abc')::testtype3 *> row(1, 'abd')::testtype3; select row(1, 'abc')::testtype3 *<> row(1, 'abd')::testtype3; create type testtype4 as (a int, b point); -- by ref, fixed length select row(1, '(1,2)')::testtype4 *< row(1, '(1,3)')::testtype4; select row(1, '(1,2)')::testtype4 *<> row(1, '(1,3)')::testtype4; -- mismatches select row(1, 2)::testtype1 *< row(1, 'abc')::testtype3; select row(1, 2)::testtype1 *<> row(1, 'abc')::testtype3; create type testtype5 as (a int); select row(1, 2)::testtype1 *< row(1)::testtype5; select row(1, 2)::testtype1 *<> row(1)::testtype5; -- non-comparable types create type testtype6 as (a int, b point); select row(1, '(1,2)')::testtype6 *< row(1, '(1,3)')::testtype6; select row(1, '(1,2)')::testtype6 *>= row(1, '(1,3)')::testtype6; select row(1, '(1,2)')::testtype6 *<> row(1, '(1,3)')::testtype6; -- anonymous rowtypes in coldeflists select q.a, q.b = row(2), q.c = array[row(3)], q.d = row(row(4)) from unnest(array[row(1, row(2), array[row(3)], row(row(4))), row(2, row(3), array[row(4)], row(row(5)))]) as q(a int, b record, c record[], d record); drop type testtype1, testtype2, testtype3, testtype4, testtype5, testtype6; -- -- Test case derived from bug #5716: check multiple uses of a rowtype result -- BEGIN; CREATE TABLE price ( id SERIAL PRIMARY KEY, active BOOLEAN NOT NULL, price NUMERIC ); CREATE TYPE price_input AS ( id INTEGER, price NUMERIC ); CREATE TYPE price_key AS ( id INTEGER ); CREATE FUNCTION price_key_from_table(price) RETURNS price_key AS $$ SELECT $1.id $$ LANGUAGE SQL; CREATE FUNCTION price_key_from_input(price_input) RETURNS price_key AS $$ SELECT $1.id $$ LANGUAGE SQL; insert into price values (1,false,42), (10,false,100), (11,true,17.99); UPDATE price SET active = true, price = input_prices.price FROM unnest(ARRAY[(10, 123.00), (11, 99.99)]::price_input[]) input_prices WHERE price_key_from_table(price.*) = price_key_from_input(input_prices.*); select * from price; rollback; -- -- Test case derived from bug #9085: check * qualification of composite -- parameters for SQL functions -- create temp table compos (f1 int, f2 text); create function fcompos1(v compos) returns void as $$ insert into compos values (v); -- fail $$ language sql; create function fcompos1(v compos) returns void as $$ insert into compos values (v.*); $$ language sql; create function fcompos2(v compos) returns void as $$ select fcompos1(v); $$ language sql; create function fcompos3(v compos) returns void as $$ select fcompos1(fcompos3.v.*); $$ language sql; select fcompos1(row(1,'one')); select fcompos2(row(2,'two')); select fcompos3(row(3,'three')); select * from compos; -- -- We allow I/O conversion casts from composite types to strings to be -- invoked via cast syntax, but not functional syntax. This is because -- the latter is too prone to be invoked unintentionally. -- select cast (fullname as text) from fullname; select fullname::text from fullname; select text(fullname) from fullname; -- error select fullname.text from fullname; -- error -- same, but RECORD instead of named composite type: select cast (row('Jim', 'Beam') as text); select (row('Jim', 'Beam'))::text; select text(row('Jim', 'Beam')); -- error select (row('Jim', 'Beam')).text; -- error -- -- Check the equivalence of functional and column notation -- insert into fullname values ('Joe', 'Blow'); select f.last from fullname f; select last(f) from fullname f; create function longname(fullname) returns text language sql as $$select $1.first || ' ' || $1.last$$; select f.longname from fullname f; select longname(f) from fullname f; -- Starting in v11, the notational form does matter if there's ambiguity alter table fullname add column longname text; select f.longname from fullname f; select longname(f) from fullname f; -- -- Test that composite values are seen to have the correct column names -- (bug #11210 and other reports) -- select row_to_json(i) from int8_tbl i; select row_to_json(i) from int8_tbl i(x,y); create temp view vv1 as select * from int8_tbl; select row_to_json(i) from vv1 i; select row_to_json(i) from vv1 i(x,y); select row_to_json(ss) from (select q1, q2 from int8_tbl) as ss; select row_to_json(ss) from (select q1, q2 from int8_tbl offset 0) as ss; select row_to_json(ss) from (select q1 as a, q2 as b from int8_tbl) as ss; select row_to_json(ss) from (select q1 as a, q2 as b from int8_tbl offset 0) as ss; select row_to_json(ss) from (select q1 as a, q2 as b from int8_tbl) as ss(x,y); select row_to_json(ss) from (select q1 as a, q2 as b from int8_tbl offset 0) as ss(x,y); explain (costs off) select row_to_json(q) from (select thousand, tenthous from tenk1 where thousand = 42 and tenthous < 2000 offset 0) q; select row_to_json(q) from (select thousand, tenthous from tenk1 where thousand = 42 and tenthous < 2000 offset 0) q; select row_to_json(q) from (select thousand as x, tenthous as y from tenk1 where thousand = 42 and tenthous < 2000 offset 0) q; select row_to_json(q) from (select thousand as x, tenthous as y from tenk1 where thousand = 42 and tenthous < 2000 offset 0) q(a,b); create temp table tt1 as select * from int8_tbl limit 2; create temp table tt2 () inherits(tt1); insert into tt2 values(0,0); select row_to_json(r) from (select q2,q1 from tt1 offset 0) r; -- check no-op rowtype conversions create temp table tt3 () inherits(tt2); insert into tt3 values(33,44); select row_to_json(tt3::tt2::tt1) from tt3; -- -- IS [NOT] NULL should not recurse into nested composites (bug #14235) -- explain (verbose, costs off) select r, r is null as isnull, r is not null as isnotnull from (values (1,row(1,2)), (1,row(null,null)), (1,null), (null,row(1,2)), (null,row(null,null)), (null,null) ) r(a,b); select r, r is null as isnull, r is not null as isnotnull from (values (1,row(1,2)), (1,row(null,null)), (1,null), (null,row(1,2)), (null,row(null,null)), (null,null) ) r(a,b); explain (verbose, costs off) with r(a,b) as materialized (values (1,row(1,2)), (1,row(null,null)), (1,null), (null,row(1,2)), (null,row(null,null)), (null,null) ) select r, r is null as isnull, r is not null as isnotnull from r; with r(a,b) as materialized (values (1,row(1,2)), (1,row(null,null)), (1,null), (null,row(1,2)), (null,row(null,null)), (null,null) ) select r, r is null as isnull, r is not null as isnotnull from r; -- -- Tests for component access / FieldSelect -- CREATE TABLE compositetable(a text, b text); INSERT INTO compositetable(a, b) VALUES('fa', 'fb'); -- composite type columns can't directly be accessed (error) SELECT d.a FROM (SELECT compositetable AS d FROM compositetable) s; -- but can be accessed with proper parens SELECT (d).a, (d).b FROM (SELECT compositetable AS d FROM compositetable) s; -- system columns can't be accessed in composite types (error) SELECT (d).ctid FROM (SELECT compositetable AS d FROM compositetable) s; -- accessing non-existing column in NULL datum errors out SELECT (NULL::compositetable).nonexistant; -- existing column in a NULL composite yield NULL SELECT (NULL::compositetable).a; -- oids can't be accessed in composite types (error) SELECT (NULL::compositetable).oid; DROP TABLE compositetable; pgFormatter-4.2/t/pg-test-files/sql/rules.sql000066400000000000000000001150661361326045100212630ustar00rootroot00000000000000-- -- RULES -- From Jan's original setup_ruletest.sql and run_ruletest.sql -- - thomas 1998-09-13 -- -- -- Tables and rules for the view test -- create table rtest_t1 (a int4, b int4); create table rtest_t2 (a int4, b int4); create table rtest_t3 (a int4, b int4); create view rtest_v1 as select * from rtest_t1; create rule rtest_v1_ins as on insert to rtest_v1 do instead insert into rtest_t1 values (new.a, new.b); create rule rtest_v1_upd as on update to rtest_v1 do instead update rtest_t1 set a = new.a, b = new.b where a = old.a; create rule rtest_v1_del as on delete to rtest_v1 do instead delete from rtest_t1 where a = old.a; -- Test comments COMMENT ON RULE rtest_v1_bad ON rtest_v1 IS 'bad rule'; COMMENT ON RULE rtest_v1_del ON rtest_v1 IS 'delete rule'; COMMENT ON RULE rtest_v1_del ON rtest_v1 IS NULL; -- -- Tables and rules for the constraint update/delete test -- -- Note: -- Now that we have multiple action rule support, we check -- both possible syntaxes to define them (The last action -- can but must not have a semicolon at the end). -- create table rtest_system (sysname text, sysdesc text); create table rtest_interface (sysname text, ifname text); create table rtest_person (pname text, pdesc text); create table rtest_admin (pname text, sysname text); create rule rtest_sys_upd as on update to rtest_system do also ( update rtest_interface set sysname = new.sysname where sysname = old.sysname; update rtest_admin set sysname = new.sysname where sysname = old.sysname ); create rule rtest_sys_del as on delete to rtest_system do also ( delete from rtest_interface where sysname = old.sysname; delete from rtest_admin where sysname = old.sysname; ); create rule rtest_pers_upd as on update to rtest_person do also update rtest_admin set pname = new.pname where pname = old.pname; create rule rtest_pers_del as on delete to rtest_person do also delete from rtest_admin where pname = old.pname; -- -- Tables and rules for the logging test -- create table rtest_emp (ename char(20), salary money); create table rtest_emplog (ename char(20), who name, action char(10), newsal money, oldsal money); create table rtest_empmass (ename char(20), salary money); create rule rtest_emp_ins as on insert to rtest_emp do insert into rtest_emplog values (new.ename, current_user, 'hired', new.salary, '0.00'); create rule rtest_emp_upd as on update to rtest_emp where new.salary != old.salary do insert into rtest_emplog values (new.ename, current_user, 'honored', new.salary, old.salary); create rule rtest_emp_del as on delete to rtest_emp do insert into rtest_emplog values (old.ename, current_user, 'fired', '0.00', old.salary); -- -- Tables and rules for the multiple cascaded qualified instead -- rule test -- create table rtest_t4 (a int4, b text); create table rtest_t5 (a int4, b text); create table rtest_t6 (a int4, b text); create table rtest_t7 (a int4, b text); create table rtest_t8 (a int4, b text); create table rtest_t9 (a int4, b text); create rule rtest_t4_ins1 as on insert to rtest_t4 where new.a >= 10 and new.a < 20 do instead insert into rtest_t5 values (new.a, new.b); create rule rtest_t4_ins2 as on insert to rtest_t4 where new.a >= 20 and new.a < 30 do insert into rtest_t6 values (new.a, new.b); create rule rtest_t5_ins as on insert to rtest_t5 where new.a > 15 do insert into rtest_t7 values (new.a, new.b); create rule rtest_t6_ins as on insert to rtest_t6 where new.a > 25 do instead insert into rtest_t8 values (new.a, new.b); -- -- Tables and rules for the rule fire order test -- -- As of PG 7.3, the rules should fire in order by name, regardless -- of INSTEAD attributes or creation order. -- create table rtest_order1 (a int4); create table rtest_order2 (a int4, b int4, c text); create sequence rtest_seq; create rule rtest_order_r3 as on insert to rtest_order1 do instead insert into rtest_order2 values (new.a, nextval('rtest_seq'), 'rule 3 - this should run 3rd'); create rule rtest_order_r4 as on insert to rtest_order1 where a < 100 do instead insert into rtest_order2 values (new.a, nextval('rtest_seq'), 'rule 4 - this should run 4th'); create rule rtest_order_r2 as on insert to rtest_order1 do insert into rtest_order2 values (new.a, nextval('rtest_seq'), 'rule 2 - this should run 2nd'); create rule rtest_order_r1 as on insert to rtest_order1 do instead insert into rtest_order2 values (new.a, nextval('rtest_seq'), 'rule 1 - this should run 1st'); -- -- Tables and rules for the instead nothing test -- create table rtest_nothn1 (a int4, b text); create table rtest_nothn2 (a int4, b text); create table rtest_nothn3 (a int4, b text); create table rtest_nothn4 (a int4, b text); create rule rtest_nothn_r1 as on insert to rtest_nothn1 where new.a >= 10 and new.a < 20 do instead nothing; create rule rtest_nothn_r2 as on insert to rtest_nothn1 where new.a >= 30 and new.a < 40 do instead nothing; create rule rtest_nothn_r3 as on insert to rtest_nothn2 where new.a >= 100 do instead insert into rtest_nothn3 values (new.a, new.b); create rule rtest_nothn_r4 as on insert to rtest_nothn2 do instead nothing; -- -- Tests on a view that is select * of a table -- and has insert/update/delete instead rules to -- behave close like the real table. -- -- -- We need test date later -- insert into rtest_t2 values (1, 21); insert into rtest_t2 values (2, 22); insert into rtest_t2 values (3, 23); insert into rtest_t3 values (1, 31); insert into rtest_t3 values (2, 32); insert into rtest_t3 values (3, 33); insert into rtest_t3 values (4, 34); insert into rtest_t3 values (5, 35); -- insert values insert into rtest_v1 values (1, 11); insert into rtest_v1 values (2, 12); select * from rtest_v1; -- delete with constant expression delete from rtest_v1 where a = 1; select * from rtest_v1; insert into rtest_v1 values (1, 11); delete from rtest_v1 where b = 12; select * from rtest_v1; insert into rtest_v1 values (2, 12); insert into rtest_v1 values (2, 13); select * from rtest_v1; ** Remember the delete rule on rtest_v1: It says ** DO INSTEAD DELETE FROM rtest_t1 WHERE a = old.a ** So this time both rows with a = 2 must get deleted \p \r delete from rtest_v1 where b = 12; select * from rtest_v1; delete from rtest_v1; -- insert select insert into rtest_v1 select * from rtest_t2; select * from rtest_v1; delete from rtest_v1; -- same with swapped targetlist insert into rtest_v1 (b, a) select b, a from rtest_t2; select * from rtest_v1; -- now with only one target attribute insert into rtest_v1 (a) select a from rtest_t3; select * from rtest_v1; select * from rtest_v1 where b isnull; -- let attribute a differ (must be done on rtest_t1 - see above) update rtest_t1 set a = a + 10 where b isnull; delete from rtest_v1 where b isnull; select * from rtest_v1; -- now updates with constant expression update rtest_v1 set b = 42 where a = 2; select * from rtest_v1; update rtest_v1 set b = 99 where b = 42; select * from rtest_v1; update rtest_v1 set b = 88 where b < 50; select * from rtest_v1; delete from rtest_v1; insert into rtest_v1 select rtest_t2.a, rtest_t3.b from rtest_t2, rtest_t3 where rtest_t2.a = rtest_t3.a; select * from rtest_v1; -- updates in a mergejoin update rtest_v1 set b = rtest_t2.b from rtest_t2 where rtest_v1.a = rtest_t2.a; select * from rtest_v1; insert into rtest_v1 select * from rtest_t3; select * from rtest_v1; update rtest_t1 set a = a + 10 where b > 30; select * from rtest_v1; update rtest_v1 set a = rtest_t3.a + 20 from rtest_t3 where rtest_v1.b = rtest_t3.b; select * from rtest_v1; -- -- Test for constraint updates/deletes -- insert into rtest_system values ('orion', 'Linux Jan Wieck'); insert into rtest_system values ('notjw', 'WinNT Jan Wieck (notebook)'); insert into rtest_system values ('neptun', 'Fileserver'); insert into rtest_interface values ('orion', 'eth0'); insert into rtest_interface values ('orion', 'eth1'); insert into rtest_interface values ('notjw', 'eth0'); insert into rtest_interface values ('neptun', 'eth0'); insert into rtest_person values ('jw', 'Jan Wieck'); insert into rtest_person values ('bm', 'Bruce Momjian'); insert into rtest_admin values ('jw', 'orion'); insert into rtest_admin values ('jw', 'notjw'); insert into rtest_admin values ('bm', 'neptun'); update rtest_system set sysname = 'pluto' where sysname = 'neptun'; select * from rtest_interface; select * from rtest_admin; update rtest_person set pname = 'jwieck' where pdesc = 'Jan Wieck'; -- Note: use ORDER BY here to ensure consistent output across all systems. -- The above UPDATE affects two rows with equal keys, so they could be -- updated in either order depending on the whim of the local qsort(). select * from rtest_admin order by pname, sysname; delete from rtest_system where sysname = 'orion'; select * from rtest_interface; select * from rtest_admin; -- -- Rule qualification test -- insert into rtest_emp values ('wiecc', '5000.00'); insert into rtest_emp values ('gates', '80000.00'); update rtest_emp set ename = 'wiecx' where ename = 'wiecc'; update rtest_emp set ename = 'wieck', salary = '6000.00' where ename = 'wiecx'; update rtest_emp set salary = '7000.00' where ename = 'wieck'; delete from rtest_emp where ename = 'gates'; select ename, who = current_user as "matches user", action, newsal, oldsal from rtest_emplog order by ename, action, newsal; insert into rtest_empmass values ('meyer', '4000.00'); insert into rtest_empmass values ('maier', '5000.00'); insert into rtest_empmass values ('mayr', '6000.00'); insert into rtest_emp select * from rtest_empmass; select ename, who = current_user as "matches user", action, newsal, oldsal from rtest_emplog order by ename, action, newsal; update rtest_empmass set salary = salary + '1000.00'; update rtest_emp set salary = rtest_empmass.salary from rtest_empmass where rtest_emp.ename = rtest_empmass.ename; select ename, who = current_user as "matches user", action, newsal, oldsal from rtest_emplog order by ename, action, newsal; delete from rtest_emp using rtest_empmass where rtest_emp.ename = rtest_empmass.ename; select ename, who = current_user as "matches user", action, newsal, oldsal from rtest_emplog order by ename, action, newsal; -- -- Multiple cascaded qualified instead rule test -- insert into rtest_t4 values (1, 'Record should go to rtest_t4'); insert into rtest_t4 values (2, 'Record should go to rtest_t4'); insert into rtest_t4 values (10, 'Record should go to rtest_t5'); insert into rtest_t4 values (15, 'Record should go to rtest_t5'); insert into rtest_t4 values (19, 'Record should go to rtest_t5 and t7'); insert into rtest_t4 values (20, 'Record should go to rtest_t4 and t6'); insert into rtest_t4 values (26, 'Record should go to rtest_t4 and t8'); insert into rtest_t4 values (28, 'Record should go to rtest_t4 and t8'); insert into rtest_t4 values (30, 'Record should go to rtest_t4'); insert into rtest_t4 values (40, 'Record should go to rtest_t4'); select * from rtest_t4; select * from rtest_t5; select * from rtest_t6; select * from rtest_t7; select * from rtest_t8; delete from rtest_t4; delete from rtest_t5; delete from rtest_t6; delete from rtest_t7; delete from rtest_t8; insert into rtest_t9 values (1, 'Record should go to rtest_t4'); insert into rtest_t9 values (2, 'Record should go to rtest_t4'); insert into rtest_t9 values (10, 'Record should go to rtest_t5'); insert into rtest_t9 values (15, 'Record should go to rtest_t5'); insert into rtest_t9 values (19, 'Record should go to rtest_t5 and t7'); insert into rtest_t9 values (20, 'Record should go to rtest_t4 and t6'); insert into rtest_t9 values (26, 'Record should go to rtest_t4 and t8'); insert into rtest_t9 values (28, 'Record should go to rtest_t4 and t8'); insert into rtest_t9 values (30, 'Record should go to rtest_t4'); insert into rtest_t9 values (40, 'Record should go to rtest_t4'); insert into rtest_t4 select * from rtest_t9 where a < 20; select * from rtest_t4; select * from rtest_t5; select * from rtest_t6; select * from rtest_t7; select * from rtest_t8; insert into rtest_t4 select * from rtest_t9 where b ~ 'and t8'; select * from rtest_t4; select * from rtest_t5; select * from rtest_t6; select * from rtest_t7; select * from rtest_t8; insert into rtest_t4 select a + 1, b from rtest_t9 where a in (20, 30, 40); select * from rtest_t4; select * from rtest_t5; select * from rtest_t6; select * from rtest_t7; select * from rtest_t8; -- -- Check that the ordering of rules fired is correct -- insert into rtest_order1 values (1); select * from rtest_order2; -- -- Check if instead nothing w/without qualification works -- insert into rtest_nothn1 values (1, 'want this'); insert into rtest_nothn1 values (2, 'want this'); insert into rtest_nothn1 values (10, 'don''t want this'); insert into rtest_nothn1 values (19, 'don''t want this'); insert into rtest_nothn1 values (20, 'want this'); insert into rtest_nothn1 values (29, 'want this'); insert into rtest_nothn1 values (30, 'don''t want this'); insert into rtest_nothn1 values (39, 'don''t want this'); insert into rtest_nothn1 values (40, 'want this'); insert into rtest_nothn1 values (50, 'want this'); insert into rtest_nothn1 values (60, 'want this'); select * from rtest_nothn1; insert into rtest_nothn2 values (10, 'too small'); insert into rtest_nothn2 values (50, 'too small'); insert into rtest_nothn2 values (100, 'OK'); insert into rtest_nothn2 values (200, 'OK'); select * from rtest_nothn2; select * from rtest_nothn3; delete from rtest_nothn1; delete from rtest_nothn2; delete from rtest_nothn3; insert into rtest_nothn4 values (1, 'want this'); insert into rtest_nothn4 values (2, 'want this'); insert into rtest_nothn4 values (10, 'don''t want this'); insert into rtest_nothn4 values (19, 'don''t want this'); insert into rtest_nothn4 values (20, 'want this'); insert into rtest_nothn4 values (29, 'want this'); insert into rtest_nothn4 values (30, 'don''t want this'); insert into rtest_nothn4 values (39, 'don''t want this'); insert into rtest_nothn4 values (40, 'want this'); insert into rtest_nothn4 values (50, 'want this'); insert into rtest_nothn4 values (60, 'want this'); insert into rtest_nothn1 select * from rtest_nothn4; select * from rtest_nothn1; delete from rtest_nothn4; insert into rtest_nothn4 values (10, 'too small'); insert into rtest_nothn4 values (50, 'too small'); insert into rtest_nothn4 values (100, 'OK'); insert into rtest_nothn4 values (200, 'OK'); insert into rtest_nothn2 select * from rtest_nothn4; select * from rtest_nothn2; select * from rtest_nothn3; create table rtest_view1 (a int4, b text, v bool); create table rtest_view2 (a int4); create table rtest_view3 (a int4, b text); create table rtest_view4 (a int4, b text, c int4); create view rtest_vview1 as select a, b from rtest_view1 X where 0 < (select count(*) from rtest_view2 Y where Y.a = X.a); create view rtest_vview2 as select a, b from rtest_view1 where v; create view rtest_vview3 as select a, b from rtest_vview2 X where 0 < (select count(*) from rtest_view2 Y where Y.a = X.a); create view rtest_vview4 as select X.a, X.b, count(Y.a) as refcount from rtest_view1 X, rtest_view2 Y where X.a = Y.a group by X.a, X.b; create function rtest_viewfunc1(int4) returns int4 as 'select count(*)::int4 from rtest_view2 where a = $1' language sql; create view rtest_vview5 as select a, b, rtest_viewfunc1(a) as refcount from rtest_view1; insert into rtest_view1 values (1, 'item 1', 't'); insert into rtest_view1 values (2, 'item 2', 't'); insert into rtest_view1 values (3, 'item 3', 't'); insert into rtest_view1 values (4, 'item 4', 'f'); insert into rtest_view1 values (5, 'item 5', 't'); insert into rtest_view1 values (6, 'item 6', 'f'); insert into rtest_view1 values (7, 'item 7', 't'); insert into rtest_view1 values (8, 'item 8', 't'); insert into rtest_view2 values (2); insert into rtest_view2 values (2); insert into rtest_view2 values (4); insert into rtest_view2 values (5); insert into rtest_view2 values (7); insert into rtest_view2 values (7); insert into rtest_view2 values (7); insert into rtest_view2 values (7); select * from rtest_vview1; select * from rtest_vview2; select * from rtest_vview3; select * from rtest_vview4 order by a, b; select * from rtest_vview5; insert into rtest_view3 select * from rtest_vview1 where a < 7; select * from rtest_view3; delete from rtest_view3; insert into rtest_view3 select * from rtest_vview2 where a != 5 and b !~ '2'; select * from rtest_view3; delete from rtest_view3; insert into rtest_view3 select * from rtest_vview3; select * from rtest_view3; delete from rtest_view3; insert into rtest_view4 select * from rtest_vview4 where 3 > refcount; select * from rtest_view4 order by a, b; delete from rtest_view4; insert into rtest_view4 select * from rtest_vview5 where a > 2 and refcount = 0; select * from rtest_view4; delete from rtest_view4; -- -- Test for computations in views -- create table rtest_comp ( part text, unit char(4), size float ); create table rtest_unitfact ( unit char(4), factor float ); create view rtest_vcomp as select X.part, (X.size * Y.factor) as size_in_cm from rtest_comp X, rtest_unitfact Y where X.unit = Y.unit; insert into rtest_unitfact values ('m', 100.0); insert into rtest_unitfact values ('cm', 1.0); insert into rtest_unitfact values ('inch', 2.54); insert into rtest_comp values ('p1', 'm', 5.0); insert into rtest_comp values ('p2', 'm', 3.0); insert into rtest_comp values ('p3', 'cm', 5.0); insert into rtest_comp values ('p4', 'cm', 15.0); insert into rtest_comp values ('p5', 'inch', 7.0); insert into rtest_comp values ('p6', 'inch', 4.4); select * from rtest_vcomp order by part; select * from rtest_vcomp where size_in_cm > 10.0 order by size_in_cm using >; -- -- In addition run the (slightly modified) queries from the -- programmers manual section on the rule system. -- CREATE TABLE shoe_data ( shoename char(10), -- primary key sh_avail integer, -- available # of pairs slcolor char(10), -- preferred shoelace color slminlen float, -- minimum shoelace length slmaxlen float, -- maximum shoelace length slunit char(8) -- length unit ); CREATE TABLE shoelace_data ( sl_name char(10), -- primary key sl_avail integer, -- available # of pairs sl_color char(10), -- shoelace color sl_len float, -- shoelace length sl_unit char(8) -- length unit ); CREATE TABLE unit ( un_name char(8), -- the primary key un_fact float -- factor to transform to cm ); CREATE VIEW shoe AS SELECT sh.shoename, sh.sh_avail, sh.slcolor, sh.slminlen, sh.slminlen * un.un_fact AS slminlen_cm, sh.slmaxlen, sh.slmaxlen * un.un_fact AS slmaxlen_cm, sh.slunit FROM shoe_data sh, unit un WHERE sh.slunit = un.un_name; CREATE VIEW shoelace AS SELECT s.sl_name, s.sl_avail, s.sl_color, s.sl_len, s.sl_unit, s.sl_len * u.un_fact AS sl_len_cm FROM shoelace_data s, unit u WHERE s.sl_unit = u.un_name; CREATE VIEW shoe_ready AS SELECT rsh.shoename, rsh.sh_avail, rsl.sl_name, rsl.sl_avail, int4smaller(rsh.sh_avail, rsl.sl_avail) AS total_avail FROM shoe rsh, shoelace rsl WHERE rsl.sl_color = rsh.slcolor AND rsl.sl_len_cm >= rsh.slminlen_cm AND rsl.sl_len_cm <= rsh.slmaxlen_cm; INSERT INTO unit VALUES ('cm', 1.0); INSERT INTO unit VALUES ('m', 100.0); INSERT INTO unit VALUES ('inch', 2.54); INSERT INTO shoe_data VALUES ('sh1', 2, 'black', 70.0, 90.0, 'cm'); INSERT INTO shoe_data VALUES ('sh2', 0, 'black', 30.0, 40.0, 'inch'); INSERT INTO shoe_data VALUES ('sh3', 4, 'brown', 50.0, 65.0, 'cm'); INSERT INTO shoe_data VALUES ('sh4', 3, 'brown', 40.0, 50.0, 'inch'); INSERT INTO shoelace_data VALUES ('sl1', 5, 'black', 80.0, 'cm'); INSERT INTO shoelace_data VALUES ('sl2', 6, 'black', 100.0, 'cm'); INSERT INTO shoelace_data VALUES ('sl3', 0, 'black', 35.0 , 'inch'); INSERT INTO shoelace_data VALUES ('sl4', 8, 'black', 40.0 , 'inch'); INSERT INTO shoelace_data VALUES ('sl5', 4, 'brown', 1.0 , 'm'); INSERT INTO shoelace_data VALUES ('sl6', 0, 'brown', 0.9 , 'm'); INSERT INTO shoelace_data VALUES ('sl7', 7, 'brown', 60 , 'cm'); INSERT INTO shoelace_data VALUES ('sl8', 1, 'brown', 40 , 'inch'); -- SELECTs in doc SELECT * FROM shoelace ORDER BY sl_name; SELECT * FROM shoe_ready WHERE total_avail >= 2 ORDER BY 1; CREATE TABLE shoelace_log ( sl_name char(10), -- shoelace changed sl_avail integer, -- new available value log_who name, -- who did it log_when timestamp -- when ); -- Want "log_who" to be CURRENT_USER, -- but that is non-portable for the regression test -- - thomas 1999-02-21 CREATE RULE log_shoelace AS ON UPDATE TO shoelace_data WHERE NEW.sl_avail != OLD.sl_avail DO INSERT INTO shoelace_log VALUES ( NEW.sl_name, NEW.sl_avail, 'Al Bundy', 'epoch' ); UPDATE shoelace_data SET sl_avail = 6 WHERE sl_name = 'sl7'; SELECT * FROM shoelace_log; CREATE RULE shoelace_ins AS ON INSERT TO shoelace DO INSTEAD INSERT INTO shoelace_data VALUES ( NEW.sl_name, NEW.sl_avail, NEW.sl_color, NEW.sl_len, NEW.sl_unit); CREATE RULE shoelace_upd AS ON UPDATE TO shoelace DO INSTEAD UPDATE shoelace_data SET sl_name = NEW.sl_name, sl_avail = NEW.sl_avail, sl_color = NEW.sl_color, sl_len = NEW.sl_len, sl_unit = NEW.sl_unit WHERE sl_name = OLD.sl_name; CREATE RULE shoelace_del AS ON DELETE TO shoelace DO INSTEAD DELETE FROM shoelace_data WHERE sl_name = OLD.sl_name; CREATE TABLE shoelace_arrive ( arr_name char(10), arr_quant integer ); CREATE TABLE shoelace_ok ( ok_name char(10), ok_quant integer ); CREATE RULE shoelace_ok_ins AS ON INSERT TO shoelace_ok DO INSTEAD UPDATE shoelace SET sl_avail = sl_avail + NEW.ok_quant WHERE sl_name = NEW.ok_name; INSERT INTO shoelace_arrive VALUES ('sl3', 10); INSERT INTO shoelace_arrive VALUES ('sl6', 20); INSERT INTO shoelace_arrive VALUES ('sl8', 20); SELECT * FROM shoelace ORDER BY sl_name; insert into shoelace_ok select * from shoelace_arrive; SELECT * FROM shoelace ORDER BY sl_name; SELECT * FROM shoelace_log ORDER BY sl_name; CREATE VIEW shoelace_obsolete AS SELECT * FROM shoelace WHERE NOT EXISTS (SELECT shoename FROM shoe WHERE slcolor = sl_color); CREATE VIEW shoelace_candelete AS SELECT * FROM shoelace_obsolete WHERE sl_avail = 0; insert into shoelace values ('sl9', 0, 'pink', 35.0, 'inch', 0.0); insert into shoelace values ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0); -- Unsupported (even though a similar updatable view construct is) insert into shoelace values ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0) on conflict do nothing; SELECT * FROM shoelace_obsolete ORDER BY sl_len_cm; SELECT * FROM shoelace_candelete; DELETE FROM shoelace WHERE EXISTS (SELECT * FROM shoelace_candelete WHERE sl_name = shoelace.sl_name); SELECT * FROM shoelace ORDER BY sl_name; SELECT * FROM shoe ORDER BY shoename; SELECT count(*) FROM shoe; -- -- Simple test of qualified ON INSERT ... this did not work in 7.0 ... -- create table rules_foo (f1 int); create table rules_foo2 (f1 int); create rule rules_foorule as on insert to rules_foo where f1 < 100 do instead nothing; insert into rules_foo values(1); insert into rules_foo values(1001); select * from rules_foo; drop rule rules_foorule on rules_foo; -- this should fail because f1 is not exposed for unqualified reference: create rule rules_foorule as on insert to rules_foo where f1 < 100 do instead insert into rules_foo2 values (f1); -- this is the correct way: create rule rules_foorule as on insert to rules_foo where f1 < 100 do instead insert into rules_foo2 values (new.f1); insert into rules_foo values(2); insert into rules_foo values(100); select * from rules_foo; select * from rules_foo2; drop rule rules_foorule on rules_foo; drop table rules_foo; drop table rules_foo2; -- -- Test rules containing INSERT ... SELECT, which is a very ugly special -- case as of 7.1. Example is based on bug report from Joel Burton. -- create table pparent (pid int, txt text); insert into pparent values (1,'parent1'); insert into pparent values (2,'parent2'); create table cchild (pid int, descrip text); insert into cchild values (1,'descrip1'); create view vview as select pparent.pid, txt, descrip from pparent left join cchild using (pid); create rule rrule as on update to vview do instead ( insert into cchild (pid, descrip) select old.pid, new.descrip where old.descrip isnull; update cchild set descrip = new.descrip where cchild.pid = old.pid; ); select * from vview; update vview set descrip='test1' where pid=1; select * from vview; update vview set descrip='test2' where pid=2; select * from vview; update vview set descrip='test3' where pid=3; select * from vview; select * from cchild; drop rule rrule on vview; drop view vview; drop table pparent; drop table cchild; -- -- Check that ruleutils are working -- -- temporarily disable fancy output, so view changes create less diff noise \a\t SELECT viewname, definition FROM pg_views WHERE schemaname IN ('pg_catalog', 'public') ORDER BY viewname; SELECT tablename, rulename, definition FROM pg_rules WHERE schemaname IN ('pg_catalog', 'public') ORDER BY tablename, rulename; -- restore normal output mode \a\t -- -- CREATE OR REPLACE RULE -- CREATE TABLE ruletest_tbl (a int, b int); CREATE TABLE ruletest_tbl2 (a int, b int); CREATE OR REPLACE RULE myrule AS ON INSERT TO ruletest_tbl DO INSTEAD INSERT INTO ruletest_tbl2 VALUES (10, 10); INSERT INTO ruletest_tbl VALUES (99, 99); CREATE OR REPLACE RULE myrule AS ON INSERT TO ruletest_tbl DO INSTEAD INSERT INTO ruletest_tbl2 VALUES (1000, 1000); INSERT INTO ruletest_tbl VALUES (99, 99); SELECT * FROM ruletest_tbl2; -- Check that rewrite rules splitting one INSERT into multiple -- conditional statements does not disable FK checking. create table rule_and_refint_t1 ( id1a integer, id1b integer, primary key (id1a, id1b) ); create table rule_and_refint_t2 ( id2a integer, id2c integer, primary key (id2a, id2c) ); create table rule_and_refint_t3 ( id3a integer, id3b integer, id3c integer, data text, primary key (id3a, id3b, id3c), foreign key (id3a, id3b) references rule_and_refint_t1 (id1a, id1b), foreign key (id3a, id3c) references rule_and_refint_t2 (id2a, id2c) ); insert into rule_and_refint_t1 values (1, 11); insert into rule_and_refint_t1 values (1, 12); insert into rule_and_refint_t1 values (2, 21); insert into rule_and_refint_t1 values (2, 22); insert into rule_and_refint_t2 values (1, 11); insert into rule_and_refint_t2 values (1, 12); insert into rule_and_refint_t2 values (2, 21); insert into rule_and_refint_t2 values (2, 22); insert into rule_and_refint_t3 values (1, 11, 11, 'row1'); insert into rule_and_refint_t3 values (1, 11, 12, 'row2'); insert into rule_and_refint_t3 values (1, 12, 11, 'row3'); insert into rule_and_refint_t3 values (1, 12, 12, 'row4'); insert into rule_and_refint_t3 values (1, 11, 13, 'row5'); insert into rule_and_refint_t3 values (1, 13, 11, 'row6'); -- Ordinary table insert into rule_and_refint_t3 values (1, 13, 11, 'row6') on conflict do nothing; -- rule not fired, so fk violation insert into rule_and_refint_t3 values (1, 13, 11, 'row6') on conflict (id3a, id3b, id3c) do update set id3b = excluded.id3b; -- rule fired, so unsupported insert into shoelace values ('sl9', 0, 'pink', 35.0, 'inch', 0.0) on conflict (sl_name) do update set sl_avail = excluded.sl_avail; create rule rule_and_refint_t3_ins as on insert to rule_and_refint_t3 where (exists (select 1 from rule_and_refint_t3 where (((rule_and_refint_t3.id3a = new.id3a) and (rule_and_refint_t3.id3b = new.id3b)) and (rule_and_refint_t3.id3c = new.id3c)))) do instead update rule_and_refint_t3 set data = new.data where (((rule_and_refint_t3.id3a = new.id3a) and (rule_and_refint_t3.id3b = new.id3b)) and (rule_and_refint_t3.id3c = new.id3c)); insert into rule_and_refint_t3 values (1, 11, 13, 'row7'); insert into rule_and_refint_t3 values (1, 13, 11, 'row8'); -- -- disallow dropping a view's rule (bug #5072) -- create view rules_fooview as select 'rules_foo'::text; drop rule "_RETURN" on rules_fooview; drop view rules_fooview; -- -- test conversion of table to view (needed to load some pg_dump files) -- create table rules_fooview (x int, y text); select xmin, * from rules_fooview; create rule "_RETURN" as on select to rules_fooview do instead select 1 as x, 'aaa'::text as y; select * from rules_fooview; select xmin, * from rules_fooview; -- fail, views don't have such a column select reltoastrelid, relkind, relfrozenxid from pg_class where oid = 'rules_fooview'::regclass; drop view rules_fooview; -- trying to convert a partitioned table to view is not allowed create table rules_fooview (x int, y text) partition by list (x); create rule "_RETURN" as on select to rules_fooview do instead select 1 as x, 'aaa'::text as y; -- nor can one convert a partition to view create table rules_fooview_part partition of rules_fooview for values in (1); create rule "_RETURN" as on select to rules_fooview_part do instead select 1 as x, 'aaa'::text as y; -- -- check for planner problems with complex inherited UPDATES -- create table id (id serial primary key, name text); -- currently, must respecify PKEY for each inherited subtable create table test_1 (id integer primary key) inherits (id); create table test_2 (id integer primary key) inherits (id); create table test_3 (id integer primary key) inherits (id); insert into test_1 (name) values ('Test 1'); insert into test_1 (name) values ('Test 2'); insert into test_2 (name) values ('Test 3'); insert into test_2 (name) values ('Test 4'); insert into test_3 (name) values ('Test 5'); insert into test_3 (name) values ('Test 6'); create view id_ordered as select * from id order by id; create rule update_id_ordered as on update to id_ordered do instead update id set name = new.name where id = old.id; select * from id_ordered; update id_ordered set name = 'update 2' where id = 2; update id_ordered set name = 'update 4' where id = 4; update id_ordered set name = 'update 5' where id = 5; select * from id_ordered; drop table id cascade; -- -- check corner case where an entirely-dummy subplan is created by -- constraint exclusion -- create temp table t1 (a integer primary key); create temp table t1_1 (check (a >= 0 and a < 10)) inherits (t1); create temp table t1_2 (check (a >= 10 and a < 20)) inherits (t1); create rule t1_ins_1 as on insert to t1 where new.a >= 0 and new.a < 10 do instead insert into t1_1 values (new.a); create rule t1_ins_2 as on insert to t1 where new.a >= 10 and new.a < 20 do instead insert into t1_2 values (new.a); create rule t1_upd_1 as on update to t1 where old.a >= 0 and old.a < 10 do instead update t1_1 set a = new.a where a = old.a; create rule t1_upd_2 as on update to t1 where old.a >= 10 and old.a < 20 do instead update t1_2 set a = new.a where a = old.a; set constraint_exclusion = on; insert into t1 select * from generate_series(5,19,1) g; update t1 set a = 4 where a = 5; select * from only t1; select * from only t1_1; select * from only t1_2; reset constraint_exclusion; -- test various flavors of pg_get_viewdef() select pg_get_viewdef('shoe'::regclass) as unpretty; select pg_get_viewdef('shoe'::regclass,true) as pretty; select pg_get_viewdef('shoe'::regclass,0) as prettier; -- -- check multi-row VALUES in rules -- create table rules_src(f1 int, f2 int); create table rules_log(f1 int, f2 int, tag text); insert into rules_src values(1,2), (11,12); create rule r1 as on update to rules_src do also insert into rules_log values(old.*, 'old'), (new.*, 'new'); update rules_src set f2 = f2 + 1; update rules_src set f2 = f2 * 10; select * from rules_src; select * from rules_log; create rule r2 as on update to rules_src do also values(old.*, 'old'), (new.*, 'new'); update rules_src set f2 = f2 / 10; select * from rules_src; select * from rules_log; create rule r3 as on delete to rules_src do notify rules_src_deletion; \d+ rules_src -- -- Ensure an aliased target relation for insert is correctly deparsed. -- create rule r4 as on insert to rules_src do instead insert into rules_log AS trgt SELECT NEW.* RETURNING trgt.f1, trgt.f2; create rule r5 as on update to rules_src do instead UPDATE rules_log AS trgt SET tag = 'updated' WHERE trgt.f1 = new.f1; \d+ rules_src -- -- check alter rename rule -- CREATE TABLE rule_t1 (a INT); CREATE VIEW rule_v1 AS SELECT * FROM rule_t1; CREATE RULE InsertRule AS ON INSERT TO rule_v1 DO INSTEAD INSERT INTO rule_t1 VALUES(new.a); ALTER RULE InsertRule ON rule_v1 RENAME to NewInsertRule; INSERT INTO rule_v1 VALUES(1); SELECT * FROM rule_v1; \d+ rule_v1 -- -- error conditions for alter rename rule -- ALTER RULE InsertRule ON rule_v1 RENAME TO NewInsertRule; -- doesn't exist ALTER RULE NewInsertRule ON rule_v1 RENAME TO "_RETURN"; -- already exists ALTER RULE "_RETURN" ON rule_v1 RENAME TO abc; -- ON SELECT rule cannot be renamed DROP VIEW rule_v1; DROP TABLE rule_t1; -- -- check display of VALUES in view definitions -- create view rule_v1 as values(1,2); \d+ rule_v1 drop view rule_v1; create view rule_v1(x) as values(1,2); \d+ rule_v1 drop view rule_v1; create view rule_v1(x) as select * from (values(1,2)) v; \d+ rule_v1 drop view rule_v1; create view rule_v1(x) as select * from (values(1,2)) v(q,w); \d+ rule_v1 drop view rule_v1; -- -- Check DO INSTEAD rules with ON CONFLICT -- CREATE TABLE hats ( hat_name char(10) primary key, hat_color char(10) -- hat color ); CREATE TABLE hat_data ( hat_name char(10), hat_color char(10) -- hat color ); create unique index hat_data_unique_idx on hat_data (hat_name COLLATE "C" bpchar_pattern_ops); -- DO NOTHING with ON CONFLICT CREATE RULE hat_nosert AS ON INSERT TO hats DO INSTEAD INSERT INTO hat_data VALUES ( NEW.hat_name, NEW.hat_color) ON CONFLICT (hat_name COLLATE "C" bpchar_pattern_ops) WHERE hat_color = 'green' DO NOTHING RETURNING *; SELECT definition FROM pg_rules WHERE tablename = 'hats' ORDER BY rulename; -- Works (projects row) INSERT INTO hats VALUES ('h7', 'black') RETURNING *; -- Works (does nothing) INSERT INTO hats VALUES ('h7', 'black') RETURNING *; SELECT tablename, rulename, definition FROM pg_rules WHERE tablename = 'hats'; DROP RULE hat_nosert ON hats; -- DO NOTHING without ON CONFLICT CREATE RULE hat_nosert_all AS ON INSERT TO hats DO INSTEAD INSERT INTO hat_data VALUES ( NEW.hat_name, NEW.hat_color) ON CONFLICT DO NOTHING RETURNING *; SELECT definition FROM pg_rules WHERE tablename = 'hats' ORDER BY rulename; DROP RULE hat_nosert_all ON hats; -- Works (does nothing) INSERT INTO hats VALUES ('h7', 'black') RETURNING *; -- DO UPDATE with a WHERE clause CREATE RULE hat_upsert AS ON INSERT TO hats DO INSTEAD INSERT INTO hat_data VALUES ( NEW.hat_name, NEW.hat_color) ON CONFLICT (hat_name) DO UPDATE SET hat_name = hat_data.hat_name, hat_color = excluded.hat_color WHERE excluded.hat_color <> 'forbidden' AND hat_data.* != excluded.* RETURNING *; SELECT definition FROM pg_rules WHERE tablename = 'hats' ORDER BY rulename; -- Works (does upsert) INSERT INTO hats VALUES ('h8', 'black') RETURNING *; SELECT * FROM hat_data WHERE hat_name = 'h8'; INSERT INTO hats VALUES ('h8', 'white') RETURNING *; SELECT * FROM hat_data WHERE hat_name = 'h8'; INSERT INTO hats VALUES ('h8', 'forbidden') RETURNING *; SELECT * FROM hat_data WHERE hat_name = 'h8'; SELECT tablename, rulename, definition FROM pg_rules WHERE tablename = 'hats'; -- ensure explain works for on insert conflict rules explain (costs off) INSERT INTO hats VALUES ('h8', 'forbidden') RETURNING *; -- ensure upserting into a rule, with a CTE (different offsets!) works WITH data(hat_name, hat_color) AS MATERIALIZED ( VALUES ('h8', 'green'), ('h9', 'blue'), ('h7', 'forbidden') ) INSERT INTO hats SELECT * FROM data RETURNING *; EXPLAIN (costs off) WITH data(hat_name, hat_color) AS MATERIALIZED ( VALUES ('h8', 'green'), ('h9', 'blue'), ('h7', 'forbidden') ) INSERT INTO hats SELECT * FROM data RETURNING *; SELECT * FROM hat_data WHERE hat_name IN ('h8', 'h9', 'h7') ORDER BY hat_name; DROP RULE hat_upsert ON hats; drop table hats; drop table hat_data; -- test for pg_get_functiondef properly regurgitating SET parameters -- Note that the function is kept around to stress pg_dump. CREATE FUNCTION func_with_set_params() RETURNS integer AS 'select 1;' LANGUAGE SQL SET search_path TO PG_CATALOG SET extra_float_digits TO 2 SET work_mem TO '4MB' SET datestyle to iso, mdy SET local_preload_libraries TO "Mixed/Case", 'c:/''a"/path', '', '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' IMMUTABLE STRICT; SELECT pg_get_functiondef('func_with_set_params()'::regprocedure); -- tests for pg_get_*def with invalid objects SELECT pg_get_constraintdef(0); SELECT pg_get_functiondef(0); SELECT pg_get_indexdef(0); SELECT pg_get_ruledef(0); SELECT pg_get_statisticsobjdef(0); SELECT pg_get_triggerdef(0); SELECT pg_get_viewdef(0); SELECT pg_get_function_arguments(0); SELECT pg_get_function_identity_arguments(0); SELECT pg_get_function_result(0); SELECT pg_get_function_arg_default(0, 0); SELECT pg_get_function_arg_default('pg_class'::regclass, 0); SELECT pg_get_partkeydef(0); -- test rename for a rule defined on a partitioned table CREATE TABLE rules_parted_table (a int) PARTITION BY LIST (a); CREATE TABLE rules_parted_table_1 PARTITION OF rules_parted_table FOR VALUES IN (1); CREATE RULE rules_parted_table_insert AS ON INSERT to rules_parted_table DO INSTEAD INSERT INTO rules_parted_table_1 VALUES (NEW.*); ALTER RULE rules_parted_table_insert ON rules_parted_table RENAME TO rules_parted_table_insert_redirect; DROP TABLE rules_parted_table; -- -- Test enabling/disabling -- CREATE TABLE ruletest1 (a int); CREATE TABLE ruletest2 (b int); CREATE RULE rule1 AS ON INSERT TO ruletest1 DO INSTEAD INSERT INTO ruletest2 VALUES (NEW.*); INSERT INTO ruletest1 VALUES (1); ALTER TABLE ruletest1 DISABLE RULE rule1; INSERT INTO ruletest1 VALUES (2); ALTER TABLE ruletest1 ENABLE RULE rule1; SET session_replication_role = replica; INSERT INTO ruletest1 VALUES (3); ALTER TABLE ruletest1 ENABLE REPLICA RULE rule1; INSERT INTO ruletest1 VALUES (4); RESET session_replication_role; INSERT INTO ruletest1 VALUES (5); SELECT * FROM ruletest1; SELECT * FROM ruletest2; DROP TABLE ruletest1; DROP TABLE ruletest2; pgFormatter-4.2/t/pg-test-files/sql/sanity_check.sql000066400000000000000000000026571361326045100225760ustar00rootroot00000000000000VACUUM; -- -- sanity check, if we don't have indices the test will take years to -- complete. But skip TOAST relations (since they will have varying -- names depending on the current OID counter) as well as temp tables -- of other backends (to avoid timing-dependent behavior). -- -- temporarily disable fancy output, so catalog changes create less diff noise \a\t SELECT relname, relhasindex FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = relnamespace WHERE relkind IN ('r', 'p') AND (nspname ~ '^pg_temp_') IS NOT TRUE ORDER BY relname; -- restore normal output mode \a\t -- -- another sanity check: every system catalog that has OIDs should have -- a unique index on OID. This ensures that the OIDs will be unique, -- even after the OID counter wraps around. -- We exclude non-system tables from the check by looking at nspname. -- SELECT relname, nspname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = relnamespace JOIN pg_attribute a ON (attrelid = c.oid AND attname = 'oid') WHERE relkind = 'r' and c.oid < 16384 AND ((nspname ~ '^pg_') IS NOT FALSE) AND NOT EXISTS (SELECT 1 FROM pg_index i WHERE indrelid = c.oid AND indkey[0] = a.attnum AND indnatts = 1 AND indisunique AND indimmediate); -- check that relations without storage don't have relfilenode SELECT relname, relkind FROM pg_class WHERE relkind IN ('v', 'c', 'f', 'p', 'I') AND relfilenode <> 0; pgFormatter-4.2/t/pg-test-files/sql/security_label.sql000066400000000000000000000030501361326045100231240ustar00rootroot00000000000000-- -- Test for facilities of security label -- -- initial setups SET client_min_messages TO 'warning'; DROP ROLE IF EXISTS regress_seclabel_user1; DROP ROLE IF EXISTS regress_seclabel_user2; RESET client_min_messages; CREATE USER regress_seclabel_user1 WITH CREATEROLE; CREATE USER regress_seclabel_user2; CREATE TABLE seclabel_tbl1 (a int, b text); CREATE TABLE seclabel_tbl2 (x int, y text); CREATE VIEW seclabel_view1 AS SELECT * FROM seclabel_tbl2; CREATE FUNCTION seclabel_four() RETURNS integer AS $$SELECT 4$$ language sql; CREATE DOMAIN seclabel_domain AS text; ALTER TABLE seclabel_tbl1 OWNER TO regress_seclabel_user1; ALTER TABLE seclabel_tbl2 OWNER TO regress_seclabel_user2; -- -- Test of SECURITY LABEL statement without a plugin -- SECURITY LABEL ON TABLE seclabel_tbl1 IS 'classified'; -- fail SECURITY LABEL FOR 'dummy' ON TABLE seclabel_tbl1 IS 'classified'; -- fail SECURITY LABEL ON TABLE seclabel_tbl1 IS '...invalid label...'; -- fail SECURITY LABEL ON TABLE seclabel_tbl3 IS 'unclassified'; -- fail SECURITY LABEL ON ROLE regress_seclabel_user1 IS 'classified'; -- fail SECURITY LABEL FOR 'dummy' ON ROLE regress_seclabel_user1 IS 'classified'; -- fail SECURITY LABEL ON ROLE regress_seclabel_user1 IS '...invalid label...'; -- fail SECURITY LABEL ON ROLE regress_seclabel_user3 IS 'unclassified'; -- fail -- clean up objects DROP FUNCTION seclabel_four(); DROP DOMAIN seclabel_domain; DROP VIEW seclabel_view1; DROP TABLE seclabel_tbl1; DROP TABLE seclabel_tbl2; DROP USER regress_seclabel_user1; DROP USER regress_seclabel_user2; pgFormatter-4.2/t/pg-test-files/sql/select.sql000066400000000000000000000175561361326045100214150ustar00rootroot00000000000000-- -- SELECT -- -- btree index -- awk '{if($1<10){print;}else{next;}}' onek.data | sort +0n -1 -- SELECT * FROM onek WHERE onek.unique1 < 10 ORDER BY onek.unique1; -- -- awk '{if($1<20){print $1,$14;}else{next;}}' onek.data | sort +0nr -1 -- SELECT onek.unique1, onek.stringu1 FROM onek WHERE onek.unique1 < 20 ORDER BY unique1 using >; -- -- awk '{if($1>980){print $1,$14;}else{next;}}' onek.data | sort +1d -2 -- SELECT onek.unique1, onek.stringu1 FROM onek WHERE onek.unique1 > 980 ORDER BY stringu1 using <; -- -- awk '{if($1>980){print $1,$16;}else{next;}}' onek.data | -- sort +1d -2 +0nr -1 -- SELECT onek.unique1, onek.string4 FROM onek WHERE onek.unique1 > 980 ORDER BY string4 using <, unique1 using >; -- -- awk '{if($1>980){print $1,$16;}else{next;}}' onek.data | -- sort +1dr -2 +0n -1 -- SELECT onek.unique1, onek.string4 FROM onek WHERE onek.unique1 > 980 ORDER BY string4 using >, unique1 using <; -- -- awk '{if($1<20){print $1,$16;}else{next;}}' onek.data | -- sort +0nr -1 +1d -2 -- SELECT onek.unique1, onek.string4 FROM onek WHERE onek.unique1 < 20 ORDER BY unique1 using >, string4 using <; -- -- awk '{if($1<20){print $1,$16;}else{next;}}' onek.data | -- sort +0n -1 +1dr -2 -- SELECT onek.unique1, onek.string4 FROM onek WHERE onek.unique1 < 20 ORDER BY unique1 using <, string4 using >; -- -- test partial btree indexes -- -- As of 7.2, planner probably won't pick an indexscan without stats, -- so ANALYZE first. Also, we want to prevent it from picking a bitmapscan -- followed by sort, because that could hide index ordering problems. -- ANALYZE onek2; SET enable_seqscan TO off; SET enable_bitmapscan TO off; SET enable_sort TO off; -- -- awk '{if($1<10){print $0;}else{next;}}' onek.data | sort +0n -1 -- SELECT onek2.* FROM onek2 WHERE onek2.unique1 < 10; -- -- awk '{if($1<20){print $1,$14;}else{next;}}' onek.data | sort +0nr -1 -- SELECT onek2.unique1, onek2.stringu1 FROM onek2 WHERE onek2.unique1 < 20 ORDER BY unique1 using >; -- -- awk '{if($1>980){print $1,$14;}else{next;}}' onek.data | sort +1d -2 -- SELECT onek2.unique1, onek2.stringu1 FROM onek2 WHERE onek2.unique1 > 980; RESET enable_seqscan; RESET enable_bitmapscan; RESET enable_sort; SELECT two, stringu1, ten, string4 INTO TABLE tmp FROM onek; -- -- awk '{print $1,$2;}' person.data | -- awk '{if(NF!=2){print $3,$2;}else{print;}}' - emp.data | -- awk '{if(NF!=2){print $3,$2;}else{print;}}' - student.data | -- awk 'BEGIN{FS=" ";}{if(NF!=2){print $4,$5;}else{print;}}' - stud_emp.data -- -- SELECT name, age FROM person*; ??? check if different SELECT p.name, p.age FROM person* p; -- -- awk '{print $1,$2;}' person.data | -- awk '{if(NF!=2){print $3,$2;}else{print;}}' - emp.data | -- awk '{if(NF!=2){print $3,$2;}else{print;}}' - student.data | -- awk 'BEGIN{FS=" ";}{if(NF!=1){print $4,$5;}else{print;}}' - stud_emp.data | -- sort +1nr -2 -- SELECT p.name, p.age FROM person* p ORDER BY age using >, name; -- -- Test some cases involving whole-row Var referencing a subquery -- select foo from (select 1 offset 0) as foo; select foo from (select null offset 0) as foo; select foo from (select 'xyzzy',1,null offset 0) as foo; -- -- Test VALUES lists -- select * from onek, (values(147, 'RFAAAA'), (931, 'VJAAAA')) as v (i, j) WHERE onek.unique1 = v.i and onek.stringu1 = v.j; -- a more complex case -- looks like we're coding lisp :-) select * from onek, (values ((select i from (values(10000), (2), (389), (1000), (2000), ((select 10029))) as foo(i) order by i asc limit 1))) bar (i) where onek.unique1 = bar.i; -- try VALUES in a subquery select * from onek where (unique1,ten) in (values (1,1), (20,0), (99,9), (17,99)) order by unique1; -- VALUES is also legal as a standalone query or a set-operation member VALUES (1,2), (3,4+4), (7,77.7); VALUES (1,2), (3,4+4), (7,77.7) UNION ALL SELECT 2+2, 57 UNION ALL TABLE int8_tbl; -- -- Test ORDER BY options -- CREATE TEMP TABLE foo (f1 int); INSERT INTO foo VALUES (42),(3),(10),(7),(null),(null),(1); SELECT * FROM foo ORDER BY f1; SELECT * FROM foo ORDER BY f1 ASC; -- same thing SELECT * FROM foo ORDER BY f1 NULLS FIRST; SELECT * FROM foo ORDER BY f1 DESC; SELECT * FROM foo ORDER BY f1 DESC NULLS LAST; -- check if indexscans do the right things CREATE INDEX fooi ON foo (f1); SET enable_sort = false; SELECT * FROM foo ORDER BY f1; SELECT * FROM foo ORDER BY f1 NULLS FIRST; SELECT * FROM foo ORDER BY f1 DESC; SELECT * FROM foo ORDER BY f1 DESC NULLS LAST; DROP INDEX fooi; CREATE INDEX fooi ON foo (f1 DESC); SELECT * FROM foo ORDER BY f1; SELECT * FROM foo ORDER BY f1 NULLS FIRST; SELECT * FROM foo ORDER BY f1 DESC; SELECT * FROM foo ORDER BY f1 DESC NULLS LAST; DROP INDEX fooi; CREATE INDEX fooi ON foo (f1 DESC NULLS LAST); SELECT * FROM foo ORDER BY f1; SELECT * FROM foo ORDER BY f1 NULLS FIRST; SELECT * FROM foo ORDER BY f1 DESC; SELECT * FROM foo ORDER BY f1 DESC NULLS LAST; -- -- Test planning of some cases with partial indexes -- -- partial index is usable explain (costs off) select * from onek2 where unique2 = 11 and stringu1 = 'ATAAAA'; select * from onek2 where unique2 = 11 and stringu1 = 'ATAAAA'; -- actually run the query with an analyze to use the partial index explain (costs off, analyze on, timing off, summary off) select * from onek2 where unique2 = 11 and stringu1 = 'ATAAAA'; explain (costs off) select unique2 from onek2 where unique2 = 11 and stringu1 = 'ATAAAA'; select unique2 from onek2 where unique2 = 11 and stringu1 = 'ATAAAA'; -- partial index predicate implies clause, so no need for retest explain (costs off) select * from onek2 where unique2 = 11 and stringu1 < 'B'; select * from onek2 where unique2 = 11 and stringu1 < 'B'; explain (costs off) select unique2 from onek2 where unique2 = 11 and stringu1 < 'B'; select unique2 from onek2 where unique2 = 11 and stringu1 < 'B'; -- but if it's an update target, must retest anyway explain (costs off) select unique2 from onek2 where unique2 = 11 and stringu1 < 'B' for update; select unique2 from onek2 where unique2 = 11 and stringu1 < 'B' for update; -- partial index is not applicable explain (costs off) select unique2 from onek2 where unique2 = 11 and stringu1 < 'C'; select unique2 from onek2 where unique2 = 11 and stringu1 < 'C'; -- partial index implies clause, but bitmap scan must recheck predicate anyway SET enable_indexscan TO off; explain (costs off) select unique2 from onek2 where unique2 = 11 and stringu1 < 'B'; select unique2 from onek2 where unique2 = 11 and stringu1 < 'B'; RESET enable_indexscan; -- check multi-index cases too explain (costs off) select unique1, unique2 from onek2 where (unique2 = 11 or unique1 = 0) and stringu1 < 'B'; select unique1, unique2 from onek2 where (unique2 = 11 or unique1 = 0) and stringu1 < 'B'; explain (costs off) select unique1, unique2 from onek2 where (unique2 = 11 and stringu1 < 'B') or unique1 = 0; select unique1, unique2 from onek2 where (unique2 = 11 and stringu1 < 'B') or unique1 = 0; -- -- Test some corner cases that have been known to confuse the planner -- -- ORDER BY on a constant doesn't really need any sorting SELECT 1 AS x ORDER BY x; -- But ORDER BY on a set-valued expression does create function sillysrf(int) returns setof int as 'values (1),(10),(2),($1)' language sql immutable; select sillysrf(42); select sillysrf(-1) order by 1; drop function sillysrf(int); -- X = X isn't a no-op, it's effectively X IS NOT NULL assuming = is strict -- (see bug #5084) select * from (values (2),(null),(1)) v(k) where k = k order by k; select * from (values (2),(null),(1)) v(k) where k = k; -- Test partitioned tables with no partitions, which should be handled the -- same as the non-inheritance case when expanding its RTE. create table list_parted_tbl (a int,b int) partition by list (a); create table list_parted_tbl1 partition of list_parted_tbl for values in (1) partition by list(b); explain (costs off) select * from list_parted_tbl; drop table list_parted_tbl; pgFormatter-4.2/t/pg-test-files/sql/select_distinct.sql000066400000000000000000000037431361326045100233070ustar00rootroot00000000000000-- -- SELECT_DISTINCT -- -- -- awk '{print $3;}' onek.data | sort -n | uniq -- SELECT DISTINCT two FROM tmp ORDER BY 1; -- -- awk '{print $5;}' onek.data | sort -n | uniq -- SELECT DISTINCT ten FROM tmp ORDER BY 1; -- -- awk '{print $16;}' onek.data | sort -d | uniq -- SELECT DISTINCT string4 FROM tmp ORDER BY 1; -- -- awk '{print $3,$16,$5;}' onek.data | sort -d | uniq | -- sort +0n -1 +1d -2 +2n -3 -- SELECT DISTINCT two, string4, ten FROM tmp ORDER BY two using <, string4 using <, ten using <; -- -- awk '{print $2;}' person.data | -- awk '{if(NF!=1){print $2;}else{print;}}' - emp.data | -- awk '{if(NF!=1){print $2;}else{print;}}' - student.data | -- awk 'BEGIN{FS=" ";}{if(NF!=1){print $5;}else{print;}}' - stud_emp.data | -- sort -n -r | uniq -- SELECT DISTINCT p.age FROM person* p ORDER BY age using >; -- -- Check mentioning same column more than once -- EXPLAIN (VERBOSE, COSTS OFF) SELECT count(*) FROM (SELECT DISTINCT two, four, two FROM tenk1) ss; SELECT count(*) FROM (SELECT DISTINCT two, four, two FROM tenk1) ss; -- -- Also, some tests of IS DISTINCT FROM, which doesn't quite deserve its -- very own regression file. -- CREATE TEMP TABLE disttable (f1 integer); INSERT INTO DISTTABLE VALUES(1); INSERT INTO DISTTABLE VALUES(2); INSERT INTO DISTTABLE VALUES(3); INSERT INTO DISTTABLE VALUES(NULL); -- basic cases SELECT f1, f1 IS DISTINCT FROM 2 as "not 2" FROM disttable; SELECT f1, f1 IS DISTINCT FROM NULL as "not null" FROM disttable; SELECT f1, f1 IS DISTINCT FROM f1 as "false" FROM disttable; SELECT f1, f1 IS DISTINCT FROM f1+1 as "not null" FROM disttable; -- check that optimizer constant-folds it properly SELECT 1 IS DISTINCT FROM 2 as "yes"; SELECT 2 IS DISTINCT FROM 2 as "no"; SELECT 2 IS DISTINCT FROM null as "yes"; SELECT null IS DISTINCT FROM null as "no"; -- negated form SELECT 1 IS NOT DISTINCT FROM 2 as "no"; SELECT 2 IS NOT DISTINCT FROM 2 as "yes"; SELECT 2 IS NOT DISTINCT FROM null as "no"; SELECT null IS NOT DISTINCT FROM null as "yes"; pgFormatter-4.2/t/pg-test-files/sql/select_distinct_on.sql000066400000000000000000000011101361326045100237650ustar00rootroot00000000000000-- -- SELECT_DISTINCT_ON -- SELECT DISTINCT ON (string4) string4, two, ten FROM tmp ORDER BY string4 using <, two using >, ten using <; -- this will fail due to conflict of ordering requirements SELECT DISTINCT ON (string4, ten) string4, two, ten FROM tmp ORDER BY string4 using <, two using <, ten using <; SELECT DISTINCT ON (string4, ten) string4, ten, two FROM tmp ORDER BY string4 using <, ten using >, two using <; -- bug #5049: early 8.4.x chokes on volatile DISTINCT ON clauses select distinct on (1) floor(random()) as r, f1 from int4_tbl order by 1,2; pgFormatter-4.2/t/pg-test-files/sql/select_having.sql000066400000000000000000000033251361326045100227360ustar00rootroot00000000000000-- -- SELECT_HAVING -- -- load test data CREATE TABLE test_having (a int, b int, c char(8), d char); INSERT INTO test_having VALUES (0, 1, 'XXXX', 'A'); INSERT INTO test_having VALUES (1, 2, 'AAAA', 'b'); INSERT INTO test_having VALUES (2, 2, 'AAAA', 'c'); INSERT INTO test_having VALUES (3, 3, 'BBBB', 'D'); INSERT INTO test_having VALUES (4, 3, 'BBBB', 'e'); INSERT INTO test_having VALUES (5, 3, 'bbbb', 'F'); INSERT INTO test_having VALUES (6, 4, 'cccc', 'g'); INSERT INTO test_having VALUES (7, 4, 'cccc', 'h'); INSERT INTO test_having VALUES (8, 4, 'CCCC', 'I'); INSERT INTO test_having VALUES (9, 4, 'CCCC', 'j'); SELECT b, c FROM test_having GROUP BY b, c HAVING count(*) = 1 ORDER BY b, c; -- HAVING is effectively equivalent to WHERE in this case SELECT b, c FROM test_having GROUP BY b, c HAVING b = 3 ORDER BY b, c; SELECT lower(c), count(c) FROM test_having GROUP BY lower(c) HAVING count(*) > 2 OR min(a) = max(a) ORDER BY lower(c); SELECT c, max(a) FROM test_having GROUP BY c HAVING count(*) > 2 OR min(a) = max(a) ORDER BY c; -- test degenerate cases involving HAVING without GROUP BY -- Per SQL spec, these should generate 0 or 1 row, even without aggregates SELECT min(a), max(a) FROM test_having HAVING min(a) = max(a); SELECT min(a), max(a) FROM test_having HAVING min(a) < max(a); -- errors: ungrouped column references SELECT a FROM test_having HAVING min(a) < max(a); SELECT 1 AS one FROM test_having HAVING a > 1; -- the really degenerate case: need not scan table at all SELECT 1 AS one FROM test_having HAVING 1 > 2; SELECT 1 AS one FROM test_having HAVING 1 < 2; -- and just to prove that we aren't scanning the table: SELECT 1 AS one FROM test_having WHERE 1/a = 1 HAVING 1 < 2; DROP TABLE test_having; pgFormatter-4.2/t/pg-test-files/sql/select_implicit.sql000066400000000000000000000125621361326045100232770ustar00rootroot00000000000000-- -- SELECT_IMPLICIT -- Test cases for queries with ordering terms missing from the target list. -- This used to be called "junkfilter.sql". -- The parser uses the term "resjunk" to handle these cases. -- - thomas 1998-07-09 -- -- load test data CREATE TABLE test_missing_target (a int, b int, c char(8), d char); INSERT INTO test_missing_target VALUES (0, 1, 'XXXX', 'A'); INSERT INTO test_missing_target VALUES (1, 2, 'ABAB', 'b'); INSERT INTO test_missing_target VALUES (2, 2, 'ABAB', 'c'); INSERT INTO test_missing_target VALUES (3, 3, 'BBBB', 'D'); INSERT INTO test_missing_target VALUES (4, 3, 'BBBB', 'e'); INSERT INTO test_missing_target VALUES (5, 3, 'bbbb', 'F'); INSERT INTO test_missing_target VALUES (6, 4, 'cccc', 'g'); INSERT INTO test_missing_target VALUES (7, 4, 'cccc', 'h'); INSERT INTO test_missing_target VALUES (8, 4, 'CCCC', 'I'); INSERT INTO test_missing_target VALUES (9, 4, 'CCCC', 'j'); -- w/ existing GROUP BY target SELECT c, count(*) FROM test_missing_target GROUP BY test_missing_target.c ORDER BY c; -- w/o existing GROUP BY target using a relation name in GROUP BY clause SELECT count(*) FROM test_missing_target GROUP BY test_missing_target.c ORDER BY c; -- w/o existing GROUP BY target and w/o existing a different ORDER BY target -- failure expected SELECT count(*) FROM test_missing_target GROUP BY a ORDER BY b; -- w/o existing GROUP BY target and w/o existing same ORDER BY target SELECT count(*) FROM test_missing_target GROUP BY b ORDER BY b; -- w/ existing GROUP BY target using a relation name in target SELECT test_missing_target.b, count(*) FROM test_missing_target GROUP BY b ORDER BY b; -- w/o existing GROUP BY target SELECT c FROM test_missing_target ORDER BY a; -- w/o existing ORDER BY target SELECT count(*) FROM test_missing_target GROUP BY b ORDER BY b desc; -- group using reference number SELECT count(*) FROM test_missing_target ORDER BY 1 desc; -- order using reference number SELECT c, count(*) FROM test_missing_target GROUP BY 1 ORDER BY 1; -- group using reference number out of range -- failure expected SELECT c, count(*) FROM test_missing_target GROUP BY 3; -- group w/o existing GROUP BY and ORDER BY target under ambiguous condition -- failure expected SELECT count(*) FROM test_missing_target x, test_missing_target y WHERE x.a = y.a GROUP BY b ORDER BY b; -- order w/ target under ambiguous condition -- failure NOT expected SELECT a, a FROM test_missing_target ORDER BY a; -- order expression w/ target under ambiguous condition -- failure NOT expected SELECT a/2, a/2 FROM test_missing_target ORDER BY a/2; -- group expression w/ target under ambiguous condition -- failure NOT expected SELECT a/2, a/2 FROM test_missing_target GROUP BY a/2 ORDER BY a/2; -- group w/ existing GROUP BY target under ambiguous condition SELECT x.b, count(*) FROM test_missing_target x, test_missing_target y WHERE x.a = y.a GROUP BY x.b ORDER BY x.b; -- group w/o existing GROUP BY target under ambiguous condition SELECT count(*) FROM test_missing_target x, test_missing_target y WHERE x.a = y.a GROUP BY x.b ORDER BY x.b; -- group w/o existing GROUP BY target under ambiguous condition -- into a table SELECT count(*) INTO TABLE test_missing_target2 FROM test_missing_target x, test_missing_target y WHERE x.a = y.a GROUP BY x.b ORDER BY x.b; SELECT * FROM test_missing_target2; -- Functions and expressions -- w/ existing GROUP BY target SELECT a%2, count(b) FROM test_missing_target GROUP BY test_missing_target.a%2 ORDER BY test_missing_target.a%2; -- w/o existing GROUP BY target using a relation name in GROUP BY clause SELECT count(c) FROM test_missing_target GROUP BY lower(test_missing_target.c) ORDER BY lower(test_missing_target.c); -- w/o existing GROUP BY target and w/o existing a different ORDER BY target -- failure expected SELECT count(a) FROM test_missing_target GROUP BY a ORDER BY b; -- w/o existing GROUP BY target and w/o existing same ORDER BY target SELECT count(b) FROM test_missing_target GROUP BY b/2 ORDER BY b/2; -- w/ existing GROUP BY target using a relation name in target SELECT lower(test_missing_target.c), count(c) FROM test_missing_target GROUP BY lower(c) ORDER BY lower(c); -- w/o existing GROUP BY target SELECT a FROM test_missing_target ORDER BY upper(d); -- w/o existing ORDER BY target SELECT count(b) FROM test_missing_target GROUP BY (b + 1) / 2 ORDER BY (b + 1) / 2 desc; -- group w/o existing GROUP BY and ORDER BY target under ambiguous condition -- failure expected SELECT count(x.a) FROM test_missing_target x, test_missing_target y WHERE x.a = y.a GROUP BY b/2 ORDER BY b/2; -- group w/ existing GROUP BY target under ambiguous condition SELECT x.b/2, count(x.b) FROM test_missing_target x, test_missing_target y WHERE x.a = y.a GROUP BY x.b/2 ORDER BY x.b/2; -- group w/o existing GROUP BY target under ambiguous condition -- failure expected due to ambiguous b in count(b) SELECT count(b) FROM test_missing_target x, test_missing_target y WHERE x.a = y.a GROUP BY x.b/2; -- group w/o existing GROUP BY target under ambiguous condition -- into a table SELECT count(x.b) INTO TABLE test_missing_target3 FROM test_missing_target x, test_missing_target y WHERE x.a = y.a GROUP BY x.b/2 ORDER BY x.b/2; SELECT * FROM test_missing_target3; -- Cleanup DROP TABLE test_missing_target; DROP TABLE test_missing_target2; DROP TABLE test_missing_target3; pgFormatter-4.2/t/pg-test-files/sql/select_into.sql000066400000000000000000000063111361326045100224310ustar00rootroot00000000000000-- -- SELECT_INTO -- SELECT * INTO TABLE sitmp1 FROM onek WHERE onek.unique1 < 2; DROP TABLE sitmp1; SELECT * INTO TABLE sitmp1 FROM onek2 WHERE onek2.unique1 < 2; DROP TABLE sitmp1; -- -- SELECT INTO and INSERT permission, if owner is not allowed to insert. -- CREATE SCHEMA selinto_schema; CREATE USER regress_selinto_user; ALTER DEFAULT PRIVILEGES FOR ROLE regress_selinto_user REVOKE INSERT ON TABLES FROM regress_selinto_user; GRANT ALL ON SCHEMA selinto_schema TO public; SET SESSION AUTHORIZATION regress_selinto_user; SELECT * INTO TABLE selinto_schema.tmp1 FROM pg_class WHERE relname like '%a%'; -- Error SELECT oid AS clsoid, relname, relnatts + 10 AS x INTO selinto_schema.tmp2 FROM pg_class WHERE relname like '%b%'; -- Error CREATE TABLE selinto_schema.tmp3 (a,b,c) AS SELECT oid,relname,relacl FROM pg_class WHERE relname like '%c%'; -- Error RESET SESSION AUTHORIZATION; ALTER DEFAULT PRIVILEGES FOR ROLE regress_selinto_user GRANT INSERT ON TABLES TO regress_selinto_user; SET SESSION AUTHORIZATION regress_selinto_user; SELECT * INTO TABLE selinto_schema.tmp1 FROM pg_class WHERE relname like '%a%'; -- OK SELECT oid AS clsoid, relname, relnatts + 10 AS x INTO selinto_schema.tmp2 FROM pg_class WHERE relname like '%b%'; -- OK CREATE TABLE selinto_schema.tmp3 (a,b,c) AS SELECT oid,relname,relacl FROM pg_class WHERE relname like '%c%'; -- OK RESET SESSION AUTHORIZATION; DROP SCHEMA selinto_schema CASCADE; DROP USER regress_selinto_user; -- Tests for WITH NO DATA and column name consistency CREATE TABLE ctas_base (i int, j int); INSERT INTO ctas_base VALUES (1, 2); CREATE TABLE ctas_nodata (ii, jj, kk) AS SELECT i, j FROM ctas_base; -- Error CREATE TABLE ctas_nodata (ii, jj, kk) AS SELECT i, j FROM ctas_base WITH NO DATA; -- Error CREATE TABLE ctas_nodata (ii, jj) AS SELECT i, j FROM ctas_base; -- OK CREATE TABLE ctas_nodata_2 (ii, jj) AS SELECT i, j FROM ctas_base WITH NO DATA; -- OK CREATE TABLE ctas_nodata_3 (ii) AS SELECT i, j FROM ctas_base; -- OK CREATE TABLE ctas_nodata_4 (ii) AS SELECT i, j FROM ctas_base WITH NO DATA; -- OK SELECT * FROM ctas_nodata; SELECT * FROM ctas_nodata_2; SELECT * FROM ctas_nodata_3; SELECT * FROM ctas_nodata_4; DROP TABLE ctas_base; DROP TABLE ctas_nodata; DROP TABLE ctas_nodata_2; DROP TABLE ctas_nodata_3; DROP TABLE ctas_nodata_4; -- -- CREATE TABLE AS/SELECT INTO as last command in a SQL function -- have been known to cause problems -- CREATE FUNCTION make_table() RETURNS VOID AS $$ CREATE TABLE created_table AS SELECT * FROM int8_tbl; $$ LANGUAGE SQL; SELECT make_table(); SELECT * FROM created_table; -- Try EXPLAIN ANALYZE SELECT INTO and EXPLAIN ANALYZE CREATE TABLE AS -- WITH NO DATA, but hide the outputs since they won't be stable. DO $$ BEGIN EXECUTE 'EXPLAIN ANALYZE SELECT * INTO TABLE easi FROM int8_tbl'; EXECUTE 'EXPLAIN ANALYZE CREATE TABLE easi2 AS SELECT * FROM int8_tbl WITH NO DATA'; END$$; DROP TABLE created_table; DROP TABLE easi, easi2; -- -- Disallowed uses of SELECT ... INTO. All should fail -- DECLARE foo CURSOR FOR SELECT 1 INTO b; COPY (SELECT 1 INTO frak UNION SELECT 2) TO 'blob'; SELECT * FROM (SELECT 1 INTO f) bar; CREATE VIEW foo AS SELECT 1 INTO b; INSERT INTO b SELECT 1 INTO f; pgFormatter-4.2/t/pg-test-files/sql/select_parallel.sql000066400000000000000000000332561361326045100232640ustar00rootroot00000000000000-- -- PARALLEL -- create function sp_parallel_restricted(int) returns int as $$begin return $1; end$$ language plpgsql parallel restricted; -- Serializable isolation would disable parallel query, so explicitly use an -- arbitrary other level. begin isolation level repeatable read; -- encourage use of parallel plans set parallel_setup_cost=0; set parallel_tuple_cost=0; set min_parallel_table_scan_size=0; set max_parallel_workers_per_gather=4; -- Parallel Append with partial-subplans explain (costs off) select round(avg(aa)), sum(aa) from a_star; select round(avg(aa)), sum(aa) from a_star a1; -- Parallel Append with both partial and non-partial subplans alter table c_star set (parallel_workers = 0); alter table d_star set (parallel_workers = 0); explain (costs off) select round(avg(aa)), sum(aa) from a_star; select round(avg(aa)), sum(aa) from a_star a2; -- Parallel Append with only non-partial subplans alter table a_star set (parallel_workers = 0); alter table b_star set (parallel_workers = 0); alter table e_star set (parallel_workers = 0); alter table f_star set (parallel_workers = 0); explain (costs off) select round(avg(aa)), sum(aa) from a_star; select round(avg(aa)), sum(aa) from a_star a3; -- Disable Parallel Append alter table a_star reset (parallel_workers); alter table b_star reset (parallel_workers); alter table c_star reset (parallel_workers); alter table d_star reset (parallel_workers); alter table e_star reset (parallel_workers); alter table f_star reset (parallel_workers); set enable_parallel_append to off; explain (costs off) select round(avg(aa)), sum(aa) from a_star; select round(avg(aa)), sum(aa) from a_star a4; reset enable_parallel_append; -- Parallel Append that runs serially create function sp_test_func() returns setof text as $$ select 'foo'::varchar union all select 'bar'::varchar $$ language sql stable; select sp_test_func() order by 1; -- Parallel Append is not to be used when the subpath depends on the outer param create table part_pa_test(a int, b int) partition by range(a); create table part_pa_test_p1 partition of part_pa_test for values from (minvalue) to (0); create table part_pa_test_p2 partition of part_pa_test for values from (0) to (maxvalue); explain (costs off) select (select max((select pa1.b from part_pa_test pa1 where pa1.a = pa2.a))) from part_pa_test pa2; drop table part_pa_test; -- test with leader participation disabled set parallel_leader_participation = off; explain (costs off) select count(*) from tenk1 where stringu1 = 'GRAAAA'; select count(*) from tenk1 where stringu1 = 'GRAAAA'; -- test with leader participation disabled, but no workers available (so -- the leader will have to run the plan despite the setting) set max_parallel_workers = 0; explain (costs off) select count(*) from tenk1 where stringu1 = 'GRAAAA'; select count(*) from tenk1 where stringu1 = 'GRAAAA'; reset max_parallel_workers; reset parallel_leader_participation; -- test that parallel_restricted function doesn't run in worker alter table tenk1 set (parallel_workers = 4); explain (verbose, costs off) select sp_parallel_restricted(unique1) from tenk1 where stringu1 = 'GRAAAA' order by 1; -- test parallel plan when group by expression is in target list. explain (costs off) select length(stringu1) from tenk1 group by length(stringu1); select length(stringu1) from tenk1 group by length(stringu1); explain (costs off) select stringu1, count(*) from tenk1 group by stringu1 order by stringu1; -- test that parallel plan for aggregates is not selected when -- target list contains parallel restricted clause. explain (costs off) select sum(sp_parallel_restricted(unique1)) from tenk1 group by(sp_parallel_restricted(unique1)); -- test prepared statement prepare tenk1_count(integer) As select count((unique1)) from tenk1 where hundred > $1; explain (costs off) execute tenk1_count(1); execute tenk1_count(1); deallocate tenk1_count; -- test parallel plans for queries containing un-correlated subplans. alter table tenk2 set (parallel_workers = 0); explain (costs off) select count(*) from tenk1 where (two, four) not in (select hundred, thousand from tenk2 where thousand > 100); select count(*) from tenk1 where (two, four) not in (select hundred, thousand from tenk2 where thousand > 100); -- this is not parallel-safe due to use of random() within SubLink's testexpr: explain (costs off) select * from tenk1 where (unique1 + random())::integer not in (select ten from tenk2); alter table tenk2 reset (parallel_workers); -- test parallel plan for a query containing initplan. set enable_indexscan = off; set enable_indexonlyscan = off; set enable_bitmapscan = off; alter table tenk2 set (parallel_workers = 2); explain (costs off) select count(*) from tenk1 where tenk1.unique1 = (Select max(tenk2.unique1) from tenk2); select count(*) from tenk1 where tenk1.unique1 = (Select max(tenk2.unique1) from tenk2); reset enable_indexscan; reset enable_indexonlyscan; reset enable_bitmapscan; alter table tenk2 reset (parallel_workers); -- test parallel index scans. set enable_seqscan to off; set enable_bitmapscan to off; explain (costs off) select count((unique1)) from tenk1 where hundred > 1; select count((unique1)) from tenk1 where hundred > 1; -- test parallel index-only scans. explain (costs off) select count(*) from tenk1 where thousand > 95; select count(*) from tenk1 where thousand > 95; -- test rescan cases too set enable_material = false; explain (costs off) select * from (select count(unique1) from tenk1 where hundred > 10) ss right join (values (1),(2),(3)) v(x) on true; select * from (select count(unique1) from tenk1 where hundred > 10) ss right join (values (1),(2),(3)) v(x) on true; explain (costs off) select * from (select count(*) from tenk1 where thousand > 99) ss right join (values (1),(2),(3)) v(x) on true; select * from (select count(*) from tenk1 where thousand > 99) ss right join (values (1),(2),(3)) v(x) on true; reset enable_material; reset enable_seqscan; reset enable_bitmapscan; -- test parallel bitmap heap scan. set enable_seqscan to off; set enable_indexscan to off; set enable_hashjoin to off; set enable_mergejoin to off; set enable_material to off; -- test prefetching, if the platform allows it DO $$ BEGIN SET effective_io_concurrency = 50; EXCEPTION WHEN invalid_parameter_value THEN END $$; set work_mem='64kB'; --set small work mem to force lossy pages explain (costs off) select count(*) from tenk1, tenk2 where tenk1.hundred > 1 and tenk2.thousand=0; select count(*) from tenk1, tenk2 where tenk1.hundred > 1 and tenk2.thousand=0; create table bmscantest (a int, t text); insert into bmscantest select r, 'fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo' FROM generate_series(1,100000) r; create index i_bmtest ON bmscantest(a); select count(*) from bmscantest where a>1; -- test accumulation of stats for parallel nodes reset enable_seqscan; alter table tenk2 set (parallel_workers = 0); explain (analyze, timing off, summary off, costs off) select count(*) from tenk1, tenk2 where tenk1.hundred > 1 and tenk2.thousand=0; alter table tenk2 reset (parallel_workers); reset work_mem; create function explain_parallel_sort_stats() returns setof text language plpgsql as $$ declare ln text; begin for ln in explain (analyze, timing off, summary off, costs off) select * from (select ten from tenk1 where ten < 100 order by ten) ss right join (values (1),(2),(3)) v(x) on true loop ln := regexp_replace(ln, 'Memory: \S*', 'Memory: xxx'); return next ln; end loop; end; $$; select * from explain_parallel_sort_stats(); reset enable_indexscan; reset enable_hashjoin; reset enable_mergejoin; reset enable_material; reset effective_io_concurrency; drop table bmscantest; drop function explain_parallel_sort_stats(); -- test parallel merge join path. set enable_hashjoin to off; set enable_nestloop to off; explain (costs off) select count(*) from tenk1, tenk2 where tenk1.unique1 = tenk2.unique1; select count(*) from tenk1, tenk2 where tenk1.unique1 = tenk2.unique1; reset enable_hashjoin; reset enable_nestloop; -- test gather merge set enable_hashagg = false; explain (costs off) select count(*) from tenk1 group by twenty; select count(*) from tenk1 group by twenty; --test expressions in targetlist are pushed down for gather merge create function sp_simple_func(var1 integer) returns integer as $$ begin return var1 + 10; end; $$ language plpgsql PARALLEL SAFE; explain (costs off, verbose) select ten, sp_simple_func(ten) from tenk1 where ten < 100 order by ten; drop function sp_simple_func(integer); -- test handling of SRFs in targetlist (bug in 10.0) explain (costs off) select count(*), generate_series(1,2) from tenk1 group by twenty; select count(*), generate_series(1,2) from tenk1 group by twenty; -- test gather merge with parallel leader participation disabled set parallel_leader_participation = off; explain (costs off) select count(*) from tenk1 group by twenty; select count(*) from tenk1 group by twenty; reset parallel_leader_participation; --test rescan behavior of gather merge set enable_material = false; explain (costs off) select * from (select string4, count(unique2) from tenk1 group by string4 order by string4) ss right join (values (1),(2),(3)) v(x) on true; select * from (select string4, count(unique2) from tenk1 group by string4 order by string4) ss right join (values (1),(2),(3)) v(x) on true; reset enable_material; reset enable_hashagg; -- check parallelized int8 aggregate (bug #14897) explain (costs off) select avg(unique1::int8) from tenk1; select avg(unique1::int8) from tenk1; -- gather merge test with a LIMIT explain (costs off) select fivethous from tenk1 order by fivethous limit 4; select fivethous from tenk1 order by fivethous limit 4; -- gather merge test with 0 worker set max_parallel_workers = 0; explain (costs off) select string4 from tenk1 order by string4 limit 5; select string4 from tenk1 order by string4 limit 5; -- gather merge test with 0 workers, with parallel leader -- participation disabled (the leader will have to run the plan -- despite the setting) set parallel_leader_participation = off; explain (costs off) select string4 from tenk1 order by string4 limit 5; select string4 from tenk1 order by string4 limit 5; reset parallel_leader_participation; reset max_parallel_workers; SAVEPOINT settings; SET LOCAL force_parallel_mode = 1; explain (costs off) select stringu1::int2 from tenk1 where unique1 = 1; ROLLBACK TO SAVEPOINT settings; -- exercise record typmod remapping between backends CREATE FUNCTION make_record(n int) RETURNS RECORD LANGUAGE plpgsql PARALLEL SAFE AS $$ BEGIN RETURN CASE n WHEN 1 THEN ROW(1) WHEN 2 THEN ROW(1, 2) WHEN 3 THEN ROW(1, 2, 3) WHEN 4 THEN ROW(1, 2, 3, 4) ELSE ROW(1, 2, 3, 4, 5) END; END; $$; SAVEPOINT settings; SET LOCAL force_parallel_mode = 1; SELECT make_record(x) FROM (SELECT generate_series(1, 5) x) ss ORDER BY x; ROLLBACK TO SAVEPOINT settings; DROP function make_record(n int); -- test the sanity of parallel query after the active role is dropped. drop role if exists regress_parallel_worker; create role regress_parallel_worker; set role regress_parallel_worker; reset session authorization; drop role regress_parallel_worker; set force_parallel_mode = 1; select count(*) from tenk1; reset force_parallel_mode; reset role; -- Window function calculation can't be pushed to workers. explain (costs off, verbose) select count(*) from tenk1 a where (unique1, two) in (select unique1, row_number() over() from tenk1 b); -- LIMIT/OFFSET within sub-selects can't be pushed to workers. explain (costs off) select * from tenk1 a where two in (select two from tenk1 b where stringu1 like '%AAAA' limit 3); -- to increase the parallel query test coverage SAVEPOINT settings; SET LOCAL force_parallel_mode = 1; EXPLAIN (analyze, timing off, summary off, costs off) SELECT * FROM tenk1; ROLLBACK TO SAVEPOINT settings; -- provoke error in worker SAVEPOINT settings; SET LOCAL force_parallel_mode = 1; select stringu1::int2 from tenk1 where unique1 = 1; ROLLBACK TO SAVEPOINT settings; -- test interaction with set-returning functions SAVEPOINT settings; -- multiple subqueries under a single Gather node -- must set parallel_setup_cost > 0 to discourage multiple Gather nodes SET LOCAL parallel_setup_cost = 10; EXPLAIN (COSTS OFF) SELECT unique1 FROM tenk1 WHERE fivethous = tenthous + 1 UNION ALL SELECT unique1 FROM tenk1 WHERE fivethous = tenthous + 1; ROLLBACK TO SAVEPOINT settings; -- can't use multiple subqueries under a single Gather node due to initPlans EXPLAIN (COSTS OFF) SELECT unique1 FROM tenk1 WHERE fivethous = (SELECT unique1 FROM tenk1 WHERE fivethous = 1 LIMIT 1) UNION ALL SELECT unique1 FROM tenk1 WHERE fivethous = (SELECT unique2 FROM tenk1 WHERE fivethous = 1 LIMIT 1) ORDER BY 1; -- test interaction with SRFs SELECT * FROM information_schema.foreign_data_wrapper_options ORDER BY 1, 2, 3; -- test passing expanded-value representations to workers CREATE FUNCTION make_some_array(int,int) returns int[] as $$declare x int[]; begin x[1] := $1; x[2] := $2; return x; end$$ language plpgsql parallel safe; CREATE TABLE fooarr(f1 text, f2 int[], f3 text); INSERT INTO fooarr VALUES('1', ARRAY[1,2], 'one'); PREPARE pstmt(text, int[]) AS SELECT * FROM fooarr WHERE f1 = $1 AND f2 = $2; EXPLAIN (COSTS OFF) EXECUTE pstmt('1', make_some_array(1,2)); EXECUTE pstmt('1', make_some_array(1,2)); DEALLOCATE pstmt; -- test interaction between subquery and partial_paths CREATE VIEW tenk1_vw_sec WITH (security_barrier) AS SELECT * FROM tenk1; EXPLAIN (COSTS OFF) SELECT 1 FROM tenk1_vw_sec WHERE (SELECT sum(f1) FROM int4_tbl WHERE f1 < unique1) < 100; rollback; pgFormatter-4.2/t/pg-test-files/sql/select_views.sql000066400000000000000000000124031361326045100226140ustar00rootroot00000000000000-- -- SELECT_VIEWS -- test the views defined in CREATE_VIEWS -- SELECT * FROM street; SELECT name, #thepath FROM iexit ORDER BY name COLLATE "C", 2; SELECT * FROM toyemp WHERE name = 'sharon'; -- -- Test for Leaky view scenario -- CREATE ROLE regress_alice; CREATE FUNCTION f_leak (text) RETURNS bool LANGUAGE 'plpgsql' COST 0.0000001 AS 'BEGIN RAISE NOTICE ''f_leak => %'', $1; RETURN true; END'; CREATE TABLE customer ( cid int primary key, name text not null, tel text, passwd text ); CREATE TABLE credit_card ( cid int references customer(cid), cnum text, climit int ); CREATE TABLE credit_usage ( cid int references customer(cid), ymd date, usage int ); INSERT INTO customer VALUES (101, 'regress_alice', '+81-12-3456-7890', 'passwd123'), (102, 'regress_bob', '+01-234-567-8901', 'beafsteak'), (103, 'regress_eve', '+49-8765-43210', 'hamburger'); INSERT INTO credit_card VALUES (101, '1111-2222-3333-4444', 4000), (102, '5555-6666-7777-8888', 3000), (103, '9801-2345-6789-0123', 2000); INSERT INTO credit_usage VALUES (101, '2011-09-15', 120), (101, '2011-10-05', 90), (101, '2011-10-18', 110), (101, '2011-10-21', 200), (101, '2011-11-10', 80), (102, '2011-09-22', 300), (102, '2011-10-12', 120), (102, '2011-10-28', 200), (103, '2011-10-15', 480); CREATE VIEW my_property_normal AS SELECT * FROM customer WHERE name = current_user; CREATE VIEW my_property_secure WITH (security_barrier) AS SELECT * FROM customer WHERE name = current_user; CREATE VIEW my_credit_card_normal AS SELECT * FROM customer l NATURAL JOIN credit_card r WHERE l.name = current_user; CREATE VIEW my_credit_card_secure WITH (security_barrier) AS SELECT * FROM customer l NATURAL JOIN credit_card r WHERE l.name = current_user; CREATE VIEW my_credit_card_usage_normal AS SELECT * FROM my_credit_card_secure l NATURAL JOIN credit_usage r; CREATE VIEW my_credit_card_usage_secure WITH (security_barrier) AS SELECT * FROM my_credit_card_secure l NATURAL JOIN credit_usage r; GRANT SELECT ON my_property_normal TO public; GRANT SELECT ON my_property_secure TO public; GRANT SELECT ON my_credit_card_normal TO public; GRANT SELECT ON my_credit_card_secure TO public; GRANT SELECT ON my_credit_card_usage_normal TO public; GRANT SELECT ON my_credit_card_usage_secure TO public; -- -- Run leaky view scenarios -- SET SESSION AUTHORIZATION regress_alice; -- -- scenario: if a qualifier with tiny-cost is given, it shall be launched -- prior to the security policy of the view. -- SELECT * FROM my_property_normal WHERE f_leak(passwd); EXPLAIN (COSTS OFF) SELECT * FROM my_property_normal WHERE f_leak(passwd); SELECT * FROM my_property_secure WHERE f_leak(passwd); EXPLAIN (COSTS OFF) SELECT * FROM my_property_secure WHERE f_leak(passwd); -- -- scenario: qualifiers can be pushed down if they contain leaky functions, -- provided they aren't passed data from inside the view. -- SELECT * FROM my_property_normal v WHERE f_leak('passwd') AND f_leak(passwd); EXPLAIN (COSTS OFF) SELECT * FROM my_property_normal v WHERE f_leak('passwd') AND f_leak(passwd); SELECT * FROM my_property_secure v WHERE f_leak('passwd') AND f_leak(passwd); EXPLAIN (COSTS OFF) SELECT * FROM my_property_secure v WHERE f_leak('passwd') AND f_leak(passwd); -- -- scenario: if a qualifier references only one-side of a particular join- -- tree, it shall be distributed to the most deep scan plan as -- possible as we can. -- SELECT * FROM my_credit_card_normal WHERE f_leak(cnum); EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_normal WHERE f_leak(cnum); SELECT * FROM my_credit_card_secure WHERE f_leak(cnum); EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_secure WHERE f_leak(cnum); -- -- scenario: an external qualifier can be pushed-down by in-front-of the -- views with "security_barrier" attribute, except for operators -- implemented with leakproof functions. -- SELECT * FROM my_credit_card_usage_normal WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01'; EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_usage_normal WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01'; SELECT * FROM my_credit_card_usage_secure WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01'; EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_usage_secure WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01'; -- -- Test for the case when security_barrier gets changed between rewriter -- and planner stage. -- PREPARE p1 AS SELECT * FROM my_property_normal WHERE f_leak(passwd); PREPARE p2 AS SELECT * FROM my_property_secure WHERE f_leak(passwd); EXECUTE p1; EXECUTE p2; RESET SESSION AUTHORIZATION; ALTER VIEW my_property_normal SET (security_barrier=true); ALTER VIEW my_property_secure SET (security_barrier=false); SET SESSION AUTHORIZATION regress_alice; EXECUTE p1; -- To be perform as a view with security-barrier EXECUTE p2; -- To be perform as a view without security-barrier -- Cleanup. RESET SESSION AUTHORIZATION; DROP ROLE regress_alice; pgFormatter-4.2/t/pg-test-files/sql/sequence.sql000066400000000000000000000272771361326045100217470ustar00rootroot00000000000000-- -- CREATE SEQUENCE -- -- various error cases CREATE UNLOGGED SEQUENCE sequence_testx; CREATE SEQUENCE sequence_testx INCREMENT BY 0; CREATE SEQUENCE sequence_testx INCREMENT BY -1 MINVALUE 20; CREATE SEQUENCE sequence_testx INCREMENT BY 1 MAXVALUE -20; CREATE SEQUENCE sequence_testx INCREMENT BY -1 START 10; CREATE SEQUENCE sequence_testx INCREMENT BY 1 START -10; CREATE SEQUENCE sequence_testx CACHE 0; -- OWNED BY errors CREATE SEQUENCE sequence_testx OWNED BY nobody; -- nonsense word CREATE SEQUENCE sequence_testx OWNED BY pg_class_oid_index.oid; -- not a table CREATE SEQUENCE sequence_testx OWNED BY pg_class.relname; -- not same schema CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column DROP TABLE sequence_test_table; -- sequence data types CREATE SEQUENCE sequence_test5 AS integer; CREATE SEQUENCE sequence_test6 AS smallint; CREATE SEQUENCE sequence_test7 AS bigint; CREATE SEQUENCE sequence_test8 AS integer MAXVALUE 100000; CREATE SEQUENCE sequence_test9 AS integer INCREMENT BY -1; CREATE SEQUENCE sequence_test10 AS integer MINVALUE -100000 START 1; CREATE SEQUENCE sequence_test11 AS smallint; CREATE SEQUENCE sequence_test12 AS smallint INCREMENT -1; CREATE SEQUENCE sequence_test13 AS smallint MINVALUE -32768; CREATE SEQUENCE sequence_test14 AS smallint MAXVALUE 32767 INCREMENT -1; CREATE SEQUENCE sequence_testx AS text; CREATE SEQUENCE sequence_testx AS nosuchtype; CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; ALTER SEQUENCE sequence_test5 AS smallint; -- success, max will be adjusted ALTER SEQUENCE sequence_test8 AS smallint; -- fail, max has to be adjusted ALTER SEQUENCE sequence_test8 AS smallint MAXVALUE 20000; -- ok now ALTER SEQUENCE sequence_test9 AS smallint; -- success, min will be adjusted ALTER SEQUENCE sequence_test10 AS smallint; -- fail, min has to be adjusted ALTER SEQUENCE sequence_test10 AS smallint MINVALUE -20000; -- ok now ALTER SEQUENCE sequence_test11 AS int; -- max will be adjusted ALTER SEQUENCE sequence_test12 AS int; -- min will be adjusted ALTER SEQUENCE sequence_test13 AS int; -- min and max will be adjusted ALTER SEQUENCE sequence_test14 AS int; -- min and max will be adjusted --- --- test creation of SERIAL column --- CREATE TABLE serialTest1 (f1 text, f2 serial); INSERT INTO serialTest1 VALUES ('foo'); INSERT INTO serialTest1 VALUES ('bar'); INSERT INTO serialTest1 VALUES ('force', 100); INSERT INTO serialTest1 VALUES ('wrong', NULL); SELECT * FROM serialTest1; SELECT pg_get_serial_sequence('serialTest1', 'f2'); -- test smallserial / bigserial CREATE TABLE serialTest2 (f1 text, f2 serial, f3 smallserial, f4 serial2, f5 bigserial, f6 serial8); INSERT INTO serialTest2 (f1) VALUES ('test_defaults'); INSERT INTO serialTest2 (f1, f2, f3, f4, f5, f6) VALUES ('test_max_vals', 2147483647, 32767, 32767, 9223372036854775807, 9223372036854775807), ('test_min_vals', -2147483648, -32768, -32768, -9223372036854775808, -9223372036854775808); -- All these INSERTs should fail: INSERT INTO serialTest2 (f1, f3) VALUES ('bogus', -32769); INSERT INTO serialTest2 (f1, f4) VALUES ('bogus', -32769); INSERT INTO serialTest2 (f1, f3) VALUES ('bogus', 32768); INSERT INTO serialTest2 (f1, f4) VALUES ('bogus', 32768); INSERT INTO serialTest2 (f1, f5) VALUES ('bogus', -9223372036854775809); INSERT INTO serialTest2 (f1, f6) VALUES ('bogus', -9223372036854775809); INSERT INTO serialTest2 (f1, f5) VALUES ('bogus', 9223372036854775808); INSERT INTO serialTest2 (f1, f6) VALUES ('bogus', 9223372036854775808); SELECT * FROM serialTest2 ORDER BY f2 ASC; SELECT nextval('serialTest2_f2_seq'); SELECT nextval('serialTest2_f3_seq'); SELECT nextval('serialTest2_f4_seq'); SELECT nextval('serialTest2_f5_seq'); SELECT nextval('serialTest2_f6_seq'); -- basic sequence operations using both text and oid references CREATE SEQUENCE sequence_test; CREATE SEQUENCE IF NOT EXISTS sequence_test; SELECT nextval('sequence_test'::text); SELECT nextval('sequence_test'::regclass); SELECT currval('sequence_test'::text); SELECT currval('sequence_test'::regclass); SELECT setval('sequence_test'::text, 32); SELECT nextval('sequence_test'::regclass); SELECT setval('sequence_test'::text, 99, false); SELECT nextval('sequence_test'::regclass); SELECT setval('sequence_test'::regclass, 32); SELECT nextval('sequence_test'::text); SELECT setval('sequence_test'::regclass, 99, false); SELECT nextval('sequence_test'::text); DISCARD SEQUENCES; SELECT currval('sequence_test'::regclass); DROP SEQUENCE sequence_test; -- renaming sequences CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; SELECT nextval('foo_seq_new'); SELECT nextval('foo_seq_new'); -- log_cnt can be higher if there is a checkpoint just at the right -- time, so just test for the expected range SELECT last_value, log_cnt IN (31, 32) AS log_cnt_ok, is_called FROM foo_seq_new; DROP SEQUENCE foo_seq_new; -- renaming serial sequences ALTER TABLE serialtest1_f2_seq RENAME TO serialtest1_f2_foo; INSERT INTO serialTest1 VALUES ('more'); SELECT * FROM serialTest1; -- -- Check dependencies of serial and ordinary sequences -- CREATE TEMP SEQUENCE myseq2; CREATE TEMP SEQUENCE myseq3; CREATE TEMP TABLE t1 ( f1 serial, f2 int DEFAULT nextval('myseq2'), f3 int DEFAULT nextval('myseq3'::text) ); -- Both drops should fail, but with different error messages: DROP SEQUENCE t1_f1_seq; DROP SEQUENCE myseq2; -- This however will work: DROP SEQUENCE myseq3; DROP TABLE t1; -- Fails because no longer existent: DROP SEQUENCE t1_f1_seq; -- Now OK: DROP SEQUENCE myseq2; -- -- Alter sequence -- ALTER SEQUENCE IF EXISTS sequence_test2 RESTART WITH 24 INCREMENT BY 4 MAXVALUE 36 MINVALUE 5 CYCLE; ALTER SEQUENCE serialTest1 CYCLE; -- error, not a sequence CREATE SEQUENCE sequence_test2 START WITH 32; CREATE SEQUENCE sequence_test4 INCREMENT BY -1; SELECT nextval('sequence_test2'); SELECT nextval('sequence_test4'); ALTER SEQUENCE sequence_test2 RESTART; SELECT nextval('sequence_test2'); ALTER SEQUENCE sequence_test2 RESTART WITH 0; -- error ALTER SEQUENCE sequence_test4 RESTART WITH 40; -- error -- test CYCLE and NO CYCLE ALTER SEQUENCE sequence_test2 RESTART WITH 24 INCREMENT BY 4 MAXVALUE 36 MINVALUE 5 CYCLE; SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); -- cycled ALTER SEQUENCE sequence_test2 RESTART WITH 24 NO CYCLE; SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); -- error ALTER SEQUENCE sequence_test2 RESTART WITH -24 START WITH -24 INCREMENT BY -4 MINVALUE -36 MAXVALUE -5 CYCLE; SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); -- cycled ALTER SEQUENCE sequence_test2 RESTART WITH -24 NO CYCLE; SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); SELECT nextval('sequence_test2'); -- error -- reset ALTER SEQUENCE IF EXISTS sequence_test2 RESTART WITH 32 START WITH 32 INCREMENT BY 4 MAXVALUE 36 MINVALUE 5 CYCLE; SELECT setval('sequence_test2', -100); -- error SELECT setval('sequence_test2', 100); -- error SELECT setval('sequence_test2', 5); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema SELECT * FROM information_schema.sequences WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); \d sequence_test4 \d serialtest2_f2_seq -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; COMMENT ON SEQUENCE sequence_test2 IS 'will work'; COMMENT ON SEQUENCE sequence_test2 IS NULL; -- Test lastval() CREATE SEQUENCE seq; SELECT nextval('seq'); SELECT lastval(); SELECT setval('seq', 99); SELECT lastval(); DISCARD SEQUENCES; SELECT lastval(); CREATE SEQUENCE seq2; SELECT nextval('seq2'); SELECT lastval(); DROP SEQUENCE seq2; -- should fail SELECT lastval(); CREATE USER regress_seq_user; -- Test sequences in read-only transactions CREATE TEMPORARY SEQUENCE sequence_test_temp1; START TRANSACTION READ ONLY; SELECT nextval('sequence_test_temp1'); -- ok SELECT nextval('sequence_test2'); -- error ROLLBACK; START TRANSACTION READ ONLY; SELECT setval('sequence_test_temp1', 1); -- ok SELECT setval('sequence_test2', 1); -- error ROLLBACK; -- privileges tests -- nextval BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; REVOKE ALL ON seq3 FROM regress_seq_user; GRANT SELECT ON seq3 TO regress_seq_user; SELECT nextval('seq3'); ROLLBACK; BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; REVOKE ALL ON seq3 FROM regress_seq_user; GRANT UPDATE ON seq3 TO regress_seq_user; SELECT nextval('seq3'); ROLLBACK; BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; REVOKE ALL ON seq3 FROM regress_seq_user; GRANT USAGE ON seq3 TO regress_seq_user; SELECT nextval('seq3'); ROLLBACK; -- currval BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; SELECT nextval('seq3'); REVOKE ALL ON seq3 FROM regress_seq_user; GRANT SELECT ON seq3 TO regress_seq_user; SELECT currval('seq3'); ROLLBACK; BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; SELECT nextval('seq3'); REVOKE ALL ON seq3 FROM regress_seq_user; GRANT UPDATE ON seq3 TO regress_seq_user; SELECT currval('seq3'); ROLLBACK; BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; SELECT nextval('seq3'); REVOKE ALL ON seq3 FROM regress_seq_user; GRANT USAGE ON seq3 TO regress_seq_user; SELECT currval('seq3'); ROLLBACK; -- lastval BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; SELECT nextval('seq3'); REVOKE ALL ON seq3 FROM regress_seq_user; GRANT SELECT ON seq3 TO regress_seq_user; SELECT lastval(); ROLLBACK; BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; SELECT nextval('seq3'); REVOKE ALL ON seq3 FROM regress_seq_user; GRANT UPDATE ON seq3 TO regress_seq_user; SELECT lastval(); ROLLBACK; BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; SELECT nextval('seq3'); REVOKE ALL ON seq3 FROM regress_seq_user; GRANT USAGE ON seq3 TO regress_seq_user; SELECT lastval(); ROLLBACK; -- setval BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; CREATE SEQUENCE seq3; REVOKE ALL ON seq3 FROM regress_seq_user; SAVEPOINT save; SELECT setval('seq3', 5); ROLLBACK TO save; GRANT UPDATE ON seq3 TO regress_seq_user; SELECT setval('seq3', 5); SELECT nextval('seq3'); ROLLBACK; -- ALTER SEQUENCE BEGIN; SET LOCAL SESSION AUTHORIZATION regress_seq_user; ALTER SEQUENCE sequence_test2 START WITH 1; ROLLBACK; -- Sequences should get wiped out as well: DROP TABLE serialTest1, serialTest2; -- Make sure sequences are gone: SELECT * FROM information_schema.sequences WHERE sequence_name IN ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') ORDER BY sequence_name ASC; DROP USER regress_seq_user; DROP SEQUENCE seq; -- cache tests CREATE SEQUENCE test_seq1 CACHE 10; SELECT nextval('test_seq1'); SELECT nextval('test_seq1'); SELECT nextval('test_seq1'); DROP SEQUENCE test_seq1; pgFormatter-4.2/t/pg-test-files/sql/spgist.sql000066400000000000000000000055311361326045100214350ustar00rootroot00000000000000-- -- Test SP-GiST indexes. -- -- There are other tests to test different SP-GiST opclasses. This is for -- testing SP-GiST code itself. create table spgist_point_tbl(id int4, p point); create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75); -- Test vacuum-root operation. It gets invoked when the root is also a leaf, -- i.e. the index is very small. insert into spgist_point_tbl (id, p) select g, point(g*10, g*10) from generate_series(1, 10) g; delete from spgist_point_tbl where id < 5; vacuum spgist_point_tbl; -- Insert more data, to make the index a few levels deep. insert into spgist_point_tbl (id, p) select g, point(g*10, g*10) from generate_series(1, 10000) g; insert into spgist_point_tbl (id, p) select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g; -- To test vacuum, delete some entries from all over the index. delete from spgist_point_tbl where id % 2 = 1; -- And also delete some concentration of values. (SP-GiST doesn't currently -- attempt to delete pages even when they become empty, but if it did, this -- would exercise it) delete from spgist_point_tbl where id < 10000; vacuum spgist_point_tbl; -- Test rescan paths (cf. bug #15378) -- use box and && rather than point, so that rescan happens when the -- traverse stack is non-empty create table spgist_box_tbl(id serial, b box); insert into spgist_box_tbl(b) select box(point(i,j),point(i+s,j+s)) from generate_series(1,100,5) i, generate_series(1,100,5) j, generate_series(1,10) s; create index spgist_box_idx on spgist_box_tbl using spgist (b); select count(*) from (values (point(5,5)),(point(8,8)),(point(12,12))) v(p) where exists(select * from spgist_box_tbl b where b.b && box(v.p,v.p)); -- The point opclass's choose method only uses the spgMatchNode action, -- so the other actions are not tested by the above. Create an index using -- text opclass, which uses the others actions. create table spgist_text_tbl(id int4, t text); create index spgist_text_idx on spgist_text_tbl using spgist(t); insert into spgist_text_tbl (id, t) select g, 'f' || repeat('o', 100) || g from generate_series(1, 10000) g union all select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g; -- Do a lot of insertions that have to split an existing node. Hopefully -- one of these will cause the page to run out of space, causing the inner -- tuple to be moved to another page. insert into spgist_text_tbl (id, t) select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g; -- Test out-of-range fillfactor values create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9); create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101); -- Modify fillfactor in existing index alter index spgist_point_idx set (fillfactor = 90); reindex index spgist_point_idx; pgFormatter-4.2/t/pg-test-files/sql/stats.sql000066400000000000000000000145451361326045100212670ustar00rootroot00000000000000-- -- Test Statistics Collector -- -- Must be run after tenk2 has been created (by create_table), -- populated (by create_misc) and indexed (by create_index). -- -- conditio sine qua non SHOW track_counts; -- must be on -- ensure that both seqscan and indexscan plans are allowed SET enable_seqscan TO on; SET enable_indexscan TO on; -- for the moment, we don't want index-only scans here SET enable_indexonlyscan TO off; -- save counters CREATE TABLE prevstats AS SELECT t.seq_scan, t.seq_tup_read, t.idx_scan, t.idx_tup_fetch, (b.heap_blks_read + b.heap_blks_hit) AS heap_blks, (b.idx_blks_read + b.idx_blks_hit) AS idx_blks, pg_stat_get_snapshot_timestamp() as snap_ts FROM pg_catalog.pg_stat_user_tables AS t, pg_catalog.pg_statio_user_tables AS b WHERE t.relname='tenk2' AND b.relname='tenk2'; -- function to wait for counters to advance create function wait_for_stats() returns void as $$ declare start_time timestamptz := clock_timestamp(); updated1 bool; updated2 bool; updated3 bool; updated4 bool; begin -- we don't want to wait forever; loop will exit after 30 seconds for i in 1 .. 300 loop -- With parallel query, the seqscan and indexscan on tenk2 might be done -- in parallel worker processes, which will send their stats counters -- asynchronously to what our own session does. So we must check for -- those counts to be registered separately from the update counts. -- check to see if seqscan has been sensed SELECT (st.seq_scan >= pr.seq_scan + 1) INTO updated1 FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr WHERE st.relname='tenk2' AND cl.relname='tenk2'; -- check to see if indexscan has been sensed SELECT (st.idx_scan >= pr.idx_scan + 1) INTO updated2 FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr WHERE st.relname='tenk2' AND cl.relname='tenk2'; -- check to see if all updates have been sensed SELECT (n_tup_ins > 0) INTO updated3 FROM pg_stat_user_tables WHERE relname='trunc_stats_test4'; -- We must also check explicitly that pg_stat_get_snapshot_timestamp has -- advanced, because that comes from the global stats file which might -- be older than the per-DB stats file we got the other values from. SELECT (pr.snap_ts < pg_stat_get_snapshot_timestamp()) INTO updated4 FROM prevstats AS pr; exit when updated1 and updated2 and updated3 and updated4; -- wait a little perform pg_sleep_for('100 milliseconds'); -- reset stats snapshot so we can test again perform pg_stat_clear_snapshot(); end loop; -- report time waited in postmaster log (where it won't change test output) raise log 'wait_for_stats delayed % seconds', extract(epoch from clock_timestamp() - start_time); end $$ language plpgsql; -- test effects of TRUNCATE on n_live_tup/n_dead_tup counters CREATE TABLE trunc_stats_test(id serial); CREATE TABLE trunc_stats_test1(id serial, stuff text); CREATE TABLE trunc_stats_test2(id serial); CREATE TABLE trunc_stats_test3(id serial, stuff text); CREATE TABLE trunc_stats_test4(id serial); -- check that n_live_tup is reset to 0 after truncate INSERT INTO trunc_stats_test DEFAULT VALUES; INSERT INTO trunc_stats_test DEFAULT VALUES; INSERT INTO trunc_stats_test DEFAULT VALUES; TRUNCATE trunc_stats_test; -- test involving a truncate in a transaction; 4 ins but only 1 live INSERT INTO trunc_stats_test1 DEFAULT VALUES; INSERT INTO trunc_stats_test1 DEFAULT VALUES; INSERT INTO trunc_stats_test1 DEFAULT VALUES; UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2); DELETE FROM trunc_stats_test1 WHERE id = 3; BEGIN; UPDATE trunc_stats_test1 SET id = id + 100; TRUNCATE trunc_stats_test1; INSERT INTO trunc_stats_test1 DEFAULT VALUES; COMMIT; -- use a savepoint: 1 insert, 1 live BEGIN; INSERT INTO trunc_stats_test2 DEFAULT VALUES; INSERT INTO trunc_stats_test2 DEFAULT VALUES; SAVEPOINT p1; INSERT INTO trunc_stats_test2 DEFAULT VALUES; TRUNCATE trunc_stats_test2; INSERT INTO trunc_stats_test2 DEFAULT VALUES; RELEASE SAVEPOINT p1; COMMIT; -- rollback a savepoint: this should count 4 inserts and have 2 -- live tuples after commit (and 2 dead ones due to aborted subxact) BEGIN; INSERT INTO trunc_stats_test3 DEFAULT VALUES; INSERT INTO trunc_stats_test3 DEFAULT VALUES; SAVEPOINT p1; INSERT INTO trunc_stats_test3 DEFAULT VALUES; INSERT INTO trunc_stats_test3 DEFAULT VALUES; TRUNCATE trunc_stats_test3; INSERT INTO trunc_stats_test3 DEFAULT VALUES; ROLLBACK TO SAVEPOINT p1; COMMIT; -- rollback a truncate: this should count 2 inserts and produce 2 dead tuples BEGIN; INSERT INTO trunc_stats_test4 DEFAULT VALUES; INSERT INTO trunc_stats_test4 DEFAULT VALUES; TRUNCATE trunc_stats_test4; INSERT INTO trunc_stats_test4 DEFAULT VALUES; ROLLBACK; -- do a seqscan SELECT count(*) FROM tenk2; -- do an indexscan -- make sure it is not a bitmap scan, which might skip fetching heap tuples SET enable_bitmapscan TO off; SELECT count(*) FROM tenk2 WHERE unique1 = 1; RESET enable_bitmapscan; -- We can't just call wait_for_stats() at this point, because we only -- transmit stats when the session goes idle, and we probably didn't -- transmit the last couple of counts yet thanks to the rate-limiting logic -- in pgstat_report_stat(). But instead of waiting for the rate limiter's -- timeout to elapse, let's just start a new session. The old one will -- then send its stats before dying. \c - -- wait for stats collector to update SELECT wait_for_stats(); -- check effects SELECT relname, n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup FROM pg_stat_user_tables WHERE relname like 'trunc_stats_test%' order by relname; SELECT st.seq_scan >= pr.seq_scan + 1, st.seq_tup_read >= pr.seq_tup_read + cl.reltuples, st.idx_scan >= pr.idx_scan + 1, st.idx_tup_fetch >= pr.idx_tup_fetch + 1 FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr WHERE st.relname='tenk2' AND cl.relname='tenk2'; SELECT st.heap_blks_read + st.heap_blks_hit >= pr.heap_blks + cl.relpages, st.idx_blks_read + st.idx_blks_hit >= pr.idx_blks + 1 FROM pg_statio_user_tables AS st, pg_class AS cl, prevstats AS pr WHERE st.relname='tenk2' AND cl.relname='tenk2'; SELECT pr.snap_ts < pg_stat_get_snapshot_timestamp() as snapshot_newer FROM prevstats AS pr; DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4; DROP TABLE prevstats; -- End of Stats Test pgFormatter-4.2/t/pg-test-files/sql/stats_ext.sql000066400000000000000000000361411361326045100221430ustar00rootroot00000000000000-- Generic extended statistics support -- We will be checking execution plans without/with statistics, so -- let's make sure we get simple non-parallel plans. Also set the -- work_mem low so that we can use small amounts of data. -- check the number of estimated/actual rows in the top node create function check_estimated_rows(text) returns table (estimated int, actual int) language plpgsql as $$ declare ln text; tmp text[]; first_row bool := true; begin for ln in execute format('explain analyze %s', $1) loop if first_row then first_row := false; tmp := regexp_match(ln, 'rows=(\d*) .* rows=(\d*)'); return query select tmp[1]::int, tmp[2]::int; end if; end loop; end; $$; -- Verify failures CREATE STATISTICS tst; CREATE STATISTICS tst ON a, b; CREATE STATISTICS tst FROM sometab; CREATE STATISTICS tst ON a, b FROM nonexistant; CREATE STATISTICS tst ON a, b FROM pg_class; CREATE STATISTICS tst ON relname, relname, relnatts FROM pg_class; CREATE STATISTICS tst ON relnatts + relpages FROM pg_class; CREATE STATISTICS tst ON (relpages, reltuples) FROM pg_class; CREATE STATISTICS tst (unrecognized) ON relname, relnatts FROM pg_class; -- Ensure stats are dropped sanely, and test IF NOT EXISTS while at it CREATE TABLE ab1 (a INTEGER, b INTEGER, c INTEGER); CREATE STATISTICS IF NOT EXISTS ab1_a_b_stats ON a, b FROM ab1; CREATE STATISTICS IF NOT EXISTS ab1_a_b_stats ON a, b FROM ab1; DROP STATISTICS ab1_a_b_stats; CREATE SCHEMA regress_schema_2; CREATE STATISTICS regress_schema_2.ab1_a_b_stats ON a, b FROM ab1; -- Let's also verify the pg_get_statisticsobjdef output looks sane. SELECT pg_get_statisticsobjdef(oid) FROM pg_statistic_ext WHERE stxname = 'ab1_a_b_stats'; DROP STATISTICS regress_schema_2.ab1_a_b_stats; -- Ensure statistics are dropped when columns are CREATE STATISTICS ab1_b_c_stats ON b, c FROM ab1; CREATE STATISTICS ab1_a_b_c_stats ON a, b, c FROM ab1; CREATE STATISTICS ab1_b_a_stats ON b, a FROM ab1; ALTER TABLE ab1 DROP COLUMN a; \d ab1 -- Ensure statistics are dropped when table is SELECT stxname FROM pg_statistic_ext WHERE stxname LIKE 'ab1%'; DROP TABLE ab1; SELECT stxname FROM pg_statistic_ext WHERE stxname LIKE 'ab1%'; -- Ensure things work sanely with SET STATISTICS 0 CREATE TABLE ab1 (a INTEGER, b INTEGER); ALTER TABLE ab1 ALTER a SET STATISTICS 0; INSERT INTO ab1 SELECT a, a%23 FROM generate_series(1, 1000) a; CREATE STATISTICS ab1_a_b_stats ON a, b FROM ab1; ANALYZE ab1; ALTER TABLE ab1 ALTER a SET STATISTICS -1; -- partial analyze doesn't build stats either ANALYZE ab1 (a); ANALYZE ab1; DROP TABLE ab1; -- Verify supported object types for extended statistics CREATE schema tststats; CREATE TABLE tststats.t (a int, b int, c text); CREATE INDEX ti ON tststats.t (a, b); CREATE SEQUENCE tststats.s; CREATE VIEW tststats.v AS SELECT * FROM tststats.t; CREATE MATERIALIZED VIEW tststats.mv AS SELECT * FROM tststats.t; CREATE TYPE tststats.ty AS (a int, b int, c text); CREATE FOREIGN DATA WRAPPER extstats_dummy_fdw; CREATE SERVER extstats_dummy_srv FOREIGN DATA WRAPPER extstats_dummy_fdw; CREATE FOREIGN TABLE tststats.f (a int, b int, c text) SERVER extstats_dummy_srv; CREATE TABLE tststats.pt (a int, b int, c text) PARTITION BY RANGE (a, b); CREATE TABLE tststats.pt1 PARTITION OF tststats.pt FOR VALUES FROM (-10, -10) TO (10, 10); CREATE STATISTICS tststats.s1 ON a, b FROM tststats.t; CREATE STATISTICS tststats.s2 ON a, b FROM tststats.ti; CREATE STATISTICS tststats.s3 ON a, b FROM tststats.s; CREATE STATISTICS tststats.s4 ON a, b FROM tststats.v; CREATE STATISTICS tststats.s5 ON a, b FROM tststats.mv; CREATE STATISTICS tststats.s6 ON a, b FROM tststats.ty; CREATE STATISTICS tststats.s7 ON a, b FROM tststats.f; CREATE STATISTICS tststats.s8 ON a, b FROM tststats.pt; CREATE STATISTICS tststats.s9 ON a, b FROM tststats.pt1; DO $$ DECLARE relname text := reltoastrelid::regclass FROM pg_class WHERE oid = 'tststats.t'::regclass; BEGIN EXECUTE 'CREATE STATISTICS tststats.s10 ON a, b FROM ' || relname; EXCEPTION WHEN wrong_object_type THEN RAISE NOTICE 'stats on toast table not created'; END; $$; DROP SCHEMA tststats CASCADE; DROP FOREIGN DATA WRAPPER extstats_dummy_fdw CASCADE; -- n-distinct tests CREATE TABLE ndistinct ( filler1 TEXT, filler2 NUMERIC, a INT, b INT, filler3 DATE, c INT, d INT ); -- over-estimates when using only per-column statistics INSERT INTO ndistinct (a, b, c, filler1) SELECT i/100, i/100, i/100, cash_words((i/100)::money) FROM generate_series(1,1000) s(i); ANALYZE ndistinct; -- Group Aggregate, due to over-estimate of the number of groups SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY b, c'); SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c'); SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c, d'); SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY b, c, d'); -- correct command CREATE STATISTICS s10 ON a, b, c FROM ndistinct; ANALYZE ndistinct; SELECT stxkind, stxndistinct FROM pg_statistic_ext WHERE stxrelid = 'ndistinct'::regclass; -- Hash Aggregate, thanks to estimates improved by the statistic SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY b, c'); SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c'); -- last two plans keep using Group Aggregate, because 'd' is not covered -- by the statistic and while it's NULL-only we assume 200 values for it SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c, d'); SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY b, c, d'); TRUNCATE TABLE ndistinct; -- under-estimates when using only per-column statistics INSERT INTO ndistinct (a, b, c, filler1) SELECT mod(i,50), mod(i,51), mod(i,32), cash_words(mod(i,33)::int::money) FROM generate_series(1,5000) s(i); ANALYZE ndistinct; SELECT stxkind, stxndistinct FROM pg_statistic_ext WHERE stxrelid = 'ndistinct'::regclass; -- correct esimates SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c'); SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c, d'); SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY b, c, d'); SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, d'); DROP STATISTICS s10; SELECT stxkind, stxndistinct FROM pg_statistic_ext WHERE stxrelid = 'ndistinct'::regclass; -- dropping the statistics results in under-estimates SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b'); SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c'); SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b, c, d'); SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY b, c, d'); SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, d'); -- functional dependencies tests CREATE TABLE functional_dependencies ( filler1 TEXT, filler2 NUMERIC, a INT, b TEXT, filler3 DATE, c INT, d TEXT ); CREATE INDEX fdeps_ab_idx ON functional_dependencies (a, b); CREATE INDEX fdeps_abc_idx ON functional_dependencies (a, b, c); -- random data (no functional dependencies) INSERT INTO functional_dependencies (a, b, c, filler1) SELECT mod(i, 23), mod(i, 29), mod(i, 31), i FROM generate_series(1,5000) s(i); ANALYZE functional_dependencies; SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'''); SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'' AND c = 1'); -- create statistics CREATE STATISTICS func_deps_stat (dependencies) ON a, b, c FROM functional_dependencies; ANALYZE functional_dependencies; SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'''); SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'' AND c = 1'); -- a => b, a => c, b => c TRUNCATE functional_dependencies; DROP STATISTICS func_deps_stat; INSERT INTO functional_dependencies (a, b, c, filler1) SELECT mod(i,100), mod(i,50), mod(i,25), i FROM generate_series(1,5000) s(i); ANALYZE functional_dependencies; SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'''); SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'' AND c = 1'); -- create statistics CREATE STATISTICS func_deps_stat (dependencies) ON a, b, c FROM functional_dependencies; ANALYZE functional_dependencies; SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'''); SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'' AND c = 1'); -- check change of column type doesn't break it ALTER TABLE functional_dependencies ALTER COLUMN c TYPE numeric; SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'' AND c = 1'); ANALYZE functional_dependencies; SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1'' AND c = 1'); -- MCV lists CREATE TABLE mcv_lists ( filler1 TEXT, filler2 NUMERIC, a INT, b VARCHAR, filler3 DATE, c INT, d TEXT ); -- random data (no MCV list) INSERT INTO mcv_lists (a, b, c, filler1) SELECT mod(i,37), mod(i,41), mod(i,43), mod(i,47) FROM generate_series(1,5000) s(i); ANALYZE mcv_lists; SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'''); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'' AND c = 1'); -- create statistics CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, c FROM mcv_lists; ANALYZE mcv_lists; SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'''); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'' AND c = 1'); -- 100 distinct combinations, all in the MCV list TRUNCATE mcv_lists; DROP STATISTICS mcv_lists_stats; INSERT INTO mcv_lists (a, b, c, filler1) SELECT mod(i,100), mod(i,50), mod(i,25), i FROM generate_series(1,5000) s(i); ANALYZE mcv_lists; SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'''); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < 1 AND b < ''1'''); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a <= 0 AND b <= ''0'''); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'' AND c = 1'); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < 5 AND b < ''1'' AND c < 5'); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a <= 4 AND b <= ''0'' AND c <= 4'); -- create statistics CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, c FROM mcv_lists; ANALYZE mcv_lists; SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'''); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < 1 AND b < ''1'''); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a <= 0 AND b <= ''0'''); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'' AND c = 1'); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < 5 AND b < ''1'' AND c < 5'); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a <= 4 AND b <= ''0'' AND c <= 4'); -- check change of unrelated column type does not reset the MCV statistics ALTER TABLE mcv_lists ALTER COLUMN d TYPE VARCHAR(64); SELECT stxmcv IS NOT NULL FROM pg_statistic_ext WHERE stxname = 'mcv_lists_stats'; -- check change of column type resets the MCV statistics ALTER TABLE mcv_lists ALTER COLUMN c TYPE numeric; SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'''); ANALYZE mcv_lists; SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1'''); -- 100 distinct combinations with NULL values, all in the MCV list TRUNCATE mcv_lists; DROP STATISTICS mcv_lists_stats; INSERT INTO mcv_lists (a, b, c, filler1) SELECT (CASE WHEN mod(i,100) = 1 THEN NULL ELSE mod(i,100) END), (CASE WHEN mod(i,50) = 1 THEN NULL ELSE mod(i,50) END), (CASE WHEN mod(i,25) = 1 THEN NULL ELSE mod(i,25) END), i FROM generate_series(1,5000) s(i); ANALYZE mcv_lists; SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IS NULL AND b IS NULL'); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IS NULL AND b IS NULL AND c IS NULL'); -- create statistics CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, c FROM mcv_lists; ANALYZE mcv_lists; SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IS NULL AND b IS NULL'); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IS NULL AND b IS NULL AND c IS NULL'); -- test pg_mcv_list_items with a very simple (single item) MCV list TRUNCATE mcv_lists; INSERT INTO mcv_lists (a, b, c) SELECT 1, 2, 3 FROM generate_series(1,1000) s(i); ANALYZE mcv_lists; SELECT m.* FROM pg_statistic_ext, pg_mcv_list_items(stxmcv) m WHERE stxname = 'mcv_lists_stats'; -- mcv with arrays CREATE TABLE mcv_lists_arrays ( a TEXT[], b NUMERIC[], c INT[] ); INSERT INTO mcv_lists_arrays (a, b, c) SELECT ARRAY[md5((i/100)::text), md5((i/100-1)::text), md5((i/100+1)::text)], ARRAY[(i/100-1)::numeric/1000, (i/100)::numeric/1000, (i/100+1)::numeric/1000], ARRAY[(i/100-1), i/100, (i/100+1)] FROM generate_series(1,5000) s(i); CREATE STATISTICS mcv_lists_arrays_stats (mcv) ON a, b, c FROM mcv_lists_arrays; ANALYZE mcv_lists_arrays; -- mcv with bool CREATE TABLE mcv_lists_bool ( a BOOL, b BOOL, c BOOL ); INSERT INTO mcv_lists_bool (a, b, c) SELECT (mod(i,2) = 0), (mod(i,4) = 0), (mod(i,8) = 0) FROM generate_series(1,10000) s(i); ANALYZE mcv_lists_bool; SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE a AND b AND c'); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE NOT a AND b AND c'); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE NOT a AND NOT b AND c'); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE NOT a AND b AND NOT c'); CREATE STATISTICS mcv_lists_bool_stats (mcv) ON a, b, c FROM mcv_lists_bool; ANALYZE mcv_lists_bool; SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE a AND b AND c'); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE NOT a AND b AND c'); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE NOT a AND NOT b AND c'); SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE NOT a AND b AND NOT c'); pgFormatter-4.2/t/pg-test-files/sql/strings.sql000066400000000000000000000475441361326045100216270ustar00rootroot00000000000000-- -- STRINGS -- Test various data entry syntaxes. -- -- SQL string continuation syntax -- E021-03 character string literals SELECT 'first line' ' - next line' ' - third line' AS "Three lines to one"; -- illegal string continuation syntax SELECT 'first line' ' - next line' /* this comment is not allowed here */ ' - third line' AS "Illegal comment within continuation"; -- Unicode escapes SET standard_conforming_strings TO on; SELECT U&'d\0061t\+000061' AS U&"d\0061t\+000061"; SELECT U&'d!0061t\+000061' UESCAPE '!' AS U&"d*0061t\+000061" UESCAPE '*'; SELECT U&' \' UESCAPE '!' AS "tricky"; SELECT 'tricky' AS U&"\" UESCAPE '!'; SELECT U&'wrong: \061'; SELECT U&'wrong: \+0061'; SELECT U&'wrong: +0061' UESCAPE '+'; SET standard_conforming_strings TO off; SELECT U&'d\0061t\+000061' AS U&"d\0061t\+000061"; SELECT U&'d!0061t\+000061' UESCAPE '!' AS U&"d*0061t\+000061" UESCAPE '*'; SELECT U&' \' UESCAPE '!' AS "tricky"; SELECT 'tricky' AS U&"\" UESCAPE '!'; SELECT U&'wrong: \061'; SELECT U&'wrong: \+0061'; SELECT U&'wrong: +0061' UESCAPE '+'; RESET standard_conforming_strings; -- bytea SET bytea_output TO hex; SELECT E'\\xDeAdBeEf'::bytea; SELECT E'\\x De Ad Be Ef '::bytea; SELECT E'\\xDeAdBeE'::bytea; SELECT E'\\xDeAdBeEx'::bytea; SELECT E'\\xDe00BeEf'::bytea; SELECT E'DeAdBeEf'::bytea; SELECT E'De\\000dBeEf'::bytea; SELECT E'De\123dBeEf'::bytea; SELECT E'De\\123dBeEf'::bytea; SELECT E'De\\678dBeEf'::bytea; SET bytea_output TO escape; SELECT E'\\xDeAdBeEf'::bytea; SELECT E'\\x De Ad Be Ef '::bytea; SELECT E'\\xDe00BeEf'::bytea; SELECT E'DeAdBeEf'::bytea; SELECT E'De\\000dBeEf'::bytea; SELECT E'De\\123dBeEf'::bytea; -- -- test conversions between various string types -- E021-10 implicit casting among the character data types -- SELECT CAST(f1 AS text) AS "text(char)" FROM CHAR_TBL; SELECT CAST(f1 AS text) AS "text(varchar)" FROM VARCHAR_TBL; SELECT CAST(name 'namefield' AS text) AS "text(name)"; -- since this is an explicit cast, it should truncate w/o error: SELECT CAST(f1 AS char(10)) AS "char(text)" FROM TEXT_TBL; -- note: implicit-cast case is tested in char.sql SELECT CAST(f1 AS char(20)) AS "char(text)" FROM TEXT_TBL; SELECT CAST(f1 AS char(10)) AS "char(varchar)" FROM VARCHAR_TBL; SELECT CAST(name 'namefield' AS char(10)) AS "char(name)"; SELECT CAST(f1 AS varchar) AS "varchar(text)" FROM TEXT_TBL; SELECT CAST(f1 AS varchar) AS "varchar(char)" FROM CHAR_TBL; SELECT CAST(name 'namefield' AS varchar) AS "varchar(name)"; -- -- test SQL string functions -- E### and T### are feature reference numbers from SQL99 -- -- E021-09 trim function SELECT TRIM(BOTH FROM ' bunch o blanks ') = 'bunch o blanks' AS "bunch o blanks"; SELECT TRIM(LEADING FROM ' bunch o blanks ') = 'bunch o blanks ' AS "bunch o blanks "; SELECT TRIM(TRAILING FROM ' bunch o blanks ') = ' bunch o blanks' AS " bunch o blanks"; SELECT TRIM(BOTH 'x' FROM 'xxxxxsome Xsxxxxx') = 'some Xs' AS "some Xs"; -- E021-06 substring expression SELECT SUBSTRING('1234567890' FROM 3) = '34567890' AS "34567890"; SELECT SUBSTRING('1234567890' FROM 4 FOR 3) = '456' AS "456"; -- T581 regular expression substring (with SQL99's bizarre regexp syntax) SELECT SUBSTRING('abcdefg' FROM 'a#"(b_d)#"%' FOR '#') AS "bcd"; -- No match should return NULL SELECT SUBSTRING('abcdefg' FROM '#"(b_d)#"%' FOR '#') IS NULL AS "True"; -- Null inputs should return NULL SELECT SUBSTRING('abcdefg' FROM '(b|c)' FOR NULL) IS NULL AS "True"; SELECT SUBSTRING(NULL FROM '(b|c)' FOR '#') IS NULL AS "True"; SELECT SUBSTRING('abcdefg' FROM NULL FOR '#') IS NULL AS "True"; -- PostgreSQL extension to allow omitting the escape character; -- here the regexp is taken as Posix syntax SELECT SUBSTRING('abcdefg' FROM 'c.e') AS "cde"; -- With a parenthesized subexpression, return only what matches the subexpr SELECT SUBSTRING('abcdefg' FROM 'b(.*)f') AS "cde"; -- PostgreSQL extension to allow using back reference in replace string; SELECT regexp_replace('1112223333', E'(\\d{3})(\\d{3})(\\d{4})', E'(\\1) \\2-\\3'); SELECT regexp_replace('AAA BBB CCC ', E'\\s+', ' ', 'g'); SELECT regexp_replace('AAA', '^|$', 'Z', 'g'); SELECT regexp_replace('AAA aaa', 'A+', 'Z', 'gi'); -- invalid regexp option SELECT regexp_replace('AAA aaa', 'A+', 'Z', 'z'); -- set so we can tell NULL from empty string \pset null '\\N' -- return all matches from regexp SELECT regexp_matches('foobarbequebaz', $re$(bar)(beque)$re$); -- test case insensitive SELECT regexp_matches('foObARbEqUEbAz', $re$(bar)(beque)$re$, 'i'); -- global option - more than one match SELECT regexp_matches('foobarbequebazilbarfbonk', $re$(b[^b]+)(b[^b]+)$re$, 'g'); -- empty capture group (matched empty string) SELECT regexp_matches('foobarbequebaz', $re$(bar)(.*)(beque)$re$); -- no match SELECT regexp_matches('foobarbequebaz', $re$(bar)(.+)(beque)$re$); -- optional capture group did not match, null entry in array SELECT regexp_matches('foobarbequebaz', $re$(bar)(.+)?(beque)$re$); -- no capture groups SELECT regexp_matches('foobarbequebaz', $re$barbeque$re$); -- start/end-of-line matches are of zero length SELECT regexp_matches('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^', 'mg'); SELECT regexp_matches('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '$', 'mg'); SELECT regexp_matches('1' || chr(10) || '2' || chr(10) || '3' || chr(10) || '4' || chr(10), '^.?', 'mg'); SELECT regexp_matches(chr(10) || '1' || chr(10) || '2' || chr(10) || '3' || chr(10) || '4' || chr(10), '.?$', 'mg'); SELECT regexp_matches(chr(10) || '1' || chr(10) || '2' || chr(10) || '3' || chr(10) || '4', '.?$', 'mg'); -- give me errors SELECT regexp_matches('foobarbequebaz', $re$(bar)(beque)$re$, 'gz'); SELECT regexp_matches('foobarbequebaz', $re$(barbeque$re$); SELECT regexp_matches('foobarbequebaz', $re$(bar)(beque){2,1}$re$); -- split string on regexp SELECT foo, length(foo) FROM regexp_split_to_table('the quick brown fox jumps over the lazy dog', $re$\s+$re$) AS foo; SELECT regexp_split_to_array('the quick brown fox jumps over the lazy dog', $re$\s+$re$); SELECT foo, length(foo) FROM regexp_split_to_table('the quick brown fox jumps over the lazy dog', $re$\s*$re$) AS foo; SELECT regexp_split_to_array('the quick brown fox jumps over the lazy dog', $re$\s*$re$); SELECT foo, length(foo) FROM regexp_split_to_table('the quick brown fox jumps over the lazy dog', '') AS foo; SELECT regexp_split_to_array('the quick brown fox jumps over the lazy dog', ''); -- case insensitive SELECT foo, length(foo) FROM regexp_split_to_table('thE QUick bROWn FOx jUMPs ovEr The lazy dOG', 'e', 'i') AS foo; SELECT regexp_split_to_array('thE QUick bROWn FOx jUMPs ovEr The lazy dOG', 'e', 'i'); -- no match of pattern SELECT foo, length(foo) FROM regexp_split_to_table('the quick brown fox jumps over the lazy dog', 'nomatch') AS foo; SELECT regexp_split_to_array('the quick brown fox jumps over the lazy dog', 'nomatch'); -- some corner cases SELECT regexp_split_to_array('123456','1'); SELECT regexp_split_to_array('123456','6'); SELECT regexp_split_to_array('123456','.'); SELECT regexp_split_to_array('123456',''); SELECT regexp_split_to_array('123456','(?:)'); SELECT regexp_split_to_array('1',''); -- errors SELECT foo, length(foo) FROM regexp_split_to_table('thE QUick bROWn FOx jUMPs ovEr The lazy dOG', 'e', 'zippy') AS foo; SELECT regexp_split_to_array('thE QUick bROWn FOx jUMPs ovEr The lazy dOG', 'e', 'iz'); -- global option meaningless for regexp_split SELECT foo, length(foo) FROM regexp_split_to_table('thE QUick bROWn FOx jUMPs ovEr The lazy dOG', 'e', 'g') AS foo; SELECT regexp_split_to_array('thE QUick bROWn FOx jUMPs ovEr The lazy dOG', 'e', 'g'); -- change NULL-display back \pset null '' -- E021-11 position expression SELECT POSITION('4' IN '1234567890') = '4' AS "4"; SELECT POSITION('5' IN '1234567890') = '5' AS "5"; -- T312 character overlay function SELECT OVERLAY('abcdef' PLACING '45' FROM 4) AS "abc45f"; SELECT OVERLAY('yabadoo' PLACING 'daba' FROM 5) AS "yabadaba"; SELECT OVERLAY('yabadoo' PLACING 'daba' FROM 5 FOR 0) AS "yabadabadoo"; SELECT OVERLAY('babosa' PLACING 'ubb' FROM 2 FOR 4) AS "bubba"; -- -- test LIKE -- Be sure to form every test as a LIKE/NOT LIKE pair. -- -- simplest examples -- E061-04 like predicate SELECT 'hawkeye' LIKE 'h%' AS "true"; SELECT 'hawkeye' NOT LIKE 'h%' AS "false"; SELECT 'hawkeye' LIKE 'H%' AS "false"; SELECT 'hawkeye' NOT LIKE 'H%' AS "true"; SELECT 'hawkeye' LIKE 'indio%' AS "false"; SELECT 'hawkeye' NOT LIKE 'indio%' AS "true"; SELECT 'hawkeye' LIKE 'h%eye' AS "true"; SELECT 'hawkeye' NOT LIKE 'h%eye' AS "false"; SELECT 'indio' LIKE '_ndio' AS "true"; SELECT 'indio' NOT LIKE '_ndio' AS "false"; SELECT 'indio' LIKE 'in__o' AS "true"; SELECT 'indio' NOT LIKE 'in__o' AS "false"; SELECT 'indio' LIKE 'in_o' AS "false"; SELECT 'indio' NOT LIKE 'in_o' AS "true"; -- unused escape character SELECT 'hawkeye' LIKE 'h%' ESCAPE '#' AS "true"; SELECT 'hawkeye' NOT LIKE 'h%' ESCAPE '#' AS "false"; SELECT 'indio' LIKE 'ind_o' ESCAPE '$' AS "true"; SELECT 'indio' NOT LIKE 'ind_o' ESCAPE '$' AS "false"; -- escape character -- E061-05 like predicate with escape clause SELECT 'h%' LIKE 'h#%' ESCAPE '#' AS "true"; SELECT 'h%' NOT LIKE 'h#%' ESCAPE '#' AS "false"; SELECT 'h%wkeye' LIKE 'h#%' ESCAPE '#' AS "false"; SELECT 'h%wkeye' NOT LIKE 'h#%' ESCAPE '#' AS "true"; SELECT 'h%wkeye' LIKE 'h#%%' ESCAPE '#' AS "true"; SELECT 'h%wkeye' NOT LIKE 'h#%%' ESCAPE '#' AS "false"; SELECT 'h%awkeye' LIKE 'h#%a%k%e' ESCAPE '#' AS "true"; SELECT 'h%awkeye' NOT LIKE 'h#%a%k%e' ESCAPE '#' AS "false"; SELECT 'indio' LIKE '_ndio' ESCAPE '$' AS "true"; SELECT 'indio' NOT LIKE '_ndio' ESCAPE '$' AS "false"; SELECT 'i_dio' LIKE 'i$_d_o' ESCAPE '$' AS "true"; SELECT 'i_dio' NOT LIKE 'i$_d_o' ESCAPE '$' AS "false"; SELECT 'i_dio' LIKE 'i$_nd_o' ESCAPE '$' AS "false"; SELECT 'i_dio' NOT LIKE 'i$_nd_o' ESCAPE '$' AS "true"; SELECT 'i_dio' LIKE 'i$_d%o' ESCAPE '$' AS "true"; SELECT 'i_dio' NOT LIKE 'i$_d%o' ESCAPE '$' AS "false"; -- escape character same as pattern character SELECT 'maca' LIKE 'm%aca' ESCAPE '%' AS "true"; SELECT 'maca' NOT LIKE 'm%aca' ESCAPE '%' AS "false"; SELECT 'ma%a' LIKE 'm%a%%a' ESCAPE '%' AS "true"; SELECT 'ma%a' NOT LIKE 'm%a%%a' ESCAPE '%' AS "false"; SELECT 'bear' LIKE 'b_ear' ESCAPE '_' AS "true"; SELECT 'bear' NOT LIKE 'b_ear' ESCAPE '_' AS "false"; SELECT 'be_r' LIKE 'b_e__r' ESCAPE '_' AS "true"; SELECT 'be_r' NOT LIKE 'b_e__r' ESCAPE '_' AS "false"; SELECT 'be_r' LIKE '__e__r' ESCAPE '_' AS "false"; SELECT 'be_r' NOT LIKE '__e__r' ESCAPE '_' AS "true"; -- -- test ILIKE (case-insensitive LIKE) -- Be sure to form every test as an ILIKE/NOT ILIKE pair. -- SELECT 'hawkeye' ILIKE 'h%' AS "true"; SELECT 'hawkeye' NOT ILIKE 'h%' AS "false"; SELECT 'hawkeye' ILIKE 'H%' AS "true"; SELECT 'hawkeye' NOT ILIKE 'H%' AS "false"; SELECT 'hawkeye' ILIKE 'H%Eye' AS "true"; SELECT 'hawkeye' NOT ILIKE 'H%Eye' AS "false"; SELECT 'Hawkeye' ILIKE 'h%' AS "true"; SELECT 'Hawkeye' NOT ILIKE 'h%' AS "false"; -- -- test %/_ combination cases, cf bugs #4821 and #5478 -- SELECT 'foo' LIKE '_%' as t, 'f' LIKE '_%' as t, '' LIKE '_%' as f; SELECT 'foo' LIKE '%_' as t, 'f' LIKE '%_' as t, '' LIKE '%_' as f; SELECT 'foo' LIKE '__%' as t, 'foo' LIKE '___%' as t, 'foo' LIKE '____%' as f; SELECT 'foo' LIKE '%__' as t, 'foo' LIKE '%___' as t, 'foo' LIKE '%____' as f; SELECT 'jack' LIKE '%____%' AS t; -- -- basic tests of LIKE with indexes -- CREATE TABLE texttest (a text PRIMARY KEY, b int); SELECT * FROM texttest WHERE a LIKE '%1%'; CREATE TABLE byteatest (a bytea PRIMARY KEY, b int); SELECT * FROM byteatest WHERE a LIKE '%1%'; DROP TABLE texttest, byteatest; -- -- test implicit type conversion -- -- E021-07 character concatenation SELECT 'unknown' || ' and unknown' AS "Concat unknown types"; SELECT text 'text' || ' and unknown' AS "Concat text to unknown type"; SELECT char(20) 'characters' || ' and text' AS "Concat char to unknown type"; SELECT text 'text' || char(20) ' and characters' AS "Concat text to char"; SELECT text 'text' || varchar ' and varchar' AS "Concat text to varchar"; -- -- test substr with toasted text values -- CREATE TABLE toasttest(f1 text); insert into toasttest values(repeat('1234567890',10000)); insert into toasttest values(repeat('1234567890',10000)); -- -- Ensure that some values are uncompressed, to test the faster substring -- operation used in that case -- alter table toasttest alter column f1 set storage external; insert into toasttest values(repeat('1234567890',10000)); insert into toasttest values(repeat('1234567890',10000)); -- If the starting position is zero or less, then return from the start of the string -- adjusting the length to be consistent with the "negative start" per SQL. SELECT substr(f1, -1, 5) from toasttest; -- If the length is less than zero, an ERROR is thrown. SELECT substr(f1, 5, -1) from toasttest; -- If no third argument (length) is provided, the length to the end of the -- string is assumed. SELECT substr(f1, 99995) from toasttest; -- If start plus length is > string length, the result is truncated to -- string length SELECT substr(f1, 99995, 10) from toasttest; TRUNCATE TABLE toasttest; INSERT INTO toasttest values (repeat('1234567890',300)); INSERT INTO toasttest values (repeat('1234567890',300)); INSERT INTO toasttest values (repeat('1234567890',300)); INSERT INTO toasttest values (repeat('1234567890',300)); -- expect >0 blocks SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty FROM pg_class where relname = 'toasttest'; TRUNCATE TABLE toasttest; ALTER TABLE toasttest set (toast_tuple_target = 4080); INSERT INTO toasttest values (repeat('1234567890',300)); INSERT INTO toasttest values (repeat('1234567890',300)); INSERT INTO toasttest values (repeat('1234567890',300)); INSERT INTO toasttest values (repeat('1234567890',300)); -- expect 0 blocks SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty FROM pg_class where relname = 'toasttest'; DROP TABLE toasttest; -- -- test substr with toasted bytea values -- CREATE TABLE toasttest(f1 bytea); insert into toasttest values(decode(repeat('1234567890',10000),'escape')); insert into toasttest values(decode(repeat('1234567890',10000),'escape')); -- -- Ensure that some values are uncompressed, to test the faster substring -- operation used in that case -- alter table toasttest alter column f1 set storage external; insert into toasttest values(decode(repeat('1234567890',10000),'escape')); insert into toasttest values(decode(repeat('1234567890',10000),'escape')); -- If the starting position is zero or less, then return from the start of the string -- adjusting the length to be consistent with the "negative start" per SQL. SELECT substr(f1, -1, 5) from toasttest; -- If the length is less than zero, an ERROR is thrown. SELECT substr(f1, 5, -1) from toasttest; -- If no third argument (length) is provided, the length to the end of the -- string is assumed. SELECT substr(f1, 99995) from toasttest; -- If start plus length is > string length, the result is truncated to -- string length SELECT substr(f1, 99995, 10) from toasttest; DROP TABLE toasttest; -- test internally compressing datums -- this tests compressing a datum to a very small size which exercises a -- corner case in packed-varlena handling: even though small, the compressed -- datum must be given a 4-byte header because there are no bits to indicate -- compression in a 1-byte header CREATE TABLE toasttest (c char(4096)); INSERT INTO toasttest VALUES('x'); SELECT length(c), c::text FROM toasttest; SELECT c FROM toasttest; DROP TABLE toasttest; -- -- test length -- SELECT length('abcdef') AS "length_6"; -- -- test strpos -- SELECT strpos('abcdef', 'cd') AS "pos_3"; SELECT strpos('abcdef', 'xy') AS "pos_0"; -- -- test replace -- SELECT replace('abcdef', 'de', '45') AS "abc45f"; SELECT replace('yabadabadoo', 'ba', '123') AS "ya123da123doo"; SELECT replace('yabadoo', 'bad', '') AS "yaoo"; -- -- test split_part -- select split_part('joeuser@mydatabase','@',0) AS "an error"; select split_part('joeuser@mydatabase','@',1) AS "joeuser"; select split_part('joeuser@mydatabase','@',2) AS "mydatabase"; select split_part('joeuser@mydatabase','@',3) AS "empty string"; select split_part('@joeuser@mydatabase@','@',2) AS "joeuser"; -- -- test to_hex -- select to_hex(256*256*256 - 1) AS "ffffff"; select to_hex(256::bigint*256::bigint*256::bigint*256::bigint - 1) AS "ffffffff"; -- -- MD5 test suite - from IETF RFC 1321 -- (see: ftp://ftp.rfc-editor.org/in-notes/rfc1321.txt) -- select md5('') = 'd41d8cd98f00b204e9800998ecf8427e' AS "TRUE"; select md5('a') = '0cc175b9c0f1b6a831c399e269772661' AS "TRUE"; select md5('abc') = '900150983cd24fb0d6963f7d28e17f72' AS "TRUE"; select md5('message digest') = 'f96b697d7cb7938d525a2f31aaf161d0' AS "TRUE"; select md5('abcdefghijklmnopqrstuvwxyz') = 'c3fcd3d76192e4007dfb496cca67e13b' AS "TRUE"; select md5('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') = 'd174ab98d277d9f5a5611c2c9f419d9f' AS "TRUE"; select md5('12345678901234567890123456789012345678901234567890123456789012345678901234567890') = '57edf4a22be3c955ac49da2e2107b67a' AS "TRUE"; select md5(''::bytea) = 'd41d8cd98f00b204e9800998ecf8427e' AS "TRUE"; select md5('a'::bytea) = '0cc175b9c0f1b6a831c399e269772661' AS "TRUE"; select md5('abc'::bytea) = '900150983cd24fb0d6963f7d28e17f72' AS "TRUE"; select md5('message digest'::bytea) = 'f96b697d7cb7938d525a2f31aaf161d0' AS "TRUE"; select md5('abcdefghijklmnopqrstuvwxyz'::bytea) = 'c3fcd3d76192e4007dfb496cca67e13b' AS "TRUE"; select md5('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'::bytea) = 'd174ab98d277d9f5a5611c2c9f419d9f' AS "TRUE"; select md5('12345678901234567890123456789012345678901234567890123456789012345678901234567890'::bytea) = '57edf4a22be3c955ac49da2e2107b67a' AS "TRUE"; -- -- SHA-2 -- SET bytea_output TO hex; SELECT sha224(''); SELECT sha224('The quick brown fox jumps over the lazy dog.'); SELECT sha256(''); SELECT sha256('The quick brown fox jumps over the lazy dog.'); SELECT sha384(''); SELECT sha384('The quick brown fox jumps over the lazy dog.'); SELECT sha512(''); SELECT sha512('The quick brown fox jumps over the lazy dog.'); -- -- test behavior of escape_string_warning and standard_conforming_strings options -- set escape_string_warning = off; set standard_conforming_strings = off; show escape_string_warning; show standard_conforming_strings; set escape_string_warning = on; set standard_conforming_strings = on; show escape_string_warning; show standard_conforming_strings; select 'a\bcd' as f1, 'a\b''cd' as f2, 'a\b''''cd' as f3, 'abcd\' as f4, 'ab\''cd' as f5, '\\' as f6; set standard_conforming_strings = off; select 'a\\bcd' as f1, 'a\\b\'cd' as f2, 'a\\b\'''cd' as f3, 'abcd\\' as f4, 'ab\\\'cd' as f5, '\\\\' as f6; set escape_string_warning = off; set standard_conforming_strings = on; select 'a\bcd' as f1, 'a\b''cd' as f2, 'a\b''''cd' as f3, 'abcd\' as f4, 'ab\''cd' as f5, '\\' as f6; set standard_conforming_strings = off; select 'a\\bcd' as f1, 'a\\b\'cd' as f2, 'a\\b\'''cd' as f3, 'abcd\\' as f4, 'ab\\\'cd' as f5, '\\\\' as f6; -- -- Additional string functions -- SET bytea_output TO escape; SELECT initcap('hi THOMAS'); SELECT lpad('hi', 5, 'xy'); SELECT lpad('hi', 5); SELECT lpad('hi', -5, 'xy'); SELECT lpad('hello', 2); SELECT lpad('hi', 5, ''); SELECT rpad('hi', 5, 'xy'); SELECT rpad('hi', 5); SELECT rpad('hi', -5, 'xy'); SELECT rpad('hello', 2); SELECT rpad('hi', 5, ''); SELECT ltrim('zzzytrim', 'xyz'); SELECT translate('', '14', 'ax'); SELECT translate('12345', '14', 'ax'); SELECT ascii('x'); SELECT ascii(''); SELECT chr(65); SELECT chr(0); SELECT repeat('Pg', 4); SELECT repeat('Pg', -4); SELECT trim(E'\\000'::bytea from E'\\000Tom\\000'::bytea); SELECT btrim(E'\\000trim\\000'::bytea, E'\\000'::bytea); SELECT btrim(''::bytea, E'\\000'::bytea); SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); pgFormatter-4.2/t/pg-test-files/sql/subscription.sql000066400000000000000000000104371361326045100226510ustar00rootroot00000000000000-- -- SUBSCRIPTION -- CREATE ROLE regress_subscription_user LOGIN SUPERUSER; CREATE ROLE regress_subscription_user2; CREATE ROLE regress_subscription_user_dummy LOGIN NOSUPERUSER; SET SESSION AUTHORIZATION 'regress_subscription_user'; -- fail - no publications CREATE SUBSCRIPTION testsub CONNECTION 'foo'; -- fail - no connection CREATE SUBSCRIPTION testsub PUBLICATION foo; -- fail - cannot do CREATE SUBSCRIPTION CREATE SLOT inside transaction block BEGIN; CREATE SUBSCRIPTION testsub CONNECTION 'testconn' PUBLICATION testpub WITH (create_slot); COMMIT; -- fail - invalid connection string CREATE SUBSCRIPTION testsub CONNECTION 'testconn' PUBLICATION testpub; -- fail - duplicate publications CREATE SUBSCRIPTION testsub CONNECTION 'dbname=doesnotexist' PUBLICATION foo, testpub, foo WITH (connect = false); -- ok CREATE SUBSCRIPTION testsub CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH (connect = false); COMMENT ON SUBSCRIPTION testsub IS 'test subscription'; SELECT obj_description(s.oid, 'pg_subscription') FROM pg_subscription s; -- fail - name already exists CREATE SUBSCRIPTION testsub CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH (connect = false); -- fail - must be superuser SET SESSION AUTHORIZATION 'regress_subscription_user2'; CREATE SUBSCRIPTION testsub2 CONNECTION 'dbname=doesnotexist' PUBLICATION foo WITH (connect = false); SET SESSION AUTHORIZATION 'regress_subscription_user'; -- fail - invalid option combinations CREATE SUBSCRIPTION testsub2 CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH (connect = false, copy_data = true); CREATE SUBSCRIPTION testsub2 CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH (connect = false, enabled = true); CREATE SUBSCRIPTION testsub2 CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH (connect = false, create_slot = true); CREATE SUBSCRIPTION testsub2 CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH (slot_name = NONE, enabled = true); CREATE SUBSCRIPTION testsub2 CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH (slot_name = NONE, create_slot = true); CREATE SUBSCRIPTION testsub2 CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH (slot_name = NONE); CREATE SUBSCRIPTION testsub2 CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH (slot_name = NONE, enabled = false); CREATE SUBSCRIPTION testsub2 CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH (slot_name = NONE, create_slot = false); -- ok - with slot_name = NONE CREATE SUBSCRIPTION testsub3 CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH (slot_name = NONE, connect = false); -- fail ALTER SUBSCRIPTION testsub3 ENABLE; ALTER SUBSCRIPTION testsub3 REFRESH PUBLICATION; DROP SUBSCRIPTION testsub3; -- fail - invalid connection string ALTER SUBSCRIPTION testsub CONNECTION 'foobar'; \dRs+ ALTER SUBSCRIPTION testsub SET PUBLICATION testpub2, testpub3 WITH (refresh = false); ALTER SUBSCRIPTION testsub CONNECTION 'dbname=doesnotexist2'; ALTER SUBSCRIPTION testsub SET (slot_name = 'newname'); -- fail ALTER SUBSCRIPTION doesnotexist CONNECTION 'dbname=doesnotexist2'; ALTER SUBSCRIPTION testsub SET (create_slot = false); \dRs+ BEGIN; ALTER SUBSCRIPTION testsub ENABLE; \dRs ALTER SUBSCRIPTION testsub DISABLE; \dRs COMMIT; -- fail - must be owner of subscription SET ROLE regress_subscription_user_dummy; ALTER SUBSCRIPTION testsub RENAME TO testsub_dummy; RESET ROLE; ALTER SUBSCRIPTION testsub RENAME TO testsub_foo; ALTER SUBSCRIPTION testsub_foo SET (synchronous_commit = local); ALTER SUBSCRIPTION testsub_foo SET (synchronous_commit = foobar); \dRs+ -- rename back to keep the rest simple ALTER SUBSCRIPTION testsub_foo RENAME TO testsub; -- fail - new owner must be superuser ALTER SUBSCRIPTION testsub OWNER TO regress_subscription_user2; ALTER ROLE regress_subscription_user2 SUPERUSER; -- now it works ALTER SUBSCRIPTION testsub OWNER TO regress_subscription_user2; -- fail - cannot do DROP SUBSCRIPTION inside transaction block with slot name BEGIN; DROP SUBSCRIPTION testsub; COMMIT; ALTER SUBSCRIPTION testsub SET (slot_name = NONE); -- now it works BEGIN; DROP SUBSCRIPTION testsub; COMMIT; DROP SUBSCRIPTION IF EXISTS testsub; DROP SUBSCRIPTION testsub; -- fail RESET SESSION AUTHORIZATION; DROP ROLE regress_subscription_user; DROP ROLE regress_subscription_user2; DROP ROLE regress_subscription_user_dummy; pgFormatter-4.2/t/pg-test-files/sql/subselect.sql000066400000000000000000000513241361326045100221160ustar00rootroot00000000000000-- -- SUBSELECT -- SELECT 1 AS one WHERE 1 IN (SELECT 1); SELECT 1 AS zero WHERE 1 NOT IN (SELECT 1); SELECT 1 AS zero WHERE 1 IN (SELECT 2); -- Check grammar's handling of extra parens in assorted contexts SELECT * FROM (SELECT 1 AS x) ss; SELECT * FROM ((SELECT 1 AS x)) ss; (SELECT 2) UNION SELECT 2; ((SELECT 2)) UNION SELECT 2; SELECT ((SELECT 2) UNION SELECT 2); SELECT (((SELECT 2)) UNION SELECT 2); SELECT (SELECT ARRAY[1,2,3])[1]; SELECT ((SELECT ARRAY[1,2,3]))[2]; SELECT (((SELECT ARRAY[1,2,3])))[3]; -- Set up some simple test tables CREATE TABLE SUBSELECT_TBL ( f1 integer, f2 integer, f3 float ); INSERT INTO SUBSELECT_TBL VALUES (1, 2, 3); INSERT INTO SUBSELECT_TBL VALUES (2, 3, 4); INSERT INTO SUBSELECT_TBL VALUES (3, 4, 5); INSERT INTO SUBSELECT_TBL VALUES (1, 1, 1); INSERT INTO SUBSELECT_TBL VALUES (2, 2, 2); INSERT INTO SUBSELECT_TBL VALUES (3, 3, 3); INSERT INTO SUBSELECT_TBL VALUES (6, 7, 8); INSERT INTO SUBSELECT_TBL VALUES (8, 9, NULL); SELECT '' AS eight, * FROM SUBSELECT_TBL; -- Uncorrelated subselects SELECT '' AS two, f1 AS "Constant Select" FROM SUBSELECT_TBL WHERE f1 IN (SELECT 1); SELECT '' AS six, f1 AS "Uncorrelated Field" FROM SUBSELECT_TBL WHERE f1 IN (SELECT f2 FROM SUBSELECT_TBL); SELECT '' AS six, f1 AS "Uncorrelated Field" FROM SUBSELECT_TBL WHERE f1 IN (SELECT f2 FROM SUBSELECT_TBL WHERE f2 IN (SELECT f1 FROM SUBSELECT_TBL)); SELECT '' AS three, f1, f2 FROM SUBSELECT_TBL WHERE (f1, f2) NOT IN (SELECT f2, CAST(f3 AS int4) FROM SUBSELECT_TBL WHERE f3 IS NOT NULL); -- Correlated subselects SELECT '' AS six, f1 AS "Correlated Field", f2 AS "Second Field" FROM SUBSELECT_TBL upper WHERE f1 IN (SELECT f2 FROM SUBSELECT_TBL WHERE f1 = upper.f1); SELECT '' AS six, f1 AS "Correlated Field", f3 AS "Second Field" FROM SUBSELECT_TBL upper WHERE f1 IN (SELECT f2 FROM SUBSELECT_TBL WHERE CAST(upper.f2 AS float) = f3); SELECT '' AS six, f1 AS "Correlated Field", f3 AS "Second Field" FROM SUBSELECT_TBL upper WHERE f3 IN (SELECT upper.f1 + f2 FROM SUBSELECT_TBL WHERE f2 = CAST(f3 AS integer)); SELECT '' AS five, f1 AS "Correlated Field" FROM SUBSELECT_TBL WHERE (f1, f2) IN (SELECT f2, CAST(f3 AS int4) FROM SUBSELECT_TBL WHERE f3 IS NOT NULL); -- -- Use some existing tables in the regression test -- SELECT '' AS eight, ss.f1 AS "Correlated Field", ss.f3 AS "Second Field" FROM SUBSELECT_TBL ss WHERE f1 NOT IN (SELECT f1+1 FROM INT4_TBL WHERE f1 != ss.f1 AND f1 < 2147483647); select q1, float8(count(*)) / (select count(*) from int8_tbl) from int8_tbl group by q1 order by q1; -- Unspecified-type literals in output columns should resolve as text SELECT *, pg_typeof(f1) FROM (SELECT 'foo' AS f1 FROM generate_series(1,3)) ss ORDER BY 1; -- ... unless there's context to suggest differently explain (verbose, costs off) select '42' union all select '43'; explain (verbose, costs off) select '42' union all select 43; -- check materialization of an initplan reference (bug #14524) explain (verbose, costs off) select 1 = all (select (select 1)); select 1 = all (select (select 1)); -- -- Check EXISTS simplification with LIMIT -- explain (costs off) select * from int4_tbl o where exists (select 1 from int4_tbl i where i.f1=o.f1 limit null); explain (costs off) select * from int4_tbl o where not exists (select 1 from int4_tbl i where i.f1=o.f1 limit 1); explain (costs off) select * from int4_tbl o where exists (select 1 from int4_tbl i where i.f1=o.f1 limit 0); -- -- Test cases to catch unpleasant interactions between IN-join processing -- and subquery pullup. -- select count(*) from (select 1 from tenk1 a where unique1 IN (select hundred from tenk1 b)) ss; select count(distinct ss.ten) from (select ten from tenk1 a where unique1 IN (select hundred from tenk1 b)) ss; select count(*) from (select 1 from tenk1 a where unique1 IN (select distinct hundred from tenk1 b)) ss; select count(distinct ss.ten) from (select ten from tenk1 a where unique1 IN (select distinct hundred from tenk1 b)) ss; -- -- Test cases to check for overenthusiastic optimization of -- "IN (SELECT DISTINCT ...)" and related cases. Per example from -- Luca Pireddu and Michael Fuhr. -- CREATE TEMP TABLE foo (id integer); CREATE TEMP TABLE bar (id1 integer, id2 integer); INSERT INTO foo VALUES (1); INSERT INTO bar VALUES (1, 1); INSERT INTO bar VALUES (2, 2); INSERT INTO bar VALUES (3, 1); -- These cases require an extra level of distinct-ing above subquery s SELECT * FROM foo WHERE id IN (SELECT id2 FROM (SELECT DISTINCT id1, id2 FROM bar) AS s); SELECT * FROM foo WHERE id IN (SELECT id2 FROM (SELECT id1,id2 FROM bar GROUP BY id1,id2) AS s); SELECT * FROM foo WHERE id IN (SELECT id2 FROM (SELECT id1, id2 FROM bar UNION SELECT id1, id2 FROM bar) AS s); -- These cases do not SELECT * FROM foo WHERE id IN (SELECT id2 FROM (SELECT DISTINCT ON (id2) id1, id2 FROM bar) AS s); SELECT * FROM foo WHERE id IN (SELECT id2 FROM (SELECT id2 FROM bar GROUP BY id2) AS s); SELECT * FROM foo WHERE id IN (SELECT id2 FROM (SELECT id2 FROM bar UNION SELECT id2 FROM bar) AS s); -- -- Test case to catch problems with multiply nested sub-SELECTs not getting -- recalculated properly. Per bug report from Didier Moens. -- CREATE TABLE orderstest ( approver_ref integer, po_ref integer, ordercanceled boolean ); INSERT INTO orderstest VALUES (1, 1, false); INSERT INTO orderstest VALUES (66, 5, false); INSERT INTO orderstest VALUES (66, 6, false); INSERT INTO orderstest VALUES (66, 7, false); INSERT INTO orderstest VALUES (66, 1, true); INSERT INTO orderstest VALUES (66, 8, false); INSERT INTO orderstest VALUES (66, 1, false); INSERT INTO orderstest VALUES (77, 1, false); INSERT INTO orderstest VALUES (1, 1, false); INSERT INTO orderstest VALUES (66, 1, false); INSERT INTO orderstest VALUES (1, 1, false); CREATE VIEW orders_view AS SELECT *, (SELECT CASE WHEN ord.approver_ref=1 THEN '---' ELSE 'Approved' END) AS "Approved", (SELECT CASE WHEN ord.ordercanceled THEN 'Canceled' ELSE (SELECT CASE WHEN ord.po_ref=1 THEN (SELECT CASE WHEN ord.approver_ref=1 THEN '---' ELSE 'Approved' END) ELSE 'PO' END) END) AS "Status", (CASE WHEN ord.ordercanceled THEN 'Canceled' ELSE (CASE WHEN ord.po_ref=1 THEN (CASE WHEN ord.approver_ref=1 THEN '---' ELSE 'Approved' END) ELSE 'PO' END) END) AS "Status_OK" FROM orderstest ord; SELECT * FROM orders_view; DROP TABLE orderstest cascade; -- -- Test cases to catch situations where rule rewriter fails to propagate -- hasSubLinks flag correctly. Per example from Kyle Bateman. -- create temp table parts ( partnum text, cost float8 ); create temp table shipped ( ttype char(2), ordnum int4, partnum text, value float8 ); create temp view shipped_view as select * from shipped where ttype = 'wt'; create rule shipped_view_insert as on insert to shipped_view do instead insert into shipped values('wt', new.ordnum, new.partnum, new.value); insert into parts (partnum, cost) values (1, 1234.56); insert into shipped_view (ordnum, partnum, value) values (0, 1, (select cost from parts where partnum = '1')); select * from shipped_view; create rule shipped_view_update as on update to shipped_view do instead update shipped set partnum = new.partnum, value = new.value where ttype = new.ttype and ordnum = new.ordnum; update shipped_view set value = 11 from int4_tbl a join int4_tbl b on (a.f1 = (select f1 from int4_tbl c where c.f1=b.f1)) where ordnum = a.f1; select * from shipped_view; select f1, ss1 as relabel from (select *, (select sum(f1) from int4_tbl b where f1 >= a.f1) as ss1 from int4_tbl a) ss; -- -- Test cases involving PARAM_EXEC parameters and min/max index optimizations. -- Per bug report from David Sanchez i Gregori. -- select * from ( select max(unique1) from tenk1 as a where exists (select 1 from tenk1 as b where b.thousand = a.unique2) ) ss; select * from ( select min(unique1) from tenk1 as a where not exists (select 1 from tenk1 as b where b.unique2 = 10000) ) ss; -- -- Test that an IN implemented using a UniquePath does unique-ification -- with the right semantics, as per bug #4113. (Unfortunately we have -- no simple way to ensure that this test case actually chooses that type -- of plan, but it does in releases 7.4-8.3. Note that an ordering difference -- here might mean that some other plan type is being used, rendering the test -- pointless.) -- create temp table numeric_table (num_col numeric); insert into numeric_table values (1), (1.000000000000000000001), (2), (3); create temp table float_table (float_col float8); insert into float_table values (1), (2), (3); select * from float_table where float_col in (select num_col from numeric_table); select * from numeric_table where num_col in (select float_col from float_table); -- -- Test case for bug #4290: bogus calculation of subplan param sets -- create temp table ta (id int primary key, val int); insert into ta values(1,1); insert into ta values(2,2); create temp table tb (id int primary key, aval int); insert into tb values(1,1); insert into tb values(2,1); insert into tb values(3,2); insert into tb values(4,2); create temp table tc (id int primary key, aid int); insert into tc values(1,1); insert into tc values(2,2); select ( select min(tb.id) from tb where tb.aval = (select ta.val from ta where ta.id = tc.aid) ) as min_tb_id from tc; -- -- Test case for 8.3 "failed to locate grouping columns" bug -- create temp table t1 (f1 numeric(14,0), f2 varchar(30)); select * from (select distinct f1, f2, (select f2 from t1 x where x.f1 = up.f1) as fs from t1 up) ss group by f1,f2,fs; -- -- Test case for bug #5514 (mishandling of whole-row Vars in subselects) -- create temp table table_a(id integer); insert into table_a values (42); create temp view view_a as select * from table_a; select view_a from view_a; select (select view_a) from view_a; select (select (select view_a)) from view_a; select (select (a.*)::text) from view_a a; -- -- Check that whole-row Vars reading the result of a subselect don't include -- any junk columns therein -- select q from (select max(f1) from int4_tbl group by f1 order by f1) q; with q as (select max(f1) from int4_tbl group by f1 order by f1) select q from q; -- -- Test case for sublinks pulled up into joinaliasvars lists in an -- inherited update/delete query -- begin; -- this shouldn't delete anything, but be safe delete from road where exists ( select 1 from int4_tbl cross join ( select f1, array(select q1 from int8_tbl) as arr from text_tbl ) ss where road.name = ss.f1 ); rollback; -- -- Test case for sublinks pushed down into subselects via join alias expansion -- select (select sq1) as qq1 from (select exists(select 1 from int4_tbl where f1 = q2) as sq1, 42 as dummy from int8_tbl) sq0 join int4_tbl i4 on dummy = i4.f1; -- -- Test case for subselect within UPDATE of INSERT...ON CONFLICT DO UPDATE -- create temp table upsert(key int4 primary key, val text); insert into upsert values(1, 'val') on conflict (key) do update set val = 'not seen'; insert into upsert values(1, 'val') on conflict (key) do update set val = 'seen with subselect ' || (select f1 from int4_tbl where f1 != 0 limit 1)::text; select * from upsert; with aa as (select 'int4_tbl' u from int4_tbl limit 1) insert into upsert values (1, 'x'), (999, 'y') on conflict (key) do update set val = (select u from aa) returning *; -- -- Test case for cross-type partial matching in hashed subplan (bug #7597) -- create temp table outer_7597 (f1 int4, f2 int4); insert into outer_7597 values (0, 0); insert into outer_7597 values (1, 0); insert into outer_7597 values (0, null); insert into outer_7597 values (1, null); create temp table inner_7597(c1 int8, c2 int8); insert into inner_7597 values(0, null); select * from outer_7597 where (f1, f2) not in (select * from inner_7597); -- -- Similar test case using text that verifies that collation -- information is passed through by execTuplesEqual() in nodeSubplan.c -- (otherwise it would error in texteq()) -- create temp table outer_text (f1 text, f2 text); insert into outer_text values ('a', 'a'); insert into outer_text values ('b', 'a'); insert into outer_text values ('a', null); insert into outer_text values ('b', null); create temp table inner_text (c1 text, c2 text); insert into inner_text values ('a', null); select * from outer_text where (f1, f2) not in (select * from inner_text); -- -- Test case for premature memory release during hashing of subplan output -- select '1'::text in (select '1'::name union all select '1'::name); -- -- Test case for planner bug with nested EXISTS handling -- select a.thousand from tenk1 a, tenk1 b where a.thousand = b.thousand and exists ( select 1 from tenk1 c where b.hundred = c.hundred and not exists ( select 1 from tenk1 d where a.thousand = d.thousand ) ); -- -- Check that nested sub-selects are not pulled up if they contain volatiles -- explain (verbose, costs off) select x, x from (select (select now()) as x from (values(1),(2)) v(y)) ss; explain (verbose, costs off) select x, x from (select (select random()) as x from (values(1),(2)) v(y)) ss; explain (verbose, costs off) select x, x from (select (select now() where y=y) as x from (values(1),(2)) v(y)) ss; explain (verbose, costs off) select x, x from (select (select random() where y=y) as x from (values(1),(2)) v(y)) ss; -- -- Check we don't misoptimize a NOT IN where the subquery returns no rows. -- create temp table notinouter (a int); create temp table notininner (b int not null); insert into notinouter values (null), (1); select * from notinouter where a not in (select b from notininner); -- -- Check we behave sanely in corner case of empty SELECT list (bug #8648) -- create temp table nocolumns(); select exists(select * from nocolumns); -- -- Check behavior with a SubPlan in VALUES (bug #14924) -- select val.x from generate_series(1,10) as s(i), lateral ( values ((select s.i + 1)), (s.i + 101) ) as val(x) where s.i < 10 and (select val.x) < 110; -- -- Check sane behavior with nested IN SubLinks -- explain (verbose, costs off) select * from int4_tbl where (case when f1 in (select unique1 from tenk1 a) then f1 else null end) in (select ten from tenk1 b); select * from int4_tbl where (case when f1 in (select unique1 from tenk1 a) then f1 else null end) in (select ten from tenk1 b); -- -- Check for incorrect optimization when IN subquery contains a SRF -- explain (verbose, costs off) select * from int4_tbl o where (f1, f1) in (select f1, generate_series(1,50) / 10 g from int4_tbl i group by f1); select * from int4_tbl o where (f1, f1) in (select f1, generate_series(1,50) / 10 g from int4_tbl i group by f1); -- -- check for over-optimization of whole-row Var referencing an Append plan -- select (select q from (select 1,2,3 where f1 > 0 union all select 4,5,6.0 where f1 <= 0 ) q ) from int4_tbl; -- -- Check that volatile quals aren't pushed down past a DISTINCT: -- nextval() should not be called more than the nominal number of times -- create temp sequence ts1; select * from (select distinct ten from tenk1) ss where ten < 10 + nextval('ts1') order by 1; select nextval('ts1'); -- -- Check that volatile quals aren't pushed down past a set-returning function; -- while a nonvolatile qual can be, if it doesn't reference the SRF. -- create function tattle(x int, y int) returns bool volatile language plpgsql as $$ begin raise notice 'x = %, y = %', x, y; return x > y; end$$; explain (verbose, costs off) select * from (select 9 as x, unnest(array[1,2,3,11,12,13]) as u) ss where tattle(x, 8); select * from (select 9 as x, unnest(array[1,2,3,11,12,13]) as u) ss where tattle(x, 8); -- if we pretend it's stable, we get different results: alter function tattle(x int, y int) stable; explain (verbose, costs off) select * from (select 9 as x, unnest(array[1,2,3,11,12,13]) as u) ss where tattle(x, 8); select * from (select 9 as x, unnest(array[1,2,3,11,12,13]) as u) ss where tattle(x, 8); -- although even a stable qual should not be pushed down if it references SRF explain (verbose, costs off) select * from (select 9 as x, unnest(array[1,2,3,11,12,13]) as u) ss where tattle(x, u); select * from (select 9 as x, unnest(array[1,2,3,11,12,13]) as u) ss where tattle(x, u); drop function tattle(x int, y int); -- -- Test that LIMIT can be pushed to SORT through a subquery that just projects -- columns. We check for that having happened by looking to see if EXPLAIN -- ANALYZE shows that a top-N sort was used. We must suppress or filter away -- all the non-invariant parts of the EXPLAIN ANALYZE output. -- create table sq_limit (pk int primary key, c1 int, c2 int); insert into sq_limit values (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 1, 1), (6, 2, 2), (7, 3, 3), (8, 4, 4); create function explain_sq_limit() returns setof text language plpgsql as $$ declare ln text; begin for ln in explain (analyze, summary off, timing off, costs off) select * from (select pk,c2 from sq_limit order by c1,pk) as x limit 3 loop ln := regexp_replace(ln, 'Memory: \S*', 'Memory: xxx'); -- this case might occur if force_parallel_mode is on: ln := regexp_replace(ln, 'Worker 0: Sort Method', 'Sort Method'); return next ln; end loop; end; $$; select * from explain_sq_limit(); select * from (select pk,c2 from sq_limit order by c1,pk) as x limit 3; drop function explain_sq_limit(); drop table sq_limit; -- -- Ensure that backward scan direction isn't propagated into -- expression subqueries (bug #15336) -- begin; declare c1 scroll cursor for select * from generate_series(1,4) i where i <> all (values (2),(3)); move forward all in c1; fetch backward all in c1; commit; -- -- Tests for CTE inlining behavior -- -- Basic subquery that can be inlined explain (verbose, costs off) with x as (select * from (select f1 from subselect_tbl) ss) select * from x where f1 = 1; -- Explicitly request materialization explain (verbose, costs off) with x as materialized (select * from (select f1 from subselect_tbl) ss) select * from x where f1 = 1; -- Stable functions are safe to inline explain (verbose, costs off) with x as (select * from (select f1, now() from subselect_tbl) ss) select * from x where f1 = 1; -- Volatile functions prevent inlining explain (verbose, costs off) with x as (select * from (select f1, random() from subselect_tbl) ss) select * from x where f1 = 1; -- SELECT FOR UPDATE cannot be inlined explain (verbose, costs off) with x as (select * from (select f1 from subselect_tbl for update) ss) select * from x where f1 = 1; -- Multiply-referenced CTEs are inlined only when requested explain (verbose, costs off) with x as (select * from (select f1, now() as n from subselect_tbl) ss) select * from x, x x2 where x.n = x2.n; explain (verbose, costs off) with x as not materialized (select * from (select f1, now() as n from subselect_tbl) ss) select * from x, x x2 where x.n = x2.n; -- Multiply-referenced CTEs can't be inlined if they contain outer self-refs explain (verbose, costs off) with recursive x(a) as ((values ('a'), ('b')) union all (with z as not materialized (select * from x) select z.a || z1.a as a from z cross join z as z1 where length(z.a || z1.a) < 5)) select * from x; with recursive x(a) as ((values ('a'), ('b')) union all (with z as not materialized (select * from x) select z.a || z1.a as a from z cross join z as z1 where length(z.a || z1.a) < 5)) select * from x; explain (verbose, costs off) with recursive x(a) as ((values ('a'), ('b')) union all (with z as not materialized (select * from x) select z.a || z.a as a from z where length(z.a || z.a) < 5)) select * from x; with recursive x(a) as ((values ('a'), ('b')) union all (with z as not materialized (select * from x) select z.a || z.a as a from z where length(z.a || z.a) < 5)) select * from x; -- Check handling of outer references explain (verbose, costs off) with x as (select * from int4_tbl) select * from (with y as (select * from x) select * from y) ss; explain (verbose, costs off) with x as materialized (select * from int4_tbl) select * from (with y as (select * from x) select * from y) ss; -- Ensure that we inline the currect CTE when there are -- multiple CTEs with the same name explain (verbose, costs off) with x as (select 1 as y) select * from (with x as (select 2 as y) select * from x) ss; -- Row marks are not pushed into CTEs explain (verbose, costs off) with x as (select * from subselect_tbl) select * from x for update; pgFormatter-4.2/t/pg-test-files/sql/sysviews.sql000066400000000000000000000042411361326045100220150ustar00rootroot00000000000000-- -- Test assorted system views -- -- This test is mainly meant to provide some code coverage for the -- set-returning functions that underlie certain system views. -- The output of most of these functions is very environment-dependent, -- so our ability to test with fixed expected output is pretty limited; -- but even a trivial check of count(*) will exercise the normal code path -- through the SRF. select count(*) >= 0 as ok from pg_available_extension_versions; select count(*) >= 0 as ok from pg_available_extensions; -- At introduction, pg_config had 23 entries; it may grow select count(*) > 20 as ok from pg_config; -- We expect no cursors in this test; see also portals.sql select count(*) = 0 as ok from pg_cursors; select count(*) >= 0 as ok from pg_file_settings; -- There will surely be at least one rule select count(*) > 0 as ok from pg_hba_file_rules; -- There will surely be at least one active lock select count(*) > 0 as ok from pg_locks; -- We expect no prepared statements in this test; see also prepare.sql select count(*) = 0 as ok from pg_prepared_statements; -- See also prepared_xacts.sql select count(*) >= 0 as ok from pg_prepared_xacts; -- This is to record the prevailing planner enable_foo settings during -- a regression test run. select name, setting from pg_settings where name like 'enable%'; -- Test that the pg_timezone_names and pg_timezone_abbrevs views are -- more-or-less working. We can't test their contents in any great detail -- without the outputs changing anytime IANA updates the underlying data, -- but it seems reasonable to expect at least one entry per major meridian. -- (At the time of writing, the actual counts are around 38 because of -- zones using fractional GMT offsets, so this is a pretty loose test.) select count(distinct utc_offset) >= 24 as ok from pg_timezone_names; select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs; -- Let's check the non-default timezone abbreviation sets, too set timezone_abbreviations = 'Australia'; select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs; set timezone_abbreviations = 'India'; select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs; pgFormatter-4.2/t/pg-test-files/sql/tablesample.sql000066400000000000000000000102451361326045100224130ustar00rootroot00000000000000CREATE TABLE test_tablesample (id int, name text) WITH (fillfactor=10); -- use fillfactor so we don't have to load too much data to get multiple pages INSERT INTO test_tablesample SELECT i, repeat(i::text, 200) FROM generate_series(0, 9) s(i); SELECT t.id FROM test_tablesample AS t TABLESAMPLE SYSTEM (50) REPEATABLE (0); SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (100.0/11) REPEATABLE (0); SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0); SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (50) REPEATABLE (0); SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (5.5) REPEATABLE (0); -- 100% should give repeatable count results (ie, all rows) in any case SELECT count(*) FROM test_tablesample TABLESAMPLE SYSTEM (100); SELECT count(*) FROM test_tablesample TABLESAMPLE SYSTEM (100) REPEATABLE (1+2); SELECT count(*) FROM test_tablesample TABLESAMPLE SYSTEM (100) REPEATABLE (0.4); CREATE VIEW test_tablesample_v1 AS SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (10*2) REPEATABLE (2); CREATE VIEW test_tablesample_v2 AS SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (99); \d+ test_tablesample_v1 \d+ test_tablesample_v2 -- check a sampled query doesn't affect cursor in progress BEGIN; DECLARE tablesample_cur CURSOR FOR SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0); FETCH FIRST FROM tablesample_cur; FETCH NEXT FROM tablesample_cur; FETCH NEXT FROM tablesample_cur; SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0); FETCH NEXT FROM tablesample_cur; FETCH NEXT FROM tablesample_cur; FETCH NEXT FROM tablesample_cur; FETCH FIRST FROM tablesample_cur; FETCH NEXT FROM tablesample_cur; FETCH NEXT FROM tablesample_cur; FETCH NEXT FROM tablesample_cur; FETCH NEXT FROM tablesample_cur; FETCH NEXT FROM tablesample_cur; CLOSE tablesample_cur; END; EXPLAIN (COSTS OFF) SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (2); EXPLAIN (COSTS OFF) SELECT * FROM test_tablesample_v1; -- check inheritance behavior explain (costs off) select count(*) from person tablesample bernoulli (100); select count(*) from person tablesample bernoulli (100); select count(*) from person; -- check that collations get assigned within the tablesample arguments SELECT count(*) FROM test_tablesample TABLESAMPLE bernoulli (('1'::text < '0'::text)::int); -- check behavior during rescans, as well as correct handling of min/max pct select * from (values (0),(100)) v(pct), lateral (select count(*) from tenk1 tablesample bernoulli (pct)) ss; select * from (values (0),(100)) v(pct), lateral (select count(*) from tenk1 tablesample system (pct)) ss; explain (costs off) select pct, count(unique1) from (values (0),(100)) v(pct), lateral (select * from tenk1 tablesample bernoulli (pct)) ss group by pct; select pct, count(unique1) from (values (0),(100)) v(pct), lateral (select * from tenk1 tablesample bernoulli (pct)) ss group by pct; select pct, count(unique1) from (values (0),(100)) v(pct), lateral (select * from tenk1 tablesample system (pct)) ss group by pct; -- errors SELECT id FROM test_tablesample TABLESAMPLE FOOBAR (1); SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (NULL); SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (NULL); SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (-1); SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (200); SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (-1); SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (200); SELECT id FROM test_tablesample_v1 TABLESAMPLE BERNOULLI (1); INSERT INTO test_tablesample_v1 VALUES(1); WITH query_select AS (SELECT * FROM test_tablesample) SELECT * FROM query_select TABLESAMPLE BERNOULLI (5.5) REPEATABLE (1); SELECT q.* FROM (SELECT * FROM test_tablesample) as q TABLESAMPLE BERNOULLI (5); -- check partitioned tables support tablesample create table parted_sample (a int) partition by list (a); create table parted_sample_1 partition of parted_sample for values in (1); create table parted_sample_2 partition of parted_sample for values in (2); explain (costs off) select * from parted_sample tablesample bernoulli (100); drop table parted_sample, parted_sample_1, parted_sample_2; pgFormatter-4.2/t/pg-test-files/sql/temp.sql000066400000000000000000000167761361326045100211060ustar00rootroot00000000000000-- -- TEMP -- Test temp relations and indexes -- -- test temp table/index masking CREATE TABLE temptest(col int); CREATE INDEX i_temptest ON temptest(col); CREATE TEMP TABLE temptest(tcol int); CREATE INDEX i_temptest ON temptest(tcol); SELECT * FROM temptest; DROP INDEX i_temptest; DROP TABLE temptest; SELECT * FROM temptest; DROP INDEX i_temptest; DROP TABLE temptest; -- test temp table selects CREATE TABLE temptest(col int); INSERT INTO temptest VALUES (1); CREATE TEMP TABLE temptest(tcol float); INSERT INTO temptest VALUES (2.1); SELECT * FROM temptest; DROP TABLE temptest; SELECT * FROM temptest; DROP TABLE temptest; -- test temp table deletion CREATE TEMP TABLE temptest(col int); \c SELECT * FROM temptest; -- Test ON COMMIT DELETE ROWS CREATE TEMP TABLE temptest(col int) ON COMMIT DELETE ROWS; BEGIN; INSERT INTO temptest VALUES (1); INSERT INTO temptest VALUES (2); SELECT * FROM temptest; COMMIT; SELECT * FROM temptest; DROP TABLE temptest; BEGIN; CREATE TEMP TABLE temptest(col) ON COMMIT DELETE ROWS AS SELECT 1; SELECT * FROM temptest; COMMIT; SELECT * FROM temptest; DROP TABLE temptest; -- Test ON COMMIT DROP BEGIN; CREATE TEMP TABLE temptest(col int) ON COMMIT DROP; INSERT INTO temptest VALUES (1); INSERT INTO temptest VALUES (2); SELECT * FROM temptest; COMMIT; SELECT * FROM temptest; BEGIN; CREATE TEMP TABLE temptest(col) ON COMMIT DROP AS SELECT 1; SELECT * FROM temptest; COMMIT; SELECT * FROM temptest; -- ON COMMIT is only allowed for TEMP CREATE TABLE temptest(col int) ON COMMIT DELETE ROWS; CREATE TABLE temptest(col) ON COMMIT DELETE ROWS AS SELECT 1; -- Test foreign keys BEGIN; CREATE TEMP TABLE temptest1(col int PRIMARY KEY); CREATE TEMP TABLE temptest2(col int REFERENCES temptest1) ON COMMIT DELETE ROWS; INSERT INTO temptest1 VALUES (1); INSERT INTO temptest2 VALUES (1); COMMIT; SELECT * FROM temptest1; SELECT * FROM temptest2; BEGIN; CREATE TEMP TABLE temptest3(col int PRIMARY KEY) ON COMMIT DELETE ROWS; CREATE TEMP TABLE temptest4(col int REFERENCES temptest3); COMMIT; -- Test manipulation of temp schema's placement in search path create table public.whereami (f1 text); insert into public.whereami values ('public'); create temp table whereami (f1 text); insert into whereami values ('temp'); create function public.whoami() returns text as $$select 'public'::text$$ language sql; create function pg_temp.whoami() returns text as $$select 'temp'::text$$ language sql; -- default should have pg_temp implicitly first, but only for tables select * from whereami; select whoami(); -- can list temp first explicitly, but it still doesn't affect functions set search_path = pg_temp, public; select * from whereami; select whoami(); -- or put it last for security set search_path = public, pg_temp; select * from whereami; select whoami(); -- you can invoke a temp function explicitly, though select pg_temp.whoami(); drop table public.whereami; -- For partitioned temp tables, ON COMMIT actions ignore storage-less -- partitioned tables. begin; create temp table temp_parted_oncommit (a int) partition by list (a) on commit delete rows; create temp table temp_parted_oncommit_1 partition of temp_parted_oncommit for values in (1) on commit delete rows; insert into temp_parted_oncommit values (1); commit; -- partitions are emptied by the previous commit select * from temp_parted_oncommit; drop table temp_parted_oncommit; -- Check dependencies between ON COMMIT actions with a partitioned -- table and its partitions. Using ON COMMIT DROP on a parent removes -- the whole set. begin; create temp table temp_parted_oncommit_test (a int) partition by list (a) on commit drop; create temp table temp_parted_oncommit_test1 partition of temp_parted_oncommit_test for values in (1) on commit delete rows; create temp table temp_parted_oncommit_test2 partition of temp_parted_oncommit_test for values in (2) on commit drop; insert into temp_parted_oncommit_test values (1), (2); commit; -- no relations remain in this case. select relname from pg_class where relname like 'temp_parted_oncommit_test%'; -- Using ON COMMIT DELETE on a partitioned table does not remove -- all rows if partitions preserve their data. begin; create temp table temp_parted_oncommit_test (a int) partition by list (a) on commit delete rows; create temp table temp_parted_oncommit_test1 partition of temp_parted_oncommit_test for values in (1) on commit preserve rows; create temp table temp_parted_oncommit_test2 partition of temp_parted_oncommit_test for values in (2) on commit drop; insert into temp_parted_oncommit_test values (1), (2); commit; -- Data from the remaining partition is still here as its rows are -- preserved. select * from temp_parted_oncommit_test; -- two relations remain in this case. select relname from pg_class where relname like 'temp_parted_oncommit_test%'; drop table temp_parted_oncommit_test; -- Check dependencies between ON COMMIT actions with inheritance trees. -- Using ON COMMIT DROP on a parent removes the whole set. begin; create temp table temp_inh_oncommit_test (a int) on commit drop; create temp table temp_inh_oncommit_test1 () inherits(temp_inh_oncommit_test) on commit delete rows; insert into temp_inh_oncommit_test1 values (1); commit; -- no relations remain in this case select relname from pg_class where relname like 'temp_inh_oncommit_test%'; -- Data on the parent is removed, and the child goes away. begin; create temp table temp_inh_oncommit_test (a int) on commit delete rows; create temp table temp_inh_oncommit_test1 () inherits(temp_inh_oncommit_test) on commit drop; insert into temp_inh_oncommit_test1 values (1); insert into temp_inh_oncommit_test values (1); commit; select * from temp_inh_oncommit_test; -- one relation remains select relname from pg_class where relname like 'temp_inh_oncommit_test%'; drop table temp_inh_oncommit_test; -- Tests with two-phase commit -- Transactions creating objects in a temporary namespace cannot be used -- with two-phase commit. -- These cases generate errors about temporary namespace. -- Function creation begin; create function pg_temp.twophase_func() returns void as $$ select '2pc_func'::text $$ language sql; prepare transaction 'twophase_func'; -- Function drop create function pg_temp.twophase_func() returns void as $$ select '2pc_func'::text $$ language sql; begin; drop function pg_temp.twophase_func(); prepare transaction 'twophase_func'; -- Operator creation begin; create operator pg_temp.@@ (leftarg = int4, rightarg = int4, procedure = int4mi); prepare transaction 'twophase_operator'; -- These generate errors about temporary tables. begin; create type pg_temp.twophase_type as (a int); prepare transaction 'twophase_type'; begin; create view pg_temp.twophase_view as select 1; prepare transaction 'twophase_view'; begin; create sequence pg_temp.twophase_seq; prepare transaction 'twophase_sequence'; -- Temporary tables cannot be used with two-phase commit. create temp table twophase_tab (a int); begin; select a from twophase_tab; prepare transaction 'twophase_tab'; begin; insert into twophase_tab values (1); prepare transaction 'twophase_tab'; begin; lock twophase_tab in access exclusive mode; prepare transaction 'twophase_tab'; begin; drop table twophase_tab; prepare transaction 'twophase_tab'; -- Corner case: current_schema may create a temporary schema if namespace -- creation is pending, so check after that. First reset the connection -- to remove the temporary namespace. \c - SET search_path TO 'pg_temp'; BEGIN; SELECT current_schema() ~ 'pg_temp' AS is_temp_schema; PREPARE TRANSACTION 'twophase_search'; pgFormatter-4.2/t/pg-test-files/sql/text.sql000066400000000000000000000101611361326045100211030ustar00rootroot00000000000000-- -- TEXT -- SELECT text 'this is a text string' = text 'this is a text string' AS true; SELECT text 'this is a text string' = text 'this is a text strin' AS false; CREATE TABLE TEXT_TBL (f1 text); INSERT INTO TEXT_TBL VALUES ('doh!'); INSERT INTO TEXT_TBL VALUES ('hi de ho neighbor'); SELECT '' AS two, * FROM TEXT_TBL; -- As of 8.3 we have removed most implicit casts to text, so that for example -- this no longer works: select length(42); -- But as a special exception for usability's sake, we still allow implicit -- casting to text in concatenations, so long as the other input is text or -- an unknown literal. So these work: select 'four: '::text || 2+2; select 'four: ' || 2+2; -- but not this: select 3 || 4.0; /* * various string functions */ select concat('one'); select concat(1,2,3,'hello',true, false, to_date('20100309','YYYYMMDD')); select concat_ws('#','one'); select concat_ws('#',1,2,3,'hello',true, false, to_date('20100309','YYYYMMDD')); select concat_ws(',',10,20,null,30); select concat_ws('',10,20,null,30); select concat_ws(NULL,10,20,null,30) is null; select reverse('abcde'); select i, left('ahoj', i), right('ahoj', i) from generate_series(-5, 5) t(i) order by i; select quote_literal(''); select quote_literal('abc'''); select quote_literal(e'\\'); -- check variadic labeled argument select concat(variadic array[1,2,3]); select concat_ws(',', variadic array[1,2,3]); select concat_ws(',', variadic NULL::int[]); select concat(variadic NULL::int[]) is NULL; select concat(variadic '{}'::int[]) = ''; --should fail select concat_ws(',', variadic 10); /* * format */ select format(NULL); select format('Hello'); select format('Hello %s', 'World'); select format('Hello %%'); select format('Hello %%%%'); -- should fail select format('Hello %s %s', 'World'); select format('Hello %s'); select format('Hello %x', 20); -- check literal and sql identifiers select format('INSERT INTO %I VALUES(%L,%L)', 'mytab', 10, 'Hello'); select format('%s%s%s','Hello', NULL,'World'); select format('INSERT INTO %I VALUES(%L,%L)', 'mytab', 10, NULL); select format('INSERT INTO %I VALUES(%L,%L)', 'mytab', NULL, 'Hello'); -- should fail, sql identifier cannot be NULL select format('INSERT INTO %I VALUES(%L,%L)', NULL, 10, 'Hello'); -- check positional placeholders select format('%1$s %3$s', 1, 2, 3); select format('%1$s %12$s', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); -- should fail select format('%1$s %4$s', 1, 2, 3); select format('%1$s %13$s', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); select format('%0$s', 'Hello'); select format('%*0$s', 'Hello'); select format('%1$', 1); select format('%1$1', 1); -- check mix of positional and ordered placeholders select format('Hello %s %1$s %s', 'World', 'Hello again'); select format('Hello %s %s, %2$s %2$s', 'World', 'Hello again'); -- check variadic labeled arguments select format('%s, %s', variadic array['Hello','World']); select format('%s, %s', variadic array[1, 2]); select format('%s, %s', variadic array[true, false]); select format('%s, %s', variadic array[true, false]::text[]); -- check variadic with positional placeholders select format('%2$s, %1$s', variadic array['first', 'second']); select format('%2$s, %1$s', variadic array[1, 2]); -- variadic argument can be array type NULL, but should not be referenced select format('Hello', variadic NULL::int[]); -- variadic argument allows simulating more than FUNC_MAX_ARGS parameters select format(string_agg('%s',','), variadic array_agg(i)) from generate_series(1,200) g(i); -- check field widths and left, right alignment select format('>>%10s<<', 'Hello'); select format('>>%10s<<', NULL); select format('>>%10s<<', ''); select format('>>%-10s<<', ''); select format('>>%-10s<<', 'Hello'); select format('>>%-10s<<', NULL); select format('>>%1$10s<<', 'Hello'); select format('>>%1$-10I<<', 'Hello'); select format('>>%2$*1$L<<', 10, 'Hello'); select format('>>%2$*1$L<<', 10, NULL); select format('>>%2$*1$L<<', -10, NULL); select format('>>%*s<<', 10, 'Hello'); select format('>>%*1$s<<', 10, 'Hello'); select format('>>%-s<<', 'Hello'); select format('>>%10L<<', NULL); select format('>>%2$*1$L<<', NULL, 'Hello'); select format('>>%2$*1$L<<', 0, 'Hello'); pgFormatter-4.2/t/pg-test-files/sql/tidscan.sql000066400000000000000000000064701361326045100215540ustar00rootroot00000000000000-- tests for tidscans CREATE TABLE tidscan(id integer); -- only insert a few rows, we don't want to spill onto a second table page INSERT INTO tidscan VALUES (1), (2), (3); -- show ctids SELECT ctid, * FROM tidscan; -- ctid equality - implemented as tidscan EXPLAIN (COSTS OFF) SELECT ctid, * FROM tidscan WHERE ctid = '(0,1)'; SELECT ctid, * FROM tidscan WHERE ctid = '(0,1)'; EXPLAIN (COSTS OFF) SELECT ctid, * FROM tidscan WHERE '(0,1)' = ctid; SELECT ctid, * FROM tidscan WHERE '(0,1)' = ctid; -- OR'd clauses EXPLAIN (COSTS OFF) SELECT ctid, * FROM tidscan WHERE ctid = '(0,2)' OR '(0,1)' = ctid; SELECT ctid, * FROM tidscan WHERE ctid = '(0,2)' OR '(0,1)' = ctid; -- ctid = ScalarArrayOp - implemented as tidscan EXPLAIN (COSTS OFF) SELECT ctid, * FROM tidscan WHERE ctid = ANY(ARRAY['(0,1)', '(0,2)']::tid[]); SELECT ctid, * FROM tidscan WHERE ctid = ANY(ARRAY['(0,1)', '(0,2)']::tid[]); -- ctid != ScalarArrayOp - can't be implemented as tidscan EXPLAIN (COSTS OFF) SELECT ctid, * FROM tidscan WHERE ctid != ANY(ARRAY['(0,1)', '(0,2)']::tid[]); SELECT ctid, * FROM tidscan WHERE ctid != ANY(ARRAY['(0,1)', '(0,2)']::tid[]); -- tid equality extracted from sub-AND clauses EXPLAIN (COSTS OFF) SELECT ctid, * FROM tidscan WHERE (id = 3 AND ctid IN ('(0,2)', '(0,3)')) OR (ctid = '(0,1)' AND id = 1); SELECT ctid, * FROM tidscan WHERE (id = 3 AND ctid IN ('(0,2)', '(0,3)')) OR (ctid = '(0,1)' AND id = 1); -- nestloop-with-inner-tidscan joins on tid SET enable_hashjoin TO off; -- otherwise hash join might win EXPLAIN (COSTS OFF) SELECT t1.ctid, t1.*, t2.ctid, t2.* FROM tidscan t1 JOIN tidscan t2 ON t1.ctid = t2.ctid WHERE t1.id = 1; SELECT t1.ctid, t1.*, t2.ctid, t2.* FROM tidscan t1 JOIN tidscan t2 ON t1.ctid = t2.ctid WHERE t1.id = 1; EXPLAIN (COSTS OFF) SELECT t1.ctid, t1.*, t2.ctid, t2.* FROM tidscan t1 LEFT JOIN tidscan t2 ON t1.ctid = t2.ctid WHERE t1.id = 1; SELECT t1.ctid, t1.*, t2.ctid, t2.* FROM tidscan t1 LEFT JOIN tidscan t2 ON t1.ctid = t2.ctid WHERE t1.id = 1; RESET enable_hashjoin; -- exercise backward scan and rewind BEGIN; DECLARE c CURSOR FOR SELECT ctid, * FROM tidscan WHERE ctid = ANY(ARRAY['(0,1)', '(0,2)']::tid[]); FETCH ALL FROM c; FETCH BACKWARD 1 FROM c; FETCH FIRST FROM c; ROLLBACK; -- tidscan via CURRENT OF BEGIN; DECLARE c CURSOR FOR SELECT ctid, * FROM tidscan; FETCH NEXT FROM c; -- skip one row FETCH NEXT FROM c; -- perform update EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) UPDATE tidscan SET id = -id WHERE CURRENT OF c RETURNING *; FETCH NEXT FROM c; -- perform update EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) UPDATE tidscan SET id = -id WHERE CURRENT OF c RETURNING *; SELECT * FROM tidscan; -- position cursor past any rows FETCH NEXT FROM c; -- should error out EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) UPDATE tidscan SET id = -id WHERE CURRENT OF c RETURNING *; ROLLBACK; -- bulk joins on CTID -- (these plans don't use TID scans, but this still seems like an -- appropriate place for these tests) EXPLAIN (COSTS OFF) SELECT count(*) FROM tenk1 t1 JOIN tenk1 t2 ON t1.ctid = t2.ctid; SELECT count(*) FROM tenk1 t1 JOIN tenk1 t2 ON t1.ctid = t2.ctid; SET enable_hashjoin TO off; EXPLAIN (COSTS OFF) SELECT count(*) FROM tenk1 t1 JOIN tenk1 t2 ON t1.ctid = t2.ctid; SELECT count(*) FROM tenk1 t1 JOIN tenk1 t2 ON t1.ctid = t2.ctid; RESET enable_hashjoin; DROP TABLE tidscan; pgFormatter-4.2/t/pg-test-files/sql/time.sql000066400000000000000000000024421361326045100210600ustar00rootroot00000000000000-- -- TIME -- CREATE TABLE TIME_TBL (f1 time(2)); INSERT INTO TIME_TBL VALUES ('00:00'); INSERT INTO TIME_TBL VALUES ('01:00'); -- as of 7.4, timezone spec should be accepted and ignored INSERT INTO TIME_TBL VALUES ('02:03 PST'); INSERT INTO TIME_TBL VALUES ('11:59 EDT'); INSERT INTO TIME_TBL VALUES ('12:00'); INSERT INTO TIME_TBL VALUES ('12:01'); INSERT INTO TIME_TBL VALUES ('23:59'); INSERT INTO TIME_TBL VALUES ('11:59:59.99 PM'); INSERT INTO TIME_TBL VALUES ('2003-03-07 15:36:39 America/New_York'); INSERT INTO TIME_TBL VALUES ('2003-07-07 15:36:39 America/New_York'); -- this should fail (the timezone offset is not known) INSERT INTO TIME_TBL VALUES ('15:36:39 America/New_York'); SELECT f1 AS "Time" FROM TIME_TBL; SELECT f1 AS "Three" FROM TIME_TBL WHERE f1 < '05:06:07'; SELECT f1 AS "Five" FROM TIME_TBL WHERE f1 > '05:06:07'; SELECT f1 AS "None" FROM TIME_TBL WHERE f1 < '00:00'; SELECT f1 AS "Eight" FROM TIME_TBL WHERE f1 >= '00:00'; -- -- TIME simple math -- -- We now make a distinction between time and intervals, -- and adding two times together makes no sense at all. -- Leave in one query to show that it is rejected, -- and do the rest of the testing in horology.sql -- where we do mixed-type arithmetic. - thomas 2000-12-02 SELECT f1 + time '00:01' AS "Illegal" FROM TIME_TBL; pgFormatter-4.2/t/pg-test-files/sql/timestamp.sql000066400000000000000000000234221361326045100221260ustar00rootroot00000000000000-- -- TIMESTAMP -- CREATE TABLE TIMESTAMP_TBL (d1 timestamp(2) without time zone); -- Test shorthand input values -- We can't just "select" the results since they aren't constants; test for -- equality instead. We can do that by running the test inside a transaction -- block, within which the value of 'now' shouldn't change. We also check -- that 'now' *does* change over a reasonable interval such as 100 msec. -- NOTE: it is possible for this part of the test to fail if the transaction -- block is entered exactly at local midnight; then 'now' and 'today' have -- the same values and the counts will come out different. INSERT INTO TIMESTAMP_TBL VALUES ('now'); SELECT pg_sleep(0.1); BEGIN; INSERT INTO TIMESTAMP_TBL VALUES ('now'); INSERT INTO TIMESTAMP_TBL VALUES ('today'); INSERT INTO TIMESTAMP_TBL VALUES ('yesterday'); INSERT INTO TIMESTAMP_TBL VALUES ('tomorrow'); -- time zone should be ignored by this data type INSERT INTO TIMESTAMP_TBL VALUES ('tomorrow EST'); INSERT INTO TIMESTAMP_TBL VALUES ('tomorrow zulu'); SELECT count(*) AS One FROM TIMESTAMP_TBL WHERE d1 = timestamp without time zone 'today'; SELECT count(*) AS Three FROM TIMESTAMP_TBL WHERE d1 = timestamp without time zone 'tomorrow'; SELECT count(*) AS One FROM TIMESTAMP_TBL WHERE d1 = timestamp without time zone 'yesterday'; SELECT count(*) AS One FROM TIMESTAMP_TBL WHERE d1 = timestamp(2) without time zone 'now'; COMMIT; DELETE FROM TIMESTAMP_TBL; -- verify uniform transaction time within transaction block BEGIN; INSERT INTO TIMESTAMP_TBL VALUES ('now'); SELECT pg_sleep(0.1); INSERT INTO TIMESTAMP_TBL VALUES ('now'); SELECT pg_sleep(0.1); SELECT count(*) AS two FROM TIMESTAMP_TBL WHERE d1 = timestamp(2) without time zone 'now'; COMMIT; TRUNCATE TIMESTAMP_TBL; -- Special values INSERT INTO TIMESTAMP_TBL VALUES ('-infinity'); INSERT INTO TIMESTAMP_TBL VALUES ('infinity'); INSERT INTO TIMESTAMP_TBL VALUES ('epoch'); -- Obsolete special values INSERT INTO TIMESTAMP_TBL VALUES ('invalid'); INSERT INTO TIMESTAMP_TBL VALUES ('undefined'); INSERT INTO TIMESTAMP_TBL VALUES ('current'); -- Postgres v6.0 standard output format INSERT INTO TIMESTAMP_TBL VALUES ('Mon Feb 10 17:32:01 1997 PST'); -- Variations on Postgres v6.1 standard output format INSERT INTO TIMESTAMP_TBL VALUES ('Mon Feb 10 17:32:01.000001 1997 PST'); INSERT INTO TIMESTAMP_TBL VALUES ('Mon Feb 10 17:32:01.999999 1997 PST'); INSERT INTO TIMESTAMP_TBL VALUES ('Mon Feb 10 17:32:01.4 1997 PST'); INSERT INTO TIMESTAMP_TBL VALUES ('Mon Feb 10 17:32:01.5 1997 PST'); INSERT INTO TIMESTAMP_TBL VALUES ('Mon Feb 10 17:32:01.6 1997 PST'); -- ISO 8601 format INSERT INTO TIMESTAMP_TBL VALUES ('1997-01-02'); INSERT INTO TIMESTAMP_TBL VALUES ('1997-01-02 03:04:05'); INSERT INTO TIMESTAMP_TBL VALUES ('1997-02-10 17:32:01-08'); INSERT INTO TIMESTAMP_TBL VALUES ('1997-02-10 17:32:01-0800'); INSERT INTO TIMESTAMP_TBL VALUES ('1997-02-10 17:32:01 -08:00'); INSERT INTO TIMESTAMP_TBL VALUES ('19970210 173201 -0800'); INSERT INTO TIMESTAMP_TBL VALUES ('1997-06-10 17:32:01 -07:00'); INSERT INTO TIMESTAMP_TBL VALUES ('2001-09-22T18:19:20'); -- POSIX format (note that the timezone abbrev is just decoration here) INSERT INTO TIMESTAMP_TBL VALUES ('2000-03-15 08:14:01 GMT+8'); INSERT INTO TIMESTAMP_TBL VALUES ('2000-03-15 13:14:02 GMT-1'); INSERT INTO TIMESTAMP_TBL VALUES ('2000-03-15 12:14:03 GMT-2'); INSERT INTO TIMESTAMP_TBL VALUES ('2000-03-15 03:14:04 PST+8'); INSERT INTO TIMESTAMP_TBL VALUES ('2000-03-15 02:14:05 MST+7:00'); -- Variations for acceptable input formats INSERT INTO TIMESTAMP_TBL VALUES ('Feb 10 17:32:01 1997 -0800'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 10 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 10 5:32PM 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('1997/02/10 17:32:01-0800'); INSERT INTO TIMESTAMP_TBL VALUES ('1997-02-10 17:32:01 PST'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb-10-1997 17:32:01 PST'); INSERT INTO TIMESTAMP_TBL VALUES ('02-10-1997 17:32:01 PST'); INSERT INTO TIMESTAMP_TBL VALUES ('19970210 173201 PST'); set datestyle to ymd; INSERT INTO TIMESTAMP_TBL VALUES ('97FEB10 5:32:01PM UTC'); INSERT INTO TIMESTAMP_TBL VALUES ('97/02/10 17:32:01 UTC'); reset datestyle; INSERT INTO TIMESTAMP_TBL VALUES ('1997.041 17:32:01 UTC'); INSERT INTO TIMESTAMP_TBL VALUES ('19970210 173201 America/New_York'); -- this fails (even though TZ is a no-op, we still look it up) INSERT INTO TIMESTAMP_TBL VALUES ('19970710 173201 America/Does_not_exist'); -- Check date conversion and date arithmetic INSERT INTO TIMESTAMP_TBL VALUES ('1997-06-10 18:32:01 PDT'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 10 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 11 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 12 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 13 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 14 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 15 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 0097 BC'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 0097'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 0597'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 1097'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 1697'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 1797'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 1897'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 2097'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 28 17:32:01 1996'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 29 17:32:01 1996'); INSERT INTO TIMESTAMP_TBL VALUES ('Mar 01 17:32:01 1996'); INSERT INTO TIMESTAMP_TBL VALUES ('Dec 30 17:32:01 1996'); INSERT INTO TIMESTAMP_TBL VALUES ('Dec 31 17:32:01 1996'); INSERT INTO TIMESTAMP_TBL VALUES ('Jan 01 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 28 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 29 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Mar 01 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Dec 30 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Dec 31 17:32:01 1997'); INSERT INTO TIMESTAMP_TBL VALUES ('Dec 31 17:32:01 1999'); INSERT INTO TIMESTAMP_TBL VALUES ('Jan 01 17:32:01 2000'); INSERT INTO TIMESTAMP_TBL VALUES ('Dec 31 17:32:01 2000'); INSERT INTO TIMESTAMP_TBL VALUES ('Jan 01 17:32:01 2001'); -- Currently unsupported syntax and ranges INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 -0097'); INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 5097 BC'); SELECT '' AS "64", d1 FROM TIMESTAMP_TBL; -- Check behavior at the lower boundary of the timestamp range SELECT '4714-11-24 00:00:00 BC'::timestamp; SELECT '4714-11-23 23:59:59 BC'::timestamp; -- out of range -- The upper boundary differs between integer and float timestamps, so no check -- Demonstrate functions and operators SELECT '' AS "48", d1 FROM TIMESTAMP_TBL WHERE d1 > timestamp without time zone '1997-01-02'; SELECT '' AS "15", d1 FROM TIMESTAMP_TBL WHERE d1 < timestamp without time zone '1997-01-02'; SELECT '' AS one, d1 FROM TIMESTAMP_TBL WHERE d1 = timestamp without time zone '1997-01-02'; SELECT '' AS "63", d1 FROM TIMESTAMP_TBL WHERE d1 != timestamp without time zone '1997-01-02'; SELECT '' AS "16", d1 FROM TIMESTAMP_TBL WHERE d1 <= timestamp without time zone '1997-01-02'; SELECT '' AS "49", d1 FROM TIMESTAMP_TBL WHERE d1 >= timestamp without time zone '1997-01-02'; SELECT '' AS "54", d1 - timestamp without time zone '1997-01-02' AS diff FROM TIMESTAMP_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; SELECT '' AS date_trunc_week, date_trunc( 'week', timestamp '2004-02-29 15:44:17.71393' ) AS week_trunc; -- Test casting within a BETWEEN qualifier SELECT '' AS "54", d1 - timestamp without time zone '1997-01-02' AS diff FROM TIMESTAMP_TBL WHERE d1 BETWEEN timestamp without time zone '1902-01-01' AND timestamp without time zone '2038-01-01'; SELECT '' AS "54", d1 as "timestamp", date_part( 'year', d1) AS year, date_part( 'month', d1) AS month, date_part( 'day', d1) AS day, date_part( 'hour', d1) AS hour, date_part( 'minute', d1) AS minute, date_part( 'second', d1) AS second FROM TIMESTAMP_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; SELECT '' AS "54", d1 as "timestamp", date_part( 'quarter', d1) AS quarter, date_part( 'msec', d1) AS msec, date_part( 'usec', d1) AS usec FROM TIMESTAMP_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; SELECT '' AS "54", d1 as "timestamp", date_part( 'isoyear', d1) AS isoyear, date_part( 'week', d1) AS week, date_part( 'dow', d1) AS dow FROM TIMESTAMP_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; -- TO_CHAR() SELECT '' AS to_char_1, to_char(d1, 'DAY Day day DY Dy dy MONTH Month month RM MON Mon mon') FROM TIMESTAMP_TBL; SELECT '' AS to_char_2, to_char(d1, 'FMDAY FMDay FMday FMMONTH FMMonth FMmonth FMRM') FROM TIMESTAMP_TBL; SELECT '' AS to_char_3, to_char(d1, 'Y,YYY YYYY YYY YY Y CC Q MM WW DDD DD D J') FROM TIMESTAMP_TBL; SELECT '' AS to_char_4, to_char(d1, 'FMY,YYY FMYYYY FMYYY FMYY FMY FMCC FMQ FMMM FMWW FMDDD FMDD FMD FMJ') FROM TIMESTAMP_TBL; SELECT '' AS to_char_5, to_char(d1, 'HH HH12 HH24 MI SS SSSS') FROM TIMESTAMP_TBL; SELECT '' AS to_char_6, to_char(d1, E'"HH:MI:SS is" HH:MI:SS "\\"text between quote marks\\""') FROM TIMESTAMP_TBL; SELECT '' AS to_char_7, to_char(d1, 'HH24--text--MI--text--SS') FROM TIMESTAMP_TBL; SELECT '' AS to_char_8, to_char(d1, 'YYYYTH YYYYth Jth') FROM TIMESTAMP_TBL; SELECT '' AS to_char_9, to_char(d1, 'YYYY A.D. YYYY a.d. YYYY bc HH:MI:SS P.M. HH:MI:SS p.m. HH:MI:SS pm') FROM TIMESTAMP_TBL; SELECT '' AS to_char_10, to_char(d1, 'IYYY IYY IY I IW IDDD ID') FROM TIMESTAMP_TBL; SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID') FROM TIMESTAMP_TBL; -- timestamp numeric fields constructor SELECT make_timestamp(2014,12,28,6,30,45.887); pgFormatter-4.2/t/pg-test-files/sql/timestamptz.sql000066400000000000000000000520741361326045100225110ustar00rootroot00000000000000-- -- TIMESTAMPTZ -- CREATE TABLE TIMESTAMPTZ_TBL (d1 timestamp(2) with time zone); -- Test shorthand input values -- We can't just "select" the results since they aren't constants; test for -- equality instead. We can do that by running the test inside a transaction -- block, within which the value of 'now' shouldn't change. We also check -- that 'now' *does* change over a reasonable interval such as 100 msec. -- NOTE: it is possible for this part of the test to fail if the transaction -- block is entered exactly at local midnight; then 'now' and 'today' have -- the same values and the counts will come out different. INSERT INTO TIMESTAMPTZ_TBL VALUES ('now'); SELECT pg_sleep(0.1); BEGIN; INSERT INTO TIMESTAMPTZ_TBL VALUES ('now'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('today'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('yesterday'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('tomorrow'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('tomorrow EST'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('tomorrow zulu'); SELECT count(*) AS One FROM TIMESTAMPTZ_TBL WHERE d1 = timestamp with time zone 'today'; SELECT count(*) AS One FROM TIMESTAMPTZ_TBL WHERE d1 = timestamp with time zone 'tomorrow'; SELECT count(*) AS One FROM TIMESTAMPTZ_TBL WHERE d1 = timestamp with time zone 'yesterday'; SELECT count(*) AS One FROM TIMESTAMPTZ_TBL WHERE d1 = timestamp(2) with time zone 'now'; COMMIT; DELETE FROM TIMESTAMPTZ_TBL; -- verify uniform transaction time within transaction block BEGIN; INSERT INTO TIMESTAMPTZ_TBL VALUES ('now'); SELECT pg_sleep(0.1); INSERT INTO TIMESTAMPTZ_TBL VALUES ('now'); SELECT pg_sleep(0.1); SELECT count(*) AS two FROM TIMESTAMPTZ_TBL WHERE d1 = timestamp(2) with time zone 'now'; COMMIT; DELETE FROM TIMESTAMPTZ_TBL; -- Special values INSERT INTO TIMESTAMPTZ_TBL VALUES ('-infinity'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('infinity'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('epoch'); -- Obsolete special values INSERT INTO TIMESTAMPTZ_TBL VALUES ('invalid'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('undefined'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('current'); -- Postgres v6.0 standard output format INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mon Feb 10 17:32:01 1997 PST'); -- Variations on Postgres v6.1 standard output format INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mon Feb 10 17:32:01.000001 1997 PST'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mon Feb 10 17:32:01.999999 1997 PST'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mon Feb 10 17:32:01.4 1997 PST'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mon Feb 10 17:32:01.5 1997 PST'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mon Feb 10 17:32:01.6 1997 PST'); -- ISO 8601 format INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-01-02'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-01-02 03:04:05'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-02-10 17:32:01-08'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-02-10 17:32:01-0800'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-02-10 17:32:01 -08:00'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('19970210 173201 -0800'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-06-10 17:32:01 -07:00'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('2001-09-22T18:19:20'); -- POSIX format (note that the timezone abbrev is just decoration here) INSERT INTO TIMESTAMPTZ_TBL VALUES ('2000-03-15 08:14:01 GMT+8'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('2000-03-15 13:14:02 GMT-1'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('2000-03-15 12:14:03 GMT-2'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('2000-03-15 03:14:04 PST+8'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('2000-03-15 02:14:05 MST+7:00'); -- Variations for acceptable input formats INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 10 17:32:01 1997 -0800'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 10 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 10 5:32PM 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997/02/10 17:32:01-0800'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-02-10 17:32:01 PST'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb-10-1997 17:32:01 PST'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('02-10-1997 17:32:01 PST'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('19970210 173201 PST'); set datestyle to ymd; INSERT INTO TIMESTAMPTZ_TBL VALUES ('97FEB10 5:32:01PM UTC'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('97/02/10 17:32:01 UTC'); reset datestyle; INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997.041 17:32:01 UTC'); -- timestamps at different timezones INSERT INTO TIMESTAMPTZ_TBL VALUES ('19970210 173201 America/New_York'); SELECT '19970210 173201' AT TIME ZONE 'America/New_York'; INSERT INTO TIMESTAMPTZ_TBL VALUES ('19970710 173201 America/New_York'); SELECT '19970710 173201' AT TIME ZONE 'America/New_York'; INSERT INTO TIMESTAMPTZ_TBL VALUES ('19970710 173201 America/Does_not_exist'); SELECT '19970710 173201' AT TIME ZONE 'America/Does_not_exist'; -- Daylight saving time for timestamps beyond 32-bit time_t range. SELECT '20500710 173201 Europe/Helsinki'::timestamptz; -- DST SELECT '20500110 173201 Europe/Helsinki'::timestamptz; -- non-DST SELECT '205000-07-10 17:32:01 Europe/Helsinki'::timestamptz; -- DST SELECT '205000-01-10 17:32:01 Europe/Helsinki'::timestamptz; -- non-DST -- Check date conversion and date arithmetic INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-06-10 18:32:01 PDT'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 10 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 11 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 12 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 13 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 14 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 15 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 0097 BC'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 0097'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 0597'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 1097'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 1697'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 1797'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 1897'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 2097'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 28 17:32:01 1996'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 29 17:32:01 1996'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mar 01 17:32:01 1996'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Dec 30 17:32:01 1996'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Dec 31 17:32:01 1996'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Jan 01 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 28 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 29 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mar 01 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Dec 30 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Dec 31 17:32:01 1997'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Dec 31 17:32:01 1999'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Jan 01 17:32:01 2000'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Dec 31 17:32:01 2000'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Jan 01 17:32:01 2001'); -- Currently unsupported syntax and ranges INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 -0097'); INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 5097 BC'); -- Alternative field order that we've historically supported (sort of) -- with regular and POSIXy timezone specs SELECT 'Wed Jul 11 10:51:14 America/New_York 2001'::timestamptz; SELECT 'Wed Jul 11 10:51:14 GMT-4 2001'::timestamptz; SELECT 'Wed Jul 11 10:51:14 GMT+4 2001'::timestamptz; SELECT 'Wed Jul 11 10:51:14 PST-03:00 2001'::timestamptz; SELECT 'Wed Jul 11 10:51:14 PST+03:00 2001'::timestamptz; SELECT '' AS "64", d1 FROM TIMESTAMPTZ_TBL; -- Check behavior at the lower boundary of the timestamp range SELECT '4714-11-24 00:00:00+00 BC'::timestamptz; SELECT '4714-11-23 16:00:00-08 BC'::timestamptz; SELECT 'Sun Nov 23 16:00:00 4714 PST BC'::timestamptz; SELECT '4714-11-23 23:59:59+00 BC'::timestamptz; -- out of range -- The upper boundary differs between integer and float timestamps, so no check -- Demonstrate functions and operators SELECT '' AS "48", d1 FROM TIMESTAMPTZ_TBL WHERE d1 > timestamp with time zone '1997-01-02'; SELECT '' AS "15", d1 FROM TIMESTAMPTZ_TBL WHERE d1 < timestamp with time zone '1997-01-02'; SELECT '' AS one, d1 FROM TIMESTAMPTZ_TBL WHERE d1 = timestamp with time zone '1997-01-02'; SELECT '' AS "63", d1 FROM TIMESTAMPTZ_TBL WHERE d1 != timestamp with time zone '1997-01-02'; SELECT '' AS "16", d1 FROM TIMESTAMPTZ_TBL WHERE d1 <= timestamp with time zone '1997-01-02'; SELECT '' AS "49", d1 FROM TIMESTAMPTZ_TBL WHERE d1 >= timestamp with time zone '1997-01-02'; SELECT '' AS "54", d1 - timestamp with time zone '1997-01-02' AS diff FROM TIMESTAMPTZ_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; SELECT '' AS date_trunc_week, date_trunc( 'week', timestamp with time zone '2004-02-29 15:44:17.71393' ) AS week_trunc; SELECT '' AS date_trunc_at_tz, date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'Australia/Sydney') as sydney_trunc; -- zone name SELECT '' AS date_trunc_at_tz, date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'GMT') as gmt_trunc; -- fixed-offset abbreviation SELECT '' AS date_trunc_at_tz, date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'VET') as vet_trunc; -- variable-offset abbreviation -- Test casting within a BETWEEN qualifier SELECT '' AS "54", d1 - timestamp with time zone '1997-01-02' AS diff FROM TIMESTAMPTZ_TBL WHERE d1 BETWEEN timestamp with time zone '1902-01-01' AND timestamp with time zone '2038-01-01'; SELECT '' AS "54", d1 as timestamptz, date_part( 'year', d1) AS year, date_part( 'month', d1) AS month, date_part( 'day', d1) AS day, date_part( 'hour', d1) AS hour, date_part( 'minute', d1) AS minute, date_part( 'second', d1) AS second FROM TIMESTAMPTZ_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; SELECT '' AS "54", d1 as timestamptz, date_part( 'quarter', d1) AS quarter, date_part( 'msec', d1) AS msec, date_part( 'usec', d1) AS usec FROM TIMESTAMPTZ_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; SELECT '' AS "54", d1 as timestamptz, date_part( 'isoyear', d1) AS isoyear, date_part( 'week', d1) AS week, date_part( 'dow', d1) AS dow FROM TIMESTAMPTZ_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; -- TO_CHAR() SELECT '' AS to_char_1, to_char(d1, 'DAY Day day DY Dy dy MONTH Month month RM MON Mon mon') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_2, to_char(d1, 'FMDAY FMDay FMday FMMONTH FMMonth FMmonth FMRM') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_3, to_char(d1, 'Y,YYY YYYY YYY YY Y CC Q MM WW DDD DD D J') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_4, to_char(d1, 'FMY,YYY FMYYYY FMYYY FMYY FMY FMCC FMQ FMMM FMWW FMDDD FMDD FMD FMJ') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_5, to_char(d1, 'HH HH12 HH24 MI SS SSSS') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_6, to_char(d1, E'"HH:MI:SS is" HH:MI:SS "\\"text between quote marks\\""') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_7, to_char(d1, 'HH24--text--MI--text--SS') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_8, to_char(d1, 'YYYYTH YYYYth Jth') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_9, to_char(d1, 'YYYY A.D. YYYY a.d. YYYY bc HH:MI:SS P.M. HH:MI:SS p.m. HH:MI:SS pm') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_10, to_char(d1, 'IYYY IYY IY I IW IDDD ID') FROM TIMESTAMPTZ_TBL; SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID') FROM TIMESTAMPTZ_TBL; -- Check OF, TZH, TZM with various zone offsets, particularly fractional hours SET timezone = '00:00'; SELECT to_char(now(), 'OF') as "OF", to_char(now(), 'TZH:TZM') as "TZH:TZM"; SET timezone = '+02:00'; SELECT to_char(now(), 'OF') as "OF", to_char(now(), 'TZH:TZM') as "TZH:TZM"; SET timezone = '-13:00'; SELECT to_char(now(), 'OF') as "OF", to_char(now(), 'TZH:TZM') as "TZH:TZM"; SET timezone = '-00:30'; SELECT to_char(now(), 'OF') as "OF", to_char(now(), 'TZH:TZM') as "TZH:TZM"; SET timezone = '00:30'; SELECT to_char(now(), 'OF') as "OF", to_char(now(), 'TZH:TZM') as "TZH:TZM"; SET timezone = '-04:30'; SELECT to_char(now(), 'OF') as "OF", to_char(now(), 'TZH:TZM') as "TZH:TZM"; SET timezone = '04:30'; SELECT to_char(now(), 'OF') as "OF", to_char(now(), 'TZH:TZM') as "TZH:TZM"; SET timezone = '-04:15'; SELECT to_char(now(), 'OF') as "OF", to_char(now(), 'TZH:TZM') as "TZH:TZM"; SET timezone = '04:15'; SELECT to_char(now(), 'OF') as "OF", to_char(now(), 'TZH:TZM') as "TZH:TZM"; RESET timezone; CREATE TABLE TIMESTAMPTZ_TST (a int , b timestamptz); -- Test year field value with len > 4 INSERT INTO TIMESTAMPTZ_TST VALUES(1, 'Sat Mar 12 23:58:48 1000 IST'); INSERT INTO TIMESTAMPTZ_TST VALUES(2, 'Sat Mar 12 23:58:48 10000 IST'); INSERT INTO TIMESTAMPTZ_TST VALUES(3, 'Sat Mar 12 23:58:48 100000 IST'); INSERT INTO TIMESTAMPTZ_TST VALUES(3, '10000 Mar 12 23:58:48 IST'); INSERT INTO TIMESTAMPTZ_TST VALUES(4, '100000312 23:58:48 IST'); INSERT INTO TIMESTAMPTZ_TST VALUES(4, '1000000312 23:58:48 IST'); --Verify data SELECT * FROM TIMESTAMPTZ_TST ORDER BY a; --Cleanup DROP TABLE TIMESTAMPTZ_TST; -- test timestamptz constructors set TimeZone to 'America/New_York'; -- numeric timezone SELECT make_timestamptz(1973, 07, 15, 08, 15, 55.33); SELECT make_timestamptz(1973, 07, 15, 08, 15, 55.33, '+2'); SELECT make_timestamptz(1973, 07, 15, 08, 15, 55.33, '-2'); WITH tzs (tz) AS (VALUES ('+1'), ('+1:'), ('+1:0'), ('+100'), ('+1:00'), ('+01:00'), ('+10'), ('+1000'), ('+10:'), ('+10:0'), ('+10:00'), ('+10:00:'), ('+10:00:1'), ('+10:00:01'), ('+10:00:10')) SELECT make_timestamptz(2010, 2, 27, 3, 45, 00, tz), tz FROM tzs; -- these should fail SELECT make_timestamptz(1973, 07, 15, 08, 15, 55.33, '2'); SELECT make_timestamptz(2014, 12, 10, 10, 10, 10, '+16'); SELECT make_timestamptz(2014, 12, 10, 10, 10, 10, '-16'); -- should be true SELECT make_timestamptz(1973, 07, 15, 08, 15, 55.33, '+2') = '1973-07-15 08:15:55.33+02'::timestamptz; -- full timezone names SELECT make_timestamptz(2014, 12, 10, 0, 0, 0, 'Europe/Prague') = timestamptz '2014-12-10 00:00:00 Europe/Prague'; SELECT make_timestamptz(2014, 12, 10, 0, 0, 0, 'Europe/Prague') AT TIME ZONE 'UTC'; SELECT make_timestamptz(1846, 12, 10, 0, 0, 0, 'Asia/Manila') AT TIME ZONE 'UTC'; SELECT make_timestamptz(1881, 12, 10, 0, 0, 0, 'Europe/Paris') AT TIME ZONE 'UTC'; SELECT make_timestamptz(1910, 12, 24, 0, 0, 0, 'Nehwon/Lankhmar'); -- abbreviations SELECT make_timestamptz(2008, 12, 10, 10, 10, 10, 'EST'); SELECT make_timestamptz(2008, 12, 10, 10, 10, 10, 'EDT'); SELECT make_timestamptz(2014, 12, 10, 10, 10, 10, 'PST8PDT'); RESET TimeZone; -- -- Test behavior with a dynamic (time-varying) timezone abbreviation. -- These tests rely on the knowledge that MSK (Europe/Moscow standard time) -- moved forwards in Mar 2011 and backwards again in Oct 2014. -- SET TimeZone to 'UTC'; SELECT '2011-03-27 00:00:00 Europe/Moscow'::timestamptz; SELECT '2011-03-27 01:00:00 Europe/Moscow'::timestamptz; SELECT '2011-03-27 01:59:59 Europe/Moscow'::timestamptz; SELECT '2011-03-27 02:00:00 Europe/Moscow'::timestamptz; SELECT '2011-03-27 02:00:01 Europe/Moscow'::timestamptz; SELECT '2011-03-27 02:59:59 Europe/Moscow'::timestamptz; SELECT '2011-03-27 03:00:00 Europe/Moscow'::timestamptz; SELECT '2011-03-27 03:00:01 Europe/Moscow'::timestamptz; SELECT '2011-03-27 04:00:00 Europe/Moscow'::timestamptz; SELECT '2011-03-27 00:00:00 MSK'::timestamptz; SELECT '2011-03-27 01:00:00 MSK'::timestamptz; SELECT '2011-03-27 01:59:59 MSK'::timestamptz; SELECT '2011-03-27 02:00:00 MSK'::timestamptz; SELECT '2011-03-27 02:00:01 MSK'::timestamptz; SELECT '2011-03-27 02:59:59 MSK'::timestamptz; SELECT '2011-03-27 03:00:00 MSK'::timestamptz; SELECT '2011-03-27 03:00:01 MSK'::timestamptz; SELECT '2011-03-27 04:00:00 MSK'::timestamptz; SELECT '2014-10-26 00:00:00 Europe/Moscow'::timestamptz; SELECT '2014-10-26 00:59:59 Europe/Moscow'::timestamptz; SELECT '2014-10-26 01:00:00 Europe/Moscow'::timestamptz; SELECT '2014-10-26 01:00:01 Europe/Moscow'::timestamptz; SELECT '2014-10-26 02:00:00 Europe/Moscow'::timestamptz; SELECT '2014-10-26 00:00:00 MSK'::timestamptz; SELECT '2014-10-26 00:59:59 MSK'::timestamptz; SELECT '2014-10-26 01:00:00 MSK'::timestamptz; SELECT '2014-10-26 01:00:01 MSK'::timestamptz; SELECT '2014-10-26 02:00:00 MSK'::timestamptz; SELECT '2011-03-27 00:00:00'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 01:00:00'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 01:59:59'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 02:00:00'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 02:00:01'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 02:59:59'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 03:00:00'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 03:00:01'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 04:00:00'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 00:00:00'::timestamp AT TIME ZONE 'MSK'; SELECT '2011-03-27 01:00:00'::timestamp AT TIME ZONE 'MSK'; SELECT '2011-03-27 01:59:59'::timestamp AT TIME ZONE 'MSK'; SELECT '2011-03-27 02:00:00'::timestamp AT TIME ZONE 'MSK'; SELECT '2011-03-27 02:00:01'::timestamp AT TIME ZONE 'MSK'; SELECT '2011-03-27 02:59:59'::timestamp AT TIME ZONE 'MSK'; SELECT '2011-03-27 03:00:00'::timestamp AT TIME ZONE 'MSK'; SELECT '2011-03-27 03:00:01'::timestamp AT TIME ZONE 'MSK'; SELECT '2011-03-27 04:00:00'::timestamp AT TIME ZONE 'MSK'; SELECT '2014-10-26 00:00:00'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-26 00:59:59'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-26 01:00:00'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-26 01:00:01'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-26 02:00:00'::timestamp AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-26 00:00:00'::timestamp AT TIME ZONE 'MSK'; SELECT '2014-10-26 00:59:59'::timestamp AT TIME ZONE 'MSK'; SELECT '2014-10-26 01:00:00'::timestamp AT TIME ZONE 'MSK'; SELECT '2014-10-26 01:00:01'::timestamp AT TIME ZONE 'MSK'; SELECT '2014-10-26 02:00:00'::timestamp AT TIME ZONE 'MSK'; SELECT make_timestamptz(2014, 10, 26, 0, 0, 0, 'MSK'); SELECT make_timestamptz(2014, 10, 26, 1, 0, 0, 'MSK'); SELECT to_timestamp( 0); -- 1970-01-01 00:00:00+00 SELECT to_timestamp( 946684800); -- 2000-01-01 00:00:00+00 SELECT to_timestamp(1262349296.7890123); -- 2010-01-01 12:34:56.789012+00 -- edge cases SELECT to_timestamp(-210866803200); -- 4714-11-24 00:00:00+00 BC -- upper limit varies between integer and float timestamps, so hard to test -- nonfinite values SELECT to_timestamp(' Infinity'::float); SELECT to_timestamp('-Infinity'::float); SELECT to_timestamp('NaN'::float); SET TimeZone to 'Europe/Moscow'; SELECT '2011-03-26 21:00:00 UTC'::timestamptz; SELECT '2011-03-26 22:00:00 UTC'::timestamptz; SELECT '2011-03-26 22:59:59 UTC'::timestamptz; SELECT '2011-03-26 23:00:00 UTC'::timestamptz; SELECT '2011-03-26 23:00:01 UTC'::timestamptz; SELECT '2011-03-26 23:59:59 UTC'::timestamptz; SELECT '2011-03-27 00:00:00 UTC'::timestamptz; SELECT '2014-10-25 21:00:00 UTC'::timestamptz; SELECT '2014-10-25 21:59:59 UTC'::timestamptz; SELECT '2014-10-25 22:00:00 UTC'::timestamptz; SELECT '2014-10-25 22:00:01 UTC'::timestamptz; SELECT '2014-10-25 23:00:00 UTC'::timestamptz; RESET TimeZone; SELECT '2011-03-26 21:00:00 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-26 22:00:00 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-26 22:59:59 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-26 23:00:00 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-26 23:00:01 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-26 23:59:59 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-27 00:00:00 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-25 21:00:00 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-25 21:59:59 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-25 22:00:00 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-25 22:00:01 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2014-10-25 23:00:00 UTC'::timestamptz AT TIME ZONE 'Europe/Moscow'; SELECT '2011-03-26 21:00:00 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2011-03-26 22:00:00 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2011-03-26 22:59:59 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2011-03-26 23:00:00 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2011-03-26 23:00:01 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2011-03-26 23:59:59 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2011-03-27 00:00:00 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2014-10-25 21:00:00 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2014-10-25 21:59:59 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2014-10-25 22:00:00 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2014-10-25 22:00:01 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2014-10-25 23:00:00 UTC'::timestamptz AT TIME ZONE 'MSK'; -- -- Test that AT TIME ZONE isn't misoptimized when using an index (bug #14504) -- create temp table tmptz (f1 timestamptz primary key); insert into tmptz values ('2017-01-18 00:00+00'); explain (costs off) select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00'; select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00'; pgFormatter-4.2/t/pg-test-files/sql/timetz.sql000066400000000000000000000026521361326045100214410ustar00rootroot00000000000000-- -- TIMETZ -- CREATE TABLE TIMETZ_TBL (f1 time(2) with time zone); INSERT INTO TIMETZ_TBL VALUES ('00:01 PDT'); INSERT INTO TIMETZ_TBL VALUES ('01:00 PDT'); INSERT INTO TIMETZ_TBL VALUES ('02:03 PDT'); INSERT INTO TIMETZ_TBL VALUES ('07:07 PST'); INSERT INTO TIMETZ_TBL VALUES ('08:08 EDT'); INSERT INTO TIMETZ_TBL VALUES ('11:59 PDT'); INSERT INTO TIMETZ_TBL VALUES ('12:00 PDT'); INSERT INTO TIMETZ_TBL VALUES ('12:01 PDT'); INSERT INTO TIMETZ_TBL VALUES ('23:59 PDT'); INSERT INTO TIMETZ_TBL VALUES ('11:59:59.99 PM PDT'); INSERT INTO TIMETZ_TBL VALUES ('2003-03-07 15:36:39 America/New_York'); INSERT INTO TIMETZ_TBL VALUES ('2003-07-07 15:36:39 America/New_York'); -- this should fail (the timezone offset is not known) INSERT INTO TIMETZ_TBL VALUES ('15:36:39 America/New_York'); SELECT f1 AS "Time TZ" FROM TIMETZ_TBL; SELECT f1 AS "Three" FROM TIMETZ_TBL WHERE f1 < '05:06:07-07'; SELECT f1 AS "Seven" FROM TIMETZ_TBL WHERE f1 > '05:06:07-07'; SELECT f1 AS "None" FROM TIMETZ_TBL WHERE f1 < '00:00-07'; SELECT f1 AS "Ten" FROM TIMETZ_TBL WHERE f1 >= '00:00-07'; -- -- TIME simple math -- -- We now make a distinction between time and intervals, -- and adding two times together makes no sense at all. -- Leave in one query to show that it is rejected, -- and do the rest of the testing in horology.sql -- where we do mixed-type arithmetic. - thomas 2000-12-02 SELECT f1 + time with time zone '00:01' AS "Illegal" FROM TIMETZ_TBL; pgFormatter-4.2/t/pg-test-files/sql/transactions.sql000066400000000000000000000337631361326045100226440ustar00rootroot00000000000000-- -- TRANSACTIONS -- BEGIN; SELECT * INTO TABLE xacttest FROM aggtest; INSERT INTO xacttest (a, b) VALUES (777, 777.777); END; -- should retrieve one value-- SELECT a FROM xacttest WHERE a > 100; BEGIN; CREATE TABLE disappear (a int4); DELETE FROM aggtest; -- should be empty SELECT * FROM aggtest; ABORT; -- should not exist SELECT oid FROM pg_class WHERE relname = 'disappear'; -- should have members again SELECT * FROM aggtest; -- Read-only tests CREATE TABLE writetest (a int); CREATE TEMPORARY TABLE temptest (a int); BEGIN; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY, DEFERRABLE; -- ok SELECT * FROM writetest; -- ok SET TRANSACTION READ WRITE; --fail COMMIT; BEGIN; SET TRANSACTION READ ONLY; -- ok SET TRANSACTION READ WRITE; -- ok SET TRANSACTION READ ONLY; -- ok SELECT * FROM writetest; -- ok SAVEPOINT x; SET TRANSACTION READ ONLY; -- ok SELECT * FROM writetest; -- ok SET TRANSACTION READ ONLY; -- ok SET TRANSACTION READ WRITE; --fail COMMIT; BEGIN; SET TRANSACTION READ WRITE; -- ok SAVEPOINT x; SET TRANSACTION READ WRITE; -- ok SET TRANSACTION READ ONLY; -- ok SELECT * FROM writetest; -- ok SET TRANSACTION READ ONLY; -- ok SET TRANSACTION READ WRITE; --fail COMMIT; BEGIN; SET TRANSACTION READ WRITE; -- ok SAVEPOINT x; SET TRANSACTION READ ONLY; -- ok SELECT * FROM writetest; -- ok ROLLBACK TO SAVEPOINT x; SHOW transaction_read_only; -- off SAVEPOINT y; SET TRANSACTION READ ONLY; -- ok SELECT * FROM writetest; -- ok RELEASE SAVEPOINT y; SHOW transaction_read_only; -- off COMMIT; SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY; DROP TABLE writetest; -- fail INSERT INTO writetest VALUES (1); -- fail SELECT * FROM writetest; -- ok DELETE FROM temptest; -- ok UPDATE temptest SET a = 0 FROM writetest WHERE temptest.a = 1 AND writetest.a = temptest.a; -- ok PREPARE test AS UPDATE writetest SET a = 0; -- ok EXECUTE test; -- fail SELECT * FROM writetest, temptest; -- ok CREATE TABLE test AS SELECT * FROM writetest; -- fail START TRANSACTION READ WRITE; DROP TABLE writetest; -- ok COMMIT; -- Subtransactions, basic tests -- create & drop tables SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE; CREATE TABLE trans_foobar (a int); BEGIN; CREATE TABLE trans_foo (a int); SAVEPOINT one; DROP TABLE trans_foo; CREATE TABLE trans_bar (a int); ROLLBACK TO SAVEPOINT one; RELEASE SAVEPOINT one; SAVEPOINT two; CREATE TABLE trans_baz (a int); RELEASE SAVEPOINT two; drop TABLE trans_foobar; CREATE TABLE trans_barbaz (a int); COMMIT; -- should exist: trans_barbaz, trans_baz, trans_foo SELECT * FROM trans_foo; -- should be empty SELECT * FROM trans_bar; -- shouldn't exist SELECT * FROM trans_barbaz; -- should be empty SELECT * FROM trans_baz; -- should be empty -- inserts BEGIN; INSERT INTO trans_foo VALUES (1); SAVEPOINT one; INSERT into trans_bar VALUES (1); ROLLBACK TO one; RELEASE SAVEPOINT one; SAVEPOINT two; INSERT into trans_barbaz VALUES (1); RELEASE two; SAVEPOINT three; SAVEPOINT four; INSERT INTO trans_foo VALUES (2); RELEASE SAVEPOINT four; ROLLBACK TO SAVEPOINT three; RELEASE SAVEPOINT three; INSERT INTO trans_foo VALUES (3); COMMIT; SELECT * FROM trans_foo; -- should have 1 and 3 SELECT * FROM trans_barbaz; -- should have 1 -- test whole-tree commit BEGIN; SAVEPOINT one; SELECT trans_foo; ROLLBACK TO SAVEPOINT one; RELEASE SAVEPOINT one; SAVEPOINT two; CREATE TABLE savepoints (a int); SAVEPOINT three; INSERT INTO savepoints VALUES (1); SAVEPOINT four; INSERT INTO savepoints VALUES (2); SAVEPOINT five; INSERT INTO savepoints VALUES (3); ROLLBACK TO SAVEPOINT five; COMMIT; COMMIT; -- should not be in a transaction block SELECT * FROM savepoints; -- test whole-tree rollback BEGIN; SAVEPOINT one; DELETE FROM savepoints WHERE a=1; RELEASE SAVEPOINT one; SAVEPOINT two; DELETE FROM savepoints WHERE a=1; SAVEPOINT three; DELETE FROM savepoints WHERE a=2; ROLLBACK; COMMIT; -- should not be in a transaction block SELECT * FROM savepoints; -- test whole-tree commit on an aborted subtransaction BEGIN; INSERT INTO savepoints VALUES (4); SAVEPOINT one; INSERT INTO savepoints VALUES (5); SELECT trans_foo; COMMIT; SELECT * FROM savepoints; BEGIN; INSERT INTO savepoints VALUES (6); SAVEPOINT one; INSERT INTO savepoints VALUES (7); RELEASE SAVEPOINT one; INSERT INTO savepoints VALUES (8); COMMIT; -- rows 6 and 8 should have been created by the same xact SELECT a.xmin = b.xmin FROM savepoints a, savepoints b WHERE a.a=6 AND b.a=8; -- rows 6 and 7 should have been created by different xacts SELECT a.xmin = b.xmin FROM savepoints a, savepoints b WHERE a.a=6 AND b.a=7; BEGIN; INSERT INTO savepoints VALUES (9); SAVEPOINT one; INSERT INTO savepoints VALUES (10); ROLLBACK TO SAVEPOINT one; INSERT INTO savepoints VALUES (11); COMMIT; SELECT a FROM savepoints WHERE a in (9, 10, 11); -- rows 9 and 11 should have been created by different xacts SELECT a.xmin = b.xmin FROM savepoints a, savepoints b WHERE a.a=9 AND b.a=11; BEGIN; INSERT INTO savepoints VALUES (12); SAVEPOINT one; INSERT INTO savepoints VALUES (13); SAVEPOINT two; INSERT INTO savepoints VALUES (14); ROLLBACK TO SAVEPOINT one; INSERT INTO savepoints VALUES (15); SAVEPOINT two; INSERT INTO savepoints VALUES (16); SAVEPOINT three; INSERT INTO savepoints VALUES (17); COMMIT; SELECT a FROM savepoints WHERE a BETWEEN 12 AND 17; BEGIN; INSERT INTO savepoints VALUES (18); SAVEPOINT one; INSERT INTO savepoints VALUES (19); SAVEPOINT two; INSERT INTO savepoints VALUES (20); ROLLBACK TO SAVEPOINT one; INSERT INTO savepoints VALUES (21); ROLLBACK TO SAVEPOINT one; INSERT INTO savepoints VALUES (22); COMMIT; SELECT a FROM savepoints WHERE a BETWEEN 18 AND 22; DROP TABLE savepoints; -- only in a transaction block: SAVEPOINT one; ROLLBACK TO SAVEPOINT one; RELEASE SAVEPOINT one; -- Only "rollback to" allowed in aborted state BEGIN; SAVEPOINT one; SELECT 0/0; SAVEPOINT two; -- ignored till the end of ... RELEASE SAVEPOINT one; -- ignored till the end of ... ROLLBACK TO SAVEPOINT one; SELECT 1; COMMIT; SELECT 1; -- this should work -- check non-transactional behavior of cursors BEGIN; DECLARE c CURSOR FOR SELECT unique2 FROM tenk1 ORDER BY unique2; SAVEPOINT one; FETCH 10 FROM c; ROLLBACK TO SAVEPOINT one; FETCH 10 FROM c; RELEASE SAVEPOINT one; FETCH 10 FROM c; CLOSE c; DECLARE c CURSOR FOR SELECT unique2/0 FROM tenk1 ORDER BY unique2; SAVEPOINT two; FETCH 10 FROM c; ROLLBACK TO SAVEPOINT two; -- c is now dead to the world ... FETCH 10 FROM c; ROLLBACK TO SAVEPOINT two; RELEASE SAVEPOINT two; FETCH 10 FROM c; COMMIT; -- -- Check that "stable" functions are really stable. They should not be -- able to see the partial results of the calling query. (Ideally we would -- also check that they don't see commits of concurrent transactions, but -- that's a mite hard to do within the limitations of pg_regress.) -- select * from xacttest; create or replace function max_xacttest() returns smallint language sql as 'select max(a) from xacttest' stable; begin; update xacttest set a = max_xacttest() + 10 where a > 0; select * from xacttest; rollback; -- But a volatile function can see the partial results of the calling query create or replace function max_xacttest() returns smallint language sql as 'select max(a) from xacttest' volatile; begin; update xacttest set a = max_xacttest() + 10 where a > 0; select * from xacttest; rollback; -- Now the same test with plpgsql (since it depends on SPI which is different) create or replace function max_xacttest() returns smallint language plpgsql as 'begin return max(a) from xacttest; end' stable; begin; update xacttest set a = max_xacttest() + 10 where a > 0; select * from xacttest; rollback; create or replace function max_xacttest() returns smallint language plpgsql as 'begin return max(a) from xacttest; end' volatile; begin; update xacttest set a = max_xacttest() + 10 where a > 0; select * from xacttest; rollback; -- test case for problems with dropping an open relation during abort BEGIN; savepoint x; CREATE TABLE koju (a INT UNIQUE); INSERT INTO koju VALUES (1); INSERT INTO koju VALUES (1); rollback to x; CREATE TABLE koju (a INT UNIQUE); INSERT INTO koju VALUES (1); INSERT INTO koju VALUES (1); ROLLBACK; DROP TABLE trans_foo; DROP TABLE trans_baz; DROP TABLE trans_barbaz; -- test case for problems with revalidating an open relation during abort create function inverse(int) returns float8 as $$ begin analyze revalidate_bug; return 1::float8/$1; exception when division_by_zero then return 0; end$$ language plpgsql volatile; create table revalidate_bug (c float8 unique); insert into revalidate_bug values (1); insert into revalidate_bug values (inverse(0)); drop table revalidate_bug; drop function inverse(int); -- verify that cursors created during an aborted subtransaction are -- closed, but that we do not rollback the effect of any FETCHs -- performed in the aborted subtransaction begin; savepoint x; create table abc (a int); insert into abc values (5); insert into abc values (10); declare foo cursor for select * from abc; fetch from foo; rollback to x; -- should fail fetch from foo; commit; begin; create table abc (a int); insert into abc values (5); insert into abc values (10); insert into abc values (15); declare foo cursor for select * from abc; fetch from foo; savepoint x; fetch from foo; rollback to x; fetch from foo; abort; -- Test for proper cleanup after a failure in a cursor portal -- that was created in an outer subtransaction CREATE FUNCTION invert(x float8) RETURNS float8 LANGUAGE plpgsql AS $$ begin return 1/x; end $$; CREATE FUNCTION create_temp_tab() RETURNS text LANGUAGE plpgsql AS $$ BEGIN CREATE TEMP TABLE new_table (f1 float8); -- case of interest is that we fail while holding an open -- relcache reference to new_table INSERT INTO new_table SELECT invert(0.0); RETURN 'foo'; END $$; BEGIN; DECLARE ok CURSOR FOR SELECT * FROM int8_tbl; DECLARE ctt CURSOR FOR SELECT create_temp_tab(); FETCH ok; SAVEPOINT s1; FETCH ok; -- should work FETCH ctt; -- error occurs here ROLLBACK TO s1; FETCH ok; -- should work FETCH ctt; -- must be rejected COMMIT; DROP FUNCTION create_temp_tab(); DROP FUNCTION invert(x float8); -- Tests for AND CHAIN CREATE TABLE abc (a int); -- set nondefault value so we have something to override below SET default_transaction_read_only = on; START TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, DEFERRABLE; SHOW transaction_isolation; SHOW transaction_read_only; SHOW transaction_deferrable; INSERT INTO abc VALUES (1); INSERT INTO abc VALUES (2); COMMIT AND CHAIN; -- TBLOCK_END SHOW transaction_isolation; SHOW transaction_read_only; SHOW transaction_deferrable; INSERT INTO abc VALUES ('error'); INSERT INTO abc VALUES (3); -- check it's really aborted COMMIT AND CHAIN; -- TBLOCK_ABORT_END SHOW transaction_isolation; SHOW transaction_read_only; SHOW transaction_deferrable; INSERT INTO abc VALUES (4); COMMIT; START TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, DEFERRABLE; SHOW transaction_isolation; SHOW transaction_read_only; SHOW transaction_deferrable; SAVEPOINT x; INSERT INTO abc VALUES ('error'); COMMIT AND CHAIN; -- TBLOCK_ABORT_PENDING SHOW transaction_isolation; SHOW transaction_read_only; SHOW transaction_deferrable; INSERT INTO abc VALUES (5); COMMIT; -- different mix of options just for fun START TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ WRITE, NOT DEFERRABLE; SHOW transaction_isolation; SHOW transaction_read_only; SHOW transaction_deferrable; INSERT INTO abc VALUES (6); ROLLBACK AND CHAIN; -- TBLOCK_ABORT_PENDING SHOW transaction_isolation; SHOW transaction_read_only; SHOW transaction_deferrable; INSERT INTO abc VALUES ('error'); ROLLBACK AND CHAIN; -- TBLOCK_ABORT_END SHOW transaction_isolation; SHOW transaction_read_only; SHOW transaction_deferrable; ROLLBACK; SELECT * FROM abc ORDER BY 1; RESET default_transaction_read_only; DROP TABLE abc; -- Test assorted behaviors around the implicit transaction block created -- when multiple SQL commands are sent in a single Query message. These -- tests rely on the fact that psql will not break SQL commands apart at a -- backslash-quoted semicolon, but will send them as one Query. create temp table i_table (f1 int); -- psql will show only the last result in a multi-statement Query SELECT 1\; SELECT 2\; SELECT 3; -- this implicitly commits: insert into i_table values(1)\; select * from i_table; -- 1/0 error will cause rolling back the whole implicit transaction insert into i_table values(2)\; select * from i_table\; select 1/0; select * from i_table; rollback; -- we are not in a transaction at this point -- can use regular begin/commit/rollback within a single Query begin\; insert into i_table values(3)\; commit; rollback; -- we are not in a transaction at this point begin\; insert into i_table values(4)\; rollback; rollback; -- we are not in a transaction at this point -- begin converts implicit transaction into a regular one that -- can extend past the end of the Query select 1\; begin\; insert into i_table values(5); commit; select 1\; begin\; insert into i_table values(6); rollback; -- commit in implicit-transaction state commits but issues a warning. insert into i_table values(7)\; commit\; insert into i_table values(8)\; select 1/0; -- similarly, rollback aborts but issues a warning. insert into i_table values(9)\; rollback\; select 2; select * from i_table; rollback; -- we are not in a transaction at this point -- implicit transaction block is still a transaction block, for e.g. VACUUM SELECT 1\; VACUUM; SELECT 1\; COMMIT\; VACUUM; -- we disallow savepoint-related commands in implicit-transaction state SELECT 1\; SAVEPOINT sp; SELECT 1\; COMMIT\; SAVEPOINT sp; ROLLBACK TO SAVEPOINT sp\; SELECT 2; SELECT 2\; RELEASE SAVEPOINT sp\; SELECT 3; -- but this is OK, because the BEGIN converts it to a regular xact SELECT 1\; BEGIN\; SAVEPOINT sp\; ROLLBACK TO SAVEPOINT sp\; COMMIT; -- Test for successful cleanup of an aborted transaction at session exit. -- THIS MUST BE THE LAST TEST IN THIS FILE. begin; select 1/0; rollback to X; -- DO NOT ADD ANYTHING HERE. pgFormatter-4.2/t/pg-test-files/sql/triggers.sql000066400000000000000000002200501361326045100217450ustar00rootroot00000000000000-- -- TRIGGERS -- create table pkeys (pkey1 int4 not null, pkey2 text not null); create table fkeys (fkey1 int4, fkey2 text, fkey3 int); create table fkeys2 (fkey21 int4, fkey22 text, pkey23 int not null); create index fkeys_i on fkeys (fkey1, fkey2); create index fkeys2_i on fkeys2 (fkey21, fkey22); create index fkeys2p_i on fkeys2 (pkey23); insert into pkeys values (10, '1'); insert into pkeys values (20, '2'); insert into pkeys values (30, '3'); insert into pkeys values (40, '4'); insert into pkeys values (50, '5'); insert into pkeys values (60, '6'); create unique index pkeys_i on pkeys (pkey1, pkey2); -- -- For fkeys: -- (fkey1, fkey2) --> pkeys (pkey1, pkey2) -- (fkey3) --> fkeys2 (pkey23) -- create trigger check_fkeys_pkey_exist before insert or update on fkeys for each row execute function check_primary_key ('fkey1', 'fkey2', 'pkeys', 'pkey1', 'pkey2'); create trigger check_fkeys_pkey2_exist before insert or update on fkeys for each row execute function check_primary_key ('fkey3', 'fkeys2', 'pkey23'); -- -- For fkeys2: -- (fkey21, fkey22) --> pkeys (pkey1, pkey2) -- create trigger check_fkeys2_pkey_exist before insert or update on fkeys2 for each row execute procedure check_primary_key ('fkey21', 'fkey22', 'pkeys', 'pkey1', 'pkey2'); -- Test comments COMMENT ON TRIGGER check_fkeys2_pkey_bad ON fkeys2 IS 'wrong'; COMMENT ON TRIGGER check_fkeys2_pkey_exist ON fkeys2 IS 'right'; COMMENT ON TRIGGER check_fkeys2_pkey_exist ON fkeys2 IS NULL; -- -- For pkeys: -- ON DELETE/UPDATE (pkey1, pkey2) CASCADE: -- fkeys (fkey1, fkey2) and fkeys2 (fkey21, fkey22) -- create trigger check_pkeys_fkey_cascade before delete or update on pkeys for each row execute procedure check_foreign_key (2, 'cascade', 'pkey1', 'pkey2', 'fkeys', 'fkey1', 'fkey2', 'fkeys2', 'fkey21', 'fkey22'); -- -- For fkeys2: -- ON DELETE/UPDATE (pkey23) RESTRICT: -- fkeys (fkey3) -- create trigger check_fkeys2_fkey_restrict before delete or update on fkeys2 for each row execute procedure check_foreign_key (1, 'restrict', 'pkey23', 'fkeys', 'fkey3'); insert into fkeys2 values (10, '1', 1); insert into fkeys2 values (30, '3', 2); insert into fkeys2 values (40, '4', 5); insert into fkeys2 values (50, '5', 3); -- no key in pkeys insert into fkeys2 values (70, '5', 3); insert into fkeys values (10, '1', 2); insert into fkeys values (30, '3', 3); insert into fkeys values (40, '4', 2); insert into fkeys values (50, '5', 2); -- no key in pkeys insert into fkeys values (70, '5', 1); -- no key in fkeys2 insert into fkeys values (60, '6', 4); delete from pkeys where pkey1 = 30 and pkey2 = '3'; delete from pkeys where pkey1 = 40 and pkey2 = '4'; update pkeys set pkey1 = 7, pkey2 = '70' where pkey1 = 50 and pkey2 = '5'; update pkeys set pkey1 = 7, pkey2 = '70' where pkey1 = 10 and pkey2 = '1'; SELECT trigger_name, event_manipulation, event_object_schema, event_object_table, action_order, action_condition, action_orientation, action_timing, action_reference_old_table, action_reference_new_table FROM information_schema.triggers WHERE event_object_table in ('pkeys', 'fkeys', 'fkeys2') ORDER BY trigger_name COLLATE "C", 2; DROP TABLE pkeys; DROP TABLE fkeys; DROP TABLE fkeys2; -- Check behavior when trigger returns unmodified trigtuple create table trigtest (f1 int, f2 text); create trigger trigger_return_old before insert or delete or update on trigtest for each row execute procedure trigger_return_old(); insert into trigtest values(1, 'foo'); select * from trigtest; update trigtest set f2 = f2 || 'bar'; select * from trigtest; delete from trigtest; select * from trigtest; drop table trigtest; create sequence ttdummy_seq increment 10 start 0 minvalue 0; create table tttest ( price_id int4, price_val int4, price_on int4, price_off int4 default 999999 ); create trigger ttdummy before delete or update on tttest for each row execute procedure ttdummy (price_on, price_off); create trigger ttserial before insert or update on tttest for each row execute procedure autoinc (price_on, ttdummy_seq); insert into tttest values (1, 1, null); insert into tttest values (2, 2, null); insert into tttest values (3, 3, 0); select * from tttest; delete from tttest where price_id = 2; select * from tttest; -- what do we see ? -- get current prices select * from tttest where price_off = 999999; -- change price for price_id == 3 update tttest set price_val = 30 where price_id = 3; select * from tttest; -- now we want to change pric_id in ALL tuples -- this gets us not what we need update tttest set price_id = 5 where price_id = 3; select * from tttest; -- restore data as before last update: select set_ttdummy(0); delete from tttest where price_id = 5; update tttest set price_off = 999999 where price_val = 30; select * from tttest; -- and try change price_id now! update tttest set price_id = 5 where price_id = 3; select * from tttest; -- isn't it what we need ? select set_ttdummy(1); -- we want to correct some "date" update tttest set price_on = -1 where price_id = 1; -- but this doesn't work -- try in this way select set_ttdummy(0); update tttest set price_on = -1 where price_id = 1; select * from tttest; -- isn't it what we need ? -- get price for price_id == 5 as it was @ "date" 35 select * from tttest where price_on <= 35 and price_off > 35 and price_id = 5; drop table tttest; drop sequence ttdummy_seq; -- -- tests for per-statement triggers -- CREATE TABLE log_table (tstamp timestamp default timeofday()::timestamp); CREATE TABLE main_table (a int unique, b int); CREATE FUNCTION trigger_func() RETURNS trigger LANGUAGE plpgsql AS ' BEGIN RAISE NOTICE ''trigger_func(%) called: action = %, when = %, level = %'', TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL; RETURN NULL; END;'; CREATE TRIGGER before_ins_stmt_trig BEFORE INSERT ON main_table FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func('before_ins_stmt'); CREATE TRIGGER after_ins_stmt_trig AFTER INSERT ON main_table FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func('after_ins_stmt'); -- -- if neither 'FOR EACH ROW' nor 'FOR EACH STATEMENT' was specified, -- CREATE TRIGGER should default to 'FOR EACH STATEMENT' -- CREATE TRIGGER after_upd_stmt_trig AFTER UPDATE ON main_table EXECUTE PROCEDURE trigger_func('after_upd_stmt'); -- Both insert and update statement level triggers (before and after) should -- fire. Doesn't fire UPDATE before trigger, but only because one isn't -- defined. INSERT INTO main_table (a, b) VALUES (5, 10) ON CONFLICT (a) DO UPDATE SET b = EXCLUDED.b; CREATE TRIGGER after_upd_row_trig AFTER UPDATE ON main_table FOR EACH ROW EXECUTE PROCEDURE trigger_func('after_upd_row'); INSERT INTO main_table DEFAULT VALUES; UPDATE main_table SET a = a + 1 WHERE b < 30; -- UPDATE that effects zero rows should still call per-statement trigger UPDATE main_table SET a = a + 2 WHERE b > 100; -- constraint now unneeded ALTER TABLE main_table DROP CONSTRAINT main_table_a_key; SELECT * FROM main_table ORDER BY a, b; -- -- test triggers with WHEN clause -- CREATE TRIGGER modified_a BEFORE UPDATE OF a ON main_table FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE trigger_func('modified_a'); CREATE TRIGGER modified_any BEFORE UPDATE OF a ON main_table FOR EACH ROW WHEN (OLD.* IS DISTINCT FROM NEW.*) EXECUTE PROCEDURE trigger_func('modified_any'); CREATE TRIGGER insert_a AFTER INSERT ON main_table FOR EACH ROW WHEN (NEW.a = 123) EXECUTE PROCEDURE trigger_func('insert_a'); CREATE TRIGGER delete_a AFTER DELETE ON main_table FOR EACH ROW WHEN (OLD.a = 123) EXECUTE PROCEDURE trigger_func('delete_a'); CREATE TRIGGER insert_when BEFORE INSERT ON main_table FOR EACH STATEMENT WHEN (true) EXECUTE PROCEDURE trigger_func('insert_when'); CREATE TRIGGER delete_when AFTER DELETE ON main_table FOR EACH STATEMENT WHEN (true) EXECUTE PROCEDURE trigger_func('delete_when'); SELECT trigger_name, event_manipulation, event_object_schema, event_object_table, action_order, action_condition, action_orientation, action_timing, action_reference_old_table, action_reference_new_table FROM information_schema.triggers WHERE event_object_table IN ('main_table') ORDER BY trigger_name COLLATE "C", 2; INSERT INTO main_table (a) VALUES (123), (456); DELETE FROM main_table WHERE a IN (123, 456); UPDATE main_table SET a = 50, b = 60; SELECT * FROM main_table ORDER BY a, b; SELECT pg_get_triggerdef(oid, true) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'modified_a'; SELECT pg_get_triggerdef(oid, false) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'modified_a'; SELECT pg_get_triggerdef(oid, true) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'modified_any'; -- Test RENAME TRIGGER ALTER TRIGGER modified_a ON main_table RENAME TO modified_modified_a; SELECT count(*) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'modified_a'; SELECT count(*) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'modified_modified_a'; DROP TRIGGER modified_modified_a ON main_table; DROP TRIGGER modified_any ON main_table; DROP TRIGGER insert_a ON main_table; DROP TRIGGER delete_a ON main_table; DROP TRIGGER insert_when ON main_table; DROP TRIGGER delete_when ON main_table; -- Test WHEN condition accessing system columns. create table table_with_oids(a int); insert into table_with_oids values (1); create trigger oid_unchanged_trig after update on table_with_oids for each row when (new.tableoid = old.tableoid AND new.tableoid <> 0) execute procedure trigger_func('after_upd_oid_unchanged'); update table_with_oids set a = a + 1; drop table table_with_oids; -- Test column-level triggers DROP TRIGGER after_upd_row_trig ON main_table; CREATE TRIGGER before_upd_a_row_trig BEFORE UPDATE OF a ON main_table FOR EACH ROW EXECUTE PROCEDURE trigger_func('before_upd_a_row'); CREATE TRIGGER after_upd_b_row_trig AFTER UPDATE OF b ON main_table FOR EACH ROW EXECUTE PROCEDURE trigger_func('after_upd_b_row'); CREATE TRIGGER after_upd_a_b_row_trig AFTER UPDATE OF a, b ON main_table FOR EACH ROW EXECUTE PROCEDURE trigger_func('after_upd_a_b_row'); CREATE TRIGGER before_upd_a_stmt_trig BEFORE UPDATE OF a ON main_table FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func('before_upd_a_stmt'); CREATE TRIGGER after_upd_b_stmt_trig AFTER UPDATE OF b ON main_table FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func('after_upd_b_stmt'); SELECT pg_get_triggerdef(oid) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'after_upd_a_b_row_trig'; UPDATE main_table SET a = 50; UPDATE main_table SET b = 10; -- -- Test case for bug with BEFORE trigger followed by AFTER trigger with WHEN -- CREATE TABLE some_t (some_col boolean NOT NULL); CREATE FUNCTION dummy_update_func() RETURNS trigger AS $$ BEGIN RAISE NOTICE 'dummy_update_func(%) called: action = %, old = %, new = %', TG_ARGV[0], TG_OP, OLD, NEW; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER some_trig_before BEFORE UPDATE ON some_t FOR EACH ROW EXECUTE PROCEDURE dummy_update_func('before'); CREATE TRIGGER some_trig_aftera AFTER UPDATE ON some_t FOR EACH ROW WHEN (NOT OLD.some_col AND NEW.some_col) EXECUTE PROCEDURE dummy_update_func('aftera'); CREATE TRIGGER some_trig_afterb AFTER UPDATE ON some_t FOR EACH ROW WHEN (NOT NEW.some_col) EXECUTE PROCEDURE dummy_update_func('afterb'); INSERT INTO some_t VALUES (TRUE); UPDATE some_t SET some_col = TRUE; UPDATE some_t SET some_col = FALSE; UPDATE some_t SET some_col = TRUE; DROP TABLE some_t; -- bogus cases CREATE TRIGGER error_upd_and_col BEFORE UPDATE OR UPDATE OF a ON main_table FOR EACH ROW EXECUTE PROCEDURE trigger_func('error_upd_and_col'); CREATE TRIGGER error_upd_a_a BEFORE UPDATE OF a, a ON main_table FOR EACH ROW EXECUTE PROCEDURE trigger_func('error_upd_a_a'); CREATE TRIGGER error_ins_a BEFORE INSERT OF a ON main_table FOR EACH ROW EXECUTE PROCEDURE trigger_func('error_ins_a'); CREATE TRIGGER error_ins_when BEFORE INSERT OR UPDATE ON main_table FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE trigger_func('error_ins_old'); CREATE TRIGGER error_del_when BEFORE DELETE OR UPDATE ON main_table FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE trigger_func('error_del_new'); CREATE TRIGGER error_del_when BEFORE INSERT OR UPDATE ON main_table FOR EACH ROW WHEN (NEW.tableoid <> 0) EXECUTE PROCEDURE trigger_func('error_when_sys_column'); CREATE TRIGGER error_stmt_when BEFORE UPDATE OF a ON main_table FOR EACH STATEMENT WHEN (OLD.* IS DISTINCT FROM NEW.*) EXECUTE PROCEDURE trigger_func('error_stmt_when'); -- check dependency restrictions ALTER TABLE main_table DROP COLUMN b; -- this should succeed, but we'll roll it back to keep the triggers around begin; DROP TRIGGER after_upd_a_b_row_trig ON main_table; DROP TRIGGER after_upd_b_row_trig ON main_table; DROP TRIGGER after_upd_b_stmt_trig ON main_table; ALTER TABLE main_table DROP COLUMN b; rollback; -- Test enable/disable triggers create table trigtest (i serial primary key); -- test that disabling RI triggers works create table trigtest2 (i int references trigtest(i) on delete cascade); create function trigtest() returns trigger as $$ begin raise notice '% % % %', TG_RELNAME, TG_OP, TG_WHEN, TG_LEVEL; return new; end;$$ language plpgsql; create trigger trigtest_b_row_tg before insert or update or delete on trigtest for each row execute procedure trigtest(); create trigger trigtest_a_row_tg after insert or update or delete on trigtest for each row execute procedure trigtest(); create trigger trigtest_b_stmt_tg before insert or update or delete on trigtest for each statement execute procedure trigtest(); create trigger trigtest_a_stmt_tg after insert or update or delete on trigtest for each statement execute procedure trigtest(); insert into trigtest default values; alter table trigtest disable trigger trigtest_b_row_tg; insert into trigtest default values; alter table trigtest disable trigger user; insert into trigtest default values; alter table trigtest enable trigger trigtest_a_stmt_tg; insert into trigtest default values; set session_replication_role = replica; insert into trigtest default values; -- does not trigger alter table trigtest enable always trigger trigtest_a_stmt_tg; insert into trigtest default values; -- now it does reset session_replication_role; insert into trigtest2 values(1); insert into trigtest2 values(2); delete from trigtest where i=2; select * from trigtest2; alter table trigtest disable trigger all; delete from trigtest where i=1; select * from trigtest2; -- ensure we still insert, even when all triggers are disabled insert into trigtest default values; select * from trigtest; drop table trigtest2; drop table trigtest; -- dump trigger data CREATE TABLE trigger_test ( i int, v varchar ); CREATE OR REPLACE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpgsql AS $$ declare argstr text; relid text; begin relid := TG_relid::regclass; -- plpgsql can't discover its trigger data in a hash like perl and python -- can, or by a sort of reflection like tcl can, -- so we have to hard code the names. raise NOTICE 'TG_NAME: %', TG_name; raise NOTICE 'TG_WHEN: %', TG_when; raise NOTICE 'TG_LEVEL: %', TG_level; raise NOTICE 'TG_OP: %', TG_op; raise NOTICE 'TG_RELID::regclass: %', relid; raise NOTICE 'TG_RELNAME: %', TG_relname; raise NOTICE 'TG_TABLE_NAME: %', TG_table_name; raise NOTICE 'TG_TABLE_SCHEMA: %', TG_table_schema; raise NOTICE 'TG_NARGS: %', TG_nargs; argstr := '['; for i in 0 .. TG_nargs - 1 loop if i > 0 then argstr := argstr || ', '; end if; argstr := argstr || TG_argv[i]; end loop; argstr := argstr || ']'; raise NOTICE 'TG_ARGV: %', argstr; if TG_OP != 'INSERT' then raise NOTICE 'OLD: %', OLD; end if; if TG_OP != 'DELETE' then raise NOTICE 'NEW: %', NEW; end if; if TG_OP = 'DELETE' then return OLD; else return NEW; end if; end; $$; CREATE TRIGGER show_trigger_data_trig BEFORE INSERT OR UPDATE OR DELETE ON trigger_test FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo'); insert into trigger_test values(1,'insert'); update trigger_test set v = 'update' where i = 1; delete from trigger_test; DROP TRIGGER show_trigger_data_trig on trigger_test; DROP FUNCTION trigger_data(); DROP TABLE trigger_test; -- -- Test use of row comparisons on OLD/NEW -- CREATE TABLE trigger_test (f1 int, f2 text, f3 text); -- this is the obvious (and wrong...) way to compare rows CREATE FUNCTION mytrigger() RETURNS trigger LANGUAGE plpgsql as $$ begin if row(old.*) = row(new.*) then raise notice 'row % not changed', new.f1; else raise notice 'row % changed', new.f1; end if; return new; end$$; CREATE TRIGGER t BEFORE UPDATE ON trigger_test FOR EACH ROW EXECUTE PROCEDURE mytrigger(); INSERT INTO trigger_test VALUES(1, 'foo', 'bar'); INSERT INTO trigger_test VALUES(2, 'baz', 'quux'); UPDATE trigger_test SET f3 = 'bar'; UPDATE trigger_test SET f3 = NULL; -- this demonstrates that the above isn't really working as desired: UPDATE trigger_test SET f3 = NULL; -- the right way when considering nulls is CREATE OR REPLACE FUNCTION mytrigger() RETURNS trigger LANGUAGE plpgsql as $$ begin if row(old.*) is distinct from row(new.*) then raise notice 'row % changed', new.f1; else raise notice 'row % not changed', new.f1; end if; return new; end$$; UPDATE trigger_test SET f3 = 'bar'; UPDATE trigger_test SET f3 = NULL; UPDATE trigger_test SET f3 = NULL; DROP TABLE trigger_test; DROP FUNCTION mytrigger(); -- Test snapshot management in serializable transactions involving triggers -- per bug report in 6bc73d4c0910042358k3d1adff3qa36f8df75198ecea@mail.gmail.com CREATE FUNCTION serializable_update_trig() RETURNS trigger LANGUAGE plpgsql AS $$ declare rec record; begin new.description = 'updated in trigger'; return new; end; $$; CREATE TABLE serializable_update_tab ( id int, filler text, description text ); CREATE TRIGGER serializable_update_trig BEFORE UPDATE ON serializable_update_tab FOR EACH ROW EXECUTE PROCEDURE serializable_update_trig(); INSERT INTO serializable_update_tab SELECT a, repeat('xyzxz', 100), 'new' FROM generate_series(1, 50) a; BEGIN; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; UPDATE serializable_update_tab SET description = 'no no', id = 1 WHERE id = 1; COMMIT; SELECT description FROM serializable_update_tab WHERE id = 1; DROP TABLE serializable_update_tab; -- minimal update trigger CREATE TABLE min_updates_test ( f1 text, f2 int, f3 int); INSERT INTO min_updates_test VALUES ('a',1,2),('b','2',null); CREATE TRIGGER z_min_update BEFORE UPDATE ON min_updates_test FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger(); \set QUIET false UPDATE min_updates_test SET f1 = f1; UPDATE min_updates_test SET f2 = f2 + 1; UPDATE min_updates_test SET f3 = 2 WHERE f3 is null; \set QUIET true SELECT * FROM min_updates_test; DROP TABLE min_updates_test; -- -- Test triggers on views -- CREATE VIEW main_view AS SELECT a, b FROM main_table; -- VIEW trigger function CREATE OR REPLACE FUNCTION view_trigger() RETURNS trigger LANGUAGE plpgsql AS $$ declare argstr text := ''; begin for i in 0 .. TG_nargs - 1 loop if i > 0 then argstr := argstr || ', '; end if; argstr := argstr || TG_argv[i]; end loop; raise notice '% % % % (%)', TG_RELNAME, TG_WHEN, TG_OP, TG_LEVEL, argstr; if TG_LEVEL = 'ROW' then if TG_OP = 'INSERT' then raise NOTICE 'NEW: %', NEW; INSERT INTO main_table VALUES (NEW.a, NEW.b); RETURN NEW; end if; if TG_OP = 'UPDATE' then raise NOTICE 'OLD: %, NEW: %', OLD, NEW; UPDATE main_table SET a = NEW.a, b = NEW.b WHERE a = OLD.a AND b = OLD.b; if NOT FOUND then RETURN NULL; end if; RETURN NEW; end if; if TG_OP = 'DELETE' then raise NOTICE 'OLD: %', OLD; DELETE FROM main_table WHERE a = OLD.a AND b = OLD.b; if NOT FOUND then RETURN NULL; end if; RETURN OLD; end if; end if; RETURN NULL; end; $$; -- Before row triggers aren't allowed on views CREATE TRIGGER invalid_trig BEFORE INSERT ON main_view FOR EACH ROW EXECUTE PROCEDURE trigger_func('before_ins_row'); CREATE TRIGGER invalid_trig BEFORE UPDATE ON main_view FOR EACH ROW EXECUTE PROCEDURE trigger_func('before_upd_row'); CREATE TRIGGER invalid_trig BEFORE DELETE ON main_view FOR EACH ROW EXECUTE PROCEDURE trigger_func('before_del_row'); -- After row triggers aren't allowed on views CREATE TRIGGER invalid_trig AFTER INSERT ON main_view FOR EACH ROW EXECUTE PROCEDURE trigger_func('before_ins_row'); CREATE TRIGGER invalid_trig AFTER UPDATE ON main_view FOR EACH ROW EXECUTE PROCEDURE trigger_func('before_upd_row'); CREATE TRIGGER invalid_trig AFTER DELETE ON main_view FOR EACH ROW EXECUTE PROCEDURE trigger_func('before_del_row'); -- Truncate triggers aren't allowed on views CREATE TRIGGER invalid_trig BEFORE TRUNCATE ON main_view EXECUTE PROCEDURE trigger_func('before_tru_row'); CREATE TRIGGER invalid_trig AFTER TRUNCATE ON main_view EXECUTE PROCEDURE trigger_func('before_tru_row'); -- INSTEAD OF triggers aren't allowed on tables CREATE TRIGGER invalid_trig INSTEAD OF INSERT ON main_table FOR EACH ROW EXECUTE PROCEDURE view_trigger('instead_of_ins'); CREATE TRIGGER invalid_trig INSTEAD OF UPDATE ON main_table FOR EACH ROW EXECUTE PROCEDURE view_trigger('instead_of_upd'); CREATE TRIGGER invalid_trig INSTEAD OF DELETE ON main_table FOR EACH ROW EXECUTE PROCEDURE view_trigger('instead_of_del'); -- Don't support WHEN clauses with INSTEAD OF triggers CREATE TRIGGER invalid_trig INSTEAD OF UPDATE ON main_view FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE view_trigger('instead_of_upd'); -- Don't support column-level INSTEAD OF triggers CREATE TRIGGER invalid_trig INSTEAD OF UPDATE OF a ON main_view FOR EACH ROW EXECUTE PROCEDURE view_trigger('instead_of_upd'); -- Don't support statement-level INSTEAD OF triggers CREATE TRIGGER invalid_trig INSTEAD OF UPDATE ON main_view EXECUTE PROCEDURE view_trigger('instead_of_upd'); -- Valid INSTEAD OF triggers CREATE TRIGGER instead_of_insert_trig INSTEAD OF INSERT ON main_view FOR EACH ROW EXECUTE PROCEDURE view_trigger('instead_of_ins'); CREATE TRIGGER instead_of_update_trig INSTEAD OF UPDATE ON main_view FOR EACH ROW EXECUTE PROCEDURE view_trigger('instead_of_upd'); CREATE TRIGGER instead_of_delete_trig INSTEAD OF DELETE ON main_view FOR EACH ROW EXECUTE PROCEDURE view_trigger('instead_of_del'); -- Valid BEFORE statement VIEW triggers CREATE TRIGGER before_ins_stmt_trig BEFORE INSERT ON main_view FOR EACH STATEMENT EXECUTE PROCEDURE view_trigger('before_view_ins_stmt'); CREATE TRIGGER before_upd_stmt_trig BEFORE UPDATE ON main_view FOR EACH STATEMENT EXECUTE PROCEDURE view_trigger('before_view_upd_stmt'); CREATE TRIGGER before_del_stmt_trig BEFORE DELETE ON main_view FOR EACH STATEMENT EXECUTE PROCEDURE view_trigger('before_view_del_stmt'); -- Valid AFTER statement VIEW triggers CREATE TRIGGER after_ins_stmt_trig AFTER INSERT ON main_view FOR EACH STATEMENT EXECUTE PROCEDURE view_trigger('after_view_ins_stmt'); CREATE TRIGGER after_upd_stmt_trig AFTER UPDATE ON main_view FOR EACH STATEMENT EXECUTE PROCEDURE view_trigger('after_view_upd_stmt'); CREATE TRIGGER after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE PROCEDURE view_trigger('after_view_del_stmt'); \set QUIET false -- Insert into view using trigger INSERT INTO main_view VALUES (20, 30); INSERT INTO main_view VALUES (21, 31) RETURNING a, b; -- Table trigger will prevent updates UPDATE main_view SET b = 31 WHERE a = 20; UPDATE main_view SET b = 32 WHERE a = 21 AND b = 31 RETURNING a, b; -- Remove table trigger to allow updates DROP TRIGGER before_upd_a_row_trig ON main_table; UPDATE main_view SET b = 31 WHERE a = 20; UPDATE main_view SET b = 32 WHERE a = 21 AND b = 31 RETURNING a, b; -- Before and after stmt triggers should fire even when no rows are affected UPDATE main_view SET b = 0 WHERE false; -- Delete from view using trigger DELETE FROM main_view WHERE a IN (20,21); DELETE FROM main_view WHERE a = 31 RETURNING a, b; \set QUIET true -- Describe view should list triggers \d main_view -- Test dropping view triggers DROP TRIGGER instead_of_insert_trig ON main_view; DROP TRIGGER instead_of_delete_trig ON main_view; \d+ main_view DROP VIEW main_view; -- -- Test triggers on a join view -- CREATE TABLE country_table ( country_id serial primary key, country_name text unique not null, continent text not null ); INSERT INTO country_table (country_name, continent) VALUES ('Japan', 'Asia'), ('UK', 'Europe'), ('USA', 'North America') RETURNING *; CREATE TABLE city_table ( city_id serial primary key, city_name text not null, population bigint, country_id int references country_table ); CREATE VIEW city_view AS SELECT city_id, city_name, population, country_name, continent FROM city_table ci LEFT JOIN country_table co ON co.country_id = ci.country_id; CREATE FUNCTION city_insert() RETURNS trigger LANGUAGE plpgsql AS $$ declare ctry_id int; begin if NEW.country_name IS NOT NULL then SELECT country_id, continent INTO ctry_id, NEW.continent FROM country_table WHERE country_name = NEW.country_name; if NOT FOUND then raise exception 'No such country: "%"', NEW.country_name; end if; else NEW.continent := NULL; end if; if NEW.city_id IS NOT NULL then INSERT INTO city_table VALUES(NEW.city_id, NEW.city_name, NEW.population, ctry_id); else INSERT INTO city_table(city_name, population, country_id) VALUES(NEW.city_name, NEW.population, ctry_id) RETURNING city_id INTO NEW.city_id; end if; RETURN NEW; end; $$; CREATE TRIGGER city_insert_trig INSTEAD OF INSERT ON city_view FOR EACH ROW EXECUTE PROCEDURE city_insert(); CREATE FUNCTION city_delete() RETURNS trigger LANGUAGE plpgsql AS $$ begin DELETE FROM city_table WHERE city_id = OLD.city_id; if NOT FOUND then RETURN NULL; end if; RETURN OLD; end; $$; CREATE TRIGGER city_delete_trig INSTEAD OF DELETE ON city_view FOR EACH ROW EXECUTE PROCEDURE city_delete(); CREATE FUNCTION city_update() RETURNS trigger LANGUAGE plpgsql AS $$ declare ctry_id int; begin if NEW.country_name IS DISTINCT FROM OLD.country_name then SELECT country_id, continent INTO ctry_id, NEW.continent FROM country_table WHERE country_name = NEW.country_name; if NOT FOUND then raise exception 'No such country: "%"', NEW.country_name; end if; UPDATE city_table SET city_name = NEW.city_name, population = NEW.population, country_id = ctry_id WHERE city_id = OLD.city_id; else UPDATE city_table SET city_name = NEW.city_name, population = NEW.population WHERE city_id = OLD.city_id; NEW.continent := OLD.continent; end if; if NOT FOUND then RETURN NULL; end if; RETURN NEW; end; $$; CREATE TRIGGER city_update_trig INSTEAD OF UPDATE ON city_view FOR EACH ROW EXECUTE PROCEDURE city_update(); \set QUIET false -- INSERT .. RETURNING INSERT INTO city_view(city_name) VALUES('Tokyo') RETURNING *; INSERT INTO city_view(city_name, population) VALUES('London', 7556900) RETURNING *; INSERT INTO city_view(city_name, country_name) VALUES('Washington DC', 'USA') RETURNING *; INSERT INTO city_view(city_id, city_name) VALUES(123456, 'New York') RETURNING *; INSERT INTO city_view VALUES(234567, 'Birmingham', 1016800, 'UK', 'EU') RETURNING *; -- UPDATE .. RETURNING UPDATE city_view SET country_name = 'Japon' WHERE city_name = 'Tokyo'; -- error UPDATE city_view SET country_name = 'Japan' WHERE city_name = 'Takyo'; -- no match UPDATE city_view SET country_name = 'Japan' WHERE city_name = 'Tokyo' RETURNING *; -- OK UPDATE city_view SET population = 13010279 WHERE city_name = 'Tokyo' RETURNING *; UPDATE city_view SET country_name = 'UK' WHERE city_name = 'New York' RETURNING *; UPDATE city_view SET country_name = 'USA', population = 8391881 WHERE city_name = 'New York' RETURNING *; UPDATE city_view SET continent = 'EU' WHERE continent = 'Europe' RETURNING *; UPDATE city_view v1 SET country_name = v2.country_name FROM city_view v2 WHERE v2.city_name = 'Birmingham' AND v1.city_name = 'London' RETURNING *; -- DELETE .. RETURNING DELETE FROM city_view WHERE city_name = 'Birmingham' RETURNING *; \set QUIET true -- read-only view with WHERE clause CREATE VIEW european_city_view AS SELECT * FROM city_view WHERE continent = 'Europe'; SELECT count(*) FROM european_city_view; CREATE FUNCTION no_op_trig_fn() RETURNS trigger LANGUAGE plpgsql AS 'begin RETURN NULL; end'; CREATE TRIGGER no_op_trig INSTEAD OF INSERT OR UPDATE OR DELETE ON european_city_view FOR EACH ROW EXECUTE PROCEDURE no_op_trig_fn(); \set QUIET false INSERT INTO european_city_view VALUES (0, 'x', 10000, 'y', 'z'); UPDATE european_city_view SET population = 10000; DELETE FROM european_city_view; \set QUIET true -- rules bypassing no-op triggers CREATE RULE european_city_insert_rule AS ON INSERT TO european_city_view DO INSTEAD INSERT INTO city_view VALUES (NEW.city_id, NEW.city_name, NEW.population, NEW.country_name, NEW.continent) RETURNING *; CREATE RULE european_city_update_rule AS ON UPDATE TO european_city_view DO INSTEAD UPDATE city_view SET city_name = NEW.city_name, population = NEW.population, country_name = NEW.country_name WHERE city_id = OLD.city_id RETURNING NEW.*; CREATE RULE european_city_delete_rule AS ON DELETE TO european_city_view DO INSTEAD DELETE FROM city_view WHERE city_id = OLD.city_id RETURNING *; \set QUIET false -- INSERT not limited by view's WHERE clause, but UPDATE AND DELETE are INSERT INTO european_city_view(city_name, country_name) VALUES ('Cambridge', 'USA') RETURNING *; UPDATE european_city_view SET country_name = 'UK' WHERE city_name = 'Cambridge'; DELETE FROM european_city_view WHERE city_name = 'Cambridge'; -- UPDATE and DELETE via rule and trigger UPDATE city_view SET country_name = 'UK' WHERE city_name = 'Cambridge' RETURNING *; UPDATE european_city_view SET population = 122800 WHERE city_name = 'Cambridge' RETURNING *; DELETE FROM european_city_view WHERE city_name = 'Cambridge' RETURNING *; -- join UPDATE test UPDATE city_view v SET population = 599657 FROM city_table ci, country_table co WHERE ci.city_name = 'Washington DC' and co.country_name = 'USA' AND v.city_id = ci.city_id AND v.country_name = co.country_name RETURNING co.country_id, v.country_name, v.city_id, v.city_name, v.population; \set QUIET true SELECT * FROM city_view; DROP TABLE city_table CASCADE; DROP TABLE country_table; -- Test pg_trigger_depth() create table depth_a (id int not null primary key); create table depth_b (id int not null primary key); create table depth_c (id int not null primary key); create function depth_a_tf() returns trigger language plpgsql as $$ begin raise notice '%: depth = %', tg_name, pg_trigger_depth(); insert into depth_b values (new.id); raise notice '%: depth = %', tg_name, pg_trigger_depth(); return new; end; $$; create trigger depth_a_tr before insert on depth_a for each row execute procedure depth_a_tf(); create function depth_b_tf() returns trigger language plpgsql as $$ begin raise notice '%: depth = %', tg_name, pg_trigger_depth(); begin execute 'insert into depth_c values (' || new.id::text || ')'; exception when sqlstate 'U9999' then raise notice 'SQLSTATE = U9999: depth = %', pg_trigger_depth(); end; raise notice '%: depth = %', tg_name, pg_trigger_depth(); if new.id = 1 then execute 'insert into depth_c values (' || new.id::text || ')'; end if; return new; end; $$; create trigger depth_b_tr before insert on depth_b for each row execute procedure depth_b_tf(); create function depth_c_tf() returns trigger language plpgsql as $$ begin raise notice '%: depth = %', tg_name, pg_trigger_depth(); if new.id = 1 then raise exception sqlstate 'U9999'; end if; raise notice '%: depth = %', tg_name, pg_trigger_depth(); return new; end; $$; create trigger depth_c_tr before insert on depth_c for each row execute procedure depth_c_tf(); select pg_trigger_depth(); insert into depth_a values (1); select pg_trigger_depth(); insert into depth_a values (2); select pg_trigger_depth(); drop table depth_a, depth_b, depth_c; drop function depth_a_tf(); drop function depth_b_tf(); drop function depth_c_tf(); -- -- Test updates to rows during firing of BEFORE ROW triggers. -- As of 9.2, such cases should be rejected (see bug #6123). -- create temp table parent ( aid int not null primary key, val1 text, val2 text, val3 text, val4 text, bcnt int not null default 0); create temp table child ( bid int not null primary key, aid int not null, val1 text); create function parent_upd_func() returns trigger language plpgsql as $$ begin if old.val1 <> new.val1 then new.val2 = new.val1; delete from child where child.aid = new.aid and child.val1 = new.val1; end if; return new; end; $$; create trigger parent_upd_trig before update on parent for each row execute procedure parent_upd_func(); create function parent_del_func() returns trigger language plpgsql as $$ begin delete from child where aid = old.aid; return old; end; $$; create trigger parent_del_trig before delete on parent for each row execute procedure parent_del_func(); create function child_ins_func() returns trigger language plpgsql as $$ begin update parent set bcnt = bcnt + 1 where aid = new.aid; return new; end; $$; create trigger child_ins_trig after insert on child for each row execute procedure child_ins_func(); create function child_del_func() returns trigger language plpgsql as $$ begin update parent set bcnt = bcnt - 1 where aid = old.aid; return old; end; $$; create trigger child_del_trig after delete on child for each row execute procedure child_del_func(); insert into parent values (1, 'a', 'a', 'a', 'a', 0); insert into child values (10, 1, 'b'); select * from parent; select * from child; update parent set val1 = 'b' where aid = 1; -- should fail select * from parent; select * from child; delete from parent where aid = 1; -- should fail select * from parent; select * from child; -- replace the trigger function with one that restarts the deletion after -- having modified a child create or replace function parent_del_func() returns trigger language plpgsql as $$ begin delete from child where aid = old.aid; if found then delete from parent where aid = old.aid; return null; -- cancel outer deletion end if; return old; end; $$; delete from parent where aid = 1; select * from parent; select * from child; drop table parent, child; drop function parent_upd_func(); drop function parent_del_func(); drop function child_ins_func(); drop function child_del_func(); -- similar case, but with a self-referencing FK so that parent and child -- rows can be affected by a single operation create temp table self_ref_trigger ( id int primary key, parent int references self_ref_trigger, data text, nchildren int not null default 0 ); create function self_ref_trigger_ins_func() returns trigger language plpgsql as $$ begin if new.parent is not null then update self_ref_trigger set nchildren = nchildren + 1 where id = new.parent; end if; return new; end; $$; create trigger self_ref_trigger_ins_trig before insert on self_ref_trigger for each row execute procedure self_ref_trigger_ins_func(); create function self_ref_trigger_del_func() returns trigger language plpgsql as $$ begin if old.parent is not null then update self_ref_trigger set nchildren = nchildren - 1 where id = old.parent; end if; return old; end; $$; create trigger self_ref_trigger_del_trig before delete on self_ref_trigger for each row execute procedure self_ref_trigger_del_func(); insert into self_ref_trigger values (1, null, 'root'); insert into self_ref_trigger values (2, 1, 'root child A'); insert into self_ref_trigger values (3, 1, 'root child B'); insert into self_ref_trigger values (4, 2, 'grandchild 1'); insert into self_ref_trigger values (5, 3, 'grandchild 2'); update self_ref_trigger set data = 'root!' where id = 1; select * from self_ref_trigger; delete from self_ref_trigger; select * from self_ref_trigger; drop table self_ref_trigger; drop function self_ref_trigger_ins_func(); drop function self_ref_trigger_del_func(); -- -- Check that statement triggers work correctly even with all children excluded -- create table stmt_trig_on_empty_upd (a int); create table stmt_trig_on_empty_upd1 () inherits (stmt_trig_on_empty_upd); create function update_stmt_notice() returns trigger as $$ begin raise notice 'updating %', TG_TABLE_NAME; return null; end; $$ language plpgsql; create trigger before_stmt_trigger before update on stmt_trig_on_empty_upd execute procedure update_stmt_notice(); create trigger before_stmt_trigger before update on stmt_trig_on_empty_upd1 execute procedure update_stmt_notice(); -- inherited no-op update update stmt_trig_on_empty_upd set a = a where false returning a+1 as aa; -- simple no-op update update stmt_trig_on_empty_upd1 set a = a where false returning a+1 as aa; drop table stmt_trig_on_empty_upd cascade; drop function update_stmt_notice(); -- -- Check that index creation (or DDL in general) is prohibited in a trigger -- create table trigger_ddl_table ( col1 integer, col2 integer ); create function trigger_ddl_func() returns trigger as $$ begin alter table trigger_ddl_table add primary key (col1); return new; end$$ language plpgsql; create trigger trigger_ddl_func before insert on trigger_ddl_table for each row execute procedure trigger_ddl_func(); insert into trigger_ddl_table values (1, 42); -- fail create or replace function trigger_ddl_func() returns trigger as $$ begin create index on trigger_ddl_table (col2); return new; end$$ language plpgsql; insert into trigger_ddl_table values (1, 42); -- fail drop table trigger_ddl_table; drop function trigger_ddl_func(); -- -- Verify behavior of before and after triggers with INSERT...ON CONFLICT -- DO UPDATE -- create table upsert (key int4 primary key, color text); create function upsert_before_func() returns trigger language plpgsql as $$ begin if (TG_OP = 'UPDATE') then raise warning 'before update (old): %', old.*::text; raise warning 'before update (new): %', new.*::text; elsif (TG_OP = 'INSERT') then raise warning 'before insert (new): %', new.*::text; if new.key % 2 = 0 then new.key := new.key + 1; new.color := new.color || ' trig modified'; raise warning 'before insert (new, modified): %', new.*::text; end if; end if; return new; end; $$; create trigger upsert_before_trig before insert or update on upsert for each row execute procedure upsert_before_func(); create function upsert_after_func() returns trigger language plpgsql as $$ begin if (TG_OP = 'UPDATE') then raise warning 'after update (old): %', old.*::text; raise warning 'after update (new): %', new.*::text; elsif (TG_OP = 'INSERT') then raise warning 'after insert (new): %', new.*::text; end if; return null; end; $$; create trigger upsert_after_trig after insert or update on upsert for each row execute procedure upsert_after_func(); insert into upsert values(1, 'black') on conflict (key) do update set color = 'updated ' || upsert.color; insert into upsert values(2, 'red') on conflict (key) do update set color = 'updated ' || upsert.color; insert into upsert values(3, 'orange') on conflict (key) do update set color = 'updated ' || upsert.color; insert into upsert values(4, 'green') on conflict (key) do update set color = 'updated ' || upsert.color; insert into upsert values(5, 'purple') on conflict (key) do update set color = 'updated ' || upsert.color; insert into upsert values(6, 'white') on conflict (key) do update set color = 'updated ' || upsert.color; insert into upsert values(7, 'pink') on conflict (key) do update set color = 'updated ' || upsert.color; insert into upsert values(8, 'yellow') on conflict (key) do update set color = 'updated ' || upsert.color; select * from upsert; drop table upsert; drop function upsert_before_func(); drop function upsert_after_func(); -- -- Verify that triggers with transition tables are not allowed on -- views -- create table my_table (i int); create view my_view as select * from my_table; create function my_trigger_function() returns trigger as $$ begin end; $$ language plpgsql; create trigger my_trigger after update on my_view referencing old table as old_table for each statement execute procedure my_trigger_function(); drop function my_trigger_function(); drop view my_view; drop table my_table; -- -- Verify cases that are unsupported with partitioned tables -- create table parted_trig (a int) partition by list (a); create function trigger_nothing() returns trigger language plpgsql as $$ begin end; $$; create trigger failed before insert or update or delete on parted_trig for each row execute procedure trigger_nothing(); create trigger failed instead of update on parted_trig for each row execute procedure trigger_nothing(); create trigger failed after update on parted_trig referencing old table as old_table for each row execute procedure trigger_nothing(); drop table parted_trig; -- -- Verify trigger creation for partitioned tables, and drop behavior -- create table trigpart (a int, b int) partition by range (a); create table trigpart1 partition of trigpart for values from (0) to (1000); create trigger trg1 after insert on trigpart for each row execute procedure trigger_nothing(); create table trigpart2 partition of trigpart for values from (1000) to (2000); create table trigpart3 (like trigpart); alter table trigpart attach partition trigpart3 for values from (2000) to (3000); select tgrelid::regclass, tgname, tgfoid::regproc from pg_trigger where tgrelid::regclass::text like 'trigpart%' order by tgrelid::regclass::text; drop trigger trg1 on trigpart1; -- fail drop trigger trg1 on trigpart2; -- fail drop trigger trg1 on trigpart3; -- fail drop table trigpart2; -- ok, trigger should be gone in that partition select tgrelid::regclass, tgname, tgfoid::regproc from pg_trigger where tgrelid::regclass::text like 'trigpart%' order by tgrelid::regclass::text; drop trigger trg1 on trigpart; -- ok, all gone select tgrelid::regclass, tgname, tgfoid::regproc from pg_trigger where tgrelid::regclass::text like 'trigpart%' order by tgrelid::regclass::text; drop table trigpart; drop function trigger_nothing(); -- -- Verify that triggers are fired for partitioned tables -- create table parted_stmt_trig (a int) partition by list (a); create table parted_stmt_trig1 partition of parted_stmt_trig for values in (1); create table parted_stmt_trig2 partition of parted_stmt_trig for values in (2); create table parted2_stmt_trig (a int) partition by list (a); create table parted2_stmt_trig1 partition of parted2_stmt_trig for values in (1); create table parted2_stmt_trig2 partition of parted2_stmt_trig for values in (2); create or replace function trigger_notice() returns trigger as $$ begin raise notice 'trigger % on % % % for %', TG_NAME, TG_TABLE_NAME, TG_WHEN, TG_OP, TG_LEVEL; if TG_LEVEL = 'ROW' then return NEW; end if; return null; end; $$ language plpgsql; -- insert/update/delete statement-level triggers on the parent create trigger trig_ins_before before insert on parted_stmt_trig for each statement execute procedure trigger_notice(); create trigger trig_ins_after after insert on parted_stmt_trig for each statement execute procedure trigger_notice(); create trigger trig_upd_before before update on parted_stmt_trig for each statement execute procedure trigger_notice(); create trigger trig_upd_after after update on parted_stmt_trig for each statement execute procedure trigger_notice(); create trigger trig_del_before before delete on parted_stmt_trig for each statement execute procedure trigger_notice(); create trigger trig_del_after after delete on parted_stmt_trig for each statement execute procedure trigger_notice(); -- insert/update/delete row-level triggers on the parent create trigger trig_ins_after_parent after insert on parted_stmt_trig for each row execute procedure trigger_notice(); create trigger trig_upd_after_parent after update on parted_stmt_trig for each row execute procedure trigger_notice(); create trigger trig_del_after_parent after delete on parted_stmt_trig for each row execute procedure trigger_notice(); -- insert/update/delete row-level triggers on the first partition create trigger trig_ins_before_child before insert on parted_stmt_trig1 for each row execute procedure trigger_notice(); create trigger trig_ins_after_child after insert on parted_stmt_trig1 for each row execute procedure trigger_notice(); create trigger trig_upd_before_child before update on parted_stmt_trig1 for each row execute procedure trigger_notice(); create trigger trig_upd_after_child after update on parted_stmt_trig1 for each row execute procedure trigger_notice(); create trigger trig_del_before_child before delete on parted_stmt_trig1 for each row execute procedure trigger_notice(); create trigger trig_del_after_child after delete on parted_stmt_trig1 for each row execute procedure trigger_notice(); -- insert/update/delete statement-level triggers on the parent create trigger trig_ins_before_3 before insert on parted2_stmt_trig for each statement execute procedure trigger_notice(); create trigger trig_ins_after_3 after insert on parted2_stmt_trig for each statement execute procedure trigger_notice(); create trigger trig_upd_before_3 before update on parted2_stmt_trig for each statement execute procedure trigger_notice(); create trigger trig_upd_after_3 after update on parted2_stmt_trig for each statement execute procedure trigger_notice(); create trigger trig_del_before_3 before delete on parted2_stmt_trig for each statement execute procedure trigger_notice(); create trigger trig_del_after_3 after delete on parted2_stmt_trig for each statement execute procedure trigger_notice(); with ins (a) as ( insert into parted2_stmt_trig values (1), (2) returning a ) insert into parted_stmt_trig select a from ins returning tableoid::regclass, a; with upd as ( update parted2_stmt_trig set a = a ) update parted_stmt_trig set a = a; delete from parted_stmt_trig; -- Disabling a trigger in the parent table should disable children triggers too alter table parted_stmt_trig disable trigger trig_ins_after_parent; insert into parted_stmt_trig values (1); alter table parted_stmt_trig enable trigger trig_ins_after_parent; insert into parted_stmt_trig values (1); drop table parted_stmt_trig, parted2_stmt_trig; -- Verify that triggers fire in alphabetical order create table parted_trig (a int) partition by range (a); create table parted_trig_1 partition of parted_trig for values from (0) to (1000) partition by range (a); create table parted_trig_1_1 partition of parted_trig_1 for values from (0) to (100); create table parted_trig_2 partition of parted_trig for values from (1000) to (2000); create trigger zzz after insert on parted_trig for each row execute procedure trigger_notice(); create trigger mmm after insert on parted_trig_1_1 for each row execute procedure trigger_notice(); create trigger aaa after insert on parted_trig_1 for each row execute procedure trigger_notice(); create trigger bbb after insert on parted_trig for each row execute procedure trigger_notice(); create trigger qqq after insert on parted_trig_1_1 for each row execute procedure trigger_notice(); insert into parted_trig values (50), (1500); drop table parted_trig; -- test irregular partitions (i.e., different column definitions), -- including that the WHEN clause works create function bark(text) returns bool language plpgsql immutable as $$ begin raise notice '% <- woof!', $1; return true; end; $$; create or replace function trigger_notice_ab() returns trigger as $$ begin raise notice 'trigger % on % % % for %: (a,b)=(%,%)', TG_NAME, TG_TABLE_NAME, TG_WHEN, TG_OP, TG_LEVEL, NEW.a, NEW.b; if TG_LEVEL = 'ROW' then return NEW; end if; return null; end; $$ language plpgsql; create table parted_irreg_ancestor (fd text, b text, fd2 int, fd3 int, a int) partition by range (b); alter table parted_irreg_ancestor drop column fd, drop column fd2, drop column fd3; create table parted_irreg (fd int, a int, fd2 int, b text) partition by range (b); alter table parted_irreg drop column fd, drop column fd2; alter table parted_irreg_ancestor attach partition parted_irreg for values from ('aaaa') to ('zzzz'); create table parted1_irreg (b text, fd int, a int); alter table parted1_irreg drop column fd; alter table parted_irreg attach partition parted1_irreg for values from ('aaaa') to ('bbbb'); create trigger parted_trig after insert on parted_irreg for each row execute procedure trigger_notice_ab(); create trigger parted_trig_odd after insert on parted_irreg for each row when (bark(new.b) AND new.a % 2 = 1) execute procedure trigger_notice_ab(); -- we should hear barking for every insert, but parted_trig_odd only emits -- noise for odd values of a. parted_trig does it for all inserts. insert into parted_irreg values (1, 'aardvark'), (2, 'aanimals'); insert into parted1_irreg values ('aardwolf', 2); insert into parted_irreg_ancestor values ('aasvogel', 3); drop table parted_irreg_ancestor; -- -- Constraint triggers and partitioned tables create table parted_constr_ancestor (a int, b text) partition by range (b); create table parted_constr (a int, b text) partition by range (b); alter table parted_constr_ancestor attach partition parted_constr for values from ('aaaa') to ('zzzz'); create table parted1_constr (a int, b text); alter table parted_constr attach partition parted1_constr for values from ('aaaa') to ('bbbb'); create constraint trigger parted_trig after insert on parted_constr_ancestor deferrable for each row execute procedure trigger_notice_ab(); create constraint trigger parted_trig_two after insert on parted_constr deferrable initially deferred for each row when (bark(new.b) AND new.a % 2 = 1) execute procedure trigger_notice_ab(); -- The immediate constraint is fired immediately; the WHEN clause of the -- deferred constraint is also called immediately. The deferred constraint -- is fired at commit time. begin; insert into parted_constr values (1, 'aardvark'); insert into parted1_constr values (2, 'aardwolf'); insert into parted_constr_ancestor values (3, 'aasvogel'); commit; -- The WHEN clause is immediate, and both constraint triggers are fired at -- commit time. begin; set constraints parted_trig deferred; insert into parted_constr values (1, 'aardvark'); insert into parted1_constr values (2, 'aardwolf'), (3, 'aasvogel'); commit; drop table parted_constr_ancestor; drop function bark(text); -- Test that the WHEN clause is set properly to partitions create table parted_trigger (a int, b text) partition by range (a); create table parted_trigger_1 partition of parted_trigger for values from (0) to (1000); create table parted_trigger_2 (drp int, a int, b text); alter table parted_trigger_2 drop column drp; alter table parted_trigger attach partition parted_trigger_2 for values from (1000) to (2000); create trigger parted_trigger after update on parted_trigger for each row when (new.a % 2 = 1 and length(old.b) >= 2) execute procedure trigger_notice_ab(); create table parted_trigger_3 (b text, a int) partition by range (length(b)); create table parted_trigger_3_1 partition of parted_trigger_3 for values from (1) to (3); create table parted_trigger_3_2 partition of parted_trigger_3 for values from (3) to (5); alter table parted_trigger attach partition parted_trigger_3 for values from (2000) to (3000); insert into parted_trigger values (0, 'a'), (1, 'bbb'), (2, 'bcd'), (3, 'c'), (1000, 'c'), (1001, 'ddd'), (1002, 'efg'), (1003, 'f'), (2000, 'e'), (2001, 'fff'), (2002, 'ghi'), (2003, 'h'); update parted_trigger set a = a + 2; -- notice for odd 'a' values, long 'b' values drop table parted_trigger; -- try a constraint trigger, also create table parted_referenced (a int); create table unparted_trigger (a int, b text); -- for comparison purposes create table parted_trigger (a int, b text) partition by range (a); create table parted_trigger_1 partition of parted_trigger for values from (0) to (1000); create table parted_trigger_2 (drp int, a int, b text); alter table parted_trigger_2 drop column drp; alter table parted_trigger attach partition parted_trigger_2 for values from (1000) to (2000); create constraint trigger parted_trigger after update on parted_trigger from parted_referenced for each row execute procedure trigger_notice_ab(); create constraint trigger parted_trigger after update on unparted_trigger from parted_referenced for each row execute procedure trigger_notice_ab(); create table parted_trigger_3 (b text, a int) partition by range (length(b)); create table parted_trigger_3_1 partition of parted_trigger_3 for values from (1) to (3); create table parted_trigger_3_2 partition of parted_trigger_3 for values from (3) to (5); alter table parted_trigger attach partition parted_trigger_3 for values from (2000) to (3000); select tgname, conname, t.tgrelid::regclass, t.tgconstrrelid::regclass, c.conrelid::regclass, c.confrelid::regclass from pg_trigger t join pg_constraint c on (t.tgconstraint = c.oid) where tgname = 'parted_trigger' order by t.tgrelid::regclass::text; drop table parted_referenced, parted_trigger, unparted_trigger; -- verify that the "AFTER UPDATE OF columns" event is propagated correctly create table parted_trigger (a int, b text) partition by range (a); create table parted_trigger_1 partition of parted_trigger for values from (0) to (1000); create table parted_trigger_2 (drp int, a int, b text); alter table parted_trigger_2 drop column drp; alter table parted_trigger attach partition parted_trigger_2 for values from (1000) to (2000); create trigger parted_trigger after update of b on parted_trigger for each row execute procedure trigger_notice_ab(); create table parted_trigger_3 (b text, a int) partition by range (length(b)); create table parted_trigger_3_1 partition of parted_trigger_3 for values from (1) to (4); create table parted_trigger_3_2 partition of parted_trigger_3 for values from (4) to (8); alter table parted_trigger attach partition parted_trigger_3 for values from (2000) to (3000); insert into parted_trigger values (0, 'a'), (1000, 'c'), (2000, 'e'), (2001, 'eeee'); update parted_trigger set a = a + 2; -- no notices here update parted_trigger set b = b || 'b'; -- all triggers should fire drop table parted_trigger; drop function trigger_notice_ab(); -- Make sure we don't end up with unnecessary copies of triggers, when -- cloning them. create table trg_clone (a int) partition by range (a); create table trg_clone1 partition of trg_clone for values from (0) to (1000); alter table trg_clone add constraint uniq unique (a) deferrable; create table trg_clone2 partition of trg_clone for values from (1000) to (2000); create table trg_clone3 partition of trg_clone for values from (2000) to (3000) partition by range (a); create table trg_clone_3_3 partition of trg_clone3 for values from (2000) to (2100); select tgrelid::regclass, count(*) from pg_trigger where tgrelid::regclass in ('trg_clone', 'trg_clone1', 'trg_clone2', 'trg_clone3', 'trg_clone_3_3') group by tgrelid::regclass order by tgrelid::regclass; drop table trg_clone; -- -- Test the interaction between transition tables and both kinds of -- inheritance. We'll dump the contents of the transition tables in a -- format that shows the attribute order, so that we can distinguish -- tuple formats (though not dropped attributes). -- create or replace function dump_insert() returns trigger language plpgsql as $$ begin raise notice 'trigger = %, new table = %', TG_NAME, (select string_agg(new_table::text, ', ' order by a) from new_table); return null; end; $$; create or replace function dump_update() returns trigger language plpgsql as $$ begin raise notice 'trigger = %, old table = %, new table = %', TG_NAME, (select string_agg(old_table::text, ', ' order by a) from old_table), (select string_agg(new_table::text, ', ' order by a) from new_table); return null; end; $$; create or replace function dump_delete() returns trigger language plpgsql as $$ begin raise notice 'trigger = %, old table = %', TG_NAME, (select string_agg(old_table::text, ', ' order by a) from old_table); return null; end; $$; -- -- Verify behavior of statement triggers on partition hierarchy with -- transition tables. Tuples should appear to each trigger in the -- format of the relation the trigger is attached to. -- -- set up a partition hierarchy with some different TupleDescriptors create table parent (a text, b int) partition by list (a); -- a child matching parent create table child1 partition of parent for values in ('AAA'); -- a child with a dropped column create table child2 (x int, a text, b int); alter table child2 drop column x; alter table parent attach partition child2 for values in ('BBB'); -- a child with a different column order create table child3 (b int, a text); alter table parent attach partition child3 for values in ('CCC'); create trigger parent_insert_trig after insert on parent referencing new table as new_table for each statement execute procedure dump_insert(); create trigger parent_update_trig after update on parent referencing old table as old_table new table as new_table for each statement execute procedure dump_update(); create trigger parent_delete_trig after delete on parent referencing old table as old_table for each statement execute procedure dump_delete(); create trigger child1_insert_trig after insert on child1 referencing new table as new_table for each statement execute procedure dump_insert(); create trigger child1_update_trig after update on child1 referencing old table as old_table new table as new_table for each statement execute procedure dump_update(); create trigger child1_delete_trig after delete on child1 referencing old table as old_table for each statement execute procedure dump_delete(); create trigger child2_insert_trig after insert on child2 referencing new table as new_table for each statement execute procedure dump_insert(); create trigger child2_update_trig after update on child2 referencing old table as old_table new table as new_table for each statement execute procedure dump_update(); create trigger child2_delete_trig after delete on child2 referencing old table as old_table for each statement execute procedure dump_delete(); create trigger child3_insert_trig after insert on child3 referencing new table as new_table for each statement execute procedure dump_insert(); create trigger child3_update_trig after update on child3 referencing old table as old_table new table as new_table for each statement execute procedure dump_update(); create trigger child3_delete_trig after delete on child3 referencing old table as old_table for each statement execute procedure dump_delete(); SELECT trigger_name, event_manipulation, event_object_schema, event_object_table, action_order, action_condition, action_orientation, action_timing, action_reference_old_table, action_reference_new_table FROM information_schema.triggers WHERE event_object_table IN ('parent', 'child1', 'child2', 'child3') ORDER BY trigger_name COLLATE "C", 2; -- insert directly into children sees respective child-format tuples insert into child1 values ('AAA', 42); insert into child2 values ('BBB', 42); insert into child3 values (42, 'CCC'); -- update via parent sees parent-format tuples update parent set b = b + 1; -- delete via parent sees parent-format tuples delete from parent; -- insert into parent sees parent-format tuples insert into parent values ('AAA', 42); insert into parent values ('BBB', 42); insert into parent values ('CCC', 42); -- delete from children sees respective child-format tuples delete from child1; delete from child2; delete from child3; -- DML affecting parent sees tuples collected from children even if -- there is no transition table trigger on the children drop trigger child1_insert_trig on child1; drop trigger child1_update_trig on child1; drop trigger child1_delete_trig on child1; drop trigger child2_insert_trig on child2; drop trigger child2_update_trig on child2; drop trigger child2_delete_trig on child2; drop trigger child3_insert_trig on child3; drop trigger child3_update_trig on child3; drop trigger child3_delete_trig on child3; delete from parent; -- insert into parent with a before trigger on a child tuple before -- insertion, and we capture the newly modified row in parent format create or replace function intercept_insert() returns trigger language plpgsql as $$ begin new.b = new.b + 1000; return new; end; $$; create trigger intercept_insert_child3 before insert on child3 for each row execute procedure intercept_insert(); -- insert, parent trigger sees post-modification parent-format tuple insert into parent values ('AAA', 42), ('BBB', 42), ('CCC', 66); drop table child1, child2, child3, parent; drop function intercept_insert(); -- -- Verify prohibition of row triggers with transition triggers on -- partitions -- create table parent (a text, b int) partition by list (a); create table child partition of parent for values in ('AAA'); -- adding row trigger with transition table fails create trigger child_row_trig after insert on child referencing new table as new_table for each row execute procedure dump_insert(); -- detaching it first works alter table parent detach partition child; create trigger child_row_trig after insert on child referencing new table as new_table for each row execute procedure dump_insert(); -- but now we're not allowed to reattach it alter table parent attach partition child for values in ('AAA'); -- drop the trigger, and now we're allowed to attach it again drop trigger child_row_trig on child; alter table parent attach partition child for values in ('AAA'); drop table child, parent; -- -- Verify behavior of statement triggers on (non-partition) -- inheritance hierarchy with transition tables; similar to the -- partition case, except there is no rerouting on insertion and child -- tables can have extra columns -- -- set up inheritance hierarchy with different TupleDescriptors create table parent (a text, b int); -- a child matching parent create table child1 () inherits (parent); -- a child with a different column order create table child2 (b int, a text); alter table child2 inherit parent; -- a child with an extra column create table child3 (c text) inherits (parent); create trigger parent_insert_trig after insert on parent referencing new table as new_table for each statement execute procedure dump_insert(); create trigger parent_update_trig after update on parent referencing old table as old_table new table as new_table for each statement execute procedure dump_update(); create trigger parent_delete_trig after delete on parent referencing old table as old_table for each statement execute procedure dump_delete(); create trigger child1_insert_trig after insert on child1 referencing new table as new_table for each statement execute procedure dump_insert(); create trigger child1_update_trig after update on child1 referencing old table as old_table new table as new_table for each statement execute procedure dump_update(); create trigger child1_delete_trig after delete on child1 referencing old table as old_table for each statement execute procedure dump_delete(); create trigger child2_insert_trig after insert on child2 referencing new table as new_table for each statement execute procedure dump_insert(); create trigger child2_update_trig after update on child2 referencing old table as old_table new table as new_table for each statement execute procedure dump_update(); create trigger child2_delete_trig after delete on child2 referencing old table as old_table for each statement execute procedure dump_delete(); create trigger child3_insert_trig after insert on child3 referencing new table as new_table for each statement execute procedure dump_insert(); create trigger child3_update_trig after update on child3 referencing old table as old_table new table as new_table for each statement execute procedure dump_update(); create trigger child3_delete_trig after delete on child3 referencing old table as old_table for each statement execute procedure dump_delete(); -- insert directly into children sees respective child-format tuples insert into child1 values ('AAA', 42); insert into child2 values (42, 'BBB'); insert into child3 values ('CCC', 42, 'foo'); -- update via parent sees parent-format tuples update parent set b = b + 1; -- delete via parent sees parent-format tuples delete from parent; -- reinsert values into children for next test... insert into child1 values ('AAA', 42); insert into child2 values (42, 'BBB'); insert into child3 values ('CCC', 42, 'foo'); -- delete from children sees respective child-format tuples delete from child1; delete from child2; delete from child3; -- same behavior for copy if there is an index (interesting because rows are -- captured by a different code path in copy.c if there are indexes) create index on parent(b); -- DML affecting parent sees tuples collected from children even if -- there is no transition table trigger on the children drop trigger child1_insert_trig on child1; drop trigger child1_update_trig on child1; drop trigger child1_delete_trig on child1; drop trigger child2_insert_trig on child2; drop trigger child2_update_trig on child2; drop trigger child2_delete_trig on child2; drop trigger child3_insert_trig on child3; drop trigger child3_update_trig on child3; drop trigger child3_delete_trig on child3; delete from parent; drop table child1, child2, child3, parent; -- -- Verify prohibition of row triggers with transition triggers on -- inheritance children -- create table parent (a text, b int); create table child () inherits (parent); -- adding row trigger with transition table fails create trigger child_row_trig after insert on child referencing new table as new_table for each row execute procedure dump_insert(); -- disinheriting it first works alter table child no inherit parent; create trigger child_row_trig after insert on child referencing new table as new_table for each row execute procedure dump_insert(); -- but now we're not allowed to make it inherit anymore alter table child inherit parent; -- drop the trigger, and now we're allowed to make it inherit again drop trigger child_row_trig on child; alter table child inherit parent; drop table child, parent; -- -- Verify behavior of queries with wCTEs, where multiple transition -- tuplestores can be active at the same time because there are -- multiple DML statements that might fire triggers with transition -- tables -- create table table1 (a int); create table table2 (a text); create trigger table1_trig after insert on table1 referencing new table as new_table for each statement execute procedure dump_insert(); create trigger table2_trig after insert on table2 referencing new table as new_table for each statement execute procedure dump_insert(); with wcte as (insert into table1 values (42)) insert into table2 values ('hello world'); with wcte as (insert into table1 values (43)) insert into table1 values (44); select * from table1; select * from table2; drop table table1; drop table table2; -- -- Verify behavior of INSERT ... ON CONFLICT DO UPDATE ... with -- transition tables. -- create table my_table (a int primary key, b text); create trigger my_table_insert_trig after insert on my_table referencing new table as new_table for each statement execute procedure dump_insert(); create trigger my_table_update_trig after update on my_table referencing old table as old_table new table as new_table for each statement execute procedure dump_update(); -- inserts only insert into my_table values (1, 'AAA'), (2, 'BBB') on conflict (a) do update set b = my_table.b || ':' || excluded.b; -- mixture of inserts and updates insert into my_table values (1, 'AAA'), (2, 'BBB'), (3, 'CCC'), (4, 'DDD') on conflict (a) do update set b = my_table.b || ':' || excluded.b; -- updates only insert into my_table values (3, 'CCC'), (4, 'DDD') on conflict (a) do update set b = my_table.b || ':' || excluded.b; -- -- now using a partitioned table -- create table iocdu_tt_parted (a int primary key, b text) partition by list (a); create table iocdu_tt_parted1 partition of iocdu_tt_parted for values in (1); create table iocdu_tt_parted2 partition of iocdu_tt_parted for values in (2); create table iocdu_tt_parted3 partition of iocdu_tt_parted for values in (3); create table iocdu_tt_parted4 partition of iocdu_tt_parted for values in (4); create trigger iocdu_tt_parted_insert_trig after insert on iocdu_tt_parted referencing new table as new_table for each statement execute procedure dump_insert(); create trigger iocdu_tt_parted_update_trig after update on iocdu_tt_parted referencing old table as old_table new table as new_table for each statement execute procedure dump_update(); -- inserts only insert into iocdu_tt_parted values (1, 'AAA'), (2, 'BBB') on conflict (a) do update set b = iocdu_tt_parted.b || ':' || excluded.b; -- mixture of inserts and updates insert into iocdu_tt_parted values (1, 'AAA'), (2, 'BBB'), (3, 'CCC'), (4, 'DDD') on conflict (a) do update set b = iocdu_tt_parted.b || ':' || excluded.b; -- updates only insert into iocdu_tt_parted values (3, 'CCC'), (4, 'DDD') on conflict (a) do update set b = iocdu_tt_parted.b || ':' || excluded.b; drop table iocdu_tt_parted; -- -- Verify that you can't create a trigger with transition tables for -- more than one event. -- create trigger my_table_multievent_trig after insert or update on my_table referencing new table as new_table for each statement execute procedure dump_insert(); -- -- Verify that you can't create a trigger with transition tables with -- a column list. -- create trigger my_table_col_update_trig after update of b on my_table referencing new table as new_table for each statement execute procedure dump_insert(); drop table my_table; -- -- Test firing of triggers with transition tables by foreign key cascades -- create table refd_table (a int primary key, b text); create table trig_table (a int, b text, foreign key (a) references refd_table on update cascade on delete cascade ); create trigger trig_table_before_trig before insert or update or delete on trig_table for each statement execute procedure trigger_func('trig_table'); create trigger trig_table_insert_trig after insert on trig_table referencing new table as new_table for each statement execute procedure dump_insert(); create trigger trig_table_update_trig after update on trig_table referencing old table as old_table new table as new_table for each statement execute procedure dump_update(); create trigger trig_table_delete_trig after delete on trig_table referencing old table as old_table for each statement execute procedure dump_delete(); insert into refd_table values (1, 'one'), (2, 'two'), (3, 'three'); insert into trig_table values (1, 'one a'), (1, 'one b'), (2, 'two a'), (2, 'two b'), (3, 'three a'), (3, 'three b'); update refd_table set a = 11 where b = 'one'; select * from trig_table; delete from refd_table where length(b) = 3; select * from trig_table; drop table refd_table, trig_table; -- -- self-referential FKs are even more fun -- create table self_ref (a int primary key, b int references self_ref(a) on delete cascade); create trigger self_ref_before_trig before delete on self_ref for each statement execute procedure trigger_func('self_ref'); create trigger self_ref_r_trig after delete on self_ref referencing old table as old_table for each row execute procedure dump_delete(); create trigger self_ref_s_trig after delete on self_ref referencing old table as old_table for each statement execute procedure dump_delete(); insert into self_ref values (1, null), (2, 1), (3, 2); delete from self_ref where a = 1; -- without AR trigger, cascaded deletes all end up in one transition table drop trigger self_ref_r_trig on self_ref; insert into self_ref values (1, null), (2, 1), (3, 2), (4, 3); delete from self_ref where a = 1; drop table self_ref; -- cleanup drop function dump_insert(); drop function dump_update(); drop function dump_delete(); pgFormatter-4.2/t/pg-test-files/sql/truncate.sql000066400000000000000000000202641361326045100217510ustar00rootroot00000000000000-- Test basic TRUNCATE functionality. CREATE TABLE truncate_a (col1 integer primary key); INSERT INTO truncate_a VALUES (1); INSERT INTO truncate_a VALUES (2); SELECT * FROM truncate_a; -- Roll truncate back BEGIN; TRUNCATE truncate_a; ROLLBACK; SELECT * FROM truncate_a; -- Commit the truncate this time BEGIN; TRUNCATE truncate_a; COMMIT; SELECT * FROM truncate_a; -- Test foreign-key checks CREATE TABLE trunc_b (a int REFERENCES truncate_a); CREATE TABLE trunc_c (a serial PRIMARY KEY); CREATE TABLE trunc_d (a int REFERENCES trunc_c); CREATE TABLE trunc_e (a int REFERENCES truncate_a, b int REFERENCES trunc_c); TRUNCATE TABLE truncate_a; -- fail TRUNCATE TABLE truncate_a,trunc_b; -- fail TRUNCATE TABLE truncate_a,trunc_b,trunc_e; -- ok TRUNCATE TABLE truncate_a,trunc_e; -- fail TRUNCATE TABLE trunc_c; -- fail TRUNCATE TABLE trunc_c,trunc_d; -- fail TRUNCATE TABLE trunc_c,trunc_d,trunc_e; -- ok TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a; -- fail TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a,trunc_b; -- ok TRUNCATE TABLE truncate_a RESTRICT; -- fail TRUNCATE TABLE truncate_a CASCADE; -- ok -- circular references ALTER TABLE truncate_a ADD FOREIGN KEY (col1) REFERENCES trunc_c; -- Add some data to verify that truncating actually works ... INSERT INTO trunc_c VALUES (1); INSERT INTO truncate_a VALUES (1); INSERT INTO trunc_b VALUES (1); INSERT INTO trunc_d VALUES (1); INSERT INTO trunc_e VALUES (1,1); TRUNCATE TABLE trunc_c; TRUNCATE TABLE trunc_c,truncate_a; TRUNCATE TABLE trunc_c,truncate_a,trunc_d; TRUNCATE TABLE trunc_c,truncate_a,trunc_d,trunc_e; TRUNCATE TABLE trunc_c,truncate_a,trunc_d,trunc_e,trunc_b; -- Verify that truncating did actually work SELECT * FROM truncate_a UNION ALL SELECT * FROM trunc_c UNION ALL SELECT * FROM trunc_b UNION ALL SELECT * FROM trunc_d; SELECT * FROM trunc_e; -- Add data again to test TRUNCATE ... CASCADE INSERT INTO trunc_c VALUES (1); INSERT INTO truncate_a VALUES (1); INSERT INTO trunc_b VALUES (1); INSERT INTO trunc_d VALUES (1); INSERT INTO trunc_e VALUES (1,1); TRUNCATE TABLE trunc_c CASCADE; -- ok SELECT * FROM truncate_a UNION ALL SELECT * FROM trunc_c UNION ALL SELECT * FROM trunc_b UNION ALL SELECT * FROM trunc_d; SELECT * FROM trunc_e; DROP TABLE truncate_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE; -- Test TRUNCATE with inheritance CREATE TABLE trunc_f (col1 integer primary key); INSERT INTO trunc_f VALUES (1); INSERT INTO trunc_f VALUES (2); CREATE TABLE trunc_fa (col2a text) INHERITS (trunc_f); INSERT INTO trunc_fa VALUES (3, 'three'); CREATE TABLE trunc_fb (col2b int) INHERITS (trunc_f); INSERT INTO trunc_fb VALUES (4, 444); CREATE TABLE trunc_faa (col3 text) INHERITS (trunc_fa); INSERT INTO trunc_faa VALUES (5, 'five', 'FIVE'); BEGIN; SELECT * FROM trunc_f; TRUNCATE trunc_f; SELECT * FROM trunc_f; ROLLBACK; BEGIN; SELECT * FROM trunc_f; TRUNCATE ONLY trunc_f; SELECT * FROM trunc_f; ROLLBACK; BEGIN; SELECT * FROM trunc_f; SELECT * FROM trunc_fa; SELECT * FROM trunc_faa; TRUNCATE ONLY trunc_fb, ONLY trunc_fa; SELECT * FROM trunc_f; SELECT * FROM trunc_fa; SELECT * FROM trunc_faa; ROLLBACK; BEGIN; SELECT * FROM trunc_f; SELECT * FROM trunc_fa; SELECT * FROM trunc_faa; TRUNCATE ONLY trunc_fb, trunc_fa; SELECT * FROM trunc_f; SELECT * FROM trunc_fa; SELECT * FROM trunc_faa; ROLLBACK; DROP TABLE trunc_f CASCADE; -- Test ON TRUNCATE triggers CREATE TABLE trunc_trigger_test (f1 int, f2 text, f3 text); CREATE TABLE trunc_trigger_log (tgop text, tglevel text, tgwhen text, tgargv text, tgtable name, rowcount bigint); CREATE FUNCTION trunctrigger() RETURNS trigger as $$ declare c bigint; begin execute 'select count(*) from ' || quote_ident(tg_table_name) into c; insert into trunc_trigger_log values (TG_OP, TG_LEVEL, TG_WHEN, TG_ARGV[0], tg_table_name, c); return null; end; $$ LANGUAGE plpgsql; -- basic before trigger INSERT INTO trunc_trigger_test VALUES(1, 'foo', 'bar'), (2, 'baz', 'quux'); CREATE TRIGGER t BEFORE TRUNCATE ON trunc_trigger_test FOR EACH STATEMENT EXECUTE PROCEDURE trunctrigger('before trigger truncate'); SELECT count(*) as "Row count in test table" FROM trunc_trigger_test; SELECT * FROM trunc_trigger_log; TRUNCATE trunc_trigger_test; SELECT count(*) as "Row count in test table" FROM trunc_trigger_test; SELECT * FROM trunc_trigger_log; DROP TRIGGER t ON trunc_trigger_test; truncate trunc_trigger_log; -- same test with an after trigger INSERT INTO trunc_trigger_test VALUES(1, 'foo', 'bar'), (2, 'baz', 'quux'); CREATE TRIGGER tt AFTER TRUNCATE ON trunc_trigger_test FOR EACH STATEMENT EXECUTE PROCEDURE trunctrigger('after trigger truncate'); SELECT count(*) as "Row count in test table" FROM trunc_trigger_test; SELECT * FROM trunc_trigger_log; TRUNCATE trunc_trigger_test; SELECT count(*) as "Row count in test table" FROM trunc_trigger_test; SELECT * FROM trunc_trigger_log; DROP TABLE trunc_trigger_test; DROP TABLE trunc_trigger_log; DROP FUNCTION trunctrigger(); -- test TRUNCATE ... RESTART IDENTITY CREATE SEQUENCE truncate_a_id1 START WITH 33; CREATE TABLE truncate_a (id serial, id1 integer default nextval('truncate_a_id1')); ALTER SEQUENCE truncate_a_id1 OWNED BY truncate_a.id1; INSERT INTO truncate_a DEFAULT VALUES; INSERT INTO truncate_a DEFAULT VALUES; SELECT * FROM truncate_a; TRUNCATE truncate_a; INSERT INTO truncate_a DEFAULT VALUES; INSERT INTO truncate_a DEFAULT VALUES; SELECT * FROM truncate_a; TRUNCATE truncate_a RESTART IDENTITY; INSERT INTO truncate_a DEFAULT VALUES; INSERT INTO truncate_a DEFAULT VALUES; SELECT * FROM truncate_a; CREATE TABLE truncate_b (id int GENERATED ALWAYS AS IDENTITY (START WITH 44)); INSERT INTO truncate_b DEFAULT VALUES; INSERT INTO truncate_b DEFAULT VALUES; SELECT * FROM truncate_b; TRUNCATE truncate_b; INSERT INTO truncate_b DEFAULT VALUES; INSERT INTO truncate_b DEFAULT VALUES; SELECT * FROM truncate_b; TRUNCATE truncate_b RESTART IDENTITY; INSERT INTO truncate_b DEFAULT VALUES; INSERT INTO truncate_b DEFAULT VALUES; SELECT * FROM truncate_b; -- check rollback of a RESTART IDENTITY operation BEGIN; TRUNCATE truncate_a RESTART IDENTITY; INSERT INTO truncate_a DEFAULT VALUES; SELECT * FROM truncate_a; ROLLBACK; INSERT INTO truncate_a DEFAULT VALUES; INSERT INTO truncate_a DEFAULT VALUES; SELECT * FROM truncate_a; DROP TABLE truncate_a; SELECT nextval('truncate_a_id1'); -- fail, seq should have been dropped -- partitioned table CREATE TABLE truncparted (a int, b char) PARTITION BY LIST (a); -- error, can't truncate a partitioned table TRUNCATE ONLY truncparted; CREATE TABLE truncparted1 PARTITION OF truncparted FOR VALUES IN (1); INSERT INTO truncparted VALUES (1, 'a'); -- error, must truncate partitions TRUNCATE ONLY truncparted; TRUNCATE truncparted; DROP TABLE truncparted; -- foreign key on partitioned table: partition key is referencing column. -- Make sure truncate did execute on all tables CREATE FUNCTION tp_ins_data() RETURNS void LANGUAGE plpgsql AS $$ BEGIN INSERT INTO truncprim VALUES (1), (100), (150); INSERT INTO truncpart VALUES (1), (100), (150); END $$; CREATE FUNCTION tp_chk_data(OUT pktb regclass, OUT pkval int, OUT fktb regclass, OUT fkval int) RETURNS SETOF record LANGUAGE plpgsql AS $$ BEGIN RETURN QUERY SELECT pk.tableoid::regclass, pk.a, fk.tableoid::regclass, fk.a FROM truncprim pk FULL JOIN truncpart fk USING (a) ORDER BY 2, 4; END $$; CREATE TABLE truncprim (a int PRIMARY KEY); CREATE TABLE truncpart (a int REFERENCES truncprim) PARTITION BY RANGE (a); CREATE TABLE truncpart_1 PARTITION OF truncpart FOR VALUES FROM (0) TO (100); CREATE TABLE truncpart_2 PARTITION OF truncpart FOR VALUES FROM (100) TO (200) PARTITION BY RANGE (a); CREATE TABLE truncpart_2_1 PARTITION OF truncpart_2 FOR VALUES FROM (100) TO (150); CREATE TABLE truncpart_2_d PARTITION OF truncpart_2 DEFAULT; TRUNCATE TABLE truncprim; -- should fail select tp_ins_data(); -- should truncate everything TRUNCATE TABLE truncprim, truncpart; select * from tp_chk_data(); select tp_ins_data(); -- should truncate everything TRUNCATE TABLE truncprim CASCADE; SELECT * FROM tp_chk_data(); SELECT tp_ins_data(); -- should truncate all partitions TRUNCATE TABLE truncpart; SELECT * FROM tp_chk_data(); DROP TABLE truncprim, truncpart; DROP FUNCTION tp_ins_data(), tp_chk_data(); pgFormatter-4.2/t/pg-test-files/sql/tsdicts.sql000066400000000000000000000173441361326045100216060ustar00rootroot00000000000000--Test text search dictionaries and configurations -- Test ISpell dictionary with ispell affix file CREATE TEXT SEARCH DICTIONARY ispell ( Template=ispell, DictFile=ispell_sample, AffFile=ispell_sample ); SELECT ts_lexize('ispell', 'skies'); SELECT ts_lexize('ispell', 'bookings'); SELECT ts_lexize('ispell', 'booking'); SELECT ts_lexize('ispell', 'foot'); SELECT ts_lexize('ispell', 'foots'); SELECT ts_lexize('ispell', 'rebookings'); SELECT ts_lexize('ispell', 'rebooking'); SELECT ts_lexize('ispell', 'rebook'); SELECT ts_lexize('ispell', 'unbookings'); SELECT ts_lexize('ispell', 'unbooking'); SELECT ts_lexize('ispell', 'unbook'); SELECT ts_lexize('ispell', 'footklubber'); SELECT ts_lexize('ispell', 'footballklubber'); SELECT ts_lexize('ispell', 'ballyklubber'); SELECT ts_lexize('ispell', 'footballyklubber'); -- Test ISpell dictionary with hunspell affix file CREATE TEXT SEARCH DICTIONARY hunspell ( Template=ispell, DictFile=ispell_sample, AffFile=hunspell_sample ); SELECT ts_lexize('hunspell', 'skies'); SELECT ts_lexize('hunspell', 'bookings'); SELECT ts_lexize('hunspell', 'booking'); SELECT ts_lexize('hunspell', 'foot'); SELECT ts_lexize('hunspell', 'foots'); SELECT ts_lexize('hunspell', 'rebookings'); SELECT ts_lexize('hunspell', 'rebooking'); SELECT ts_lexize('hunspell', 'rebook'); SELECT ts_lexize('hunspell', 'unbookings'); SELECT ts_lexize('hunspell', 'unbooking'); SELECT ts_lexize('hunspell', 'unbook'); SELECT ts_lexize('hunspell', 'footklubber'); SELECT ts_lexize('hunspell', 'footballklubber'); SELECT ts_lexize('hunspell', 'ballyklubber'); SELECT ts_lexize('hunspell', 'footballyklubber'); -- Test ISpell dictionary with hunspell affix file with FLAG long parameter CREATE TEXT SEARCH DICTIONARY hunspell_long ( Template=ispell, DictFile=hunspell_sample_long, AffFile=hunspell_sample_long ); SELECT ts_lexize('hunspell_long', 'skies'); SELECT ts_lexize('hunspell_long', 'bookings'); SELECT ts_lexize('hunspell_long', 'booking'); SELECT ts_lexize('hunspell_long', 'foot'); SELECT ts_lexize('hunspell_long', 'foots'); SELECT ts_lexize('hunspell_long', 'rebookings'); SELECT ts_lexize('hunspell_long', 'rebooking'); SELECT ts_lexize('hunspell_long', 'rebook'); SELECT ts_lexize('hunspell_long', 'unbookings'); SELECT ts_lexize('hunspell_long', 'unbooking'); SELECT ts_lexize('hunspell_long', 'unbook'); SELECT ts_lexize('hunspell_long', 'booked'); SELECT ts_lexize('hunspell_long', 'footklubber'); SELECT ts_lexize('hunspell_long', 'footballklubber'); SELECT ts_lexize('hunspell_long', 'ballyklubber'); SELECT ts_lexize('hunspell_long', 'ballsklubber'); SELECT ts_lexize('hunspell_long', 'footballyklubber'); SELECT ts_lexize('hunspell_long', 'ex-machina'); -- Test ISpell dictionary with hunspell affix file with FLAG num parameter CREATE TEXT SEARCH DICTIONARY hunspell_num ( Template=ispell, DictFile=hunspell_sample_num, AffFile=hunspell_sample_num ); SELECT ts_lexize('hunspell_num', 'skies'); SELECT ts_lexize('hunspell_num', 'sk'); SELECT ts_lexize('hunspell_num', 'bookings'); SELECT ts_lexize('hunspell_num', 'booking'); SELECT ts_lexize('hunspell_num', 'foot'); SELECT ts_lexize('hunspell_num', 'foots'); SELECT ts_lexize('hunspell_num', 'rebookings'); SELECT ts_lexize('hunspell_num', 'rebooking'); SELECT ts_lexize('hunspell_num', 'rebook'); SELECT ts_lexize('hunspell_num', 'unbookings'); SELECT ts_lexize('hunspell_num', 'unbooking'); SELECT ts_lexize('hunspell_num', 'unbook'); SELECT ts_lexize('hunspell_num', 'booked'); SELECT ts_lexize('hunspell_num', 'footklubber'); SELECT ts_lexize('hunspell_num', 'footballklubber'); SELECT ts_lexize('hunspell_num', 'ballyklubber'); SELECT ts_lexize('hunspell_num', 'footballyklubber'); -- Synonym dictionary CREATE TEXT SEARCH DICTIONARY synonym ( Template=synonym, Synonyms=synonym_sample ); SELECT ts_lexize('synonym', 'PoStGrEs'); SELECT ts_lexize('synonym', 'Gogle'); SELECT ts_lexize('synonym', 'indices'); -- Create and simple test thesaurus dictionary -- More tests in configuration checks because ts_lexize() -- cannot pass more than one word to thesaurus. CREATE TEXT SEARCH DICTIONARY thesaurus ( Template=thesaurus, DictFile=thesaurus_sample, Dictionary=english_stem ); SELECT ts_lexize('thesaurus', 'one'); -- Test ispell dictionary in configuration CREATE TEXT SEARCH CONFIGURATION ispell_tst ( COPY=english ); ALTER TEXT SEARCH CONFIGURATION ispell_tst ALTER MAPPING FOR word, numword, asciiword, hword, numhword, asciihword, hword_part, hword_numpart, hword_asciipart WITH ispell, english_stem; SELECT to_tsvector('ispell_tst', 'Booking the skies after rebookings for footballklubber from a foot'); SELECT to_tsquery('ispell_tst', 'footballklubber'); SELECT to_tsquery('ispell_tst', 'footballyklubber:b & rebookings:A & sky'); -- Test ispell dictionary with hunspell affix in configuration CREATE TEXT SEARCH CONFIGURATION hunspell_tst ( COPY=ispell_tst ); ALTER TEXT SEARCH CONFIGURATION hunspell_tst ALTER MAPPING REPLACE ispell WITH hunspell; SELECT to_tsvector('hunspell_tst', 'Booking the skies after rebookings for footballklubber from a foot'); SELECT to_tsquery('hunspell_tst', 'footballklubber'); SELECT to_tsquery('hunspell_tst', 'footballyklubber:b & rebookings:A & sky'); SELECT to_tsquery('hunspell_tst', 'footballyklubber:b <-> sky'); SELECT phraseto_tsquery('hunspell_tst', 'footballyklubber sky'); -- Test ispell dictionary with hunspell affix with FLAG long in configuration ALTER TEXT SEARCH CONFIGURATION hunspell_tst ALTER MAPPING REPLACE hunspell WITH hunspell_long; SELECT to_tsvector('hunspell_tst', 'Booking the skies after rebookings for footballklubber from a foot'); SELECT to_tsquery('hunspell_tst', 'footballklubber'); SELECT to_tsquery('hunspell_tst', 'footballyklubber:b & rebookings:A & sky'); -- Test ispell dictionary with hunspell affix with FLAG num in configuration ALTER TEXT SEARCH CONFIGURATION hunspell_tst ALTER MAPPING REPLACE hunspell_long WITH hunspell_num; SELECT to_tsvector('hunspell_tst', 'Booking the skies after rebookings for footballklubber from a foot'); SELECT to_tsquery('hunspell_tst', 'footballklubber'); SELECT to_tsquery('hunspell_tst', 'footballyklubber:b & rebookings:A & sky'); -- Test synonym dictionary in configuration CREATE TEXT SEARCH CONFIGURATION synonym_tst ( COPY=english ); ALTER TEXT SEARCH CONFIGURATION synonym_tst ALTER MAPPING FOR asciiword, hword_asciipart, asciihword WITH synonym, english_stem; SELECT to_tsvector('synonym_tst', 'Postgresql is often called as postgres or pgsql and pronounced as postgre'); SELECT to_tsvector('synonym_tst', 'Most common mistake is to write Gogle instead of Google'); SELECT to_tsvector('synonym_tst', 'Indexes or indices - Which is right plural form of index?'); SELECT to_tsquery('synonym_tst', 'Index & indices'); -- test thesaurus in configuration -- see thesaurus_sample.ths to understand 'odd' resulting tsvector CREATE TEXT SEARCH CONFIGURATION thesaurus_tst ( COPY=synonym_tst ); ALTER TEXT SEARCH CONFIGURATION thesaurus_tst ALTER MAPPING FOR asciiword, hword_asciipart, asciihword WITH synonym, thesaurus, english_stem; SELECT to_tsvector('thesaurus_tst', 'one postgres one two one two three one'); SELECT to_tsvector('thesaurus_tst', 'Supernovae star is very new star and usually called supernovae (abbreviation SN)'); SELECT to_tsvector('thesaurus_tst', 'Booking tickets is looking like a booking a tickets'); -- invalid: non-lowercase quoted identifiers CREATE TEXT SEARCH DICTIONARY tsdict_case ( Template = ispell, "DictFile" = ispell_sample, "AffFile" = ispell_sample ); pgFormatter-4.2/t/pg-test-files/sql/tsearch.sql000066400000000000000000000643321361326045100215610ustar00rootroot00000000000000-- -- Sanity checks for text search catalogs -- -- NB: we assume the oidjoins test will have caught any dangling links, -- that is OID or REGPROC fields that are not zero and do not match some -- row in the linked-to table. However, if we want to enforce that a link -- field can't be 0, we have to check it here. -- Find unexpected zero link entries SELECT oid, prsname FROM pg_ts_parser WHERE prsnamespace = 0 OR prsstart = 0 OR prstoken = 0 OR prsend = 0 OR -- prsheadline is optional prslextype = 0; SELECT oid, dictname FROM pg_ts_dict WHERE dictnamespace = 0 OR dictowner = 0 OR dicttemplate = 0; SELECT oid, tmplname FROM pg_ts_template WHERE tmplnamespace = 0 OR tmpllexize = 0; -- tmplinit is optional SELECT oid, cfgname FROM pg_ts_config WHERE cfgnamespace = 0 OR cfgowner = 0 OR cfgparser = 0; SELECT mapcfg, maptokentype, mapseqno FROM pg_ts_config_map WHERE mapcfg = 0 OR mapdict = 0; -- Look for pg_ts_config_map entries that aren't one of parser's token types SELECT * FROM ( SELECT oid AS cfgid, (ts_token_type(cfgparser)).tokid AS tokid FROM pg_ts_config ) AS tt RIGHT JOIN pg_ts_config_map AS m ON (tt.cfgid=m.mapcfg AND tt.tokid=m.maptokentype) WHERE tt.cfgid IS NULL OR tt.tokid IS NULL; -- test basic text search behavior without indexes, then with SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*'; SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}'); SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme'; SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme'; create index wowidx on test_tsvector using gist (a); SET enable_seqscan=OFF; SET enable_indexscan=ON; SET enable_bitmapscan=OFF; explain (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*'; SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}'); SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme'; SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme'; SET enable_indexscan=OFF; SET enable_bitmapscan=ON; explain (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*'; SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}'); SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme'; SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme'; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; DROP INDEX wowidx; CREATE INDEX wowidx ON test_tsvector USING gin (a); SET enable_seqscan=OFF; -- GIN only supports bitmapscan, so no need to test plain indexscan explain (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*'; SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}'); SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme'; SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme'; RESET enable_seqscan; INSERT INTO test_tsvector VALUES ('???', 'DFG:1A,2B,6C,10 FGH'); SELECT * FROM ts_stat('SELECT a FROM test_tsvector') ORDER BY ndoc DESC, nentry DESC, word LIMIT 10; SELECT * FROM ts_stat('SELECT a FROM test_tsvector', 'AB') ORDER BY ndoc DESC, nentry DESC, word; --dictionaries and to_tsvector SELECT ts_lexize('english_stem', 'skies'); SELECT ts_lexize('english_stem', 'identity'); SELECT * FROM ts_token_type('default'); SELECT * FROM ts_parse('default', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net teodor@123-stack.net 123_teodor@stack.net 123-teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 /usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 wow < jqw <> qwerty'); SELECT to_tsvector('english', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net teodor@123-stack.net 123_teodor@stack.net 123-teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 /usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 wow < jqw <> qwerty'); SELECT length(to_tsvector('english', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net teodor@123-stack.net 123_teodor@stack.net 123-teodor@stack.net qwe-wer asdf qwer jf sdjk ewr1> ewri2 /usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 wow < jqw <> qwerty')); -- ts_debug SELECT * from ts_debug('english', 'abc&nm1;def©ghiõjkl'); -- check parsing of URLs SELECT * from ts_debug('english', 'http://www.harewoodsolutions.co.uk/press.aspx'); SELECT * from ts_debug('english', 'http://aew.wer0c.ewr/id?ad=qwe&dw'); SELECT * from ts_debug('english', 'http://5aew.werc.ewr:8100/?'); SELECT * from ts_debug('english', '5aew.werc.ewr:8100/?xx'); SELECT token, alias, dictionaries, dictionaries is null as dnull, array_dims(dictionaries) as ddims, lexemes, lexemes is null as lnull, array_dims(lexemes) as ldims from ts_debug('english', 'a title'); -- to_tsquery SELECT to_tsquery('english', 'qwe & sKies '); SELECT to_tsquery('simple', 'qwe & sKies '); SELECT to_tsquery('english', '''the wether'':dc & '' sKies '':BC '); SELECT to_tsquery('english', 'asd&(and|fghj)'); SELECT to_tsquery('english', '(asd&and)|fghj'); SELECT to_tsquery('english', '(asd&!and)|fghj'); SELECT to_tsquery('english', '(the|and&(i&1))&fghj'); SELECT plainto_tsquery('english', 'the and z 1))& fghj'); SELECT plainto_tsquery('english', 'foo bar') && plainto_tsquery('english', 'asd'); SELECT plainto_tsquery('english', 'foo bar') || plainto_tsquery('english', 'asd fg'); SELECT plainto_tsquery('english', 'foo bar') || !!plainto_tsquery('english', 'asd fg'); SELECT plainto_tsquery('english', 'foo bar') && 'asd | fg'; -- Check stop word deletion, a and s are stop-words SELECT to_tsquery('english', '!(a & !b) & c'); SELECT to_tsquery('english', '!(a & !b)'); SELECT to_tsquery('english', '(1 <-> 2) <-> a'); SELECT to_tsquery('english', '(1 <-> a) <-> 2'); SELECT to_tsquery('english', '(a <-> 1) <-> 2'); SELECT to_tsquery('english', 'a <-> (1 <-> 2)'); SELECT to_tsquery('english', '1 <-> (a <-> 2)'); SELECT to_tsquery('english', '1 <-> (2 <-> a)'); SELECT to_tsquery('english', '(1 <-> 2) <3> a'); SELECT to_tsquery('english', '(1 <-> a) <3> 2'); SELECT to_tsquery('english', '(a <-> 1) <3> 2'); SELECT to_tsquery('english', 'a <3> (1 <-> 2)'); SELECT to_tsquery('english', '1 <3> (a <-> 2)'); SELECT to_tsquery('english', '1 <3> (2 <-> a)'); SELECT to_tsquery('english', '(1 <3> 2) <-> a'); SELECT to_tsquery('english', '(1 <3> a) <-> 2'); SELECT to_tsquery('english', '(a <3> 1) <-> 2'); SELECT to_tsquery('english', 'a <-> (1 <3> 2)'); SELECT to_tsquery('english', '1 <-> (a <3> 2)'); SELECT to_tsquery('english', '1 <-> (2 <3> a)'); SELECT to_tsquery('english', '((a <-> 1) <-> 2) <-> s'); SELECT to_tsquery('english', '(2 <-> (a <-> 1)) <-> s'); SELECT to_tsquery('english', '((1 <-> a) <-> 2) <-> s'); SELECT to_tsquery('english', '(2 <-> (1 <-> a)) <-> s'); SELECT to_tsquery('english', 's <-> ((a <-> 1) <-> 2)'); SELECT to_tsquery('english', 's <-> (2 <-> (a <-> 1))'); SELECT to_tsquery('english', 's <-> ((1 <-> a) <-> 2)'); SELECT to_tsquery('english', 's <-> (2 <-> (1 <-> a))'); SELECT to_tsquery('english', '((a <-> 1) <-> s) <-> 2'); SELECT to_tsquery('english', '(s <-> (a <-> 1)) <-> 2'); SELECT to_tsquery('english', '((1 <-> a) <-> s) <-> 2'); SELECT to_tsquery('english', '(s <-> (1 <-> a)) <-> 2'); SELECT to_tsquery('english', '2 <-> ((a <-> 1) <-> s)'); SELECT to_tsquery('english', '2 <-> (s <-> (a <-> 1))'); SELECT to_tsquery('english', '2 <-> ((1 <-> a) <-> s)'); SELECT to_tsquery('english', '2 <-> (s <-> (1 <-> a))'); SELECT to_tsquery('english', 'foo <-> (a <-> (the <-> bar))'); SELECT to_tsquery('english', '((foo <-> a) <-> the) <-> bar'); SELECT to_tsquery('english', 'foo <-> a <-> the <-> bar'); SELECT phraseto_tsquery('english', 'PostgreSQL can be extended by the user in many ways'); SELECT ts_rank_cd(to_tsvector('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) '), to_tsquery('english', 'paint&water')); SELECT ts_rank_cd(to_tsvector('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) '), to_tsquery('english', 'breath&motion&water')); SELECT ts_rank_cd(to_tsvector('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) '), to_tsquery('english', 'ocean')); SELECT ts_rank_cd(to_tsvector('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) '), to_tsquery('english', 'painted <-> Ship')); SELECT ts_rank_cd(strip(to_tsvector('both stripped')), to_tsquery('both & stripped')); SELECT ts_rank_cd(to_tsvector('unstripped') || strip(to_tsvector('stripped')), to_tsquery('unstripped & stripped')); --headline tests SELECT ts_headline('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) ', to_tsquery('english', 'paint&water')); SELECT ts_headline('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) ', to_tsquery('english', 'breath&motion&water')); SELECT ts_headline('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) ', to_tsquery('english', 'ocean')); SELECT ts_headline('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) ', phraseto_tsquery('english', 'painted Ocean')); SELECT ts_headline('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) ', phraseto_tsquery('english', 'idle as a painted Ship')); SELECT ts_headline('english', ' Sea view wow foo bar qq YES   ff-bg ', to_tsquery('english', 'sea&foo'), 'HighlightAll=true'); SELECT ts_headline('simple', '1 2 3 1 3'::text, '1 <-> 3', 'MaxWords=2, MinWords=1'); SELECT ts_headline('simple', '1 2 3 1 3'::text, '1 & 3', 'MaxWords=4, MinWords=1'); SELECT ts_headline('simple', '1 2 3 1 3'::text, '1 <-> 3', 'MaxWords=4, MinWords=1'); --Check if headline fragments work SELECT ts_headline('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) ', to_tsquery('english', 'ocean'), 'MaxFragments=1'); --Check if more than one fragments are displayed SELECT ts_headline('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) ', to_tsquery('english', 'Coleridge & stuck'), 'MaxFragments=2'); --Fragments when there all query words are not in the document SELECT ts_headline('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) ', to_tsquery('english', 'ocean & seahorse'), 'MaxFragments=1'); --FragmentDelimiter option SELECT ts_headline('english', ' Day after day, day after day, We stuck, nor breath nor motion, As idle as a painted Ship Upon a painted Ocean. Water, water, every where And all the boards did shrink; Water, water, every where, Nor any drop to drink. S. T. Coleridge (1772-1834) ', to_tsquery('english', 'Coleridge & stuck'), 'MaxFragments=2,FragmentDelimiter=***'); --Rewrite sub system CREATE TABLE test_tsquery (txtkeyword TEXT, txtsample TEXT); \set ECHO none \set ECHO all ALTER TABLE test_tsquery ADD COLUMN keyword tsquery; UPDATE test_tsquery SET keyword = to_tsquery('english', txtkeyword); ALTER TABLE test_tsquery ADD COLUMN sample tsquery; UPDATE test_tsquery SET sample = to_tsquery('english', txtsample::text); SELECT COUNT(*) FROM test_tsquery WHERE keyword < 'new & york'; SELECT COUNT(*) FROM test_tsquery WHERE keyword <= 'new & york'; SELECT COUNT(*) FROM test_tsquery WHERE keyword = 'new & york'; SELECT COUNT(*) FROM test_tsquery WHERE keyword >= 'new & york'; SELECT COUNT(*) FROM test_tsquery WHERE keyword > 'new & york'; CREATE UNIQUE INDEX bt_tsq ON test_tsquery (keyword); SET enable_seqscan=OFF; SELECT COUNT(*) FROM test_tsquery WHERE keyword < 'new & york'; SELECT COUNT(*) FROM test_tsquery WHERE keyword <= 'new & york'; SELECT COUNT(*) FROM test_tsquery WHERE keyword = 'new & york'; SELECT COUNT(*) FROM test_tsquery WHERE keyword >= 'new & york'; SELECT COUNT(*) FROM test_tsquery WHERE keyword > 'new & york'; RESET enable_seqscan; SELECT ts_rewrite('foo & bar & qq & new & york', 'new & york'::tsquery, 'big & apple | nyc | new & york & city'); SELECT ts_rewrite(ts_rewrite('new & !york ', 'york', '!jersey'), 'jersey', 'mexico'); SELECT ts_rewrite('moscow', 'SELECT keyword, sample FROM test_tsquery'::text ); SELECT ts_rewrite('moscow & hotel', 'SELECT keyword, sample FROM test_tsquery'::text ); SELECT ts_rewrite('bar & new & qq & foo & york', 'SELECT keyword, sample FROM test_tsquery'::text ); SELECT ts_rewrite( 'moscow', 'SELECT keyword, sample FROM test_tsquery'); SELECT ts_rewrite( 'moscow & hotel', 'SELECT keyword, sample FROM test_tsquery'); SELECT ts_rewrite( 'bar & new & qq & foo & york', 'SELECT keyword, sample FROM test_tsquery'); SELECT ts_rewrite('1 & (2 <-> 3)', 'SELECT keyword, sample FROM test_tsquery'::text ); SELECT ts_rewrite('1 & (2 <2> 3)', 'SELECT keyword, sample FROM test_tsquery'::text ); SELECT ts_rewrite('5 <-> (1 & (2 <-> 3))', 'SELECT keyword, sample FROM test_tsquery'::text ); SELECT ts_rewrite('5 <-> (6 | 8)', 'SELECT keyword, sample FROM test_tsquery'::text ); -- Check empty substitution SELECT ts_rewrite(to_tsquery('5 & (6 | 5)'), to_tsquery('5'), to_tsquery('')); SELECT ts_rewrite(to_tsquery('!5'), to_tsquery('5'), to_tsquery('')); SELECT keyword FROM test_tsquery WHERE keyword @> 'new'; SELECT keyword FROM test_tsquery WHERE keyword @> 'moscow'; SELECT keyword FROM test_tsquery WHERE keyword <@ 'new'; SELECT keyword FROM test_tsquery WHERE keyword <@ 'moscow'; SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow') AS query; SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow & hotel') AS query; SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query; SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow') AS query; SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow & hotel') AS query; SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query; CREATE INDEX qq ON test_tsquery USING gist (keyword tsquery_ops); SET enable_seqscan=OFF; SELECT keyword FROM test_tsquery WHERE keyword @> 'new'; SELECT keyword FROM test_tsquery WHERE keyword @> 'moscow'; SELECT keyword FROM test_tsquery WHERE keyword <@ 'new'; SELECT keyword FROM test_tsquery WHERE keyword <@ 'moscow'; SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow') AS query; SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow & hotel') AS query; SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query; SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow') AS query; SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow & hotel') AS query; SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query; SELECT ts_rewrite(tsquery_phrase('foo', 'foo'), 'foo', 'bar | baz'); SELECT to_tsvector('foo bar') @@ ts_rewrite(tsquery_phrase('foo', 'foo'), 'foo', 'bar | baz'); SELECT to_tsvector('bar baz') @@ ts_rewrite(tsquery_phrase('foo', 'foo'), 'foo', 'bar | baz'); RESET enable_seqscan; --test GUC SET default_text_search_config=simple; SELECT to_tsvector('SKIES My booKs'); SELECT plainto_tsquery('SKIES My booKs'); SELECT to_tsquery('SKIES & My | booKs'); SET default_text_search_config=english; SELECT to_tsvector('SKIES My booKs'); SELECT plainto_tsquery('SKIES My booKs'); SELECT to_tsquery('SKIES & My | booKs'); --trigger CREATE TRIGGER tsvectorupdate BEFORE UPDATE OR INSERT ON test_tsvector FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger(a, 'pg_catalog.english', t); SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); INSERT INTO test_tsvector (t) VALUES ('345 qwerty'); SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); UPDATE test_tsvector SET t = null WHERE t = '345 qwerty'; SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); INSERT INTO test_tsvector (t) VALUES ('345 qwerty'); SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); -- test finding items in GIN's pending list create temp table pendtest (ts tsvector); create index pendtest_idx on pendtest using gin(ts); insert into pendtest values (to_tsvector('Lore ipsam')); insert into pendtest values (to_tsvector('Lore ipsum')); select * from pendtest where 'ipsu:*'::tsquery @@ ts; select * from pendtest where 'ipsa:*'::tsquery @@ ts; select * from pendtest where 'ips:*'::tsquery @@ ts; select * from pendtest where 'ipt:*'::tsquery @@ ts; select * from pendtest where 'ipi:*'::tsquery @@ ts; --check OP_PHRASE on index create temp table phrase_index_test(fts tsvector); insert into phrase_index_test values ('A fat cat has just eaten a rat.'); insert into phrase_index_test values (to_tsvector('english', 'A fat cat has just eaten a rat.')); create index phrase_index_test_idx on phrase_index_test using gin(fts); set enable_seqscan = off; select * from phrase_index_test where fts @@ phraseto_tsquery('english', 'fat cat'); set enable_seqscan = on; -- test websearch_to_tsquery function select websearch_to_tsquery('simple', 'I have a fat:*ABCD cat'); select websearch_to_tsquery('simple', 'orange:**AABBCCDD'); select websearch_to_tsquery('simple', 'fat:A!cat:B|rat:C<'); select websearch_to_tsquery('simple', 'fat:A : cat:B'); select websearch_to_tsquery('simple', 'fat*rat'); select websearch_to_tsquery('simple', 'fat-rat'); select websearch_to_tsquery('simple', 'fat_rat'); -- weights are completely ignored select websearch_to_tsquery('simple', 'abc : def'); select websearch_to_tsquery('simple', 'abc:def'); select websearch_to_tsquery('simple', 'a:::b'); select websearch_to_tsquery('simple', 'abc:d'); select websearch_to_tsquery('simple', ':'); -- these operators are ignored select websearch_to_tsquery('simple', 'abc & def'); select websearch_to_tsquery('simple', 'abc | def'); select websearch_to_tsquery('simple', 'abc <-> def'); select websearch_to_tsquery('simple', 'abc (pg or class)'); -- NOT is ignored in quotes select websearch_to_tsquery('english', 'My brand new smartphone'); select websearch_to_tsquery('english', 'My brand "new smartphone"'); select websearch_to_tsquery('english', 'My brand "new -smartphone"'); -- test OR operator select websearch_to_tsquery('simple', 'cat or rat'); select websearch_to_tsquery('simple', 'cat OR rat'); select websearch_to_tsquery('simple', 'cat "OR" rat'); select websearch_to_tsquery('simple', 'cat OR'); select websearch_to_tsquery('simple', 'OR rat'); select websearch_to_tsquery('simple', '"fat cat OR rat"'); select websearch_to_tsquery('simple', 'fat (cat OR rat'); select websearch_to_tsquery('simple', 'or OR or'); -- OR is an operator here ... select websearch_to_tsquery('simple', '"fat cat"or"fat rat"'); select websearch_to_tsquery('simple', 'fat or(rat'); select websearch_to_tsquery('simple', 'fat or)rat'); select websearch_to_tsquery('simple', 'fat or&rat'); select websearch_to_tsquery('simple', 'fat or|rat'); select websearch_to_tsquery('simple', 'fat or!rat'); select websearch_to_tsquery('simple', 'fat orrat'); select websearch_to_tsquery('simple', 'fat or '); -- ... but not here select websearch_to_tsquery('simple', 'abc orange'); select websearch_to_tsquery('simple', 'abc OR1234'); select websearch_to_tsquery('simple', 'abc or-abc'); select websearch_to_tsquery('simple', 'abc OR_abc'); -- test quotes select websearch_to_tsquery('english', '"pg_class pg'); select websearch_to_tsquery('english', 'pg_class pg"'); select websearch_to_tsquery('english', '"pg_class pg"'); select websearch_to_tsquery('english', 'abc "pg_class pg"'); select websearch_to_tsquery('english', '"pg_class pg" def'); select websearch_to_tsquery('english', 'abc "pg pg_class pg" def'); select websearch_to_tsquery('english', ' or "pg pg_class pg" or '); select websearch_to_tsquery('english', '""pg pg_class pg""'); select websearch_to_tsquery('english', 'abc """"" def'); select websearch_to_tsquery('english', 'cat -"fat rat"'); select websearch_to_tsquery('english', 'cat -"fat rat" cheese'); select websearch_to_tsquery('english', 'abc "def -"'); select websearch_to_tsquery('english', 'abc "def :"'); select websearch_to_tsquery('english', '"A fat cat" has just eaten a -rat.'); select websearch_to_tsquery('english', '"A fat cat" has just eaten OR !rat.'); select websearch_to_tsquery('english', '"A fat cat" has just (+eaten OR -rat)'); select websearch_to_tsquery('english', 'this is ----fine'); select websearch_to_tsquery('english', '(()) )))) this ||| is && -fine, "dear friend" OR good'); select websearch_to_tsquery('english', 'an old <-> cat " is fine &&& too'); select websearch_to_tsquery('english', '"A the" OR just on'); select websearch_to_tsquery('english', '"a fat cat" ate a rat'); select to_tsvector('english', 'A fat cat ate a rat') @@ websearch_to_tsquery('english', '"a fat cat" ate a rat'); select to_tsvector('english', 'A fat grey cat ate a rat') @@ websearch_to_tsquery('english', '"a fat cat" ate a rat'); -- cases handled by gettoken_tsvector() select websearch_to_tsquery(''''); select websearch_to_tsquery('''abc''''def'''); select websearch_to_tsquery('\abc'); select websearch_to_tsquery('\'); pgFormatter-4.2/t/pg-test-files/sql/tsrf.sql000066400000000000000000000173551361326045100211110ustar00rootroot00000000000000-- -- tsrf - targetlist set returning function tests -- -- simple srf SELECT generate_series(1, 3); -- parallel iteration SELECT generate_series(1, 3), generate_series(3,5); -- parallel iteration, different number of rows SELECT generate_series(1, 2), generate_series(1,4); -- srf, with SRF argument SELECT generate_series(1, generate_series(1, 3)); -- but we've traditionally rejected the same in FROM SELECT * FROM generate_series(1, generate_series(1, 3)); -- srf, with two SRF arguments SELECT generate_series(generate_series(1,3), generate_series(2, 4)); -- check proper nesting of SRFs in different expressions explain (verbose, costs off) SELECT generate_series(1, generate_series(1, 3)), generate_series(2, 4); SELECT generate_series(1, generate_series(1, 3)), generate_series(2, 4); CREATE TABLE few(id int, dataa text, datab text); INSERT INTO few VALUES(1, 'a', 'foo'),(2, 'a', 'bar'),(3, 'b', 'bar'); -- SRF with a provably-dummy relation explain (verbose, costs off) SELECT unnest(ARRAY[1, 2]) FROM few WHERE false; SELECT unnest(ARRAY[1, 2]) FROM few WHERE false; -- SRF shouldn't prevent upper query from recognizing lower as dummy explain (verbose, costs off) SELECT * FROM few f1, (SELECT unnest(ARRAY[1,2]) FROM few f2 WHERE false OFFSET 0) ss; SELECT * FROM few f1, (SELECT unnest(ARRAY[1,2]) FROM few f2 WHERE false OFFSET 0) ss; -- SRF output order of sorting is maintained, if SRF is not referenced SELECT few.id, generate_series(1,3) g FROM few ORDER BY id DESC; -- but SRFs can be referenced in sort SELECT few.id, generate_series(1,3) g FROM few ORDER BY id, g DESC; SELECT few.id, generate_series(1,3) g FROM few ORDER BY id, generate_series(1,3) DESC; -- it's weird to have ORDER BYs that increase the number of results SELECT few.id FROM few ORDER BY id, generate_series(1,3) DESC; -- SRFs are computed after aggregation SET enable_hashagg TO 0; -- stable output order SELECT few.dataa, count(*), min(id), max(id), unnest('{1,1,3}'::int[]) FROM few WHERE few.id = 1 GROUP BY few.dataa; -- unless referenced in GROUP BY clause SELECT few.dataa, count(*), min(id), max(id), unnest('{1,1,3}'::int[]) FROM few WHERE few.id = 1 GROUP BY few.dataa, unnest('{1,1,3}'::int[]); SELECT few.dataa, count(*), min(id), max(id), unnest('{1,1,3}'::int[]) FROM few WHERE few.id = 1 GROUP BY few.dataa, 5; RESET enable_hashagg; -- check HAVING works when GROUP BY does [not] reference SRF output SELECT dataa, generate_series(1,1), count(*) FROM few GROUP BY 1 HAVING count(*) > 1; SELECT dataa, generate_series(1,1), count(*) FROM few GROUP BY 1, 2 HAVING count(*) > 1; -- it's weird to have GROUP BYs that increase the number of results SELECT few.dataa, count(*) FROM few WHERE dataa = 'a' GROUP BY few.dataa ORDER BY 2; SELECT few.dataa, count(*) FROM few WHERE dataa = 'a' GROUP BY few.dataa, unnest('{1,1,3}'::int[]) ORDER BY 2; -- SRFs are not allowed if they'd need to be conditionally executed SELECT q1, case when q1 > 0 then generate_series(1,3) else 0 end FROM int8_tbl; SELECT q1, coalesce(generate_series(1,3), 0) FROM int8_tbl; -- SRFs are not allowed in aggregate arguments SELECT min(generate_series(1, 3)) FROM few; -- ... unless they're within a sub-select SELECT sum((3 = ANY(SELECT generate_series(1,4)))::int); SELECT sum((3 = ANY(SELECT lag(x) over(order by x) FROM generate_series(1,4) x))::int); -- SRFs are not allowed in window function arguments, either SELECT min(generate_series(1, 3)) OVER() FROM few; -- SRFs are normally computed after window functions SELECT id,lag(id) OVER(), count(*) OVER(), generate_series(1,3) FROM few; -- unless referencing SRFs SELECT SUM(count(*)) OVER(PARTITION BY generate_series(1,3) ORDER BY generate_series(1,3)), generate_series(1,3) g FROM few GROUP BY g; -- sorting + grouping SELECT few.dataa, count(*), min(id), max(id), generate_series(1,3) FROM few GROUP BY few.dataa ORDER BY 5, 1; -- grouping sets are a bit special, they produce NULLs in columns not actually NULL set enable_hashagg = false; SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab); SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab) ORDER BY dataa; SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab) ORDER BY g; SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab, g); SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab, g) ORDER BY dataa; SELECT dataa, datab b, generate_series(1,2) g, count(*) FROM few GROUP BY CUBE(dataa, datab, g) ORDER BY g; reset enable_hashagg; -- case with degenerate ORDER BY explain (verbose, costs off) select 'foo' as f, generate_series(1,2) as g from few order by 1; select 'foo' as f, generate_series(1,2) as g from few order by 1; -- data modification CREATE TABLE fewmore AS SELECT generate_series(1,3) AS data; INSERT INTO fewmore VALUES(generate_series(4,5)); SELECT * FROM fewmore; -- SRFs are not allowed in UPDATE (they once were, but it was nonsense) UPDATE fewmore SET data = generate_series(4,9); -- SRFs are not allowed in RETURNING INSERT INTO fewmore VALUES(1) RETURNING generate_series(1,3); -- nor standalone VALUES (but surely this is a bug?) VALUES(1, generate_series(1,2)); -- We allow tSRFs that are not at top level SELECT int4mul(generate_series(1,2), 10); SELECT generate_series(1,3) IS DISTINCT FROM 2; -- but SRFs in function RTEs must be at top level (annoying restriction) SELECT * FROM int4mul(generate_series(1,2), 10); -- DISTINCT ON is evaluated before tSRF evaluation if SRF is not -- referenced either in ORDER BY or in the DISTINCT ON list. The ORDER -- BY reference can be implicitly generated, if there's no other ORDER BY. -- implicit reference (via implicit ORDER) to all columns SELECT DISTINCT ON (a) a, b, generate_series(1,3) g FROM (VALUES (3, 2), (3,1), (1,1), (1,4), (5,3), (5,1)) AS t(a, b); -- unreferenced in DISTINCT ON or ORDER BY SELECT DISTINCT ON (a) a, b, generate_series(1,3) g FROM (VALUES (3, 2), (3,1), (1,1), (1,4), (5,3), (5,1)) AS t(a, b) ORDER BY a, b DESC; -- referenced in ORDER BY SELECT DISTINCT ON (a) a, b, generate_series(1,3) g FROM (VALUES (3, 2), (3,1), (1,1), (1,4), (5,3), (5,1)) AS t(a, b) ORDER BY a, b DESC, g DESC; -- referenced in ORDER BY and DISTINCT ON SELECT DISTINCT ON (a, b, g) a, b, generate_series(1,3) g FROM (VALUES (3, 2), (3,1), (1,1), (1,4), (5,3), (5,1)) AS t(a, b) ORDER BY a, b DESC, g DESC; -- only SRF mentioned in DISTINCT ON SELECT DISTINCT ON (g) a, b, generate_series(1,3) g FROM (VALUES (3, 2), (3,1), (1,1), (1,4), (5,3), (5,1)) AS t(a, b); -- LIMIT / OFFSET is evaluated after SRF evaluation SELECT a, generate_series(1,2) FROM (VALUES(1),(2),(3)) r(a) LIMIT 2 OFFSET 2; -- SRFs are not allowed in LIMIT. SELECT 1 LIMIT generate_series(1,3); -- tSRF in correlated subquery, referencing table outside SELECT (SELECT generate_series(1,3) LIMIT 1 OFFSET few.id) FROM few; -- tSRF in correlated subquery, referencing SRF outside SELECT (SELECT generate_series(1,3) LIMIT 1 OFFSET g.i) FROM generate_series(0,3) g(i); -- Operators can return sets too CREATE OPERATOR |@| (PROCEDURE = unnest, RIGHTARG = ANYARRAY); SELECT |@|ARRAY[1,2,3]; -- Some fun cases involving duplicate SRF calls explain (verbose, costs off) select generate_series(1,3) as x, generate_series(1,3) + 1 as xp1; select generate_series(1,3) as x, generate_series(1,3) + 1 as xp1; explain (verbose, costs off) select generate_series(1,3)+1 order by generate_series(1,3); select generate_series(1,3)+1 order by generate_series(1,3); -- Check that SRFs of same nesting level run in lockstep explain (verbose, costs off) select generate_series(1,3) as x, generate_series(3,6) + 1 as y; select generate_series(1,3) as x, generate_series(3,6) + 1 as y; -- Clean up DROP TABLE few; DROP TABLE fewmore; pgFormatter-4.2/t/pg-test-files/sql/tstypes.sql000066400000000000000000000276521361326045100216470ustar00rootroot00000000000000-- deal with numeric instability of ts_rank SET extra_float_digits = 0; --Base tsvector test SELECT '1'::tsvector; SELECT '1 '::tsvector; SELECT ' 1'::tsvector; SELECT ' 1 '::tsvector; SELECT '1 2'::tsvector; SELECT '''1 2'''::tsvector; SELECT E'''1 \\''2'''::tsvector; SELECT E'''1 \\''2''3'::tsvector; SELECT E'''1 \\''2'' 3'::tsvector; SELECT E'''1 \\''2'' '' 3'' 4 '::tsvector; SELECT $$'\\as' ab\c ab\\c AB\\\c ab\\\\c$$::tsvector; SELECT tsvectorin(tsvectorout($$'\\as' ab\c ab\\c AB\\\c ab\\\\c$$::tsvector)); SELECT '''w'':4A,3B,2C,1D,5 a:8'; SELECT 'a:3A b:2a'::tsvector || 'ba:1234 a:1B'; --Base tsquery test SELECT '1'::tsquery; SELECT '1 '::tsquery; SELECT ' 1'::tsquery; SELECT ' 1 '::tsquery; SELECT '''1 2'''::tsquery; SELECT E'''1 \\''2'''::tsquery; SELECT '!1'::tsquery; SELECT '1|2'::tsquery; SELECT '1|!2'::tsquery; SELECT '!1|2'::tsquery; SELECT '!1|!2'::tsquery; SELECT '!(!1|!2)'::tsquery; SELECT '!(!1|2)'::tsquery; SELECT '!(1|!2)'::tsquery; SELECT '!(1|2)'::tsquery; SELECT '1&2'::tsquery; SELECT '!1&2'::tsquery; SELECT '1&!2'::tsquery; SELECT '!1&!2'::tsquery; SELECT '(1&2)'::tsquery; SELECT '1&(2)'::tsquery; SELECT '!(1)&2'::tsquery; SELECT '!(1&2)'::tsquery; SELECT '1|2&3'::tsquery; SELECT '1|(2&3)'::tsquery; SELECT '(1|2)&3'::tsquery; SELECT '1|2&!3'::tsquery; SELECT '1|!2&3'::tsquery; SELECT '!1|2&3'::tsquery; SELECT '!1|(2&3)'::tsquery; SELECT '!(1|2)&3'::tsquery; SELECT '(!1|2)&3'::tsquery; SELECT '1|(2|(4|(5|6)))'::tsquery; SELECT '1|2|4|5|6'::tsquery; SELECT '1&(2&(4&(5&6)))'::tsquery; SELECT '1&2&4&5&6'::tsquery; SELECT '1&(2&(4&(5|6)))'::tsquery; SELECT '1&(2&(4&(5|!6)))'::tsquery; SELECT E'1&(''2''&('' 4''&(\\|5 | ''6 \\'' !|&'')))'::tsquery; SELECT $$'\\as'$$::tsquery; SELECT 'a:* & nbb:*ac | doo:a* | goo'::tsquery; SELECT '!!b'::tsquery; SELECT '!!!b'::tsquery; SELECT '!(!b)'::tsquery; SELECT 'a & !!b'::tsquery; SELECT '!!a & b'::tsquery; SELECT '!!a & !!b'::tsquery; --comparisons SELECT 'a' < 'b & c'::tsquery as "true"; SELECT 'a' > 'b & c'::tsquery as "false"; SELECT 'a | f' < 'b & c'::tsquery as "false"; SELECT 'a | ff' < 'b & c'::tsquery as "false"; SELECT 'a | f | g' < 'b & c'::tsquery as "false"; --concatenation SELECT numnode( 'new'::tsquery ); SELECT numnode( 'new & york'::tsquery ); SELECT numnode( 'new & york | qwery'::tsquery ); SELECT 'foo & bar'::tsquery && 'asd'; SELECT 'foo & bar'::tsquery || 'asd & fg'; SELECT 'foo & bar'::tsquery || !!'asd & fg'::tsquery; SELECT 'foo & bar'::tsquery && 'asd | fg'; SELECT 'a' <-> 'b & d'::tsquery; SELECT 'a & g' <-> 'b & d'::tsquery; SELECT 'a & g' <-> 'b | d'::tsquery; SELECT 'a & g' <-> 'b <-> d'::tsquery; SELECT tsquery_phrase('a <3> g', 'b & d', 10); -- tsvector-tsquery operations SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca' as "true"; SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:B' as "true"; SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:A' as "true"; SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:C' as "false"; SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:CB' as "true"; SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & c:*C' as "false"; SELECT 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & c:*CB' as "true"; SELECT 'a b:89 ca:23A,64b cb:80c d:34c'::tsvector @@ 'd:AC & c:*C' as "true"; SELECT 'a b:89 ca:23A,64c cb:80b d:34c'::tsvector @@ 'd:AC & c:*C' as "true"; SELECT 'a b:89 ca:23A,64c cb:80b d:34c'::tsvector @@ 'd:AC & c:*B' as "true"; SELECT 'supernova'::tsvector @@ 'super'::tsquery AS "false"; SELECT 'supeanova supernova'::tsvector @@ 'super'::tsquery AS "false"; SELECT 'supeznova supernova'::tsvector @@ 'super'::tsquery AS "false"; SELECT 'supernova'::tsvector @@ 'super:*'::tsquery AS "true"; SELECT 'supeanova supernova'::tsvector @@ 'super:*'::tsquery AS "true"; SELECT 'supeznova supernova'::tsvector @@ 'super:*'::tsquery AS "true"; --phrase search SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <-> 2' AS "true"; SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <2> 2' AS "false"; SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <-> 3' AS "false"; SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <2> 3' AS "true"; SELECT to_tsvector('simple', '1 2 1 2') @@ '1 <3> 2' AS "true"; SELECT to_tsvector('simple', '1 2 11 3') @@ '1 <-> 3' AS "false"; SELECT to_tsvector('simple', '1 2 11 3') @@ '1:* <-> 3' AS "true"; SELECT to_tsvector('simple', '1 2 3 4') @@ '1 <-> 2 <-> 3' AS "true"; SELECT to_tsvector('simple', '1 2 3 4') @@ '(1 <-> 2) <-> 3' AS "true"; SELECT to_tsvector('simple', '1 2 3 4') @@ '1 <-> (2 <-> 3)' AS "true"; SELECT to_tsvector('simple', '1 2 3 4') @@ '1 <2> (2 <-> 3)' AS "false"; SELECT to_tsvector('simple', '1 2 1 2 3 4') @@ '(1 <-> 2) <-> 3' AS "true"; SELECT to_tsvector('simple', '1 2 1 2 3 4') @@ '1 <-> 2 <-> 3' AS "true"; -- without position data, phrase search does not match SELECT strip(to_tsvector('simple', '1 2 3 4')) @@ '1 <-> 2 <-> 3' AS "false"; select to_tsvector('simple', 'q x q y') @@ 'q <-> (x & y)' AS "false"; select to_tsvector('simple', 'q x') @@ 'q <-> (x | y <-> z)' AS "true"; select to_tsvector('simple', 'q y') @@ 'q <-> (x | y <-> z)' AS "false"; select to_tsvector('simple', 'q y z') @@ 'q <-> (x | y <-> z)' AS "true"; select to_tsvector('simple', 'q y x') @@ 'q <-> (x | y <-> z)' AS "false"; select to_tsvector('simple', 'q x y') @@ 'q <-> (x | y <-> z)' AS "true"; select to_tsvector('simple', 'q x') @@ '(x | y <-> z) <-> q' AS "false"; select to_tsvector('simple', 'x q') @@ '(x | y <-> z) <-> q' AS "true"; select to_tsvector('simple', 'x y q') @@ '(x | y <-> z) <-> q' AS "false"; select to_tsvector('simple', 'x y z') @@ '(x | y <-> z) <-> q' AS "false"; select to_tsvector('simple', 'x y z q') @@ '(x | y <-> z) <-> q' AS "true"; select to_tsvector('simple', 'y z q') @@ '(x | y <-> z) <-> q' AS "true"; select to_tsvector('simple', 'y y q') @@ '(x | y <-> z) <-> q' AS "false"; select to_tsvector('simple', 'y y q') @@ '(!x | y <-> z) <-> q' AS "true"; select to_tsvector('simple', 'x y q') @@ '(!x | y <-> z) <-> q' AS "true"; select to_tsvector('simple', 'y y q') @@ '(x | y <-> !z) <-> q' AS "true"; select to_tsvector('simple', 'x q') @@ '(x | y <-> !z) <-> q' AS "true"; select to_tsvector('simple', 'x q') @@ '(!x | y <-> z) <-> q' AS "false"; select to_tsvector('simple', 'z q') @@ '(!x | y <-> z) <-> q' AS "true"; select to_tsvector('simple', 'x y q y') @@ '!x <-> y' AS "true"; select to_tsvector('simple', 'x y q y') @@ '!foo' AS "true"; select to_tsvector('simple', '') @@ '!foo' AS "true"; --ranking SELECT ts_rank(' a:1 s:2C d g'::tsvector, 'a | s'); SELECT ts_rank(' a:1 sa:2C d g'::tsvector, 'a | s'); SELECT ts_rank(' a:1 sa:2C d g'::tsvector, 'a | s:*'); SELECT ts_rank(' a:1 sa:2C d g'::tsvector, 'a | sa:*'); SELECT ts_rank(' a:1 s:2B d g'::tsvector, 'a | s'); SELECT ts_rank(' a:1 s:2 d g'::tsvector, 'a | s'); SELECT ts_rank(' a:1 s:2C d g'::tsvector, 'a & s'); SELECT ts_rank(' a:1 s:2B d g'::tsvector, 'a & s'); SELECT ts_rank(' a:1 s:2 d g'::tsvector, 'a & s'); SELECT ts_rank_cd(' a:1 s:2C d g'::tsvector, 'a | s'); SELECT ts_rank_cd(' a:1 sa:2C d g'::tsvector, 'a | s'); SELECT ts_rank_cd(' a:1 sa:2C d g'::tsvector, 'a | s:*'); SELECT ts_rank_cd(' a:1 sa:2C d g'::tsvector, 'a | sa:*'); SELECT ts_rank_cd(' a:1 sa:3C sab:2c d g'::tsvector, 'a | sa:*'); SELECT ts_rank_cd(' a:1 s:2B d g'::tsvector, 'a | s'); SELECT ts_rank_cd(' a:1 s:2 d g'::tsvector, 'a | s'); SELECT ts_rank_cd(' a:1 s:2C d g'::tsvector, 'a & s'); SELECT ts_rank_cd(' a:1 s:2B d g'::tsvector, 'a & s'); SELECT ts_rank_cd(' a:1 s:2 d g'::tsvector, 'a & s'); SELECT ts_rank_cd(' a:1 s:2A d g'::tsvector, 'a <-> s'); SELECT ts_rank_cd(' a:1 s:2C d g'::tsvector, 'a <-> s'); SELECT ts_rank_cd(' a:1 s:2 d g'::tsvector, 'a <-> s'); SELECT ts_rank_cd(' a:1 s:2 d:2A g'::tsvector, 'a <-> s'); SELECT ts_rank_cd(' a:1 s:2,3A d:2A g'::tsvector, 'a <2> s:A'); SELECT ts_rank_cd(' a:1 b:2 s:3A d:2A g'::tsvector, 'a <2> s:A'); SELECT ts_rank_cd(' a:1 sa:2D sb:2A g'::tsvector, 'a <-> s:*'); SELECT ts_rank_cd(' a:1 sa:2A sb:2D g'::tsvector, 'a <-> s:*'); SELECT ts_rank_cd(' a:1 sa:2A sb:2D g'::tsvector, 'a <-> s:* <-> sa:A'); SELECT ts_rank_cd(' a:1 sa:2A sb:2D g'::tsvector, 'a <-> s:* <-> sa:B'); SELECT 'a:1 b:2'::tsvector @@ 'a <-> b'::tsquery AS "true"; SELECT 'a:1 b:2'::tsvector @@ 'a <0> b'::tsquery AS "false"; SELECT 'a:1 b:2'::tsvector @@ 'a <1> b'::tsquery AS "true"; SELECT 'a:1 b:2'::tsvector @@ 'a <2> b'::tsquery AS "false"; SELECT 'a:1 b:3'::tsvector @@ 'a <-> b'::tsquery AS "false"; SELECT 'a:1 b:3'::tsvector @@ 'a <0> b'::tsquery AS "false"; SELECT 'a:1 b:3'::tsvector @@ 'a <1> b'::tsquery AS "false"; SELECT 'a:1 b:3'::tsvector @@ 'a <2> b'::tsquery AS "true"; SELECT 'a:1 b:3'::tsvector @@ 'a <3> b'::tsquery AS "false"; SELECT 'a:1 b:3'::tsvector @@ 'a <0> a:*'::tsquery AS "true"; -- tsvector editing operations SELECT strip('w:12B w:13* w:12,5,6 a:1,3* a:3 w asd:1dc asd'::tsvector); SELECT strip('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector); SELECT strip('base hidden rebel spaceship strike'::tsvector); SELECT ts_delete(to_tsvector('english', 'Rebel spaceships, striking from a hidden base'), 'spaceship'); SELECT ts_delete('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector, 'base'); SELECT ts_delete('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector, 'bas'); SELECT ts_delete('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector, 'bases'); SELECT ts_delete('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector, 'spaceship'); SELECT ts_delete('base hidden rebel spaceship strike'::tsvector, 'spaceship'); SELECT ts_delete('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector, ARRAY['spaceship','rebel']); SELECT ts_delete('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector, ARRAY['spaceships','rebel']); SELECT ts_delete('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector, ARRAY['spaceshi','rebel']); SELECT ts_delete('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector, ARRAY['spaceship','leya','rebel']); SELECT ts_delete('base hidden rebel spaceship strike'::tsvector, ARRAY['spaceship','leya','rebel']); SELECT ts_delete('base hidden rebel spaceship strike'::tsvector, ARRAY['spaceship','leya','rebel','rebel']); SELECT ts_delete('base hidden rebel spaceship strike'::tsvector, ARRAY['spaceship','leya','rebel', NULL]); SELECT unnest('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector); SELECT unnest('base hidden rebel spaceship strike'::tsvector); SELECT * FROM unnest('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector); SELECT * FROM unnest('base hidden rebel spaceship strike'::tsvector); SELECT lexeme, positions[1] from unnest('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector); SELECT tsvector_to_array('base:7 hidden:6 rebel:1 spaceship:2,33A,34B,35C,36D strike:3'::tsvector); SELECT tsvector_to_array('base hidden rebel spaceship strike'::tsvector); SELECT array_to_tsvector(ARRAY['base','hidden','rebel','spaceship','strike']); SELECT array_to_tsvector(ARRAY['base','hidden','rebel','spaceship', NULL]); -- array_to_tsvector must sort and de-dup SELECT array_to_tsvector(ARRAY['foo','bar','baz','bar']); SELECT setweight('w:12B w:13* w:12,5,6 a:1,3* a:3 w asd:1dc asd zxc:81,567,222A'::tsvector, 'c'); SELECT setweight('a:1,3A asd:1C w:5,6,12B,13A zxc:81,222A,567'::tsvector, 'c'); SELECT setweight('a:1,3A asd:1C w:5,6,12B,13A zxc:81,222A,567'::tsvector, 'c', '{a}'); SELECT setweight('a:1,3A asd:1C w:5,6,12B,13A zxc:81,222A,567'::tsvector, 'c', '{a}'); SELECT setweight('a:1,3A asd:1C w:5,6,12B,13A zxc:81,222A,567'::tsvector, 'c', '{a,zxc}'); SELECT setweight('a asd w:5,6,12B,13A zxc'::tsvector, 'c', '{a,zxc}'); SELECT setweight('a asd w:5,6,12B,13A zxc'::tsvector, 'c', ARRAY['a', 'zxc', NULL]); SELECT ts_filter('base:7A empir:17 evil:15 first:11 galact:16 hidden:6A rebel:1A spaceship:2A strike:3A victori:12 won:9'::tsvector, '{a}'); SELECT ts_filter('base hidden rebel spaceship strike'::tsvector, '{a}'); SELECT ts_filter('base hidden rebel spaceship strike'::tsvector, '{a,b,NULL}'); pgFormatter-4.2/t/pg-test-files/sql/txid.sql000066400000000000000000000056631361326045100211020ustar00rootroot00000000000000-- txid_snapshot data type and related functions -- i/o select '12:13:'::txid_snapshot; select '12:18:14,16'::txid_snapshot; select '12:16:14,14'::txid_snapshot; -- errors select '31:12:'::txid_snapshot; select '0:1:'::txid_snapshot; select '12:13:0'::txid_snapshot; select '12:16:14,13'::txid_snapshot; create temp table snapshot_test ( nr integer, snap txid_snapshot ); insert into snapshot_test values (1, '12:13:'); insert into snapshot_test values (2, '12:20:13,15,18'); insert into snapshot_test values (3, '100001:100009:100005,100007,100008'); insert into snapshot_test values (4, '100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131'); select snap from snapshot_test order by nr; select txid_snapshot_xmin(snap), txid_snapshot_xmax(snap), txid_snapshot_xip(snap) from snapshot_test order by nr; select id, txid_visible_in_snapshot(id, snap) from snapshot_test, generate_series(11, 21) id where nr = 2; -- test bsearch select id, txid_visible_in_snapshot(id, snap) from snapshot_test, generate_series(90, 160) id where nr = 4; -- test current values also select txid_current() >= txid_snapshot_xmin(txid_current_snapshot()); -- we can't assume current is always less than xmax, however select txid_visible_in_snapshot(txid_current(), txid_current_snapshot()); -- test 64bitness select txid_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013'; select txid_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013'); select txid_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013'); -- test 64bit overflow SELECT txid_snapshot '1:9223372036854775807:3'; SELECT txid_snapshot '1:9223372036854775808:3'; -- test txid_current_if_assigned BEGIN; SELECT txid_current_if_assigned() IS NULL; SELECT txid_current() \gset SELECT txid_current_if_assigned() IS NOT DISTINCT FROM BIGINT :'txid_current'; COMMIT; -- test xid status functions BEGIN; SELECT txid_current() AS committed \gset COMMIT; BEGIN; SELECT txid_current() AS rolledback \gset ROLLBACK; BEGIN; SELECT txid_current() AS inprogress \gset SELECT txid_status(:committed) AS committed; SELECT txid_status(:rolledback) AS rolledback; SELECT txid_status(:inprogress) AS inprogress; SELECT txid_status(1); -- BootstrapTransactionId is always committed SELECT txid_status(2); -- FrozenTransactionId is always committed SELECT txid_status(3); -- in regress testing FirstNormalTransactionId will always be behind oldestXmin COMMIT; BEGIN; CREATE FUNCTION test_future_xid_status(bigint) RETURNS void LANGUAGE plpgsql AS $$ BEGIN PERFORM txid_status($1); RAISE EXCEPTION 'didn''t ERROR at xid in the future as expected'; EXCEPTION WHEN invalid_parameter_value THEN RAISE NOTICE 'Got expected error for xid in the future'; END; $$; SELECT test_future_xid_status(:inprogress + 10000); ROLLBACK; pgFormatter-4.2/t/pg-test-files/sql/type_sanity.sql000066400000000000000000000407571361326045100225050ustar00rootroot00000000000000-- -- TYPE_SANITY -- Sanity checks for common errors in making type-related system tables: -- pg_type, pg_class, pg_attribute, pg_range. -- -- None of the SELECTs here should ever find any matching entries, -- so the expected output is easy to maintain ;-). -- A test failure indicates someone messed up an entry in the system tables. -- -- NB: we assume the oidjoins test will have caught any dangling links, -- that is OID or REGPROC fields that are not zero and do not match some -- row in the linked-to table. However, if we want to enforce that a link -- field can't be 0, we have to check it here. -- **************** pg_type **************** -- Look for illegal values in pg_type fields. SELECT p1.oid, p1.typname FROM pg_type as p1 WHERE p1.typnamespace = 0 OR (p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR NOT p1.typisdefined OR (p1.typalign not in ('c', 's', 'i', 'd')) OR (p1.typstorage not in ('p', 'x', 'e', 'm')); -- Look for "pass by value" types that can't be passed by value. SELECT p1.oid, p1.typname FROM pg_type as p1 WHERE p1.typbyval AND (p1.typlen != 1 OR p1.typalign != 'c') AND (p1.typlen != 2 OR p1.typalign != 's') AND (p1.typlen != 4 OR p1.typalign != 'i') AND (p1.typlen != 8 OR p1.typalign != 'd'); -- Look for "toastable" types that aren't varlena. SELECT p1.oid, p1.typname FROM pg_type as p1 WHERE p1.typstorage != 'p' AND (p1.typbyval OR p1.typlen != -1); -- Look for complex types that do not have a typrelid entry, -- or basic types that do. SELECT p1.oid, p1.typname FROM pg_type as p1 WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR (p1.typtype != 'c' AND p1.typrelid != 0); -- Look for types that should have an array type according to their typtype, -- but don't. We exclude composites here because we have not bothered to -- make array types corresponding to the system catalogs' rowtypes. -- NOTE: as of v10, this check finds pg_node_tree, pg_ndistinct, smgr. SELECT p1.oid, p1.typname FROM pg_type as p1 WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%' AND NOT EXISTS (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid and p1.typarray = p2.oid); -- Make sure typarray points to a varlena array type of our own base SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype, p2.typelem, p2.typlen FROM pg_type p1 LEFT JOIN pg_type p2 ON (p1.typarray = p2.oid) WHERE p1.typarray <> 0 AND (p2.oid IS NULL OR p2.typelem <> p1.oid OR p2.typlen <> -1); -- Look for range types that do not have a pg_range entry SELECT p1.oid, p1.typname FROM pg_type as p1 WHERE p1.typtype = 'r' AND NOT EXISTS(SELECT 1 FROM pg_range r WHERE rngtypid = p1.oid); -- Look for range types whose typalign isn't sufficient SELECT p1.oid, p1.typname, p1.typalign, p2.typname, p2.typalign FROM pg_type as p1 LEFT JOIN pg_range as r ON rngtypid = p1.oid LEFT JOIN pg_type as p2 ON rngsubtype = p2.oid WHERE p1.typtype = 'r' AND (p1.typalign != (CASE WHEN p2.typalign = 'd' THEN 'd'::"char" ELSE 'i'::"char" END) OR p2.oid IS NULL); -- Text conversion routines must be provided. SELECT p1.oid, p1.typname FROM pg_type as p1 WHERE (p1.typinput = 0 OR p1.typoutput = 0); -- Check for bogus typinput routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typinput = p2.oid AND NOT ((p2.pronargs = 1 AND p2.proargtypes[0] = 'cstring'::regtype) OR (p2.pronargs = 2 AND p2.proargtypes[0] = 'cstring'::regtype AND p2.proargtypes[1] = 'oid'::regtype) OR (p2.pronargs = 3 AND p2.proargtypes[0] = 'cstring'::regtype AND p2.proargtypes[1] = 'oid'::regtype AND p2.proargtypes[2] = 'int4'::regtype)); -- Check for type of the variadic array parameter's elements. -- provariadic should be ANYOID if the type of the last element is ANYOID, -- ANYELEMENTOID if the type of the last element is ANYARRAYOID, and otherwise -- the element type corresponding to the array type. SELECT oid::regprocedure, provariadic::regtype, proargtypes::regtype[] FROM pg_proc WHERE provariadic != 0 AND case proargtypes[array_length(proargtypes, 1)-1] WHEN 2276 THEN 2276 -- any -> any WHEN 2277 THEN 2283 -- anyarray -> anyelement ELSE (SELECT t.oid FROM pg_type t WHERE t.typarray = proargtypes[array_length(proargtypes, 1)-1]) END != provariadic; -- Check that all and only those functions with a variadic type have -- a variadic argument. SELECT oid::regprocedure, proargmodes, provariadic FROM pg_proc WHERE (proargmodes IS NOT NULL AND 'v' = any(proargmodes)) IS DISTINCT FROM (provariadic != 0); -- As of 8.0, this check finds refcursor, which is borrowing -- other types' I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT (p1.typelem != 0 AND p1.typlen < 0) AND NOT (p2.prorettype = p1.oid AND NOT p2.proretset) ORDER BY 1; -- Varlena array types will point to array_in -- Exception as of 8.1: int2vector and oidvector have their own I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typinput = p2.oid AND (p1.typelem != 0 AND p1.typlen < 0) AND NOT (p2.oid = 'array_in'::regproc) ORDER BY 1; -- typinput routines should not be volatile SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's'); -- Composites, domains, enums, ranges should all use the same input routines SELECT DISTINCT typtype, typinput FROM pg_type AS p1 WHERE p1.typtype not in ('b', 'p') ORDER BY 1; -- Check for bogus typoutput routines -- As of 8.0, this check finds refcursor, which is borrowing -- other types' I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT (p2.pronargs = 1 AND (p2.proargtypes[0] = p1.oid OR (p2.oid = 'array_out'::regproc AND p1.typelem != 0 AND p1.typlen = -1))) ORDER BY 1; SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typoutput = p2.oid AND NOT (p2.prorettype = 'cstring'::regtype AND NOT p2.proretset); -- typoutput routines should not be volatile SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's'); -- Composites, enums, ranges should all use the same output routines SELECT DISTINCT typtype, typoutput FROM pg_type AS p1 WHERE p1.typtype not in ('b', 'd', 'p') ORDER BY 1; -- Domains should have same typoutput as their base types SELECT p1.oid, p1.typname, p2.oid, p2.typname FROM pg_type AS p1 LEFT JOIN pg_type AS p2 ON p1.typbasetype = p2.oid WHERE p1.typtype = 'd' AND p1.typoutput IS DISTINCT FROM p2.typoutput; -- Check for bogus typreceive routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typreceive = p2.oid AND NOT ((p2.pronargs = 1 AND p2.proargtypes[0] = 'internal'::regtype) OR (p2.pronargs = 2 AND p2.proargtypes[0] = 'internal'::regtype AND p2.proargtypes[1] = 'oid'::regtype) OR (p2.pronargs = 3 AND p2.proargtypes[0] = 'internal'::regtype AND p2.proargtypes[1] = 'oid'::regtype AND p2.proargtypes[2] = 'int4'::regtype)); -- As of 7.4, this check finds refcursor, which is borrowing -- other types' I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typreceive = p2.oid AND p1.typtype in ('b', 'p') AND NOT (p1.typelem != 0 AND p1.typlen < 0) AND NOT (p2.prorettype = p1.oid AND NOT p2.proretset) ORDER BY 1; -- Varlena array types will point to array_recv -- Exception as of 8.1: int2vector and oidvector have their own I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typreceive = p2.oid AND (p1.typelem != 0 AND p1.typlen < 0) AND NOT (p2.oid = 'array_recv'::regproc) ORDER BY 1; -- Suspicious if typreceive doesn't take same number of args as typinput SELECT p1.oid, p1.typname, p2.oid, p2.proname, p3.oid, p3.proname FROM pg_type AS p1, pg_proc AS p2, pg_proc AS p3 WHERE p1.typinput = p2.oid AND p1.typreceive = p3.oid AND p2.pronargs != p3.pronargs; -- typreceive routines should not be volatile SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's'); -- Composites, domains, enums, ranges should all use the same receive routines SELECT DISTINCT typtype, typreceive FROM pg_type AS p1 WHERE p1.typtype not in ('b', 'p') ORDER BY 1; -- Check for bogus typsend routines -- As of 7.4, this check finds refcursor, which is borrowing -- other types' I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typsend = p2.oid AND p1.typtype in ('b', 'p') AND NOT (p2.pronargs = 1 AND (p2.proargtypes[0] = p1.oid OR (p2.oid = 'array_send'::regproc AND p1.typelem != 0 AND p1.typlen = -1))) ORDER BY 1; SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typsend = p2.oid AND NOT (p2.prorettype = 'bytea'::regtype AND NOT p2.proretset); -- typsend routines should not be volatile SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's'); -- Composites, enums, ranges should all use the same send routines SELECT DISTINCT typtype, typsend FROM pg_type AS p1 WHERE p1.typtype not in ('b', 'd', 'p') ORDER BY 1; -- Domains should have same typsend as their base types SELECT p1.oid, p1.typname, p2.oid, p2.typname FROM pg_type AS p1 LEFT JOIN pg_type AS p2 ON p1.typbasetype = p2.oid WHERE p1.typtype = 'd' AND p1.typsend IS DISTINCT FROM p2.typsend; -- Check for bogus typmodin routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typmodin = p2.oid AND NOT (p2.pronargs = 1 AND p2.proargtypes[0] = 'cstring[]'::regtype AND p2.prorettype = 'int4'::regtype AND NOT p2.proretset); -- typmodin routines should not be volatile SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typmodin = p2.oid AND p2.provolatile NOT IN ('i', 's'); -- Check for bogus typmodout routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typmodout = p2.oid AND NOT (p2.pronargs = 1 AND p2.proargtypes[0] = 'int4'::regtype AND p2.prorettype = 'cstring'::regtype AND NOT p2.proretset); -- typmodout routines should not be volatile SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typmodout = p2.oid AND p2.provolatile NOT IN ('i', 's'); -- Array types should have same typmodin/out as their element types SELECT p1.oid, p1.typname, p2.oid, p2.typname FROM pg_type AS p1, pg_type AS p2 WHERE p1.typelem = p2.oid AND NOT (p1.typmodin = p2.typmodin AND p1.typmodout = p2.typmodout); -- Array types should have same typdelim as their element types SELECT p1.oid, p1.typname, p2.oid, p2.typname FROM pg_type AS p1, pg_type AS p2 WHERE p1.typarray = p2.oid AND NOT (p1.typdelim = p2.typdelim); -- Look for array types whose typalign isn't sufficient SELECT p1.oid, p1.typname, p1.typalign, p2.typname, p2.typalign FROM pg_type AS p1, pg_type AS p2 WHERE p1.typarray = p2.oid AND p2.typalign != (CASE WHEN p1.typalign = 'd' THEN 'd'::"char" ELSE 'i'::"char" END); -- Check for bogus typanalyze routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typanalyze = p2.oid AND NOT (p2.pronargs = 1 AND p2.proargtypes[0] = 'internal'::regtype AND p2.prorettype = 'bool'::regtype AND NOT p2.proretset); -- there does not seem to be a reason to care about volatility of typanalyze -- domains inherit their base type's typanalyze SELECT d.oid, d.typname, d.typanalyze, t.oid, t.typname, t.typanalyze FROM pg_type d JOIN pg_type t ON d.typbasetype = t.oid WHERE d.typanalyze != t.typanalyze; -- range_typanalyze should be used for all and only range types -- (but exclude domains, which we checked above) SELECT t.oid, t.typname, t.typanalyze FROM pg_type t LEFT JOIN pg_range r on t.oid = r.rngtypid WHERE t.typbasetype = 0 AND (t.typanalyze = 'range_typanalyze'::regproc) != (r.rngtypid IS NOT NULL); -- array_typanalyze should be used for all and only array types -- (but exclude domains, which we checked above) -- As of 9.2 this finds int2vector and oidvector, which are weird anyway SELECT t.oid, t.typname, t.typanalyze FROM pg_type t WHERE t.typbasetype = 0 AND (t.typanalyze = 'array_typanalyze'::regproc) != (typelem != 0 AND typlen < 0) ORDER BY 1; -- **************** pg_class **************** -- Look for illegal values in pg_class fields SELECT p1.oid, p1.relname FROM pg_class as p1 WHERE relkind NOT IN ('r', 'i', 'S', 't', 'v', 'm', 'c', 'f', 'p') OR relpersistence NOT IN ('p', 'u', 't') OR relreplident NOT IN ('d', 'n', 'f', 'i'); -- All tables and indexes should have an access method. SELECT p1.oid, p1.relname FROM pg_class as p1 WHERE p1.relkind NOT IN ('S', 'v', 'f', 'c') and p1.relam = 0; -- Conversely, sequences, views, types shouldn't have them SELECT p1.oid, p1.relname FROM pg_class as p1 WHERE p1.relkind IN ('S', 'v', 'f', 'c') and p1.relam != 0; -- Indexes should have AMs of type 'i' SELECT pc.oid, pc.relname, pa.amname, pa.amtype FROM pg_class as pc JOIN pg_am AS pa ON (pc.relam = pa.oid) WHERE pc.relkind IN ('i') and pa.amtype != 'i'; -- Tables, matviews etc should have AMs of type 't' SELECT pc.oid, pc.relname, pa.amname, pa.amtype FROM pg_class as pc JOIN pg_am AS pa ON (pc.relam = pa.oid) WHERE pc.relkind IN ('r', 't', 'm') and pa.amtype != 't'; -- **************** pg_attribute **************** -- Look for illegal values in pg_attribute fields SELECT p1.attrelid, p1.attname FROM pg_attribute as p1 WHERE p1.attrelid = 0 OR p1.atttypid = 0 OR p1.attnum = 0 OR p1.attcacheoff != -1 OR p1.attinhcount < 0 OR (p1.attinhcount = 0 AND NOT p1.attislocal); -- Cross-check attnum against parent relation SELECT p1.attrelid, p1.attname, p2.oid, p2.relname FROM pg_attribute AS p1, pg_class AS p2 WHERE p1.attrelid = p2.oid AND p1.attnum > p2.relnatts; -- Detect missing pg_attribute entries: should have as many non-system -- attributes as parent relation expects SELECT p1.oid, p1.relname FROM pg_class AS p1 WHERE p1.relnatts != (SELECT count(*) FROM pg_attribute AS p2 WHERE p2.attrelid = p1.oid AND p2.attnum > 0); -- Cross-check against pg_type entry -- NOTE: we allow attstorage to be 'plain' even when typstorage is not; -- this is mainly for toast tables. SELECT p1.attrelid, p1.attname, p2.oid, p2.typname FROM pg_attribute AS p1, pg_type AS p2 WHERE p1.atttypid = p2.oid AND (p1.attlen != p2.typlen OR p1.attalign != p2.typalign OR p1.attbyval != p2.typbyval OR (p1.attstorage != p2.typstorage AND p1.attstorage != 'p')); -- **************** pg_range **************** -- Look for illegal values in pg_range fields. SELECT p1.rngtypid, p1.rngsubtype FROM pg_range as p1 WHERE p1.rngtypid = 0 OR p1.rngsubtype = 0 OR p1.rngsubopc = 0; -- rngcollation should be specified iff subtype is collatable SELECT p1.rngtypid, p1.rngsubtype, p1.rngcollation, t.typcollation FROM pg_range p1 JOIN pg_type t ON t.oid = p1.rngsubtype WHERE (rngcollation = 0) != (typcollation = 0); -- opclass had better be a btree opclass accepting the subtype. -- We must allow anyarray matches, cf opr_sanity's binary_coercible() SELECT p1.rngtypid, p1.rngsubtype, o.opcmethod, o.opcname FROM pg_range p1 JOIN pg_opclass o ON o.oid = p1.rngsubopc WHERE o.opcmethod != 403 OR ((o.opcintype != p1.rngsubtype) AND NOT (o.opcintype = 'pg_catalog.anyarray'::regtype AND EXISTS(select 1 from pg_catalog.pg_type where oid = p1.rngsubtype and typelem != 0 and typlen = -1))); -- canonical function, if any, had better match the range type SELECT p1.rngtypid, p1.rngsubtype, p.proname FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngcanonical WHERE pronargs != 1 OR proargtypes[0] != rngtypid OR prorettype != rngtypid; -- subdiff function, if any, had better match the subtype SELECT p1.rngtypid, p1.rngsubtype, p.proname FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff WHERE pronargs != 2 OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype OR prorettype != 'pg_catalog.float8'::regtype; pgFormatter-4.2/t/pg-test-files/sql/typed_table.sql000066400000000000000000000033361361326045100224210ustar00rootroot00000000000000CREATE TABLE ttable1 OF nothing; CREATE TYPE person_type AS (id int, name text); CREATE TABLE persons OF person_type; CREATE TABLE IF NOT EXISTS persons OF person_type; SELECT * FROM persons; \d persons CREATE FUNCTION get_all_persons() RETURNS SETOF person_type LANGUAGE SQL AS $$ SELECT * FROM persons; $$; SELECT * FROM get_all_persons(); -- certain ALTER TABLE operations on typed tables are not allowed ALTER TABLE persons ADD COLUMN comment text; ALTER TABLE persons DROP COLUMN name; ALTER TABLE persons RENAME COLUMN id TO num; ALTER TABLE persons ALTER COLUMN name TYPE varchar; CREATE TABLE stuff (id int); ALTER TABLE persons INHERIT stuff; CREATE TABLE personsx OF person_type (myname WITH OPTIONS NOT NULL); -- error CREATE TABLE persons2 OF person_type ( id WITH OPTIONS PRIMARY KEY, UNIQUE (name) ); \d persons2 CREATE TABLE persons3 OF person_type ( PRIMARY KEY (id), name WITH OPTIONS DEFAULT '' ); \d persons3 CREATE TABLE persons4 OF person_type ( name WITH OPTIONS NOT NULL, name WITH OPTIONS DEFAULT '' -- error, specified more than once ); DROP TYPE person_type RESTRICT; DROP TYPE person_type CASCADE; CREATE TABLE persons5 OF stuff; -- only CREATE TYPE AS types may be used DROP TABLE stuff; -- implicit casting CREATE TYPE person_type AS (id int, name text); CREATE TABLE persons OF person_type; INSERT INTO persons VALUES (1, 'test'); CREATE FUNCTION namelen(person_type) RETURNS int LANGUAGE SQL AS $$ SELECT length($1.name) $$; SELECT id, namelen(persons) FROM persons; CREATE TABLE persons2 OF person_type ( id WITH OPTIONS PRIMARY KEY, UNIQUE (name) ); \d persons2 CREATE TABLE persons3 OF person_type ( PRIMARY KEY (id), name NOT NULL DEFAULT '' ); \d persons3 pgFormatter-4.2/t/pg-test-files/sql/union.sql000066400000000000000000000255631361326045100212630ustar00rootroot00000000000000-- -- UNION (also INTERSECT, EXCEPT) -- -- Simple UNION constructs SELECT 1 AS two UNION SELECT 2 ORDER BY 1; SELECT 1 AS one UNION SELECT 1 ORDER BY 1; SELECT 1 AS two UNION ALL SELECT 2; SELECT 1 AS two UNION ALL SELECT 1; SELECT 1 AS three UNION SELECT 2 UNION SELECT 3 ORDER BY 1; SELECT 1 AS two UNION SELECT 2 UNION SELECT 2 ORDER BY 1; SELECT 1 AS three UNION SELECT 2 UNION ALL SELECT 2 ORDER BY 1; SELECT 1.1 AS two UNION SELECT 2.2 ORDER BY 1; -- Mixed types SELECT 1.1 AS two UNION SELECT 2 ORDER BY 1; SELECT 1 AS two UNION SELECT 2.2 ORDER BY 1; SELECT 1 AS one UNION SELECT 1.0::float8 ORDER BY 1; SELECT 1.1 AS two UNION ALL SELECT 2 ORDER BY 1; SELECT 1.0::float8 AS two UNION ALL SELECT 1 ORDER BY 1; SELECT 1.1 AS three UNION SELECT 2 UNION SELECT 3 ORDER BY 1; SELECT 1.1::float8 AS two UNION SELECT 2 UNION SELECT 2.0::float8 ORDER BY 1; SELECT 1.1 AS three UNION SELECT 2 UNION ALL SELECT 2 ORDER BY 1; SELECT 1.1 AS two UNION (SELECT 2 UNION ALL SELECT 2) ORDER BY 1; -- -- Try testing from tables... -- SELECT f1 AS five FROM FLOAT8_TBL UNION SELECT f1 FROM FLOAT8_TBL ORDER BY 1; SELECT f1 AS ten FROM FLOAT8_TBL UNION ALL SELECT f1 FROM FLOAT8_TBL; SELECT f1 AS nine FROM FLOAT8_TBL UNION SELECT f1 FROM INT4_TBL ORDER BY 1; SELECT f1 AS ten FROM FLOAT8_TBL UNION ALL SELECT f1 FROM INT4_TBL; SELECT f1 AS five FROM FLOAT8_TBL WHERE f1 BETWEEN -1e6 AND 1e6 UNION SELECT f1 FROM INT4_TBL WHERE f1 BETWEEN 0 AND 1000000 ORDER BY 1; SELECT CAST(f1 AS char(4)) AS three FROM VARCHAR_TBL UNION SELECT f1 FROM CHAR_TBL ORDER BY 1; SELECT f1 AS three FROM VARCHAR_TBL UNION SELECT CAST(f1 AS varchar) FROM CHAR_TBL ORDER BY 1; SELECT f1 AS eight FROM VARCHAR_TBL UNION ALL SELECT f1 FROM CHAR_TBL; SELECT f1 AS five FROM TEXT_TBL UNION SELECT f1 FROM VARCHAR_TBL UNION SELECT TRIM(TRAILING FROM f1) FROM CHAR_TBL ORDER BY 1; -- -- INTERSECT and EXCEPT -- SELECT q2 FROM int8_tbl INTERSECT SELECT q1 FROM int8_tbl ORDER BY 1; SELECT q2 FROM int8_tbl INTERSECT ALL SELECT q1 FROM int8_tbl ORDER BY 1; SELECT q2 FROM int8_tbl EXCEPT SELECT q1 FROM int8_tbl ORDER BY 1; SELECT q2 FROM int8_tbl EXCEPT ALL SELECT q1 FROM int8_tbl ORDER BY 1; SELECT q2 FROM int8_tbl EXCEPT ALL SELECT DISTINCT q1 FROM int8_tbl ORDER BY 1; SELECT q1 FROM int8_tbl EXCEPT SELECT q2 FROM int8_tbl ORDER BY 1; SELECT q1 FROM int8_tbl EXCEPT ALL SELECT q2 FROM int8_tbl ORDER BY 1; SELECT q1 FROM int8_tbl EXCEPT ALL SELECT DISTINCT q2 FROM int8_tbl ORDER BY 1; SELECT q1 FROM int8_tbl EXCEPT ALL SELECT q1 FROM int8_tbl FOR NO KEY UPDATE; -- nested cases (SELECT 1,2,3 UNION SELECT 4,5,6) INTERSECT SELECT 4,5,6; (SELECT 1,2,3 UNION SELECT 4,5,6 ORDER BY 1,2) INTERSECT SELECT 4,5,6; (SELECT 1,2,3 UNION SELECT 4,5,6) EXCEPT SELECT 4,5,6; (SELECT 1,2,3 UNION SELECT 4,5,6 ORDER BY 1,2) EXCEPT SELECT 4,5,6; -- exercise both hashed and sorted implementations of INTERSECT/EXCEPT set enable_hashagg to on; explain (costs off) select count(*) from ( select unique1 from tenk1 intersect select fivethous from tenk1 ) ss; select count(*) from ( select unique1 from tenk1 intersect select fivethous from tenk1 ) ss; explain (costs off) select unique1 from tenk1 except select unique2 from tenk1 where unique2 != 10; select unique1 from tenk1 except select unique2 from tenk1 where unique2 != 10; set enable_hashagg to off; explain (costs off) select count(*) from ( select unique1 from tenk1 intersect select fivethous from tenk1 ) ss; select count(*) from ( select unique1 from tenk1 intersect select fivethous from tenk1 ) ss; explain (costs off) select unique1 from tenk1 except select unique2 from tenk1 where unique2 != 10; select unique1 from tenk1 except select unique2 from tenk1 where unique2 != 10; reset enable_hashagg; -- -- Mixed types -- SELECT f1 FROM float8_tbl INTERSECT SELECT f1 FROM int4_tbl ORDER BY 1; SELECT f1 FROM float8_tbl EXCEPT SELECT f1 FROM int4_tbl ORDER BY 1; -- -- Operator precedence and (((((extra))))) parentheses -- SELECT q1 FROM int8_tbl INTERSECT SELECT q2 FROM int8_tbl UNION ALL SELECT q2 FROM int8_tbl ORDER BY 1; SELECT q1 FROM int8_tbl INTERSECT (((SELECT q2 FROM int8_tbl UNION ALL SELECT q2 FROM int8_tbl))) ORDER BY 1; (((SELECT q1 FROM int8_tbl INTERSECT SELECT q2 FROM int8_tbl ORDER BY 1))) UNION ALL SELECT q2 FROM int8_tbl; SELECT q1 FROM int8_tbl UNION ALL SELECT q2 FROM int8_tbl EXCEPT SELECT q1 FROM int8_tbl ORDER BY 1; SELECT q1 FROM int8_tbl UNION ALL (((SELECT q2 FROM int8_tbl EXCEPT SELECT q1 FROM int8_tbl ORDER BY 1))); (((SELECT q1 FROM int8_tbl UNION ALL SELECT q2 FROM int8_tbl))) EXCEPT SELECT q1 FROM int8_tbl ORDER BY 1; -- -- Subqueries with ORDER BY & LIMIT clauses -- -- In this syntax, ORDER BY/LIMIT apply to the result of the EXCEPT SELECT q1,q2 FROM int8_tbl EXCEPT SELECT q2,q1 FROM int8_tbl ORDER BY q2,q1; -- This should fail, because q2 isn't a name of an EXCEPT output column SELECT q1 FROM int8_tbl EXCEPT SELECT q2 FROM int8_tbl ORDER BY q2 LIMIT 1; -- But this should work: SELECT q1 FROM int8_tbl EXCEPT (((SELECT q2 FROM int8_tbl ORDER BY q2 LIMIT 1))) ORDER BY 1; -- -- New syntaxes (7.1) permit new tests -- (((((select * from int8_tbl))))); -- -- Check behavior with empty select list (allowed since 9.4) -- select union select; select intersect select; select except select; -- check hashed implementation set enable_hashagg = true; set enable_sort = false; explain (costs off) select from generate_series(1,5) union select from generate_series(1,3); explain (costs off) select from generate_series(1,5) intersect select from generate_series(1,3); select from generate_series(1,5) union select from generate_series(1,3); select from generate_series(1,5) union all select from generate_series(1,3); select from generate_series(1,5) intersect select from generate_series(1,3); select from generate_series(1,5) intersect all select from generate_series(1,3); select from generate_series(1,5) except select from generate_series(1,3); select from generate_series(1,5) except all select from generate_series(1,3); -- check sorted implementation set enable_hashagg = false; set enable_sort = true; explain (costs off) select from generate_series(1,5) union select from generate_series(1,3); explain (costs off) select from generate_series(1,5) intersect select from generate_series(1,3); select from generate_series(1,5) union select from generate_series(1,3); select from generate_series(1,5) union all select from generate_series(1,3); select from generate_series(1,5) intersect select from generate_series(1,3); select from generate_series(1,5) intersect all select from generate_series(1,3); select from generate_series(1,5) except select from generate_series(1,3); select from generate_series(1,5) except all select from generate_series(1,3); reset enable_hashagg; reset enable_sort; -- -- Check handling of a case with unknown constants. We don't guarantee -- an undecorated constant will work in all cases, but historically this -- usage has worked, so test we don't break it. -- SELECT a.f1 FROM (SELECT 'test' AS f1 FROM varchar_tbl) a UNION SELECT b.f1 FROM (SELECT f1 FROM varchar_tbl) b ORDER BY 1; -- This should fail, but it should produce an error cursor SELECT '3.4'::numeric UNION SELECT 'foo'; -- -- Test that expression-index constraints can be pushed down through -- UNION or UNION ALL -- CREATE TEMP TABLE t1 (a text, b text); CREATE INDEX t1_ab_idx on t1 ((a || b)); CREATE TEMP TABLE t2 (ab text primary key); INSERT INTO t1 VALUES ('a', 'b'), ('x', 'y'); INSERT INTO t2 VALUES ('ab'), ('xy'); set enable_seqscan = off; set enable_indexscan = on; set enable_bitmapscan = off; explain (costs off) SELECT * FROM (SELECT a || b AS ab FROM t1 UNION ALL SELECT * FROM t2) t WHERE ab = 'ab'; explain (costs off) SELECT * FROM (SELECT a || b AS ab FROM t1 UNION SELECT * FROM t2) t WHERE ab = 'ab'; -- -- Test that ORDER BY for UNION ALL can be pushed down to inheritance -- children. -- CREATE TEMP TABLE t1c (b text, a text); ALTER TABLE t1c INHERIT t1; CREATE TEMP TABLE t2c (primary key (ab)) INHERITS (t2); INSERT INTO t1c VALUES ('v', 'w'), ('c', 'd'), ('m', 'n'), ('e', 'f'); INSERT INTO t2c VALUES ('vw'), ('cd'), ('mn'), ('ef'); CREATE INDEX t1c_ab_idx on t1c ((a || b)); set enable_seqscan = on; set enable_indexonlyscan = off; explain (costs off) SELECT * FROM (SELECT a || b AS ab FROM t1 UNION ALL SELECT ab FROM t2) t ORDER BY 1 LIMIT 8; SELECT * FROM (SELECT a || b AS ab FROM t1 UNION ALL SELECT ab FROM t2) t ORDER BY 1 LIMIT 8; reset enable_seqscan; reset enable_indexscan; reset enable_bitmapscan; -- This simpler variant of the above test has been observed to fail differently create table events (event_id int primary key); create table other_events (event_id int primary key); create table events_child () inherits (events); explain (costs off) select event_id from (select event_id from events union all select event_id from other_events) ss order by event_id; drop table events_child, events, other_events; reset enable_indexonlyscan; -- Test constraint exclusion of UNION ALL subqueries explain (costs off) SELECT * FROM (SELECT 1 AS t, * FROM tenk1 a UNION ALL SELECT 2 AS t, * FROM tenk1 b) c WHERE t = 2; -- Test that we push quals into UNION sub-selects only when it's safe explain (costs off) SELECT * FROM (SELECT 1 AS t, 2 AS x UNION SELECT 2 AS t, 4 AS x) ss WHERE x < 4 ORDER BY x; SELECT * FROM (SELECT 1 AS t, 2 AS x UNION SELECT 2 AS t, 4 AS x) ss WHERE x < 4 ORDER BY x; explain (costs off) SELECT * FROM (SELECT 1 AS t, generate_series(1,10) AS x UNION SELECT 2 AS t, 4 AS x) ss WHERE x < 4 ORDER BY x; SELECT * FROM (SELECT 1 AS t, generate_series(1,10) AS x UNION SELECT 2 AS t, 4 AS x) ss WHERE x < 4 ORDER BY x; explain (costs off) SELECT * FROM (SELECT 1 AS t, (random()*3)::int AS x UNION SELECT 2 AS t, 4 AS x) ss WHERE x > 3 ORDER BY x; SELECT * FROM (SELECT 1 AS t, (random()*3)::int AS x UNION SELECT 2 AS t, 4 AS x) ss WHERE x > 3 ORDER BY x; -- Test proper handling of parameterized appendrel paths when the -- potential join qual is expensive create function expensivefunc(int) returns int language plpgsql immutable strict cost 10000 as $$begin return $1; end$$; create temp table t3 as select generate_series(-1000,1000) as x; create index t3i on t3 (expensivefunc(x)); analyze t3; explain (costs off) select * from (select * from t3 a union all select * from t3 b) ss join int4_tbl on f1 = expensivefunc(x); select * from (select * from t3 a union all select * from t3 b) ss join int4_tbl on f1 = expensivefunc(x); drop table t3; drop function expensivefunc(int); -- Test handling of appendrel quals that const-simplify into an AND explain (costs off) select * from (select *, 0 as x from int8_tbl a union all select *, 1 as x from int8_tbl b) ss where (x = 0) or (q1 >= q2 and q1 <= q2); select * from (select *, 0 as x from int8_tbl a union all select *, 1 as x from int8_tbl b) ss where (x = 0) or (q1 >= q2 and q1 <= q2); pgFormatter-4.2/t/pg-test-files/sql/updatable_views.sql000066400000000000000000001461301361326045100233030ustar00rootroot00000000000000-- -- UPDATABLE VIEWS -- -- avoid bit-exact output here because operations may not be bit-exact. SET extra_float_digits = 0; -- check that non-updatable views and columns are rejected with useful error -- messages CREATE TABLE base_tbl (a int PRIMARY KEY, b text DEFAULT 'Unspecified'); INSERT INTO base_tbl SELECT i, 'Row ' || i FROM generate_series(-2, 2) g(i); CREATE VIEW ro_view1 AS SELECT DISTINCT a, b FROM base_tbl; -- DISTINCT not supported CREATE VIEW ro_view2 AS SELECT a, b FROM base_tbl GROUP BY a, b; -- GROUP BY not supported CREATE VIEW ro_view3 AS SELECT 1 FROM base_tbl HAVING max(a) > 0; -- HAVING not supported CREATE VIEW ro_view4 AS SELECT count(*) FROM base_tbl; -- Aggregate functions not supported CREATE VIEW ro_view5 AS SELECT a, rank() OVER() FROM base_tbl; -- Window functions not supported CREATE VIEW ro_view6 AS SELECT a, b FROM base_tbl UNION SELECT -a, b FROM base_tbl; -- Set ops not supported CREATE VIEW ro_view7 AS WITH t AS (SELECT a, b FROM base_tbl) SELECT * FROM t; -- WITH not supported CREATE VIEW ro_view8 AS SELECT a, b FROM base_tbl ORDER BY a OFFSET 1; -- OFFSET not supported CREATE VIEW ro_view9 AS SELECT a, b FROM base_tbl ORDER BY a LIMIT 1; -- LIMIT not supported CREATE VIEW ro_view10 AS SELECT 1 AS a; -- No base relations CREATE VIEW ro_view11 AS SELECT b1.a, b2.b FROM base_tbl b1, base_tbl b2; -- Multiple base relations CREATE VIEW ro_view12 AS SELECT * FROM generate_series(1, 10) AS g(a); -- SRF in rangetable CREATE VIEW ro_view13 AS SELECT a, b FROM (SELECT * FROM base_tbl) AS t; -- Subselect in rangetable CREATE VIEW rw_view14 AS SELECT ctid, a, b FROM base_tbl; -- System columns may be part of an updatable view CREATE VIEW rw_view15 AS SELECT a, upper(b) FROM base_tbl; -- Expression/function may be part of an updatable view CREATE VIEW rw_view16 AS SELECT a, b, a AS aa FROM base_tbl; -- Repeated column may be part of an updatable view CREATE VIEW ro_view17 AS SELECT * FROM ro_view1; -- Base relation not updatable CREATE VIEW ro_view18 AS SELECT * FROM (VALUES(1)) AS tmp(a); -- VALUES in rangetable CREATE SEQUENCE uv_seq; CREATE VIEW ro_view19 AS SELECT * FROM uv_seq; -- View based on a sequence CREATE VIEW ro_view20 AS SELECT a, b, generate_series(1, a) g FROM base_tbl; -- SRF in targetlist not supported SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE E'r_\\_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name LIKE E'r_\\_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE E'r_\\_view%' ORDER BY table_name, ordinal_position; -- Read-only views DELETE FROM ro_view1; DELETE FROM ro_view2; DELETE FROM ro_view3; DELETE FROM ro_view4; DELETE FROM ro_view5; DELETE FROM ro_view6; UPDATE ro_view7 SET a=a+1; UPDATE ro_view8 SET a=a+1; UPDATE ro_view9 SET a=a+1; UPDATE ro_view10 SET a=a+1; UPDATE ro_view11 SET a=a+1; UPDATE ro_view12 SET a=a+1; INSERT INTO ro_view13 VALUES (3, 'Row 3'); -- Partially updatable view INSERT INTO rw_view14 VALUES (null, 3, 'Row 3'); -- should fail INSERT INTO rw_view14 (a, b) VALUES (3, 'Row 3'); -- should be OK UPDATE rw_view14 SET ctid=null WHERE a=3; -- should fail UPDATE rw_view14 SET b='ROW 3' WHERE a=3; -- should be OK SELECT * FROM base_tbl; DELETE FROM rw_view14 WHERE a=3; -- should be OK -- Partially updatable view INSERT INTO rw_view15 VALUES (3, 'ROW 3'); -- should fail INSERT INTO rw_view15 (a) VALUES (3); -- should be OK INSERT INTO rw_view15 (a) VALUES (3) ON CONFLICT DO NOTHING; -- succeeds SELECT * FROM rw_view15; INSERT INTO rw_view15 (a) VALUES (3) ON CONFLICT (a) DO NOTHING; -- succeeds SELECT * FROM rw_view15; INSERT INTO rw_view15 (a) VALUES (3) ON CONFLICT (a) DO UPDATE set a = excluded.a; -- succeeds SELECT * FROM rw_view15; INSERT INTO rw_view15 (a) VALUES (3) ON CONFLICT (a) DO UPDATE set upper = 'blarg'; -- fails SELECT * FROM rw_view15; SELECT * FROM rw_view15; ALTER VIEW rw_view15 ALTER COLUMN upper SET DEFAULT 'NOT SET'; INSERT INTO rw_view15 (a) VALUES (4); -- should fail UPDATE rw_view15 SET upper='ROW 3' WHERE a=3; -- should fail UPDATE rw_view15 SET upper=DEFAULT WHERE a=3; -- should fail UPDATE rw_view15 SET a=4 WHERE a=3; -- should be OK SELECT * FROM base_tbl; DELETE FROM rw_view15 WHERE a=4; -- should be OK -- Partially updatable view INSERT INTO rw_view16 VALUES (3, 'Row 3', 3); -- should fail INSERT INTO rw_view16 (a, b) VALUES (3, 'Row 3'); -- should be OK UPDATE rw_view16 SET a=3, aa=-3 WHERE a=3; -- should fail UPDATE rw_view16 SET aa=-3 WHERE a=3; -- should be OK SELECT * FROM base_tbl; DELETE FROM rw_view16 WHERE a=-3; -- should be OK -- Read-only views INSERT INTO ro_view17 VALUES (3, 'ROW 3'); DELETE FROM ro_view18; UPDATE ro_view19 SET last_value=1000; UPDATE ro_view20 SET b=upper(b); DROP TABLE base_tbl CASCADE; DROP VIEW ro_view10, ro_view12, ro_view18; DROP SEQUENCE uv_seq CASCADE; -- simple updatable view CREATE TABLE base_tbl (a int PRIMARY KEY, b text DEFAULT 'Unspecified'); INSERT INTO base_tbl SELECT i, 'Row ' || i FROM generate_series(-2, 2) g(i); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a>0; SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name = 'rw_view1'; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name = 'rw_view1'; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name = 'rw_view1' ORDER BY ordinal_position; INSERT INTO rw_view1 VALUES (3, 'Row 3'); INSERT INTO rw_view1 (a) VALUES (4); UPDATE rw_view1 SET a=5 WHERE a=4; DELETE FROM rw_view1 WHERE b='Row 2'; SELECT * FROM base_tbl; EXPLAIN (costs off) UPDATE rw_view1 SET a=6 WHERE a=5; EXPLAIN (costs off) DELETE FROM rw_view1 WHERE a=5; DROP TABLE base_tbl CASCADE; -- view on top of view CREATE TABLE base_tbl (a int PRIMARY KEY, b text DEFAULT 'Unspecified'); INSERT INTO base_tbl SELECT i, 'Row ' || i FROM generate_series(-2, 2) g(i); CREATE VIEW rw_view1 AS SELECT b AS bb, a AS aa FROM base_tbl WHERE a>0; CREATE VIEW rw_view2 AS SELECT aa AS aaa, bb AS bbb FROM rw_view1 WHERE aa<10; SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name = 'rw_view2'; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name = 'rw_view2'; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name = 'rw_view2' ORDER BY ordinal_position; INSERT INTO rw_view2 VALUES (3, 'Row 3'); INSERT INTO rw_view2 (aaa) VALUES (4); SELECT * FROM rw_view2; UPDATE rw_view2 SET bbb='Row 4' WHERE aaa=4; DELETE FROM rw_view2 WHERE aaa=2; SELECT * FROM rw_view2; EXPLAIN (costs off) UPDATE rw_view2 SET aaa=5 WHERE aaa=4; EXPLAIN (costs off) DELETE FROM rw_view2 WHERE aaa=4; DROP TABLE base_tbl CASCADE; -- view on top of view with rules CREATE TABLE base_tbl (a int PRIMARY KEY, b text DEFAULT 'Unspecified'); INSERT INTO base_tbl SELECT i, 'Row ' || i FROM generate_series(-2, 2) g(i); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a>0 OFFSET 0; -- not updatable without rules/triggers CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a<10; SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE 'rw_view%' ORDER BY table_name, ordinal_position; CREATE RULE rw_view1_ins_rule AS ON INSERT TO rw_view1 DO INSTEAD INSERT INTO base_tbl VALUES (NEW.a, NEW.b) RETURNING *; SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE 'rw_view%' ORDER BY table_name, ordinal_position; CREATE RULE rw_view1_upd_rule AS ON UPDATE TO rw_view1 DO INSTEAD UPDATE base_tbl SET b=NEW.b WHERE a=OLD.a RETURNING NEW.*; SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE 'rw_view%' ORDER BY table_name, ordinal_position; CREATE RULE rw_view1_del_rule AS ON DELETE TO rw_view1 DO INSTEAD DELETE FROM base_tbl WHERE a=OLD.a RETURNING OLD.*; SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE 'rw_view%' ORDER BY table_name, ordinal_position; INSERT INTO rw_view2 VALUES (3, 'Row 3') RETURNING *; UPDATE rw_view2 SET b='Row three' WHERE a=3 RETURNING *; SELECT * FROM rw_view2; DELETE FROM rw_view2 WHERE a=3 RETURNING *; SELECT * FROM rw_view2; EXPLAIN (costs off) UPDATE rw_view2 SET a=3 WHERE a=2; EXPLAIN (costs off) DELETE FROM rw_view2 WHERE a=2; DROP TABLE base_tbl CASCADE; -- view on top of view with triggers CREATE TABLE base_tbl (a int PRIMARY KEY, b text DEFAULT 'Unspecified'); INSERT INTO base_tbl SELECT i, 'Row ' || i FROM generate_series(-2, 2) g(i); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a>0 OFFSET 0; -- not updatable without rules/triggers CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a<10; SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into, is_trigger_updatable, is_trigger_deletable, is_trigger_insertable_into FROM information_schema.views WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE 'rw_view%' ORDER BY table_name, ordinal_position; CREATE FUNCTION rw_view1_trig_fn() RETURNS trigger AS $$ BEGIN IF TG_OP = 'INSERT' THEN INSERT INTO base_tbl VALUES (NEW.a, NEW.b); RETURN NEW; ELSIF TG_OP = 'UPDATE' THEN UPDATE base_tbl SET b=NEW.b WHERE a=OLD.a; RETURN NEW; ELSIF TG_OP = 'DELETE' THEN DELETE FROM base_tbl WHERE a=OLD.a; RETURN OLD; END IF; END; $$ LANGUAGE plpgsql; CREATE TRIGGER rw_view1_ins_trig INSTEAD OF INSERT ON rw_view1 FOR EACH ROW EXECUTE PROCEDURE rw_view1_trig_fn(); SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into, is_trigger_updatable, is_trigger_deletable, is_trigger_insertable_into FROM information_schema.views WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE 'rw_view%' ORDER BY table_name, ordinal_position; CREATE TRIGGER rw_view1_upd_trig INSTEAD OF UPDATE ON rw_view1 FOR EACH ROW EXECUTE PROCEDURE rw_view1_trig_fn(); SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into, is_trigger_updatable, is_trigger_deletable, is_trigger_insertable_into FROM information_schema.views WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE 'rw_view%' ORDER BY table_name, ordinal_position; CREATE TRIGGER rw_view1_del_trig INSTEAD OF DELETE ON rw_view1 FOR EACH ROW EXECUTE PROCEDURE rw_view1_trig_fn(); SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into, is_trigger_updatable, is_trigger_deletable, is_trigger_insertable_into FROM information_schema.views WHERE table_name LIKE 'rw_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE 'rw_view%' ORDER BY table_name, ordinal_position; INSERT INTO rw_view2 VALUES (3, 'Row 3') RETURNING *; UPDATE rw_view2 SET b='Row three' WHERE a=3 RETURNING *; SELECT * FROM rw_view2; DELETE FROM rw_view2 WHERE a=3 RETURNING *; SELECT * FROM rw_view2; EXPLAIN (costs off) UPDATE rw_view2 SET a=3 WHERE a=2; EXPLAIN (costs off) DELETE FROM rw_view2 WHERE a=2; DROP TABLE base_tbl CASCADE; DROP FUNCTION rw_view1_trig_fn(); -- update using whole row from view CREATE TABLE base_tbl (a int PRIMARY KEY, b text DEFAULT 'Unspecified'); INSERT INTO base_tbl SELECT i, 'Row ' || i FROM generate_series(-2, 2) g(i); CREATE VIEW rw_view1 AS SELECT b AS bb, a AS aa FROM base_tbl; CREATE FUNCTION rw_view1_aa(x rw_view1) RETURNS int AS $$ SELECT x.aa $$ LANGUAGE sql; UPDATE rw_view1 v SET bb='Updated row 2' WHERE rw_view1_aa(v)=2 RETURNING rw_view1_aa(v), v.bb; SELECT * FROM base_tbl; EXPLAIN (costs off) UPDATE rw_view1 v SET bb='Updated row 2' WHERE rw_view1_aa(v)=2 RETURNING rw_view1_aa(v), v.bb; DROP TABLE base_tbl CASCADE; -- permissions checks CREATE USER regress_view_user1; CREATE USER regress_view_user2; SET SESSION AUTHORIZATION regress_view_user1; CREATE TABLE base_tbl(a int, b text, c float); INSERT INTO base_tbl VALUES (1, 'Row 1', 1.0); CREATE VIEW rw_view1 AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl; INSERT INTO rw_view1 VALUES ('Row 2', 2.0, 2); GRANT SELECT ON base_tbl TO regress_view_user2; GRANT SELECT ON rw_view1 TO regress_view_user2; GRANT UPDATE (a,c) ON base_tbl TO regress_view_user2; GRANT UPDATE (bb,cc) ON rw_view1 TO regress_view_user2; RESET SESSION AUTHORIZATION; SET SESSION AUTHORIZATION regress_view_user2; CREATE VIEW rw_view2 AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl; SELECT * FROM base_tbl; -- ok SELECT * FROM rw_view1; -- ok SELECT * FROM rw_view2; -- ok INSERT INTO base_tbl VALUES (3, 'Row 3', 3.0); -- not allowed INSERT INTO rw_view1 VALUES ('Row 3', 3.0, 3); -- not allowed INSERT INTO rw_view2 VALUES ('Row 3', 3.0, 3); -- not allowed UPDATE base_tbl SET a=a, c=c; -- ok UPDATE base_tbl SET b=b; -- not allowed UPDATE rw_view1 SET bb=bb, cc=cc; -- ok UPDATE rw_view1 SET aa=aa; -- not allowed UPDATE rw_view2 SET aa=aa, cc=cc; -- ok UPDATE rw_view2 SET bb=bb; -- not allowed DELETE FROM base_tbl; -- not allowed DELETE FROM rw_view1; -- not allowed DELETE FROM rw_view2; -- not allowed RESET SESSION AUTHORIZATION; SET SESSION AUTHORIZATION regress_view_user1; GRANT INSERT, DELETE ON base_tbl TO regress_view_user2; RESET SESSION AUTHORIZATION; SET SESSION AUTHORIZATION regress_view_user2; INSERT INTO base_tbl VALUES (3, 'Row 3', 3.0); -- ok INSERT INTO rw_view1 VALUES ('Row 4', 4.0, 4); -- not allowed INSERT INTO rw_view2 VALUES ('Row 4', 4.0, 4); -- ok DELETE FROM base_tbl WHERE a=1; -- ok DELETE FROM rw_view1 WHERE aa=2; -- not allowed DELETE FROM rw_view2 WHERE aa=2; -- ok SELECT * FROM base_tbl; RESET SESSION AUTHORIZATION; SET SESSION AUTHORIZATION regress_view_user1; REVOKE INSERT, DELETE ON base_tbl FROM regress_view_user2; GRANT INSERT, DELETE ON rw_view1 TO regress_view_user2; RESET SESSION AUTHORIZATION; SET SESSION AUTHORIZATION regress_view_user2; INSERT INTO base_tbl VALUES (5, 'Row 5', 5.0); -- not allowed INSERT INTO rw_view1 VALUES ('Row 5', 5.0, 5); -- ok INSERT INTO rw_view2 VALUES ('Row 6', 6.0, 6); -- not allowed DELETE FROM base_tbl WHERE a=3; -- not allowed DELETE FROM rw_view1 WHERE aa=3; -- ok DELETE FROM rw_view2 WHERE aa=4; -- not allowed SELECT * FROM base_tbl; RESET SESSION AUTHORIZATION; DROP TABLE base_tbl CASCADE; -- nested-view permissions CREATE TABLE base_tbl(a int, b text, c float); INSERT INTO base_tbl VALUES (1, 'Row 1', 1.0); SET SESSION AUTHORIZATION regress_view_user1; CREATE VIEW rw_view1 AS SELECT * FROM base_tbl; SELECT * FROM rw_view1; -- not allowed SELECT * FROM rw_view1 FOR UPDATE; -- not allowed UPDATE rw_view1 SET b = 'foo' WHERE a = 1; -- not allowed SET SESSION AUTHORIZATION regress_view_user2; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1; SELECT * FROM rw_view2; -- not allowed SELECT * FROM rw_view2 FOR UPDATE; -- not allowed UPDATE rw_view2 SET b = 'bar' WHERE a = 1; -- not allowed RESET SESSION AUTHORIZATION; GRANT SELECT ON base_tbl TO regress_view_user1; SET SESSION AUTHORIZATION regress_view_user1; SELECT * FROM rw_view1; SELECT * FROM rw_view1 FOR UPDATE; -- not allowed UPDATE rw_view1 SET b = 'foo' WHERE a = 1; -- not allowed SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; -- not allowed SELECT * FROM rw_view2 FOR UPDATE; -- not allowed UPDATE rw_view2 SET b = 'bar' WHERE a = 1; -- not allowed SET SESSION AUTHORIZATION regress_view_user1; GRANT SELECT ON rw_view1 TO regress_view_user2; SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; SELECT * FROM rw_view2 FOR UPDATE; -- not allowed UPDATE rw_view2 SET b = 'bar' WHERE a = 1; -- not allowed RESET SESSION AUTHORIZATION; GRANT UPDATE ON base_tbl TO regress_view_user1; SET SESSION AUTHORIZATION regress_view_user1; SELECT * FROM rw_view1; SELECT * FROM rw_view1 FOR UPDATE; UPDATE rw_view1 SET b = 'foo' WHERE a = 1; SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; SELECT * FROM rw_view2 FOR UPDATE; -- not allowed UPDATE rw_view2 SET b = 'bar' WHERE a = 1; -- not allowed SET SESSION AUTHORIZATION regress_view_user1; GRANT UPDATE ON rw_view1 TO regress_view_user2; SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; SELECT * FROM rw_view2 FOR UPDATE; UPDATE rw_view2 SET b = 'bar' WHERE a = 1; RESET SESSION AUTHORIZATION; REVOKE UPDATE ON base_tbl FROM regress_view_user1; SET SESSION AUTHORIZATION regress_view_user1; SELECT * FROM rw_view1; SELECT * FROM rw_view1 FOR UPDATE; -- not allowed UPDATE rw_view1 SET b = 'foo' WHERE a = 1; -- not allowed SET SESSION AUTHORIZATION regress_view_user2; SELECT * FROM rw_view2; SELECT * FROM rw_view2 FOR UPDATE; -- not allowed UPDATE rw_view2 SET b = 'bar' WHERE a = 1; -- not allowed RESET SESSION AUTHORIZATION; DROP TABLE base_tbl CASCADE; DROP USER regress_view_user1; DROP USER regress_view_user2; -- column defaults CREATE TABLE base_tbl (a int PRIMARY KEY, b text DEFAULT 'Unspecified', c serial); INSERT INTO base_tbl VALUES (1, 'Row 1'); INSERT INTO base_tbl VALUES (2, 'Row 2'); INSERT INTO base_tbl VALUES (3); CREATE VIEW rw_view1 AS SELECT a AS aa, b AS bb FROM base_tbl; ALTER VIEW rw_view1 ALTER COLUMN bb SET DEFAULT 'View default'; INSERT INTO rw_view1 VALUES (4, 'Row 4'); INSERT INTO rw_view1 (aa) VALUES (5); SELECT * FROM base_tbl; DROP TABLE base_tbl CASCADE; -- Table having triggers CREATE TABLE base_tbl (a int PRIMARY KEY, b text DEFAULT 'Unspecified'); INSERT INTO base_tbl VALUES (1, 'Row 1'); INSERT INTO base_tbl VALUES (2, 'Row 2'); CREATE FUNCTION rw_view1_trig_fn() RETURNS trigger AS $$ BEGIN IF TG_OP = 'INSERT' THEN UPDATE base_tbl SET b=NEW.b WHERE a=1; RETURN NULL; END IF; RETURN NULL; END; $$ LANGUAGE plpgsql; CREATE TRIGGER rw_view1_ins_trig AFTER INSERT ON base_tbl FOR EACH ROW EXECUTE PROCEDURE rw_view1_trig_fn(); CREATE VIEW rw_view1 AS SELECT a AS aa, b AS bb FROM base_tbl; INSERT INTO rw_view1 VALUES (3, 'Row 3'); select * from base_tbl; DROP VIEW rw_view1; DROP TRIGGER rw_view1_ins_trig on base_tbl; DROP FUNCTION rw_view1_trig_fn(); DROP TABLE base_tbl; -- view with ORDER BY CREATE TABLE base_tbl (a int, b int); INSERT INTO base_tbl VALUES (1,2), (4,5), (3,-3); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl ORDER BY a+b; SELECT * FROM rw_view1; INSERT INTO rw_view1 VALUES (7,-8); SELECT * FROM rw_view1; EXPLAIN (verbose, costs off) UPDATE rw_view1 SET b = b + 1 RETURNING *; UPDATE rw_view1 SET b = b + 1 RETURNING *; SELECT * FROM rw_view1; DROP TABLE base_tbl CASCADE; -- multiple array-column updates CREATE TABLE base_tbl (a int, arr int[]); INSERT INTO base_tbl VALUES (1,ARRAY[2]), (3,ARRAY[4]); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl; UPDATE rw_view1 SET arr[1] = 42, arr[2] = 77 WHERE a = 3; SELECT * FROM rw_view1; DROP TABLE base_tbl CASCADE; -- views with updatable and non-updatable columns CREATE TABLE base_tbl(a float); INSERT INTO base_tbl SELECT i/10.0 FROM generate_series(1,10) g(i); CREATE VIEW rw_view1 AS SELECT ctid, sin(a) s, a, cos(a) c FROM base_tbl WHERE a != 0 ORDER BY abs(a); INSERT INTO rw_view1 VALUES (null, null, 1.1, null); -- should fail INSERT INTO rw_view1 (s, c, a) VALUES (null, null, 1.1); -- should fail INSERT INTO rw_view1 (a) VALUES (1.1) RETURNING a, s, c; -- OK UPDATE rw_view1 SET s = s WHERE a = 1.1; -- should fail UPDATE rw_view1 SET a = 1.05 WHERE a = 1.1 RETURNING s; -- OK DELETE FROM rw_view1 WHERE a = 1.05; -- OK CREATE VIEW rw_view2 AS SELECT s, c, s/c t, a base_a, ctid FROM rw_view1; INSERT INTO rw_view2 VALUES (null, null, null, 1.1, null); -- should fail INSERT INTO rw_view2(s, c, base_a) VALUES (null, null, 1.1); -- should fail INSERT INTO rw_view2(base_a) VALUES (1.1) RETURNING t; -- OK UPDATE rw_view2 SET s = s WHERE base_a = 1.1; -- should fail UPDATE rw_view2 SET t = t WHERE base_a = 1.1; -- should fail UPDATE rw_view2 SET base_a = 1.05 WHERE base_a = 1.1; -- OK DELETE FROM rw_view2 WHERE base_a = 1.05 RETURNING base_a, s, c, t; -- OK CREATE VIEW rw_view3 AS SELECT s, c, s/c t, ctid FROM rw_view1; INSERT INTO rw_view3 VALUES (null, null, null, null); -- should fail INSERT INTO rw_view3(s) VALUES (null); -- should fail UPDATE rw_view3 SET s = s; -- should fail DELETE FROM rw_view3 WHERE s = sin(0.1); -- should be OK SELECT * FROM base_tbl ORDER BY a; SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name LIKE E'r_\\_view%' ORDER BY table_name; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name LIKE E'r_\\_view%' ORDER BY table_name; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name LIKE E'r_\\_view%' ORDER BY table_name, ordinal_position; SELECT events & 4 != 0 AS upd, events & 8 != 0 AS ins, events & 16 != 0 AS del FROM pg_catalog.pg_relation_is_updatable('rw_view3'::regclass, false) t(events); DROP TABLE base_tbl CASCADE; -- inheritance tests CREATE TABLE base_tbl_parent (a int); CREATE TABLE base_tbl_child (CHECK (a > 0)) INHERITS (base_tbl_parent); INSERT INTO base_tbl_parent SELECT * FROM generate_series(-8, -1); INSERT INTO base_tbl_child SELECT * FROM generate_series(1, 8); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl_parent; CREATE VIEW rw_view2 AS SELECT * FROM ONLY base_tbl_parent; SELECT * FROM rw_view1 ORDER BY a; SELECT * FROM ONLY rw_view1 ORDER BY a; SELECT * FROM rw_view2 ORDER BY a; INSERT INTO rw_view1 VALUES (-100), (100); INSERT INTO rw_view2 VALUES (-200), (200); UPDATE rw_view1 SET a = a*10 WHERE a IN (-1, 1); -- Should produce -10 and 10 UPDATE ONLY rw_view1 SET a = a*10 WHERE a IN (-2, 2); -- Should produce -20 and 20 UPDATE rw_view2 SET a = a*10 WHERE a IN (-3, 3); -- Should produce -30 only UPDATE ONLY rw_view2 SET a = a*10 WHERE a IN (-4, 4); -- Should produce -40 only DELETE FROM rw_view1 WHERE a IN (-5, 5); -- Should delete -5 and 5 DELETE FROM ONLY rw_view1 WHERE a IN (-6, 6); -- Should delete -6 and 6 DELETE FROM rw_view2 WHERE a IN (-7, 7); -- Should delete -7 only DELETE FROM ONLY rw_view2 WHERE a IN (-8, 8); -- Should delete -8 only SELECT * FROM ONLY base_tbl_parent ORDER BY a; SELECT * FROM base_tbl_child ORDER BY a; CREATE TABLE other_tbl_parent (id int); CREATE TABLE other_tbl_child () INHERITS (other_tbl_parent); INSERT INTO other_tbl_parent VALUES (7),(200); INSERT INTO other_tbl_child VALUES (8),(100); EXPLAIN (costs off) UPDATE rw_view1 SET a = a + 1000 FROM other_tbl_parent WHERE a = id; UPDATE rw_view1 SET a = a + 1000 FROM other_tbl_parent WHERE a = id; SELECT * FROM ONLY base_tbl_parent ORDER BY a; SELECT * FROM base_tbl_child ORDER BY a; DROP TABLE base_tbl_parent, base_tbl_child CASCADE; DROP TABLE other_tbl_parent CASCADE; -- simple WITH CHECK OPTION CREATE TABLE base_tbl (a int, b int DEFAULT 10); INSERT INTO base_tbl VALUES (1,2), (2,3), (1,-1); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b WITH LOCAL CHECK OPTION; \d+ rw_view1 SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; INSERT INTO rw_view1 VALUES(3,4); -- ok INSERT INTO rw_view1 VALUES(4,3); -- should fail INSERT INTO rw_view1 VALUES(5,null); -- should fail UPDATE rw_view1 SET b = 5 WHERE a = 3; -- ok UPDATE rw_view1 SET b = -5 WHERE a = 3; -- should fail INSERT INTO rw_view1(a) VALUES (9); -- ok INSERT INTO rw_view1(a) VALUES (10); -- should fail SELECT * FROM base_tbl; DROP TABLE base_tbl CASCADE; -- WITH LOCAL/CASCADED CHECK OPTION CREATE TABLE base_tbl (a int); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a > 0; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 WITH CHECK OPTION; -- implicitly cascaded \d+ rw_view2 SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; INSERT INTO rw_view2 VALUES (-5); -- should fail INSERT INTO rw_view2 VALUES (5); -- ok INSERT INTO rw_view2 VALUES (15); -- should fail SELECT * FROM base_tbl; UPDATE rw_view2 SET a = a - 10; -- should fail UPDATE rw_view2 SET a = a + 10; -- should fail CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 WITH LOCAL CHECK OPTION; \d+ rw_view2 SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view INSERT INTO rw_view2 VALUES (20); -- should fail SELECT * FROM base_tbl; ALTER VIEW rw_view1 SET (check_option=here); -- invalid ALTER VIEW rw_view1 SET (check_option=local); INSERT INTO rw_view2 VALUES (-20); -- should fail INSERT INTO rw_view2 VALUES (30); -- should fail ALTER VIEW rw_view2 RESET (check_option); \d+ rw_view2 SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; INSERT INTO rw_view2 VALUES (30); -- ok, but not in view SELECT * FROM base_tbl; DROP TABLE base_tbl CASCADE; -- WITH CHECK OPTION with no local view qual CREATE TABLE base_tbl (a int); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; INSERT INTO rw_view1 VALUES (-1); -- ok INSERT INTO rw_view1 VALUES (1); -- ok INSERT INTO rw_view2 VALUES (-2); -- ok, but not in view INSERT INTO rw_view2 VALUES (2); -- ok INSERT INTO rw_view3 VALUES (-3); -- should fail INSERT INTO rw_view3 VALUES (3); -- ok DROP TABLE base_tbl CASCADE; -- WITH CHECK OPTION with scalar array ops CREATE TABLE base_tbl (a int, b int[]); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a = ANY (b) WITH CHECK OPTION; INSERT INTO rw_view1 VALUES (1, ARRAY[1,2,3]); -- ok INSERT INTO rw_view1 VALUES (10, ARRAY[4,5]); -- should fail UPDATE rw_view1 SET b[2] = -b[2] WHERE a = 1; -- ok UPDATE rw_view1 SET b[1] = -b[1] WHERE a = 1; -- should fail PREPARE ins(int, int[]) AS INSERT INTO rw_view1 VALUES($1, $2); EXECUTE ins(2, ARRAY[1,2,3]); -- ok EXECUTE ins(10, ARRAY[4,5]); -- should fail DEALLOCATE PREPARE ins; DROP TABLE base_tbl CASCADE; -- WITH CHECK OPTION with subquery CREATE TABLE base_tbl (a int); CREATE TABLE ref_tbl (a int PRIMARY KEY); INSERT INTO ref_tbl SELECT * FROM generate_series(1,10); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl b WHERE EXISTS(SELECT 1 FROM ref_tbl r WHERE r.a = b.a) WITH CHECK OPTION; INSERT INTO rw_view1 VALUES (5); -- ok INSERT INTO rw_view1 VALUES (15); -- should fail UPDATE rw_view1 SET a = a + 5; -- ok UPDATE rw_view1 SET a = a + 5; -- should fail EXPLAIN (costs off) INSERT INTO rw_view1 VALUES (5); EXPLAIN (costs off) UPDATE rw_view1 SET a = a + 5; DROP TABLE base_tbl, ref_tbl CASCADE; -- WITH CHECK OPTION with BEFORE trigger on base table CREATE TABLE base_tbl (a int, b int); CREATE FUNCTION base_tbl_trig_fn() RETURNS trigger AS $$ BEGIN NEW.b := 10; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER base_tbl_trig BEFORE INSERT OR UPDATE ON base_tbl FOR EACH ROW EXECUTE PROCEDURE base_tbl_trig_fn(); CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b WITH CHECK OPTION; INSERT INTO rw_view1 VALUES (5,0); -- ok INSERT INTO rw_view1 VALUES (15, 20); -- should fail UPDATE rw_view1 SET a = 20, b = 30; -- should fail DROP TABLE base_tbl CASCADE; DROP FUNCTION base_tbl_trig_fn(); -- WITH LOCAL CHECK OPTION with INSTEAD OF trigger on base view CREATE TABLE base_tbl (a int, b int); CREATE VIEW rw_view1 AS SELECT a FROM base_tbl WHERE a < b; CREATE FUNCTION rw_view1_trig_fn() RETURNS trigger AS $$ BEGIN IF TG_OP = 'INSERT' THEN INSERT INTO base_tbl VALUES (NEW.a, 10); RETURN NEW; ELSIF TG_OP = 'UPDATE' THEN UPDATE base_tbl SET a=NEW.a WHERE a=OLD.a; RETURN NEW; ELSIF TG_OP = 'DELETE' THEN DELETE FROM base_tbl WHERE a=OLD.a; RETURN OLD; END IF; END; $$ LANGUAGE plpgsql; CREATE TRIGGER rw_view1_trig INSTEAD OF INSERT OR UPDATE OR DELETE ON rw_view1 FOR EACH ROW EXECUTE PROCEDURE rw_view1_trig_fn(); CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0 WITH LOCAL CHECK OPTION; INSERT INTO rw_view2 VALUES (-5); -- should fail INSERT INTO rw_view2 VALUES (5); -- ok INSERT INTO rw_view2 VALUES (50); -- ok, but not in view UPDATE rw_view2 SET a = a - 10; -- should fail SELECT * FROM base_tbl; -- Check option won't cascade down to base view with INSTEAD OF triggers ALTER VIEW rw_view2 SET (check_option=cascaded); INSERT INTO rw_view2 VALUES (100); -- ok, but not in view (doesn't fail rw_view1's check) UPDATE rw_view2 SET a = 200 WHERE a = 5; -- ok, but not in view (doesn't fail rw_view1's check) SELECT * FROM base_tbl; -- Neither local nor cascaded check options work with INSTEAD rules DROP TRIGGER rw_view1_trig ON rw_view1; CREATE RULE rw_view1_ins_rule AS ON INSERT TO rw_view1 DO INSTEAD INSERT INTO base_tbl VALUES (NEW.a, 10); CREATE RULE rw_view1_upd_rule AS ON UPDATE TO rw_view1 DO INSTEAD UPDATE base_tbl SET a=NEW.a WHERE a=OLD.a; INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view (doesn't fail rw_view2's check) INSERT INTO rw_view2 VALUES (5); -- ok INSERT INTO rw_view2 VALUES (20); -- ok, but not in view (doesn't fail rw_view1's check) UPDATE rw_view2 SET a = 30 WHERE a = 5; -- ok, but not in view (doesn't fail rw_view1's check) INSERT INTO rw_view2 VALUES (5); -- ok UPDATE rw_view2 SET a = -5 WHERE a = 5; -- ok, but not in view (doesn't fail rw_view2's check) SELECT * FROM base_tbl; DROP TABLE base_tbl CASCADE; DROP FUNCTION rw_view1_trig_fn(); CREATE TABLE base_tbl (a int); CREATE VIEW rw_view1 AS SELECT a,10 AS b FROM base_tbl; CREATE RULE rw_view1_ins_rule AS ON INSERT TO rw_view1 DO INSTEAD INSERT INTO base_tbl VALUES (NEW.a); CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > b WITH LOCAL CHECK OPTION; INSERT INTO rw_view2 VALUES (2,3); -- ok, but not in view (doesn't fail rw_view2's check) DROP TABLE base_tbl CASCADE; -- security barrier view CREATE TABLE base_tbl (person text, visibility text); INSERT INTO base_tbl VALUES ('Tom', 'public'), ('Dick', 'private'), ('Harry', 'public'); CREATE VIEW rw_view1 AS SELECT person FROM base_tbl WHERE visibility = 'public'; CREATE FUNCTION snoop(anyelement) RETURNS boolean AS $$ BEGIN RAISE NOTICE 'snooped value: %', $1; RETURN true; END; $$ LANGUAGE plpgsql COST 0.000001; CREATE OR REPLACE FUNCTION leakproof(anyelement) RETURNS boolean AS $$ BEGIN RETURN true; END; $$ LANGUAGE plpgsql STRICT IMMUTABLE LEAKPROOF; SELECT * FROM rw_view1 WHERE snoop(person); UPDATE rw_view1 SET person=person WHERE snoop(person); DELETE FROM rw_view1 WHERE NOT snoop(person); ALTER VIEW rw_view1 SET (security_barrier = true); SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name = 'rw_view1'; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name = 'rw_view1'; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name = 'rw_view1' ORDER BY ordinal_position; SELECT * FROM rw_view1 WHERE snoop(person); UPDATE rw_view1 SET person=person WHERE snoop(person); DELETE FROM rw_view1 WHERE NOT snoop(person); EXPLAIN (costs off) SELECT * FROM rw_view1 WHERE snoop(person); EXPLAIN (costs off) UPDATE rw_view1 SET person=person WHERE snoop(person); EXPLAIN (costs off) DELETE FROM rw_view1 WHERE NOT snoop(person); -- security barrier view on top of security barrier view CREATE VIEW rw_view2 WITH (security_barrier = true) AS SELECT * FROM rw_view1 WHERE snoop(person); SELECT table_name, is_insertable_into FROM information_schema.tables WHERE table_name = 'rw_view2'; SELECT table_name, is_updatable, is_insertable_into FROM information_schema.views WHERE table_name = 'rw_view2'; SELECT table_name, column_name, is_updatable FROM information_schema.columns WHERE table_name = 'rw_view2' ORDER BY ordinal_position; SELECT * FROM rw_view2 WHERE snoop(person); UPDATE rw_view2 SET person=person WHERE snoop(person); DELETE FROM rw_view2 WHERE NOT snoop(person); EXPLAIN (costs off) SELECT * FROM rw_view2 WHERE snoop(person); EXPLAIN (costs off) UPDATE rw_view2 SET person=person WHERE snoop(person); EXPLAIN (costs off) DELETE FROM rw_view2 WHERE NOT snoop(person); DROP TABLE base_tbl CASCADE; -- security barrier view on top of table with rules CREATE TABLE base_tbl(id int PRIMARY KEY, data text, deleted boolean); INSERT INTO base_tbl VALUES (1, 'Row 1', false), (2, 'Row 2', true); CREATE RULE base_tbl_ins_rule AS ON INSERT TO base_tbl WHERE EXISTS (SELECT 1 FROM base_tbl t WHERE t.id = new.id) DO INSTEAD UPDATE base_tbl SET data = new.data, deleted = false WHERE id = new.id; CREATE RULE base_tbl_del_rule AS ON DELETE TO base_tbl DO INSTEAD UPDATE base_tbl SET deleted = true WHERE id = old.id; CREATE VIEW rw_view1 WITH (security_barrier=true) AS SELECT id, data FROM base_tbl WHERE NOT deleted; SELECT * FROM rw_view1; EXPLAIN (costs off) DELETE FROM rw_view1 WHERE id = 1 AND snoop(data); DELETE FROM rw_view1 WHERE id = 1 AND snoop(data); EXPLAIN (costs off) INSERT INTO rw_view1 VALUES (2, 'New row 2'); INSERT INTO rw_view1 VALUES (2, 'New row 2'); SELECT * FROM base_tbl; DROP TABLE base_tbl CASCADE; -- security barrier view based on inheritance set CREATE TABLE t1 (a int, b float, c text); CREATE INDEX t1_a_idx ON t1(a); INSERT INTO t1 SELECT i,i,'t1' FROM generate_series(1,10) g(i); ANALYZE t1; CREATE TABLE t11 (d text) INHERITS (t1); CREATE INDEX t11_a_idx ON t11(a); INSERT INTO t11 SELECT i,i,'t11','t11d' FROM generate_series(1,10) g(i); ANALYZE t11; CREATE TABLE t12 (e int[]) INHERITS (t1); CREATE INDEX t12_a_idx ON t12(a); INSERT INTO t12 SELECT i,i,'t12','{1,2}'::int[] FROM generate_series(1,10) g(i); ANALYZE t12; CREATE TABLE t111 () INHERITS (t11, t12); CREATE INDEX t111_a_idx ON t111(a); INSERT INTO t111 SELECT i,i,'t111','t111d','{1,1,1}'::int[] FROM generate_series(1,10) g(i); ANALYZE t111; CREATE VIEW v1 WITH (security_barrier=true) AS SELECT *, (SELECT d FROM t11 WHERE t11.a = t1.a LIMIT 1) AS d FROM t1 WHERE a > 5 AND EXISTS(SELECT 1 FROM t12 WHERE t12.a = t1.a); SELECT * FROM v1 WHERE a=3; -- should not see anything SELECT * FROM v1 WHERE a=8; EXPLAIN (VERBOSE, COSTS OFF) UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; SELECT * FROM v1 WHERE a=100; -- Nothing should have been changed to 100 SELECT * FROM t1 WHERE a=100; -- Nothing should have been changed to 100 EXPLAIN (VERBOSE, COSTS OFF) UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; SELECT * FROM v1 WHERE b=8; DELETE FROM v1 WHERE snoop(a) AND leakproof(a); -- should not delete everything, just where a>5 TABLE t1; -- verify all a<=5 are intact DROP TABLE t1, t11, t12, t111 CASCADE; DROP FUNCTION snoop(anyelement); DROP FUNCTION leakproof(anyelement); CREATE TABLE tx1 (a integer); CREATE TABLE tx2 (b integer); CREATE TABLE tx3 (c integer); CREATE VIEW vx1 AS SELECT a FROM tx1 WHERE EXISTS(SELECT 1 FROM tx2 JOIN tx3 ON b=c); INSERT INTO vx1 values (1); SELECT * FROM tx1; SELECT * FROM vx1; DROP VIEW vx1; DROP TABLE tx1; DROP TABLE tx2; DROP TABLE tx3; CREATE TABLE tx1 (a integer); CREATE TABLE tx2 (b integer); CREATE TABLE tx3 (c integer); CREATE VIEW vx1 AS SELECT a FROM tx1 WHERE EXISTS(SELECT 1 FROM tx2 JOIN tx3 ON b=c); INSERT INTO vx1 VALUES (1); INSERT INTO vx1 VALUES (1); SELECT * FROM tx1; SELECT * FROM vx1; DROP VIEW vx1; DROP TABLE tx1; DROP TABLE tx2; DROP TABLE tx3; CREATE TABLE tx1 (a integer, b integer); CREATE TABLE tx2 (b integer, c integer); CREATE TABLE tx3 (c integer, d integer); ALTER TABLE tx1 DROP COLUMN b; ALTER TABLE tx2 DROP COLUMN c; ALTER TABLE tx3 DROP COLUMN d; CREATE VIEW vx1 AS SELECT a FROM tx1 WHERE EXISTS(SELECT 1 FROM tx2 JOIN tx3 ON b=c); INSERT INTO vx1 VALUES (1); INSERT INTO vx1 VALUES (1); SELECT * FROM tx1; SELECT * FROM vx1; DROP VIEW vx1; DROP TABLE tx1; DROP TABLE tx2; DROP TABLE tx3; -- -- Test handling of vars from correlated subqueries in quals from outer -- security barrier views, per bug #13988 -- CREATE TABLE t1 (a int, b text, c int); INSERT INTO t1 VALUES (1, 'one', 10); CREATE TABLE t2 (cc int); INSERT INTO t2 VALUES (10), (20); CREATE VIEW v1 WITH (security_barrier = true) AS SELECT * FROM t1 WHERE (a > 0) WITH CHECK OPTION; CREATE VIEW v2 WITH (security_barrier = true) AS SELECT * FROM v1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.cc = v1.c) WITH CHECK OPTION; INSERT INTO v2 VALUES (2, 'two', 20); -- ok INSERT INTO v2 VALUES (-2, 'minus two', 20); -- not allowed INSERT INTO v2 VALUES (3, 'three', 30); -- not allowed UPDATE v2 SET b = 'ONE' WHERE a = 1; -- ok UPDATE v2 SET a = -1 WHERE a = 1; -- not allowed UPDATE v2 SET c = 30 WHERE a = 1; -- not allowed DELETE FROM v2 WHERE a = 2; -- ok SELECT * FROM v2; DROP VIEW v2; DROP VIEW v1; DROP TABLE t2; DROP TABLE t1; -- -- Test CREATE OR REPLACE VIEW turning a non-updatable view into an -- auto-updatable view and adding check options in a single step -- CREATE TABLE t1 (a int, b text); CREATE VIEW v1 AS SELECT null::int AS a; CREATE OR REPLACE VIEW v1 AS SELECT * FROM t1 WHERE a > 0 WITH CHECK OPTION; INSERT INTO v1 VALUES (1, 'ok'); -- ok INSERT INTO v1 VALUES (-1, 'invalid'); -- should fail DROP VIEW v1; DROP TABLE t1; -- check that an auto-updatable view on a partitioned table works correctly create table uv_pt (a int, b int, v varchar) partition by range (a, b); create table uv_pt1 (b int not null, v varchar, a int not null) partition by range (b); create table uv_pt11 (like uv_pt1); alter table uv_pt11 drop a; alter table uv_pt11 add a int; alter table uv_pt11 drop a; alter table uv_pt11 add a int not null; alter table uv_pt1 attach partition uv_pt11 for values from (2) to (5); alter table uv_pt attach partition uv_pt1 for values from (1, 2) to (1, 10); create view uv_ptv as select * from uv_pt; select events & 4 != 0 AS upd, events & 8 != 0 AS ins, events & 16 != 0 AS del from pg_catalog.pg_relation_is_updatable('uv_pt'::regclass, false) t(events); select pg_catalog.pg_column_is_updatable('uv_pt'::regclass, 1::smallint, false); select pg_catalog.pg_column_is_updatable('uv_pt'::regclass, 2::smallint, false); select table_name, is_updatable, is_insertable_into from information_schema.views where table_name = 'uv_ptv'; select table_name, column_name, is_updatable from information_schema.columns where table_name = 'uv_ptv' order by column_name; insert into uv_ptv values (1, 2); select tableoid::regclass, * from uv_pt; create view uv_ptv_wco as select * from uv_pt where a = 0 with check option; insert into uv_ptv_wco values (1, 2); drop view uv_ptv, uv_ptv_wco; drop table uv_pt, uv_pt1, uv_pt11; -- check that wholerow vars appearing in WITH CHECK OPTION constraint expressions -- work fine with partitioned tables create table wcowrtest (a int) partition by list (a); create table wcowrtest1 partition of wcowrtest for values in (1); create view wcowrtest_v as select * from wcowrtest where wcowrtest = '(2)'::wcowrtest with check option; insert into wcowrtest_v values (1); alter table wcowrtest add b text; create table wcowrtest2 (b text, c int, a int); alter table wcowrtest2 drop c; alter table wcowrtest attach partition wcowrtest2 for values in (2); create table sometable (a int, b text); insert into sometable values (1, 'a'), (2, 'b'); create view wcowrtest_v2 as select * from wcowrtest r where r in (select s from sometable s where r.a = s.a) with check option; -- WITH CHECK qual will be processed with wcowrtest2's -- rowtype after tuple-routing insert into wcowrtest_v2 values (2, 'no such row in sometable'); drop view wcowrtest_v, wcowrtest_v2; drop table wcowrtest, sometable; -- Check INSERT .. ON CONFLICT DO UPDATE works correctly when the view's -- columns are named and ordered differently than the underlying table's. create table uv_iocu_tab (a text unique, b float); insert into uv_iocu_tab values ('xyxyxy', 0); create view uv_iocu_view as select b, b+1 as c, a, '2.0'::text as two from uv_iocu_tab; insert into uv_iocu_view (a, b) values ('xyxyxy', 1) on conflict (a) do update set b = uv_iocu_view.b; select * from uv_iocu_tab; insert into uv_iocu_view (a, b) values ('xyxyxy', 1) on conflict (a) do update set b = excluded.b; select * from uv_iocu_tab; -- OK to access view columns that are not present in underlying base -- relation in the ON CONFLICT portion of the query insert into uv_iocu_view (a, b) values ('xyxyxy', 3) on conflict (a) do update set b = cast(excluded.two as float); select * from uv_iocu_tab; explain (costs off) insert into uv_iocu_view (a, b) values ('xyxyxy', 3) on conflict (a) do update set b = excluded.b where excluded.c > 0; insert into uv_iocu_view (a, b) values ('xyxyxy', 3) on conflict (a) do update set b = excluded.b where excluded.c > 0; select * from uv_iocu_tab; drop view uv_iocu_view; drop table uv_iocu_tab; -- Test whole-row references to the view create table uv_iocu_tab (a int unique, b text); create view uv_iocu_view as select b as bb, a as aa, uv_iocu_tab::text as cc from uv_iocu_tab; insert into uv_iocu_view (aa,bb) values (1,'x'); explain (costs off) insert into uv_iocu_view (aa,bb) values (1,'y') on conflict (aa) do update set bb = 'Rejected: '||excluded.* where excluded.aa > 0 and excluded.bb != '' and excluded.cc is not null; insert into uv_iocu_view (aa,bb) values (1,'y') on conflict (aa) do update set bb = 'Rejected: '||excluded.* where excluded.aa > 0 and excluded.bb != '' and excluded.cc is not null; select * from uv_iocu_view; -- Test omitting a column of the base relation delete from uv_iocu_view; insert into uv_iocu_view (aa,bb) values (1,'x'); insert into uv_iocu_view (aa) values (1) on conflict (aa) do update set bb = 'Rejected: '||excluded.*; select * from uv_iocu_view; alter table uv_iocu_tab alter column b set default 'table default'; insert into uv_iocu_view (aa) values (1) on conflict (aa) do update set bb = 'Rejected: '||excluded.*; select * from uv_iocu_view; alter view uv_iocu_view alter column bb set default 'view default'; insert into uv_iocu_view (aa) values (1) on conflict (aa) do update set bb = 'Rejected: '||excluded.*; select * from uv_iocu_view; -- Should fail to update non-updatable columns insert into uv_iocu_view (aa) values (1) on conflict (aa) do update set cc = 'XXX'; drop view uv_iocu_view; drop table uv_iocu_tab; -- ON CONFLICT DO UPDATE permissions checks create user regress_view_user1; create user regress_view_user2; set session authorization regress_view_user1; create table base_tbl(a int unique, b text, c float); insert into base_tbl values (1,'xxx',1.0); create view rw_view1 as select b as bb, c as cc, a as aa from base_tbl; grant select (aa,bb) on rw_view1 to regress_view_user2; grant insert on rw_view1 to regress_view_user2; grant update (bb) on rw_view1 to regress_view_user2; set session authorization regress_view_user2; insert into rw_view1 values ('yyy',2.0,1) on conflict (aa) do update set bb = excluded.cc; -- Not allowed insert into rw_view1 values ('yyy',2.0,1) on conflict (aa) do update set bb = rw_view1.cc; -- Not allowed insert into rw_view1 values ('yyy',2.0,1) on conflict (aa) do update set bb = excluded.bb; -- OK insert into rw_view1 values ('zzz',2.0,1) on conflict (aa) do update set bb = rw_view1.bb||'xxx'; -- OK insert into rw_view1 values ('zzz',2.0,1) on conflict (aa) do update set cc = 3.0; -- Not allowed reset session authorization; select * from base_tbl; set session authorization regress_view_user1; grant select (a,b) on base_tbl to regress_view_user2; grant insert (a,b) on base_tbl to regress_view_user2; grant update (a,b) on base_tbl to regress_view_user2; set session authorization regress_view_user2; create view rw_view2 as select b as bb, c as cc, a as aa from base_tbl; insert into rw_view2 (aa,bb) values (1,'xxx') on conflict (aa) do update set bb = excluded.bb; -- Not allowed create view rw_view3 as select b as bb, a as aa from base_tbl; insert into rw_view3 (aa,bb) values (1,'xxx') on conflict (aa) do update set bb = excluded.bb; -- OK reset session authorization; select * from base_tbl; set session authorization regress_view_user2; create view rw_view4 as select aa, bb, cc FROM rw_view1; insert into rw_view4 (aa,bb) values (1,'yyy') on conflict (aa) do update set bb = excluded.bb; -- Not allowed create view rw_view5 as select aa, bb FROM rw_view1; insert into rw_view5 (aa,bb) values (1,'yyy') on conflict (aa) do update set bb = excluded.bb; -- OK reset session authorization; select * from base_tbl; drop view rw_view5; drop view rw_view4; drop view rw_view3; drop view rw_view2; drop view rw_view1; drop table base_tbl; drop user regress_view_user1; drop user regress_view_user2; -- Test single- and multi-row inserts with table and view defaults. -- Table defaults should be used, unless overridden by view defaults. create table base_tab_def (a int, b text default 'Table default', c text default 'Table default', d text, e text); create view base_tab_def_view as select * from base_tab_def; alter view base_tab_def_view alter b set default 'View default'; alter view base_tab_def_view alter d set default 'View default'; insert into base_tab_def values (1); insert into base_tab_def values (2), (3); insert into base_tab_def values (4, default, default, default, default); insert into base_tab_def values (5, default, default, default, default), (6, default, default, default, default); insert into base_tab_def_view values (11); insert into base_tab_def_view values (12), (13); insert into base_tab_def_view values (14, default, default, default, default); insert into base_tab_def_view values (15, default, default, default, default), (16, default, default, default, default); insert into base_tab_def_view values (17), (default); select * from base_tab_def order by a; -- Adding an INSTEAD OF trigger should cause NULLs to be inserted instead of -- table defaults, where there are no view defaults. create function base_tab_def_view_instrig_func() returns trigger as $$ begin insert into base_tab_def values (new.a, new.b, new.c, new.d, new.e); return new; end; $$ language plpgsql; create trigger base_tab_def_view_instrig instead of insert on base_tab_def_view for each row execute function base_tab_def_view_instrig_func(); truncate base_tab_def; insert into base_tab_def values (1); insert into base_tab_def values (2), (3); insert into base_tab_def values (4, default, default, default, default); insert into base_tab_def values (5, default, default, default, default), (6, default, default, default, default); insert into base_tab_def_view values (11); insert into base_tab_def_view values (12), (13); insert into base_tab_def_view values (14, default, default, default, default); insert into base_tab_def_view values (15, default, default, default, default), (16, default, default, default, default); insert into base_tab_def_view values (17), (default); select * from base_tab_def order by a; -- Using an unconditional DO INSTEAD rule should also cause NULLs to be -- inserted where there are no view defaults. drop trigger base_tab_def_view_instrig on base_tab_def_view; drop function base_tab_def_view_instrig_func; create rule base_tab_def_view_ins_rule as on insert to base_tab_def_view do instead insert into base_tab_def values (new.a, new.b, new.c, new.d, new.e); truncate base_tab_def; insert into base_tab_def values (1); insert into base_tab_def values (2), (3); insert into base_tab_def values (4, default, default, default, default); insert into base_tab_def values (5, default, default, default, default), (6, default, default, default, default); insert into base_tab_def_view values (11); insert into base_tab_def_view values (12), (13); insert into base_tab_def_view values (14, default, default, default, default); insert into base_tab_def_view values (15, default, default, default, default), (16, default, default, default, default); insert into base_tab_def_view values (17), (default); select * from base_tab_def order by a; -- A DO ALSO rule should cause each row to be inserted twice. The first -- insert should behave the same as an auto-updatable view (using table -- defaults, unless overridden by view defaults). The second insert should -- behave the same as a rule-updatable view (inserting NULLs where there are -- no view defaults). drop rule base_tab_def_view_ins_rule on base_tab_def_view; create rule base_tab_def_view_ins_rule as on insert to base_tab_def_view do also insert into base_tab_def values (new.a, new.b, new.c, new.d, new.e); truncate base_tab_def; insert into base_tab_def values (1); insert into base_tab_def values (2), (3); insert into base_tab_def values (4, default, default, default, default); insert into base_tab_def values (5, default, default, default, default), (6, default, default, default, default); insert into base_tab_def_view values (11); insert into base_tab_def_view values (12), (13); insert into base_tab_def_view values (14, default, default, default, default); insert into base_tab_def_view values (15, default, default, default, default), (16, default, default, default, default); insert into base_tab_def_view values (17), (default); select * from base_tab_def order by a, c NULLS LAST; drop view base_tab_def_view; drop table base_tab_def; -- Test defaults with array assignments create table base_tab (a serial, b int[], c text, d text default 'Table default'); create view base_tab_view as select c, a, b from base_tab; alter view base_tab_view alter column c set default 'View default'; insert into base_tab_view (b[1], b[2], c, b[5], b[4], a, b[3]) values (1, 2, default, 5, 4, default, 3), (10, 11, 'C value', 14, 13, 100, 12); select * from base_tab order by a; drop view base_tab_view; drop table base_tab; pgFormatter-4.2/t/pg-test-files/sql/update.sql000066400000000000000000000557271361326045100214220ustar00rootroot00000000000000-- -- UPDATE syntax tests -- CREATE TABLE update_test ( a INT DEFAULT 10, b INT, c TEXT ); CREATE TABLE upsert_test ( a INT PRIMARY KEY, b TEXT ); INSERT INTO update_test VALUES (5, 10, 'foo'); INSERT INTO update_test(b, a) VALUES (15, 10); SELECT * FROM update_test; UPDATE update_test SET a = DEFAULT, b = DEFAULT; SELECT * FROM update_test; -- aliases for the UPDATE target table UPDATE update_test AS t SET b = 10 WHERE t.a = 10; SELECT * FROM update_test; UPDATE update_test t SET b = t.b + 10 WHERE t.a = 10; SELECT * FROM update_test; -- -- Test VALUES in FROM -- UPDATE update_test SET a=v.i FROM (VALUES(100, 20)) AS v(i, j) WHERE update_test.b = v.j; SELECT * FROM update_test; -- fail, wrong data type: UPDATE update_test SET a = v.* FROM (VALUES(100, 20)) AS v(i, j) WHERE update_test.b = v.j; -- -- Test multiple-set-clause syntax -- INSERT INTO update_test SELECT a,b+1,c FROM update_test; SELECT * FROM update_test; UPDATE update_test SET (c,b,a) = ('bugle', b+11, DEFAULT) WHERE c = 'foo'; SELECT * FROM update_test; UPDATE update_test SET (c,b) = ('car', a+b), a = a + 1 WHERE a = 10; SELECT * FROM update_test; -- fail, multi assignment to same column: UPDATE update_test SET (c,b) = ('car', a+b), b = a + 1 WHERE a = 10; -- uncorrelated sub-select: UPDATE update_test SET (b,a) = (select a,b from update_test where b = 41 and c = 'car') WHERE a = 100 AND b = 20; SELECT * FROM update_test; -- correlated sub-select: UPDATE update_test o SET (b,a) = (select a+1,b from update_test i where i.a=o.a and i.b=o.b and i.c is not distinct from o.c); SELECT * FROM update_test; -- fail, multiple rows supplied: UPDATE update_test SET (b,a) = (select a+1,b from update_test); -- set to null if no rows supplied: UPDATE update_test SET (b,a) = (select a+1,b from update_test where a = 1000) WHERE a = 11; SELECT * FROM update_test; -- *-expansion should work in this context: UPDATE update_test SET (a,b) = ROW(v.*) FROM (VALUES(21, 100)) AS v(i, j) WHERE update_test.a = v.i; -- you might expect this to work, but syntactically it's not a RowExpr: UPDATE update_test SET (a,b) = (v.*) FROM (VALUES(21, 101)) AS v(i, j) WHERE update_test.a = v.i; -- if an alias for the target table is specified, don't allow references -- to the original table name UPDATE update_test AS t SET b = update_test.b + 10 WHERE t.a = 10; -- Make sure that we can update to a TOASTed value. UPDATE update_test SET c = repeat('x', 10000) WHERE c = 'car'; SELECT a, b, char_length(c) FROM update_test; -- Check multi-assignment with a Result node to handle a one-time filter. EXPLAIN (VERBOSE, COSTS OFF) UPDATE update_test t SET (a, b) = (SELECT b, a FROM update_test s WHERE s.a = t.a) WHERE CURRENT_USER = SESSION_USER; UPDATE update_test t SET (a, b) = (SELECT b, a FROM update_test s WHERE s.a = t.a) WHERE CURRENT_USER = SESSION_USER; SELECT a, b, char_length(c) FROM update_test; -- Test ON CONFLICT DO UPDATE INSERT INTO upsert_test VALUES(1, 'Boo'); -- uncorrelated sub-select: WITH aaa AS (SELECT 1 AS a, 'Foo' AS b) INSERT INTO upsert_test VALUES (1, 'Bar') ON CONFLICT(a) DO UPDATE SET (b, a) = (SELECT b, a FROM aaa) RETURNING *; -- correlated sub-select: INSERT INTO upsert_test VALUES (1, 'Baz') ON CONFLICT(a) DO UPDATE SET (b, a) = (SELECT b || ', Correlated', a from upsert_test i WHERE i.a = upsert_test.a) RETURNING *; -- correlated sub-select (EXCLUDED.* alias): INSERT INTO upsert_test VALUES (1, 'Bat') ON CONFLICT(a) DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a) RETURNING *; DROP TABLE update_test; DROP TABLE upsert_test; --------------------------- -- UPDATE with row movement --------------------------- -- When a partitioned table receives an UPDATE to the partitioned key and the -- new values no longer meet the partition's bound, the row must be moved to -- the correct partition for the new partition key (if one exists). We must -- also ensure that updatable views on partitioned tables properly enforce any -- WITH CHECK OPTION that is defined. The situation with triggers in this case -- also requires thorough testing as partition key updates causing row -- movement convert UPDATEs into DELETE+INSERT. CREATE TABLE range_parted ( a text, b bigint, c numeric, d int, e varchar ) PARTITION BY RANGE (a, b); -- Create partitions intentionally in descending bound order, so as to test -- that update-row-movement works with the leaf partitions not in bound order. CREATE TABLE part_b_20_b_30 (e varchar, c numeric, a text, b bigint, d int); ALTER TABLE range_parted ATTACH PARTITION part_b_20_b_30 FOR VALUES FROM ('b', 20) TO ('b', 30); CREATE TABLE part_b_10_b_20 (e varchar, c numeric, a text, b bigint, d int) PARTITION BY RANGE (c); CREATE TABLE part_b_1_b_10 PARTITION OF range_parted FOR VALUES FROM ('b', 1) TO ('b', 10); ALTER TABLE range_parted ATTACH PARTITION part_b_10_b_20 FOR VALUES FROM ('b', 10) TO ('b', 20); CREATE TABLE part_a_10_a_20 PARTITION OF range_parted FOR VALUES FROM ('a', 10) TO ('a', 20); CREATE TABLE part_a_1_a_10 PARTITION OF range_parted FOR VALUES FROM ('a', 1) TO ('a', 10); -- Check that partition-key UPDATE works sanely on a partitioned table that -- does not have any child partitions. UPDATE part_b_10_b_20 set b = b - 6; -- Create some more partitions following the above pattern of descending bound -- order, but let's make the situation a bit more complex by having the -- attribute numbers of the columns vary from their parent partition. CREATE TABLE part_c_100_200 (e varchar, c numeric, a text, b bigint, d int) PARTITION BY range (abs(d)); ALTER TABLE part_c_100_200 DROP COLUMN e, DROP COLUMN c, DROP COLUMN a; ALTER TABLE part_c_100_200 ADD COLUMN c numeric, ADD COLUMN e varchar, ADD COLUMN a text; ALTER TABLE part_c_100_200 DROP COLUMN b; ALTER TABLE part_c_100_200 ADD COLUMN b bigint; CREATE TABLE part_d_1_15 PARTITION OF part_c_100_200 FOR VALUES FROM (1) TO (15); CREATE TABLE part_d_15_20 PARTITION OF part_c_100_200 FOR VALUES FROM (15) TO (20); ALTER TABLE part_b_10_b_20 ATTACH PARTITION part_c_100_200 FOR VALUES FROM (100) TO (200); CREATE TABLE part_c_1_100 (e varchar, d int, c numeric, b bigint, a text); ALTER TABLE part_b_10_b_20 ATTACH PARTITION part_c_1_100 FOR VALUES FROM (1) TO (100); \set init_range_parted 'truncate range_parted; insert into range_parted VALUES (''a'', 1, 1, 1), (''a'', 10, 200, 1), (''b'', 12, 96, 1), (''b'', 13, 97, 2), (''b'', 15, 105, 16), (''b'', 17, 105, 19)' \set show_data 'select tableoid::regclass::text COLLATE "C" partname, * from range_parted ORDER BY 1, 2, 3, 4, 5, 6' :init_range_parted; :show_data; -- The order of subplans should be in bound order EXPLAIN (costs off) UPDATE range_parted set c = c - 50 WHERE c > 97; -- fail, row movement happens only within the partition subtree. UPDATE part_c_100_200 set c = c - 20, d = c WHERE c = 105; -- fail, no partition key update, so no attempt to move tuple, -- but "a = 'a'" violates partition constraint enforced by root partition) UPDATE part_b_10_b_20 set a = 'a'; -- ok, partition key update, no constraint violation UPDATE range_parted set d = d - 10 WHERE d > 10; -- ok, no partition key update, no constraint violation UPDATE range_parted set e = d; -- No row found UPDATE part_c_1_100 set c = c + 20 WHERE c = 98; -- ok, row movement UPDATE part_b_10_b_20 set c = c + 20 returning c, b, a; :show_data; -- fail, row movement happens only within the partition subtree. UPDATE part_b_10_b_20 set b = b - 6 WHERE c > 116 returning *; -- ok, row movement, with subset of rows moved into different partition. UPDATE range_parted set b = b - 6 WHERE c > 116 returning a, b + c; :show_data; -- Common table needed for multiple test scenarios. CREATE TABLE mintab(c1 int); INSERT into mintab VALUES (120); -- update partition key using updatable view. CREATE VIEW upview AS SELECT * FROM range_parted WHERE (select c > c1 FROM mintab) WITH CHECK OPTION; -- ok UPDATE upview set c = 199 WHERE b = 4; -- fail, check option violation UPDATE upview set c = 120 WHERE b = 4; -- fail, row movement with check option violation UPDATE upview set a = 'b', b = 15, c = 120 WHERE b = 4; -- ok, row movement, check option passes UPDATE upview set a = 'b', b = 15 WHERE b = 4; :show_data; -- cleanup DROP VIEW upview; -- RETURNING having whole-row vars. :init_range_parted; UPDATE range_parted set c = 95 WHERE a = 'b' and b > 10 and c > 100 returning (range_parted), *; :show_data; -- Transition tables with update row movement :init_range_parted; CREATE FUNCTION trans_updatetrigfunc() RETURNS trigger LANGUAGE plpgsql AS $$ begin raise notice 'trigger = %, old table = %, new table = %', TG_NAME, (select string_agg(old_table::text, ', ' ORDER BY a) FROM old_table), (select string_agg(new_table::text, ', ' ORDER BY a) FROM new_table); return null; end; $$; CREATE TRIGGER trans_updatetrig AFTER UPDATE ON range_parted REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table FOR EACH STATEMENT EXECUTE PROCEDURE trans_updatetrigfunc(); UPDATE range_parted set c = (case when c = 96 then 110 else c + 1 end ) WHERE a = 'b' and b > 10 and c >= 96; :show_data; :init_range_parted; -- Enabling OLD TABLE capture for both DELETE as well as UPDATE stmt triggers -- should not cause DELETEd rows to be captured twice. Similar thing for -- INSERT triggers and inserted rows. CREATE TRIGGER trans_deletetrig AFTER DELETE ON range_parted REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE PROCEDURE trans_updatetrigfunc(); CREATE TRIGGER trans_inserttrig AFTER INSERT ON range_parted REFERENCING NEW TABLE AS new_table FOR EACH STATEMENT EXECUTE PROCEDURE trans_updatetrigfunc(); UPDATE range_parted set c = c + 50 WHERE a = 'b' and b > 10 and c >= 96; :show_data; DROP TRIGGER trans_deletetrig ON range_parted; DROP TRIGGER trans_inserttrig ON range_parted; -- Don't drop trans_updatetrig yet. It is required below. -- Test with transition tuple conversion happening for rows moved into the -- new partition. This requires a trigger that references transition table -- (we already have trans_updatetrig). For inserted rows, the conversion -- is not usually needed, because the original tuple is already compatible with -- the desired transition tuple format. But conversion happens when there is a -- BR trigger because the trigger can change the inserted row. So install a -- BR triggers on those child partitions where the rows will be moved. CREATE FUNCTION func_parted_mod_b() RETURNS trigger AS $$ BEGIN NEW.b = NEW.b + 1; return NEW; END $$ language plpgsql; CREATE TRIGGER trig_c1_100 BEFORE UPDATE OR INSERT ON part_c_1_100 FOR EACH ROW EXECUTE PROCEDURE func_parted_mod_b(); CREATE TRIGGER trig_d1_15 BEFORE UPDATE OR INSERT ON part_d_1_15 FOR EACH ROW EXECUTE PROCEDURE func_parted_mod_b(); CREATE TRIGGER trig_d15_20 BEFORE UPDATE OR INSERT ON part_d_15_20 FOR EACH ROW EXECUTE PROCEDURE func_parted_mod_b(); :init_range_parted; UPDATE range_parted set c = (case when c = 96 then 110 else c + 1 end) WHERE a = 'b' and b > 10 and c >= 96; :show_data; :init_range_parted; UPDATE range_parted set c = c + 50 WHERE a = 'b' and b > 10 and c >= 96; :show_data; -- Case where per-partition tuple conversion map array is allocated, but the -- map is not required for the particular tuple that is routed, thanks to -- matching table attributes of the partition and the target table. :init_range_parted; UPDATE range_parted set b = 15 WHERE b = 1; :show_data; DROP TRIGGER trans_updatetrig ON range_parted; DROP TRIGGER trig_c1_100 ON part_c_1_100; DROP TRIGGER trig_d1_15 ON part_d_1_15; DROP TRIGGER trig_d15_20 ON part_d_15_20; DROP FUNCTION func_parted_mod_b(); -- RLS policies with update-row-movement ----------------------------------------- ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY; CREATE USER regress_range_parted_user; GRANT ALL ON range_parted, mintab TO regress_range_parted_user; CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (true); CREATE POLICY policy_range_parted ON range_parted for UPDATE USING (true) WITH CHECK (c % 2 = 0); :init_range_parted; SET SESSION AUTHORIZATION regress_range_parted_user; -- This should fail with RLS violation error while moving row from -- part_a_10_a_20 to part_d_1_15, because we are setting 'c' to an odd number. UPDATE range_parted set a = 'b', c = 151 WHERE a = 'a' and c = 200; RESET SESSION AUTHORIZATION; -- Create a trigger on part_d_1_15 CREATE FUNCTION func_d_1_15() RETURNS trigger AS $$ BEGIN NEW.c = NEW.c + 1; -- Make even numbers odd, or vice versa return NEW; END $$ LANGUAGE plpgsql; CREATE TRIGGER trig_d_1_15 BEFORE INSERT ON part_d_1_15 FOR EACH ROW EXECUTE PROCEDURE func_d_1_15(); :init_range_parted; SET SESSION AUTHORIZATION regress_range_parted_user; -- Here, RLS checks should succeed while moving row from part_a_10_a_20 to -- part_d_1_15. Even though the UPDATE is setting 'c' to an odd number, the -- trigger at the destination partition again makes it an even number. UPDATE range_parted set a = 'b', c = 151 WHERE a = 'a' and c = 200; RESET SESSION AUTHORIZATION; :init_range_parted; SET SESSION AUTHORIZATION regress_range_parted_user; -- This should fail with RLS violation error. Even though the UPDATE is setting -- 'c' to an even number, the trigger at the destination partition again makes -- it an odd number. UPDATE range_parted set a = 'b', c = 150 WHERE a = 'a' and c = 200; -- Cleanup RESET SESSION AUTHORIZATION; DROP TRIGGER trig_d_1_15 ON part_d_1_15; DROP FUNCTION func_d_1_15(); -- Policy expression contains SubPlan RESET SESSION AUTHORIZATION; :init_range_parted; CREATE POLICY policy_range_parted_subplan on range_parted AS RESTRICTIVE for UPDATE USING (true) WITH CHECK ((SELECT range_parted.c <= c1 FROM mintab)); SET SESSION AUTHORIZATION regress_range_parted_user; -- fail, mintab has row with c1 = 120 UPDATE range_parted set a = 'b', c = 122 WHERE a = 'a' and c = 200; -- ok UPDATE range_parted set a = 'b', c = 120 WHERE a = 'a' and c = 200; -- RLS policy expression contains whole row. RESET SESSION AUTHORIZATION; :init_range_parted; CREATE POLICY policy_range_parted_wholerow on range_parted AS RESTRICTIVE for UPDATE USING (true) WITH CHECK (range_parted = row('b', 10, 112, 1, NULL)::range_parted); SET SESSION AUTHORIZATION regress_range_parted_user; -- ok, should pass the RLS check UPDATE range_parted set a = 'b', c = 112 WHERE a = 'a' and c = 200; RESET SESSION AUTHORIZATION; :init_range_parted; SET SESSION AUTHORIZATION regress_range_parted_user; -- fail, the whole row RLS check should fail UPDATE range_parted set a = 'b', c = 116 WHERE a = 'a' and c = 200; -- Cleanup RESET SESSION AUTHORIZATION; DROP POLICY policy_range_parted ON range_parted; DROP POLICY policy_range_parted_subplan ON range_parted; DROP POLICY policy_range_parted_wholerow ON range_parted; REVOKE ALL ON range_parted, mintab FROM regress_range_parted_user; DROP USER regress_range_parted_user; DROP TABLE mintab; -- statement triggers with update row movement --------------------------------------------------- :init_range_parted; CREATE FUNCTION trigfunc() returns trigger language plpgsql as $$ begin raise notice 'trigger = % fired on table % during %', TG_NAME, TG_TABLE_NAME, TG_OP; return null; end; $$; -- Triggers on root partition CREATE TRIGGER parent_delete_trig AFTER DELETE ON range_parted for each statement execute procedure trigfunc(); CREATE TRIGGER parent_update_trig AFTER UPDATE ON range_parted for each statement execute procedure trigfunc(); CREATE TRIGGER parent_insert_trig AFTER INSERT ON range_parted for each statement execute procedure trigfunc(); -- Triggers on leaf partition part_c_1_100 CREATE TRIGGER c1_delete_trig AFTER DELETE ON part_c_1_100 for each statement execute procedure trigfunc(); CREATE TRIGGER c1_update_trig AFTER UPDATE ON part_c_1_100 for each statement execute procedure trigfunc(); CREATE TRIGGER c1_insert_trig AFTER INSERT ON part_c_1_100 for each statement execute procedure trigfunc(); -- Triggers on leaf partition part_d_1_15 CREATE TRIGGER d1_delete_trig AFTER DELETE ON part_d_1_15 for each statement execute procedure trigfunc(); CREATE TRIGGER d1_update_trig AFTER UPDATE ON part_d_1_15 for each statement execute procedure trigfunc(); CREATE TRIGGER d1_insert_trig AFTER INSERT ON part_d_1_15 for each statement execute procedure trigfunc(); -- Triggers on leaf partition part_d_15_20 CREATE TRIGGER d15_delete_trig AFTER DELETE ON part_d_15_20 for each statement execute procedure trigfunc(); CREATE TRIGGER d15_update_trig AFTER UPDATE ON part_d_15_20 for each statement execute procedure trigfunc(); CREATE TRIGGER d15_insert_trig AFTER INSERT ON part_d_15_20 for each statement execute procedure trigfunc(); -- Move all rows from part_c_100_200 to part_c_1_100. None of the delete or -- insert statement triggers should be fired. UPDATE range_parted set c = c - 50 WHERE c > 97; :show_data; DROP TRIGGER parent_delete_trig ON range_parted; DROP TRIGGER parent_update_trig ON range_parted; DROP TRIGGER parent_insert_trig ON range_parted; DROP TRIGGER c1_delete_trig ON part_c_1_100; DROP TRIGGER c1_update_trig ON part_c_1_100; DROP TRIGGER c1_insert_trig ON part_c_1_100; DROP TRIGGER d1_delete_trig ON part_d_1_15; DROP TRIGGER d1_update_trig ON part_d_1_15; DROP TRIGGER d1_insert_trig ON part_d_1_15; DROP TRIGGER d15_delete_trig ON part_d_15_20; DROP TRIGGER d15_update_trig ON part_d_15_20; DROP TRIGGER d15_insert_trig ON part_d_15_20; -- Creating default partition for range :init_range_parted; create table part_def partition of range_parted default; \d+ part_def insert into range_parted values ('c', 9); -- ok update part_def set a = 'd' where a = 'c'; -- fail update part_def set a = 'a' where a = 'd'; :show_data; -- Update row movement from non-default to default partition. -- fail, default partition is not under part_a_10_a_20; UPDATE part_a_10_a_20 set a = 'ad' WHERE a = 'a'; -- ok UPDATE range_parted set a = 'ad' WHERE a = 'a'; UPDATE range_parted set a = 'bd' WHERE a = 'b'; :show_data; -- Update row movement from default to non-default partitions. -- ok UPDATE range_parted set a = 'a' WHERE a = 'ad'; UPDATE range_parted set a = 'b' WHERE a = 'bd'; :show_data; -- Cleanup: range_parted no longer needed. DROP TABLE range_parted; CREATE TABLE list_parted ( a text, b int ) PARTITION BY list (a); CREATE TABLE list_part1 PARTITION OF list_parted for VALUES in ('a', 'b'); CREATE TABLE list_default PARTITION OF list_parted default; INSERT into list_part1 VALUES ('a', 1); INSERT into list_default VALUES ('d', 10); -- fail UPDATE list_default set a = 'a' WHERE a = 'd'; -- ok UPDATE list_default set a = 'x' WHERE a = 'd'; DROP TABLE list_parted; -------------- -- Some more update-partition-key test scenarios below. This time use list -- partitions. -------------- -- Setup for list partitions CREATE TABLE list_parted (a numeric, b int, c int8) PARTITION BY list (a); CREATE TABLE sub_parted PARTITION OF list_parted for VALUES in (1) PARTITION BY list (b); CREATE TABLE sub_part1(b int, c int8, a numeric); ALTER TABLE sub_parted ATTACH PARTITION sub_part1 for VALUES in (1); CREATE TABLE sub_part2(b int, c int8, a numeric); ALTER TABLE sub_parted ATTACH PARTITION sub_part2 for VALUES in (2); CREATE TABLE list_part1(a numeric, b int, c int8); ALTER TABLE list_parted ATTACH PARTITION list_part1 for VALUES in (2,3); INSERT into list_parted VALUES (2,5,50); INSERT into list_parted VALUES (3,6,60); INSERT into sub_parted VALUES (1,1,60); INSERT into sub_parted VALUES (1,2,10); -- Test partition constraint violation when intermediate ancestor is used and -- constraint is inherited from upper root. UPDATE sub_parted set a = 2 WHERE c = 10; -- Test update-partition-key, where the unpruned partitions do not have their -- partition keys updated. SELECT tableoid::regclass::text, * FROM list_parted WHERE a = 2 ORDER BY 1; UPDATE list_parted set b = c + a WHERE a = 2; SELECT tableoid::regclass::text, * FROM list_parted WHERE a = 2 ORDER BY 1; -- Test the case where BR UPDATE triggers change the partition key. CREATE FUNCTION func_parted_mod_b() returns trigger as $$ BEGIN NEW.b = 2; -- This is changing partition key column. return NEW; END $$ LANGUAGE plpgsql; CREATE TRIGGER parted_mod_b before update on sub_part1 for each row execute procedure func_parted_mod_b(); SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; -- This should do the tuple routing even though there is no explicit -- partition-key update, because there is a trigger on sub_part1. UPDATE list_parted set c = 70 WHERE b = 1; SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; DROP TRIGGER parted_mod_b ON sub_part1; -- If BR DELETE trigger prevented DELETE from happening, we should also skip -- the INSERT if that delete is part of UPDATE=>DELETE+INSERT. CREATE OR REPLACE FUNCTION func_parted_mod_b() returns trigger as $$ BEGIN raise notice 'Trigger: Got OLD row %, but returning NULL', OLD; return NULL; END $$ LANGUAGE plpgsql; CREATE TRIGGER trig_skip_delete before delete on sub_part2 for each row execute procedure func_parted_mod_b(); UPDATE list_parted set b = 1 WHERE c = 70; SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; -- Drop the trigger. Now the row should be moved. DROP TRIGGER trig_skip_delete ON sub_part2; UPDATE list_parted set b = 1 WHERE c = 70; SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; DROP FUNCTION func_parted_mod_b(); -- UPDATE partition-key with FROM clause. If join produces multiple output -- rows for the same row to be modified, we should tuple-route the row only -- once. There should not be any rows inserted. CREATE TABLE non_parted (id int); INSERT into non_parted VALUES (1), (1), (1), (2), (2), (2), (3), (3), (3); UPDATE list_parted t1 set a = 2 FROM non_parted t2 WHERE t1.a = t2.id and a = 1; SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; DROP TABLE non_parted; -- Cleanup: list_parted no longer needed. DROP TABLE list_parted; -- create custom operator class and hash function, for the same reason -- explained in alter_table.sql create or replace function dummy_hashint4(a int4, seed int8) returns int8 as $$ begin return (a + seed); end; $$ language 'plpgsql' immutable; create operator class custom_opclass for type int4 using hash as operator 1 = , function 2 dummy_hashint4(int4, int8); create table hash_parted ( a int, b int ) partition by hash (a custom_opclass, b custom_opclass); create table hpart1 partition of hash_parted for values with (modulus 2, remainder 1); create table hpart2 partition of hash_parted for values with (modulus 4, remainder 2); create table hpart3 partition of hash_parted for values with (modulus 8, remainder 0); create table hpart4 partition of hash_parted for values with (modulus 8, remainder 4); insert into hpart1 values (1, 1); insert into hpart2 values (2, 5); insert into hpart4 values (3, 4); -- fail update hpart1 set a = 3, b=4 where a = 1; -- ok, row movement update hash_parted set b = b - 1 where b = 1; -- ok update hash_parted set b = b + 8 where b = 1; -- cleanup drop table hash_parted; drop operator class custom_opclass using hash; drop function dummy_hashint4(a int4, seed int8); pgFormatter-4.2/t/pg-test-files/sql/uuid.sql000066400000000000000000000056541361326045100211000ustar00rootroot00000000000000-- regression test for the uuid datatype -- creating test tables CREATE TABLE guid1 ( guid_field UUID, text_field TEXT DEFAULT(now()) ); CREATE TABLE guid2 ( guid_field UUID, text_field TEXT DEFAULT(now()) ); -- inserting invalid data tests -- too long INSERT INTO guid1(guid_field) VALUES('11111111-1111-1111-1111-111111111111F'); -- too short INSERT INTO guid1(guid_field) VALUES('{11111111-1111-1111-1111-11111111111}'); -- valid data but invalid format INSERT INTO guid1(guid_field) VALUES('111-11111-1111-1111-1111-111111111111'); INSERT INTO guid1(guid_field) VALUES('{22222222-2222-2222-2222-222222222222 '); -- invalid data INSERT INTO guid1(guid_field) VALUES('11111111-1111-1111-G111-111111111111'); INSERT INTO guid1(guid_field) VALUES('11+11111-1111-1111-1111-111111111111'); --inserting three input formats INSERT INTO guid1(guid_field) VALUES('11111111-1111-1111-1111-111111111111'); INSERT INTO guid1(guid_field) VALUES('{22222222-2222-2222-2222-222222222222}'); INSERT INTO guid1(guid_field) VALUES('3f3e3c3b3a3039383736353433a2313e'); -- retrieving the inserted data SELECT guid_field FROM guid1; -- ordering test SELECT guid_field FROM guid1 ORDER BY guid_field ASC; SELECT guid_field FROM guid1 ORDER BY guid_field DESC; -- = operator test SELECT COUNT(*) FROM guid1 WHERE guid_field = '3f3e3c3b-3a30-3938-3736-353433a2313e'; -- <> operator test SELECT COUNT(*) FROM guid1 WHERE guid_field <> '11111111111111111111111111111111'; -- < operator test SELECT COUNT(*) FROM guid1 WHERE guid_field < '22222222-2222-2222-2222-222222222222'; -- <= operator test SELECT COUNT(*) FROM guid1 WHERE guid_field <= '22222222-2222-2222-2222-222222222222'; -- > operator test SELECT COUNT(*) FROM guid1 WHERE guid_field > '22222222-2222-2222-2222-222222222222'; -- >= operator test SELECT COUNT(*) FROM guid1 WHERE guid_field >= '22222222-2222-2222-2222-222222222222'; -- btree and hash index creation test CREATE INDEX guid1_btree ON guid1 USING BTREE (guid_field); CREATE INDEX guid1_hash ON guid1 USING HASH (guid_field); -- unique index test CREATE UNIQUE INDEX guid1_unique_BTREE ON guid1 USING BTREE (guid_field); -- should fail INSERT INTO guid1(guid_field) VALUES('11111111-1111-1111-1111-111111111111'); -- check to see whether the new indexes are actually there SELECT count(*) FROM pg_class WHERE relkind='i' AND relname LIKE 'guid%'; -- populating the test tables with additional records INSERT INTO guid1(guid_field) VALUES('44444444-4444-4444-4444-444444444444'); INSERT INTO guid2(guid_field) VALUES('11111111-1111-1111-1111-111111111111'); INSERT INTO guid2(guid_field) VALUES('{22222222-2222-2222-2222-222222222222}'); INSERT INTO guid2(guid_field) VALUES('3f3e3c3b3a3039383736353433a2313e'); -- join test SELECT COUNT(*) FROM guid1 g1 INNER JOIN guid2 g2 ON g1.guid_field = g2.guid_field; SELECT COUNT(*) FROM guid1 g1 LEFT JOIN guid2 g2 ON g1.guid_field = g2.guid_field WHERE g2.guid_field IS NULL; -- clean up DROP TABLE guid1, guid2 CASCADE; pgFormatter-4.2/t/pg-test-files/sql/vacuum.sql000066400000000000000000000141541361326045100214250ustar00rootroot00000000000000-- -- VACUUM -- CREATE TABLE vactst (i INT); INSERT INTO vactst VALUES (1); INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst VALUES (0); SELECT count(*) FROM vactst; DELETE FROM vactst WHERE i != 0; SELECT * FROM vactst; VACUUM FULL vactst; UPDATE vactst SET i = i + 1; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst SELECT * FROM vactst; INSERT INTO vactst VALUES (0); SELECT count(*) FROM vactst; DELETE FROM vactst WHERE i != 0; VACUUM (FULL) vactst; DELETE FROM vactst; SELECT * FROM vactst; VACUUM (FULL, FREEZE) vactst; VACUUM (ANALYZE, FULL) vactst; CREATE TABLE vaccluster (i INT PRIMARY KEY); ALTER TABLE vaccluster CLUSTER ON vaccluster_pkey; CLUSTER vaccluster; CREATE FUNCTION do_analyze() RETURNS VOID VOLATILE LANGUAGE SQL AS 'ANALYZE pg_am'; CREATE FUNCTION wrap_do_analyze(c INT) RETURNS INT IMMUTABLE LANGUAGE SQL AS 'SELECT $1 FROM do_analyze()'; CREATE INDEX ON vaccluster(wrap_do_analyze(i)); INSERT INTO vaccluster VALUES (1), (2); ANALYZE vaccluster; VACUUM FULL pg_am; VACUUM FULL pg_class; VACUUM FULL pg_database; VACUUM FULL vaccluster; VACUUM FULL vactst; VACUUM (DISABLE_PAGE_SKIPPING) vaccluster; -- INDEX_CLEANUP option CREATE TABLE no_index_cleanup (i INT PRIMARY KEY) WITH (vacuum_index_cleanup = false); VACUUM (INDEX_CLEANUP FALSE) vaccluster; VACUUM (INDEX_CLEANUP FALSE) vactst; -- index cleanup option is ignored if no indexes VACUUM (INDEX_CLEANUP FALSE, FREEZE TRUE) vaccluster; -- index cleanup option is ignored if VACUUM FULL VACUUM (INDEX_CLEANUP TRUE, FULL TRUE) no_index_cleanup; VACUUM (FULL TRUE) no_index_cleanup; -- partitioned table CREATE TABLE vacparted (a int, b char) PARTITION BY LIST (a); CREATE TABLE vacparted1 PARTITION OF vacparted FOR VALUES IN (1); INSERT INTO vacparted VALUES (1, 'a'); UPDATE vacparted SET b = 'b'; VACUUM (ANALYZE) vacparted; VACUUM (FULL) vacparted; VACUUM (FREEZE) vacparted; -- check behavior with duplicate column mentions VACUUM ANALYZE vacparted(a,b,a); ANALYZE vacparted(a,b,b); -- multiple tables specified VACUUM vaccluster, vactst; VACUUM vacparted, does_not_exist; VACUUM (FREEZE) vacparted, vaccluster, vactst; VACUUM (FREEZE) does_not_exist, vaccluster; VACUUM ANALYZE vactst, vacparted (a); VACUUM ANALYZE vactst (does_not_exist), vacparted (b); VACUUM FULL vacparted, vactst; VACUUM FULL vactst, vacparted (a, b), vaccluster (i); ANALYZE vactst, vacparted; ANALYZE vacparted (b), vactst; ANALYZE vactst, does_not_exist, vacparted; ANALYZE vactst (i), vacparted (does_not_exist); -- parenthesized syntax for ANALYZE ANALYZE (VERBOSE) does_not_exist; ANALYZE (nonexistent-arg) does_not_exist; ANALYZE (nonexistentarg) does_not_exit; -- ensure argument order independence, and that SKIP_LOCKED on non-existing -- relation still errors out. ANALYZE (SKIP_LOCKED, VERBOSE) does_not_exist; ANALYZE (VERBOSE, SKIP_LOCKED) does_not_exist; -- SKIP_LOCKED option VACUUM (SKIP_LOCKED) vactst; VACUUM (SKIP_LOCKED, FULL) vactst; ANALYZE (SKIP_LOCKED) vactst; DROP TABLE vaccluster; DROP TABLE vactst; DROP TABLE vacparted; DROP TABLE no_index_cleanup; -- relation ownership, WARNING logs generated as all are skipped. CREATE TABLE vacowned (a int); CREATE TABLE vacowned_parted (a int) PARTITION BY LIST (a); CREATE TABLE vacowned_part1 PARTITION OF vacowned_parted FOR VALUES IN (1); CREATE TABLE vacowned_part2 PARTITION OF vacowned_parted FOR VALUES IN (2); CREATE ROLE regress_vacuum; SET ROLE regress_vacuum; -- Simple table VACUUM vacowned; ANALYZE vacowned; VACUUM (ANALYZE) vacowned; -- Catalog VACUUM pg_catalog.pg_class; ANALYZE pg_catalog.pg_class; VACUUM (ANALYZE) pg_catalog.pg_class; -- Shared catalog VACUUM pg_catalog.pg_authid; ANALYZE pg_catalog.pg_authid; VACUUM (ANALYZE) pg_catalog.pg_authid; -- Partitioned table and its partitions, nothing owned by other user. -- Relations are not listed in a single command to test ownership -- independently. VACUUM vacowned_parted; VACUUM vacowned_part1; VACUUM vacowned_part2; ANALYZE vacowned_parted; ANALYZE vacowned_part1; ANALYZE vacowned_part2; VACUUM (ANALYZE) vacowned_parted; VACUUM (ANALYZE) vacowned_part1; VACUUM (ANALYZE) vacowned_part2; RESET ROLE; -- Partitioned table and one partition owned by other user. ALTER TABLE vacowned_parted OWNER TO regress_vacuum; ALTER TABLE vacowned_part1 OWNER TO regress_vacuum; SET ROLE regress_vacuum; VACUUM vacowned_parted; VACUUM vacowned_part1; VACUUM vacowned_part2; ANALYZE vacowned_parted; ANALYZE vacowned_part1; ANALYZE vacowned_part2; VACUUM (ANALYZE) vacowned_parted; VACUUM (ANALYZE) vacowned_part1; VACUUM (ANALYZE) vacowned_part2; RESET ROLE; -- Only one partition owned by other user. ALTER TABLE vacowned_parted OWNER TO CURRENT_USER; SET ROLE regress_vacuum; VACUUM vacowned_parted; VACUUM vacowned_part1; VACUUM vacowned_part2; ANALYZE vacowned_parted; ANALYZE vacowned_part1; ANALYZE vacowned_part2; VACUUM (ANALYZE) vacowned_parted; VACUUM (ANALYZE) vacowned_part1; VACUUM (ANALYZE) vacowned_part2; RESET ROLE; -- Only partitioned table owned by other user. ALTER TABLE vacowned_parted OWNER TO regress_vacuum; ALTER TABLE vacowned_part1 OWNER TO CURRENT_USER; SET ROLE regress_vacuum; VACUUM vacowned_parted; VACUUM vacowned_part1; VACUUM vacowned_part2; ANALYZE vacowned_parted; ANALYZE vacowned_part1; ANALYZE vacowned_part2; VACUUM (ANALYZE) vacowned_parted; VACUUM (ANALYZE) vacowned_part1; VACUUM (ANALYZE) vacowned_part2; RESET ROLE; DROP TABLE vacowned; DROP TABLE vacowned_parted; DROP ROLE regress_vacuum; pgFormatter-4.2/t/pg-test-files/sql/varchar.sql000066400000000000000000000025131361326045100215470ustar00rootroot00000000000000-- -- VARCHAR -- CREATE TABLE VARCHAR_TBL(f1 varchar(1)); INSERT INTO VARCHAR_TBL (f1) VALUES ('a'); INSERT INTO VARCHAR_TBL (f1) VALUES ('A'); -- any of the following three input formats are acceptable INSERT INTO VARCHAR_TBL (f1) VALUES ('1'); INSERT INTO VARCHAR_TBL (f1) VALUES (2); INSERT INTO VARCHAR_TBL (f1) VALUES ('3'); -- zero-length char INSERT INTO VARCHAR_TBL (f1) VALUES (''); -- try varchar's of greater than 1 length INSERT INTO VARCHAR_TBL (f1) VALUES ('cd'); INSERT INTO VARCHAR_TBL (f1) VALUES ('c '); SELECT '' AS seven, * FROM VARCHAR_TBL; SELECT '' AS six, c.* FROM VARCHAR_TBL c WHERE c.f1 <> 'a'; SELECT '' AS one, c.* FROM VARCHAR_TBL c WHERE c.f1 = 'a'; SELECT '' AS five, c.* FROM VARCHAR_TBL c WHERE c.f1 < 'a'; SELECT '' AS six, c.* FROM VARCHAR_TBL c WHERE c.f1 <= 'a'; SELECT '' AS one, c.* FROM VARCHAR_TBL c WHERE c.f1 > 'a'; SELECT '' AS two, c.* FROM VARCHAR_TBL c WHERE c.f1 >= 'a'; DROP TABLE VARCHAR_TBL; -- -- Now test longer arrays of char -- CREATE TABLE VARCHAR_TBL(f1 varchar(4)); INSERT INTO VARCHAR_TBL (f1) VALUES ('a'); INSERT INTO VARCHAR_TBL (f1) VALUES ('ab'); INSERT INTO VARCHAR_TBL (f1) VALUES ('abcd'); INSERT INTO VARCHAR_TBL (f1) VALUES ('abcde'); INSERT INTO VARCHAR_TBL (f1) VALUES ('abcd '); SELECT '' AS four, * FROM VARCHAR_TBL; pgFormatter-4.2/t/pg-test-files/sql/window.sql000066400000000000000000001373261361326045100214430ustar00rootroot00000000000000-- -- WINDOW FUNCTIONS -- CREATE TEMPORARY TABLE empsalary ( depname varchar, empno bigint, salary int, enroll_date date ); INSERT INTO empsalary VALUES ('develop', 10, 5200, '2007-08-01'), ('sales', 1, 5000, '2006-10-01'), ('personnel', 5, 3500, '2007-12-10'), ('sales', 4, 4800, '2007-08-08'), ('personnel', 2, 3900, '2006-12-23'), ('develop', 7, 4200, '2008-01-01'), ('develop', 9, 4500, '2008-01-01'), ('sales', 3, 4800, '2007-08-01'), ('develop', 8, 6000, '2006-10-01'), ('develop', 11, 5200, '2007-08-15'); SELECT depname, empno, salary, sum(salary) OVER (PARTITION BY depname) FROM empsalary ORDER BY depname, salary; SELECT depname, empno, salary, rank() OVER (PARTITION BY depname ORDER BY salary) FROM empsalary; -- with GROUP BY SELECT four, ten, SUM(SUM(four)) OVER (PARTITION BY four), AVG(ten) FROM tenk1 GROUP BY four, ten ORDER BY four, ten; SELECT depname, empno, salary, sum(salary) OVER w FROM empsalary WINDOW w AS (PARTITION BY depname); SELECT depname, empno, salary, rank() OVER w FROM empsalary WINDOW w AS (PARTITION BY depname ORDER BY salary) ORDER BY rank() OVER w; -- empty window specification SELECT COUNT(*) OVER () FROM tenk1 WHERE unique2 < 10; SELECT COUNT(*) OVER w FROM tenk1 WHERE unique2 < 10 WINDOW w AS (); -- no window operation SELECT four FROM tenk1 WHERE FALSE WINDOW w AS (PARTITION BY ten); -- cumulative aggregate SELECT sum(four) OVER (PARTITION BY ten ORDER BY unique2) AS sum_1, ten, four FROM tenk1 WHERE unique2 < 10; SELECT row_number() OVER (ORDER BY unique2) FROM tenk1 WHERE unique2 < 10; SELECT rank() OVER (PARTITION BY four ORDER BY ten) AS rank_1, ten, four FROM tenk1 WHERE unique2 < 10; SELECT dense_rank() OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT percent_rank() OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT cume_dist() OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT ntile(3) OVER (ORDER BY ten, four), ten, four FROM tenk1 WHERE unique2 < 10; SELECT ntile(NULL) OVER (ORDER BY ten, four), ten, four FROM tenk1 LIMIT 2; SELECT lag(ten) OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT lag(ten, four) OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT lag(ten, four, 0) OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT lead(ten) OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT lead(ten * 2, 1) OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT lead(ten * 2, 1, -1) OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT first_value(ten) OVER (PARTITION BY four ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; -- last_value returns the last row of the frame, which is CURRENT ROW in ORDER BY window. SELECT last_value(four) OVER (ORDER BY ten), ten, four FROM tenk1 WHERE unique2 < 10; SELECT last_value(ten) OVER (PARTITION BY four), ten, four FROM (SELECT * FROM tenk1 WHERE unique2 < 10 ORDER BY four, ten)s ORDER BY four, ten; SELECT nth_value(ten, four + 1) OVER (PARTITION BY four), ten, four FROM (SELECT * FROM tenk1 WHERE unique2 < 10 ORDER BY four, ten)s; SELECT ten, two, sum(hundred) AS gsum, sum(sum(hundred)) OVER (PARTITION BY two ORDER BY ten) AS wsum FROM tenk1 GROUP BY ten, two; SELECT count(*) OVER (PARTITION BY four), four FROM (SELECT * FROM tenk1 WHERE two = 1)s WHERE unique2 < 10; SELECT (count(*) OVER (PARTITION BY four ORDER BY ten) + sum(hundred) OVER (PARTITION BY four ORDER BY ten))::varchar AS cntsum FROM tenk1 WHERE unique2 < 10; -- opexpr with different windows evaluation. SELECT * FROM( SELECT count(*) OVER (PARTITION BY four ORDER BY ten) + sum(hundred) OVER (PARTITION BY two ORDER BY ten) AS total, count(*) OVER (PARTITION BY four ORDER BY ten) AS fourcount, sum(hundred) OVER (PARTITION BY two ORDER BY ten) AS twosum FROM tenk1 )sub WHERE total <> fourcount + twosum; SELECT avg(four) OVER (PARTITION BY four ORDER BY thousand / 100) FROM tenk1 WHERE unique2 < 10; SELECT ten, two, sum(hundred) AS gsum, sum(sum(hundred)) OVER win AS wsum FROM tenk1 GROUP BY ten, two WINDOW win AS (PARTITION BY two ORDER BY ten); -- more than one window with GROUP BY SELECT sum(salary), row_number() OVER (ORDER BY depname), sum(sum(salary)) OVER (ORDER BY depname DESC) FROM empsalary GROUP BY depname; -- identical windows with different names SELECT sum(salary) OVER w1, count(*) OVER w2 FROM empsalary WINDOW w1 AS (ORDER BY salary), w2 AS (ORDER BY salary); -- subplan SELECT lead(ten, (SELECT two FROM tenk1 WHERE s.unique2 = unique2)) OVER (PARTITION BY four ORDER BY ten) FROM tenk1 s WHERE unique2 < 10; -- empty table SELECT count(*) OVER (PARTITION BY four) FROM (SELECT * FROM tenk1 WHERE FALSE)s; -- mixture of agg/wfunc in the same window SELECT sum(salary) OVER w, rank() OVER w FROM empsalary WINDOW w AS (PARTITION BY depname ORDER BY salary DESC); -- strict aggs SELECT empno, depname, salary, bonus, depadj, MIN(bonus) OVER (ORDER BY empno), MAX(depadj) OVER () FROM( SELECT *, CASE WHEN enroll_date < '2008-01-01' THEN 2008 - extract(YEAR FROM enroll_date) END * 500 AS bonus, CASE WHEN AVG(salary) OVER (PARTITION BY depname) < salary THEN 200 END AS depadj FROM empsalary )s; -- window function over ungrouped agg over empty row set (bug before 9.1) SELECT SUM(COUNT(f1)) OVER () FROM int4_tbl WHERE f1=42; -- window function with ORDER BY an expression involving aggregates (9.1 bug) select ten, sum(unique1) + sum(unique2) as res, rank() over (order by sum(unique1) + sum(unique2)) as rank from tenk1 group by ten order by ten; -- window and aggregate with GROUP BY expression (9.2 bug) explain (costs off) select first_value(max(x)) over (), y from (select unique1 as x, ten+four as y from tenk1) ss group by y; -- test non-default frame specifications SELECT four, ten, sum(ten) over (partition by four order by ten), last_value(ten) over (partition by four order by ten) FROM (select distinct ten, four from tenk1) ss; SELECT four, ten, sum(ten) over (partition by four order by ten range between unbounded preceding and current row), last_value(ten) over (partition by four order by ten range between unbounded preceding and current row) FROM (select distinct ten, four from tenk1) ss; SELECT four, ten, sum(ten) over (partition by four order by ten range between unbounded preceding and unbounded following), last_value(ten) over (partition by four order by ten range between unbounded preceding and unbounded following) FROM (select distinct ten, four from tenk1) ss; SELECT four, ten/4 as two, sum(ten/4) over (partition by four order by ten/4 range between unbounded preceding and current row), last_value(ten/4) over (partition by four order by ten/4 range between unbounded preceding and current row) FROM (select distinct ten, four from tenk1) ss; SELECT four, ten/4 as two, sum(ten/4) over (partition by four order by ten/4 rows between unbounded preceding and current row), last_value(ten/4) over (partition by four order by ten/4 rows between unbounded preceding and current row) FROM (select distinct ten, four from tenk1) ss; SELECT sum(unique1) over (order by four range between current row and unbounded following), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (rows between current row and unbounded following), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (rows between 2 preceding and 2 following), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (rows between 2 preceding and 2 following exclude no others), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (rows between 2 preceding and 2 following exclude current row), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (rows between 2 preceding and 2 following exclude group), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (rows between 2 preceding and 2 following exclude ties), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT first_value(unique1) over (ORDER BY four rows between current row and 2 following exclude current row), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT first_value(unique1) over (ORDER BY four rows between current row and 2 following exclude group), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT first_value(unique1) over (ORDER BY four rows between current row and 2 following exclude ties), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT last_value(unique1) over (ORDER BY four rows between current row and 2 following exclude current row), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT last_value(unique1) over (ORDER BY four rows between current row and 2 following exclude group), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT last_value(unique1) over (ORDER BY four rows between current row and 2 following exclude ties), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (rows between 2 preceding and 1 preceding), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (rows between 1 following and 3 following), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (rows between unbounded preceding and 1 following), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (w range between current row and unbounded following), unique1, four FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four); SELECT sum(unique1) over (w range between unbounded preceding and current row exclude current row), unique1, four FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four); SELECT sum(unique1) over (w range between unbounded preceding and current row exclude group), unique1, four FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four); SELECT sum(unique1) over (w range between unbounded preceding and current row exclude ties), unique1, four FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four); SELECT first_value(unique1) over w, nth_value(unique1, 2) over w AS nth_2, last_value(unique1) over w, unique1, four FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four range between current row and unbounded following); SELECT sum(unique1) over (order by unique1 rows (SELECT unique1 FROM tenk1 ORDER BY unique1 LIMIT 1) + 1 PRECEDING), unique1 FROM tenk1 WHERE unique1 < 10; CREATE TEMP VIEW v_window AS SELECT i, sum(i) over (order by i rows between 1 preceding and 1 following) as sum_rows FROM generate_series(1, 10) i; SELECT * FROM v_window; SELECT pg_get_viewdef('v_window'); CREATE OR REPLACE TEMP VIEW v_window AS SELECT i, sum(i) over (order by i rows between 1 preceding and 1 following exclude current row) as sum_rows FROM generate_series(1, 10) i; SELECT * FROM v_window; SELECT pg_get_viewdef('v_window'); CREATE OR REPLACE TEMP VIEW v_window AS SELECT i, sum(i) over (order by i rows between 1 preceding and 1 following exclude group) as sum_rows FROM generate_series(1, 10) i; SELECT * FROM v_window; SELECT pg_get_viewdef('v_window'); CREATE OR REPLACE TEMP VIEW v_window AS SELECT i, sum(i) over (order by i rows between 1 preceding and 1 following exclude ties) as sum_rows FROM generate_series(1, 10) i; SELECT * FROM v_window; SELECT pg_get_viewdef('v_window'); CREATE OR REPLACE TEMP VIEW v_window AS SELECT i, sum(i) over (order by i rows between 1 preceding and 1 following exclude no others) as sum_rows FROM generate_series(1, 10) i; SELECT * FROM v_window; SELECT pg_get_viewdef('v_window'); CREATE OR REPLACE TEMP VIEW v_window AS SELECT i, sum(i) over (order by i groups between 1 preceding and 1 following) as sum_rows FROM generate_series(1, 10) i; SELECT * FROM v_window; SELECT pg_get_viewdef('v_window'); DROP VIEW v_window; CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); -- RANGE offset PRECEDING/FOLLOWING tests SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (order by four desc range between 2::int8 preceding and 1::int2 preceding), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding exclude no others), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding exclude current row), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding exclude group), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding exclude ties), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (order by four range between 2::int8 preceding and 6::int2 following exclude ties), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (order by four range between 2::int8 preceding and 6::int2 following exclude group), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (partition by four order by unique1 range between 5::int8 preceding and 6::int2 following), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (partition by four order by unique1 range between 5::int8 preceding and 6::int2 following exclude current row),unique1, four FROM tenk1 WHERE unique1 < 10; select sum(salary) over (order by enroll_date range between '1 year'::interval preceding and '1 year'::interval following), salary, enroll_date from empsalary; select sum(salary) over (order by enroll_date desc range between '1 year'::interval preceding and '1 year'::interval following), salary, enroll_date from empsalary; select sum(salary) over (order by enroll_date desc range between '1 year'::interval following and '1 year'::interval following), salary, enroll_date from empsalary; select sum(salary) over (order by enroll_date range between '1 year'::interval preceding and '1 year'::interval following exclude current row), salary, enroll_date from empsalary; select sum(salary) over (order by enroll_date range between '1 year'::interval preceding and '1 year'::interval following exclude group), salary, enroll_date from empsalary; select sum(salary) over (order by enroll_date range between '1 year'::interval preceding and '1 year'::interval following exclude ties), salary, enroll_date from empsalary; select first_value(salary) over(order by salary range between 1000 preceding and 1000 following), lead(salary) over(order by salary range between 1000 preceding and 1000 following), nth_value(salary, 1) over(order by salary range between 1000 preceding and 1000 following), salary from empsalary; select last_value(salary) over(order by salary range between 1000 preceding and 1000 following), lag(salary) over(order by salary range between 1000 preceding and 1000 following), salary from empsalary; select first_value(salary) over(order by salary range between 1000 following and 3000 following exclude current row), lead(salary) over(order by salary range between 1000 following and 3000 following exclude ties), nth_value(salary, 1) over(order by salary range between 1000 following and 3000 following exclude ties), salary from empsalary; select last_value(salary) over(order by salary range between 1000 following and 3000 following exclude group), lag(salary) over(order by salary range between 1000 following and 3000 following exclude group), salary from empsalary; select first_value(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following exclude ties), last_value(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following), salary, enroll_date from empsalary; select first_value(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following exclude ties), last_value(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following exclude ties), salary, enroll_date from empsalary; select first_value(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following exclude group), last_value(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following exclude group), salary, enroll_date from empsalary; select first_value(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following exclude current row), last_value(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following exclude current row), salary, enroll_date from empsalary; -- RANGE offset PRECEDING/FOLLOWING with null values select x, y, first_value(y) over w, last_value(y) over w from (select x, x as y from generate_series(1,5) as x union all select null, 42 union all select null, 43) ss window w as (order by x asc nulls first range between 2 preceding and 2 following); select x, y, first_value(y) over w, last_value(y) over w from (select x, x as y from generate_series(1,5) as x union all select null, 42 union all select null, 43) ss window w as (order by x asc nulls last range between 2 preceding and 2 following); select x, y, first_value(y) over w, last_value(y) over w from (select x, x as y from generate_series(1,5) as x union all select null, 42 union all select null, 43) ss window w as (order by x desc nulls first range between 2 preceding and 2 following); select x, y, first_value(y) over w, last_value(y) over w from (select x, x as y from generate_series(1,5) as x union all select null, 42 union all select null, 43) ss window w as (order by x desc nulls last range between 2 preceding and 2 following); -- Check overflow behavior for various integer sizes select x, last_value(x) over (order by x::smallint range between current row and 2147450884 following) from generate_series(32764, 32766) x; select x, last_value(x) over (order by x::smallint desc range between current row and 2147450885 following) from generate_series(-32766, -32764) x; select x, last_value(x) over (order by x range between current row and 4 following) from generate_series(2147483644, 2147483646) x; select x, last_value(x) over (order by x desc range between current row and 5 following) from generate_series(-2147483646, -2147483644) x; select x, last_value(x) over (order by x range between current row and 4 following) from generate_series(9223372036854775804, 9223372036854775806) x; select x, last_value(x) over (order by x desc range between current row and 5 following) from generate_series(-9223372036854775806, -9223372036854775804) x; -- Test in_range for other numeric datatypes create temp table numerics( id int, f_float4 float4, f_float8 float8, f_numeric numeric ); insert into numerics values (0, '-infinity', '-infinity', '-1000'), -- numeric type lacks infinities (1, -3, -3, -3), (2, -1, -1, -1), (3, 0, 0, 0), (4, 1.1, 1.1, 1.1), (5, 1.12, 1.12, 1.12), (6, 2, 2, 2), (7, 100, 100, 100), (8, 'infinity', 'infinity', '1000'), (9, 'NaN', 'NaN', 'NaN'); select id, f_float4, first_value(id) over w, last_value(id) over w from numerics window w as (order by f_float4 range between 1 preceding and 1 following); select id, f_float4, first_value(id) over w, last_value(id) over w from numerics window w as (order by f_float4 range between 1 preceding and 1.1::float4 following); select id, f_float4, first_value(id) over w, last_value(id) over w from numerics window w as (order by f_float4 range between 'inf' preceding and 'inf' following); select id, f_float4, first_value(id) over w, last_value(id) over w from numerics window w as (order by f_float4 range between 1.1 preceding and 'NaN' following); -- error, NaN disallowed select id, f_float8, first_value(id) over w, last_value(id) over w from numerics window w as (order by f_float8 range between 1 preceding and 1 following); select id, f_float8, first_value(id) over w, last_value(id) over w from numerics window w as (order by f_float8 range between 1 preceding and 1.1::float8 following); select id, f_float8, first_value(id) over w, last_value(id) over w from numerics window w as (order by f_float8 range between 'inf' preceding and 'inf' following); select id, f_float8, first_value(id) over w, last_value(id) over w from numerics window w as (order by f_float8 range between 1.1 preceding and 'NaN' following); -- error, NaN disallowed select id, f_numeric, first_value(id) over w, last_value(id) over w from numerics window w as (order by f_numeric range between 1 preceding and 1 following); select id, f_numeric, first_value(id) over w, last_value(id) over w from numerics window w as (order by f_numeric range between 1 preceding and 1.1::numeric following); select id, f_numeric, first_value(id) over w, last_value(id) over w from numerics window w as (order by f_numeric range between 1 preceding and 1.1::float8 following); -- currently unsupported select id, f_numeric, first_value(id) over w, last_value(id) over w from numerics window w as (order by f_numeric range between 1.1 preceding and 'NaN' following); -- error, NaN disallowed -- Test in_range for other datetime datatypes create temp table datetimes( id int, f_time time, f_timetz timetz, f_interval interval, f_timestamptz timestamptz, f_timestamp timestamp ); insert into datetimes values (1, '11:00', '11:00 BST', '1 year', '2000-10-19 10:23:54+01', '2000-10-19 10:23:54'), (2, '12:00', '12:00 BST', '2 years', '2001-10-19 10:23:54+01', '2001-10-19 10:23:54'), (3, '13:00', '13:00 BST', '3 years', '2001-10-19 10:23:54+01', '2001-10-19 10:23:54'), (4, '14:00', '14:00 BST', '4 years', '2002-10-19 10:23:54+01', '2002-10-19 10:23:54'), (5, '15:00', '15:00 BST', '5 years', '2003-10-19 10:23:54+01', '2003-10-19 10:23:54'), (6, '15:00', '15:00 BST', '5 years', '2004-10-19 10:23:54+01', '2004-10-19 10:23:54'), (7, '17:00', '17:00 BST', '7 years', '2005-10-19 10:23:54+01', '2005-10-19 10:23:54'), (8, '18:00', '18:00 BST', '8 years', '2006-10-19 10:23:54+01', '2006-10-19 10:23:54'), (9, '19:00', '19:00 BST', '9 years', '2007-10-19 10:23:54+01', '2007-10-19 10:23:54'), (10, '20:00', '20:00 BST', '10 years', '2008-10-19 10:23:54+01', '2008-10-19 10:23:54'); select id, f_time, first_value(id) over w, last_value(id) over w from datetimes window w as (order by f_time range between '70 min'::interval preceding and '2 hours'::interval following); select id, f_time, first_value(id) over w, last_value(id) over w from datetimes window w as (order by f_time desc range between '70 min' preceding and '2 hours' following); select id, f_timetz, first_value(id) over w, last_value(id) over w from datetimes window w as (order by f_timetz range between '70 min'::interval preceding and '2 hours'::interval following); select id, f_timetz, first_value(id) over w, last_value(id) over w from datetimes window w as (order by f_timetz desc range between '70 min' preceding and '2 hours' following); select id, f_interval, first_value(id) over w, last_value(id) over w from datetimes window w as (order by f_interval range between '1 year'::interval preceding and '1 year'::interval following); select id, f_interval, first_value(id) over w, last_value(id) over w from datetimes window w as (order by f_interval desc range between '1 year' preceding and '1 year' following); select id, f_timestamptz, first_value(id) over w, last_value(id) over w from datetimes window w as (order by f_timestamptz range between '1 year'::interval preceding and '1 year'::interval following); select id, f_timestamptz, first_value(id) over w, last_value(id) over w from datetimes window w as (order by f_timestamptz desc range between '1 year' preceding and '1 year' following); select id, f_timestamp, first_value(id) over w, last_value(id) over w from datetimes window w as (order by f_timestamp range between '1 year'::interval preceding and '1 year'::interval following); select id, f_timestamp, first_value(id) over w, last_value(id) over w from datetimes window w as (order by f_timestamp desc range between '1 year' preceding and '1 year' following); -- RANGE offset PRECEDING/FOLLOWING error cases select sum(salary) over (order by enroll_date, salary range between '1 year'::interval preceding and '2 years'::interval following exclude ties), salary, enroll_date from empsalary; select sum(salary) over (range between '1 year'::interval preceding and '2 years'::interval following exclude ties), salary, enroll_date from empsalary; select sum(salary) over (order by depname range between '1 year'::interval preceding and '2 years'::interval following exclude ties), salary, enroll_date from empsalary; select max(enroll_date) over (order by enroll_date range between 1 preceding and 2 following exclude ties), salary, enroll_date from empsalary; select max(enroll_date) over (order by salary range between -1 preceding and 2 following exclude ties), salary, enroll_date from empsalary; select max(enroll_date) over (order by salary range between 1 preceding and -2 following exclude ties), salary, enroll_date from empsalary; select max(enroll_date) over (order by salary range between '1 year'::interval preceding and '2 years'::interval following exclude ties), salary, enroll_date from empsalary; select max(enroll_date) over (order by enroll_date range between '1 year'::interval preceding and '-2 years'::interval following exclude ties), salary, enroll_date from empsalary; -- GROUPS tests SELECT sum(unique1) over (order by four groups between unbounded preceding and current row), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (order by four groups between unbounded preceding and unbounded following), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (order by four groups between current row and unbounded following), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (order by four groups between 1 preceding and unbounded following), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (order by four groups between 1 following and unbounded following), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (order by four groups between unbounded preceding and 2 following), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (order by four groups between 2 preceding and 1 preceding), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (order by four groups between 2 preceding and 1 following), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (order by four groups between 0 preceding and 0 following), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (order by four groups between 2 preceding and 1 following exclude current row), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (order by four groups between 2 preceding and 1 following exclude group), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (order by four groups between 2 preceding and 1 following exclude ties), unique1, four FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (partition by ten order by four groups between 0 preceding and 0 following),unique1, four, ten FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (partition by ten order by four groups between 0 preceding and 0 following exclude current row), unique1, four, ten FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (partition by ten order by four groups between 0 preceding and 0 following exclude group), unique1, four, ten FROM tenk1 WHERE unique1 < 10; SELECT sum(unique1) over (partition by ten order by four groups between 0 preceding and 0 following exclude ties), unique1, four, ten FROM tenk1 WHERE unique1 < 10; select first_value(salary) over(order by enroll_date groups between 1 preceding and 1 following), lead(salary) over(order by enroll_date groups between 1 preceding and 1 following), nth_value(salary, 1) over(order by enroll_date groups between 1 preceding and 1 following), salary, enroll_date from empsalary; select last_value(salary) over(order by enroll_date groups between 1 preceding and 1 following), lag(salary) over(order by enroll_date groups between 1 preceding and 1 following), salary, enroll_date from empsalary; select first_value(salary) over(order by enroll_date groups between 1 following and 3 following exclude current row), lead(salary) over(order by enroll_date groups between 1 following and 3 following exclude ties), nth_value(salary, 1) over(order by enroll_date groups between 1 following and 3 following exclude ties), salary, enroll_date from empsalary; select last_value(salary) over(order by enroll_date groups between 1 following and 3 following exclude group), lag(salary) over(order by enroll_date groups between 1 following and 3 following exclude group), salary, enroll_date from empsalary; -- Show differences in offset interpretation between ROWS, RANGE, and GROUPS WITH cte (x) AS ( SELECT * FROM generate_series(1, 35, 2) ) SELECT x, (sum(x) over w) FROM cte WINDOW w AS (ORDER BY x rows between 1 preceding and 1 following); WITH cte (x) AS ( SELECT * FROM generate_series(1, 35, 2) ) SELECT x, (sum(x) over w) FROM cte WINDOW w AS (ORDER BY x range between 1 preceding and 1 following); WITH cte (x) AS ( SELECT * FROM generate_series(1, 35, 2) ) SELECT x, (sum(x) over w) FROM cte WINDOW w AS (ORDER BY x groups between 1 preceding and 1 following); WITH cte (x) AS ( select 1 union all select 1 union all select 1 union all SELECT * FROM generate_series(5, 49, 2) ) SELECT x, (sum(x) over w) FROM cte WINDOW w AS (ORDER BY x rows between 1 preceding and 1 following); WITH cte (x) AS ( select 1 union all select 1 union all select 1 union all SELECT * FROM generate_series(5, 49, 2) ) SELECT x, (sum(x) over w) FROM cte WINDOW w AS (ORDER BY x range between 1 preceding and 1 following); WITH cte (x) AS ( select 1 union all select 1 union all select 1 union all SELECT * FROM generate_series(5, 49, 2) ) SELECT x, (sum(x) over w) FROM cte WINDOW w AS (ORDER BY x groups between 1 preceding and 1 following); -- with UNION SELECT count(*) OVER (PARTITION BY four) FROM (SELECT * FROM tenk1 UNION ALL SELECT * FROM tenk2)s LIMIT 0; -- check some degenerate cases create temp table t1 (f1 int, f2 int8); insert into t1 values (1,1),(1,2),(2,2); select f1, sum(f1) over (partition by f1 range between 1 preceding and 1 following) from t1 where f1 = f2; -- error, must have order by explain (costs off) select f1, sum(f1) over (partition by f1 order by f2 range between 1 preceding and 1 following) from t1 where f1 = f2; select f1, sum(f1) over (partition by f1 order by f2 range between 1 preceding and 1 following) from t1 where f1 = f2; select f1, sum(f1) over (partition by f1, f1 order by f2 range between 2 preceding and 1 preceding) from t1 where f1 = f2; select f1, sum(f1) over (partition by f1, f2 order by f2 range between 1 following and 2 following) from t1 where f1 = f2; select f1, sum(f1) over (partition by f1 groups between 1 preceding and 1 following) from t1 where f1 = f2; -- error, must have order by explain (costs off) select f1, sum(f1) over (partition by f1 order by f2 groups between 1 preceding and 1 following) from t1 where f1 = f2; select f1, sum(f1) over (partition by f1 order by f2 groups between 1 preceding and 1 following) from t1 where f1 = f2; select f1, sum(f1) over (partition by f1, f1 order by f2 groups between 2 preceding and 1 preceding) from t1 where f1 = f2; select f1, sum(f1) over (partition by f1, f2 order by f2 groups between 1 following and 2 following) from t1 where f1 = f2; -- ordering by a non-integer constant is allowed SELECT rank() OVER (ORDER BY length('abc')); -- can't order by another window function SELECT rank() OVER (ORDER BY rank() OVER (ORDER BY random())); -- some other errors SELECT * FROM empsalary WHERE row_number() OVER (ORDER BY salary) < 10; SELECT * FROM empsalary INNER JOIN tenk1 ON row_number() OVER (ORDER BY salary) < 10; SELECT rank() OVER (ORDER BY 1), count(*) FROM empsalary GROUP BY 1; SELECT * FROM rank() OVER (ORDER BY random()); DELETE FROM empsalary WHERE (rank() OVER (ORDER BY random())) > 10; DELETE FROM empsalary RETURNING rank() OVER (ORDER BY random()); SELECT count(*) OVER w FROM tenk1 WINDOW w AS (ORDER BY unique1), w AS (ORDER BY unique1); SELECT rank() OVER (PARTITION BY four, ORDER BY ten) FROM tenk1; SELECT count() OVER () FROM tenk1; SELECT generate_series(1, 100) OVER () FROM empsalary; SELECT ntile(0) OVER (ORDER BY ten), ten, four FROM tenk1; SELECT nth_value(four, 0) OVER (ORDER BY ten), ten, four FROM tenk1; -- filter SELECT sum(salary), row_number() OVER (ORDER BY depname), sum( sum(salary) FILTER (WHERE enroll_date > '2007-01-01') ) FILTER (WHERE depname <> 'sales') OVER (ORDER BY depname DESC) AS "filtered_sum", depname FROM empsalary GROUP BY depname; -- Test pushdown of quals into a subquery containing window functions -- pushdown is safe because all PARTITION BY clauses include depname: EXPLAIN (COSTS OFF) SELECT * FROM (SELECT depname, sum(salary) OVER (PARTITION BY depname) depsalary, min(salary) OVER (PARTITION BY depname || 'A', depname) depminsalary FROM empsalary) emp WHERE depname = 'sales'; -- pushdown is unsafe because there's a PARTITION BY clause without depname: EXPLAIN (COSTS OFF) SELECT * FROM (SELECT depname, sum(salary) OVER (PARTITION BY enroll_date) enroll_salary, min(salary) OVER (PARTITION BY depname) depminsalary FROM empsalary) emp WHERE depname = 'sales'; -- Test Sort node collapsing EXPLAIN (COSTS OFF) SELECT * FROM (SELECT depname, sum(salary) OVER (PARTITION BY depname order by empno) depsalary, min(salary) OVER (PARTITION BY depname, empno order by enroll_date) depminsalary FROM empsalary) emp WHERE depname = 'sales'; -- Test Sort node reordering EXPLAIN (COSTS OFF) SELECT lead(1) OVER (PARTITION BY depname ORDER BY salary, enroll_date), lag(1) OVER (PARTITION BY depname ORDER BY salary,enroll_date,empno) FROM empsalary; -- cleanup DROP TABLE empsalary; -- test user-defined window function with named args and default args CREATE FUNCTION nth_value_def(val anyelement, n integer = 1) RETURNS anyelement LANGUAGE internal WINDOW IMMUTABLE STRICT AS 'window_nth_value'; SELECT nth_value_def(n := 2, val := ten) OVER (PARTITION BY four), ten, four FROM (SELECT * FROM tenk1 WHERE unique2 < 10 ORDER BY four, ten) s; SELECT nth_value_def(ten) OVER (PARTITION BY four), ten, four FROM (SELECT * FROM tenk1 WHERE unique2 < 10 ORDER BY four, ten) s; -- -- Test the basic moving-aggregate machinery -- -- create aggregates that record the series of transform calls (these are -- intentionally not true inverses) CREATE FUNCTION logging_sfunc_nonstrict(text, anyelement) RETURNS text AS $$ SELECT COALESCE($1, '') || '*' || quote_nullable($2) $$ LANGUAGE SQL IMMUTABLE; CREATE FUNCTION logging_msfunc_nonstrict(text, anyelement) RETURNS text AS $$ SELECT COALESCE($1, '') || '+' || quote_nullable($2) $$ LANGUAGE SQL IMMUTABLE; CREATE FUNCTION logging_minvfunc_nonstrict(text, anyelement) RETURNS text AS $$ SELECT $1 || '-' || quote_nullable($2) $$ LANGUAGE SQL IMMUTABLE; CREATE AGGREGATE logging_agg_nonstrict (anyelement) ( stype = text, sfunc = logging_sfunc_nonstrict, mstype = text, msfunc = logging_msfunc_nonstrict, minvfunc = logging_minvfunc_nonstrict ); CREATE AGGREGATE logging_agg_nonstrict_initcond (anyelement) ( stype = text, sfunc = logging_sfunc_nonstrict, mstype = text, msfunc = logging_msfunc_nonstrict, minvfunc = logging_minvfunc_nonstrict, initcond = 'I', minitcond = 'MI' ); CREATE FUNCTION logging_sfunc_strict(text, anyelement) RETURNS text AS $$ SELECT $1 || '*' || quote_nullable($2) $$ LANGUAGE SQL STRICT IMMUTABLE; CREATE FUNCTION logging_msfunc_strict(text, anyelement) RETURNS text AS $$ SELECT $1 || '+' || quote_nullable($2) $$ LANGUAGE SQL STRICT IMMUTABLE; CREATE FUNCTION logging_minvfunc_strict(text, anyelement) RETURNS text AS $$ SELECT $1 || '-' || quote_nullable($2) $$ LANGUAGE SQL STRICT IMMUTABLE; CREATE AGGREGATE logging_agg_strict (text) ( stype = text, sfunc = logging_sfunc_strict, mstype = text, msfunc = logging_msfunc_strict, minvfunc = logging_minvfunc_strict ); CREATE AGGREGATE logging_agg_strict_initcond (anyelement) ( stype = text, sfunc = logging_sfunc_strict, mstype = text, msfunc = logging_msfunc_strict, minvfunc = logging_minvfunc_strict, initcond = 'I', minitcond = 'MI' ); -- test strict and non-strict cases SELECT p::text || ',' || i::text || ':' || COALESCE(v::text, 'NULL') AS row, logging_agg_nonstrict(v) over wnd as nstrict, logging_agg_nonstrict_initcond(v) over wnd as nstrict_init, logging_agg_strict(v::text) over wnd as strict, logging_agg_strict_initcond(v) over wnd as strict_init FROM (VALUES (1, 1, NULL), (1, 2, 'a'), (1, 3, 'b'), (1, 4, NULL), (1, 5, NULL), (1, 6, 'c'), (2, 1, NULL), (2, 2, 'x'), (3, 1, 'z') ) AS t(p, i, v) WINDOW wnd AS (PARTITION BY P ORDER BY i ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) ORDER BY p, i; -- and again, but with filter SELECT p::text || ',' || i::text || ':' || CASE WHEN f THEN COALESCE(v::text, 'NULL') ELSE '-' END as row, logging_agg_nonstrict(v) filter(where f) over wnd as nstrict_filt, logging_agg_nonstrict_initcond(v) filter(where f) over wnd as nstrict_init_filt, logging_agg_strict(v::text) filter(where f) over wnd as strict_filt, logging_agg_strict_initcond(v) filter(where f) over wnd as strict_init_filt FROM (VALUES (1, 1, true, NULL), (1, 2, false, 'a'), (1, 3, true, 'b'), (1, 4, false, NULL), (1, 5, false, NULL), (1, 6, false, 'c'), (2, 1, false, NULL), (2, 2, true, 'x'), (3, 1, true, 'z') ) AS t(p, i, f, v) WINDOW wnd AS (PARTITION BY p ORDER BY i ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) ORDER BY p, i; -- test that volatile arguments disable moving-aggregate mode SELECT i::text || ':' || COALESCE(v::text, 'NULL') as row, logging_agg_strict(v::text) over wnd as inverse, logging_agg_strict(v::text || CASE WHEN random() < 0 then '?' ELSE '' END) over wnd as noinverse FROM (VALUES (1, 'a'), (2, 'b'), (3, 'c') ) AS t(i, v) WINDOW wnd AS (ORDER BY i ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) ORDER BY i; SELECT i::text || ':' || COALESCE(v::text, 'NULL') as row, logging_agg_strict(v::text) filter(where true) over wnd as inverse, logging_agg_strict(v::text) filter(where random() >= 0) over wnd as noinverse FROM (VALUES (1, 'a'), (2, 'b'), (3, 'c') ) AS t(i, v) WINDOW wnd AS (ORDER BY i ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) ORDER BY i; -- test that non-overlapping windows don't use inverse transitions SELECT logging_agg_strict(v::text) OVER wnd FROM (VALUES (1, 'a'), (2, 'b'), (3, 'c') ) AS t(i, v) WINDOW wnd AS (ORDER BY i ROWS BETWEEN CURRENT ROW AND CURRENT ROW) ORDER BY i; -- test that returning NULL from the inverse transition functions -- restarts the aggregation from scratch. The second aggregate is supposed -- to test cases where only some aggregates restart, the third one checks -- that one aggregate restarting doesn't cause others to restart. CREATE FUNCTION sum_int_randrestart_minvfunc(int4, int4) RETURNS int4 AS $$ SELECT CASE WHEN random() < 0.2 THEN NULL ELSE $1 - $2 END $$ LANGUAGE SQL STRICT; CREATE AGGREGATE sum_int_randomrestart (int4) ( stype = int4, sfunc = int4pl, mstype = int4, msfunc = int4pl, minvfunc = sum_int_randrestart_minvfunc ); WITH vs AS ( SELECT i, (random() * 100)::int4 AS v FROM generate_series(1, 100) AS i ), sum_following AS ( SELECT i, SUM(v) OVER (ORDER BY i DESC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS s FROM vs ) SELECT DISTINCT sum_following.s = sum_int_randomrestart(v) OVER fwd AS eq1, -sum_following.s = sum_int_randomrestart(-v) OVER fwd AS eq2, 100*3+(vs.i-1)*3 = length(logging_agg_nonstrict(''::text) OVER fwd) AS eq3 FROM vs JOIN sum_following ON sum_following.i = vs.i WINDOW fwd AS ( ORDER BY vs.i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING ); -- -- Test various built-in aggregates that have moving-aggregate support -- -- test inverse transition functions handle NULLs properly SELECT i,AVG(v::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v); SELECT i,AVG(v::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v); SELECT i,AVG(v::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v); SELECT i,AVG(v::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,1.5),(2,2.5),(3,NULL),(4,NULL)) t(i,v); SELECT i,AVG(v::interval) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,'1 sec'),(2,'2 sec'),(3,NULL),(4,NULL)) t(i,v); SELECT i,SUM(v::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v); SELECT i,SUM(v::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v); SELECT i,SUM(v::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v); SELECT i,SUM(v::money) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,'1.10'),(2,'2.20'),(3,NULL),(4,NULL)) t(i,v); SELECT i,SUM(v::interval) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,'1 sec'),(2,'2 sec'),(3,NULL),(4,NULL)) t(i,v); SELECT i,SUM(v::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,1.1),(2,2.2),(3,NULL),(4,NULL)) t(i,v); SELECT SUM(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,1.01),(2,2),(3,3)) v(i,n); SELECT i,COUNT(v) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v); SELECT i,COUNT(*) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v); SELECT VAR_POP(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n); SELECT VAR_POP(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n); SELECT VAR_POP(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n); SELECT VAR_POP(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n); SELECT VAR_SAMP(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n); SELECT VAR_SAMP(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n); SELECT VAR_SAMP(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n); SELECT VAR_SAMP(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n); SELECT VARIANCE(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n); SELECT VARIANCE(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n); SELECT VARIANCE(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n); SELECT VARIANCE(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n); SELECT STDDEV_POP(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n); SELECT STDDEV_POP(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n); SELECT STDDEV_POP(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n); SELECT STDDEV_POP(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n); SELECT STDDEV_SAMP(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n); SELECT STDDEV_SAMP(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n); SELECT STDDEV_SAMP(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n); SELECT STDDEV_SAMP(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,NULL),(2,600),(3,470),(4,170),(5,430),(6,300)) r(i,n); SELECT STDDEV(n::bigint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(0,NULL),(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n); SELECT STDDEV(n::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(0,NULL),(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n); SELECT STDDEV(n::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(0,NULL),(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n); SELECT STDDEV(n::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(0,NULL),(1,600),(2,470),(3,170),(4,430),(5,300)) r(i,n); -- test that inverse transition functions work with various frame options SELECT i,SUM(v::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND CURRENT ROW) FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v); SELECT i,SUM(v::int) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v); SELECT i,SUM(v::int) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM (VALUES(1,1),(2,2),(3,3),(4,4)) t(i,v); -- ensure aggregate over numeric properly recovers from NaN values SELECT a, b, SUM(b) OVER(ORDER BY A ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) FROM (VALUES(1,1::numeric),(2,2),(3,'NaN'),(4,3),(5,4)) t(a,b); -- It might be tempting for someone to add an inverse trans function for -- float and double precision. This should not be done as it can give incorrect -- results. This test should fail if anyone ever does this without thinking too -- hard about it. SELECT to_char(SUM(n::float8) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING),'999999999999999999999D9') FROM (VALUES(1,1e20),(2,1)) n(i,n); SELECT i, b, bool_and(b) OVER w, bool_or(b) OVER w FROM (VALUES (1,true), (2,true), (3,false), (4,false), (5,true)) v(i,b) WINDOW w AS (ORDER BY i ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING); pgFormatter-4.2/t/pg-test-files/sql/with.sql000066400000000000000000000621761361326045100211070ustar00rootroot00000000000000-- -- Tests for common table expressions (WITH query, ... SELECT ...) -- -- Basic WITH WITH q1(x,y) AS (SELECT 1,2) SELECT * FROM q1, q1 AS q2; -- Multiple uses are evaluated only once SELECT count(*) FROM ( WITH q1(x) AS (SELECT random() FROM generate_series(1, 5)) SELECT * FROM q1 UNION SELECT * FROM q1 ) ss; -- WITH RECURSIVE -- sum of 1..100 WITH RECURSIVE t(n) AS ( VALUES (1) UNION ALL SELECT n+1 FROM t WHERE n < 100 ) SELECT sum(n) FROM t; WITH RECURSIVE t(n) AS ( SELECT (VALUES(1)) UNION ALL SELECT n+1 FROM t WHERE n < 5 ) SELECT * FROM t; -- recursive view CREATE RECURSIVE VIEW nums (n) AS VALUES (1) UNION ALL SELECT n+1 FROM nums WHERE n < 5; SELECT * FROM nums; CREATE OR REPLACE RECURSIVE VIEW nums (n) AS VALUES (1) UNION ALL SELECT n+1 FROM nums WHERE n < 6; SELECT * FROM nums; -- This is an infinite loop with UNION ALL, but not with UNION WITH RECURSIVE t(n) AS ( SELECT 1 UNION SELECT 10-n FROM t) SELECT * FROM t; -- This'd be an infinite loop, but outside query reads only as much as needed WITH RECURSIVE t(n) AS ( VALUES (1) UNION ALL SELECT n+1 FROM t) SELECT * FROM t LIMIT 10; -- UNION case should have same property WITH RECURSIVE t(n) AS ( SELECT 1 UNION SELECT n+1 FROM t) SELECT * FROM t LIMIT 10; -- Test behavior with an unknown-type literal in the WITH WITH q AS (SELECT 'foo' AS x) SELECT x, x IS OF (text) AS is_text FROM q; WITH RECURSIVE t(n) AS ( SELECT 'foo' UNION ALL SELECT n || ' bar' FROM t WHERE length(n) < 20 ) SELECT n, n IS OF (text) AS is_text FROM t; -- In a perfect world, this would work and resolve the literal as int ... -- but for now, we have to be content with resolving to text too soon. WITH RECURSIVE t(n) AS ( SELECT '7' UNION ALL SELECT n+1 FROM t WHERE n < 10 ) SELECT n, n IS OF (int) AS is_int FROM t; -- -- Some examples with a tree -- -- department structure represented here is as follows: -- -- ROOT-+->A-+->B-+->C -- | | -- | +->D-+->F -- +->E-+->G CREATE TEMP TABLE department ( id INTEGER PRIMARY KEY, -- department ID parent_department INTEGER REFERENCES department, -- upper department ID name TEXT -- department name ); INSERT INTO department VALUES (0, NULL, 'ROOT'); INSERT INTO department VALUES (1, 0, 'A'); INSERT INTO department VALUES (2, 1, 'B'); INSERT INTO department VALUES (3, 2, 'C'); INSERT INTO department VALUES (4, 2, 'D'); INSERT INTO department VALUES (5, 0, 'E'); INSERT INTO department VALUES (6, 4, 'F'); INSERT INTO department VALUES (7, 5, 'G'); -- extract all departments under 'A'. Result should be A, B, C, D and F WITH RECURSIVE subdepartment AS ( -- non recursive term SELECT name as root_name, * FROM department WHERE name = 'A' UNION ALL -- recursive term SELECT sd.root_name, d.* FROM department AS d, subdepartment AS sd WHERE d.parent_department = sd.id ) SELECT * FROM subdepartment ORDER BY name; -- extract all departments under 'A' with "level" number WITH RECURSIVE subdepartment(level, id, parent_department, name) AS ( -- non recursive term SELECT 1, * FROM department WHERE name = 'A' UNION ALL -- recursive term SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd WHERE d.parent_department = sd.id ) SELECT * FROM subdepartment ORDER BY name; -- extract all departments under 'A' with "level" number. -- Only shows level 2 or more WITH RECURSIVE subdepartment(level, id, parent_department, name) AS ( -- non recursive term SELECT 1, * FROM department WHERE name = 'A' UNION ALL -- recursive term SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd WHERE d.parent_department = sd.id ) SELECT * FROM subdepartment WHERE level >= 2 ORDER BY name; -- "RECURSIVE" is ignored if the query has no self-reference WITH RECURSIVE subdepartment AS ( -- note lack of recursive UNION structure SELECT * FROM department WHERE name = 'A' ) SELECT * FROM subdepartment ORDER BY name; -- inside subqueries SELECT count(*) FROM ( WITH RECURSIVE t(n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 500 ) SELECT * FROM t) AS t WHERE n < ( SELECT count(*) FROM ( WITH RECURSIVE t(n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 100 ) SELECT * FROM t WHERE n < 50000 ) AS t WHERE n < 100); -- use same CTE twice at different subquery levels WITH q1(x,y) AS ( SELECT hundred, sum(ten) FROM tenk1 GROUP BY hundred ) SELECT count(*) FROM q1 WHERE y > (SELECT sum(y)/100 FROM q1 qsub); -- via a VIEW CREATE TEMPORARY VIEW vsubdepartment AS WITH RECURSIVE subdepartment AS ( -- non recursive term SELECT * FROM department WHERE name = 'A' UNION ALL -- recursive term SELECT d.* FROM department AS d, subdepartment AS sd WHERE d.parent_department = sd.id ) SELECT * FROM subdepartment; SELECT * FROM vsubdepartment ORDER BY name; -- Check reverse listing SELECT pg_get_viewdef('vsubdepartment'::regclass); SELECT pg_get_viewdef('vsubdepartment'::regclass, true); -- Another reverse-listing example CREATE VIEW sums_1_100 AS WITH RECURSIVE t(n) AS ( VALUES (1) UNION ALL SELECT n+1 FROM t WHERE n < 100 ) SELECT sum(n) FROM t; \d+ sums_1_100 -- corner case in which sub-WITH gets initialized first with recursive q as ( select * from department union all (with x as (select * from q) select * from x) ) select * from q limit 24; with recursive q as ( select * from department union all (with recursive x as ( select * from department union all (select * from q union all select * from x) ) select * from x) ) select * from q limit 32; -- recursive term has sub-UNION WITH RECURSIVE t(i,j) AS ( VALUES (1,2) UNION ALL SELECT t2.i, t.j+1 FROM (SELECT 2 AS i UNION ALL SELECT 3 AS i) AS t2 JOIN t ON (t2.i = t.i+1)) SELECT * FROM t; -- -- different tree example -- CREATE TEMPORARY TABLE tree( id INTEGER PRIMARY KEY, parent_id INTEGER REFERENCES tree(id) ); INSERT INTO tree VALUES (1, NULL), (2, 1), (3,1), (4,2), (5,2), (6,2), (7,3), (8,3), (9,4), (10,4), (11,7), (12,7), (13,7), (14, 9), (15,11), (16,11); -- -- get all paths from "second level" nodes to leaf nodes -- WITH RECURSIVE t(id, path) AS ( VALUES(1,ARRAY[]::integer[]) UNION ALL SELECT tree.id, t.path || tree.id FROM tree JOIN t ON (tree.parent_id = t.id) ) SELECT t1.*, t2.* FROM t AS t1 JOIN t AS t2 ON (t1.path[1] = t2.path[1] AND array_upper(t1.path,1) = 1 AND array_upper(t2.path,1) > 1) ORDER BY t1.id, t2.id; -- just count 'em WITH RECURSIVE t(id, path) AS ( VALUES(1,ARRAY[]::integer[]) UNION ALL SELECT tree.id, t.path || tree.id FROM tree JOIN t ON (tree.parent_id = t.id) ) SELECT t1.id, count(t2.*) FROM t AS t1 JOIN t AS t2 ON (t1.path[1] = t2.path[1] AND array_upper(t1.path,1) = 1 AND array_upper(t2.path,1) > 1) GROUP BY t1.id ORDER BY t1.id; -- this variant tickled a whole-row-variable bug in 8.4devel WITH RECURSIVE t(id, path) AS ( VALUES(1,ARRAY[]::integer[]) UNION ALL SELECT tree.id, t.path || tree.id FROM tree JOIN t ON (tree.parent_id = t.id) ) SELECT t1.id, t2.path, t2 FROM t AS t1 JOIN t AS t2 ON (t1.id=t2.id); -- -- test cycle detection -- create temp table graph( f int, t int, label text ); insert into graph values (1, 2, 'arc 1 -> 2'), (1, 3, 'arc 1 -> 3'), (2, 3, 'arc 2 -> 3'), (1, 4, 'arc 1 -> 4'), (4, 5, 'arc 4 -> 5'), (5, 1, 'arc 5 -> 1'); with recursive search_graph(f, t, label, path, cycle) as ( select *, array[row(g.f, g.t)], false from graph g union all select g.*, path || row(g.f, g.t), row(g.f, g.t) = any(path) from graph g, search_graph sg where g.f = sg.t and not cycle ) select * from search_graph; -- ordering by the path column has same effect as SEARCH DEPTH FIRST with recursive search_graph(f, t, label, path, cycle) as ( select *, array[row(g.f, g.t)], false from graph g union all select g.*, path || row(g.f, g.t), row(g.f, g.t) = any(path) from graph g, search_graph sg where g.f = sg.t and not cycle ) select * from search_graph order by path; -- -- test multiple WITH queries -- WITH RECURSIVE y (id) AS (VALUES (1)), x (id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5) SELECT * FROM x; -- forward reference OK WITH RECURSIVE x(id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5), y(id) AS (values (1)) SELECT * FROM x; WITH RECURSIVE x(id) AS (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5), y(id) AS (VALUES (1) UNION ALL SELECT id+1 FROM y WHERE id < 10) SELECT y.*, x.* FROM y LEFT JOIN x USING (id); WITH RECURSIVE x(id) AS (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5), y(id) AS (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 10) SELECT y.*, x.* FROM y LEFT JOIN x USING (id); WITH RECURSIVE x(id) AS (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ), y(id) AS (SELECT * FROM x UNION ALL SELECT * FROM x), z(id) AS (SELECT * FROM x UNION ALL SELECT id+1 FROM z WHERE id < 10) SELECT * FROM z; WITH RECURSIVE x(id) AS (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ), y(id) AS (SELECT * FROM x UNION ALL SELECT * FROM x), z(id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM z WHERE id < 10) SELECT * FROM z; -- -- Test WITH attached to a data-modifying statement -- CREATE TEMPORARY TABLE y (a INTEGER); INSERT INTO y SELECT generate_series(1, 10); WITH t AS ( SELECT a FROM y ) INSERT INTO y SELECT a+20 FROM t RETURNING *; SELECT * FROM y; WITH t AS ( SELECT a FROM y ) UPDATE y SET a = y.a-10 FROM t WHERE y.a > 20 AND t.a = y.a RETURNING y.a; SELECT * FROM y; WITH RECURSIVE t(a) AS ( SELECT 11 UNION ALL SELECT a+1 FROM t WHERE a < 50 ) DELETE FROM y USING t WHERE t.a = y.a RETURNING y.a; SELECT * FROM y; DROP TABLE y; -- -- error cases -- -- INTERSECT WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT SELECT n+1 FROM x) SELECT * FROM x; WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT ALL SELECT n+1 FROM x) SELECT * FROM x; -- EXCEPT WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT SELECT n+1 FROM x) SELECT * FROM x; WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT ALL SELECT n+1 FROM x) SELECT * FROM x; -- no non-recursive term WITH RECURSIVE x(n) AS (SELECT n FROM x) SELECT * FROM x; -- recursive term in the left hand side (strictly speaking, should allow this) WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1) SELECT * FROM x; CREATE TEMPORARY TABLE y (a INTEGER); INSERT INTO y SELECT generate_series(1, 10); -- LEFT JOIN WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 UNION ALL SELECT x.n+1 FROM y LEFT JOIN x ON x.n = y.a WHERE n < 10) SELECT * FROM x; -- RIGHT JOIN WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 UNION ALL SELECT x.n+1 FROM x RIGHT JOIN y ON x.n = y.a WHERE n < 10) SELECT * FROM x; -- FULL JOIN WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 UNION ALL SELECT x.n+1 FROM x FULL JOIN y ON x.n = y.a WHERE n < 10) SELECT * FROM x; -- subquery WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x WHERE n IN (SELECT * FROM x)) SELECT * FROM x; -- aggregate functions WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT count(*) FROM x) SELECT * FROM x; WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT sum(n) FROM x) SELECT * FROM x; -- ORDER BY WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x ORDER BY 1) SELECT * FROM x; -- LIMIT/OFFSET WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x LIMIT 10 OFFSET 1) SELECT * FROM x; -- FOR UPDATE WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x FOR UPDATE) SELECT * FROM x; -- target list has a recursive query name WITH RECURSIVE x(id) AS (values (1) UNION ALL SELECT (SELECT * FROM x) FROM x WHERE id < 5 ) SELECT * FROM x; -- mutual recursive query (not implemented) WITH RECURSIVE x (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM y WHERE id < 5), y (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 5) SELECT * FROM x; -- non-linear recursion is not allowed WITH RECURSIVE foo(i) AS (values (1) UNION ALL (SELECT i+1 FROM foo WHERE i < 10 UNION ALL SELECT i+1 FROM foo WHERE i < 5) ) SELECT * FROM foo; WITH RECURSIVE foo(i) AS (values (1) UNION ALL SELECT * FROM (SELECT i+1 FROM foo WHERE i < 10 UNION ALL SELECT i+1 FROM foo WHERE i < 5) AS t ) SELECT * FROM foo; WITH RECURSIVE foo(i) AS (values (1) UNION ALL (SELECT i+1 FROM foo WHERE i < 10 EXCEPT SELECT i+1 FROM foo WHERE i < 5) ) SELECT * FROM foo; WITH RECURSIVE foo(i) AS (values (1) UNION ALL (SELECT i+1 FROM foo WHERE i < 10 INTERSECT SELECT i+1 FROM foo WHERE i < 5) ) SELECT * FROM foo; -- Wrong type induced from non-recursive term WITH RECURSIVE foo(i) AS (SELECT i FROM (VALUES(1),(2)) t(i) UNION ALL SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) SELECT * FROM foo; -- rejects different typmod, too (should we allow this?) WITH RECURSIVE foo(i) AS (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i) UNION ALL SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) SELECT * FROM foo; -- disallow OLD/NEW reference in CTE CREATE TEMPORARY TABLE x (n integer); CREATE RULE r2 AS ON UPDATE TO x DO INSTEAD WITH t AS (SELECT OLD.*) UPDATE y SET a = t.n FROM t; -- -- test for bug #4902 -- with cte(foo) as ( values(42) ) values((select foo from cte)); with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q; -- test CTE referencing an outer-level variable (to see that changed-parameter -- signaling still works properly after fixing this bug) select ( with cte(foo) as ( values(f1) ) select (select foo from cte) ) from int4_tbl; select ( with cte(foo) as ( values(f1) ) values((select foo from cte)) ) from int4_tbl; -- -- test for nested-recursive-WITH bug -- WITH RECURSIVE t(j) AS ( WITH RECURSIVE s(i) AS ( VALUES (1) UNION ALL SELECT i+1 FROM s WHERE i < 10 ) SELECT i FROM s UNION ALL SELECT j+1 FROM t WHERE j < 10 ) SELECT * FROM t; -- -- test WITH attached to intermediate-level set operation -- WITH outermost(x) AS ( SELECT 1 UNION (WITH innermost as (SELECT 2) SELECT * FROM innermost UNION SELECT 3) ) SELECT * FROM outermost ORDER BY 1; WITH outermost(x) AS ( SELECT 1 UNION (WITH innermost as (SELECT 2) SELECT * FROM outermost -- fail UNION SELECT * FROM innermost) ) SELECT * FROM outermost ORDER BY 1; WITH RECURSIVE outermost(x) AS ( SELECT 1 UNION (WITH innermost as (SELECT 2) SELECT * FROM outermost UNION SELECT * FROM innermost) ) SELECT * FROM outermost ORDER BY 1; WITH RECURSIVE outermost(x) AS ( WITH innermost as (SELECT 2 FROM outermost) -- fail SELECT * FROM innermost UNION SELECT * from outermost ) SELECT * FROM outermost ORDER BY 1; -- -- This test will fail with the old implementation of PARAM_EXEC parameter -- assignment, because the "q1" Var passed down to A's targetlist subselect -- looks exactly like the "A.id" Var passed down to C's subselect, causing -- the old code to give them the same runtime PARAM_EXEC slot. But the -- lifespans of the two parameters overlap, thanks to B also reading A. -- with A as ( select q2 as id, (select q1) as x from int8_tbl ), B as ( select id, row_number() over (partition by id) as r from A ), C as ( select A.id, array(select B.id from B where B.id = A.id) from A ) select * from C; -- -- Test CTEs read in non-initialization orders -- WITH RECURSIVE tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)), iter (id_key, row_type, link) AS ( SELECT 0, 'base', 17 UNION ALL ( WITH remaining(id_key, row_type, link, min) AS ( SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () FROM tab INNER JOIN iter USING (link) WHERE tab.id_key > iter.id_key ), first_remaining AS ( SELECT id_key, row_type, link FROM remaining WHERE id_key=min ), effect AS ( SELECT tab.id_key, 'new'::text, tab.link FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key WHERE e.row_type = 'false' ) SELECT * FROM first_remaining UNION ALL SELECT * FROM effect ) ) SELECT * FROM iter; WITH RECURSIVE tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)), iter (id_key, row_type, link) AS ( SELECT 0, 'base', 17 UNION ( WITH remaining(id_key, row_type, link, min) AS ( SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () FROM tab INNER JOIN iter USING (link) WHERE tab.id_key > iter.id_key ), first_remaining AS ( SELECT id_key, row_type, link FROM remaining WHERE id_key=min ), effect AS ( SELECT tab.id_key, 'new'::text, tab.link FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key WHERE e.row_type = 'false' ) SELECT * FROM first_remaining UNION ALL SELECT * FROM effect ) ) SELECT * FROM iter; -- -- Data-modifying statements in WITH -- -- INSERT ... RETURNING WITH t AS ( INSERT INTO y VALUES (11), (12), (13), (14), (15), (16), (17), (18), (19), (20) RETURNING * ) SELECT * FROM t; SELECT * FROM y; -- UPDATE ... RETURNING WITH t AS ( UPDATE y SET a=a+1 RETURNING * ) SELECT * FROM t; SELECT * FROM y; -- DELETE ... RETURNING WITH t AS ( DELETE FROM y WHERE a <= 10 RETURNING * ) SELECT * FROM t; SELECT * FROM y; -- forward reference WITH RECURSIVE t AS ( INSERT INTO y SELECT a+5 FROM t2 WHERE a > 5 RETURNING * ), t2 AS ( UPDATE y SET a=a-11 RETURNING * ) SELECT * FROM t UNION ALL SELECT * FROM t2; SELECT * FROM y; -- unconditional DO INSTEAD rule CREATE RULE y_rule AS ON DELETE TO y DO INSTEAD INSERT INTO y VALUES(42) RETURNING *; WITH t AS ( DELETE FROM y RETURNING * ) SELECT * FROM t; SELECT * FROM y; DROP RULE y_rule ON y; -- check merging of outer CTE with CTE in a rule action CREATE TEMP TABLE bug6051 AS select i from generate_series(1,3) as t(i); SELECT * FROM bug6051; WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) INSERT INTO bug6051 SELECT * FROM t1; SELECT * FROM bug6051; CREATE TEMP TABLE bug6051_2 (i int); CREATE RULE bug6051_ins AS ON INSERT TO bug6051 DO INSTEAD INSERT INTO bug6051_2 SELECT NEW.i; WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) INSERT INTO bug6051 SELECT * FROM t1; SELECT * FROM bug6051; SELECT * FROM bug6051_2; -- a truly recursive CTE in the same list WITH RECURSIVE t(a) AS ( SELECT 0 UNION ALL SELECT a+1 FROM t WHERE a+1 < 5 ), t2 as ( INSERT INTO y SELECT * FROM t RETURNING * ) SELECT * FROM t2 JOIN y USING (a) ORDER BY a; SELECT * FROM y; -- data-modifying WITH in a modifying statement WITH t AS ( DELETE FROM y WHERE a <= 10 RETURNING * ) INSERT INTO y SELECT -a FROM t RETURNING *; SELECT * FROM y; -- check that WITH query is run to completion even if outer query isn't WITH t AS ( UPDATE y SET a = a * 100 RETURNING * ) SELECT * FROM t LIMIT 10; SELECT * FROM y; -- data-modifying WITH containing INSERT...ON CONFLICT DO UPDATE CREATE TABLE withz AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i; ALTER TABLE withz ADD UNIQUE (k); WITH t AS ( INSERT INTO withz SELECT i, 'insert' FROM generate_series(0, 16) i ON CONFLICT (k) DO UPDATE SET v = withz.v || ', now update' RETURNING * ) SELECT * FROM t JOIN y ON t.k = y.a ORDER BY a, k; -- Test EXCLUDED.* reference within CTE WITH aa AS ( INSERT INTO withz VALUES(1, 5) ON CONFLICT (k) DO UPDATE SET v = EXCLUDED.v WHERE withz.k != EXCLUDED.k RETURNING * ) SELECT * FROM aa; -- New query/snapshot demonstrates side-effects of previous query. SELECT * FROM withz ORDER BY k; -- -- Ensure subqueries within the update clause work, even if they -- reference outside values -- WITH aa AS (SELECT 1 a, 2 b) INSERT INTO withz VALUES(1, 'insert') ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); WITH aa AS (SELECT 1 a, 2 b) INSERT INTO withz VALUES(1, 'insert') ON CONFLICT (k) DO UPDATE SET v = ' update' WHERE withz.k = (SELECT a FROM aa); WITH aa AS (SELECT 1 a, 2 b) INSERT INTO withz VALUES(1, 'insert') ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); WITH aa AS (SELECT 'a' a, 'b' b UNION ALL SELECT 'a' a, 'b' b) INSERT INTO withz VALUES(1, 'insert') ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 'a' LIMIT 1); WITH aa AS (SELECT 1 a, 2 b) INSERT INTO withz VALUES(1, (SELECT b || ' insert' FROM aa WHERE a = 1 )) ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); -- Update a row more than once, in different parts of a wCTE. That is -- an allowed, presumably very rare, edge case, but since it was -- broken in the past, having a test seems worthwhile. WITH simpletup AS ( SELECT 2 k, 'Green' v), upsert_cte AS ( INSERT INTO withz VALUES(2, 'Blue') ON CONFLICT (k) DO UPDATE SET (k, v) = (SELECT k, v FROM simpletup WHERE simpletup.k = withz.k) RETURNING k, v) INSERT INTO withz VALUES(2, 'Red') ON CONFLICT (k) DO UPDATE SET (k, v) = (SELECT k, v FROM upsert_cte WHERE upsert_cte.k = withz.k) RETURNING k, v; DROP TABLE withz; -- check that run to completion happens in proper ordering TRUNCATE TABLE y; INSERT INTO y SELECT generate_series(1, 3); CREATE TEMPORARY TABLE yy (a INTEGER); WITH RECURSIVE t1 AS ( INSERT INTO y SELECT * FROM y RETURNING * ), t2 AS ( INSERT INTO yy SELECT * FROM t1 RETURNING * ) SELECT 1; SELECT * FROM y; SELECT * FROM yy; WITH RECURSIVE t1 AS ( INSERT INTO yy SELECT * FROM t2 RETURNING * ), t2 AS ( INSERT INTO y SELECT * FROM y RETURNING * ) SELECT 1; SELECT * FROM y; SELECT * FROM yy; -- triggers TRUNCATE TABLE y; INSERT INTO y SELECT generate_series(1, 10); CREATE FUNCTION y_trigger() RETURNS trigger AS $$ begin raise notice 'y_trigger: a = %', new.a; return new; end; $$ LANGUAGE plpgsql; CREATE TRIGGER y_trig BEFORE INSERT ON y FOR EACH ROW EXECUTE PROCEDURE y_trigger(); WITH t AS ( INSERT INTO y VALUES (21), (22), (23) RETURNING * ) SELECT * FROM t; SELECT * FROM y; DROP TRIGGER y_trig ON y; CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH ROW EXECUTE PROCEDURE y_trigger(); WITH t AS ( INSERT INTO y VALUES (31), (32), (33) RETURNING * ) SELECT * FROM t LIMIT 1; SELECT * FROM y; DROP TRIGGER y_trig ON y; CREATE OR REPLACE FUNCTION y_trigger() RETURNS trigger AS $$ begin raise notice 'y_trigger'; return null; end; $$ LANGUAGE plpgsql; CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH STATEMENT EXECUTE PROCEDURE y_trigger(); WITH t AS ( INSERT INTO y VALUES (41), (42), (43) RETURNING * ) SELECT * FROM t; SELECT * FROM y; DROP TRIGGER y_trig ON y; DROP FUNCTION y_trigger(); -- WITH attached to inherited UPDATE or DELETE CREATE TEMP TABLE parent ( id int, val text ); CREATE TEMP TABLE child1 ( ) INHERITS ( parent ); CREATE TEMP TABLE child2 ( ) INHERITS ( parent ); INSERT INTO parent VALUES ( 1, 'p1' ); INSERT INTO child1 VALUES ( 11, 'c11' ),( 12, 'c12' ); INSERT INTO child2 VALUES ( 23, 'c21' ),( 24, 'c22' ); WITH rcte AS ( SELECT sum(id) AS totalid FROM parent ) UPDATE parent SET id = id + totalid FROM rcte; SELECT * FROM parent; WITH wcte AS ( INSERT INTO child1 VALUES ( 42, 'new' ) RETURNING id AS newid ) UPDATE parent SET id = id + newid FROM wcte; SELECT * FROM parent; WITH rcte AS ( SELECT max(id) AS maxid FROM parent ) DELETE FROM parent USING rcte WHERE id = maxid; SELECT * FROM parent; WITH wcte AS ( INSERT INTO child2 VALUES ( 42, 'new2' ) RETURNING id AS newid ) DELETE FROM parent USING wcte WHERE id = newid; SELECT * FROM parent; -- check EXPLAIN VERBOSE for a wCTE with RETURNING EXPLAIN (VERBOSE, COSTS OFF) WITH wcte AS ( INSERT INTO int8_tbl VALUES ( 42, 47 ) RETURNING q2 ) DELETE FROM a USING wcte WHERE aa = q2; -- error cases -- data-modifying WITH tries to use its own output WITH RECURSIVE t AS ( INSERT INTO y SELECT * FROM t ) VALUES(FALSE); -- no RETURNING in a referenced data-modifying WITH WITH t AS ( INSERT INTO y VALUES(0) ) SELECT * FROM t; -- data-modifying WITH allowed only at the top level SELECT * FROM ( WITH t AS (UPDATE y SET a=a+1 RETURNING *) SELECT * FROM t ) ss; -- most variants of rules aren't allowed CREATE RULE y_rule AS ON INSERT TO y WHERE a=0 DO INSTEAD DELETE FROM y; WITH t AS ( INSERT INTO y VALUES(0) ) VALUES(FALSE); DROP RULE y_rule ON y; -- check that parser lookahead for WITH doesn't cause any odd behavior create table foo (with baz); -- fail, WITH is a reserved word create table foo (with ordinality); -- fail, WITH is a reserved word with ordinality as (select 1 as x) select * from ordinality; -- check sane response to attempt to modify CTE relation WITH test AS (SELECT 42) INSERT INTO test VALUES (1); -- check response to attempt to modify table with same name as a CTE (perhaps -- surprisingly it works, because CTEs don't hide tables from data-modifying -- statements) create temp table test (i int); with test as (select 42) insert into test select * from test; select * from test; drop table test; pgFormatter-4.2/t/pg-test-files/sql/write_parallel.sql000066400000000000000000000026071361326045100231330ustar00rootroot00000000000000-- -- PARALLEL -- -- Serializable isolation would disable parallel query, so explicitly use an -- arbitrary other level. begin isolation level repeatable read; -- encourage use of parallel plans set parallel_setup_cost=0; set parallel_tuple_cost=0; set min_parallel_table_scan_size=0; set max_parallel_workers_per_gather=4; -- -- Test write operations that has an underlying query that is eligble -- for parallel plans -- explain (costs off) create table parallel_write as select length(stringu1) from tenk1 group by length(stringu1); create table parallel_write as select length(stringu1) from tenk1 group by length(stringu1); drop table parallel_write; explain (costs off) select length(stringu1) into parallel_write from tenk1 group by length(stringu1); select length(stringu1) into parallel_write from tenk1 group by length(stringu1); drop table parallel_write; explain (costs off) create materialized view parallel_mat_view as select length(stringu1) from tenk1 group by length(stringu1); create materialized view parallel_mat_view as select length(stringu1) from tenk1 group by length(stringu1); drop materialized view parallel_mat_view; prepare prep_stmt as select length(stringu1) from tenk1 group by length(stringu1); explain (costs off) create table parallel_write as execute prep_stmt; create table parallel_write as execute prep_stmt; drop table parallel_write; rollback; pgFormatter-4.2/t/pg-test-files/sql/xml.sql000066400000000000000000000671321361326045100207310ustar00rootroot00000000000000CREATE TABLE xmltest ( id int, data xml ); INSERT INTO xmltest VALUES (1, 'one'); INSERT INTO xmltest VALUES (2, 'two'); INSERT INTO xmltest VALUES (3, '', NULL, ''); SELECT xmlconcat('', NULL, ''); SELECT xmlconcat(NULL); SELECT xmlconcat(NULL, NULL); SELECT xmlelement(name element, xmlattributes (1 as one, 'deuce' as two), 'content'); SELECT xmlelement(name element, xmlattributes ('unnamed and wrong')); SELECT xmlelement(name element, xmlelement(name nested, 'stuff')); SELECT xmlelement(name employee, xmlforest(name, age, salary as pay)) FROM emp; SELECT xmlelement(name duplicate, xmlattributes(1 as a, 2 as b, 3 as a)); SELECT xmlelement(name num, 37); SELECT xmlelement(name foo, text 'bar'); SELECT xmlelement(name foo, xml 'bar'); SELECT xmlelement(name foo, text 'br'); SELECT xmlelement(name foo, xml 'br'); SELECT xmlelement(name foo, array[1, 2, 3]); SET xmlbinary TO base64; SELECT xmlelement(name foo, bytea 'bar'); SET xmlbinary TO hex; SELECT xmlelement(name foo, bytea 'bar'); SELECT xmlelement(name foo, xmlattributes(true as bar)); SELECT xmlelement(name foo, xmlattributes('2009-04-09 00:24:37'::timestamp as bar)); SELECT xmlelement(name foo, xmlattributes('infinity'::timestamp as bar)); SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'br' as funnier)); SELECT xmlparse(content ''); SELECT xmlparse(content ' '); SELECT xmlparse(content 'abc'); SELECT xmlparse(content 'x'); SELECT xmlparse(content '&'); SELECT xmlparse(content '&idontexist;'); SELECT xmlparse(content ''); SELECT xmlparse(content ''); SELECT xmlparse(content '&idontexist;'); SELECT xmlparse(content ''); SELECT xmlparse(document ' '); SELECT xmlparse(document 'abc'); SELECT xmlparse(document 'x'); SELECT xmlparse(document '&'); SELECT xmlparse(document '&idontexist;'); SELECT xmlparse(document ''); SELECT xmlparse(document ''); SELECT xmlparse(document '&idontexist;'); SELECT xmlparse(document ''); SELECT xmlpi(name foo); SELECT xmlpi(name xml); SELECT xmlpi(name xmlstuff); SELECT xmlpi(name foo, 'bar'); SELECT xmlpi(name foo, 'in?>valid'); SELECT xmlpi(name foo, null); SELECT xmlpi(name xml, null); SELECT xmlpi(name xmlstuff, null); SELECT xmlpi(name "xml-stylesheet", 'href="mystyle.css" type="text/css"'); SELECT xmlpi(name foo, ' bar'); SELECT xmlroot(xml '', version no value, standalone no value); SELECT xmlroot(xml '', version '2.0'); SELECT xmlroot(xml '', version no value, standalone yes); SELECT xmlroot(xml '', version no value, standalone yes); SELECT xmlroot(xmlroot(xml '', version '1.0'), version '1.1', standalone no); SELECT xmlroot('', version no value, standalone no); SELECT xmlroot('', version no value, standalone no value); SELECT xmlroot('', version no value); SELECT xmlroot ( xmlelement ( name gazonk, xmlattributes ( 'val' AS name, 1 + 1 AS num ), xmlelement ( NAME qux, 'foo' ) ), version '1.0', standalone yes ); SELECT xmlserialize(content data as character varying(20)) FROM xmltest; SELECT xmlserialize(content 'good' as char(10)); SELECT xmlserialize(document 'bad' as text); SELECT xml 'bar' IS DOCUMENT; SELECT xml 'barfoo' IS DOCUMENT; SELECT xml '' IS NOT DOCUMENT; SELECT xml 'abc' IS NOT DOCUMENT; SELECT '<>' IS NOT DOCUMENT; SELECT xmlagg(data) FROM xmltest; SELECT xmlagg(data) FROM xmltest WHERE id > 10; SELECT xmlelement(name employees, xmlagg(xmlelement(name name, name))) FROM emp; -- Check mapping SQL identifier to XML name SELECT xmlpi(name ":::_xml_abc135.%-&_"); SELECT xmlpi(name "123"); PREPARE foo (xml) AS SELECT xmlconcat('', $1); SET XML OPTION DOCUMENT; EXECUTE foo (''); EXECUTE foo ('bad'); SELECT xml ''; SET XML OPTION CONTENT; EXECUTE foo (''); EXECUTE foo ('good'); SELECT xml ' '; SELECT xml ' '; SELECT xml ''; SELECT xml ' oops '; SELECT xml ' '; SELECT xml ''; -- Test backwards parsing CREATE VIEW xmlview1 AS SELECT xmlcomment('test'); CREATE VIEW xmlview2 AS SELECT xmlconcat('hello', 'you'); CREATE VIEW xmlview3 AS SELECT xmlelement(name element, xmlattributes (1 as ":one:", 'deuce' as two), 'content&'); CREATE VIEW xmlview4 AS SELECT xmlelement(name employee, xmlforest(name, age, salary as pay)) FROM emp; CREATE VIEW xmlview5 AS SELECT xmlparse(content 'x'); CREATE VIEW xmlview6 AS SELECT xmlpi(name foo, 'bar'); CREATE VIEW xmlview7 AS SELECT xmlroot(xml '', version no value, standalone yes); CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; -- Text XPath expressions evaluation SELECT xpath('/value', data) FROM xmltest; SELECT xpath(NULL, NULL) IS NULL FROM xmltest; SELECT xpath('', ''); SELECT xpath('//text()', 'number one'); SELECT xpath('//loc:piece/@id', 'number one', ARRAY[ARRAY['loc', 'http://127.0.0.1']]); SELECT xpath('//loc:piece', 'number one', ARRAY[ARRAY['loc', 'http://127.0.0.1']]); SELECT xpath('//loc:piece', 'number one', ARRAY[ARRAY['loc', 'http://127.0.0.1']]); SELECT xpath('//b', 'one two three etc'); SELECT xpath('//text()', '<'); SELECT xpath('//@value', ''); SELECT xpath('''<>''', ''); SELECT xpath('count(//*)', ''); SELECT xpath('count(//*)=0', ''); SELECT xpath('count(//*)=3', ''); SELECT xpath('name(/*)', ''); SELECT xpath('/nosuchtag', ''); SELECT xpath('root', ''); -- Round-trip non-ASCII data through xpath(). DO $$ DECLARE xml_declaration text := ''; degree_symbol text; res xml[]; BEGIN -- Per the documentation, except when the server encoding is UTF8, xpath() -- may not work on non-ASCII data. The untranslatable_character and -- undefined_function traps below, currently dead code, will become relevant -- if we remove this limitation. IF current_setting('server_encoding') <> 'UTF8' THEN RAISE LOG 'skip: encoding % unsupported for xpath', current_setting('server_encoding'); RETURN; END IF; degree_symbol := convert_from('\xc2b0', 'UTF8'); res := xpath('text()', (xml_declaration || '' || degree_symbol || '')::xml); IF degree_symbol <> res[1]::text THEN RAISE 'expected % (%), got % (%)', degree_symbol, convert_to(degree_symbol, 'UTF8'), res[1], convert_to(res[1]::text, 'UTF8'); END IF; EXCEPTION -- character with byte sequence 0xc2 0xb0 in encoding "UTF8" has no equivalent in encoding "LATIN8" WHEN untranslatable_character -- default conversion function for encoding "UTF8" to "MULE_INTERNAL" does not exist OR undefined_function -- unsupported XML feature OR feature_not_supported THEN RAISE LOG 'skip: %', SQLERRM; END $$; -- Test xmlexists and xpath_exists SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF 'Bidford-on-AvonCwmbranBristol'); SELECT xmlexists('//town[text() = ''Cwmbran'']' PASSING BY REF 'Bidford-on-AvonCwmbranBristol'); SELECT xmlexists('count(/nosuchtag)' PASSING BY REF ''); SELECT xpath_exists('//town[text() = ''Toronto'']','Bidford-on-AvonCwmbranBristol'::xml); SELECT xpath_exists('//town[text() = ''Cwmbran'']','Bidford-on-AvonCwmbranBristol'::xml); SELECT xpath_exists('count(/nosuchtag)', ''::xml); INSERT INTO xmltest VALUES (4, 'BudvarfreeCarlinglots'::xml); INSERT INTO xmltest VALUES (5, 'MolsonfreeCarlinglots'::xml); INSERT INTO xmltest VALUES (6, 'BudvarfreeCarlinglots'::xml); INSERT INTO xmltest VALUES (7, 'MolsonfreeCarlinglots'::xml); SELECT COUNT(id) FROM xmltest WHERE xmlexists('/menu/beer' PASSING data); SELECT COUNT(id) FROM xmltest WHERE xmlexists('/menu/beer' PASSING BY REF data BY REF); SELECT COUNT(id) FROM xmltest WHERE xmlexists('/menu/beers' PASSING BY REF data); SELECT COUNT(id) FROM xmltest WHERE xmlexists('/menu/beers/name[text() = ''Molson'']' PASSING BY REF data); SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/menu/beer',data); SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/menu/beers',data); SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/menu/beers/name[text() = ''Molson'']',data); SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/myns:menu/myns:beer',data,ARRAY[ARRAY['myns','http://myns.com']]); SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/myns:menu/myns:beers',data,ARRAY[ARRAY['myns','http://myns.com']]); SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/myns:menu/myns:beers/myns:name[text() = ''Molson'']',data,ARRAY[ARRAY['myns','http://myns.com']]); CREATE TABLE query ( expr TEXT ); INSERT INTO query VALUES ('/menu/beers/cost[text() = ''lots'']'); SELECT COUNT(id) FROM xmltest, query WHERE xmlexists(expr PASSING BY REF data); -- Test xml_is_well_formed and variants SELECT xml_is_well_formed_document('bar'); SELECT xml_is_well_formed_document('abc'); SELECT xml_is_well_formed_content('bar'); SELECT xml_is_well_formed_content('abc'); SET xmloption TO DOCUMENT; SELECT xml_is_well_formed('abc'); SELECT xml_is_well_formed('<>'); SELECT xml_is_well_formed(''); SELECT xml_is_well_formed('bar'); SELECT xml_is_well_formed('barbaz'); SELECT xml_is_well_formed('number one'); SELECT xml_is_well_formed('bar'); SELECT xml_is_well_formed('bar'); SELECT xml_is_well_formed('&'); SELECT xml_is_well_formed('&idontexist;'); SELECT xml_is_well_formed(''); SELECT xml_is_well_formed(''); SELECT xml_is_well_formed('&idontexist;'); SET xmloption TO CONTENT; SELECT xml_is_well_formed('abc'); -- Since xpath() deals with namespaces, it's a bit stricter about -- what's well-formed and what's not. If we don't obey these rules -- (i.e. ignore namespace-related errors from libxml), xpath() -- fails in subtle ways. The following would for example produce -- the xml value -- -- which is invalid because '<' may not appear un-escaped in -- attribute values. -- Since different libxml versions emit slightly different -- error messages, we suppress the DETAIL in this test. \set VERBOSITY terse SELECT xpath('/*', ''); \set VERBOSITY default -- Again, the XML isn't well-formed for namespace purposes SELECT xpath('/*', ''); -- XPath deprecates relative namespaces, but they're not supposed to -- throw an error, only a warning. SELECT xpath('/*', ''); -- External entity references should not leak filesystem information. SELECT XMLPARSE(DOCUMENT ']>&c;'); SELECT XMLPARSE(DOCUMENT ']>&c;'); -- This might or might not load the requested DTD, but it mustn't throw error. SELECT XMLPARSE(DOCUMENT ' '); -- XMLPATH tests CREATE TABLE xmldata(data xml); INSERT INTO xmldata VALUES(' AU Australia 3 CN China 3 HK HongKong 3 IN India 3 JP Japan 3Sinzo Abe SG Singapore 3791 '); -- XMLTABLE with columns SELECT xmltable.* FROM (SELECT data FROM xmldata) x, LATERAL XMLTABLE('/ROWS/ROW' PASSING data COLUMNS id int PATH '@id', _id FOR ORDINALITY, country_name text PATH 'COUNTRY_NAME/text()' NOT NULL, country_id text PATH 'COUNTRY_ID', region_id int PATH 'REGION_ID', size float PATH 'SIZE', unit text PATH 'SIZE/@unit', premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'); CREATE VIEW xmltableview1 AS SELECT xmltable.* FROM (SELECT data FROM xmldata) x, LATERAL XMLTABLE('/ROWS/ROW' PASSING data COLUMNS id int PATH '@id', _id FOR ORDINALITY, country_name text PATH 'COUNTRY_NAME/text()' NOT NULL, country_id text PATH 'COUNTRY_ID', region_id int PATH 'REGION_ID', size float PATH 'SIZE', unit text PATH 'SIZE/@unit', premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'); SELECT * FROM xmltableview1; \sv xmltableview1 EXPLAIN (COSTS OFF) SELECT * FROM xmltableview1; EXPLAIN (COSTS OFF, VERBOSE) SELECT * FROM xmltableview1; -- XMLNAMESPACES tests SELECT * FROM XMLTABLE(XMLNAMESPACES('http://x.y' AS zz), '/zz:rows/zz:row' PASSING '10' COLUMNS a int PATH 'zz:a'); CREATE VIEW xmltableview2 AS SELECT * FROM XMLTABLE(XMLNAMESPACES('http://x.y' AS zz), '/zz:rows/zz:row' PASSING '10' COLUMNS a int PATH 'zz:a'); SELECT * FROM xmltableview2; SELECT * FROM XMLTABLE(XMLNAMESPACES(DEFAULT 'http://x.y'), '/rows/row' PASSING '10' COLUMNS a int PATH 'a'); -- used in prepare statements PREPARE pp AS SELECT xmltable.* FROM (SELECT data FROM xmldata) x, LATERAL XMLTABLE('/ROWS/ROW' PASSING data COLUMNS id int PATH '@id', _id FOR ORDINALITY, country_name text PATH 'COUNTRY_NAME' NOT NULL, country_id text PATH 'COUNTRY_ID', region_id int PATH 'REGION_ID', size float PATH 'SIZE', unit text PATH 'SIZE/@unit', premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'); EXECUTE pp; SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS "COUNTRY_NAME" text, "REGION_ID" int); SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS id FOR ORDINALITY, "COUNTRY_NAME" text, "REGION_ID" int); SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS id int PATH '@id', "COUNTRY_NAME" text, "REGION_ID" int); SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS id int PATH '@id'); SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS id FOR ORDINALITY); SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS id int PATH '@id', "COUNTRY_NAME" text, "REGION_ID" int, rawdata xml PATH '.'); SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS id int PATH '@id', "COUNTRY_NAME" text, "REGION_ID" int, rawdata xml PATH './*'); SELECT * FROM xmltable('/root' passing 'a1aa2a bbbbxxxcccc' COLUMNS element text); SELECT * FROM xmltable('/root' passing 'a1aa2a bbbbxxxcccc' COLUMNS element text PATH 'element/text()'); -- should fail -- CDATA test select * from xmltable('d/r' passing ' &"<>!foo]]>2' columns c text); -- XML builtin entities SELECT * FROM xmltable('/x/a' PASSING ''"&<>' COLUMNS ent text); SELECT * FROM xmltable('/x/a' PASSING ''"&<>' COLUMNS ent xml); EXPLAIN (VERBOSE, COSTS OFF) SELECT xmltable.* FROM (SELECT data FROM xmldata) x, LATERAL XMLTABLE('/ROWS/ROW' PASSING data COLUMNS id int PATH '@id', _id FOR ORDINALITY, country_name text PATH 'COUNTRY_NAME' NOT NULL, country_id text PATH 'COUNTRY_ID', region_id int PATH 'REGION_ID', size float PATH 'SIZE', unit text PATH 'SIZE/@unit', premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'); -- test qual SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS "COUNTRY_NAME" text, "REGION_ID" int) WHERE "COUNTRY_NAME" = 'Japan'; EXPLAIN (VERBOSE, COSTS OFF) SELECT xmltable.* FROM xmldata, LATERAL xmltable('/ROWS/ROW[COUNTRY_NAME="Japan" or COUNTRY_NAME="India"]' PASSING data COLUMNS "COUNTRY_NAME" text, "REGION_ID" int) WHERE "COUNTRY_NAME" = 'Japan'; -- should to work with more data INSERT INTO xmldata VALUES(' CZ Czech Republic 2Milos Zeman DE Germany 2 FR France 2 '); INSERT INTO xmldata VALUES(' EG Egypt 1 SD Sudan 1 '); SELECT xmltable.* FROM (SELECT data FROM xmldata) x, LATERAL XMLTABLE('/ROWS/ROW' PASSING data COLUMNS id int PATH '@id', _id FOR ORDINALITY, country_name text PATH 'COUNTRY_NAME' NOT NULL, country_id text PATH 'COUNTRY_ID', region_id int PATH 'REGION_ID', size float PATH 'SIZE', unit text PATH 'SIZE/@unit', premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'); SELECT xmltable.* FROM (SELECT data FROM xmldata) x, LATERAL XMLTABLE('/ROWS/ROW' PASSING data COLUMNS id int PATH '@id', _id FOR ORDINALITY, country_name text PATH 'COUNTRY_NAME' NOT NULL, country_id text PATH 'COUNTRY_ID', region_id int PATH 'REGION_ID', size float PATH 'SIZE', unit text PATH 'SIZE/@unit', premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified') WHERE region_id = 2; EXPLAIN (VERBOSE, COSTS OFF) SELECT xmltable.* FROM (SELECT data FROM xmldata) x, LATERAL XMLTABLE('/ROWS/ROW' PASSING data COLUMNS id int PATH '@id', _id FOR ORDINALITY, country_name text PATH 'COUNTRY_NAME' NOT NULL, country_id text PATH 'COUNTRY_ID', region_id int PATH 'REGION_ID', size float PATH 'SIZE', unit text PATH 'SIZE/@unit', premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified') WHERE region_id = 2; -- should fail, NULL value SELECT xmltable.* FROM (SELECT data FROM xmldata) x, LATERAL XMLTABLE('/ROWS/ROW' PASSING data COLUMNS id int PATH '@id', _id FOR ORDINALITY, country_name text PATH 'COUNTRY_NAME' NOT NULL, country_id text PATH 'COUNTRY_ID', region_id int PATH 'REGION_ID', size float PATH 'SIZE' NOT NULL, unit text PATH 'SIZE/@unit', premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'); -- if all is ok, then result is empty -- one line xml test WITH x AS (SELECT proname, proowner, procost::numeric, pronargs, array_to_string(proargnames,',') as proargnames, case when proargtypes <> '' then array_to_string(proargtypes::oid[],',') end as proargtypes FROM pg_proc WHERE proname = 'f_leak'), y AS (SELECT xmlelement(name proc, xmlforest(proname, proowner, procost, pronargs, proargnames, proargtypes)) as proc FROM x), z AS (SELECT xmltable.* FROM y, LATERAL xmltable('/proc' PASSING proc COLUMNS proname name, proowner oid, procost float, pronargs int, proargnames text, proargtypes text)) SELECT * FROM z EXCEPT SELECT * FROM x; -- multi line xml test, result should be empty too WITH x AS (SELECT proname, proowner, procost::numeric, pronargs, array_to_string(proargnames,',') as proargnames, case when proargtypes <> '' then array_to_string(proargtypes::oid[],',') end as proargtypes FROM pg_proc), y AS (SELECT xmlelement(name data, xmlagg(xmlelement(name proc, xmlforest(proname, proowner, procost, pronargs, proargnames, proargtypes)))) as doc FROM x), z AS (SELECT xmltable.* FROM y, LATERAL xmltable('/data/proc' PASSING doc COLUMNS proname name, proowner oid, procost float, pronargs int, proargnames text, proargtypes text)) SELECT * FROM z EXCEPT SELECT * FROM x; CREATE TABLE xmltest2(x xml, _path text); INSERT INTO xmltest2 VALUES('1', 'A'); INSERT INTO xmltest2 VALUES('2', 'B'); INSERT INTO xmltest2 VALUES('3', 'C'); INSERT INTO xmltest2 VALUES('2', 'D'); SELECT xmltable.* FROM xmltest2, LATERAL xmltable('/d/r' PASSING x COLUMNS a int PATH '' || lower(_path) || 'c'); SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c') PASSING x COLUMNS a int PATH '.'); SELECT xmltable.* FROM xmltest2, LATERAL xmltable(('/d/r/' || lower(_path) || 'c') PASSING x COLUMNS a int PATH 'x' DEFAULT ascii(_path) - 54); -- XPath result can be boolean or number too SELECT * FROM XMLTABLE('*' PASSING 'a' COLUMNS a xml PATH '.', b text PATH '.', c text PATH '"hi"', d boolean PATH '. = "a"', e integer PATH 'string-length(.)'); \x SELECT * FROM XMLTABLE('*' PASSING 'pre&deeppost' COLUMNS x xml PATH 'node()', y xml PATH '/'); \x SELECT * FROM XMLTABLE('.' PASSING XMLELEMENT(NAME a) columns a varchar(20) PATH '""', b xml PATH '""'); pgFormatter-4.2/t/pg-test-files/sql/xmlmap.sql000066400000000000000000000055201361326045100214200ustar00rootroot00000000000000CREATE SCHEMA testxmlschema; CREATE TABLE testxmlschema.test1 (a int, b text); INSERT INTO testxmlschema.test1 VALUES (1, 'one'), (2, 'two'), (-1, null); CREATE DOMAIN testxmldomain AS varchar; CREATE TABLE testxmlschema.test2 (z int, y varchar(500), x char(6), w numeric(9,2), v smallint, u bigint, t real, s time, r timestamp, q date, p xml, o testxmldomain, n bool, m bytea, aaa text); ALTER TABLE testxmlschema.test2 DROP COLUMN aaa; INSERT INTO testxmlschema.test2 VALUES (55, 'abc', 'def', 98.6, 2, 999, 0, '21:07', '2009-06-08 21:07:30', '2009-06-08', NULL, 'ABC', true, 'XYZ'); SELECT table_to_xml('testxmlschema.test1', false, false, ''); SELECT table_to_xml('testxmlschema.test1', true, false, 'foo'); SELECT table_to_xml('testxmlschema.test1', false, true, ''); SELECT table_to_xml('testxmlschema.test1', true, true, ''); SELECT table_to_xml('testxmlschema.test2', false, false, ''); SELECT table_to_xmlschema('testxmlschema.test1', false, false, ''); SELECT table_to_xmlschema('testxmlschema.test1', true, false, ''); SELECT table_to_xmlschema('testxmlschema.test1', false, true, 'foo'); SELECT table_to_xmlschema('testxmlschema.test1', true, true, ''); SELECT table_to_xmlschema('testxmlschema.test2', false, false, ''); SELECT table_to_xml_and_xmlschema('testxmlschema.test1', false, false, ''); SELECT table_to_xml_and_xmlschema('testxmlschema.test1', true, false, ''); SELECT table_to_xml_and_xmlschema('testxmlschema.test1', false, true, ''); SELECT table_to_xml_and_xmlschema('testxmlschema.test1', true, true, 'foo'); SELECT query_to_xml('SELECT * FROM testxmlschema.test1', false, false, ''); SELECT query_to_xmlschema('SELECT * FROM testxmlschema.test1', false, false, ''); SELECT query_to_xml_and_xmlschema('SELECT * FROM testxmlschema.test1', true, true, ''); DECLARE xc CURSOR WITH HOLD FOR SELECT * FROM testxmlschema.test1 ORDER BY 1, 2; SELECT cursor_to_xml('xc'::refcursor, 5, false, true, ''); SELECT cursor_to_xmlschema('xc'::refcursor, false, true, ''); MOVE BACKWARD ALL IN xc; SELECT cursor_to_xml('xc'::refcursor, 5, true, false, ''); SELECT cursor_to_xmlschema('xc'::refcursor, true, false, ''); SELECT schema_to_xml('testxmlschema', false, true, ''); SELECT schema_to_xml('testxmlschema', true, false, ''); SELECT schema_to_xmlschema('testxmlschema', false, true, ''); SELECT schema_to_xmlschema('testxmlschema', true, false, ''); SELECT schema_to_xml_and_xmlschema('testxmlschema', true, true, 'foo'); -- test that domains are transformed like their base types CREATE DOMAIN testboolxmldomain AS bool; CREATE DOMAIN testdatexmldomain AS date; CREATE TABLE testxmlschema.test3 AS SELECT true c1, true::testboolxmldomain c2, '2013-02-21'::date c3, '2013-02-21'::testdatexmldomain c4; SELECT xmlforest(c1, c2, c3, c4) FROM testxmlschema.test3; SELECT table_to_xml('testxmlschema.test3', true, true, ''); pgFormatter-4.2/t/regress_test.pl000066400000000000000000000034061361326045100171660ustar00rootroot00000000000000my @files = `find t/test-files/ -maxdepth 1 -name '*.sql' | sort`; chomp(@files); my $pg_format = $ENV{PG_FORMAT} // './pg_format'; # set to 'pg_format' to test installed binary in /usr/bin my $exit = 0; foreach my $f (@files) { next if ( $#ARGV >= 0 and lc($ARGV[0]) ne 'update' and !grep(m#^$f$#, @ARGV) ); print "Running test on file $f...\n"; my $opt = ''; $opt = "-S '\$f\$'" if ($f =~ m#/ex19.sql$#); $opt = "-W 4" if ($f =~ m#/ex46.sql$#); #$opt .= ' -t' if (grep(/^-t/, @ARGV) or $f =~ /float4\.sql/); $opt .= ' -t' if (grep(/^-t/, @ARGV)); $opt = "-T -n " if ($f =~ m#/ex51.sql$#); my $cmd = "./pg_format $opt -u 2 $f >/tmp/output.sql"; `$cmd`; $f =~ s/test-files\//test-files\/expected\//; if (lc($ARGV[0]) eq 'update') { `cp -f /tmp/output.sql $f`; } else { my @diff = `diff -u /tmp/output.sql $f | grep "^[+-]" | grep -v "^[+-]\t\$" | grep -v "^[+-][+-][+-]"`; if ($#diff < 0) { print "\ttest ok.\n"; } else { print "\ttest failed!!!\n"; print @diff; $exit = 1; } } unlink("/tmp/output.sql"); } @files = `find t/pg-test-files/sql/ -maxdepth 1 -name '*.sql' | sort`; chomp(@files); foreach my $f (@files) { next if ( $#ARGV >= 0 and lc($ARGV[0]) ne 'update' and !grep(m#^$f$#, @ARGV) ); print "Running test on file $f...\n"; my $opt = ''; $opt .= ' -t' if (grep(/^-t/, @ARGV)); my $cmd = "./pg_format $opt -u 2 $f >/tmp/output.sql"; `$cmd`; $f =~ s/\/sql\//\/expected\//; if (lc($ARGV[0]) eq 'update') { `cp -f /tmp/output.sql $f`; } else { my @diff = `diff -u /tmp/output.sql $f | grep "^[+-]" | grep -v "^[+-]\$" | grep -v "^[+-]\t\$" | grep -v "^[+-][+-][+-]"`; if ($#diff < 0) { print "\ttest ok.\n"; } else { print "\ttest failed!!!\n"; print @diff; $exit = 1; } } unlink("/tmp/output.sql"); } exit $exit; pgFormatter-4.2/t/test-files/000077500000000000000000000000001361326045100161745ustar00rootroot00000000000000pgFormatter-4.2/t/test-files/ex0.sql000066400000000000000000000004261361326045100174130ustar00rootroot00000000000000select a,b,c from tablea join tableb on ( tablea.a=tableb.a) join tablec on ( tablec.a=tableb.a) left outer join tabled on ( tabled.a=tableb.a) left join tablee on ( tabled.a=tableb.a) where tablea.x = 1 and tableb.y=1 group by tablea.a, tablec.c order by tablea.a, tablec.c; pgFormatter-4.2/t/test-files/ex1.sql000066400000000000000000000127371361326045100174240ustar00rootroot00000000000000------------------------------------------ -- This is an example SQL -- Please click the button -- or "ctrl+F" ------------------------------------------ SELECT price.col1 AS col1, price.col2 AS col2 , price.col3 AS col3, max(price.col4) AS col4, max(price.col5) AS col5, max(price.col6) AS col6, max(price.col7) AS col7 FROM table_1 t1, table_2 t2 WHERE col1 = col2 AND column_1 = small_column AND column_3411 <= column_12_sup and col1 = 'Test Run' AND column_4532 = c1.dert UNION SELECT price.col1 AS col1, price.col2 AS col2 , price.col3 AS col3, max(price.col4) AS col4, max(price.col5) AS col5, max(price.col6) AS col6, /******************* * This is a block * * comment within a * * SQL statement * *******************/ max(price.col7) AS col7 FROM (SELECT store.column1, cast (store.column2 AS integer) AS column2, -- inline comment store.columnwe34r3 AS column3, -- inline comment store.column4_prod AS column4, -- inline comment store.column5_pre_prod_first AS column5 , -- inline comment substr(store.column6,11,1) AS column6, -- inline comment store.column7 AS column7 -- inline comment FROM (SELECT library.column1, --------------------- -- This is a line -- -- comment in a -- -- SQL statement -- --------------------- library.column2, library.column3 -- inline comment , CASE library.column4 WHEN cheap THEN digits(library.column27) concat library.column28 ELSE 123456 END AS column4, CASE library.column5 WHEN expensive THEN digits(library.column27) concat library.column28 ELSE 123456 END AS library.column6, CASE column7 WHEN free THEN digits(library.column27) concat library.column28 ELSE 123456 END AS column7, FROM (SELECT integer(substr(onelibrarysales.column1,11,10)) AS column1, substr(onelibrarysales.column2,21,10) AS column2 , onelibrarysales.column3, onelibrarysales.column4, substr(onelibrarysales.column5,31,6) AS column5, substr(onelibrarysales.column6,37,2) AS column6, substr(onelibrarysales.column7,39,6) AS column7, FROM (SELECT alllibrarysales.column1, alllibrarysales.column2, max(alllibrarysales.column3) AS alllibrarysales.column3 , max(char(alllibrarysales.column4,iso) concat char(alllibrarysales.column5,iso) concat digits(alllibrarysales.column6) concat (alllibrarysales.column7)) AS column5 FROM /******************* * This is a block * * comment within a * * SQL statement * *******************/ (SELECT libraryprod.column1, libraryprod.column2, libraryprod.column3, libraryprod.column4, /******************* * This is a block * * comment within a * * SQL statement * *******************/ libraryprod.column5, libraryprod.column6, libraryprod.column7 FROM (SELECT tv.column1, tv.column2, max(digits(tv.column3) concat digits(tv.column4) ) AS librarymax FROM db1.v_table1 tv WHERE tv.column1 <> 'Y' AND tv.column1 in ( 'a' , '1' , '12' , '123' , ' 1234' , '12345' , '123456' , '1234567' , '12345678' , '123456789' , '1234567890' , '1 12 123 1234 12345 123456 1234567 12345678' , 'b' , 'c' ) AND tv.column2 >= date(tv.column4) AND tv.column3 < date(tv.column15) GROUP BY tv.column1, tv.column2 ) AS libraryprod, db1.table2 th WHERE th.column1 = libraryprod.column1 AND th.column2 = libraryprod.column2 ) AS alllibrarysales GROUP BY alllibrarysales.column1, alllibrarysales.column2 ) AS onelibrarysales ) AS library LEFT OUTER JOIN db1.v_table3 librarystat ON librarystat.column1 = library.column1 AND librarystat.column2 = library.column2 OR ( librarystat.column4 = library.column4 AND librarystat.column5 = library.column5 ) /******************* * This is a block * * comment within a * * SQL statement * *******************/ AND ( librarystat.column5 = 'I' OR librarystat.column4 = 'Gold' OR librarystat.column5 = 'Bold' ) AND librarystat.column6 <= 'Z74' ) AS x ) AS price WHERE price.column1 < 'R45' OR ( price.column2= 'R46' /******************* * This is a block * * comment within a * * SQL statement * *******************/ AND price.column3 = 6 ) GROUP BY price.column1, price.column2, /******************* * This is a block * * comment within a * * SQL statement * *******************/ price.column3, price.column4, price.column5, price.column6, price.column7 ; pgFormatter-4.2/t/test-files/ex10.sql000066400000000000000000000002201361326045100174640ustar00rootroot00000000000000with a as ( select x, y, z from twelve join nine on a = 2 and b = a ), b as ( select * from a ) select * from b; pgFormatter-4.2/t/test-files/ex11.sql000066400000000000000000000001441361326045100174720ustar00rootroot00000000000000SELECT * FROM a, ONLY (c) JOIN b USING (id, id2) LEFT JOIN d USING (id) WHERE id > 10 AND id <= 20; pgFormatter-4.2/t/test-files/ex12.sql000066400000000000000000000006531361326045100175000ustar00rootroot00000000000000SET client_encoding TO 'UTF8'; UPDATE weather SET temp_lo = temp_lo+1, temp_hi = temp_lo+15, prcp = DEFAULT WHERE city = 'San Francisco' AND date = '2003-07-03' RETURNING temp_lo,temp_hi,prcp; \set ON_ERROR_STOP ON SELECT * FROM (SELECT * FROM mytable FOR UPDATE) AS ss WHERE col1 = 5; BEGIN; SELECT * FROM mytable WHERE key = 1 FOR NO KEY UPDATE; SAVEPOINT s; UPDATE mytable SET col1=NULL WHERE key = 1; ROLLBACK TO s; pgFormatter-4.2/t/test-files/ex13.sql000066400000000000000000000016171361326045100175020ustar00rootroot00000000000000WITH RECURSIVE employee_recursive (distance, employee_name, manager_name) AS ( SELECT 1, employee_name, manager_name FROM employee WHERE manager_name = 'Mary' UNION ALL SELECT er.distance + 1, e.employee_name, e.manager_name FROM employee_recursive er, employee e WHERE er.employee_name = e.manager_name ) SELECT distance, employee_name FROM employee_recursive; WITH RECURSIVE t(nombre) AS ( VALUES (2) UNION ALL SELECT 2 * nombre FROM t WHERE 2 * nombre < 100 ) SELECT nombre FROM t; CREATE FUNCTION tg_phone_bu () RETURNS TRIGGER AS $$ BEGIN IF new.slotname != old.slotname THEN DELETE FROM PHone WHERE slotname = old.slotname; INSERT INTO PHone (slotname, comment, slotlink) VALUES (new.slotname, new.comment, new.slotlink); RETURN NULL; END IF; RETURN new; END; $$ LANGUAGE plpgsql; pgFormatter-4.2/t/test-files/ex14.sql000066400000000000000000000010121361326045100174700ustar00rootroot00000000000000SELECT regexp_matches('foobarbequebazilbarfbonk', '(b[^b]+)(b[^b]+)', 'g'); SELECT SUBSTRING('XY1234Z', 'Y*([0-9]{1,3})'); SELECT m.name AS mname, pname FROM manufacturers m, LATERAL get_product_names(m.id) pname; SELECT m.name AS mname, pname FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true; with one as (select 1 one) select count(one),avg(one) from one; SELECT * FROM a FULL JOIN b USING (c); SELECT * FROM a FULL OUTER JOIN b USING (c); CREATE TYPE jwt_token AS (token TEXT, field: TEXT); pgFormatter-4.2/t/test-files/ex15.sql000066400000000000000000000016111361326045100174760ustar00rootroot00000000000000SELECT user_id, view_homepage, view_homepage_time, enter_credit_card, enter_credit_card_time FROM ( -- Get the first time each user viewed the homepage. SELECT user_id, 1 AS view_homepage, min (time) AS view_homepage_time FROM event WHERE data ->> 'type' = 'view_homepage' GROUP BY user_id) e1 LEFT JOIN LATERAL ( -- For each row, get the first time the user_id did the enter_credit_card -- event, if one exists within two weeks of view_homepage_time. SELECT 1 AS enter_credit_card, time AS enter_credit_card_time FROM event WHERE user_id = e1.user_id AND data ->> 'type' = 'enter_credit_card' AND time BETWEEN view_homepage_time AND (view_homepage_time + 1000 * 60 * 60 * 24 * 14) ORDER BY time LIMIT 1) e2 ON TRUE; pgFormatter-4.2/t/test-files/ex16.sql000066400000000000000000000035561361326045100175110ustar00rootroot00000000000000SET client_encoding TO 'UTF8'; \set ON_ERROR_STOP ON CREATE TABLE new_table_test ( start_date timestamp NOT NULL, end_date timestamp NOT NULL, name varchar(40) NOT NULL CHECK (name <> ''), fk_organization_unit_id numeric(20), fk_product_id numeric(20), id numeric(20) NOT NULL PRIMARY KEY, migrated varchar(1) ) ; COMMENT ON TABLE new_table_test IS E'Associação dos produtos as Unidades de Estrutura responsável'; COMMENT ON COLUMN new_table_test.end_date IS E'Data fim da associação da unidade. '; COMMENT ON COLUMN new_table_test.migrated IS E'Indica se o registro foi migrado. Valores possíveis: S - Sim, N - Não.'; CREATE INDEX ni_ansu_3 ON new_table_test (fk_product_id, start_date); CREATE INDEX ni_ansu_2 ON new_table_test (fk_organization_unit_id asc, fk_product_id desc); CREATE INDEX ni_ansu_1 ON new_table_test (fk_product_id asc, start_date asc); ALTER TABLE new_table_test ADD CONSTRAINT ora2pg_ckey_fk_organization_unit_id CHECK (fk_organization_unit_id is not NULL); ALTER TABLE new_table_test ADD CONSTRAINT ck_ansu_fk_org_unit_id CHECK (fk_organization_unit_id is not NULL AND fk_organization_unit_id > 1000); ALTER TABLE new_table_test ADD CONSTRAINT fk_ansu_produ_id FOREIGN KEY (FK_PRODUCT_ID) REFERENCES PRODUCT (ID); CREATE TABLE test_uuid ( nom varchar(25), uid_col bytea NOT NULL DEFAULT uuid_generate_v4() ) ; CREATE TABLE test_boolean ( id bigint, is_deleted boolean, is_updated boolean ) ; ALTER TABLE test_boolean ADD PRIMARY KEY (id,is_deleted,is_updated); CREATE TABLE table_0 ( ref_1 integer REFERENCES table_1 ON DELETE RESTRICT, ref_2 integer REFERENCES table_2 ON DELETE CASCADE, ref_3 integer REFERENCES table_3 ON DELETE SET NULL ); COMMENT ON TABLE foo.bar IS $comment$ This is a nicely formatted comment line that spans over multiple lines that should remain untouched $comment$; COMMENT ON TABLE foo2 IS 'Hello world'; pgFormatter-4.2/t/test-files/ex17.sql000066400000000000000000000417441361326045100175130ustar00rootroot00000000000000-- -- PostgreSQL database dump -- -- Dumped from database version 9.6.2 -- Dumped by pg_dump version 9.6.2 SET statement_timeout = 0; SET lock_timeout = 0; SET idle_in_transaction_session_timeout = 0; SET client_encoding = 'UTF8'; SET standard_conforming_strings = on; SET check_function_bodies = false; SET client_min_messages = warning; SET row_security = off; -- -- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: -- CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; -- -- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: -- COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; SET search_path = public, pg_catalog; -- -- Name: add(integer, integer); Type: FUNCTION; Schema: public; Owner: gilles -- CREATE FUNCTION add(integer, integer) RETURNS integer LANGUAGE sql IMMUTABLE STRICT AS $_$select $1 + $2;$_$; ALTER FUNCTION public.add(integer, integer) OWNER TO gilles; -- -- Name: check_password(text, text, text, text, text, text, text, text, text, text, text, text, text, text, text, text, text, text); Type: FUNCTION; Schema: public; Owner: gilles -- CREATE FUNCTION check_password(uname1 text, pass1 text, uname2 text, pass2 text, uname3 text, pass3 text, uname4 text, pass4 text, uname5 text, pass5 text, uname6 text, pass6 text, uname7 text, pass7 text, uname8 text, pass8 text, uname9 text, pass9 text) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER SET search_path TO admin, pg_temp AS $_$ DECLARE passed BOOLEAN; BEGIN SELECT (pwd = $2) INTO passed FROM pwds WHERE username = $1; RETURN passed; END; $_$; ALTER FUNCTION public.check_password(uname1 text, pass1 text, uname2 text, pass2 text, uname3 text, pass3 text, uname4 text, pass4 text, uname5 text, pass5 text, uname6 text, pass6 text, uname7 text, pass7 text, uname8 text, pass8 text, uname9 text, pass9 text) OWNER TO gilles; -- -- Name: dup(integer); Type: FUNCTION; Schema: public; Owner: gilles -- CREATE FUNCTION dup(integer, OUT f1 integer, OUT f2 text) RETURNS record LANGUAGE sql AS $_$ SELECT $1, CAST($1 AS text) || ' is text' $_$; ALTER FUNCTION public.dup(integer, OUT f1 integer, OUT f2 text) OWNER TO gilles; -- -- Name: increment(integer); Type: FUNCTION; Schema: public; Owner: gilles -- CREATE FUNCTION increment(i integer) RETURNS integer LANGUAGE plpgsql AS $$ BEGIN RETURN i + 1; END; $$; ALTER FUNCTION public.increment(i integer) OWNER TO gilles; -- -- Name: peuple_stock(integer, integer); Type: FUNCTION; Schema: public; Owner: userdb -- CREATE FUNCTION peuple_stock(annee_debut integer, annee_fin integer) RETURNS bigint LANGUAGE plpgsql AS $$ DECLARE v_annee integer; v_nombre integer; v_contenant_id integer; v_vin_id integer; compteur bigint :=0; annees integer; contenants integer; vins integer; tuples_a_generer integer; BEGIN -- vider la table de stock truncate table stock; -- calculer le nombre d'annees select (annee_fin-annee_debut)+1 into annees; -- nombre de contenants select count(*) from contenant into contenants; -- nombre de vins select count(*) from vin into vins; -- calcul des combinaisons select annees*contenants*vins into tuples_a_generer; --on boucle sur tous les millesimes: disons 1930 a 2000 -- soit 80 annees for v_annee in annee_debut .. annee_fin loop -- on boucle sur les contenants possibles for v_contenant_id in 1 .. contenants loop -- idem pour l'id du vin for v_vin_id in 1 .. vins loop -- on prends un nombre de bouteilles compris entre 6 et 18 select round(random()*12)+6 into v_nombre; -- insertion dans la table de stock insert into stock (vin_id, contenant_id, annee, nombre) values (v_vin_id, v_contenant_id, v_annee, v_nombre); if (((compteur%1000)=0) or (compteur=tuples_a_generer)) then raise notice 'stock : % sur % tuples generes', compteur, tuples_a_generer; end if; compteur := compteur + 1; end loop; --fin boucle vin end loop; -- fin boucle contenant end loop; --fin boucle annee RETURN compteur; END; $$; ALTER FUNCTION public.peuple_stock(annee_debut integer, annee_fin integer) OWNER TO userdb; -- -- Name: peuple_vin(); Type: FUNCTION; Schema: public; Owner: userdb -- CREATE FUNCTION peuple_vin() RETURNS bigint LANGUAGE plpgsql AS $$ DECLARE v_recoltant_id integer; v_appellation_id integer; v_type_vin_id integer; recoltants integer; appellations integer; types_vins integer; tuples_a_generer integer; compteur bigint :=0; BEGIN -- vider la table de stock, qui depend de vin, puis vin delete from stock; delete from vin; -- compter le nombre de recoltants select count(*) from recoltant into recoltants; -- compter le nombre d'appellations select count(*) from appellation into appellations; -- compter le nombre de types de vins select count(*) from type_vin into types_vins; -- calculer le nombre de combinaisons possibles select (recoltants*appellations*types_vins) into tuples_a_generer; --on boucle sur tous les recoltants for v_recoltant_id in 1 .. recoltants loop -- on boucle sur les appelations for v_appellation_id in 1 .. appellations loop -- on boucle sur les types de vins for v_type_vin_id in 1 .. types_vins loop -- insertion dans la table de vin insert into vin (recoltant_id, appellation_id, type_vin_id) values (v_recoltant_id, v_appellation_id, v_type_vin_id); if (((compteur%1000)=0) or (compteur=tuples_a_generer)) then raise notice 'vins : % sur % tuples generes', compteur, tuples_a_generer; end if; compteur := compteur + 1; end loop; --fin boucle type vin end loop; -- fin boucle appellations end loop; --fin boucle recoltants RETURN compteur; END; $$; ALTER FUNCTION public.peuple_vin() OWNER TO userdb; -- -- Name: trous_stock(); Type: FUNCTION; Schema: public; Owner: userdb -- CREATE FUNCTION trous_stock() RETURNS bigint LANGUAGE plpgsql AS $$ DECLARE stock_total integer; echantillon integer; vins_disponibles integer; contenants_disponibles integer; v_vin_id integer; v_contenant_id integer; v_tuples bigint := 0; annee_min integer; annee_max integer; v_annee integer; BEGIN -- on compte le nombre de tuples dans stock select count(*) from stock into stock_total; raise NOTICE 'taille du stock %', stock_total; -- on calcule la taille de l'echantillon a -- supprimer de la table stock select round(stock_total/10) into echantillon; raise NOTICE 'taille de l''echantillon %', echantillon; -- on compte le nombre de vins disponibles select count(*) from vin into vins_disponibles; raise NOTICE '% vins disponibles', vins_disponibles; -- on compte le nombre de contenants disponibles select count(*) from contenant into contenants_disponibles; raise NOTICE '% contenants disponibles', contenants_disponibles; -- on recupere les bornes min/max de annees select min(annee), max(annee) from stock into annee_min, annee_max; -- on fait une boucle correspondant a 1% des tuples -- de la table stock for v_tuples in 1 .. echantillon loop -- selection d'identifiant, au hasard --select round(random()*contenants_disponibles) into v_contenant_id; v_contenant_id := round(random()*contenants_disponibles); --select round(random()*vins_disponibles) into v_vin_id; v_vin_id := round(random()*vins_disponibles); v_annee := round(random()*(annee_max-annee_min))+(annee_min); -- si le tuple est deja efface, ce n'est pas grave.. delete from stock where contenant_id = v_contenant_id and vin_id = v_vin_id and annee = v_annee; if (((v_tuples%100)=0) or (v_tuples=echantillon)) then raise notice 'stock : % sur % echantillon effaces',v_tuples, echantillon; end if; end loop; --fin boucle v_tuples RETURN echantillon; END; $$; ALTER FUNCTION public.trous_stock() OWNER TO userdb; -- -- Name: trous_vin(); Type: FUNCTION; Schema: public; Owner: userdb -- CREATE FUNCTION trous_vin() RETURNS bigint LANGUAGE plpgsql AS $$ DECLARE vin_total integer; echantillon integer; v_vin_id integer; v_tuples bigint := 0; v_annee integer; BEGIN -- on compte le nombre de tuples dans vin select count(*) from vin into vin_total; raise NOTICE '% vins disponibles', vin_total; -- on calcule la taille de l'echantillon a -- supprimer de la table vin select round(vin_total/10) into echantillon; raise NOTICE 'taille de l''echantillon %', echantillon; -- on fait une boucle correspondant a 10% des tuples -- de la table vin for v_tuples in 1 .. echantillon loop -- selection d'identifiant, au hasard v_vin_id := round(random()*vin_total); -- si le tuple est deja efface, ce n'est pas grave.. -- TODO remplacer ce delete par un trigger on delete cascade -- voir dans druid le schema??? delete from stock where vin_id = v_vin_id; delete from vin where id = v_vin_id; if (((v_tuples%100)=0) or (v_tuples=echantillon)) then raise notice 'vin : % sur % echantillon effaces',v_tuples, echantillon; end if; end loop; --fin boucle v_tuples RETURN echantillon; END; $$; ALTER FUNCTION public.trous_vin() OWNER TO userdb; SET default_tablespace = ''; SET default_with_oids = false; -- -- Name: appellation; Type: TABLE; Schema: public; Owner: userdb -- CREATE TABLE appellation ( id integer NOT NULL, libelle text NOT NULL, region_id integer ) WITH (autovacuum_enabled=off); ALTER TABLE appellation OWNER TO userdb; -- -- Name: appellation_id_seq; Type: SEQUENCE; Schema: public; Owner: userdb -- CREATE SEQUENCE appellation_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE appellation_id_seq OWNER TO userdb; -- -- Name: appellation_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: userdb -- ALTER SEQUENCE appellation_id_seq OWNED BY appellation.id; -- -- Name: contenant; Type: TABLE; Schema: public; Owner: userdb -- CREATE TABLE contenant ( id integer NOT NULL, contenance real NOT NULL, libelle text ) WITH (autovacuum_enabled=off, fillfactor='20'); ALTER TABLE contenant OWNER TO userdb; -- -- Name: contenant_id_seq; Type: SEQUENCE; Schema: public; Owner: userdb -- CREATE SEQUENCE contenant_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE contenant_id_seq OWNER TO userdb; -- -- Name: contenant_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: userdb -- ALTER SEQUENCE contenant_id_seq OWNED BY contenant.id; -- -- Name: recoltant; Type: TABLE; Schema: public; Owner: userdb -- CREATE TABLE recoltant ( id integer NOT NULL, nom text, adresse text ) WITH (autovacuum_enabled=off); ALTER TABLE recoltant OWNER TO userdb; -- -- Name: recoltant_id_seq; Type: SEQUENCE; Schema: public; Owner: userdb -- CREATE SEQUENCE recoltant_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE recoltant_id_seq OWNER TO userdb; -- -- Name: recoltant_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: userdb -- ALTER SEQUENCE recoltant_id_seq OWNED BY recoltant.id; -- -- Name: region; Type: TABLE; Schema: public; Owner: userdb -- CREATE TABLE region ( id integer NOT NULL, libelle text NOT NULL ) WITH (autovacuum_enabled=off); ALTER TABLE region OWNER TO userdb; -- -- Name: region_id_seq; Type: SEQUENCE; Schema: public; Owner: userdb -- CREATE SEQUENCE region_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE region_id_seq OWNER TO userdb; -- -- Name: region_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: userdb -- ALTER SEQUENCE region_id_seq OWNED BY region.id; -- -- Name: stock; Type: TABLE; Schema: public; Owner: userdb -- CREATE TABLE stock ( vin_id integer NOT NULL, contenant_id integer NOT NULL, annee integer NOT NULL, nombre integer NOT NULL ) WITH (autovacuum_enabled=off); ALTER TABLE stock OWNER TO userdb; -- -- Name: type_vin; Type: TABLE; Schema: public; Owner: userdb -- CREATE TABLE type_vin ( id integer NOT NULL, libelle text NOT NULL ) WITH (autovacuum_enabled=off); ALTER TABLE type_vin OWNER TO userdb; -- -- Name: type_vin_id_seq; Type: SEQUENCE; Schema: public; Owner: userdb -- CREATE SEQUENCE type_vin_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE type_vin_id_seq OWNER TO userdb; -- -- Name: type_vin_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: userdb -- ALTER SEQUENCE type_vin_id_seq OWNED BY type_vin.id; -- -- Name: vin; Type: TABLE; Schema: public; Owner: userdb -- CREATE TABLE vin ( id integer NOT NULL, recoltant_id integer, appellation_id integer NOT NULL, type_vin_id integer NOT NULL ) WITH (autovacuum_enabled=off); ALTER TABLE vin OWNER TO userdb; -- -- Name: vin_id_seq; Type: SEQUENCE; Schema: public; Owner: userdb -- CREATE SEQUENCE vin_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE vin_id_seq OWNER TO userdb; -- -- Name: vin_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: userdb -- ALTER SEQUENCE vin_id_seq OWNED BY vin.id; -- -- Name: appellation id; Type: DEFAULT; Schema: public; Owner: userdb -- ALTER TABLE ONLY appellation ALTER COLUMN id SET DEFAULT nextval('appellation_id_seq'::regclass); -- -- Name: contenant id; Type: DEFAULT; Schema: public; Owner: userdb -- ALTER TABLE ONLY contenant ALTER COLUMN id SET DEFAULT nextval('contenant_id_seq'::regclass); -- -- Name: recoltant id; Type: DEFAULT; Schema: public; Owner: userdb -- ALTER TABLE ONLY recoltant ALTER COLUMN id SET DEFAULT nextval('recoltant_id_seq'::regclass); -- -- Name: region id; Type: DEFAULT; Schema: public; Owner: userdb -- ALTER TABLE ONLY region ALTER COLUMN id SET DEFAULT nextval('region_id_seq'::regclass); -- -- Name: type_vin id; Type: DEFAULT; Schema: public; Owner: userdb -- ALTER TABLE ONLY type_vin ALTER COLUMN id SET DEFAULT nextval('type_vin_id_seq'::regclass); -- -- Name: vin id; Type: DEFAULT; Schema: public; Owner: userdb -- ALTER TABLE ONLY vin ALTER COLUMN id SET DEFAULT nextval('vin_id_seq'::regclass); -- -- Name: appellation appellation_libelle_key; Type: CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY appellation ADD CONSTRAINT appellation_libelle_key UNIQUE (libelle); -- -- Name: appellation appellation_pkey; Type: CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY appellation ADD CONSTRAINT appellation_pkey PRIMARY KEY (id); -- -- Name: contenant contenant_pkey; Type: CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY contenant ADD CONSTRAINT contenant_pkey PRIMARY KEY (id); -- -- Name: recoltant recoltant_pkey; Type: CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY recoltant ADD CONSTRAINT recoltant_pkey PRIMARY KEY (id); -- -- Name: region region_pkey; Type: CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY region ADD CONSTRAINT region_pkey PRIMARY KEY (id); -- -- Name: stock stock_pkey; Type: CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY stock ADD CONSTRAINT stock_pkey PRIMARY KEY (vin_id, contenant_id, annee); -- -- Name: type_vin type_vin_pkey; Type: CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY type_vin ADD CONSTRAINT type_vin_pkey PRIMARY KEY (id); -- -- Name: vin vin_pkey; Type: CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY vin ADD CONSTRAINT vin_pkey PRIMARY KEY (id); -- -- Name: appellation appellation_region_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY appellation ADD CONSTRAINT appellation_region_id_fkey FOREIGN KEY (region_id) REFERENCES region(id) ON DELETE CASCADE; -- -- Name: stock stock_contenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY stock ADD CONSTRAINT stock_contenant_id_fkey FOREIGN KEY (contenant_id) REFERENCES contenant(id) ON DELETE CASCADE; -- -- Name: stock stock_vin_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY stock ADD CONSTRAINT stock_vin_id_fkey FOREIGN KEY (vin_id) REFERENCES vin(id) ON DELETE CASCADE; -- -- Name: vin vin_appellation_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY vin ADD CONSTRAINT vin_appellation_id_fkey FOREIGN KEY (appellation_id) REFERENCES appellation(id) ON DELETE CASCADE; -- -- Name: vin vin_recoltant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY vin ADD CONSTRAINT vin_recoltant_id_fkey FOREIGN KEY (recoltant_id) REFERENCES recoltant(id) ON DELETE CASCADE; -- -- Name: vin vin_type_vin_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY vin ADD CONSTRAINT vin_type_vin_id_fkey FOREIGN KEY (type_vin_id) REFERENCES type_vin(id) ON DELETE CASCADE; -- -- PostgreSQL database dump complete -- pgFormatter-4.2/t/test-files/ex18.sql000066400000000000000000000005411361326045100175020ustar00rootroot00000000000000GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA foo TO role_bar, role_baz; SELECT * FROM t WHERE a IS NOT DISTINCT FROM b; -- Deploy schemas/custom/grants/grant_schema_to_authenticated to pg -- requires: schemas/custom/schema BEGIN; GRANT USAGE ON SCHEMA custom TO authenticated; GRANT USAGE ON SCHEMA custom TO authenticated; COMMIT; pgFormatter-4.2/t/test-files/ex19.sql000066400000000000000000000064621361326045100175130ustar00rootroot00000000000000create or replace function toastcheck_writer(text) returns void language plpgsql as $ff$ declare func text; funcname text; attrecord record; pkc record; indent text; colrec record; pkcols text; pkformat text; detoast_funcname text; pk_col_ary text[]; begin pkcols = ''; pkformat = ''; pk_col_ary = '{}'; funcname = 'toastcheck__' || $1; FOR pkc IN EXECUTE $f$ SELECT attname FROM pg_attribute JOIN pg_class ON (oid = attrelid) JOIN pg_index on (pg_class.oid = pg_index.indrelid and attnum = any (indkey)) WHERE pg_class.oid = '$f$ || $1 || $f$ '::regclass and indisprimary $f$ LOOP IF pkcols = '' THEN pkcols = quote_ident(pkc.attname); pkformat = '%'; ELSE pkcols = pkcols || ', ' || quote_ident(pkc.attname); pkformat = pkformat || ', %'; END IF; pk_col_ary = array_append(pk_col_ary, quote_ident(pkc.attname)); END LOOP; /* * This is the function header. It's basically a constant string, with the * table name replaced a couple of times and the primary key columns replaced * once. Make sure we don't fail if there's no primary key. */ IF pkcols <> '' THEN pkcols = ', ' || pkcols; pkformat = ', PK=( ' || pkformat || ' )'; END IF; func = $f$ CREATE OR REPLACE FUNCTION $f$ || funcname || $f$() RETURNS void LANGUAGE plpgsql AS $$ DECLARE rec record; BEGIN FOR rec IN SELECT ctid $f$ || pkcols || $f$ FROM $f$ || $1 || $f$ LOOP DECLARE f record; l int; BEGIN SELECT * INTO f FROM $f$ || $1 || $f$ WHERE ctid = rec.ctid; -- make sure each column is detoasted and reported separately $f$; /* We now need one exception block per toastable column */ indent = ' '; FOR attrecord in SELECT attname, atttypid FROM pg_attribute JOIN pg_class on (oid=attrelid) WHERE pg_class.oid = $1::regclass and attlen = -1 LOOP func := func || indent || E'BEGIN\n'; if attrecord.atttypid = 'numeric'::regtype then detoast_funcname = 'numeric_sign'; else detoast_funcname = 'length'; end if; func := func || indent || $f$ SELECT $f$ || detoast_funcname || $f$(f.$f$ || quote_ident(attrecord.attname) || E') INTO l;\n'; /* The interesting part here needs some replacement of the PK columns */ func := func || indent || $f$EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'TID %$f$ || pkformat || $f$, column "$f$ || attrecord.attname || $f$": exception {{%}}', rec.ctid, $f$; /* This iterates zero times if there are no PK columns */ FOR colrec IN SELECT f.i[a] AS pknm FROM (select pk_col_ary as i) as f, generate_series(array_lower(pk_col_ary, 1), array_upper(pk_col_ary, 1)) as a LOOP func := func || $f$ rec.$f$ || colrec.pknm || $f$, $f$; END LOOP; func := func || E'sqlerrm;\n'; func := func || indent || E'END;\n'; END LOOP; /* And this is our constant footer */ func := func || $f$ END; END LOOP; END; $$; $f$; EXECUTE func; RAISE NOTICE $f$Successfully created function %()$f$, funcname; RETURN; END; $ff$; pgFormatter-4.2/t/test-files/ex2.sql000066400000000000000000000015541361326045100174200ustar00rootroot00000000000000SELECT n.nspname as "Schema", p.proname as "Name", pg_catalog.pg_get_function_result(p.oid) as "Result data type", pg_catalog.pg_get_function_arguments(p.oid) as "Argument data types", CASE WHEN p.proisagg THEN 'agg' WHEN p.proiswindow THEN 'window' WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN 'trigger' ELSE 'normal' END as "Type" FROM pg_catalog.pg_proc p LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace WHERE p.proname ~ '^(version)$' AND pg_catalog.pg_function_is_visible(p.oid) ORDER BY 1, 2, 4; SELECT CASE WHEN (FALSE) THEN 0 WHEN (TRUE) THEN 2 END AS dummy1 FROM my_table; CREATE OR REPLACE FUNCTION task_job_maint_after () RETURNS TRIGGER AS $$ BEGIN CASE new.state WHEN 'final' THEN NOTIFY task_job_final; ELSE NULL; END CASE; RETURN NEW; END; $$ LANGUAGE plpgsql; pgFormatter-4.2/t/test-files/ex20.sql000066400000000000000000000006161361326045100174760ustar00rootroot00000000000000SELECT group_concat(k.column_name ORDER BY k.ordinal_position) AS column_names, t.table_name AS table_name, t.table_schema AS table_schema, t.constraint_name AS constraint_name FROM information_schema.table_constraints t LEFT JOIN information_schema.key_column_usage k USING (constraint_name, table_schema, table_name) WHERE t.constraint_type = 'PRIMARY KEY' GROUP BY t.table_schema, t.table_name; pgFormatter-4.2/t/test-files/ex21.sql000066400000000000000000000002131361326045100174700ustar00rootroot00000000000000SELECT 1 as a \gset \echo :a \\ SELECT :a; \unset a SELECT 'test' \g testfile.txt \! cat testfile.txt SELECT current_timestamp; \watch 3 pgFormatter-4.2/t/test-files/ex22.sql000066400000000000000000000027671361326045100175110ustar00rootroot00000000000000SELECT code.code, properties_code.valeur, properties_code1.valeur, properties_code2.valeur FROM code INNER JOIN linkcode ON (code.id_code = linkcode.id_codeparent) INNER JOIN linkcode linkcode1 ON (linkcode.id_codeenfant = linkcode1.id_codeparent) INNER JOIN code code1 ON (linkcode1.id_codeenfant = code1.id_code) INNER JOIN properties_code ON (code1.id_code = properties_code.id_code) INNER JOIN properties_code properties_code1 ON (code1.id_code = properties_code1.id_code) INNER JOIN properties_code properties_code2 ON (code1.id_code = properties_code2.id_code) INNER JOIN properties ON (properties_code.id_propriete = properties.id_propriete) INNER JOIN properties properties1 ON (properties_code1.id_propriete = properties1.id_propriete) INNER JOIN properties properties2 ON (properties_code2.id_propriete = properties2.id_propriete) INNER JOIN variables ON (properties.id_variable = variables.id_variable) INNER JOIN variables variables1 ON (properties1.id_variable = variables1.id_variable) INNER JOIN variables variables2 ON (properties2.id_variable = variables2.id_variable) INNER JOIN mvt_temps ON (code.id_code = mvt_temps.id_code) INNER JOIN transactions ON (mvt_temps.id_transaction = transactions.id_transaction) INNER JOIN products ON (code.id_product = products.id_product) WHERE variables.name = 'etat_DEC_BPO_sMacAdresse' AND variables1.name = 'variable_name' AND variables2.name = 'variable_name' AND transactions.code = 'XXXXXXXXXXXXX' AND mvt_temps.statutok = True AND products.codeproduct = '123456789' pgFormatter-4.2/t/test-files/ex23.sql000066400000000000000000000002071361326045100174750ustar00rootroot00000000000000INSERT INTO task_item (task_item_id, task_item_description, priority, on_hold, task_type) VALUES (1, 'a desc', 'low', 't', 'an item'); pgFormatter-4.2/t/test-files/ex24.sql000066400000000000000000000003261361326045100175000ustar00rootroot00000000000000SELECT id, count(test), CASE WHEN true THEN 1 END AS looks_good, count( CASE WHEN true THEN 1 END) AS looks_wrong_and_indent_is_off, count( CASE WHEN false THEN 1 END) AS looks_wrong FROM test; pgFormatter-4.2/t/test-files/ex25.sql000066400000000000000000000001531361326045100174770ustar00rootroot00000000000000CREATE TABLE demo.test ( foo text, bar timestamp WITH time zone, baz text ) WITH (OIDS = FALSE); pgFormatter-4.2/t/test-files/ex26.sql000066400000000000000000000004001361326045100174730ustar00rootroot00000000000000PREPARE demo AS INSERT INTO demo VALUES (1, 2, 3, 4); PREPARE demo AS SELECT * FROM demo WHERE id IN (1, 2, 3, 4); PREPARE demo AS UPDATE demo SET lbl = 'unknown' WHERE id IN (1, 2, 3, 4); PREPARE demo AS DELETE FROM demo WHERE id IN (1, 2, 3, 4); pgFormatter-4.2/t/test-files/ex27.sql000066400000000000000000000045171361326045100175110ustar00rootroot00000000000000SELECT opgt.part_id, opgt.art_id, rGpe.slot_id, rGpe.grp_art_id FROM ( SELECT id, part_product_kind_id FROM part_product ldm WHERE nb_colis > 0 AND statut_part_product IN ('DISPATCH','CANCELED','SALE_PROD') AND ldm.art_frect_id IS NULL AND NOT EXISTS ( SELECT subparts.id FROM part_product subparts WHERE ldm.id = subparts.part_product_parent_id )) ldm JOIN LATERAL ( SELECT opgt.id, part.id as part_id, opgt.art_id FROM part_product part JOIN part_product_kind opgt ON opgt.id = part.part_product_kind_id WHERE part.id = ldm.id AND opgt.plateforme_distribution_id = ($1) AND opgt.day_export_shop_id IN ( SELECT id FROM day_export_shop jem WHERE day <= to_timestamp($2) AND day > current_date - interval '4 mons')) opgt ON true JOIN LATERAL (SELECT r.id AS slot_id, ga.id AS grp_art_id FROM part_product_kind lm JOIN art a ON a.id = lm.art_id JOIN grp_art ga ON ga.id = a.grp_art_id JOIN slot r ON r.id = ga.slot_id WHERE opgt.id = lm.id) rGpe ON true UNION SELECT opgt.part_id, opgt.art_id, rGpe.slot_id, rGpe.grp_art_id FROM ( SELECT id, art_frect_id, part_product_kind_id FROM part_product ldm WHERE nb_colis > 0 AND statut_part_product IN ('DISPATCH','CANCELED','SALE_PROD') AND art_frect_id IS NOT NULL AND NOT EXISTS ( SELECT subparts.id FROM part_product subparts WHERE ldm.id = subparts.part_product_parent_id )) ldm JOIN LATERAL ( SELECT opgt.id, part.id as part_id, opgt.art_id FROM part_product part JOIN part_product_kind opgt ON opgt.id = part.part_product_kind_id WHERE part.id = ldm.id AND opgt.plateforme_distribution_id = ($3) AND opgt.day_export_shop_id IN ( SELECT id FROM day_export_shop jem WHERE day <= to_timestamp($4) AND day > current_date - interval '4 mons')) opgt ON true JOIN LATERAL (SELECT r.id AS slot_id, ga.id AS grp_art_id FROM part_product lm JOIN art a ON a.id = lm.art_frect_id JOIN grp_art ga ON ga.id = a.grp_art_id JOIN slot r ON r.id = ga.slot_id WHERE opgt.id = lm.id) rGpe ON true; SELECT "INSTANCE_STATE_ID", "FORM_ID" FROM "INSTANCE" INNER JOIN "FORM" ON "INSTANCE"."FORM_ID" = "FORM"."FORM_ID" LEFT JOIN "FORM_T" ON "FORM"."FORM_ID" = "FORM_T"."FORM_ID" AND "FORM_T"."LANGUAGE" = 'de' INNER JOIN "INSTANCE_STATE" ON "INSTANCE_STATE"."INSTANCE_STATE_ID" = "INSTANCE"."INSTANCE_STATE_ID" LEFT JOIN "INSTANCE_STATE_T" ON "INSTANCE_STATE"."INSTANCE_STATE_ID" = "INSTANCE_STATE_T"."INSTANCE_STATE_ID" pgFormatter-4.2/t/test-files/ex28.sql000066400000000000000000000003051361326045100175010ustar00rootroot00000000000000BEGIN; CREATE FUNCTION basename(path text) RETURNS text AS $$ return path.replace(/.*\//, ''); $$ LANGUAGE 'plv8' IMMUTABLE; COMMIT; UPDATE article a SET title = 'x' WHERE (a.perm & 8)::bool pgFormatter-4.2/t/test-files/ex29.sql000066400000000000000000000025431361326045100175100ustar00rootroot00000000000000SELECT DISTINCT (current_database())::information_schema.sql_identifier AS view_catalog, (nv.nspname)::information_schema.sql_identifier AS view_schema, (v.relname)::information_schema.sql_identifier AS view_name, (current_database())::information_schema.sql_identifier AS table_catalog, (nt.nspname)::information_schema.sql_identifier AS table_schema, (t.relname)::information_schema.sql_identifier AS table_name FROM pg_namespace nv, pg_class v, pg_depend dv, pg_depend dt, pg_class t, pg_namespace nt WHERE ((((((((((((((nv.oid = v.relnamespace) AND (v.relkind = 'v'::"char")) AND (v.oid = dv.refobjid)) AND (dv.refclassid = ('pg_class'::regclass)::oid)) AND (dv.classid = ('pg_rewrite'::regclass)::oid)) AND (dv.deptype = 'i'::"char")) AND (dv.objid = dt.objid)) AND (dv.refobjid <> dt.refobjid)) AND (dt.classid = ('pg_rewrite'::regclass)::oid)) AND (dt.refclassid = ('pg_class'::regclass)::oid)) AND (dt.refobjid = t.oid)) AND (t.relnamespace = nt.oid)) AND (t.relkind = ANY (ARRAY['r'::"char", 'v'::"char"]))) AND pg_has_role(t.relowner, 'USAGE'::text)) ORDER BY (current_database())::information_schema.sql_identifier, (nv.nspname)::information_schema.sql_identifier, (v.relname)::information_schema.sql_identifier, (current_database())::information_schema.sql_identifier, (nt.nspname)::information_schema.sql_identifier, (t.relname)::information_schema.sql_identifier; pgFormatter-4.2/t/test-files/ex3.sql000066400000000000000000000004711361326045100174160ustar00rootroot00000000000000CREATE TABLE test (col text); INSERT INTO test VALUES ('123'); CREATE FUNCTION fonction_reference(refcursor) RETURNS refcursor AS $$ BEGIN OPEN $1 FOR SELECT col FROM test; RETURN $1; END; $$ LANGUAGE plpgsql; BEGIN; SELECT fonction_reference('curseur_fonction'); FETCH ALL IN curseur_fonction; COMMIT; pgFormatter-4.2/t/test-files/ex30.sql000066400000000000000000000034371361326045100175030ustar00rootroot00000000000000 CREATE OR REPLACE FUNCTION foo () RETURNS TRIGGER AS $$ BEGIN IF NEW.role NOT IN ( SELECT rolname FROM pg_authid) THEN RAISE EXCEPTION 'role % does not exist.', NEW.role; END IF; END; $$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION plpython_demo () RETURNS void AS $$ from this import s, d for char in s: print(d.get(char, char), end="") print() $$ LANGUAGE 'plpython3u'; CREATE OR REPLACE FUNCTION plpython_demo2 () RETURNS void LANGUAGE 'plpython3u' AS $body$ from this import u, f for char in u: print(f.get(char, char), end="") print() $body$ ; CREATE FUNCTION add(integer, integer) RETURNS integer LANGUAGE sql IMMUTABLE STRICT AS $_$select $1 + $2;$_$; CREATE FUNCTION dup(integer, OUT f1 integer, OUT f2 text) RETURNS record LANGUAGE sql AS $_$ SELECT $1, CAST($1 AS text) || ' is text' $_$; CREATE TABLE IF NOT EXISTS foo ( id bigint PRIMARY KEY, /* This text will receive an extra level of indentation every time pg_format is executed */ bar text NOT NULL /* this is the end*/ ); COMMENT ON TABLE xx.yy IS 'Line 1 - Line 2 - Line 3'; CREATE TABLE IF NOT EXISTS foo ( /******************************************************* * This text will receive an extra level of indentation * * every time pg_format is executed * ********************************************************/ id bigint PRIMARY KEY, /* This text will receive an extra level of indentation every time pg_format is executed */ bar text NOT NULL /* this is the end*/ ); ALTER TABLE app_public.users ENABLE ROW LEVEL SECURITY; COMMENT ON FUNCTION my_function () IS 'Here is my function that has a comment; will this become a sql clause or statement?'; pgFormatter-4.2/t/test-files/ex31.sql000066400000000000000000000025421361326045100175000ustar00rootroot00000000000000CREATE PROCEDURE insert_data(a integer, b integer) LANGUAGE SQL AS $$ INSERT INTO tbl VALUES (a); INSERT INTO tbl VALUES (b); $$; CALL insert_data(1, 2); ALTER PROCEDURE insert_data1(integer, integer) RENAME TO insert_record; ALTER PROCEDURE insert_data2(integer, integer) OWNER TO joe; ALTER PROCEDURE insert_data3(integer, integer) SET SCHEMA accounting; ALTER PROCEDURE insert_data4(integer, integer) DEPENDS ON EXTENSION myext; ALTER PROCEDURE check_password1(text) SET search_path = admin, pg_temp; ALTER PROCEDURE check_password2(text) RESET search_path; ALTER ROUTINE foo1(integer) RENAME TO foobar; ALTER ROUTINE foo2(integer, varchar(255)) OWNER TO ufoo; ALTER ROUTINE foo3(integer, varchar(255), boolean) SET SCHEMA sfoo; ALTER ROUTINE foo4(varchar(25),integer) DEPENDS ON EXTENSION fooext; ALTER ROUTINE foo5(integer) IMMUTABLE; ALTER ROUTINE foo6(integer) SECURITY INVOKER; ALTER ROUTINE foo7(integer) RESET ALL; ALTER ROUTINE foo8(integer) SET work_mem = '1GB'; ALTER ROUTINE foo9(integer) SET work_mem FROM CURRENT; CREATE PUBLICATION mypublication1 FOR TABLE ONLY emps; CREATE PUBLICATION mypublication2 FOR TABLE users, departments; CREATE PUBLICATION alltables FOR ALL TABLES; CREATE PUBLICATION insert_only FOR TABLE mydata WITH ( publish = 'insert' ); SELECT my_func(p_type_cd => t.type_cd) AS my_func_result FROM tab t WHERE t.id=12; pgFormatter-4.2/t/test-files/ex32.sql000066400000000000000000000011251361326045100174750ustar00rootroot00000000000000CREATE TABLE projects ( id UUID PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4 (), name citext NOT NULL CHECK (name ~* '^[a-z0-9_-]{3,255}$'), owner_id UUID NOT NULL, UNIQUE (owner_id, name), PRIMARY KEY (id, name) ); CREATE TABLE projects ( id UUID PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4 (), name citext NOT NULL CHECK (name ~* '^[a-z0-9_-]{3,255}$'), owner_id UUID NOT NULL, UNIQUE name, PRIMARY KEY id ); CREATE TABLE sample ( fkey integer REFERENCES other (id) ON UPDATE CASCADE ON DELETE CASCADE, other integer, columns integer ); pgFormatter-4.2/t/test-files/ex33.sql000066400000000000000000000004251361326045100175000ustar00rootroot00000000000000SELECT CASE WHEN my_col IS NOT NULL THEN 'Y' ELSE 'N' END AS my_new_col, CASE WHEN TRIM(my_other_col) = 'confirmed' THEN 'Y' ELSE 'N' END AS new_col FROM my_table; select distinct on (a, b) a, b, c from d order by a, b, c; select distinct a, b, b, c from d order by a, b, c; pgFormatter-4.2/t/test-files/ex34.sql000066400000000000000000000006621361326045100175040ustar00rootroot00000000000000SELECT count(*) filter (where b) as count_b, count(*) filter (where c) as count_c FROM a; SELECT array_agg(a ORDER BY b DESC) FROM table; SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY income) FROM households; SELECT count(*) AS unfiltered, count(*) FILTER (WHERE i < 5) AS filtered FROM generate_series(1,10) AS s(i); SELECT make, model, GROUPING(make,model), sum(sales) FROM items_sold GROUP BY ROLLUP(make,model); pgFormatter-4.2/t/test-files/ex35.sql000066400000000000000000000024351361326045100175050ustar00rootroot00000000000000CREATE TABLE kbln ( id integer NOT NULL, blank_series varchar(50) NOT NULL, company_id varchar(8)) partition by range ( id); create table kbln_p0 partition of kbln for values from ( minvalue) to ( 500000) partition by hash ( blank_series); create table kbln_p0_1 partition of kbln_p0 for values with ( modulus 2, remainder 0); create table kbln_p0_2 partition of kbln_p0 for values with ( modulus 2, remainder 1); alter table t1 detach partition t1_a; alter table t1 attach partition t1_a for values in (1, 2, 3); CREATE TABLE kbln ( id integer NOT NULL, blank_series varchar(50) NOT NULL, company_id varchar(8)) partition by list ( id); select id, for_group, some_val, sum(some_val) over (partition by for_group order by id) as sum_so_far_in_group, sum(some_val) over (partition by for_group) as sum_in_group, sum(some_val) over (partition by for_group order by id range 3 preceding) as sum_current_and_3_preceeding, sum(some_val) over (partition by for_group order by id range between 3 preceding and 3 following) as sum_current_and_3_preceeding_and_3_following, sum(some_val) over (partition by for_group order by id range between current row and unbounded following) as sum_current_and_all_following from test order by for_group, id; pgFormatter-4.2/t/test-files/ex36.sql000066400000000000000000000002341361326045100175010ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION foo () RETURNS TRIGGER AS $$ BEGIN CREATE TEMPORARY TABLE tb ( id integer ); SELECT * FROM nothing; END; $$ LANGUAGE 'plpgsql'; pgFormatter-4.2/t/test-files/ex37.sql000066400000000000000000000007551361326045100175120ustar00rootroot00000000000000CREATE POLICY can_select_object ON object FOR SELECT USING ( can_do_the_thing(get_current_user(), owner_id) ); CREATE POLICY can_insert_object ON object FOR INSERT WITH CHECK ( can_do_the_thing(get_current_user(), owner_id) ); CREATE POLICY can_update_object ON object FOR UPDATE USING ( can_do_the_thing(get_current_user(), owner_id) ); CREATE POLICY can_delete_object ON object FOR DELETE USING ( can_do_the_thing(get_current_user(), owner_id) ); pgFormatter-4.2/t/test-files/ex38.sql000066400000000000000000000006551361326045100175120ustar00rootroot00000000000000CREATE TRIGGER check_update BEFORE UPDATE ON accounts FOR EACH ROW EXECUTE PROCEDURE check_account_update(); CREATE TRIGGER check_update BEFORE UPDATE OF balance ON accounts FOR EACH ROW EXECUTE PROCEDURE check_account_update(); CREATE TRIGGER check_update BEFORE UPDATE ON accounts FOR EACH ROW WHEN (OLD.balance IS DISTINCT FROM NEW.balance) EXECUTE PROCEDURE check_account_update(); pgFormatter-4.2/t/test-files/ex39.sql000066400000000000000000000006121361326045100175040ustar00rootroot00000000000000ALTER TABLE boxes ADD CONSTRAINT my_constraint EXCLUDE USING gist ( some_id WITH =, make_tsrange(created_at, expires_at) WITH &&); CREATE TABLE circles ( c circle, EXCLUDE USING gist (c WITH &&) ); ALTER TABLE ONLY public.circles ADD CONSTRAINT circles_c_excl EXCLUDE USING gist (c WITH &&); ALTER TABLE truck ADD EXCLUDE USING gist (id WITH =, system_period WITH &&); pgFormatter-4.2/t/test-files/ex4.sql000066400000000000000000000010701361326045100174130ustar00rootroot00000000000000SELECT 1, 2, 10, 'depesz', 'hubert', 'depesz', 'hubert depesz', '1 2 3 4'; SELECT tbl_lots.id, to_char ( tbl_lots.dt_crea, 'DD/MM/YYYY HH24:MI:SS' ) AS date_crea FROM tbl_lots WHERE tbl_lots.dt_crea > current_timestamp - interval '1 day' AND tbl_lots.dt_crea > current_timestamp - (dayrett || ' days')::interval AND tbl_lots.type = 'SECRET'; SELECT extract( year from school_day ) AS year; SELECT substring( firstname from 1 for 10 ) AS sname; select * from (select 1 i) a INNER JOIN (select 1 i) b ON (a.i=b.i) inner join (select 1 i) ON (c.i=a.i) where a.i=1 ; pgFormatter-4.2/t/test-files/ex40.sql000066400000000000000000000003641361326045100175000ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION myfunc() RETURNS void AS $BODY$ BEGIN set client_min_messages to warning; DROP TABLE IF EXISTS tt_BIP; DROP TABLE IF EXISTS tt_tmp; set client_min_messages to notice; END; $BODY$ LANGUAGE plpgsql VOLATILE COST 100; pgFormatter-4.2/t/test-files/ex41.sql000066400000000000000000000011321361326045100174730ustar00rootroot00000000000000/* Insert values for environment 1 */ INSERT INTO table VALUES (1, 2, '3'); /* Insert values for environment 2 */ /* Insert values for environment 3 */ INSERT INTO table VALUES (2, 17, 'hello'); -- New comment for a query SELECT library.column1, --------------------- -- This is a line -- -- comment in a -- -- SQL statement -- --------------------- library.column2, library.column3 -- inline comment FROM library; pgFormatter-4.2/t/test-files/ex42.sql000066400000000000000000000003451361326045100175010ustar00rootroot00000000000000WITH styles AS ( SELECT style, color FROM style_table s WHERE s.id = 'D' ), reviews AS ( SELECT style, body FROM review_table ) SELECT * FROM styles s; pgFormatter-4.2/t/test-files/ex43.sql000066400000000000000000000020121361326045100174730ustar00rootroot00000000000000INSERT INTO tt_tmp_wrk1 ( SELECT MAR , ( CASE WHEN MAR = 'SEUIL_DEST' THEN ( SELECT COUNT(*) FROM CUS WHERE MAR.obj4 = ( CASE WHEN MAR.type4 = 'CF' THEN CUS.account_no::text WHEN MAR.type4 = 'CR' THEN CUS.owning_account_no::text WHEN MAR.type4 = 'SI' THEN CUS.point_origin END ) AND ( CUS = ( SELECT DISTINCT DD FROM PCM WHERE PCM.compo = CMF.compo ) OR CUS = ( SELECT DISTINCT CPIR FROM PCM WHERE PCM.compo = CMF.compo ) OR CUS = ( SELECT DISTINCT CPIR FROM PCM, CT, PIDR, CPIR, CMF WHERE PCM.compo = CMF.compo ) ) ) WHEN MAR = 'SEUIL_OBJ' THEN ( SELECT COUNT(*) FROM CPC WHERE MAR.obj4 = ( CASE WHEN MAR.type4 = 'CF' THEN CPC.parent::text WHEN MAR.type4 = 'CR' THEN CPC.parent::text WHEN MAR.type4 = 'SI' THEN CPC.parent::text END ) AND CPC IN ( SELECT SPT FROM SPT WHERE SPT = 'SEUIL_OBJ' AND SPT IN ( SELECT DISTINCT compo FROM CPC WHERE MAR = ( CASE WHEN MAR = 'CF' THEN CPC.parent::text WHEN MAR = 'CR' THEN CPC.parent::text WHEN MAR.type4 = 'SI' THEN CPC.parent::text END ) ) ) ) END ) FROM MAR, BIP WHERE BIP = (MAR)::int ) ; pgFormatter-4.2/t/test-files/ex44.sql000066400000000000000000000002501361326045100174760ustar00rootroot00000000000000SELECT * FROM tabl WHERE (a = c) AND (b = c OR c = d); SELECT * FROM tabl WHERE (a = c AND b = c) OR (c = d); SELECT * FROM tabl WHERE (a = c) AND (b = c) OR (c = d); pgFormatter-4.2/t/test-files/ex45.sql000066400000000000000000000014011361326045100174760ustar00rootroot00000000000000WITH cte1 AS ( SELECT * FROM table_a a -- Comment out or remove this JOIN to fix the indentation issue below INNER JOIN table_b b ON a.id = b.id ), cte2 AS ( SELECT CASE WHEN TRUE THEN TRUE WHEN NULL IS NULL OR TRUE = FALSE THEN NULL -- Indentation is off starting here WHEN FALSE AND TRUE THEN TRUE ELSE FALSE END AS value FROM cte1 -- Indentation is correct after this line ) SELECT * FROM cte2; SELECT *, SUM(( SELECT count(*) FROM b )) AS something, SUM(( SELECT count(*) FROM b )) AS something, a.b FROM a; pgFormatter-4.2/t/test-files/ex46.sql000066400000000000000000000030701361326045100175030ustar00rootroot00000000000000SELECT a, b, c, d FROM t_1, t_2, t3 WHERE a = 10 AND b = 10 AND c = 10 AND d IN (1, 2, 3 , 4, 5 , 6, 7); SELECT a, b, c, d FROM t_1, t_2, (SELECT * FROM t6) as t3, t4 WHERE a = 10 AND b = 10 AND c = 10 AND d IN (1, 2, 3 , 4, 5 , 6, 7); SELECT '--data' FROM test; DECLARE c_mvcc_demo CURSOR FOR SELECT xmin, xmax, cmax, * FROM mvcc_demo; SELECT 1; SELECT * FROM pg_class ORDER BY relname; SELECT RANK() OVER s AS dept_rank FROM emp WINDOW s AS (PARTITION BY department ORDER BY salary DESC) ORDER BY department, salary DESC; SELECT x, COUNT(x) OVER w, SUM(x) OVER w FROM generate_series(1, 10) AS f (x) WINDOW w AS (); SELECT name, department, salary, RANK() OVER s AS dept_rank, RANK() OVER () AS global_rank FROM empa WINDOW s AS (PARTITION BY department ORDER BY salary DESC) ORDER BY department, salary DESC; SELECT name, department, salary, RANK() OVER () AS global_rank, RANK() OVER s AS dept_rank FROM empb WINDOW s AS (PARTITION BY department ORDER BY salary DESC) ORDER BY department, salary DESC; UPDATE mvcc_demo SET val = val + 1 WHERE val > 0; WITH driver (name) AS ( SELECT DISTINCT unnest(xpath('//driver/text()', doc))::text FROM printer ) SELECT name FROM driver WHERE name LIKE 'hp%' ORDER BY 1; WITH source (x1, x2) AS ( SELECT 1 ) SELECT * FROM source; SELECT DISTINCT relkind, relname FROM pg_class ORDER BY 1, 2; SELECT salary, RANK() OVER s FROM emp WINDOW s AS ( ORDER BY salary DESC) ORDER BY salary DESC; EXPLAIN (COSTS OFF, ANALYZE ) SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; pgFormatter-4.2/t/test-files/ex47.sql000066400000000000000000000032661361326045100175130ustar00rootroot00000000000000WITH source AS source2 AS ( DELETE FROM items USING source2 ) INSERT INTO old_orders SELECT order_id FROM source2; SELECT '--data' FROM test; DECLARE c_mvcc_demo CURSOR FOR SELECT xmin, xmax, cmax, * FROM mvcc_demo; SELECT 1; SELECT * FROM pg_class ORDER BY relname; SELECT RANK() OVER s AS dept_rank FROM emp WINDOW s AS (PARTITION BY department ORDER BY salary DESC) ORDER BY department, salary DESC; SELECT x, COUNT(x) OVER w, SUM(x) OVER w FROM generate_series(1, 10) AS f (x) WINDOW w AS (); SELECT name, department, salary, RANK() OVER s AS dept_rank, RANK() OVER () AS global_rank FROM empa WINDOW s AS (PARTITION BY department ORDER BY salary DESC) ORDER BY department, salary DESC; SELECT name, department, salary, RANK() OVER () AS global_rank, RANK() OVER s AS dept_rank FROM empb WINDOW s AS (PARTITION BY department ORDER BY salary DESC) ORDER BY department, salary DESC; UPDATE mvcc_demo SET val = val + 1 WHERE val > 0; WITH driver (name) AS ( SELECT DISTINCT unnest(xpath('//driver/text()', doc))::text FROM printer ) SELECT name FROM driver WHERE name LIKE 'hp%' ORDER BY 1; WITH source (x1, x2) AS ( SELECT 1 ) SELECT * FROM source; SELECT DISTINCT relkind, relname FROM pg_class ORDER BY 1, 2; SELECT salary, RANK() OVER s FROM emp WINDOW s AS ( ORDER BY salary DESC) ORDER BY salary DESC; CREATE TRIGGER test_trigger_exists BEFORE UPDATE ON test_exists FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger(); CREATE RULE test_rule_exists AS ON INSERT TO test_exists DO INSTEAD INSERT INTO test_exists VALUES (NEW.a, NEW.b || NEW.a::text); DROP RULE test_rule_exists ON test_exists; pgFormatter-4.2/t/test-files/ex48.sql000066400000000000000000000031231361326045100175040ustar00rootroot00000000000000INSERT INTO test_exists VALUES (NEW.a, NEW.b || NEW.a::text); CREATE RULE test_rule_exists AS ON INSERT TO test_exists DO INSTEAD INSERT INTO test_exists VALUES (NEW.a, NEW.b || NEW.a::text); DROP RULE test_rule_exists ON test_exists; DROP SEQUENCE test_sequence_exists; CREATE GROUP regress_test_g1; DROP GROUP regress_test_g1; DROP USER IF EXISTS regress_test_u1, regress_test_u2; CREATE CONVERSION test_conversion_exists FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; CREATE DOMAIN test_domain_exists AS int NOT NULL CHECK ( value > 0 ); DROP OPERATOR @#@ (int, int); DROP OPERATOR IF EXISTS @#@ (int, int); CREATE OPERATOR @#@ (leftarg = int8, rightarg = int8, procedure = int8xor); DROP OPERATOR @#@ (int8, int8); create operator alter1.=(procedure = alter1.same, leftarg = alter1.ctype, rightarg = alter1.ctype); CREATE OPERATOR !== ( PROCEDURE = int8ne, LEFTARG = bigint, RIGHTARG = bigint, COMMUTATOR = !==, NEGATOR = === ); EXPLAIN (COSTS OFF, ANALYZE ) SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; CREATE FUNCTION sql_is_distinct_from (anyelement, anyelement) RETURNS boolean LANGUAGE sql AS 'INSERT INTO dom_table VALUES (1, 2, 3)' ; INSERT INTO dom_table VALUES ('1'); INSERT INTO dom_table VALUES ('1'); CREATE FUNCTION customcontsel(internal, oid, internal, integer) RETURNS float8 AS 'contsel' LANGUAGE internal STABLE STRICT; CREATE VIEW attmp_view (unique1) AS SELECT unique1 FROM tenk1; CREATE VIEW attmp_view (col1, col2) AS SELECT cola, colb FROM tenk1; CREATE TABLE a (u) AS SELECT u; pgFormatter-4.2/t/test-files/ex49.sql000066400000000000000000000102551361326045100175110ustar00rootroot00000000000000-- Round-trip non-ASCII data through xpath(). DO $$ DECLARE xml_declaration text := ''; degree_symbol text; res xml[]; BEGIN -- Per the documentation, except when the server encoding is UTF8, xpath() -- may not work on non-ASCII data. The untranslatable_character and -- undefined_function traps below, currently dead code, will become relevant -- if we remove this limitation. IF current_setting('server_encoding') <> 'UTF8' THEN RAISE LOG 'skip: encoding % unsupported for xpath', current_setting('server_encoding'); RETURN; END IF; degree_symbol := convert_from('\xc2b0', 'UTF8'); res := xpath('text()', (xml_declaration || '' || degree_symbol || '')::xml); IF degree_symbol <> res[1]::text THEN RAISE 'expected % (%), got % (%)', degree_symbol, convert_to(degree_symbol, 'UTF8'), res[1], convert_to(res[1]::text, 'UTF8'); END IF; EXCEPTION -- character with byte sequence 0xc2 0xb0 in encoding "UTF8" has no equivalent in encoding "LATIN8" WHEN untranslatable_character -- default conversion function for encoding "UTF8" to "MULE_INTERNAL" does not exist OR undefined_function -- unsupported XML feature OR feature_not_supported THEN RAISE LOG 'skip: %', SQLERRM; END $$; CREATE TRIGGER invalid_trig INSTEAD OF UPDATE ON main_view FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE view_trigger ('instead_of_upd'); -- -- PARALLEL -- -- Serializable isolation would disable parallel query, so explicitly use an -- arbitrary other level. BEGIN ISOLATION level REPEATABLE read; -- encourage use of parallel plans SET parallel_setup_cost = 0; SET parallel_tuple_cost = 0; SET min_parallel_table_scan_size = 0; SET max_parallel_workers_per_gather = 4; -- -- Test write operations that has an underlying query that is eligble -- for parallel plans -- EXPLAIN ( COSTS OFF ) CREATE TABLE parallel_write AS SELECT length( stringu1 ) FROM tenk1 GROUP BY length( stringu1 ); CREATE TABLE parallel_write AS SELECT length( stringu1 ) FROM tenk1 GROUP BY length( stringu1 ); DROP TABLE parallel_write; EXPLAIN ( COSTS OFF ) SELECT length(stringu1) INTO parallel_write FROM tenk1 GROUP BY length(stringu1); SELECT length(stringu1) INTO parallel_write FROM tenk1 GROUP BY length(stringu1); DROP TABLE parallel_write; EXPLAIN ( COSTS OFF ) CREATE materialized VIEW parallel_mat_view AS SELECT length(stringu1) FROM tenk1 GROUP BY length(stringu1); CREATE materialized VIEW parallel_mat_view AS SELECT length(stringu1) FROM tenk1 GROUP BY length(stringu1); DROP materialized VIEW parallel_mat_view; PREPARE prep_stmt AS SELECT length(stringu1) FROM tenk1 GROUP BY length(stringu1); EXPLAIN ( COSTS OFF ) CREATE TABLE parallel_write AS EXECUTE prep_stmt; CREATE TABLE parallel_write AS EXECUTE prep_stmt; DROP TABLE parallel_write; ROLLBACK; SELECT first_value(unique1) OVER (ORDER BY four ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING exclude GROUP), unique1, four FROM tenk1 WHERE unique1 < 10; CREATE aggregate my_avg (int4) ( stype = avg_state, sfunc = avg_transfn, finalfunc = avg_finalfn ); CREATE aggregate my_sum_init ( int4) ( stype = avg_state, sfunc = avg_transfn, finalfunc = sum_finalfn, initcond = '(10,0)' ); CREATE AGGREGATE balk ( int4 ) ( SFUNC = balkifnull (int8, int4 ), STYPE = int8, PARALLEL = SAFE, INITCOND = '0' ); CREATE AGGREGATE balk ( int4 ) ( SFUNC = int4_sum(int8, int4 ), STYPE = int8, COMBINEFUNC = balkifnull (int8, int8 ), PARALLEL = SAFE, INITCOND = '0' ); CREATE AGGREGATE myaggn08a (BASETYPE = anyelement, SFUNC = tf2p, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}'); pgFormatter-4.2/t/test-files/ex5.sql000066400000000000000000000005271361326045100174220ustar00rootroot00000000000000create or replace function inserta_esquema_pago_backup() returns trigger as $BODY$ BEGIN INSERT INTO educaciondistancia.esquema_pago_backup (curso, numpago, montopagar, estatus, usuario, fecha ) VALUES ( NEW.curso, NEW.numpago, NEW.montopagar, NEW.estatus, NEW.usuario, NEW.fecha ); RETURN NEW; END; $BODY$ LANGUAGE plpgsql VOLATILE COST 100; pgFormatter-4.2/t/test-files/ex50.sql000066400000000000000000000046621361326045100175060ustar00rootroot00000000000000-- function to wait for counters to advance create function wait_for_stats() returns void as $$ declare start_time timestamptz := clock_timestamp(); updated1 bool; updated2 bool; updated3 bool; updated4 bool; begin -- we don't want to wait forever; loop will exit after 30 seconds for i in 1 .. 300 loop -- With parallel query, the seqscan and indexscan on tenk2 might be done -- in parallel worker processes, which will send their stats counters -- asynchronously to what our own session does. So we must check for -- those counts to be registered separately from the update counts. -- check to see if seqscan has been sensed SELECT (st.seq_scan >= pr.seq_scan + 1) INTO updated1 FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr WHERE st.relname='tenk2' AND cl.relname='tenk2'; -- check to see if indexscan has been sensed SELECT (st.idx_scan >= pr.idx_scan + 1) INTO updated2 FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr WHERE st.relname='tenk2' AND cl.relname='tenk2'; -- check to see if all updates have been sensed SELECT (n_tup_ins > 0) INTO updated3 FROM pg_stat_user_tables WHERE relname='trunc_stats_test4'; -- We must also check explicitly that pg_stat_get_snapshot_timestamp has -- advanced, because that comes from the global stats file which might -- be older than the per-DB stats file we got the other values from. SELECT (pr.snap_ts < pg_stat_get_snapshot_timestamp()) INTO updated4 FROM prevstats AS pr; exit when updated1 and updated2 and updated3 and updated4; -- wait a little perform pg_sleep_for('100 milliseconds'); -- reset stats snapshot so we can test again perform pg_stat_clear_snapshot(); end loop; -- report time waited in postmaster log (where it won't change test output) raise log 'wait_for_stats delayed % seconds', extract(epoch from clock_timestamp() - start_time); end $$ language plpgsql; -- test ordered-set aggs using built-in support functions create aggregate my_percentile_disc(float8 ORDER BY anyelement) ( stype = internal, sfunc = ordered_set_transition, finalfunc = percentile_disc_final, finalfunc_extra = true, finalfunc_modify = read_write ); create aggregate my_rank(VARIADIC "any" ORDER BY VARIADIC "any") ( stype = internal, sfunc = ordered_set_transition_multi, finalfunc = rank_final, finalfunc_extra = true, hypothetical ); pgFormatter-4.2/t/test-files/ex51.sql000066400000000000000000000002311361326045100174730ustar00rootroot00000000000000/* comment1 */ -- comment2 select * from table; select * from pg_stat_activity where state = 'active' -- tabs should be used only for indentation ; pgFormatter-4.2/t/test-files/ex52.sql000066400000000000000000000072211361326045100175020ustar00rootroot00000000000000drop operator === (); CREATE TYPE jwt_token AS (token TEXT, field: TEXT); CREATE TABLE IF NOT EXISTS relationships ( id SERIAL PRIMARY KEY, users_id INTEGER NOT NULL REFERENCES users (id), students_id INTEGER NOT NULL REFERENCES students (id), communication BOOLEAN NOT NULL ); CREATE TABLE nulltest ( col1 dnotnull, col2 dnotnull NULL -- NOT NULL in the domain cannot be overridden , col3 dnull NOT NULL, col4 dnull, col5 dcheck CHECK (col5 IN ('c', 'd')) ); CREATE TABLE new_table_test ( start_date timestamp NOT NULL, end_date timestamp NOT NULL, name varchar(40) NOT NULL CHECK (name <> ''), fk_organization_unit_id numeric(20), fk_product_id numeric(20), id numeric(20) NOT NULL PRIMARY KEY, migrated varchar(1) ); Select Date_trunc('day' , last_updated_on) , Count(*) From exports.a Group By Date_trunc('day' , last_updated_on) Order By Date_trunc('day' , last_updated_on), toto Desc; CREATE FUNCTION customcontsel(internal, oid, internal, integer) RETURNS float8 AS 'contsel' LANGUAGE internal STABLE STRICT; CREATE FUNCTION ADD (integer, integer) RETURNS integer LANGUAGE sql IMMUTABLE STRICT AS $_$ SELECT $1 + $2; $_$; DO $$ BEGIN INSERT INTO table01 (id, user_id, group_id) SELECT nextval('foobar'), ug.user_id, 14 FROM table01 ug WHERE ug.group_id = 13 AND NOT EXISTS ( SELECT * FROM table01 ug2 WHERE ug2 = ug.user_id AND ug2.group_id = 14); END; $$; with ins (a, b, c) as (insert into mlparted (b, a) select s.a, 1 from generate_series(2, 39) s(a) returning tableoid::regclass, *) select a, b, min(c), max(c) from ins group by a, b order by 1; CREATE OR REPLACE FUNCTION chkrolattr() RETURNS TABLE ("role" name, rolekeyword text, canlogin bool, replication bool) AS $$ SELECT r.rolname, v.keyword, r.rolcanlogin, r.rolreplication FROM pg_roles r JOIN (VALUES(CURRENT_USER, 'current_user'), (SESSION_USER, 'session_user'), ('current_user', '-'), ('session_user', '-'), ('Public', '-'), ('None', '-')) AS v(uname, keyword) ON (r.rolname = v.uname) ORDER BY 1; $$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION hash_join_batches (query text) RETURNS TABLE ( original int, final int) LANGUAGE plpgsql AS $$ DECLARE whole_plan json; hash_node json; BEGIN FOR whole_plan IN EXECUTE 'explain (analyze, format ''json'') ' || query LOOP hash_node := find_hash (json_extract_path(whole_plan, '0', 'Plan')); original := hash_node ->> 'Original Hash Batches'; final := hash_node ->> 'Hash Batches'; RETURN NEXT; END LOOP; END; $$; create table nv_child_2009 (check (d between '2009-01-01'::date and '2009-12-31'::date)) inherits (nv_parent); create table part1 ( a int not null check (a = 1), b int not null check (b >= 1 and b <= 10) ); create trigger parted_trig after insert on parted_irreg for each row execute procedure trigger_notice_ab (); create constraint trigger parted_trig_two after insert on parted_constr deferrable initially deferred for each row when ( bark (new.b) and new.a % 2 = 1) execute procedure trigger_notice_ab (); begin; create trigger ttdummy before delete or update on alterlock for each row execute procedure ttdummy (1, 1); select * from my_locks order by 1; rollback; CREATE TRIGGER base_tab_def_view_instrig INSTEAD OF INSERT ON base_tab_def_view FOR EACH ROW EXECUTE FUNCTION base_tab_def_view_instrig_func (); pgFormatter-4.2/t/test-files/ex53.sql000066400000000000000000000114261361326045100175050ustar00rootroot00000000000000INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (KEY, fruit) DO UPDATE SET fruit = excluded.fruit WHERE EXISTS ( SELECT 1 FROM insertconflicttest ii WHERE ii.key = excluded.key); INSERT INTO tbl1 SELECT column1, coilumn2, column3 FROM tbl2 ON CONFLICT ON CONSTRAINT pk$tbl1 DO UPDATE SET asgen = excluded.asgen, long_rev = excluded.long_rev, short_rev = excluded.short_rev; UPDATE toto SET asgen = excluded.asgen, long_rev = excluded.long_rev, short_rev = excluded.short_rev; CREATE TABLE FKTABLE ( ftest1 int REFERENCES PKTABLE MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int ); CREATE TABLE FKTABLE ( ftest1 int DEFAULT -1, ftest2 int DEFAULT -2, ftest3 int, CONSTRAINT constrname2 FOREIGN KEY(ftest1, ftest2) REFERENCES PKTABLE MATCH FULL ON DELETE SET DEFAULT ON UPDATE SET DEFAULT); ALTER TABLE fk_partitioned_fk ADD FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk MATCH SIMPLE ON DELETE SET NULL ON UPDATE SET NULL; CREATE TABLE persons OF nothing; CREATE TABLE IF NOT EXISTS persons OF nothing; SELECT * FROM nothing; SET client_encoding TO 'UTF8'; UPDATE weather SET temp_lo = temp_lo+1, temp_hi = temp_lo+15, prcp = DEFAULT WHERE city = 'San Francisco' AND date = '2003-07-03' RETURNING temp_lo,temp_hi,prcp; INSERT INTO tbl1 SELECT column1, coilumn2, column3 FROM tbl2 ON CONFLICT ON CONSTRAINT pk$tbl1 DO UPDATE SET asgen = excluded.asgen, long_rev = excluded.long_rev, short_rev = excluded.short_rev; explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (fruit, key, fruit, key) do nothing; explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (lower(fruit), key, lower(fruit), key) do nothing; explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key, fruit) do update set fruit = excluded.fruit where exists (select 1 from insertconflicttest ii where ii.key = excluded.key); PREPARE demo AS INSERT INTO demo VALUES (1, 2, 3, 4); PREPARE demo AS UPDATE demo SET lbl = 'unknown' WHERE id IN (1, 2, 3, 4); BEGIN; LOCK hs1 IN SHARE UPDATE EXCLUSIVE MODE; COMMIT; create function tg_hub_a() returns trigger as ' declare hname text; dummy integer; begin if tg_op = ''INSERT'' then dummy := tg_hub_adjustslots(new.name, 0, new.nslots); return new; end if; if tg_op = ''UPDATE'' then if new.name != old.name then update HSlot set hubname = new.name where hubname = old.name; end if; dummy := tg_hub_adjustslots(new.name, old.nslots, new.nslots); return new; end if; if tg_op = ''DELETE'' then dummy := tg_hub_adjustslots(old.name, old.nslots, 0); return old; end if; end; ' language plpgsql; EXPLAIN ( COSTS OFF ) create TABLE parallel_write AS EXECUTE prep_stmt; CREATE FUNCTION tg_hub_adjustslots (hname bpchar, oldnslots integer, newnslots integer) RETURNS integer AS ' begin if newnslots = oldnslots then return 0; end if; if newnslots < oldnslots then delete from HSlot where hubname = hname and slotno > newnslots; return 0; end if; for i in oldnslots + 1 .. newnslots loop insert into HSlot (slotname, hubname, slotno, slotlink) values (''HS.dummy'', hname, i, ''''); end loop; return 0; end ' LANGUAGE plpgsql; create function tg_backlink_a() returns trigger as ' declare dummy integer; begin if tg_op = ''INSERT'' then if new.backlink != '''' then dummy := tg_backlink_set(new.backlink, new.slotname); end if; return new; end if; if tg_op = ''UPDATE'' then if new.backlink != old.backlink then if old.backlink != '''' then dummy := tg_backlink_unset(old.backlink, old.slotname); end if; if new.backlink != '''' then dummy := tg_backlink_set(new.backlink, new.slotname); end if; else if new.slotname != old.slotname and new.backlink != '''' then dummy := tg_slotlink_set(new.backlink, new.slotname); end if; end if; return new; end if; if tg_op = ''DELETE'' then if old.backlink != '''' then dummy := tg_backlink_unset(old.backlink, old.slotname); end if; return old; end if; end; ' language plpgsql; CREATE VIEW v AS WITH a AS ( SELECT * FROM aa ); -- original snippet \set user `echo $PGRST_DB_USER` \set passwd `echo $PGRST_DB_PWD` CREATE ROLE :user WITH LOGIN noinherit PASSWORD :'passwd'; pgFormatter-4.2/t/test-files/ex54.sql000066400000000000000000000165051361326045100175110ustar00rootroot00000000000000SELECT CASE WHEN 1 = 1 THEN 2 ELSE 3 end::text AS col1, col2, col3 FROM tb1; SELECT ( CASE WHEN 1 = 1 THEN 2 ELSE 3 END)::text AS col1, col2, col3 FROM tb1; UPDATE point_tbl SET f1[0] = NULL WHERE f1::text = '(10,10)'::point::text RETURNING *; SELECT 'TrUe'::text::boolean AS true, 'fAlse'::text::boolean AS false; SELECT true::boolean::text AS true, false::boolean::text AS false; CREATE PROCEDURE testns.bar() AS 'select 1' LANGUAGE sql; ALTER TABLE test9b ALTER COLUMN b TYPE priv_testdomain1; CREATE TYPE test7b AS ( a int, b priv_testdomain1 ); CREATE TYPE test8b AS ( a int, b int ); ALTER TYPE test8b ADD ATTRIBUTE c priv_testdomain1; CREATE OR REPLACE PROCEDURE foo ( ) AS $BODY$ DECLARE BEGIN INSERT INTO bar (COLUMN) VALUES (1); COMMIT; BEGIN INSERT INTO bar (COLUMN) VALUES (2); COMMIT; BEGIN INSERT INTO bar (COLUMN) VALUES (3); COMMIT; END; END; END; $BODY$ LANGUAGE plpgsql; CREATE TEXT SEARCH PARSER addr_ts_prs ( START = prsd_start, gettoken = prsd_nexttoken, END = prsd_end, lextypes = prsd_lextype ); CREATE FUNCTION fonction_reference(refcursor) RETURNS refcursor AS $$ BEGIN OPEN $1 FOR SELECT col FROM test; RETURN $1; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION foo () RETURNS TRIGGER AS $$ BEGIN IF NEW.role NOT IN ( SELECT rolname FROM pg_authid) THEN RAISE EXCEPTION 'role % does not exist.', NEW.role; END IF; END; $$ LANGUAGE 'plpgsql'; DO $$ DECLARE xml_declaration text := ''; degree_symbol text; res xml[]; BEGIN -- Per the documentation, except when the server encoding is UTF8, xpath() -- may not work on non-ASCII data. The untranslatable_character and -- undefined_function traps below, currently dead code, will become relevant -- if we remove this limitation. IF current_setting('server_encoding') <> 'UTF8' THEN RAISE LOG 'skip: encoding % unsupported for xpath', current_setting('server_encoding'); RETURN; END IF; degree_symbol := convert_from('\xc2b0', 'UTF8'); res := xpath('text()', (xml_declaration || '' || degree_symbol || '')::xml); IF degree_symbol <> res[1]::text THEN RAISE 'expected % (%), got % (%)', degree_symbol, convert_to(degree_symbol, 'UTF8'), res[1], convert_to(res[1]::text, 'UTF8'); END IF; EXCEPTION -- character with byte sequence 0xc2 0xb0 in encoding "UTF8" has no equivalent in encoding "LATIN8" WHEN untranslatable_character -- default conversion function for encoding "UTF8" to "MULE_INTERNAL" does not exist OR undefined_function -- unsupported XML feature OR feature_not_supported THEN RAISE LOG 'skip: %', SQLERRM; END $$; CREATE FUNCTION wait_for_stats () RETURNS void AS $$ DECLARE start_time timestamptz := clock_timestamp(); updated1 bool; updated2 bool; updated3 bool; updated4 bool; BEGIN -- we don't want to wait forever; loop will exit after 30 seconds FOR i IN 1..300 LOOP -- With parallel query, the seqscan and indexscan on tenk2 might be done -- in parallel worker processes, which will send their stats counters -- asynchronously to what our own session does. So we must check for -- those counts to be registered separately from the update counts. -- check to see if seqscan has been sensed SELECT (st.seq_scan >= pr.seq_scan + 1) INTO updated1 FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr WHERE st.relname = 'tenk2' AND cl.relname = 'tenk2'; -- check to see if indexscan has been sensed SELECT (st.idx_scan >= pr.idx_scan + 1) INTO updated2 FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr WHERE st.relname = 'tenk2' AND cl.relname = 'tenk2'; -- check to see if all updates have been sensed SELECT (n_tup_ins > 0) INTO updated3 FROM pg_stat_user_tables WHERE relname = 'trunc_stats_test4'; -- We must also check explicitly that pg_stat_get_snapshot_timestamp has -- advanced, because that comes from the global stats file which might -- be older than the per-DB stats file we got the other values from. SELECT (pr.snap_ts < pg_stat_get_snapshot_timestamp ()) INTO updated4 FROM prevstats AS pr; exit WHEN updated1 AND updated2 AND updated3 AND updated4; -- wait a little PERFORM pg_sleep_for ('100 milliseconds'); -- reset stats snapshot so we can test again PERFORM pg_stat_clear_snapshot(); END LOOP; -- report time waited in postmaster log (where it won't change test output) raise log 'wait_for_stats delayed % seconds', extract(epoch FROM clock_timestamp() - start_time); END $$ LANGUAGE plpgsql; DO $$ DECLARE objtype text; names text[]; args text[]; BEGIN FOR objtype IN VALUES ('table'), ('publication relation') LOOP FOR names IN VALUES ('{eins}'), ('{eins, zwei, drei}') LOOP FOR args IN VALUES ('{}'), ('{integer}') LOOP BEGIN PERFORM pg_get_object_address(objtype, names, args); EXCEPTION WHEN OTHERS THEN RAISE WARNING 'error for %,%,%: %', objtype, names, args, sqlerrm; END; END LOOP; END LOOP; END LOOP; END; $$; -- test successful cases WITH objects ( TYPE, name, args ) AS ( VALUES ('table', '{addr_nsp, gentable}'::text[], '{}'::text[]), ('index', '{addr_nsp, parttable_pkey}', '{}'), ('sequence', '{addr_nsp, gentable_a_seq}', '{}'), -- toast table ('view', '{addr_nsp, genview}', '{}'), -- large object ('operator', '{+}', '{int4, int4}'), -- database -- tablespace ('foreign-data wrapper', '{addr_fdw}', '{}'), -- extension -- event trigger ('policy', '{addr_nsp, gentable, genpol}', '{}'), ('statistics object', '{addr_nsp, gentable_stat}', '{}')) SELECT (pg_identify_object (addr1.classid, addr1.objid, addr1.objsubid)).*, -- test roundtrip through pg_identify_object_as_address ROW (pg_identify_object (addr1.classid, addr1.objid, addr1.objsubid)) = ROW (pg_identify_object (addr2.classid, addr 2.objid, addr2.objsubid)) FROM objects, pg_get_object_address(TYPE, name, args) addr1, pg_identify_object_as_address(classid, objid, objsubid) ioa (typ, nms, args), pg_get_object_address(typ, nms, ioa.args) AS addr2 ORDER BY addr1.classid, addr1.objid, addr1.objsubid; --- --- Cleanup resources --- DROP FOREIGN DATA WRAPPER addr_fdw CASCADE; DROP PUBLICATION addr_pub; DROP SUBSCRIPTION addr_sub; DROP SCHEMA addr_nsp CASCADE; DROP OWNED BY regress_addr_user; DROP USER regress_addr_user; CREATE PROCEDURE insert_data (a integer, b integer) LANGUAGE SQL AS $$ INSERT INTO tbl VALUES (a); INSERT INTO tbl VALUES (b); $$; pgFormatter-4.2/t/test-files/ex55.sql000066400000000000000000000010151361326045100175000ustar00rootroot00000000000000CREATE FUNCTION job_next () RETURNS SETOF job AS $$ DECLARE id uuid; BEGIN SELECT id INTO id FROM job WHERE state = 'sched' AND scheduled >= now() ORDER BY scheduled, modified LIMIT 1 FOR UPDATE SKIP LOCKED; -- ### 1 indent lost END; $$ LANGUAGE plpgsql; SELECT * FROM ( SELECT * FROM mytable FOR UPDATE) AS ss WHERE col1 = 5; BEGIN; SELECT * FROM mytable WHERE KEY = 1 FOR NO KEY UPDATE; END; pgFormatter-4.2/t/test-files/ex56.sql000066400000000000000000000044241361326045100175100ustar00rootroot00000000000000CREATE VIEW ticket. "view_ticket_inquiry" AS SELECT i.*, ( SELECT max(tl.creation_time) FROM wf.transition_log tl WHERE tl.workflow_id = i.id AND tl.dst_station_id = 25346145527 /* this is a comment */ ) AS last_answered FROM ticket.inquiry i; CREATE FUNCTION state_update (id int4, new int4) RETURNS int4 AS $$ BEGIN INSERT INTO state (id, state, when) VALUES (id, new, CURRENT_TIMESTAMP); INSERT INTO state (id, state, when) VALUES (id, new, CURRENT_TIMESTAMP) ON CONFLICT (id) -- ### this line should not be dedented DO UPDATE SET state = excluded.state, when = excluded.when; RETURN 1; END; $$ LANGUAGE plpgsql; CREATE PROCEDURE insert_data (a integer, b integer) LANGUAGE SQL AS $$ INSERT INTO tbl VALUES (a); INSERT INTO foo AS bar DEFAULT VALUES RETURNING foo.*; INSERT INTO tbl VALUES (b) RETURNING b; $$; INSERT INTO foo AS bar DEFAULT VALUES RETURNING foo.*; CREATE TYPE foo AS enum ( 'busy', 'help' ); CREATE TYPE IF NOT EXISTS foo AS enum ( 'busy', 'help' ); CREATE TABLE IF NOT EXISTS foo ( id bigint PRIMARY KEY, /* This text will receive an extra level of indentation every time pg_format is executed */ bar text NOT NULL /* this is the end*/ ); CREATE INDEX onek_unique1 ON onek USING btree (unique1 int4_ops); CREATE INDEX IF NOT EXISTS onek_unique1 ON onek USING btree (unique1 int4_ops); CREATE STATISTICS ab1_a_b_stats ON a, b FROM ab1; CREATE STATISTICS IF NOT EXISTS ab1_a_b_stats ON a, b FROM ab1; SELECT a , b , Row_number() OVER (PARTITION BY Coalesce(c.order_id , c.id)ORDER BY c.id ASC) , e , d FROM c; CREATE TABLE sh.z ( id SERIAL NOT NULL , b SmALLINT NULL , v TEXT NULL , d TexT NULL , e TiMESTAMP NULL , f BOOLEAN NOT NULL , g BOoLEAN NOT NULL); SET TIME ZONE 'CST7CDT'; SELECT f1, f1::INTERVAL DAY TO MINUTE AS "minutes", (f1 + INTERVAL '1 month')::INTERVAL MONTH::INTERVAL YEAR AS "years" FROM interval_tbl; SELECT '19970210 173201' AT TIME ZONE 'America/New_York'; SELECT * FROM XMLTABLE('*' PASSING 'a' COLUMNS a xml PATH '.', b text PATH '.', c text PATH '"hi"', d boolean PATH '. = "a"', e integer PATH 'string-length(.)'); pgFormatter-4.2/t/test-files/ex6.sql000066400000000000000000000005771361326045100174300ustar00rootroot00000000000000SELECT attributes->'key' FROM json_test; CREATE TABLE foobar ( id integer NOT NULL, version integer NOT NULL, prev_id integer NOT NULL, prev_version integer NOT NULL, PRIMARY KEY (id, version), UNIQUE (prev_id, prev_version), FOREIGN KEY (prev_id, prev_version) REFERENCES foobar (id, version), FOREIGN KEY (id, version) REFERENCES barfoo (next_id, next_version) ); pgFormatter-4.2/t/test-files/ex7.sql000066400000000000000000000013651361326045100174250ustar00rootroot00000000000000-- From 9.4 JSON page - examples SELECT '[{"a":"foo"},{"b":"bar"},{"c":"baz"}]'::json -> 2; SELECT '{"a": {"b":"foo"}}'::json -> 'a'; SELECT '[1,2,3]'::json ->> 2; SELECT '{"a":1,"b":2}'::json ->> 'b'; SELECT '{"a": {"b":{"c": "foo"}}}'::json #> '{a,b}'; SELECT '{"a":[1,2,3],"b":[4,5,6]}'::json #>> '{a,2}'; -- From 9.4 JSON page - jsonb examples SELECT '{"a":1, "b":2}'::jsonb @> '{"b":2}'::jsonb; SELECT '{"b":2}'::jsonb <@ '{"a":1, "b":2}'::jsonb; SELECT '{"a":1, "b":2}'::jsonb ? 'b'; SELECT '{"a":1, "b":2, "c":3}'::jsonb ?| ARRAY [ 'b', 'c' ]; SELECT '{"a":1, "b":2, "c":3}'::jsonb ?| ARRAY [ 'b', 'c' ]; SELECT '["a", "b"]'::jsonb ?& ARRAY [ 'a', 'b' ]; SELECT'{"a": {"b":{"c": "foo"}}}'::json#>'{a,b}', '{"a":[1,2,3],"b":[4,5,6]}'::json#>>'{a,2}'; pgFormatter-4.2/t/test-files/ex8.sql000066400000000000000000000001671361326045100174250ustar00rootroot00000000000000select 'hello', 2+2, 'o\'grady', 0, '', count(*), 'that\'s the position you\'re in now no matter where you select it'; pgFormatter-4.2/t/test-files/ex9.sql000066400000000000000000000002471361326045100174250ustar00rootroot00000000000000-- test placeholder: perl pg_format samples/ex9.sql -p '<<(?:.*)?>>' SELECT * FROM projects WHERE projectnumber in <> and username = <>; pgFormatter-4.2/t/test-files/expected/000077500000000000000000000000001361326045100177755ustar00rootroot00000000000000pgFormatter-4.2/t/test-files/expected/ex0.sql000066400000000000000000000005251361326045100212140ustar00rootroot00000000000000SELECT a, b, c FROM tablea JOIN tableb ON (tablea.a = tableb.a) JOIN tablec ON (tablec.a = tableb.a) LEFT OUTER JOIN tabled ON (tabled.a = tableb.a) LEFT JOIN tablee ON (tabled.a = tableb.a) WHERE tablea.x = 1 AND tableb.y = 1 GROUP BY tablea.a, tablec.c ORDER BY tablea.a, tablec.c; pgFormatter-4.2/t/test-files/expected/ex1.sql000066400000000000000000000144161361326045100212210ustar00rootroot00000000000000------------------------------------------ -- This is an example SQL -- Please click the button -- or "ctrl+F" ------------------------------------------ SELECT price.col1 AS col1, price.col2 AS col2, price.col3 AS col3, max(price.col4) AS col4, max(price.col5) AS col5, max(price.col6) AS col6, max(price.col7) AS col7 FROM table_1 t1, table_2 t2 WHERE col1 = col2 AND column_1 = small_column AND column_3411 <= column_12_sup AND col1 = 'Test Run' AND column_4532 = c1.dert UNION SELECT price.col1 AS col1, price.col2 AS col2, price.col3 AS col3, max(price.col4) AS col4, max(price.col5) AS col5, max(price.col6) AS col6, /******************* * This is a block * * comment within a * * SQL statement * *******************/ max(price.col7) AS col7 FROM ( SELECT store.column1, cast(store.column2 AS integer) AS column2, -- inline comment store.columnwe34r3 AS column3, -- inline comment store.column4_prod AS column4, -- inline comment store.column5_pre_prod_first AS column5, -- inline comment substr(store.column6, 11, 1) AS column6, -- inline comment store.column7 AS column7 -- inline comment FROM ( SELECT library.column1, --------------------- -- This is a line -- -- comment in a -- -- SQL statement -- --------------------- library.column2, library.column3 -- inline comment , CASE library.column4 WHEN cheap THEN digits (library.column27) || library.column28 ELSE 123456 END AS column4, CASE library.column5 WHEN expensive THEN digits (library.column27) || library.column28 ELSE 123456 END AS library.column6, CASE column7 WHEN free THEN digits (library.column27) || library.column28 ELSE 123456 END AS column7, FROM ( SELECT integer(substr(onelibrarysales.column1, 11, 10)) AS column1, substr(onelibrarysales.column2, 21, 10) AS column2, onelibrarysales.column3, onelibrarysales.column4, substr(onelibrarysales.column5, 31, 6) AS column5, substr(onelibrarysales.column6, 37, 2) AS column6, substr(onelibrarysales.column7, 39, 6) AS column7, FROM ( SELECT alllibrarysales.column1, alllibrarysales.column2, max(alllibrarysales.column3) AS alllibrarysales.column3, max(char(alllibrarysales.column4, iso) || char(alllibrarysales.column5, iso) || digits (alllibrarysales.column6) concat(alllibrarysales.column7)) AS column5 FROM /******************* * This is a block * * comment within a * * SQL statement * *******************/ ( SELECT libraryprod.column1, libraryprod.column2, libraryprod.column3, libraryprod.column4, /******************* * This is a block * * comment within a * * SQL statement * *******************/ libraryprod.column5, libraryprod.column6, libraryprod.column7 FROM ( SELECT tv.column1, tv.column2, max(digits (tv.column3) || digits (tv.column4)) AS librarymax FROM db1.v_table1 tv WHERE tv.column1 <> 'Y' AND tv.column1 IN ('a', '1', '12', '123', ' 1234', '12345', '123456', '1234567', '12345678', '123456789', '1234567890', '1 12 123 1234 12345 123456 1234567 12345678', 'b', 'c') AND tv.column2 >= date(tv.column4) AND tv.column3 < date(tv.column15) GROUP BY tv.column1, tv.column2) AS libraryprod, db1.table2 th WHERE th.column1 = libraryprod.column1 AND th.column2 = libraryprod.column2) AS alllibrarysales GROUP BY alllibrarysales.column1, alllibrarysales.column2) AS onelibrarysales) AS library LEFT OUTER JOIN db1.v_table3 librarystat ON librarystat.column1 = library.column1 AND librarystat.column2 = library.column2 OR (librarystat.column4 = library.column4 AND librarystat.column5 = library.column5) /******************* * This is a block * * comment within a * * SQL statement * *******************/ AND (librarystat.column5 = 'I' OR librarystat.column4 = 'Gold' OR librarystat.column5 = 'Bold') AND librarystat.column6 <= 'Z74') AS x) AS price WHERE price.column1 < 'R45' OR (price.column2 = 'R46' /******************* * This is a block * * comment within a * * SQL statement * *******************/ AND price.column3 = 6) GROUP BY price.column1, price.column2, /******************* * This is a block * * comment within a * * SQL statement * *******************/ price.column3, price.column4, price.column5, price.column6, price.column7; pgFormatter-4.2/t/test-files/expected/ex10.sql000066400000000000000000000003161361326045100212730ustar00rootroot00000000000000WITH a AS ( SELECT x, y, z FROM twelve JOIN nine ON a = 2 AND b = a ), b AS ( SELECT * FROM a ) SELECT * FROM b; pgFormatter-4.2/t/test-files/expected/ex11.sql000066400000000000000000000002011361326045100212650ustar00rootroot00000000000000SELECT * FROM a, ONLY (c) JOIN b USING (id, id2) LEFT JOIN d USING (id) WHERE id > 10 AND id <= 20; pgFormatter-4.2/t/test-files/expected/ex12.sql000066400000000000000000000010211361326045100212670ustar00rootroot00000000000000SET client_encoding TO 'UTF8'; UPDATE weather SET temp_lo = temp_lo + 1, temp_hi = temp_lo + 15, prcp = DEFAULT WHERE city = 'San Francisco' AND date = '2003-07-03' RETURNING temp_lo, temp_hi, prcp; \set ON_ERROR_STOP ON SELECT * FROM ( SELECT * FROM mytable FOR UPDATE) AS ss WHERE col1 = 5; BEGIN; SELECT * FROM mytable WHERE KEY = 1 FOR NO KEY UPDATE; SAVEPOINT s; UPDATE mytable SET col1 = NULL WHERE KEY = 1; ROLLBACK TO s; pgFormatter-4.2/t/test-files/expected/ex13.sql000066400000000000000000000020371361326045100213000ustar00rootroot00000000000000WITH RECURSIVE employee_recursive ( distance, employee_name, manager_name ) AS ( SELECT 1, employee_name, manager_name FROM employee WHERE manager_name = 'Mary' UNION ALL SELECT er.distance + 1, e.employee_name, e.manager_name FROM employee_recursive er, employee e WHERE er.employee_name = e.manager_name ) SELECT distance, employee_name FROM employee_recursive; WITH RECURSIVE t ( nombre ) AS ( VALUES (2) UNION ALL SELECT 2 * nombre FROM t WHERE 2 * nombre < 100 ) SELECT nombre FROM t; CREATE FUNCTION tg_phone_bu () RETURNS TRIGGER AS $$ BEGIN IF new.slotname != old.slotname THEN DELETE FROM PHone WHERE slotname = old.slotname; INSERT INTO PHone (slotname, comment, slotlink) VALUES (new.slotname, new.comment, new.slotlink); RETURN NULL; END IF; RETURN new; END; $$ LANGUAGE plpgsql; pgFormatter-4.2/t/test-files/expected/ex14.sql000066400000000000000000000011711361326045100212770ustar00rootroot00000000000000SELECT regexp_matches('foobarbequebazilbarfbonk', '(b[^b]+)(b[^b]+)', 'g'); SELECT SUBSTRING('XY1234Z', 'Y*([0-9]{1,3})'); SELECT m.name AS mname, pname FROM manufacturers m, LATERAL get_product_names (m.id) pname; SELECT m.name AS mname, pname FROM manufacturers m LEFT JOIN LATERAL get_product_names (m.id) pname ON TRUE; WITH one AS ( SELECT 1 one ) SELECT count(one), avg(one) FROM one; SELECT * FROM a FULL JOIN b USING (c); SELECT * FROM a FULL OUTER JOIN b USING (c); CREATE TYPE jwt_token AS ( token text, field: text ); pgFormatter-4.2/t/test-files/expected/ex15.sql000066400000000000000000000016651361326045100213100ustar00rootroot00000000000000SELECT user_id, view_homepage, view_homepage_time, enter_credit_card, enter_credit_card_time FROM ( -- Get the first time each user viewed the homepage. SELECT user_id, 1 AS view_homepage, min(time) AS view_homepage_time FROM event WHERE data ->> 'type' = 'view_homepage' GROUP BY user_id) e1 LEFT JOIN LATERAL ( -- For each row, get the first time the user_id did the enter_credit_card -- event, if one exists within two weeks of view_homepage_time. SELECT 1 AS enter_credit_card, time AS enter_credit_card_time FROM event WHERE user_id = e1.user_id AND data ->> 'type' = 'enter_credit_card' AND time BETWEEN view_homepage_time AND (view_homepage_time + 1000 * 60 * 60 * 24 * 14) ORDER BY time LIMIT 1) e2 ON TRUE; pgFormatter-4.2/t/test-files/expected/ex16.sql000066400000000000000000000036501361326045100213050ustar00rootroot00000000000000SET client_encoding TO 'UTF8'; \set ON_ERROR_STOP ON CREATE TABLE new_table_test ( start_date timestamp NOT NULL, end_date timestamp NOT NULL, name varchar(40) NOT NULL CHECK (name <> ''), fk_organization_unit_id numeric(20), fk_product_id numeric(20), id numeric(20) NOT NULL PRIMARY KEY, migrated varchar(1) ); COMMENT ON TABLE new_table_test IS E'Associação dos produtos as Unidades de Estrutura responsável'; COMMENT ON COLUMN new_table_test.end_date IS E'Data fim da associação da unidade. '; COMMENT ON COLUMN new_table_test.migrated IS E'Indica se o registro foi migrado. Valores possíveis: S - Sim, N - Não.'; CREATE INDEX ni_ansu_3 ON new_table_test (fk_product_id, start_date); CREATE INDEX ni_ansu_2 ON new_table_test (fk_organization_unit_id ASC, fk_product_id DESC); CREATE INDEX ni_ansu_1 ON new_table_test (fk_product_id ASC, start_date ASC); ALTER TABLE new_table_test ADD CONSTRAINT ora2pg_ckey_fk_organization_unit_id CHECK (fk_organization_unit_id IS NOT NULL); ALTER TABLE new_table_test ADD CONSTRAINT ck_ansu_fk_org_unit_id CHECK (fk_organization_unit_id IS NOT NULL AND fk_organization_unit_id > 1000); ALTER TABLE new_table_test ADD CONSTRAINT fk_ansu_produ_id FOREIGN KEY (FK_PRODUCT_ID) REFERENCES PRODUCT (ID); CREATE TABLE test_uuid ( nom varchar(25), uid_col bytea NOT NULL DEFAULT uuid_generate_v4 () ); CREATE TABLE test_boolean ( id bigint, is_deleted boolean, is_updated boolean ); ALTER TABLE test_boolean ADD PRIMARY KEY (id, is_deleted, is_updated); CREATE TABLE table_0 ( ref_1 integer REFERENCES table_1 ON DELETE RESTRICT, ref_2 integer REFERENCES table_2 ON DELETE CASCADE, ref_3 integer REFERENCES table_3 ON DELETE SET NULL ); COMMENT ON TABLE foo.bar IS $comment$ This is a nicely formatted comment line that spans over multiple lines that should remain untouched $comment$; COMMENT ON TABLE foo2 IS 'Hello world'; pgFormatter-4.2/t/test-files/expected/ex17.sql000066400000000000000000000434731361326045100213150ustar00rootroot00000000000000-- -- PostgreSQL database dump -- -- Dumped from database version 9.6.2 -- Dumped by pg_dump version 9.6.2 SET statement_timeout = 0; SET lock_timeout = 0; SET idle_in_transaction_session_timeout = 0; SET client_encoding = 'UTF8'; SET standard_conforming_strings = ON; SET check_function_bodies = FALSE; SET client_min_messages = warning; SET row_security = OFF; -- -- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: -- CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; -- -- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: -- COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; SET search_path = public, pg_catalog; -- -- Name: add(integer, integer); Type: FUNCTION; Schema: public; Owner: gilles -- CREATE FUNCTION ADD (integer, integer) RETURNS integer LANGUAGE sql IMMUTABLE STRICT AS $_$ SELECT $1 + $2; $_$; ALTER FUNCTION public.add (integer, integer) OWNER TO gilles; -- -- Name: check_password(text, text, text, text, text, text, text, text, text, text, text, text, text, text, text, text, text, text); Type: FUNCTION; Schema: public; Owner: gilles -- CREATE FUNCTION check_password (uname1 text, pass1 text, uname2 text, pass2 text, uname3 text, pass3 text, uname4 text, pass4 text, uname5 text, pass5 text, uname6 text, pass6 text, uname7 text, pass7 text, uname8 text, pass8 text, uname9 text, pass9 text) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER SET search_path TO admin, pg_temp AS $_$ DECLARE passed boolean; BEGIN SELECT (pwd = $2) INTO passed FROM pwds WHERE username = $1; RETURN passed; END; $_$; ALTER FUNCTION public.check_password (uname1 text, pass1 text, uname2 text, pass2 text, uname3 text, pass3 text, uname4 text, pass4 text, uname5 text, pass5 text, uname6 text, pass6 text, uname7 text, pass7 text, uname8 text, pass8 text, uname9 text, pass9 text) OWNER TO gilles; -- -- Name: dup(integer); Type: FUNCTION; Schema: public; Owner: gilles -- CREATE FUNCTION dup (integer, OUT f1 integer, OUT f2 text) RETURNS record LANGUAGE sql AS $_$ SELECT $1, CAST($1 AS text) || ' is text' $_$; ALTER FUNCTION public.dup (integer, OUT f1 integer, OUT f2 text) OWNER TO gilles; -- -- Name: increment(integer); Type: FUNCTION; Schema: public; Owner: gilles -- CREATE FUNCTION INCREMENT (i integer) RETURNS integer LANGUAGE plpgsql AS $$ BEGIN RETURN i + 1; END; $$; ALTER FUNCTION public.increment (i integer) OWNER TO gilles; -- -- Name: peuple_stock(integer, integer); Type: FUNCTION; Schema: public; Owner: userdb -- CREATE FUNCTION peuple_stock (annee_debut integer, annee_fin integer) RETURNS bigint LANGUAGE plpgsql AS $$ DECLARE v_annee integer; v_nombre integer; v_contenant_id integer; v_vin_id integer; compteur bigint := 0; annees integer; contenants integer; vins integer; tuples_a_generer integer; BEGIN -- vider la table de stock TRUNCATE TABLE stock; -- calculer le nombre d'annees SELECT (annee_fin - annee_debut) + 1 INTO annees; -- nombre de contenants SELECT count(*) FROM contenant INTO contenants; -- nombre de vins SELECT count(*) FROM vin INTO vins; -- calcul des combinaisons SELECT annees * contenants * vins INTO tuples_a_generer; --on boucle sur tous les millesimes: disons 1930 a 2000 -- soit 80 annees FOR v_annee IN annee_debut..annee_fin LOOP -- on boucle sur les contenants possibles FOR v_contenant_id IN 1..contenants LOOP -- idem pour l'id du vin FOR v_vin_id IN 1..vins LOOP -- on prends un nombre de bouteilles compris entre 6 et 18 SELECT round(random() * 12) + 6 INTO v_nombre; -- insertion dans la table de stock INSERT INTO stock (vin_id, contenant_id, annee, nombre) VALUES (v_vin_id, v_contenant_id, v_annee, v_nombre); IF (((compteur % 1000) = 0) OR (compteur = tuples_a_generer)) THEN RAISE notice 'stock : % sur % tuples generes', compteur, tuples_a_generer; END IF; compteur := compteur + 1; END LOOP; --fin boucle vin END LOOP; -- fin boucle contenant END LOOP; --fin boucle annee RETURN compteur; END; $$; ALTER FUNCTION public.peuple_stock (annee_debut integer, annee_fin integer) OWNER TO userdb; -- -- Name: peuple_vin(); Type: FUNCTION; Schema: public; Owner: userdb -- CREATE FUNCTION peuple_vin () RETURNS bigint LANGUAGE plpgsql AS $$ DECLARE v_recoltant_id integer; v_appellation_id integer; v_type_vin_id integer; recoltants integer; appellations integer; types_vins integer; tuples_a_generer integer; compteur bigint := 0; BEGIN -- vider la table de stock, qui depend de vin, puis vin DELETE FROM stock; DELETE FROM vin; -- compter le nombre de recoltants SELECT count(*) FROM recoltant INTO recoltants; -- compter le nombre d'appellations SELECT count(*) FROM appellation INTO appellations; -- compter le nombre de types de vins SELECT count(*) FROM type_vin INTO types_vins; -- calculer le nombre de combinaisons possibles SELECT (recoltants * appellations * types_vins) INTO tuples_a_generer; --on boucle sur tous les recoltants FOR v_recoltant_id IN 1..recoltants LOOP -- on boucle sur les appelations FOR v_appellation_id IN 1..appellations LOOP -- on boucle sur les types de vins FOR v_type_vin_id IN 1..types_vins LOOP -- insertion dans la table de vin INSERT INTO vin (recoltant_id, appellation_id, type_vin_id) VALUES (v_recoltant_id, v_appellation_id, v_type_vin_id); IF (((compteur % 1000) = 0) OR (compteur = tuples_a_generer)) THEN RAISE notice 'vins : % sur % tuples generes', compteur, tuples_a_generer; END IF; compteur := compteur + 1; END LOOP; --fin boucle type vin END LOOP; -- fin boucle appellations END LOOP; --fin boucle recoltants RETURN compteur; END; $$; ALTER FUNCTION public.peuple_vin () OWNER TO userdb; -- -- Name: trous_stock(); Type: FUNCTION; Schema: public; Owner: userdb -- CREATE FUNCTION trous_stock () RETURNS bigint LANGUAGE plpgsql AS $$ DECLARE stock_total integer; echantillon integer; vins_disponibles integer; contenants_disponibles integer; v_vin_id integer; v_contenant_id integer; v_tuples bigint := 0; annee_min integer; annee_max integer; v_annee integer; BEGIN -- on compte le nombre de tuples dans stock SELECT count(*) FROM stock INTO stock_total; RAISE NOTICE 'taille du stock %', stock_total; -- on calcule la taille de l'echantillon a -- supprimer de la table stock SELECT round(stock_total / 10) INTO echantillon; RAISE NOTICE 'taille de l''echantillon %', echantillon; -- on compte le nombre de vins disponibles SELECT count(*) FROM vin INTO vins_disponibles; RAISE NOTICE '% vins disponibles', vins_disponibles; -- on compte le nombre de contenants disponibles SELECT count(*) FROM contenant INTO contenants_disponibles; RAISE NOTICE '% contenants disponibles', contenants_disponibles; -- on recupere les bornes min/max de annees SELECT min(annee), max(annee) FROM stock INTO annee_min, annee_max; -- on fait une boucle correspondant a 1% des tuples -- de la table stock FOR v_tuples IN 1..echantillon LOOP -- selection d'identifiant, au hasard --select round(random()*contenants_disponibles) into v_contenant_id; v_contenant_id := round(random() * contenants_disponibles); --select round(random()*vins_disponibles) into v_vin_id; v_vin_id := round(random() * vins_disponibles); v_annee := round(random() * (annee_max - annee_min)) + (annee_min); -- si le tuple est deja efface, ce n'est pas grave.. DELETE FROM stock WHERE contenant_id = v_contenant_id AND vin_id = v_vin_id AND annee = v_annee; IF (((v_tuples % 100) = 0) OR (v_tuples = echantillon)) THEN RAISE notice 'stock : % sur % echantillon effaces', v_tuples, echantillon; END IF; END LOOP; --fin boucle v_tuples RETURN echantillon; END; $$; ALTER FUNCTION public.trous_stock () OWNER TO userdb; -- -- Name: trous_vin(); Type: FUNCTION; Schema: public; Owner: userdb -- CREATE FUNCTION trous_vin () RETURNS bigint LANGUAGE plpgsql AS $$ DECLARE vin_total integer; echantillon integer; v_vin_id integer; v_tuples bigint := 0; v_annee integer; BEGIN -- on compte le nombre de tuples dans vin SELECT count(*) FROM vin INTO vin_total; RAISE NOTICE '% vins disponibles', vin_total; -- on calcule la taille de l'echantillon a -- supprimer de la table vin SELECT round(vin_total / 10) INTO echantillon; RAISE NOTICE 'taille de l''echantillon %', echantillon; -- on fait une boucle correspondant a 10% des tuples -- de la table vin FOR v_tuples IN 1..echantillon LOOP -- selection d'identifiant, au hasard v_vin_id := round(random() * vin_total); -- si le tuple est deja efface, ce n'est pas grave.. -- TODO remplacer ce delete par un trigger on delete cascade -- voir dans druid le schema??? DELETE FROM stock WHERE vin_id = v_vin_id; DELETE FROM vin WHERE id = v_vin_id; IF (((v_tuples % 100) = 0) OR (v_tuples = echantillon)) THEN RAISE notice 'vin : % sur % echantillon effaces', v_tuples, echantillon; END IF; END LOOP; --fin boucle v_tuples RETURN echantillon; END; $$; ALTER FUNCTION public.trous_vin () OWNER TO userdb; SET default_tablespace = ''; SET default_with_oids = FALSE; -- -- Name: appellation; Type: TABLE; Schema: public; Owner: userdb -- CREATE TABLE appellation ( id integer NOT NULL, libelle text NOT NULL, region_id integer ) WITH ( autovacuum_enabled = OFF ); ALTER TABLE appellation OWNER TO userdb; -- -- Name: appellation_id_seq; Type: SEQUENCE; Schema: public; Owner: userdb -- CREATE SEQUENCE appellation_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE appellation_id_seq OWNER TO userdb; -- -- Name: appellation_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: userdb -- ALTER SEQUENCE appellation_id_seq OWNED BY appellation.id; -- -- Name: contenant; Type: TABLE; Schema: public; Owner: userdb -- CREATE TABLE contenant ( id integer NOT NULL, contenance real NOT NULL, libelle text ) WITH ( autovacuum_enabled = OFF, fillfactor = '20' ); ALTER TABLE contenant OWNER TO userdb; -- -- Name: contenant_id_seq; Type: SEQUENCE; Schema: public; Owner: userdb -- CREATE SEQUENCE contenant_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE contenant_id_seq OWNER TO userdb; -- -- Name: contenant_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: userdb -- ALTER SEQUENCE contenant_id_seq OWNED BY contenant.id; -- -- Name: recoltant; Type: TABLE; Schema: public; Owner: userdb -- CREATE TABLE recoltant ( id integer NOT NULL, nom text, adresse text ) WITH ( autovacuum_enabled = OFF ); ALTER TABLE recoltant OWNER TO userdb; -- -- Name: recoltant_id_seq; Type: SEQUENCE; Schema: public; Owner: userdb -- CREATE SEQUENCE recoltant_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE recoltant_id_seq OWNER TO userdb; -- -- Name: recoltant_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: userdb -- ALTER SEQUENCE recoltant_id_seq OWNED BY recoltant.id; -- -- Name: region; Type: TABLE; Schema: public; Owner: userdb -- CREATE TABLE region ( id integer NOT NULL, libelle text NOT NULL ) WITH ( autovacuum_enabled = OFF ); ALTER TABLE region OWNER TO userdb; -- -- Name: region_id_seq; Type: SEQUENCE; Schema: public; Owner: userdb -- CREATE SEQUENCE region_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE region_id_seq OWNER TO userdb; -- -- Name: region_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: userdb -- ALTER SEQUENCE region_id_seq OWNED BY region.id; -- -- Name: stock; Type: TABLE; Schema: public; Owner: userdb -- CREATE TABLE stock ( vin_id integer NOT NULL, contenant_id integer NOT NULL, annee integer NOT NULL, nombre integer NOT NULL ) WITH ( autovacuum_enabled = OFF ); ALTER TABLE stock OWNER TO userdb; -- -- Name: type_vin; Type: TABLE; Schema: public; Owner: userdb -- CREATE TABLE type_vin ( id integer NOT NULL, libelle text NOT NULL ) WITH ( autovacuum_enabled = OFF ); ALTER TABLE type_vin OWNER TO userdb; -- -- Name: type_vin_id_seq; Type: SEQUENCE; Schema: public; Owner: userdb -- CREATE SEQUENCE type_vin_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE type_vin_id_seq OWNER TO userdb; -- -- Name: type_vin_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: userdb -- ALTER SEQUENCE type_vin_id_seq OWNED BY type_vin.id; -- -- Name: vin; Type: TABLE; Schema: public; Owner: userdb -- CREATE TABLE vin ( id integer NOT NULL, recoltant_id integer, appellation_id integer NOT NULL, type_vin_id integer NOT NULL ) WITH ( autovacuum_enabled = OFF ); ALTER TABLE vin OWNER TO userdb; -- -- Name: vin_id_seq; Type: SEQUENCE; Schema: public; Owner: userdb -- CREATE SEQUENCE vin_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE vin_id_seq OWNER TO userdb; -- -- Name: vin_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: userdb -- ALTER SEQUENCE vin_id_seq OWNED BY vin.id; -- -- Name: appellation id; Type: DEFAULT; Schema: public; Owner: userdb -- ALTER TABLE ONLY appellation ALTER COLUMN id SET DEFAULT nextval('appellation_id_seq'::regclass); -- -- Name: contenant id; Type: DEFAULT; Schema: public; Owner: userdb -- ALTER TABLE ONLY contenant ALTER COLUMN id SET DEFAULT nextval('contenant_id_seq'::regclass); -- -- Name: recoltant id; Type: DEFAULT; Schema: public; Owner: userdb -- ALTER TABLE ONLY recoltant ALTER COLUMN id SET DEFAULT nextval('recoltant_id_seq'::regclass); -- -- Name: region id; Type: DEFAULT; Schema: public; Owner: userdb -- ALTER TABLE ONLY region ALTER COLUMN id SET DEFAULT nextval('region_id_seq'::regclass); -- -- Name: type_vin id; Type: DEFAULT; Schema: public; Owner: userdb -- ALTER TABLE ONLY type_vin ALTER COLUMN id SET DEFAULT nextval('type_vin_id_seq'::regclass); -- -- Name: vin id; Type: DEFAULT; Schema: public; Owner: userdb -- ALTER TABLE ONLY vin ALTER COLUMN id SET DEFAULT nextval('vin_id_seq'::regclass); -- -- Name: appellation appellation_libelle_key; Type: CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY appellation ADD CONSTRAINT appellation_libelle_key UNIQUE (libelle); -- -- Name: appellation appellation_pkey; Type: CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY appellation ADD CONSTRAINT appellation_pkey PRIMARY KEY (id); -- -- Name: contenant contenant_pkey; Type: CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY contenant ADD CONSTRAINT contenant_pkey PRIMARY KEY (id); -- -- Name: recoltant recoltant_pkey; Type: CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY recoltant ADD CONSTRAINT recoltant_pkey PRIMARY KEY (id); -- -- Name: region region_pkey; Type: CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY region ADD CONSTRAINT region_pkey PRIMARY KEY (id); -- -- Name: stock stock_pkey; Type: CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY stock ADD CONSTRAINT stock_pkey PRIMARY KEY (vin_id, contenant_id, annee); -- -- Name: type_vin type_vin_pkey; Type: CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY type_vin ADD CONSTRAINT type_vin_pkey PRIMARY KEY (id); -- -- Name: vin vin_pkey; Type: CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY vin ADD CONSTRAINT vin_pkey PRIMARY KEY (id); -- -- Name: appellation appellation_region_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY appellation ADD CONSTRAINT appellation_region_id_fkey FOREIGN KEY (region_id) REFERENCES region (id) ON DELETE CASCADE; -- -- Name: stock stock_contenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY stock ADD CONSTRAINT stock_contenant_id_fkey FOREIGN KEY (contenant_id) REFERENCES contenant (id) ON DELETE CASCADE; -- -- Name: stock stock_vin_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY stock ADD CONSTRAINT stock_vin_id_fkey FOREIGN KEY (vin_id) REFERENCES vin (id) ON DELETE CASCADE; -- -- Name: vin vin_appellation_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY vin ADD CONSTRAINT vin_appellation_id_fkey FOREIGN KEY (appellation_id) REFERENCES appellation (id) ON DELETE CASCADE; -- -- Name: vin vin_recoltant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY vin ADD CONSTRAINT vin_recoltant_id_fkey FOREIGN KEY (recoltant_id) REFERENCES recoltant (id) ON DELETE CASCADE; -- -- Name: vin vin_type_vin_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: userdb -- ALTER TABLE ONLY vin ADD CONSTRAINT vin_type_vin_id_fkey FOREIGN KEY (type_vin_id) REFERENCES type_vin (id) ON DELETE CASCADE; -- -- PostgreSQL database dump complete -- pgFormatter-4.2/t/test-files/expected/ex18.sql000066400000000000000000000005541361326045100213070ustar00rootroot00000000000000GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA foo TO role_bar, role_baz; SELECT * FROM t WHERE a IS NOT DISTINCT FROM b; -- Deploy schemas/custom/grants/grant_schema_to_authenticated to pg -- requires: schemas/custom/schema BEGIN; GRANT USAGE ON SCHEMA custom TO authenticated; GRANT USAGE ON SCHEMA custom TO authenticated; COMMIT; pgFormatter-4.2/t/test-files/expected/ex19.sql000066400000000000000000000070541361326045100213120ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION toastcheck_writer (text) RETURNS void LANGUAGE plpgsql AS $ff$ DECLARE func text; funcname text; attrecord record; pkc record; indent text; colrec record; pkcols text; pkformat text; detoast_funcname text; pk_col_ary text[]; BEGIN pkcols = ''; pkformat = ''; pk_col_ary = '{}'; funcname = 'toastcheck__' || $1; FOR pkc IN EXECUTE $f$ SELECT attname FROM pg_attribute JOIN pg_class ON (oid = attrelid) JOIN pg_index on (pg_class.oid = pg_index.indrelid and attnum = any (indkey)) WHERE pg_class.oid = '$f$ || $1 || $f$ '::regclass and indisprimary $f$ LOOP IF pkcols = '' THEN pkcols = quote_ident(pkc.attname); pkformat = '%'; ELSE pkcols = pkcols || ', ' || quote_ident(pkc.attname); pkformat = pkformat || ', %'; END IF; pk_col_ary = array_append(pk_col_ary, quote_ident(pkc.attname)); END LOOP; /* * This is the function header. It's basically a constant string, with the * table name replaced a couple of times and the primary key columns replaced * once. Make sure we don't fail if there's no primary key. */ IF pkcols <> '' THEN pkcols = ', ' || pkcols; pkformat = ', PK=( ' || pkformat || ' )'; END IF; func = $f$ CREATE OR REPLACE FUNCTION $f$ || funcname || $f$() RETURNS void LANGUAGE plpgsql AS $$ DECLARE rec record; BEGIN FOR rec IN SELECT ctid $f$ || pkcols || $f$ FROM $f$ || $1 || $f$ LOOP DECLARE f record; l int; BEGIN SELECT * INTO f FROM $f$ || $1 || $f$ WHERE ctid = rec.ctid; -- make sure each column is detoasted and reported separately $f$; /* We now need one exception block per toastable column */ indent = ' '; FOR attrecord IN SELECT attname, atttypid FROM pg_attribute JOIN pg_class ON (oid = attrelid) WHERE pg_class.oid = $1::regclass AND attlen = - 1 LOOP func := func || indent || E'BEGIN\n'; IF attrecord.atttypid = 'numeric'::regtype THEN detoast_funcname = 'numeric_sign'; ELSE detoast_funcname = 'length'; END IF; func := func || indent || $f$ SELECT $f$ || detoast_funcname || $f$(f.$f$ || quote_ident(attrecord.attname) || E') INTO l;\n'; /* The interesting part here needs some replacement of the PK columns */ func := func || indent || $f$EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'TID %$f$ || pkformat || $f$, column "$f$ || attrecord.attname || $f$": exception {{%}}', rec.ctid, $f$; /* This iterates zero times if there are no PK columns */ FOR colrec IN SELECT f.i[a] AS pknm FROM ( SELECT pk_col_ary AS i) AS f, generate_series(array_lower(pk_col_ary, 1), array_upper(pk_col_ary, 1)) AS a LOOP func := func || $f$ rec.$f$ || colrec.pknm || $f$, $f$; END LOOP; func := func || E'sqlerrm;\n'; func := func || indent || E'END;\n'; END LOOP; /* And this is our constant footer */ func := func || $f$ END; END LOOP; END; $$; $f$; EXECUTE func; RAISE NOTICE $f$Successfully created function %()$f$, funcname; RETURN; END; $ff$; pgFormatter-4.2/t/test-files/expected/ex2.sql000066400000000000000000000017321361326045100212170ustar00rootroot00000000000000SELECT n.nspname AS "Schema", p.proname AS "Name", pg_catalog.pg_get_function_result(p.oid) AS "Result data type", pg_catalog.pg_get_function_arguments(p.oid) AS "Argument data types", CASE WHEN p.proisagg THEN 'agg' WHEN p.proiswindow THEN 'window' WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN 'trigger' ELSE 'normal' END AS "Type" FROM pg_catalog.pg_proc p LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace WHERE p.proname ~ '^(version)$' AND pg_catalog.pg_function_is_visible(p.oid) ORDER BY 1, 2, 4; SELECT CASE WHEN (FALSE) THEN 0 WHEN (TRUE) THEN 2 END AS dummy1 FROM my_table; CREATE OR REPLACE FUNCTION task_job_maint_after () RETURNS TRIGGER AS $$ BEGIN CASE new.state WHEN 'final' THEN NOTIFY task_job_final; ELSE NULL; END CASE; RETURN NEW; END; $$ LANGUAGE plpgsql; pgFormatter-4.2/t/test-files/expected/ex20.sql000066400000000000000000000006641361326045100213020ustar00rootroot00000000000000SELECT group_concat (k.column_name ORDER BY k.ordinal_position) AS column_names, t.table_name AS table_name, t.table_schema AS table_schema, t.constraint_name AS constraint_name FROM information_schema.table_constraints t LEFT JOIN information_schema.key_column_usage k USING (constraint_name, table_schema, table_name) WHERE t.constraint_type = 'PRIMARY KEY' GROUP BY t.table_schema, t.table_name; pgFormatter-4.2/t/test-files/expected/ex21.sql000066400000000000000000000002351361326045100212750ustar00rootroot00000000000000SELECT 1 AS a \gset \echo :a SELECT :a; \unset a SELECT 'test' \g testfile.txt \! cat testfile.txt SELECT CURRENT_TIMESTAMP; \watch 3 pgFormatter-4.2/t/test-files/expected/ex22.sql000066400000000000000000000031371361326045100213020ustar00rootroot00000000000000SELECT code.code, properties_code.valeur, properties_code1.valeur, properties_code2.valeur FROM code INNER JOIN linkcode ON (code.id_code = linkcode.id_codeparent) INNER JOIN linkcode linkcode1 ON (linkcode.id_codeenfant = linkcode1.id_codeparent) INNER JOIN code code1 ON (linkcode1.id_codeenfant = code1.id_code) INNER JOIN properties_code ON (code1.id_code = properties_code.id_code) INNER JOIN properties_code properties_code1 ON (code1.id_code = properties_code1.id_code) INNER JOIN properties_code properties_code2 ON (code1.id_code = properties_code2.id_code) INNER JOIN properties ON (properties_code.id_propriete = properties.id_propriete) INNER JOIN properties properties1 ON (properties_code1.id_propriete = properties1.id_propriete) INNER JOIN properties properties2 ON (properties_code2.id_propriete = properties2.id_propriete) INNER JOIN variables ON (properties.id_variable = variables.id_variable) INNER JOIN variables variables1 ON (properties1.id_variable = variables1.id_variable) INNER JOIN variables variables2 ON (properties2.id_variable = variables2.id_variable) INNER JOIN mvt_temps ON (code.id_code = mvt_temps.id_code) INNER JOIN transactions ON (mvt_temps.id_transaction = transactions.id_transaction) INNER JOIN products ON (code.id_product = products.id_product) WHERE variables.name = 'etat_DEC_BPO_sMacAdresse' AND variables1.name = 'variable_name' AND variables2.name = 'variable_name' AND transactions.code = 'XXXXXXXXXXXXX' AND mvt_temps.statutok = TRUE AND products.codeproduct = '123456789' pgFormatter-4.2/t/test-files/expected/ex23.sql000066400000000000000000000002141361326045100212740ustar00rootroot00000000000000INSERT INTO task_item (task_item_id, task_item_description, priority, on_hold, task_type) VALUES (1, 'a desc', 'low', 't', 'an item'); pgFormatter-4.2/t/test-files/expected/ex24.sql000066400000000000000000000004371361326045100213040ustar00rootroot00000000000000SELECT id, count(test), CASE WHEN TRUE THEN 1 END AS looks_good, count( CASE WHEN TRUE THEN 1 END) AS looks_wrong_and_indent_is_off, count( CASE WHEN FALSE THEN 1 END) AS looks_wrong FROM test; pgFormatter-4.2/t/test-files/expected/ex25.sql000066400000000000000000000001641361326045100213020ustar00rootroot00000000000000CREATE TABLE demo.test ( foo text, bar timestamp WITH time zone, baz text ) WITH ( OIDS = FALSE ); pgFormatter-4.2/t/test-files/expected/ex26.sql000066400000000000000000000004301361326045100212770ustar00rootroot00000000000000PREPARE demo AS INSERT INTO demo VALUES (1, 2, 3, 4); PREPARE demo AS SELECT * FROM demo WHERE id IN (1, 2, 3, 4); PREPARE demo AS UPDATE demo SET lbl = 'unknown' WHERE id IN (1, 2, 3, 4); PREPARE demo AS DELETE FROM demo WHERE id IN (1, 2, 3, 4); pgFormatter-4.2/t/test-files/expected/ex27.sql000066400000000000000000000066001361326045100213050ustar00rootroot00000000000000SELECT opgt.part_id, opgt.art_id, rGpe.slot_id, rGpe.grp_art_id FROM ( SELECT id, part_product_kind_id FROM part_product ldm WHERE nb_colis > 0 AND statut_part_product IN ('DISPATCH', 'CANCELED', 'SALE_PROD') AND ldm.art_frect_id IS NULL AND NOT EXISTS ( SELECT subparts.id FROM part_product subparts WHERE ldm.id = subparts.part_product_parent_id)) ldm JOIN LATERAL ( SELECT opgt.id, part.id AS part_id, opgt.art_id FROM part_product part JOIN part_product_kind opgt ON opgt.id = part.part_product_kind_id WHERE part.id = ldm.id AND opgt.plateforme_distribution_id = ($1) AND opgt.day_export_shop_id IN ( SELECT id FROM day_export_shop jem WHERE day <= to_timestamp($2) AND day > CURRENT_DATE - interval '4 mons')) opgt ON TRUE JOIN LATERAL ( SELECT r.id AS slot_id, ga.id AS grp_art_id FROM part_product_kind lm JOIN art a ON a.id = lm.art_id JOIN grp_art ga ON ga.id = a.grp_art_id JOIN slot r ON r.id = ga.slot_id WHERE opgt.id = lm.id) rGpe ON TRUE UNION SELECT opgt.part_id, opgt.art_id, rGpe.slot_id, rGpe.grp_art_id FROM ( SELECT id, art_frect_id, part_product_kind_id FROM part_product ldm WHERE nb_colis > 0 AND statut_part_product IN ('DISPATCH', 'CANCELED', 'SALE_PROD') AND art_frect_id IS NOT NULL AND NOT EXISTS ( SELECT subparts.id FROM part_product subparts WHERE ldm.id = subparts.part_product_parent_id)) ldm JOIN LATERAL ( SELECT opgt.id, part.id AS part_id, opgt.art_id FROM part_product part JOIN part_product_kind opgt ON opgt.id = part.part_product_kind_id WHERE part.id = ldm.id AND opgt.plateforme_distribution_id = ($3) AND opgt.day_export_shop_id IN ( SELECT id FROM day_export_shop jem WHERE day <= to_timestamp($4) AND day > CURRENT_DATE - interval '4 mons')) opgt ON TRUE JOIN LATERAL ( SELECT r.id AS slot_id, ga.id AS grp_art_id FROM part_product lm JOIN art a ON a.id = lm.art_frect_id JOIN grp_art ga ON ga.id = a.grp_art_id JOIN slot r ON r.id = ga.slot_id WHERE opgt.id = lm.id) rGpe ON TRUE; SELECT "INSTANCE_STATE_ID", "FORM_ID" FROM "INSTANCE" INNER JOIN "FORM" ON "INSTANCE"."FORM_ID" = "FORM"."FORM_ID" LEFT JOIN "FORM_T" ON "FORM"."FORM_ID" = "FORM_T"."FORM_ID" AND "FORM_T"."LANGUAGE" = 'de' INNER JOIN "INSTANCE_STATE" ON "INSTANCE_STATE"."INSTANCE_STATE_ID" = "INSTANCE"."INSTANCE_STATE_ID" LEFT JOIN "INSTANCE_STATE_T" ON "INSTANCE_STATE"."INSTANCE_STATE_ID" = "INSTANCE_STATE_T"."INSTANCE_STATE_ID" pgFormatter-4.2/t/test-files/expected/ex28.sql000066400000000000000000000003231361326045100213020ustar00rootroot00000000000000BEGIN; CREATE FUNCTION basename (path text) RETURNS text AS $$ return path.replace(/.*\//, ''); $$ LANGUAGE 'plv8' IMMUTABLE; COMMIT; UPDATE article a SET title = 'x' WHERE (a.perm & 8)::bool pgFormatter-4.2/t/test-files/expected/ex29.sql000066400000000000000000000034271361326045100213130ustar00rootroot00000000000000SELECT DISTINCT (current_database())::information_schema.sql_identifier AS view_catalog, (nv.nspname)::information_schema.sql_identifier AS view_schema, (v.relname)::information_schema.sql_identifier AS view_name, (current_database())::information_schema.sql_identifier AS table_catalog, (nt.nspname)::information_schema.sql_identifier AS table_schema, (t.relname)::information_schema.sql_identifier AS table_name FROM pg_namespace nv, pg_class v, pg_depend dv, pg_depend dt, pg_class t, pg_namespace nt WHERE ((((((((((((((nv.oid = v.relnamespace) AND (v.relkind = 'v'::"char")) AND (v.oid = dv.refobjid)) AND (dv.refclassid = ('pg_class'::regclass)::oid)) AND (dv.classid = ('pg_rewrite'::regclass)::oid)) AND (dv.deptype = 'i'::"char")) AND (dv.objid = dt.objid)) AND (dv.refobjid <> dt.refobjid)) AND (dt.classid = ('pg_rewrite'::regclass)::oid)) AND (dt.refclassid = ('pg_class'::regclass)::oid)) AND (dt.refobjid = t.oid)) AND (t.relnamespace = nt.oid)) AND (t.relkind = ANY (ARRAY['r'::"char", 'v'::"char"]))) AND pg_has_role(t.relowner, 'USAGE'::text)) ORDER BY (current_database())::information_schema.sql_identifier, (nv.nspname)::information_schema.sql_identifier, (v.relname)::information_schema.sql_identifier, (current_database())::information_schema.sql_identifier, (nt.nspname)::information_schema.sql_identifier, (t.relname)::information_schema.sql_identifier; pgFormatter-4.2/t/test-files/expected/ex3.sql000066400000000000000000000005731361326045100212220ustar00rootroot00000000000000CREATE TABLE test ( col text ); INSERT INTO test VALUES ('123'); CREATE FUNCTION fonction_reference (refcursor) RETURNS refcursor AS $$ BEGIN OPEN $1 FOR SELECT col FROM test; RETURN $1; END; $$ LANGUAGE plpgsql; BEGIN; SELECT fonction_reference ('curseur_fonction'); FETCH ALL IN curseur_fonction; COMMIT; pgFormatter-4.2/t/test-files/expected/ex30.sql000066400000000000000000000035521361326045100213020ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION foo () RETURNS TRIGGER AS $$ BEGIN IF NEW.role NOT IN ( SELECT rolname FROM pg_authid) THEN RAISE EXCEPTION 'role % does not exist.', NEW.role; END IF; END; $$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION plpython_demo () RETURNS void AS $$ from this import s, d for char in s: print(d.get(char, char), end="") print() $$ LANGUAGE 'plpython3u'; CREATE OR REPLACE FUNCTION plpython_demo2 () RETURNS void LANGUAGE 'plpython3u' AS $body$ from this import u, f for char in u: print(f.get(char, char), end="") print() $body$; CREATE FUNCTION ADD (integer, integer) RETURNS integer LANGUAGE sql IMMUTABLE STRICT AS $_$ SELECT $1 + $2; $_$; CREATE FUNCTION dup (integer, OUT f1 integer, OUT f2 text) RETURNS record LANGUAGE sql AS $_$ SELECT $1, CAST($1 AS text) || ' is text' $_$; CREATE TABLE IF NOT EXISTS foo ( id bigint PRIMARY KEY, /* This text will receive an extra level of indentation every time pg_format is executed */ bar text NOT NULL /* this is the end*/ ); COMMENT ON TABLE xx.yy IS 'Line 1 - Line 2 - Line 3'; CREATE TABLE IF NOT EXISTS foo ( /******************************************************* * This text will receive an extra level of indentation * * every time pg_format is executed * ********************************************************/ id bigint PRIMARY KEY, /* This text will receive an extra level of indentation every time pg_format is executed */ bar text NOT NULL /* this is the end*/ ); ALTER TABLE app_public.users ENABLE ROW LEVEL SECURITY; COMMENT ON FUNCTION my_function () IS 'Here is my function that has a comment; will this become a sql clause or statement?'; pgFormatter-4.2/t/test-files/expected/ex31.sql000066400000000000000000000026271361326045100213050ustar00rootroot00000000000000CREATE PROCEDURE insert_data (a integer, b integer) LANGUAGE SQL AS $$ INSERT INTO tbl VALUES (a); INSERT INTO tbl VALUES (b); $$; CALL insert_data (1, 2); ALTER PROCEDURE insert_data1 (integer, integer) RENAME TO insert_record; ALTER PROCEDURE insert_data2 (integer, integer) OWNER TO joe; ALTER PROCEDURE insert_data3 (integer, integer) SET SCHEMA accounting; ALTER PROCEDURE insert_data4 (integer, integer) DEPENDS ON EXTENSION myext; ALTER PROCEDURE check_password1 (text) SET search_path = admin, pg_temp; ALTER PROCEDURE check_password2 (text) RESET search_path; ALTER ROUTINE foo1 (integer) RENAME TO foobar; ALTER ROUTINE foo2 (integer, varchar(255)) OWNER TO ufoo; ALTER ROUTINE foo3 (integer, varchar(255), boolean) SET SCHEMA sfoo; ALTER ROUTINE foo4 (varchar(25), integer) DEPENDS ON EXTENSION fooext; ALTER ROUTINE foo5 (integer) IMMUTABLE; ALTER ROUTINE foo6 (integer) SECURITY INVOKER; ALTER ROUTINE foo7 (integer) RESET ALL; ALTER ROUTINE foo8 (integer) SET work_mem = '1GB'; ALTER ROUTINE foo9 (integer) SET work_mem FROM CURRENT; CREATE PUBLICATION mypublication1 FOR TABLE ONLY emps; CREATE PUBLICATION mypublication2 FOR TABLE users, departments; CREATE PUBLICATION alltables FOR ALL TABLES; CREATE PUBLICATION insert_only FOR TABLE mydata WITH (publish = 'insert'); SELECT my_func (p_type_cd => t.type_cd) AS my_func_result FROM tab t WHERE t.id = 12; pgFormatter-4.2/t/test-files/expected/ex32.sql000066400000000000000000000011251361326045100212760ustar00rootroot00000000000000CREATE TABLE projects ( id uuid PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4 (), name citext NOT NULL CHECK (name ~* '^[a-z0-9_-]{3,255}$'), owner_id uuid NOT NULL, UNIQUE (owner_id, name), PRIMARY KEY (id, name) ); CREATE TABLE projects ( id uuid PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4 (), name citext NOT NULL CHECK (name ~* '^[a-z0-9_-]{3,255}$'), owner_id uuid NOT NULL, UNIQUE name, PRIMARY KEY id ); CREATE TABLE sample ( fkey integer REFERENCES other (id) ON UPDATE CASCADE ON DELETE CASCADE, other integer, columns integer ); pgFormatter-4.2/t/test-files/expected/ex33.sql000066400000000000000000000006161361326045100213030ustar00rootroot00000000000000SELECT CASE WHEN my_col IS NOT NULL THEN 'Y' ELSE 'N' END AS my_new_col, CASE WHEN TRIM(my_other_col) = 'confirmed' THEN 'Y' ELSE 'N' END AS new_col FROM my_table; SELECT DISTINCT ON (a, b) a, b, c FROM d ORDER BY a, b, c; SELECT DISTINCT a, b, b, c FROM d ORDER BY a, b, c; pgFormatter-4.2/t/test-files/expected/ex34.sql000066400000000000000000000007601361326045100213040ustar00rootroot00000000000000SELECT count(*) FILTER (WHERE b) AS count_b, count(*) FILTER (WHERE c) AS count_c FROM a; SELECT array_agg(a ORDER BY b DESC) FROM TABLE; SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY income) FROM households; SELECT count(*) AS unfiltered, count(*) FILTER (WHERE i < 5) AS filtered FROM generate_series(1, 10) AS s (i); SELECT make, model, GROUPING (make, model), sum(sales) FROM items_sold GROUP BY ROLLUP (make, model); pgFormatter-4.2/t/test-files/expected/ex35.sql000066400000000000000000000024351361326045100213060ustar00rootroot00000000000000CREATE TABLE kbln ( id integer NOT NULL, blank_series varchar(50) NOT NULL, company_id varchar(8) ) PARTITION BY RANGE (id); CREATE TABLE kbln_p0 PARTITION OF kbln FOR VALUES FROM (MINVALUE) TO (500000) PARTITION BY HASH (blank_series); CREATE TABLE kbln_p0_1 PARTITION OF kbln_p0 FOR VALUES WITH (MODULUS 2, REMAINDER 0); CREATE TABLE kbln_p0_2 PARTITION OF kbln_p0 FOR VALUES WITH (MODULUS 2, REMAINDER 1); ALTER TABLE t1 DETACH PARTITION t1_a; ALTER TABLE t1 ATTACH PARTITION t1_a FOR VALUES IN (1, 2, 3); CREATE TABLE kbln ( id integer NOT NULL, blank_series varchar(50) NOT NULL, company_id varchar(8) ) PARTITION BY LIST (id); SELECT id, for_group, some_val, sum(some_val) OVER (PARTITION BY for_group ORDER BY id) AS sum_so_far_in_group, sum(some_val) OVER (PARTITION BY for_group) AS sum_in_group, sum(some_val) OVER (PARTITION BY for_group ORDER BY id RANGE 3 PRECEDING) AS sum_current_and_3_preceeding, sum(some_val) OVER (PARTITION BY for_group ORDER BY id RANGE BETWEEN 3 PRECEDING AND 3 FOLLOWING) AS sum_current_and_3_preceeding_and_3_following, sum(some_val) OVER (PARTITION BY for_group ORDER BY id RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS sum_current_and_all_following FROM test ORDER BY for_group, id; pgFormatter-4.2/t/test-files/expected/ex36.sql000066400000000000000000000003141361326045100213010ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION foo () RETURNS TRIGGER AS $$ BEGIN CREATE TEMPORARY TABLE tb ( id integer ); SELECT * FROM NOTHING; END; $$ LANGUAGE 'plpgsql'; pgFormatter-4.2/t/test-files/expected/ex37.sql000066400000000000000000000007051361326045100213060ustar00rootroot00000000000000CREATE POLICY can_select_object ON object FOR SELECT USING (can_do_the_thing (get_current_user (), owner_id)); CREATE POLICY can_insert_object ON object FOR INSERT WITH CHECK (can_do_the_thing (get_current_user (), owner_id)); CREATE POLICY can_update_object ON object FOR UPDATE USING (can_do_the_thing (get_current_user (), owner_id)); CREATE POLICY can_delete_object ON object FOR DELETE USING (can_do_the_thing (get_current_user (), owner_id)); pgFormatter-4.2/t/test-files/expected/ex38.sql000066400000000000000000000006601361326045100213070ustar00rootroot00000000000000CREATE TRIGGER check_update BEFORE UPDATE ON accounts FOR EACH ROW EXECUTE PROCEDURE check_account_update (); CREATE TRIGGER check_update BEFORE UPDATE OF balance ON accounts FOR EACH ROW EXECUTE PROCEDURE check_account_update (); CREATE TRIGGER check_update BEFORE UPDATE ON accounts FOR EACH ROW WHEN (OLD.balance IS DISTINCT FROM NEW.balance) EXECUTE PROCEDURE check_account_update (); pgFormatter-4.2/t/test-files/expected/ex39.sql000066400000000000000000000006201361326045100213040ustar00rootroot00000000000000ALTER TABLE boxes ADD CONSTRAINT my_constraint EXCLUDE USING gist (some_id WITH =, make_tsrange(created_at, expires_at) WITH &&); CREATE TABLE circles ( c circle, EXCLUDE USING gist (c WITH &&) ); ALTER TABLE ONLY public.circles ADD CONSTRAINT circles_c_excl EXCLUDE USING gist (c WITH &&); ALTER TABLE truck ADD EXCLUDE USING gist (id WITH =, system_period WITH &&); pgFormatter-4.2/t/test-files/expected/ex4.sql000066400000000000000000000012751361326045100212230ustar00rootroot00000000000000SELECT 1, 2, 10, 'depesz', 'hubert', 'depesz', 'hubert depesz', '1 2 3 4'; SELECT tbl_lots.id, to_char(tbl_lots.dt_crea, 'DD/MM/YYYY HH24:MI:SS') AS date_crea FROM tbl_lots WHERE tbl_lots.dt_crea > CURRENT_TIMESTAMP - interval '1 day' AND tbl_lots.dt_crea > CURRENT_TIMESTAMP - (dayrett || ' days')::interval AND tbl_lots.type = 'SECRET'; SELECT extract(year FROM school_day) AS year; SELECT substring(firstname FROM 1 FOR 10) AS sname; SELECT * FROM ( SELECT 1 i) a INNER JOIN ( SELECT 1 i) b ON (a.i = b.i) INNER JOIN ( SELECT 1 i) ON (c.i = a.i) WHERE a.i = 1; pgFormatter-4.2/t/test-files/expected/ex40.sql000066400000000000000000000004141361326045100212750ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION myfunc () RETURNS void AS $BODY$ BEGIN SET client_min_messages TO warning; DROP TABLE IF EXISTS tt_BIP; DROP TABLE IF EXISTS tt_tmp; SET client_min_messages TO notice; END; $BODY$ LANGUAGE plpgsql VOLATILE COST 100; pgFormatter-4.2/t/test-files/expected/ex41.sql000066400000000000000000000007251361326045100213030ustar00rootroot00000000000000/* Insert values for environment 1 */ INSERT INTO TABLE VALUES (1, 2, '3'); /* Insert values for environment 2 */ /* Insert values for environment 3 */ INSERT INTO TABLE VALUES (2, 17, 'hello'); -- New comment for a query SELECT library.column1, --------------------- -- This is a line -- -- comment in a -- -- SQL statement -- --------------------- library.column2, library.column3 -- inline comment FROM library; pgFormatter-4.2/t/test-files/expected/ex42.sql000066400000000000000000000003551361326045100213030ustar00rootroot00000000000000WITH styles AS ( SELECT style, color FROM style_table s WHERE s.id = 'D' ), reviews AS ( SELECT style, body FROM review_table ) SELECT * FROM styles s; pgFormatter-4.2/t/test-files/expected/ex43.sql000066400000000000000000000060731361326045100213070ustar00rootroot00000000000000INSERT INTO tt_tmp_wrk1 ( SELECT MAR, ( CASE WHEN MAR = 'SEUIL_DEST' THEN ( SELECT COUNT(*) FROM CUS WHERE MAR.obj4 = ( CASE WHEN MAR.type4 = 'CF' THEN CUS.account_no::text WHEN MAR.type4 = 'CR' THEN CUS.owning_account_no::text WHEN MAR.type4 = 'SI' THEN CUS.point_origin END) AND (CUS = ( SELECT DISTINCT DD FROM PCM WHERE PCM.compo = CMF.compo) OR CUS = ( SELECT DISTINCT CPIR FROM PCM WHERE PCM.compo = CMF.compo) OR CUS = ( SELECT DISTINCT CPIR FROM PCM, CT, PIDR, CPIR, CMF WHERE PCM.compo = CMF.compo))) WHEN MAR = 'SEUIL_OBJ' THEN ( SELECT COUNT(*) FROM CPC WHERE MAR.obj4 = ( CASE WHEN MAR.type4 = 'CF' THEN CPC.parent::text WHEN MAR.type4 = 'CR' THEN CPC.parent::text WHEN MAR.type4 = 'SI' THEN CPC.parent::text END) AND CPC IN ( SELECT SPT FROM SPT WHERE SPT = 'SEUIL_OBJ' AND SPT IN ( SELECT DISTINCT compo FROM CPC WHERE MAR = ( CASE WHEN MAR = 'CF' THEN CPC.parent::text WHEN MAR = 'CR' THEN CPC.parent::text WHEN MAR.type4 = 'SI' THEN CPC.parent::text END)))) END) FROM MAR, BIP WHERE BIP = (MAR)::int); pgFormatter-4.2/t/test-files/expected/ex44.sql000066400000000000000000000003361361326045100213040ustar00rootroot00000000000000SELECT * FROM tabl WHERE (a = c) AND (b = c OR c = d); SELECT * FROM tabl WHERE (a = c AND b = c) OR (c = d); SELECT * FROM tabl WHERE (a = c) AND (b = c) OR (c = d); pgFormatter-4.2/t/test-files/expected/ex45.sql000066400000000000000000000014201361326045100213000ustar00rootroot00000000000000WITH cte1 AS ( SELECT * FROM table_a a -- Comment out or remove this JOIN to fix the indentation issue below INNER JOIN table_b b ON a.id = b.id ), cte2 AS ( SELECT CASE WHEN TRUE THEN TRUE WHEN NULL IS NULL OR TRUE = FALSE THEN NULL -- Indentation is off starting here WHEN FALSE AND TRUE THEN TRUE ELSE FALSE END AS value FROM cte1 -- Indentation is correct after this line ) SELECT * FROM cte2; SELECT *, SUM(( SELECT count(*) FROM b)) AS something, SUM(( SELECT count(*) FROM b)) AS something, a.b FROM a; pgFormatter-4.2/t/test-files/expected/ex46.sql000066400000000000000000000030741361326045100213100ustar00rootroot00000000000000SELECT a, b, c, d FROM t_1, t_2, t3 WHERE a = 10 AND b = 10 AND c = 10 AND d IN (1, 2, 3, 4, 5, 6, 7); SELECT a, b, c, d FROM t_1, t_2, ( SELECT * FROM t6) AS t3, t4 WHERE a = 10 AND b = 10 AND c = 10 AND d IN (1, 2, 3, 4, 5, 6, 7); SELECT '--data' FROM test; DECLARE c_mvcc_demo CURSOR FOR SELECT xmin, xmax, cmax, * FROM mvcc_demo; SELECT 1; SELECT * FROM pg_class ORDER BY relname; SELECT RANK() OVER s AS dept_rank FROM emp WINDOW s AS (PARTITION BY department ORDER BY salary DESC) ORDER BY department, salary DESC; SELECT x, COUNT(x) OVER w, SUM(x) OVER w FROM generate_series(1, 10) AS f (x) WINDOW w AS (); SELECT name, department, salary, RANK() OVER s AS dept_rank, RANK() OVER () AS global_rank FROM empa WINDOW s AS (PARTITION BY department ORDER BY salary DESC) ORDER BY department, salary DESC; SELECT name, department, salary, RANK() OVER () AS global_rank, RANK() OVER s AS dept_rank FROM empb WINDOW s AS (PARTITION BY department ORDER BY salary DESC) ORDER BY department, salary DESC; UPDATE mvcc_demo SET val = val + 1 WHERE val > 0; WITH driver (name) AS ( SELECT DISTINCT unnest(xpath('//driver/text()', doc))::text FROM printer ) SELECT name FROM driver WHERE name LIKE 'hp%' ORDER BY 1; WITH source (x1, x2) AS ( SELECT 1 ) SELECT * FROM source; SELECT DISTINCT relkind, relname FROM pg_class ORDER BY 1, 2; SELECT salary, RANK() OVER s FROM emp WINDOW s AS (ORDER BY salary DESC) ORDER BY salary DESC; EXPLAIN (COSTS OFF, ANALYZE) SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; pgFormatter-4.2/t/test-files/expected/ex47.sql000066400000000000000000000036331361326045100213120ustar00rootroot00000000000000WITH source AS source2 AS ( DELETE FROM items USING source2) INSERT INTO old_orders SELECT order_id FROM source2; SELECT '--data' FROM test; DECLARE c_mvcc_demo CURSOR FOR SELECT xmin, xmax, cmax, * FROM mvcc_demo; SELECT 1; SELECT * FROM pg_class ORDER BY relname; SELECT RANK() OVER s AS dept_rank FROM emp WINDOW s AS (PARTITION BY department ORDER BY salary DESC) ORDER BY department, salary DESC; SELECT x, COUNT(x) OVER w, SUM(x) OVER w FROM generate_series(1, 10) AS f (x) WINDOW w AS (); SELECT name, department, salary, RANK() OVER s AS dept_rank, RANK() OVER () AS global_rank FROM empa WINDOW s AS (PARTITION BY department ORDER BY salary DESC) ORDER BY department, salary DESC; SELECT name, department, salary, RANK() OVER () AS global_rank, RANK() OVER s AS dept_rank FROM empb WINDOW s AS (PARTITION BY department ORDER BY salary DESC) ORDER BY department, salary DESC; UPDATE mvcc_demo SET val = val + 1 WHERE val > 0; WITH driver ( name ) AS ( SELECT DISTINCT unnest(xpath('//driver/text()', doc))::text FROM printer ) SELECT name FROM driver WHERE name LIKE 'hp%' ORDER BY 1; WITH source ( x1, x2 ) AS ( SELECT 1 ) SELECT * FROM source; SELECT DISTINCT relkind, relname FROM pg_class ORDER BY 1, 2; SELECT salary, RANK() OVER s FROM emp WINDOW s AS (ORDER BY salary DESC) ORDER BY salary DESC; CREATE TRIGGER test_trigger_exists BEFORE UPDATE ON test_exists FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger (); CREATE RULE test_rule_exists AS ON INSERT TO test_exists DO INSTEAD INSERT INTO test_exists VALUES (NEW.a, NEW.b || NEW.a::text); DROP RULE test_rule_exists ON test_exists; pgFormatter-4.2/t/test-files/expected/ex48.sql000066400000000000000000000032241361326045100213070ustar00rootroot00000000000000INSERT INTO test_exists VALUES (NEW.a, NEW.b || NEW.a::text); CREATE RULE test_rule_exists AS ON INSERT TO test_exists DO INSTEAD INSERT INTO test_exists VALUES (NEW.a, NEW.b || NEW.a::text); DROP RULE test_rule_exists ON test_exists; DROP SEQUENCE test_sequence_exists; CREATE GROUP regress_test_g1; DROP GROUP regress_test_g1; DROP USER IF EXISTS regress_test_u1, regress_test_u2; CREATE CONVERSION test_conversion_exists FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; CREATE DOMAIN test_domain_exists AS int NOT NULL CHECK (value > 0); DROP OPERATOR @#@ (int, int); DROP OPERATOR IF EXISTS @#@ (int, int); CREATE OPERATOR @#@ ( LEFTARG = int8, RIGHTARG = int8, PROCEDURE = int8xor ); DROP OPERATOR @#@ (int8, int8); CREATE OPERATOR alter1.= ( PROCEDURE = alter1.same, LEFTARG = alter1.ctype, RIGHTARG = alter1.ctype ); CREATE OPERATOR !== ( PROCEDURE = int8ne, LEFTARG = bigint, RIGHTARG = bigint, COMMUTATOR = !==, NEGATOR = === ); EXPLAIN ( COSTS OFF, ANALYZE ) SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; CREATE FUNCTION sql_is_distinct_from (anyelement, anyelement) RETURNS boolean LANGUAGE sql AS 'INSERT INTO dom_table VALUES (1, 2, 3)' ; INSERT INTO dom_table VALUES ('1'); INSERT INTO dom_table VALUES ('1'); CREATE FUNCTION customcontsel (internal, oid, internal, integer) RETURNS float8 AS 'contsel' LANGUAGE internal STABLE STRICT; CREATE VIEW attmp_view (unique1) AS SELECT unique1 FROM tenk1; CREATE VIEW attmp_view (col1, col2) AS SELECT cola, colb FROM tenk1; CREATE TABLE a ( u ) AS SELECT u; pgFormatter-4.2/t/test-files/expected/ex49.sql000066400000000000000000000074531361326045100213200ustar00rootroot00000000000000-- Round-trip non-ASCII data through xpath(). DO $$ DECLARE xml_declaration text := ''; degree_symbol text; res xml[]; BEGIN -- Per the documentation, except when the server encoding is UTF8, xpath() -- may not work on non-ASCII data. The untranslatable_character and -- undefined_function traps below, currently dead code, will become relevant -- if we remove this limitation. IF current_setting('server_encoding') <> 'UTF8' THEN RAISE LOG 'skip: encoding % unsupported for xpath', current_setting('server_encoding'); RETURN; END IF; degree_symbol := convert_from('\xc2b0', 'UTF8'); res := xpath('text()', (xml_declaration || '' || degree_symbol || '')::xml); IF degree_symbol <> res[1]::text THEN RAISE 'expected % (%), got % (%)', degree_symbol, convert_to(degree_symbol, 'UTF8'), res[1], convert_to(res[1]::text, 'UTF8'); END IF; EXCEPTION -- character with byte sequence 0xc2 0xb0 in encoding "UTF8" has no equivalent in encoding "LATIN8" WHEN untranslatable_character -- default conversion function for encoding "UTF8" to "MULE_INTERNAL" does not exist OR undefined_function -- unsupported XML feature OR feature_not_supported THEN RAISE LOG 'skip: %', SQLERRM; END $$; CREATE TRIGGER invalid_trig INSTEAD OF UPDATE ON main_view FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE view_trigger ('instead_of_upd'); -- -- PARALLEL -- -- Serializable isolation would disable parallel query, so explicitly use an -- arbitrary other level. BEGIN ISOLATION level REPEATABLE read; -- encourage use of parallel plans SET parallel_setup_cost = 0; SET parallel_tuple_cost = 0; SET min_parallel_table_scan_size = 0; SET max_parallel_workers_per_gather = 4; -- -- Test write operations that has an underlying query that is eligble -- for parallel plans -- EXPLAIN ( COSTS OFF ) CREATE TABLE parallel_write AS SELECT length(stringu1) FROM tenk1 GROUP BY length(stringu1); CREATE TABLE parallel_write AS SELECT length(stringu1) FROM tenk1 GROUP BY length(stringu1); DROP TABLE parallel_write; EXPLAIN ( COSTS OFF ) SELECT length(stringu1) INTO parallel_write FROM tenk1 GROUP BY length(stringu1); SELECT length(stringu1) INTO parallel_write FROM tenk1 GROUP BY length(stringu1); DROP TABLE parallel_write; EXPLAIN ( COSTS OFF ) CREATE MATERIALIZED VIEW parallel_mat_view AS SELECT length(stringu1) FROM tenk1 GROUP BY length(stringu1); CREATE MATERIALIZED VIEW parallel_mat_view AS SELECT length(stringu1) FROM tenk1 GROUP BY length(stringu1); DROP MATERIALIZED VIEW parallel_mat_view; PREPARE prep_stmt AS SELECT length(stringu1) FROM tenk1 GROUP BY length(stringu1); EXPLAIN ( COSTS OFF ) CREATE TABLE parallel_write AS EXECUTE prep_stmt; CREATE TABLE parallel_write AS EXECUTE prep_stmt; DROP TABLE parallel_write; ROLLBACK; SELECT first_value(unique1) OVER (ORDER BY four ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING EXCLUDE GROUP), unique1, four FROM tenk1 WHERE unique1 < 10; CREATE AGGREGATE my_avg (int4) ( STYPE = avg_state, SFUNC = avg_transfn, FINALFUNC = avg_finalfn ); CREATE AGGREGATE my_sum_init (int4) ( STYPE = avg_state, SFUNC = avg_transfn, FINALFUNC = sum_finalfn, INITCOND = '(10,0)' ); CREATE AGGREGATE balk (int4) ( SFUNC = balkifnull (int8, int4), STYPE = int8, PARALLEL = SAFE, INITCOND = '0' ); CREATE AGGREGATE balk (int4) ( SFUNC = int4_sum(int8, int4), STYPE = int8, COMBINEFUNC = balkifnull (int8, int8), PARALLEL = SAFE, INITCOND = '0' ); CREATE AGGREGATE myaggn08a ( BASETYPE = anyelement, SFUNC = tf2p, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}' ); pgFormatter-4.2/t/test-files/expected/ex5.sql000066400000000000000000000005561361326045100212250ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION inserta_esquema_pago_backup () RETURNS TRIGGER AS $BODY$ BEGIN INSERT INTO educaciondistancia.esquema_pago_backup (curso, numpago, montopagar, estatus, usuario, fecha) VALUES (NEW.curso, NEW.numpago, NEW.montopagar, NEW.estatus, NEW.usuario, NEW.fecha); RETURN NEW; END; $BODY$ LANGUAGE plpgsql VOLATILE COST 100; pgFormatter-4.2/t/test-files/expected/ex50.sql000066400000000000000000000055231361326045100213040ustar00rootroot00000000000000-- function to wait for counters to advance CREATE FUNCTION wait_for_stats () RETURNS void AS $$ DECLARE start_time timestamptz := clock_timestamp(); updated1 bool; updated2 bool; updated3 bool; updated4 bool; BEGIN -- we don't want to wait forever; loop will exit after 30 seconds FOR i IN 1..300 LOOP -- With parallel query, the seqscan and indexscan on tenk2 might be done -- in parallel worker processes, which will send their stats counters -- asynchronously to what our own session does. So we must check for -- those counts to be registered separately from the update counts. -- check to see if seqscan has been sensed SELECT (st.seq_scan >= pr.seq_scan + 1) INTO updated1 FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr WHERE st.relname = 'tenk2' AND cl.relname = 'tenk2'; -- check to see if indexscan has been sensed SELECT (st.idx_scan >= pr.idx_scan + 1) INTO updated2 FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr WHERE st.relname = 'tenk2' AND cl.relname = 'tenk2'; -- check to see if all updates have been sensed SELECT (n_tup_ins > 0) INTO updated3 FROM pg_stat_user_tables WHERE relname = 'trunc_stats_test4'; -- We must also check explicitly that pg_stat_get_snapshot_timestamp has -- advanced, because that comes from the global stats file which might -- be older than the per-DB stats file we got the other values from. SELECT (pr.snap_ts < pg_stat_get_snapshot_timestamp ()) INTO updated4 FROM prevstats AS pr; exit WHEN updated1 AND updated2 AND updated3 AND updated4; -- wait a little PERFORM pg_sleep_for ('100 milliseconds'); -- reset stats snapshot so we can test again PERFORM pg_stat_clear_snapshot(); END LOOP; -- report time waited in postmaster log (where it won't change test output) RAISE log 'wait_for_stats delayed % seconds', extract(epoch FROM clock_timestamp() - start_time); END $$ LANGUAGE plpgsql; -- test ordered-set aggs using built-in support functions CREATE AGGREGATE my_percentile_disc (float8 ORDER BY anyelement) ( STYPE = internal, SFUNC = ordered_set_transition, FINALFUNC = percentile_disc_final, FINALFUNC_EXTRA = TRUE, FINALFUNC_MODIFY = READ_WRITE ); CREATE AGGREGATE my_rank (VARIADIC "any" ORDER BY VARIADIC "any") ( STYPE = internal, SFUNC = ordered_set_transition_multi, FINALFUNC = rank_final, FINALFUNC_EXTRA = TRUE, hypothetical ); pgFormatter-4.2/t/test-files/expected/ex51.sql000066400000000000000000000001231361326045100212740ustar00rootroot00000000000000SELECT * FROM TABLE; SELECT * FROM pg_stat_activity WHERE state = 'active'; pgFormatter-4.2/t/test-files/expected/ex52.sql000066400000000000000000000076661361326045100213200ustar00rootroot00000000000000DROP OPERATOR === (); CREATE TYPE jwt_token AS ( token text, field: text ); CREATE TABLE IF NOT EXISTS relationships ( id serial PRIMARY KEY, users_id integer NOT NULL REFERENCES users (id), students_id integer NOT NULL REFERENCES students (id), communication boolean NOT NULL ); CREATE TABLE nulltest ( col1 dnotnull, col2 dnotnull NULL -- NOT NULL in the domain cannot be overridden , col3 dnull NOT NULL, col4 dnull, col5 dcheck CHECK (col5 IN ('c', 'd')) ); CREATE TABLE new_table_test ( start_date timestamp NOT NULL, end_date timestamp NOT NULL, name varchar(40) NOT NULL CHECK (name <> ''), fk_organization_unit_id numeric(20), fk_product_id numeric(20), id numeric(20) NOT NULL PRIMARY KEY, migrated varchar(1) ); SELECT Date_trunc('day', last_updated_on), Count(*) FROM exports.a GROUP BY Date_trunc('day', last_updated_on) ORDER BY Date_trunc('day', last_updated_on), toto DESC; CREATE FUNCTION customcontsel (internal, oid, internal, integer) RETURNS float8 AS 'contsel' LANGUAGE internal STABLE STRICT; CREATE FUNCTION ADD (integer, integer) RETURNS integer LANGUAGE sql IMMUTABLE STRICT AS $_$ SELECT $1 + $2; $_$; DO $$ BEGIN INSERT INTO table01 (id, user_id, group_id) SELECT nextval('foobar'), ug.user_id, 14 FROM table01 ug WHERE ug.group_id = 13 AND NOT EXISTS ( SELECT * FROM table01 ug2 WHERE ug2 = ug.user_id AND ug2.group_id = 14); END; $$; WITH ins ( a, b, c ) AS ( INSERT INTO mlparted (b, a) SELECT s.a, 1 FROM generate_series(2, 39) s (a) RETURNING tableoid::regclass, * ) SELECT a, b, min(c), max(c) FROM ins GROUP BY a, b ORDER BY 1; CREATE OR REPLACE FUNCTION chkrolattr () RETURNS TABLE ( "role" name, rolekeyword text, canlogin bool, replication bool ) AS $$ SELECT r.rolname, v.keyword, r.rolcanlogin, r.rolreplication FROM pg_roles r JOIN ( VALUES (CURRENT_USER, 'current_user'), (SESSION_USER, 'session_user'), ('current_user', '-'), ('session_user', '-'), ('Public', '-'), ('None', '-')) AS v (uname, keyword) ON (r.rolname = v.uname) ORDER BY 1; $$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION hash_join_batches (query text) RETURNS TABLE ( original int, final int) LANGUAGE plpgsql AS $$ DECLARE whole_plan json; hash_node json; BEGIN FOR whole_plan IN EXECUTE 'explain (analyze, format ''json'') ' || query LOOP hash_node := find_hash (json_extract_path(whole_plan, '0', 'Plan')); original := hash_node ->> 'Original Hash Batches'; final := hash_node ->> 'Hash Batches'; RETURN NEXT; END LOOP; END; $$; CREATE TABLE nv_child_2009 ( CHECK (d BETWEEN '2009-01-01'::date AND '2009-12-31'::date) ) INHERITS ( nv_parent ); CREATE TABLE part1 ( a int NOT NULL CHECK (a = 1), b int NOT NULL CHECK (b >= 1 AND b <= 10) ); CREATE TRIGGER parted_trig AFTER INSERT ON parted_irreg FOR EACH ROW EXECUTE PROCEDURE trigger_notice_ab (); CREATE CONSTRAINT TRIGGER parted_trig_two AFTER INSERT ON parted_constr DEFERRABLE INITIALLY DEFERRED FOR EACH ROW WHEN (bark (new.b) AND new.a % 2 = 1) EXECUTE PROCEDURE trigger_notice_ab (); BEGIN; CREATE TRIGGER ttdummy BEFORE DELETE OR UPDATE ON alterlock FOR EACH ROW EXECUTE PROCEDURE ttdummy (1, 1); SELECT * FROM my_locks ORDER BY 1; ROLLBACK; CREATE TRIGGER base_tab_def_view_instrig INSTEAD OF INSERT ON base_tab_def_view FOR EACH ROW EXECUTE FUNCTION base_tab_def_view_instrig_func (); pgFormatter-4.2/t/test-files/expected/ex53.sql000066400000000000000000000123541361326045100213070ustar00rootroot00000000000000INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (KEY, fruit) DO UPDATE SET fruit = excluded.fruit WHERE EXISTS ( SELECT 1 FROM insertconflicttest ii WHERE ii.key = excluded.key); INSERT INTO tbl1 SELECT column1, coilumn2, column3 FROM tbl2 ON CONFLICT ON CONSTRAINT pk$tbl1 DO UPDATE SET asgen = excluded.asgen, long_rev = excluded.long_rev, short_rev = excluded.short_rev; UPDATE toto SET asgen = excluded.asgen, long_rev = excluded.long_rev, short_rev = excluded.short_rev; CREATE TABLE FKTABLE ( ftest1 int REFERENCES PKTABLE MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int ); CREATE TABLE FKTABLE ( ftest1 int DEFAULT - 1, ftest2 int DEFAULT - 2, ftest3 int, CONSTRAINT constrname2 FOREIGN KEY (ftest1, ftest2) REFERENCES PKTABLE MATCH FULL ON DELETE SET DEFAULT ON UPDATE SET DEFAULT ); ALTER TABLE fk_partitioned_fk ADD FOREIGN KEY (a, b) REFERENCES fk_notpartitioned_pk MATCH SIMPLE ON DELETE SET NULL ON UPDATE SET NULL; CREATE TABLE persons OF nothing; CREATE TABLE IF NOT EXISTS persons OF nothing; SELECT * FROM NOTHING; SET client_encoding TO 'UTF8'; UPDATE weather SET temp_lo = temp_lo + 1, temp_hi = temp_lo + 15, prcp = DEFAULT WHERE city = 'San Francisco' AND date = '2003-07-03' RETURNING temp_lo, temp_hi, prcp; INSERT INTO tbl1 SELECT column1, coilumn2, column3 FROM tbl2 ON CONFLICT ON CONSTRAINT pk$tbl1 DO UPDATE SET asgen = excluded.asgen, long_rev = excluded.long_rev, short_rev = excluded.short_rev; EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (fruit, KEY, fruit, KEY) DO NOTHING; EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (lower(fruit), KEY, lower(fruit), KEY) DO NOTHING; EXPLAIN ( COSTS OFF ) INSERT INTO insertconflicttest VALUES (0, 'Crowberry') ON CONFLICT (KEY, fruit) DO UPDATE SET fruit = excluded.fruit WHERE EXISTS ( SELECT 1 FROM insertconflicttest ii WHERE ii.key = excluded.key); PREPARE demo AS INSERT INTO demo VALUES (1, 2, 3, 4); PREPARE demo AS UPDATE demo SET lbl = 'unknown' WHERE id IN (1, 2, 3, 4); BEGIN; LOCK hs1 IN SHARE UPDATE EXCLUSIVE MODE; COMMIT; CREATE FUNCTION tg_hub_a () RETURNS TRIGGER AS $$ DECLARE hname text; dummy integer; BEGIN IF tg_op = 'INSERT' THEN dummy := tg_hub_adjustslots (new.name, 0, new.nslots); RETURN new; END IF; IF tg_op = 'UPDATE' THEN IF new.name != old.name THEN UPDATE HSlot SET hubname = new.name WHERE hubname = old.name; END IF; dummy := tg_hub_adjustslots (new.name, old.nslots, new.nslots); RETURN new; END IF; IF tg_op = 'DELETE' THEN dummy := tg_hub_adjustslots (old.name, old.nslots, 0); RETURN old; END IF; END; $$ LANGUAGE plpgsql; EXPLAIN ( COSTS OFF ) CREATE TABLE parallel_write AS EXECUTE prep_stmt; CREATE FUNCTION tg_hub_adjustslots (hname bpchar, oldnslots integer, newnslots integer) RETURNS integer AS $$ BEGIN IF newnslots = oldnslots THEN RETURN 0; END IF; IF newnslots < oldnslots THEN DELETE FROM HSlot WHERE hubname = hname AND slotno > newnslots; RETURN 0; END IF; FOR i IN oldnslots + 1..newnslots LOOP INSERT INTO HSlot (slotname, hubname, slotno, slotlink) VALUES ('HS.dummy', hname, i, ''); END LOOP; RETURN 0; END $$ LANGUAGE plpgsql; CREATE FUNCTION tg_backlink_a () RETURNS TRIGGER AS $$ DECLARE dummy integer; BEGIN IF tg_op = 'INSERT' THEN IF new.backlink != '' THEN dummy := tg_backlink_set (new.backlink, new.slotname); END IF; RETURN new; END IF; IF tg_op = 'UPDATE' THEN IF new.backlink != old.backlink THEN IF old.backlink != '' THEN dummy := tg_backlink_unset (old.backlink, old.slotname); END IF; IF new.backlink != '' THEN dummy := tg_backlink_set (new.backlink, new.slotname); END IF; ELSE IF new.slotname != old.slotname AND new.backlink != '' THEN dummy := tg_slotlink_set (new.backlink, new.slotname); END IF; END IF; RETURN new; END IF; IF tg_op = 'DELETE' THEN IF old.backlink != '' THEN dummy := tg_backlink_unset (old.backlink, old.slotname); END IF; RETURN old; END IF; END; $$ LANGUAGE plpgsql; CREATE VIEW v AS WITH a AS ( SELECT * FROM aa); -- original snippet \set user `echo $PGRST_DB_USER` \set passwd `echo $PGRST_DB_PWD` CREATE ROLE :user WITH LOGIN noinherit PASSWORD :'passwd'; pgFormatter-4.2/t/test-files/expected/ex54.sql000066400000000000000000000170641361326045100213130ustar00rootroot00000000000000SELECT CASE WHEN 1 = 1 THEN 2 ELSE 3 END::text AS col1, col2, col3 FROM tb1; SELECT ( CASE WHEN 1 = 1 THEN 2 ELSE 3 END)::text AS col1, col2, col3 FROM tb1; UPDATE point_tbl SET f1[0] = NULL WHERE f1::text = '(10,10)'::point::text RETURNING *; SELECT 'TrUe'::text::boolean AS true, 'fAlse'::text::boolean AS false; SELECT TRUE::boolean::text AS true, FALSE::boolean::text AS false; CREATE PROCEDURE testns.bar () AS 'select 1' LANGUAGE sql; ALTER TABLE test9b ALTER COLUMN b TYPE priv_testdomain1; CREATE TYPE test7b AS ( a int, b priv_testdomain1 ); CREATE TYPE test8b AS ( a int, b int ); ALTER TYPE test8b ADD ATTRIBUTE c priv_testdomain1; CREATE OR REPLACE PROCEDURE foo () AS $BODY$ DECLARE BEGIN INSERT INTO bar (COLUMN) VALUES (1); COMMIT; BEGIN INSERT INTO bar (COLUMN) VALUES (2); COMMIT; BEGIN INSERT INTO bar (COLUMN) VALUES (3); COMMIT; END; END; END; $BODY$ LANGUAGE plpgsql; CREATE TEXT SEARCH PARSER addr_ts_prs ( START = prsd_start, gettoken = prsd_nexttoken, END = prsd_end, lextypes = prsd_lextype ); CREATE FUNCTION fonction_reference (refcursor) RETURNS refcursor AS $$ BEGIN OPEN $1 FOR SELECT col FROM test; RETURN $1; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION foo () RETURNS TRIGGER AS $$ BEGIN IF NEW.role NOT IN ( SELECT rolname FROM pg_authid) THEN RAISE EXCEPTION 'role % does not exist.', NEW.role; END IF; END; $$ LANGUAGE 'plpgsql'; DO $$ DECLARE xml_declaration text := ''; degree_symbol text; res xml[]; BEGIN -- Per the documentation, except when the server encoding is UTF8, xpath() -- may not work on non-ASCII data. The untranslatable_character and -- undefined_function traps below, currently dead code, will become relevant -- if we remove this limitation. IF current_setting('server_encoding') <> 'UTF8' THEN RAISE LOG 'skip: encoding % unsupported for xpath', current_setting('server_encoding'); RETURN; END IF; degree_symbol := convert_from('\xc2b0', 'UTF8'); res := xpath('text()', (xml_declaration || '' || degree_symbol || '')::xml); IF degree_symbol <> res[1]::text THEN RAISE 'expected % (%), got % (%)', degree_symbol, convert_to(degree_symbol, 'UTF8'), res[1], convert_to(res[1]::text, 'UTF8'); END IF; EXCEPTION -- character with byte sequence 0xc2 0xb0 in encoding "UTF8" has no equivalent in encoding "LATIN8" WHEN untranslatable_character -- default conversion function for encoding "UTF8" to "MULE_INTERNAL" does not exist OR undefined_function -- unsupported XML feature OR feature_not_supported THEN RAISE LOG 'skip: %', SQLERRM; END $$; CREATE FUNCTION wait_for_stats () RETURNS void AS $$ DECLARE start_time timestamptz := clock_timestamp(); updated1 bool; updated2 bool; updated3 bool; updated4 bool; BEGIN -- we don't want to wait forever; loop will exit after 30 seconds FOR i IN 1..300 LOOP -- With parallel query, the seqscan and indexscan on tenk2 might be done -- in parallel worker processes, which will send their stats counters -- asynchronously to what our own session does. So we must check for -- those counts to be registered separately from the update counts. -- check to see if seqscan has been sensed SELECT (st.seq_scan >= pr.seq_scan + 1) INTO updated1 FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr WHERE st.relname = 'tenk2' AND cl.relname = 'tenk2'; -- check to see if indexscan has been sensed SELECT (st.idx_scan >= pr.idx_scan + 1) INTO updated2 FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr WHERE st.relname = 'tenk2' AND cl.relname = 'tenk2'; -- check to see if all updates have been sensed SELECT (n_tup_ins > 0) INTO updated3 FROM pg_stat_user_tables WHERE relname = 'trunc_stats_test4'; -- We must also check explicitly that pg_stat_get_snapshot_timestamp has -- advanced, because that comes from the global stats file which might -- be older than the per-DB stats file we got the other values from. SELECT (pr.snap_ts < pg_stat_get_snapshot_timestamp ()) INTO updated4 FROM prevstats AS pr; exit WHEN updated1 AND updated2 AND updated3 AND updated4; -- wait a little PERFORM pg_sleep_for ('100 milliseconds'); -- reset stats snapshot so we can test again PERFORM pg_stat_clear_snapshot(); END LOOP; -- report time waited in postmaster log (where it won't change test output) RAISE log 'wait_for_stats delayed % seconds', extract(epoch FROM clock_timestamp() - start_time); END $$ LANGUAGE plpgsql; DO $$ DECLARE objtype text; names text[]; args text[]; BEGIN FOR objtype IN VALUES ('table'), ('publication relation') LOOP FOR names IN VALUES ('{eins}'), ('{eins, zwei, drei}') LOOP FOR args IN VALUES ('{}'), ('{integer}') LOOP BEGIN PERFORM pg_get_object_address(objtype, names, args); EXCEPTION WHEN OTHERS THEN RAISE WARNING 'error for %,%,%: %', objtype, names, args, sqlerrm; END; END LOOP; END LOOP; END LOOP; END; $$; -- test successful cases WITH objects ( TYPE, name, args ) AS ( VALUES ('table', '{addr_nsp, gentable}'::text[], '{}'::text[]), ('index', '{addr_nsp, parttable_pkey}', '{}'), ('sequence', '{addr_nsp, gentable_a_seq}', '{}'), -- toast table ('view', '{addr_nsp, genview}', '{}'), -- large object ('operator', '{+}', '{int4, int4}'), -- database -- tablespace ('foreign-data wrapper', '{addr_fdw}', '{}'), -- extension -- event trigger ('policy', '{addr_nsp, gentable, genpol}', '{}'), ('statistics object', '{addr_nsp, gentable_stat}', '{}')) SELECT (pg_identify_object (addr1.classid, addr1.objid, addr1.objsubid)).*, -- test roundtrip through pg_identify_object_as_address ROW (pg_identify_object (addr1.classid, addr1.objid, addr1.objsubid)) = ROW (pg_identify_object (addr2.classid, addr 2.objid, addr2.objsubid)) FROM objects, pg_get_object_address(TYPE, name, args) addr1, pg_identify_object_as_address(classid, objid, objsubid) ioa (typ, nms, args), pg_get_object_address(typ, nms, ioa.args) AS addr2 ORDER BY addr1.classid, addr1.objid, addr1.objsubid; --- --- Cleanup resources --- DROP FOREIGN DATA WRAPPER addr_fdw CASCADE; DROP PUBLICATION addr_pub; DROP SUBSCRIPTION addr_sub; DROP SCHEMA addr_nsp CASCADE; DROP OWNED BY regress_addr_user; DROP USER regress_addr_user; CREATE PROCEDURE insert_data (a integer, b integer) LANGUAGE SQL AS $$ INSERT INTO tbl VALUES (a); INSERT INTO tbl VALUES (b); $$; pgFormatter-4.2/t/test-files/expected/ex55.sql000066400000000000000000000010341361326045100213020ustar00rootroot00000000000000CREATE FUNCTION job_next () RETURNS SETOF job AS $$ DECLARE id uuid; BEGIN SELECT id INTO id FROM job WHERE state = 'sched' AND scheduled >= now() ORDER BY scheduled, modified LIMIT 1 FOR UPDATE SKIP LOCKED; -- ### 1 indent lost END; $$ LANGUAGE plpgsql; SELECT * FROM ( SELECT * FROM mytable FOR UPDATE) AS ss WHERE col1 = 5; BEGIN; SELECT * FROM mytable WHERE KEY = 1 FOR NO KEY UPDATE; END; pgFormatter-4.2/t/test-files/expected/ex56.sql000066400000000000000000000046131361326045100213110ustar00rootroot00000000000000CREATE VIEW ticket. "view_ticket_inquiry" AS SELECT i.*, ( SELECT max(tl.creation_time) FROM wf.transition_log tl WHERE tl.workflow_id = i.id AND tl.dst_station_id = 25346145527 /* this is a comment */) AS last_answered FROM ticket.inquiry i; CREATE FUNCTION state_update (id int4, new int4) RETURNS int4 AS $$ BEGIN INSERT INTO state (id, state, WHEN) VALUES (id, new, CURRENT_TIMESTAMP); INSERT INTO state (id, state, WHEN) VALUES (id, new, CURRENT_TIMESTAMP) ON CONFLICT (id) -- ### this line should not be dedented DO UPDATE SET state = excluded.state, WHEN = excluded.when; RETURN 1; END; $$ LANGUAGE plpgsql; CREATE PROCEDURE insert_data (a integer, b integer) LANGUAGE SQL AS $$ INSERT INTO tbl VALUES (a); INSERT INTO foo AS bar DEFAULT VALUES RETURNING foo.*; INSERT INTO tbl VALUES (b) RETURNING b; $$; INSERT INTO foo AS bar DEFAULT VALUES RETURNING foo.*; CREATE TYPE foo AS enum ( 'busy', 'help' ); CREATE TYPE IF NOT EXISTS foo AS enum ( 'busy', 'help' ); CREATE TABLE IF NOT EXISTS foo ( id bigint PRIMARY KEY, /* This text will receive an extra level of indentation every time pg_format is executed */ bar text NOT NULL /* this is the end*/ ); CREATE INDEX onek_unique1 ON onek USING btree (unique1 int4_ops); CREATE INDEX IF NOT EXISTS onek_unique1 ON onek USING btree (unique1 int4_ops); CREATE STATISTICS ab1_a_b_stats ON a, b FROM ab1; CREATE STATISTICS IF NOT EXISTS ab1_a_b_stats ON a, b FROM ab1; SELECT a, b, Row_number() OVER (PARTITION BY Coalesce(c.order_id, c.id) ORDER BY c.id ASC), e, d FROM c; CREATE TABLE sh.z ( id serial NOT NULL, b smallint NULL, v text NULL, d text NULL, e timestamp NULL, f boolean NOT NULL, g boolean NOT NULL ); SET TIME ZONE 'CST7CDT'; SELECT f1, f1::INTERVAL DAY TO MINUTE AS "minutes", (f1 + interval '1 month')::INTERVAL MONTH::INTERVAL YEAR AS "years" FROM interval_tbl; SELECT '19970210 173201' AT TIME ZONE 'America/New_York'; SELECT * FROM XMLTABLE('*' PASSING 'a' COLUMNS a xml PATH '.', b text PATH '.', c text PATH '"hi"', d boolean PATH '. = "a"', e integer PATH 'string-length(.)'); pgFormatter-4.2/t/test-files/expected/ex6.sql000066400000000000000000000006321361326045100212210ustar00rootroot00000000000000SELECT attributes -> 'key' FROM json_test; CREATE TABLE foobar ( id integer NOT NULL, version integer NOT NULL, prev_id integer NOT NULL, prev_version integer NOT NULL, PRIMARY KEY (id, version), UNIQUE (prev_id, prev_version), FOREIGN KEY (prev_id, prev_version) REFERENCES foobar (id, version), FOREIGN KEY (id, version) REFERENCES barfoo (next_id, next_version) ); pgFormatter-4.2/t/test-files/expected/ex7.sql000066400000000000000000000014701361326045100212230ustar00rootroot00000000000000-- From 9.4 JSON page - examples SELECT '[{"a":"foo"},{"b":"bar"},{"c":"baz"}]'::json -> 2; SELECT '{"a": {"b":"foo"}}'::json -> 'a'; SELECT '[1,2,3]'::json ->> 2; SELECT '{"a":1,"b":2}'::json ->> 'b'; SELECT '{"a": {"b":{"c": "foo"}}}'::json #> '{a,b}'; SELECT '{"a":[1,2,3],"b":[4,5,6]}'::json #>> '{a,2}'; -- From 9.4 JSON page - jsonb examples SELECT '{"a":1, "b":2}'::jsonb @> '{"b":2}'::jsonb; SELECT '{"b":2}'::jsonb <@ '{"a":1, "b":2}'::jsonb; SELECT '{"a":1, "b":2}'::jsonb ? 'b'; SELECT '{"a":1, "b":2, "c":3}'::jsonb ? | ARRAY['b', 'c']; SELECT '{"a":1, "b":2, "c":3}'::jsonb ? | ARRAY['b', 'c']; SELECT '["a", "b"]'::jsonb ?& ARRAY['a', 'b']; SELECT '{"a": {"b":{"c": "foo"}}}'::json #> '{a,b}', '{"a":[1,2,3],"b":[4,5,6]}'::json #>> '{a,2}'; pgFormatter-4.2/t/test-files/expected/ex8.sql000066400000000000000000000002261361326045100212220ustar00rootroot00000000000000SELECT 'hello', 2 + 2, 'o\'grady', 0, '', count(*), 'that\'s the position you\'re in now no matter where you select it'; pgFormatter-4.2/t/test-files/expected/ex9.sql000066400000000000000000000002731361326045100212250ustar00rootroot00000000000000-- test placeholder: perl pg_format samples/ex9.sql -p '<<(?:.*)?>>' SELECT * FROM projects WHERE projectnumber IN << internalprojects >> AND username = << loginname >>;