diff options
author | Shigeru Hanada | 2011-03-14 01:55:54 +0000 |
---|---|---|
committer | Shigeru Hanada | 2011-03-14 01:55:54 +0000 |
commit | dc1f2f932d32ddde977c057bd8798484ccf77551 (patch) | |
tree | d974a926acdff0422b4af074f0e296463d422507 | |
parent | 6ef467bfe4b55f28ab6c4c9414cab49795c1e6b0 (diff) | |
parent | a2eb9e0c08ee73208b5419f5a53a6eba55809b92 (diff) |
Merge branch 'master' into postgresql_fdw
68 files changed, 756 insertions, 380 deletions
diff --git a/doc/src/sgml/array.sgml b/doc/src/sgml/array.sgml index b3ecda5f25..bb318c5fc5 100644 --- a/doc/src/sgml/array.sgml +++ b/doc/src/sgml/array.sgml @@ -646,7 +646,7 @@ SELECT f1[1][-2][3] AS e1, f1[1][-1][5] AS e2 If the value written for an element is <literal>NULL</> (in any case variant), the element is taken to be NULL. The presence of any quotes or backslashes disables this and allows the literal string value - <quote>NULL</> to be entered. Also, for backwards compatibility with + <quote>NULL</> to be entered. Also, for backward compatibility with pre-8.2 versions of <productname>PostgreSQL</>, the <xref linkend="guc-array-nulls"> configuration parameter can be turned <literal>off</> to suppress recognition of <literal>NULL</> as a NULL. diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 297ad53208..64d1cda899 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -2132,7 +2132,8 @@ <entry><structfield>collencoding</structfield></entry> <entry><type>int4</type></entry> <entry></entry> - <entry>Encoding to which the collation is applicable</entry> + <entry>Encoding in which the collation is applicable, or -1 if it + works for any encoding</entry> </row> <row> @@ -2157,12 +2158,13 @@ <structfield>collencoding</>, <structfield>collnamespace</>) not just (<structfield>collname</>, <structfield>collnamespace</>). <productname>PostgreSQL</productname> generally ignores all - collations not belonging to the current database's encoding; therefore - it is sufficient to use a qualified SQL name + collations that do not have <structfield>collencoding</> equal to + either the current database's encoding or -1, and creation of new + entries matching an entry with <structfield>collencoding</> = -1 + is forbidden. Therefore it is sufficient to use a qualified SQL name (<replaceable>schema</>.<replaceable>name</>) to identify a collation, even though this is not unique according to the catalog definition. - The current database's encoding is automatically used as an additional - lookup key. The reason for defining the catalog this way is that + The reason for defining the catalog this way is that <application>initdb</> fills it in at cluster initialization time with entries for all locales available on the system, so it must be able to hold entries for all encodings that might ever be used in the cluster. diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 8e27f730a4..a4c1e84ad8 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -324,7 +324,9 @@ SET ENABLE_SEQSCAN TO OFF; to listen for connections from client applications. The value takes the form of a comma-separated list of host names and/or numeric IP addresses. The special entry <literal>*</> - corresponds to all available IP interfaces. + corresponds to all available IP interfaces. The entry + <literal>0.0.0.0</> allows listening for all IPv4 addresses and + <literal>::</> allows listening for all IPv6 addresses. If the list is empty, the server does not listen on any IP interface at all, in which case only Unix-domain sockets can be used to connect to it. @@ -5355,7 +5357,7 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir' null values to be entered. However, <productname>PostgreSQL</> versions before 8.2 did not support null values in arrays, and therefore would treat <literal>NULL</> as specifying a normal array element with - the string value <quote>NULL</>. For backwards compatibility with + the string value <quote>NULL</>. For backward compatibility with applications that require the old behavior, this variable can be turned <literal>off</>. </para> diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 4635cf2bb5..8b5c3c51ae 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -5446,7 +5446,7 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})'); <tbody> <row> <entry><literal>FM</literal> prefix</entry> - <entry>fill mode (suppress padding blanks and zeroes)</entry> + <entry>fill mode (suppress padding blanks and trailing zeroes)</entry> <entry><literal>FMMonth</literal></entry> </row> <row> @@ -5812,7 +5812,7 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})'); <tbody> <row> <entry><literal>FM</literal> prefix</entry> - <entry>fill mode (suppress padding blanks and zeroes)</entry> + <entry>fill mode (suppress padding blanks and trailing zeroes)</entry> <entry><literal>FM9999</literal></entry> </row> <row> @@ -6605,13 +6605,20 @@ SELECT EXTRACT(CENTURY FROM TIMESTAMP '2001-02-16 20:38:40'); <term><literal>day</literal></term> <listitem> <para> - The day (of the month) field (1 - 31) + For <type>timestamp</type> values, the day (of the month) field + (1 - 31) ; for <type>interval</type> values, the number of days </para> <screen> SELECT EXTRACT(DAY FROM TIMESTAMP '2001-02-16 20:38:40'); <lineannotation>Result: </lineannotation><computeroutput>16</computeroutput> + +SELECT EXTRACT(DAY FROM INTERVAL '40 days 1 minute'); +<lineannotation>Result: </lineannotation><computeroutput>40</computeroutput> </screen> + + + </listitem> </varlistentry> @@ -6827,7 +6834,7 @@ SELECT EXTRACT(MINUTE FROM TIMESTAMP '2001-02-16 20:38:40'); <listitem> <para> For <type>timestamp</type> values, the number of the month - within the year (1 - 12) ; for <type>interval</type> values + within the year (1 - 12) ; for <type>interval</type> values, the number of months, modulo 12 (0 - 11) </para> @@ -9688,7 +9695,7 @@ nextval('foo') <lineannotation>searches search path for <literal>fo Before <productname>PostgreSQL</productname> 8.1, the arguments of the sequence functions were of type <type>text</>, not <type>regclass</>, and the above-described conversion from a text string to an OID value would - happen at run time during each call. For backwards compatibility, this + happen at run time during each call. For backward compatibility, this facility still exists, but internally it is now handled as an implicit coercion from <type>text</> to <type>regclass</> before the function is invoked. @@ -12502,6 +12509,10 @@ postgres=# SELECT * FROM unnest2(ARRAY[[1,2],[3,4]]); </indexterm> <indexterm> + <primary>session_user</primary> + </indexterm> + + <indexterm> <primary>user</primary> <secondary>current</secondary> </indexterm> @@ -12760,34 +12771,34 @@ SET search_path TO <replaceable>schema</> <optional>, <replaceable>schema</>, .. <entry>does current user have privilege for schema</entry> </row> <row> - <entry><literal><function>has_server_privilege</function>(<parameter>user</parameter>, - <parameter>server</parameter>, + <entry><literal><function>has_sequence_privilege</function>(<parameter>user</parameter>, + <parameter>sequence</parameter>, <parameter>privilege</parameter>)</literal> </entry> <entry><type>boolean</type></entry> - <entry>does user have privilege for foreign server</entry> + <entry>does user have privilege for sequence</entry> </row> <row> - <entry><literal><function>has_server_privilege</function>(<parameter>server</parameter>, + <entry><literal><function>has_sequence_privilege</function>(<parameter>sequence</parameter>, <parameter>privilege</parameter>)</literal> </entry> <entry><type>boolean</type></entry> - <entry>does current user have privilege for foreign server</entry> + <entry>does current user have privilege for sequence</entry> </row> <row> - <entry><literal><function>has_sequence_privilege</function>(<parameter>user</parameter>, - <parameter>sequence</parameter>, + <entry><literal><function>has_server_privilege</function>(<parameter>user</parameter>, + <parameter>server</parameter>, <parameter>privilege</parameter>)</literal> </entry> <entry><type>boolean</type></entry> - <entry>does user have privilege for sequence</entry> + <entry>does user have privilege for foreign server</entry> </row> <row> - <entry><literal><function>has_sequence_privilege</function>(<parameter>sequence</parameter>, + <entry><literal><function>has_server_privilege</function>(<parameter>server</parameter>, <parameter>privilege</parameter>)</literal> </entry> <entry><type>boolean</type></entry> - <entry>does current user have privilege for sequence</entry> + <entry>does current user have privilege for foreign server</entry> </row> <row> <entry><literal><function>has_table_privilege</function>(<parameter>user</parameter>, @@ -13192,6 +13203,10 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); </indexterm> <indexterm> + <primary>pg_describe_object</primary> + </indexterm> + + <indexterm> <primary>pg_get_constraintdef</primary> </indexterm> @@ -13274,6 +13289,11 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); <entry>get SQL name of a data type</entry> </row> <row> + <entry><literal><function>pg_describe_object(<parameter>catalog_id</parameter>, <parameter>object_id</parameter>, <parameter>object_sub_id</parameter>)</function></literal></entry> + <entry><type>text</type></entry> + <entry>get description of a database object</entry> + </row> + <row> <entry><literal><function>pg_get_constraintdef(<parameter>constraint_oid</parameter>)</function></literal></entry> <entry><type>text</type></entry> <entry>get definition of a constraint</entry> @@ -13394,11 +13414,6 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); <entry>get the set of database OIDs that have objects in the tablespace</entry> </row> <row> - <entry><literal><function>pg_describe_object(<parameter>catalog_id</parameter>, <parameter>object_id</parameter>, <parameter>object_sub_id</parameter>)</function>)</literal></entry> - <entry><type>text</type></entry> - <entry>get description of a database object</entry> - </row> - <row> <entry><literal><function>pg_typeof(<parameter>any</parameter>)</function></literal></entry> <entry><type>regtype</type></entry> <entry>get the data type of any value</entry> diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 49edc51dba..eddc65e3b4 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1313,7 +1313,7 @@ char *PQport(const PGconn *conn); Returns the debug <acronym>TTY</acronym> of the connection. (This is obsolete, since the server no longer pays attention to the <acronym>TTY</acronym> setting, but the function remains - for backwards compatibility.) + for backward compatibility.) <synopsis> char *PQtty(const PGconn *conn); @@ -1612,7 +1612,7 @@ int PQsocket(const PGconn *conn); <para> Returns the process <acronym>ID</acronym> (PID)<indexterm><primary>PID</><secondary>determining PID of - server process</><tertiary>in libpq</></> of the backend server + server process</><tertiary>in libpq</></> of the backend process handling this connection. <synopsis> @@ -3846,6 +3846,15 @@ PGresult *PQgetResult(PGconn *conn); active and the necessary response data has not yet been read by <function>PQconsumeInput</function>. </para> + + <note> + <para> + Even when <function>PQresultStatus</function> indicates a fatal + error, <function>PQgetResult</function> should be called until it + returns a null pointer to allow <application>libpq</> to + process the error information completely. + </para> + </note> </listitem> </varlistentry> </variablelist> @@ -6924,7 +6933,7 @@ ldap://ldap.acme.com/cn=dbserver,cn=hosts?pgconnectinfo?base?(objectclass=*) The default value for <literal>sslmode</> is <literal>prefer</>. As is shown in the table, this makes no sense from a security point of view, and it only promises performance overhead if possible. It is only provided as the default - for backwards compatibility, and is not recommended in secure deployments. + for backward compatibility, and is not recommended in secure deployments. </para> </sect2> diff --git a/doc/src/sgml/lobj.sgml b/doc/src/sgml/lobj.sgml index 0e6b705835..291409fde0 100644 --- a/doc/src/sgml/lobj.sgml +++ b/doc/src/sgml/lobj.sgml @@ -114,7 +114,7 @@ Oid lo_creat(PGconn *conn, int mode); <replaceable class="parameter">mode</replaceable> is unused and ignored as of <productname>PostgreSQL</productname> 8.1; however, for - backwards compatibility with earlier releases it is best to + backward compatibility with earlier releases it is best to set it to <symbol>INV_READ</symbol>, <symbol>INV_WRITE</symbol>, or <symbol>INV_READ</symbol> <literal>|</> <symbol>INV_WRITE</symbol>. (These symbolic constants are defined diff --git a/doc/src/sgml/plperl.sgml b/doc/src/sgml/plperl.sgml index 16984aab63..0aab10bbd6 100644 --- a/doc/src/sgml/plperl.sgml +++ b/doc/src/sgml/plperl.sgml @@ -202,7 +202,7 @@ select returns_array(); <para> Perl passes <productname>PostgreSQL</productname> arrays as a blessed PostgreSQL::InServer::ARRAY object. This object may be treated as an array - reference or a string, allowing for backwards compatibility with Perl + reference or a string, allowing for backward compatibility with Perl code written for <productname>PostgreSQL</productname> versions below 9.1 to run. For example: diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index d0672bbc89..8da093b738 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -2169,21 +2169,20 @@ CREATE FUNCTION cs_refresh_mviews() RETURNS integer AS $$ DECLARE mviews RECORD; BEGIN - PERFORM cs_log('Refreshing materialized views...'); + RAISE NOTICE 'Refreshing materialized views...'; FOR mviews IN SELECT * FROM cs_materialized_views ORDER BY sort_key LOOP -- Now "mviews" has one record from cs_materialized_views - PERFORM cs_log('Refreshing materialized view ' - || quote_ident(mviews.mv_name) || ' ...'); + RAISE NOTICE 'Refreshing materialized view %s ...', quote_ident(mviews.mv_name); EXECUTE 'TRUNCATE TABLE ' || quote_ident(mviews.mv_name); EXECUTE 'INSERT INTO ' || quote_ident(mviews.mv_name) || ' ' || mviews.mv_query; END LOOP; - PERFORM cs_log('Done refreshing materialized views.'); + RAISE NOTICE 'Done refreshing materialized views.'; RETURN 1; END; $$ LANGUAGE plpgsql; diff --git a/doc/src/sgml/problems.sgml b/doc/src/sgml/problems.sgml index a65baa3157..3f79c6ef90 100644 --- a/doc/src/sgml/problems.sgml +++ b/doc/src/sgml/problems.sgml @@ -78,7 +78,7 @@ </listitem> </itemizedlist> - Here <quote>program</quote> refers to any executable, not only the backend server. + Here <quote>program</quote> refers to any executable, not only the backend process. </para> <para> @@ -280,9 +280,9 @@ When writing a bug report, please avoid confusing terminology. The software package in total is called <quote>PostgreSQL</quote>, sometimes <quote>Postgres</quote> for short. If you - are specifically talking about the backend server, mention that, do not + are specifically talking about the backend process, mention that, do not just say <quote>PostgreSQL crashes</quote>. A crash of a single - backend server process is quite different from crash of the parent + backend process is quite different from crash of the parent <quote>postgres</> process; please don't say <quote>the server crashed</> when you mean a single backend process went down, nor vice versa. Also, client programs such as the interactive frontend <quote><application>psql</application></quote> diff --git a/doc/src/sgml/query.sgml b/doc/src/sgml/query.sgml index 681d08abed..f4fbf11a8d 100644 --- a/doc/src/sgml/query.sgml +++ b/doc/src/sgml/query.sgml @@ -264,8 +264,8 @@ INSERT INTO weather (date, city, temp_hi, temp_lo) COPY weather FROM '/home/user/weather.txt'; </programlisting> - where the file name for the source file must be available to the - backend server machine, not the client, since the backend server + where the file name for the source file must be available on the + machine running the backend process, not the client, since the backend process reads the file directly. You can read more about the <command>COPY</command> command in <xref linkend="sql-copy">. </para> diff --git a/doc/src/sgml/ref/pg_ctl-ref.sgml b/doc/src/sgml/ref/pg_ctl-ref.sgml index 608749f4c6..626ed1f8eb 100644 --- a/doc/src/sgml/ref/pg_ctl-ref.sgml +++ b/doc/src/sgml/ref/pg_ctl-ref.sgml @@ -134,7 +134,7 @@ PostgreSQL documentation <application>pg_ctl</application> is a utility for initializing a <productname>PostgreSQL</productname> database cluster, starting, stopping, or restarting the <productname>PostgreSQL</productname> - backend server (<xref linkend="app-postgres">), or displaying the + database server (<xref linkend="app-postgres">), or displaying the status of a running server. Although the server can be started manually, <application>pg_ctl</application> encapsulates tasks such as redirecting log output and properly detaching from the terminal diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml index e78d275c0d..7f12460426 100644 --- a/doc/src/sgml/ref/pg_dump.sgml +++ b/doc/src/sgml/ref/pg_dump.sgml @@ -142,7 +142,8 @@ PostgreSQL documentation <listitem> <para> Output commands to clean (drop) - database objects prior to (the commands for) creating them. + database objects prior to outputing the commands for creating them. + (Restore might generate some harmless errors.) </para> <para> diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml index dee1cc35ee..87283bca50 100644 --- a/doc/src/sgml/ref/vacuum.sgml +++ b/doc/src/sgml/ref/vacuum.sgml @@ -70,9 +70,9 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER"> When the option list is surrounded by parentheses, the options can be written in any order. Without parentheses, options must be specified in exactly the order shown above. - Prior to <productname>PostgreSQL</productname> 9.0, the unparenthesized - syntax was the only one supported. It is expected that all new options - will be supported only in the parenthesized syntax. + The parenthesized syntax was added in + <productname>PostgreSQL</productname> 9.0; the unparenthesized + syntax is deprecated. </para> </refsect1> @@ -102,8 +102,7 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER"> Specifying <literal>FREEZE</literal> is equivalent to performing <command>VACUUM</command> with the <xref linkend="guc-vacuum-freeze-min-age"> parameter - set to zero. The <literal>FREEZE</literal> option is deprecated and - will be removed in a future release; set the parameter instead. + set to zero. </para> </listitem> </varlistentry> diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml index 4a989df4f9..f77673791b 100644 --- a/doc/src/sgml/runtime.sgml +++ b/doc/src/sgml/runtime.sgml @@ -1028,14 +1028,14 @@ sysctl -w kern.sysv.shmall <varlistentry> - <term><systemitem class="osname">Solaris</></term> + <term><systemitem class="osname">Solaris</> 2.6 to 2.9 (Solaris + 6 to Solaris 9)</term> <indexterm><primary>Solaris</><secondary>IPC configuration</></> <listitem> <para> - At least in version 2.6, the default maximum size of a shared - memory segment is too low for <productname>PostgreSQL</>. The - relevant settings can be changed in <filename>/etc/system</>, - for example: + The default maximum size of a shared memory segment is too low for + <productname>PostgreSQL</>. The relevant settings can be changed in + <filename>/etc/system</>, for example: <programlisting> set shmsys:shminfo_shmmax=0x2000000 set shmsys:shminfo_shmmin=1 @@ -1047,14 +1047,58 @@ set semsys:seminfo_semmni=512 set semsys:seminfo_semmns=512 set semsys:seminfo_semmsl=32 </programlisting> - You need to reboot for the changes to take effect. + You need to reboot for the changes to take effect. See also + <ulink url="http://sunsite.uakom.sk/sunworldonline/swol-09-1997/swol-09-insidesolaris.html"></ulink> + for information on shared memory under older versions of Solaris. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><systemitem class="osname">Solaris</> 2.10 (Solaris + 10)</term> + <term><systemitem class="osname">OpenSolaris</></term> + <listitem> + <para> + In Solaris 10 and OpenSolaris, the default shared memory and + semaphore settings are good enough for most + <productname>PostgreSQL</> applications. Solaris now defaults + to a <varname>SHMMAX</> of one-quarter of system <acronym>RAM</>. If + you need to increase this in order to set shared memory settings + slightly higher, you should use a project setting associated + with the <literal>postgres</> user. For example, run the + following as <literal>root</>: +<programlisting> +projadd -c "PostgreSQL DB User" -K "project.max-shm-memory=(privileged,8GB,deny)" -U postgres -G postgres user.postgres +</programlisting> + </para> + + <para> + This command adds the <literal>user.postgres</> project and + raises the shared memory maximum for the <literal>postgres</> + user to 8GB, and takes effect the next time that user logs + in, or when you restart <productname>PostgreSQL</> (not reload). + The above assumes that <productname>PostgreSQL</> is run by + the <literal>postgres</> user in the <literal>postgres</> + group. No server reboot is required. + </para> + + <para> + Other recommended kernel setting changes for database servers which will + have a large number of connections are: +<programlisting> +project.max-shm-ids=(priv,32768,deny) +project.max-sem-ids=(priv,4096,deny) +project.max-msg-ids=(priv,4096,deny) +</programlisting> </para> <para> - See also <ulink - url="http://sunsite.uakom.sk/sunworldonline/swol-09-1997/swol-09-insidesolaris.html"></> - for information on shared memory under - <productname>Solaris</>. + Additionally, if you are running <productname>PostgreSQL</> + inside a zone, you may need to raise the zone resource usage + limits as well. See "Chapter2: Projects and Tasks" in the + <citetitle>Solaris 10 System Administrator's Guide</> for more + information on <literal>projects</> and <command>prctl</>. </para> </listitem> </varlistentry> diff --git a/doc/src/sgml/user-manag.sgml b/doc/src/sgml/user-manag.sgml index 1a3ebc650d..d96ff77a04 100644 --- a/doc/src/sgml/user-manag.sgml +++ b/doc/src/sgml/user-manag.sgml @@ -384,7 +384,7 @@ RESET ROLE; roles being used as SQL roles the <literal>INHERIT</> attribute, while giving roles being used as SQL users the <literal>NOINHERIT</> attribute. However, <productname>PostgreSQL</productname> defaults to giving all roles - the <literal>INHERIT</> attribute, for backwards compatibility with pre-8.1 + the <literal>INHERIT</> attribute, for backward compatibility with pre-8.1 releases in which users always had use of permissions granted to groups they were members of. </para> diff --git a/doc/src/sgml/wal.sgml b/doc/src/sgml/wal.sgml index 96d4916e0e..4d111903a7 100644 --- a/doc/src/sgml/wal.sgml +++ b/doc/src/sgml/wal.sgml @@ -85,8 +85,9 @@ <listitem> <para> On <productname>FreeBSD</>, IDE drives can be queried using - <command>atacontrol</command>, and SCSI drives using - <command>sdparm</command>. + <command>atacontrol</command> and write caching turned off using + <literal>hw.ata.wc=0</> in <filename>/boot/loader.conf</>; + SCSI drives use <command>sdparm</command>. </para> </listitem> diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml index 1a35014170..c8d8999659 100644 --- a/doc/src/sgml/xfunc.sgml +++ b/doc/src/sgml/xfunc.sgml @@ -1368,7 +1368,7 @@ CREATE FUNCTION test(int, int) RETURNS int The <quote>body</quote> of the function definition specifies the C-language name of the function, which need not be the same as the name being declared for SQL use. - (For reasons of backwards compatibility, an empty body + (For reasons of backward compatibility, an empty body is accepted as meaning that the C-language function name is the same as the SQL name.) </para> @@ -2701,7 +2701,7 @@ typedef struct /* * OPTIONAL pointer to result slot * - * This is obsolete and only present for backwards compatibility, viz, + * This is obsolete and only present for backward compatibility, viz, * user-defined SRFs that use the deprecated TupleDescGetSlot(). */ TupleTableSlot *slot; diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index bce0e07683..9e2028a64f 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -1357,6 +1357,9 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender, * on the datatype, and OpExpr nodes depend on the operator which depends on * the datatype. However we do need a type dependency if there is no such * indirect dependency, as for example in Const and CoerceToDomain nodes. + * + * Similarly, we don't need to create dependencies on collations except where + * the collation is being freshly introduced to the expression. */ static bool find_expr_references_walker(Node *node, @@ -1425,7 +1428,15 @@ find_expr_references_walker(Node *node, /* A constant must depend on the constant's datatype */ add_object_address(OCLASS_TYPE, con->consttype, 0, context->addrs); - if (OidIsValid(con->constcollid)) + + /* + * We must also depend on the constant's collation: it could be + * different from the datatype's, if a CollateExpr was const-folded + * to a simple constant. However we can save work in the most common + * case where the collation is "default", since we know that's pinned. + */ + if (OidIsValid(con->constcollid) && + con->constcollid != DEFAULT_COLLATION_OID) add_object_address(OCLASS_COLLATION, con->constcollid, 0, context->addrs); @@ -1494,7 +1505,9 @@ find_expr_references_walker(Node *node, /* A parameter must depend on the parameter's datatype */ add_object_address(OCLASS_TYPE, param->paramtype, 0, context->addrs); - if (OidIsValid(param->paramcollation)) + /* and its collation, just as for Consts */ + if (OidIsValid(param->paramcollation) && + param->paramcollation != DEFAULT_COLLATION_OID) add_object_address(OCLASS_COLLATION, param->paramcollation, 0, context->addrs); } @@ -1567,13 +1580,6 @@ find_expr_references_walker(Node *node, add_object_address(OCLASS_TYPE, relab->resulttype, 0, context->addrs); } - else if (IsA(node, CollateClause)) - { - CollateClause *coll = (CollateClause *) node; - - add_object_address(OCLASS_COLLATION, coll->collOid, 0, - context->addrs); - } else if (IsA(node, CoerceViaIO)) { CoerceViaIO *iocoerce = (CoerceViaIO *) node; @@ -1601,6 +1607,13 @@ find_expr_references_walker(Node *node, add_object_address(OCLASS_TYPE, cvt->resulttype, 0, context->addrs); } + else if (IsA(node, CollateExpr)) + { + CollateExpr *coll = (CollateExpr *) node; + + add_object_address(OCLASS_COLLATION, coll->collOid, 0, + context->addrs); + } else if (IsA(node, RowExpr)) { RowExpr *rowexpr = (RowExpr *) node; @@ -1652,10 +1665,11 @@ find_expr_references_walker(Node *node, /* * Add whole-relation refs for each plain relation mentioned in the - * subquery's rtable, as well as datatype refs for any datatypes used - * as a RECORD function's output. (Note: query_tree_walker takes care - * of recursing into RTE_FUNCTION RTEs, subqueries, etc, so no need to - * do that here. But keep it from looking at join alias lists.) + * subquery's rtable, as well as refs for any datatypes and collations + * used in a RECORD function's output. (Note: query_tree_walker takes + * care of recursing into RTE_FUNCTION RTEs, subqueries, etc, so no + * need to do that here. But keep it from looking at join alias + * lists.) */ foreach(lc, query->rtable) { @@ -1678,7 +1692,8 @@ find_expr_references_walker(Node *node, { Oid collid = lfirst_oid(ct); - if (OidIsValid(collid)) + if (OidIsValid(collid) && + collid != DEFAULT_COLLATION_OID) add_object_address(OCLASS_COLLATION, collid, 0, context->addrs); } diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 1c47d81ba8..c623fb7e75 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -448,7 +448,7 @@ CREATE VIEW collations AS CAST('NO PAD' AS character_data) AS pad_attribute FROM pg_collation c, pg_namespace nc WHERE c.collnamespace = nc.oid - AND collencoding = (SELECT encoding FROM pg_catalog.pg_database WHERE datname = pg_catalog.current_database()); + AND collencoding IN (-1, (SELECT encoding FROM pg_database WHERE datname = current_database())); GRANT SELECT ON collations TO PUBLIC; @@ -467,7 +467,7 @@ CREATE VIEW collation_character_set_applicability AS CAST(getdatabaseencoding() AS sql_identifier) AS character_set_name FROM pg_collation c, pg_namespace nc WHERE c.collnamespace = nc.oid - AND collencoding = (SELECT encoding FROM pg_catalog.pg_database WHERE datname = pg_catalog.current_database()); + AND collencoding IN (-1, (SELECT encoding FROM pg_database WHERE datname = current_database())); GRANT SELECT ON collation_character_set_applicability TO PUBLIC; @@ -2036,7 +2036,7 @@ CREATE VIEW usage_privileges AS WHERE u.oid = c.collowner AND c.collnamespace = n.oid - AND c.collencoding = (SELECT encoding FROM pg_catalog.pg_database WHERE datname = pg_catalog.current_database()) + AND collencoding IN (-1, (SELECT encoding FROM pg_database WHERE datname = current_database())) UNION ALL diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 8b04b9fd9b..77c9805aed 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -1617,13 +1617,11 @@ OpfamilyIsVisible(Oid opfid) * CollationGetCollid * Try to resolve an unqualified collation name. * Returns OID if collation found in search path, else InvalidOid. - * - * This is essentially the same as RelnameGetRelid. */ Oid CollationGetCollid(const char *collname) { - Oid collid; + int32 dbencoding = GetDatabaseEncoding(); ListCell *l; recomputeNamespacePath(); @@ -1631,13 +1629,23 @@ CollationGetCollid(const char *collname) foreach(l, activeSearchPath) { Oid namespaceId = lfirst_oid(l); + Oid collid; if (namespaceId == myTempNamespace) continue; /* do not look in temp namespace */ + /* Check for database-encoding-specific entry */ + collid = GetSysCacheOid3(COLLNAMEENCNSP, + PointerGetDatum(collname), + Int32GetDatum(dbencoding), + ObjectIdGetDatum(namespaceId)); + if (OidIsValid(collid)) + return collid; + + /* Check for any-encoding entry */ collid = GetSysCacheOid3(COLLNAMEENCNSP, PointerGetDatum(collname), - Int32GetDatum(GetDatabaseEncoding()), + Int32GetDatum(-1), ObjectIdGetDatum(namespaceId)); if (OidIsValid(collid)) return collid; @@ -2901,12 +2909,10 @@ get_collation_oid(List *name, bool missing_ok) { char *schemaname; char *collation_name; + int32 dbencoding = GetDatabaseEncoding(); Oid namespaceId; - Oid colloid = InvalidOid; + Oid colloid; ListCell *l; - int encoding; - - encoding = GetDatabaseEncoding(); /* deconstruct the name list */ DeconstructQualifiedName(name, &schemaname, &collation_name); @@ -2915,10 +2921,20 @@ get_collation_oid(List *name, bool missing_ok) { /* use exact schema given */ namespaceId = LookupExplicitNamespace(schemaname); + + /* first try for encoding-specific entry, then any-encoding */ colloid = GetSysCacheOid3(COLLNAMEENCNSP, PointerGetDatum(collation_name), - Int32GetDatum(encoding), + Int32GetDatum(dbencoding), ObjectIdGetDatum(namespaceId)); + if (OidIsValid(colloid)) + return colloid; + colloid = GetSysCacheOid3(COLLNAMEENCNSP, + PointerGetDatum(collation_name), + Int32GetDatum(-1), + ObjectIdGetDatum(namespaceId)); + if (OidIsValid(colloid)) + return colloid; } else { @@ -2934,7 +2950,13 @@ get_collation_oid(List *name, bool missing_ok) colloid = GetSysCacheOid3(COLLNAMEENCNSP, PointerGetDatum(collation_name), - Int32GetDatum(encoding), + Int32GetDatum(dbencoding), + ObjectIdGetDatum(namespaceId)); + if (OidIsValid(colloid)) + return colloid; + colloid = GetSysCacheOid3(COLLNAMEENCNSP, + PointerGetDatum(collation_name), + Int32GetDatum(-1), ObjectIdGetDatum(namespaceId)); if (OidIsValid(colloid)) return colloid; @@ -2942,12 +2964,12 @@ get_collation_oid(List *name, bool missing_ok) } /* Not found in path */ - if (!OidIsValid(colloid) && !missing_ok) + if (!missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("collation \"%s\" for current database encoding \"%s\" does not exist", + errmsg("collation \"%s\" for encoding \"%s\" does not exist", NameListToString(name), GetDatabaseEncodingName()))); - return colloid; + return InvalidOid; } /* diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c index 54a75a6f62..708078463b 100644 --- a/src/backend/catalog/pg_collation.c +++ b/src/backend/catalog/pg_collation.c @@ -14,6 +14,7 @@ */ #include "postgres.h" +#include "access/genam.h" #include "access/heapam.h" #include "access/sysattr.h" #include "catalog/dependency.h" @@ -22,16 +23,13 @@ #include "catalog/pg_collation.h" #include "catalog/pg_collation_fn.h" #include "catalog/pg_namespace.h" -#include "catalog/pg_proc.h" #include "mb/pg_wchar.h" -#include "miscadmin.h" -#include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" -#include "utils/rel.h" #include "utils/syscache.h" #include "utils/tqual.h" + /* * CollationCreate * @@ -43,12 +41,11 @@ CollationCreate(const char *collname, Oid collnamespace, int32 collencoding, const char *collcollate, const char *collctype) { - int i; Relation rel; TupleDesc tupDesc; HeapTuple tup; - bool nulls[Natts_pg_collation]; Datum values[Natts_pg_collation]; + bool nulls[Natts_pg_collation]; NameData name_name, name_collate, name_ctype; Oid oid; ObjectAddress myself, @@ -60,7 +57,13 @@ CollationCreate(const char *collname, Oid collnamespace, AssertArg(collcollate); AssertArg(collctype); - /* make sure there is no existing collation of same name */ + /* + * Make sure there is no existing collation of same name & encoding. + * + * This would be caught by the unique index anyway; we're just giving + * a friendlier error message. The unique index provides a backstop + * against race conditions. + */ if (SearchSysCacheExists3(COLLNAMEENCNSP, PointerGetDatum(collname), Int32GetDatum(collencoding), @@ -70,18 +73,27 @@ CollationCreate(const char *collname, Oid collnamespace, errmsg("collation \"%s\" for encoding \"%s\" already exists", collname, pg_encoding_to_char(collencoding)))); + /* + * Also forbid matching an any-encoding entry. This test of course is + * not backed up by the unique index, but it's not a problem since we + * don't support adding any-encoding entries after initdb. + */ + if (SearchSysCacheExists3(COLLNAMEENCNSP, + PointerGetDatum(collname), + Int32GetDatum(-1), + ObjectIdGetDatum(collnamespace))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("collation \"%s\" already exists", + collname))); + /* open pg_collation */ rel = heap_open(CollationRelationId, RowExclusiveLock); - tupDesc = rel->rd_att; - - /* initialize nulls and values */ - for (i = 0; i < Natts_pg_collation; i++) - { - nulls[i] = false; - values[i] = (Datum) NULL; - } + tupDesc = RelationGetDescr(rel); /* form a tuple */ + memset(nulls, 0, sizeof(nulls)); + namestrcpy(&name_name, collname); values[Anum_pg_collation_collname - 1] = NameGetDatum(&name_name); values[Anum_pg_collation_collnamespace - 1] = ObjectIdGetDatum(collnamespace); @@ -101,8 +113,9 @@ CollationCreate(const char *collname, Oid collnamespace, /* update the index if any */ CatalogUpdateIndexes(rel, tup); + /* set up dependencies for the new collation */ myself.classId = CollationRelationId; - myself.objectId = HeapTupleGetOid(tup); + myself.objectId = oid; myself.objectSubId = 0; /* create dependency on namespace */ @@ -120,7 +133,7 @@ CollationCreate(const char *collname, Oid collnamespace, /* Post creation hook for new collation */ InvokeObjectAccessHook(OAT_POST_CREATE, - CollationRelationId, HeapTupleGetOid(tup), 0); + CollationRelationId, oid, 0); heap_freetuple(tup); heap_close(rel, RowExclusiveLock); @@ -138,26 +151,28 @@ void RemoveCollationById(Oid collationOid) { Relation rel; - HeapTuple tuple; - HeapScanDesc scan; ScanKeyData scanKeyData; + SysScanDesc scandesc; + HeapTuple tuple; + + rel = heap_open(CollationRelationId, RowExclusiveLock); ScanKeyInit(&scanKeyData, ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(collationOid)); - /* open pg_collation */ - rel = heap_open(CollationRelationId, RowExclusiveLock); + scandesc = systable_beginscan(rel, CollationOidIndexId, true, + SnapshotNow, 1, &scanKeyData); - scan = heap_beginscan(rel, SnapshotNow, - 1, &scanKeyData); + tuple = systable_getnext(scandesc); - /* search for the target tuple */ - if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) + if (HeapTupleIsValid(tuple)) simple_heap_delete(rel, &tuple->t_self); else elog(ERROR, "could not find tuple for collation %u", collationOid); - heap_endscan(scan); + + systable_endscan(scandesc); + heap_close(rel, RowExclusiveLock); } diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index bafdc80d58..a9acc7c303 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -24,6 +24,7 @@ #include "catalog/index.h" #include "catalog/indexing.h" #include "catalog/namespace.h" +#include "catalog/pg_collation.h" #include "catalog/pg_inherits_fn.h" #include "catalog/pg_namespace.h" #include "commands/dbcommands.h" @@ -862,13 +863,11 @@ examine_attribute(Relation onerel, int attnum, Node *index_expr) { stats->attrtypid = exprType(index_expr); stats->attrtypmod = exprTypmod(index_expr); - stats->attrcollation = exprCollation(index_expr); } else { stats->attrtypid = attr->atttypid; stats->attrtypmod = attr->atttypmod; - stats->attrcollation = attr->attcollation; } typtuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(stats->attrtypid)); @@ -1931,7 +1930,8 @@ compute_minimal_stats(VacAttrStatsP stats, track_cnt = 0; fmgr_info(mystats->eqfunc, &f_cmpeq); - fmgr_info_collation(stats->attrcollation, &f_cmpeq); + /* We always use the default collation for statistics */ + fmgr_info_collation(DEFAULT_COLLATION_OID, &f_cmpeq); for (i = 0; i < samplerows; i++) { @@ -2253,7 +2253,8 @@ compute_scalar_stats(VacAttrStatsP stats, SelectSortFunction(mystats->ltopr, false, &cmpFn, &cmpFlags); fmgr_info(cmpFn, &f_cmpfn); - fmgr_info_collation(stats->attrcollation, &f_cmpfn); + /* We always use the default collation for statistics */ + fmgr_info_collation(DEFAULT_COLLATION_OID, &f_cmpfn); /* Initial scan to find sortable values */ for (i = 0; i < samplerows; i++) diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c index a52cb351ac..6dafb7223c 100644 --- a/src/backend/commands/collationcmds.c +++ b/src/backend/commands/collationcmds.c @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * collationcmds.c - * collation creation command support code + * collation-related commands support code * * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California @@ -27,7 +27,6 @@ #include "commands/defrem.h" #include "mb/pg_wchar.h" #include "miscadmin.h" -#include "parser/parse_type.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -134,11 +133,11 @@ DefineCollation(List *names, List *parameters) check_encoding_locale_matches(GetDatabaseEncoding(), collcollate, collctype); newoid = CollationCreate(collName, - collNamespace, - GetUserId(), - GetDatabaseEncoding(), - collcollate, - collctype); + collNamespace, + GetUserId(), + GetDatabaseEncoding(), + collcollate, + collctype); /* check that the locales can be loaded */ CommandCounterIncrement(); @@ -235,11 +234,22 @@ RenameCollation(List *name, const char *newname) ObjectIdGetDatum(namespaceOid))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("collation \"%s\" for current database encoding \"%s\" already exists in schema \"%s\"", + errmsg("collation \"%s\" for encoding \"%s\" already exists in schema \"%s\"", newname, GetDatabaseEncodingName(), get_namespace_name(namespaceOid)))); + /* mustn't match an any-encoding entry, either */ + if (SearchSysCacheExists3(COLLNAMEENCNSP, + CStringGetDatum(newname), + Int32GetDatum(-1), + ObjectIdGetDatum(namespaceOid))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("collation \"%s\" already exists in schema \"%s\"", + newname, + get_namespace_name(namespaceOid)))); + /* must be owner */ if (!pg_collation_ownercheck(collationOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_COLLATION, @@ -256,8 +266,9 @@ RenameCollation(List *name, const char *newname) simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); - heap_close(rel, NoLock); heap_freetuple(tup); + + heap_close(rel, RowExclusiveLock); } /* @@ -275,7 +286,7 @@ AlterCollationOwner(List *name, Oid newOwnerId) AlterCollationOwner_internal(rel, collationOid, newOwnerId); - heap_close(rel, NoLock); + heap_close(rel, RowExclusiveLock); } /* @@ -290,7 +301,7 @@ AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId) AlterCollationOwner_internal(rel, collationOid, newOwnerId); - heap_close(rel, NoLock); + heap_close(rel, RowExclusiveLock); } /* @@ -364,24 +375,14 @@ AlterCollationOwner_internal(Relation rel, Oid collationOid, Oid newOwnerId) void AlterCollationNamespace(List *name, const char *newschema) { - Oid collOid, nspOid; - Relation rel; - - rel = heap_open(CollationRelationId, RowExclusiveLock); + Oid collOid, + nspOid; collOid = get_collation_oid(name, false); - /* get schema OID */ nspOid = LookupCreationNamespace(newschema); - AlterObjectNamespace(rel, COLLOID, -1, - collOid, nspOid, - Anum_pg_collation_collname, - Anum_pg_collation_collnamespace, - Anum_pg_collation_collowner, - ACL_KIND_COLLATION); - - heap_close(rel, NoLock); + AlterCollationNamespace_oid(collOid, nspOid); } /* @@ -392,9 +393,43 @@ AlterCollationNamespace_oid(Oid collOid, Oid newNspOid) { Oid oldNspOid; Relation rel; + char *collation_name; rel = heap_open(CollationRelationId, RowExclusiveLock); + /* + * We have to check for name collision ourselves, because + * AlterObjectNamespace doesn't know how to deal with the encoding + * considerations. + */ + collation_name = get_collation_name(collOid); + if (!collation_name) + elog(ERROR, "cache lookup failed for collation %u", collOid); + + /* make sure the name doesn't already exist in new schema */ + if (SearchSysCacheExists3(COLLNAMEENCNSP, + CStringGetDatum(collation_name), + Int32GetDatum(GetDatabaseEncoding()), + ObjectIdGetDatum(newNspOid))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("collation \"%s\" for encoding \"%s\" already exists in schema \"%s\"", + collation_name, + GetDatabaseEncodingName(), + get_namespace_name(newNspOid)))); + + /* mustn't match an any-encoding entry, either */ + if (SearchSysCacheExists3(COLLNAMEENCNSP, + CStringGetDatum(collation_name), + Int32GetDatum(-1), + ObjectIdGetDatum(newNspOid))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("collation \"%s\" already exists in schema \"%s\"", + collation_name, + get_namespace_name(newNspOid)))); + + /* OK, do the work */ oldNspOid = AlterObjectNamespace(rel, COLLOID, -1, collOid, newNspOid, Anum_pg_collation_collname, diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 3513256b9a..ee3bca17d1 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -831,7 +831,7 @@ DefineDomain(CreateDomainStmt *stmt) */ baseColl = baseType->typcollation; if (stmt->collClause) - domaincoll = get_collation_oid(stmt->collClause->collnames, false); + domaincoll = get_collation_oid(stmt->collClause->collname, false); else domaincoll = baseColl; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 2b5dd2dbf8..0faf52dfd7 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -120,6 +120,9 @@ static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, static Datum ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); +static Datum ExecEvalCollateExpr(GenericExprState *exprstate, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalCaseTestExpr(ExprState *exprstate, @@ -166,9 +169,6 @@ static Datum ExecEvalFieldStore(FieldStoreState *fstate, static Datum ExecEvalRelabelType(GenericExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); -static Datum ExecEvalCollateClause(GenericExprState *exprstate, - ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalCoerceViaIO(CoerceViaIOState *iostate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); @@ -2754,6 +2754,20 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate, } /* ---------------------------------------------------------------- + * ExecEvalCollateExpr + * + * Evaluate a CollateExpr node. + * ---------------------------------------------------------------- + */ +static Datum +ExecEvalCollateExpr(GenericExprState *exprstate, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone) +{ + return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone); +} + +/* ---------------------------------------------------------------- * ExecEvalCase * * Evaluate a CASE clause. Will have boolean expressions @@ -4029,20 +4043,6 @@ ExecEvalRelabelType(GenericExprState *exprstate, } /* ---------------------------------------------------------------- - * ExecEvalCollateClause - * - * Evaluate a CollateClause node. - * ---------------------------------------------------------------- - */ -static Datum -ExecEvalCollateClause(GenericExprState *exprstate, - ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) -{ - return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone); -} - -/* ---------------------------------------------------------------- * ExecEvalCoerceViaIO * * Evaluate a CoerceViaIO node. @@ -4501,16 +4501,6 @@ ExecInitExpr(Expr *node, PlanState *parent) state = (ExprState *) gstate; } break; - case T_CollateClause: - { - CollateClause *collate = (CollateClause *) node; - GenericExprState *gstate = makeNode(GenericExprState); - - gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCollateClause; - gstate->arg = ExecInitExpr(collate->arg, parent); - state = (ExprState *) gstate; - } - break; case T_CoerceViaIO: { CoerceViaIO *iocoerce = (CoerceViaIO *) node; @@ -4561,6 +4551,16 @@ ExecInitExpr(Expr *node, PlanState *parent) state = (ExprState *) cstate; } break; + case T_CollateExpr: + { + CollateExpr *collate = (CollateExpr *) node; + GenericExprState *gstate = makeNode(GenericExprState); + + gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCollateExpr; + gstate->arg = ExecInitExpr(collate->arg, parent); + state = (ExprState *) gstate; + } + break; case T_CaseExpr: { CaseExpr *caseexpr = (CaseExpr *) node; diff --git a/src/backend/libpq/pqsignal.c b/src/backend/libpq/pqsignal.c index 31e37e9a2b..0fee280ae3 100644 --- a/src/backend/libpq/pqsignal.c +++ b/src/backend/libpq/pqsignal.c @@ -22,7 +22,7 @@ * how to handle signalling. * * signal(2) handling - this is here because it affects some of - * the frontend commands as well as the backend server. + * the frontend commands as well as the backend processes. * * Ultrix and SunOS provide BSD signal(2) semantics by default. * diff --git a/src/backend/main/main.c b/src/backend/main/main.c index 5d077888c9..43d182b4db 100644 --- a/src/backend/main/main.c +++ b/src/backend/main/main.c @@ -310,7 +310,7 @@ help(const char *progname) printf(_(" -O allow system table structure changes\n")); printf(_(" -P disable system indexes\n")); printf(_(" -t pa|pl|ex show timings after each query\n")); - printf(_(" -T send SIGSTOP to all backend servers if one dies\n")); + printf(_(" -T send SIGSTOP to all backend processes if one dies\n")); printf(_(" -W NUM wait NUM seconds to allow attach from a debugger\n")); printf(_("\nOptions for single-user mode:\n")); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index b948af604d..c0490e93ea 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1435,6 +1435,21 @@ _copyConvertRowtypeExpr(ConvertRowtypeExpr *from) } /* + * _copyCollateExpr + */ +static CollateExpr * +_copyCollateExpr(CollateExpr *from) +{ + CollateExpr *newnode = makeNode(CollateExpr); + + COPY_NODE_FIELD(arg); + COPY_SCALAR_FIELD(collOid); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* * _copyCaseExpr */ static CaseExpr * @@ -2260,8 +2275,7 @@ _copyCollateClause(CollateClause *from) CollateClause *newnode = makeNode(CollateClause); COPY_NODE_FIELD(arg); - COPY_NODE_FIELD(collnames); - COPY_SCALAR_FIELD(collOid); + COPY_NODE_FIELD(collname); COPY_LOCATION_FIELD(location); return newnode; @@ -4017,6 +4031,9 @@ copyObject(void *from) case T_ConvertRowtypeExpr: retval = _copyConvertRowtypeExpr(from); break; + case T_CollateExpr: + retval = _copyCollateExpr(from); + break; case T_CaseExpr: retval = _copyCaseExpr(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index c8ee474436..3726006f1d 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -494,6 +494,16 @@ _equalConvertRowtypeExpr(ConvertRowtypeExpr *a, ConvertRowtypeExpr *b) } static bool +_equalCollateExpr(CollateExpr *a, CollateExpr *b) +{ + COMPARE_NODE_FIELD(arg); + COMPARE_SCALAR_FIELD(collOid); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool _equalCaseExpr(CaseExpr *a, CaseExpr *b) { COMPARE_SCALAR_FIELD(casetype); @@ -2149,8 +2159,7 @@ static bool _equalCollateClause(CollateClause *a, CollateClause *b) { COMPARE_NODE_FIELD(arg); - COMPARE_NODE_FIELD(collnames); - COMPARE_SCALAR_FIELD(collOid); + COMPARE_NODE_FIELD(collname); COMPARE_LOCATION_FIELD(location); return true; @@ -2583,6 +2592,9 @@ equal(void *a, void *b) case T_ConvertRowtypeExpr: retval = _equalConvertRowtypeExpr(a, b); break; + case T_CollateExpr: + retval = _equalCollateExpr(a, b); + break; case T_CaseExpr: retval = _equalCaseExpr(a, b); break; diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index c3c5d8e6e5..5394851a1f 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -162,9 +162,6 @@ exprType(Node *expr) case T_RelabelType: type = ((RelabelType *) expr)->resulttype; break; - case T_CollateClause: - type = exprType((Node *) ((CollateClause *) expr)->arg); - break; case T_CoerceViaIO: type = ((CoerceViaIO *) expr)->resulttype; break; @@ -174,6 +171,9 @@ exprType(Node *expr) case T_ConvertRowtypeExpr: type = ((ConvertRowtypeExpr *) expr)->resulttype; break; + case T_CollateExpr: + type = exprType((Node *) ((CollateExpr *) expr)->arg); + break; case T_CaseExpr: type = ((CaseExpr *) expr)->casetype; break; @@ -321,6 +321,8 @@ exprTypmod(Node *expr) return ((RelabelType *) expr)->resulttypmod; case T_ArrayCoerceExpr: return ((ArrayCoerceExpr *) expr)->resulttypmod; + case T_CollateExpr: + return exprTypmod((Node *) ((CollateExpr *) expr)->arg); case T_CaseExpr: { /* @@ -571,9 +573,6 @@ exprCollation(Node *expr) case T_RelabelType: coll = exprCollation((Node *) ((RelabelType *) expr)->arg); break; - case T_CollateClause: - coll = ((CollateClause *) expr)->collOid; - break; case T_CoerceViaIO: { CoerceViaIO *cvio = (CoerceViaIO *) expr; @@ -592,6 +591,9 @@ exprCollation(Node *expr) coll = coercion_expression_result_collation(cre->resulttype, (Node *) cre->arg); break; } + case T_CollateExpr: + coll = ((CollateExpr *) expr)->collOid; + break; case T_CaseExpr: coll = ((CaseExpr *) expr)->casecollation; break; @@ -989,6 +991,10 @@ exprLocation(Node *expr) exprLocation((Node *) cexpr->arg)); } break; + case T_CollateExpr: + /* just use argument's location */ + loc = exprLocation((Node *) ((CollateExpr *) expr)->arg); + break; case T_CaseExpr: /* CASE keyword should always be the first thing */ loc = ((CaseExpr *) expr)->location; @@ -1122,7 +1128,8 @@ exprLocation(Node *expr) } break; case T_CollateClause: - loc = ((CollateClause *) expr)->location; + /* just use argument's location */ + loc = exprLocation(((CollateClause *) expr)->arg); break; case T_SortBy: /* just use argument's location (ignore operator, if any) */ @@ -1436,14 +1443,14 @@ expression_tree_walker(Node *node, break; case T_RelabelType: return walker(((RelabelType *) node)->arg, context); - case T_CollateClause: - return walker(((CollateClause *) node)->arg, context); case T_CoerceViaIO: return walker(((CoerceViaIO *) node)->arg, context); case T_ArrayCoerceExpr: return walker(((ArrayCoerceExpr *) node)->arg, context); case T_ConvertRowtypeExpr: return walker(((ConvertRowtypeExpr *) node)->arg, context); + case T_CollateExpr: + return walker(((CollateExpr *) node)->arg, context); case T_CaseExpr: { CaseExpr *caseexpr = (CaseExpr *) node; @@ -1993,16 +2000,6 @@ expression_tree_mutator(Node *node, return (Node *) newnode; } break; - case T_CollateClause: - { - CollateClause *collate = (CollateClause *) node; - CollateClause *newnode; - - FLATCOPY(newnode, collate, CollateClause); - MUTATE(newnode->arg, collate->arg, Expr *); - return (Node *) newnode; - } - break; case T_CoerceViaIO: { CoerceViaIO *iocoerce = (CoerceViaIO *) node; @@ -2033,6 +2030,16 @@ expression_tree_mutator(Node *node, return (Node *) newnode; } break; + case T_CollateExpr: + { + CollateExpr *collate = (CollateExpr *) node; + CollateExpr *newnode; + + FLATCOPY(newnode, collate, CollateExpr); + MUTATE(newnode->arg, collate->arg, Expr *); + return (Node *) newnode; + } + break; case T_CaseExpr: { CaseExpr *caseexpr = (CaseExpr *) node; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 06fd7ff818..d56e4dac01 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1196,6 +1196,16 @@ _outConvertRowtypeExpr(StringInfo str, ConvertRowtypeExpr *node) } static void +_outCollateExpr(StringInfo str, CollateExpr *node) +{ + WRITE_NODE_TYPE("COLLATE"); + + WRITE_NODE_FIELD(arg); + WRITE_OID_FIELD(collOid); + WRITE_LOCATION_FIELD(location); +} + +static void _outCaseExpr(StringInfo str, CaseExpr *node) { WRITE_NODE_TYPE("CASE"); @@ -2104,11 +2114,10 @@ _outTypeCast(StringInfo str, TypeCast *node) static void _outCollateClause(StringInfo str, CollateClause *node) { - WRITE_NODE_TYPE("COLLATE"); + WRITE_NODE_TYPE("COLLATECLAUSE"); WRITE_NODE_FIELD(arg); - WRITE_NODE_FIELD(collnames); - WRITE_OID_FIELD(collOid); + WRITE_NODE_FIELD(collname); WRITE_LOCATION_FIELD(location); } @@ -2829,9 +2838,6 @@ _outNode(StringInfo str, void *obj) case T_RelabelType: _outRelabelType(str, obj); break; - case T_CollateClause: - _outCollateClause(str, obj); - break; case T_CoerceViaIO: _outCoerceViaIO(str, obj); break; @@ -2841,6 +2847,9 @@ _outNode(StringInfo str, void *obj) case T_ConvertRowtypeExpr: _outConvertRowtypeExpr(str, obj); break; + case T_CollateExpr: + _outCollateExpr(str, obj); + break; case T_CaseExpr: _outCaseExpr(str, obj); break; @@ -3020,6 +3029,9 @@ _outNode(StringInfo str, void *obj) case T_TypeCast: _outTypeCast(str, obj); break; + case T_CollateClause: + _outCollateClause(str, obj); + break; case T_IndexElem: _outIndexElem(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 09c5e25012..6da61285b0 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -744,22 +744,6 @@ _readRelabelType(void) } /* - * _readCollateClause - */ -static CollateClause * -_readCollateClause(void) -{ - READ_LOCALS(CollateClause); - - READ_NODE_FIELD(arg); - READ_NODE_FIELD(collnames); - READ_OID_FIELD(collOid); - READ_LOCATION_FIELD(location); - - READ_DONE(); -} - -/* * _readCoerceViaIO */ static CoerceViaIO * @@ -811,6 +795,21 @@ _readConvertRowtypeExpr(void) } /* + * _readCollateExpr + */ +static CollateExpr * +_readCollateExpr(void) +{ + READ_LOCALS(CollateExpr); + + READ_NODE_FIELD(arg); + READ_OID_FIELD(collOid); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* * _readCaseExpr */ static CaseExpr * @@ -1286,14 +1285,14 @@ parseNodeString(void) return_value = _readFieldStore(); else if (MATCH("RELABELTYPE", 11)) return_value = _readRelabelType(); - else if (MATCH("COLLATE", 7)) - return_value = _readCollateClause(); else if (MATCH("COERCEVIAIO", 11)) return_value = _readCoerceViaIO(); else if (MATCH("ARRAYCOERCEEXPR", 15)) return_value = _readArrayCoerceExpr(); else if (MATCH("CONVERTROWTYPEEXPR", 18)) return_value = _readConvertRowtypeExpr(); + else if (MATCH("COLLATE", 7)) + return_value = _readCollateExpr(); else if (MATCH("CASE", 4)) return_value = _readCaseExpr(); else if (MATCH("WHEN", 4)) diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index c292e24ac3..756874b817 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -2056,7 +2056,6 @@ cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey) mergejoinscansel(root, (Node *) rinfo->clause, pathkey->pk_opfamily, - pathkey->pk_collation, pathkey->pk_strategy, pathkey->pk_nulls_first, &leftstartsel, diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index fa0952618b..8503792df4 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -1308,6 +1308,12 @@ find_nonnullable_rels_walker(Node *node, bool top_level) result = find_nonnullable_rels_walker((Node *) expr->arg, top_level); } + else if (IsA(node, CollateExpr)) + { + CollateExpr *expr = (CollateExpr *) node; + + result = find_nonnullable_rels_walker((Node *) expr->arg, top_level); + } else if (IsA(node, NullTest)) { /* IS NOT NULL can be considered strict, but only at top level */ @@ -1510,6 +1516,12 @@ find_nonnullable_vars_walker(Node *node, bool top_level) result = find_nonnullable_vars_walker((Node *) expr->arg, top_level); } + else if (IsA(node, CollateExpr)) + { + CollateExpr *expr = (CollateExpr *) node; + + result = find_nonnullable_vars_walker((Node *) expr->arg, top_level); + } else if (IsA(node, NullTest)) { /* IS NOT NULL can be considered strict, but only at top level */ @@ -2580,6 +2592,42 @@ eval_const_expressions_mutator(Node *node, /* Else we must return the partially-simplified node */ return (Node *) newexpr; } + if (IsA(node, CollateExpr)) + { + /* + * If we can simplify the input to a constant, then we don't need the + * CollateExpr node anymore: just change the constcollid field of the + * Const node. Otherwise, must copy the CollateExpr node. + */ + CollateExpr *collate = (CollateExpr *) node; + Node *arg; + + arg = eval_const_expressions_mutator((Node *) collate->arg, + context); + + /* + * If we find stacked CollateExprs, we can discard all but the top one. + */ + while (arg && IsA(arg, CollateExpr)) + arg = (Node *) ((CollateExpr *) arg)->arg; + + if (arg && IsA(arg, Const)) + { + Const *con = (Const *) arg; + + con->constcollid = collate->collOid; + return (Node *) con; + } + else + { + CollateExpr *newcollate = makeNode(CollateExpr); + + newcollate->arg = (Expr *) arg; + newcollate->collOid = collate->collOid; + newcollate->location = collate->location; + return (Node *) newcollate; + } + } if (IsA(node, CaseExpr)) { /*---------- diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 1158f7715b..b60c88d925 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -254,8 +254,9 @@ add_path(RelOptInfo *parent_rel, Path *new_path) { bool accept_new = true; /* unless we find a superior old path */ ListCell *insert_after = NULL; /* where to insert new item */ - ListCell *p1_prev = NULL; ListCell *p1; + ListCell *p1_prev; + ListCell *p1_next; /* * This is a convenient place to check for query cancel --- no part of the @@ -267,14 +268,19 @@ add_path(RelOptInfo *parent_rel, Path *new_path) * Loop to check proposed new path against old paths. Note it is possible * for more than one old path to be tossed out because new_path dominates * it. + * + * We can't use foreach here because the loop body may delete the current + * list cell. */ - p1 = list_head(parent_rel->pathlist); /* cannot use foreach here */ - while (p1 != NULL) + p1_prev = NULL; + for (p1 = list_head(parent_rel->pathlist); p1 != NULL; p1 = p1_next) { Path *old_path = (Path *) lfirst(p1); bool remove_old = false; /* unless new proves superior */ int costcmp; + p1_next = lnext(p1); + /* * As of Postgres 8.0, we use fuzzy cost comparison to avoid wasting * cycles keeping paths that are really not significantly different in @@ -343,20 +349,15 @@ add_path(RelOptInfo *parent_rel, Path *new_path) */ if (!IsA(old_path, IndexPath)) pfree(old_path); - /* Advance list pointer */ - if (p1_prev) - p1 = lnext(p1_prev); - else - p1 = list_head(parent_rel->pathlist); + /* p1_prev does not advance */ } else { /* new belongs after this old path if it has cost >= old's */ if (costcmp >= 0) insert_after = p1; - /* Advance list pointers */ + /* p1_prev advances */ p1_prev = p1; - p1 = lnext(p1); } /* diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 373d2adc71..1633499f93 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -1993,8 +1993,7 @@ opt_collate_clause: { CollateClause *n = makeNode(CollateClause); n->arg = NULL; - n->collnames = $2; - n->collOid = InvalidOid; + n->collname = $2; n->location = @1; $$ = (Node *) n; } @@ -2537,8 +2536,7 @@ ColConstraint: */ CollateClause *n = makeNode(CollateClause); n->arg = NULL; - n->collnames = $2; - n->collOid = InvalidOid; + n->collname = $2; n->location = @1; $$ = (Node *) n; } @@ -9690,8 +9688,8 @@ a_expr: c_expr { $$ = $1; } | a_expr COLLATE any_name { CollateClause *n = makeNode(CollateClause); - n->arg = (Expr *) $1; - n->collnames = $3; + n->arg = $1; + n->collname = $3; n->location = @2; $$ = (Node *) n; } diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 2fd808d26b..6aff34dd90 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -279,11 +279,17 @@ coerce_type(ParseState *pstate, Node *node, if (result) return result; } - if (IsA(node, CollateClause)) + if (IsA(node, CollateExpr)) { - CollateClause *cc = (CollateClause *) node; + /* + * XXX very ugly kluge to push the coercion underneath the CollateExpr. + * This needs to be rethought, as it almost certainly doesn't cover + * all cases. + */ + CollateExpr *cc = (CollateExpr *) node; - cc->arg = (Expr *) coerce_type(pstate, (Node *) cc->arg, inputTypeId, targetTypeId, targetTypeMod, + cc->arg = (Expr *) coerce_type(pstate, (Node *) cc->arg, + inputTypeId, targetTypeId, targetTypeMod, ccontext, cformat, location); return (Node *) cc; } @@ -2121,7 +2127,7 @@ select_common_collation(ParseState *pstate, List *exprs, bool none_ok) { Node *pexpr = (Node *) lfirst(lc); Oid pcoll = exprCollation(pexpr); - bool pexplicit = IsA(pexpr, CollateClause); + bool pexplicit = IsA(pexpr, CollateExpr); if (pcoll && pexplicit) { @@ -2130,7 +2136,7 @@ select_common_collation(ParseState *pstate, List *exprs, bool none_ok) { Node *nexpr = (Node *) lfirst(lc2); Oid ncoll = exprCollation(nexpr); - bool nexplicit = IsA(nexpr, CollateClause); + bool nexplicit = IsA(nexpr, CollateExpr); if (!ncoll || !nexplicit) continue; diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 7a4f8cc249..17bd2bf50a 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -318,6 +318,7 @@ transformExpr(ParseState *pstate, Node *expr) case T_CoerceViaIO: case T_ArrayCoerceExpr: case T_ConvertRowtypeExpr: + case T_CollateExpr: case T_CaseTestExpr: case T_ArrayExpr: case T_CoerceToDomain: @@ -2103,11 +2104,11 @@ transformTypeCast(ParseState *pstate, TypeCast *tc) static Node * transformCollateClause(ParseState *pstate, CollateClause *c) { - CollateClause *newc; + CollateExpr *newc; Oid argtype; - newc = makeNode(CollateClause); - newc->arg = (Expr *) transformExpr(pstate, (Node *) c->arg); + newc = makeNode(CollateExpr); + newc->arg = (Expr *) transformExpr(pstate, c->arg); argtype = exprType((Node *) newc->arg); /* @@ -2121,8 +2122,7 @@ transformCollateClause(ParseState *pstate, CollateClause *c) format_type_be(argtype)), parser_errposition(pstate, c->location))); - newc->collOid = LookupCollation(pstate, c->collnames, c->location); - newc->collnames = c->collnames; + newc->collOid = LookupCollation(pstate, c->collname, c->location); newc->location = c->location; return (Node *) newc; diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index c0eaea71a6..fd1529fb3f 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1583,7 +1583,7 @@ FigureColnameInternal(Node *node, char **name) } break; case T_CollateClause: - return FigureColnameInternal((Node *) ((CollateClause *) node)->arg, name); + return FigureColnameInternal(((CollateClause *) node)->arg, name); case T_CaseExpr: strength = FigureColnameInternal((Node *) ((CaseExpr *) node)->defresult, name); diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 2ba9bf5181..f413593f60 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -471,7 +471,7 @@ GetColumnDefCollation(ParseState *pstate, ColumnDef *coldef, Oid typeOid) { /* We have a raw COLLATE clause, so look up the collation */ location = coldef->collClause->location; - result = LookupCollation(pstate, coldef->collClause->collnames, + result = LookupCollation(pstate, coldef->collClause->collname, location); } else if (OidIsValid(coldef->collOid)) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index e876853af0..06baf89886 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -2467,7 +2467,7 @@ transformColumnType(CreateStmtContext *cxt, ColumnDef *column) Oid collOid; collOid = LookupCollation(cxt->pstate, - column->collClause->collnames, + column->collClause->collname, column->collClause->location); /* Complain if COLLATE is applied to an uncollatable type */ if (!OidIsValid(typtup->typcollation)) diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 80dd10b841..f96fa6cb72 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -2612,7 +2612,7 @@ timetz_zone(PG_FUNCTION_ARGS) type = DecodeSpecial(0, lowzone, &val); if (type == TZ || type == DTZ) - tz = val * 60; + tz = val * MINS_PER_HOUR; else { tzp = pg_tzset(tzname); diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 85f0206a6a..f0fe2e31a2 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -342,7 +342,7 @@ j2date(int jd, int *year, int *month, int *day) *year = y - 4800; quad = julian * 2141 / 65536; *day = julian - 7834 * quad / 256; - *month = (quad + 10) % 12 + 1; + *month = (quad + 10) % MONTHS_PER_YEAR + 1; return; } /* j2date() */ @@ -952,8 +952,8 @@ DecodeDateTime(char **field, int *ftype, int nf, * DecodeTime() */ /* test for > 24:00:00 */ - if (tm->tm_hour > 24 || - (tm->tm_hour == 24 && + if (tm->tm_hour > HOURS_PER_DAY || + (tm->tm_hour == HOURS_PER_DAY && (tm->tm_min > 0 || tm->tm_sec > 0 || *fsec > 0))) return DTERR_FIELD_OVERFLOW; break; @@ -1371,12 +1371,12 @@ DecodeDateTime(char **field, int *ftype, int nf, return dterr; /* handle AM/PM */ - if (mer != HR24 && tm->tm_hour > 12) + if (mer != HR24 && tm->tm_hour > HOURS_PER_DAY / 2) return DTERR_FIELD_OVERFLOW; - if (mer == AM && tm->tm_hour == 12) + if (mer == AM && tm->tm_hour == HOURS_PER_DAY / 2) tm->tm_hour = 0; - else if (mer == PM && tm->tm_hour != 12) - tm->tm_hour += 12; + else if (mer == PM && tm->tm_hour != HOURS_PER_DAY / 2) + tm->tm_hour += HOURS_PER_DAY / 2; /* do additional checking for full date specs... */ if (*dtype == DTK_DATE) @@ -2058,17 +2058,18 @@ DecodeTimeOnly(char **field, int *ftype, int nf, return dterr; /* handle AM/PM */ - if (mer != HR24 && tm->tm_hour > 12) + if (mer != HR24 && tm->tm_hour > HOURS_PER_DAY / 2) return DTERR_FIELD_OVERFLOW; - if (mer == AM && tm->tm_hour == 12) + if (mer == AM && tm->tm_hour == HOURS_PER_DAY / 2) tm->tm_hour = 0; - else if (mer == PM && tm->tm_hour != 12) - tm->tm_hour += 12; + else if (mer == PM && tm->tm_hour != HOURS_PER_DAY / 2) + tm->tm_hour += HOURS_PER_DAY / 2; - if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > 59 || - tm->tm_sec < 0 || tm->tm_sec > 60 || tm->tm_hour > 24 || + if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > MINS_PER_HOUR - 1 || + tm->tm_sec < 0 || tm->tm_sec > SECS_PER_MINUTE || + tm->tm_hour > HOURS_PER_DAY || /* test for > 24:00:00 */ - (tm->tm_hour == 24 && + (tm->tm_hour == HOURS_PER_DAY && (tm->tm_min > 0 || tm->tm_sec > 0 || *fsec > 0)) || #ifdef HAVE_INT64_TIMESTAMP *fsec < INT64CONST(0) || *fsec > USECS_PER_SEC @@ -2396,13 +2397,15 @@ DecodeTime(char *str, int fmask, int range, /* do a sanity check */ #ifdef HAVE_INT64_TIMESTAMP - if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > 59 || - tm->tm_sec < 0 || tm->tm_sec > 60 || *fsec < INT64CONST(0) || + if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > MINS_PER_HOUR -1 || + tm->tm_sec < 0 || tm->tm_sec > SECS_PER_MINUTE || + *fsec < INT64CONST(0) || *fsec > USECS_PER_SEC) return DTERR_FIELD_OVERFLOW; #else - if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > 59 || - tm->tm_sec < 0 || tm->tm_sec > 60 || *fsec < 0 || *fsec > 1) + if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > MINS_PER_HOUR - 1 || + tm->tm_sec < 0 || tm->tm_sec > SECS_PER_MINUTE || + *fsec < 0 || *fsec > 1) return DTERR_FIELD_OVERFLOW; #endif @@ -2748,9 +2751,9 @@ DecodeTimezone(char *str, int *tzp) if (hr < 0 || hr > 14) return DTERR_TZDISP_OVERFLOW; - if (min < 0 || min >= 60) + if (min < 0 || min >= MINS_PER_HOUR) return DTERR_TZDISP_OVERFLOW; - if (sec < 0 || sec >= 60) + if (sec < 0 || sec >= SECS_PER_MINUTE) return DTERR_TZDISP_OVERFLOW; tz = (hr * MINS_PER_HOUR + min) * SECS_PER_MINUTE + sec; @@ -3324,7 +3327,7 @@ DecodeISO8601Interval(char *str, { case 'Y': tm->tm_year += val; - tm->tm_mon += (fval * 12); + tm->tm_mon += (fval * MONTHS_PER_YEAR); break; case 'M': tm->tm_mon += val; @@ -3359,7 +3362,7 @@ DecodeISO8601Interval(char *str, return DTERR_BAD_FORMAT; tm->tm_year += val; - tm->tm_mon += (fval * 12); + tm->tm_mon += (fval * MONTHS_PER_YEAR); if (unit == '\0') return 0; if (unit == 'T') @@ -4155,7 +4158,7 @@ InstallTimeZoneAbbrevs(tzEntry *abbrevs, int n) { strncpy(newtbl[i].token, abbrevs[i].abbrev, TOKMAXLEN); newtbl[i].type = abbrevs[i].is_dst ? DTZ : TZ; - TOVAL(&newtbl[i], abbrevs[i].offset / 60); + TOVAL(&newtbl[i], abbrevs[i].offset / MINS_PER_HOUR); } /* Check the ordering, if testing */ diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index f90d36d24c..aba11459bb 100644 --- a/src/backend/utils/adt/formatting.c +++ b/src/backend/utils/adt/formatting.c @@ -2129,7 +2129,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col * intervals */ sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, - tm->tm_hour % (HOURS_PER_DAY / 2) == 0 ? 12 : + tm->tm_hour % (HOURS_PER_DAY / 2) == 0 ? HOURS_PER_DAY / 2 : tm->tm_hour % (HOURS_PER_DAY / 2)); if (S_THth(n->suffix)) str_numth(s, s, S_TH_TYPE(n->suffix)); @@ -2486,14 +2486,14 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col if (!tm->tm_mon) break; sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -4, - rm_months_upper[12 - tm->tm_mon]); + rm_months_upper[MONTHS_PER_YEAR - tm->tm_mon]); s += strlen(s); break; case DCH_rm: if (!tm->tm_mon) break; sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -4, - rm_months_lower[12 - tm->tm_mon]); + rm_months_lower[MONTHS_PER_YEAR - tm->tm_mon]); s += strlen(s); break; case DCH_W: @@ -2779,12 +2779,12 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out) case DCH_RM: from_char_seq_search(&value, &s, rm_months_upper, ALL_UPPER, MAX_RM_LEN, n); - from_char_set_int(&out->mm, 12 - value, n); + from_char_set_int(&out->mm, MONTHS_PER_YEAR - value, n); break; case DCH_rm: from_char_seq_search(&value, &s, rm_months_lower, ALL_LOWER, MAX_RM_LEN, n); - from_char_set_int(&out->mm, 12 - value, n); + from_char_set_int(&out->mm, MONTHS_PER_YEAR - value, n); break; case DCH_W: from_char_parse_int(&out->w, &s, n); @@ -3236,16 +3236,16 @@ do_to_timestamp(text *date_txt, text *fmt, if (tmfc.clock == CLOCK_12_HOUR) { - if (tm->tm_hour < 1 || tm->tm_hour > 12) + if (tm->tm_hour < 1 || tm->tm_hour > HOURS_PER_DAY / 2) ereport(ERROR, (errcode(ERRCODE_INVALID_DATETIME_FORMAT), errmsg("hour \"%d\" is invalid for the 12-hour clock", tm->tm_hour), errhint("Use the 24-hour clock, or give an hour between 1 and 12."))); - if (tmfc.pm && tm->tm_hour < 12) - tm->tm_hour += 12; - else if (!tmfc.pm && tm->tm_hour == 12) + if (tmfc.pm && tm->tm_hour < HOURS_PER_DAY / 2) + tm->tm_hour += HOURS_PER_DAY / 2; + else if (!tmfc.pm && tm->tm_hour == HOURS_PER_DAY / 2) tm->tm_hour = 0; } @@ -3347,7 +3347,7 @@ do_to_timestamp(text *date_txt, text *fmt, y = ysum[isleap(tm->tm_year)]; - for (i = 1; i <= 12; i++) + for (i = 1; i <= MONTHS_PER_YEAR; i++) { if (tmfc.ddd < y[i]) break; diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c index 78bac406ed..dcb31586b2 100644 --- a/src/backend/utils/adt/int.c +++ b/src/backend/utils/adt/int.c @@ -723,9 +723,13 @@ int4div(PG_FUNCTION_ARGS) int32 result; if (arg2 == 0) + { ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } #ifdef WIN32 @@ -864,9 +868,13 @@ int2div(PG_FUNCTION_ARGS) int16 result; if (arg2 == 0) + { ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } result = arg1 / arg2; @@ -1048,9 +1056,13 @@ int42div(PG_FUNCTION_ARGS) int32 result; if (arg2 == 0) + { ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } result = arg1 / arg2; @@ -1074,9 +1086,13 @@ int4mod(PG_FUNCTION_ARGS) int32 arg2 = PG_GETARG_INT32(1); if (arg2 == 0) + { ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } /* SELECT ((-2147483648)::int4) % (-1); causes a floating point exception */ if (arg1 == INT_MIN && arg2 == -1) @@ -1094,9 +1110,14 @@ int2mod(PG_FUNCTION_ARGS) int16 arg2 = PG_GETARG_INT16(1); if (arg2 == 0) + { ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } + /* No overflow is possible */ PG_RETURN_INT16(arg1 % arg2); diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c index bbab90c0c1..451916480c 100644 --- a/src/backend/utils/adt/int8.c +++ b/src/backend/utils/adt/int8.c @@ -590,9 +590,13 @@ int8div(PG_FUNCTION_ARGS) int64 result; if (arg2 == 0) + { ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } result = arg1 / arg2; @@ -637,9 +641,14 @@ int8mod(PG_FUNCTION_ARGS) int64 arg2 = PG_GETARG_INT64(1); if (arg2 == 0) + { ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } + /* No overflow is possible */ PG_RETURN_INT64(arg1 % arg2); @@ -813,9 +822,13 @@ int84div(PG_FUNCTION_ARGS) int64 result; if (arg2 == 0) + { ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } result = arg1 / arg2; @@ -997,9 +1010,13 @@ int82div(PG_FUNCTION_ARGS) int64 result; if (arg2 == 0) + { ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } result = arg1 / arg2; diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c index 0e25c5fbfe..4b61b21cbe 100644 --- a/src/backend/utils/adt/nabstime.c +++ b/src/backend/utils/adt/nabstime.c @@ -179,13 +179,13 @@ tm2abstime(struct pg_tm * tm, int tz) /* validate, before going out of range on some members */ if (tm->tm_year < 1901 || tm->tm_year > 2038 || - tm->tm_mon < 1 || tm->tm_mon > 12 || + tm->tm_mon < 1 || tm->tm_mon > MONTHS_PER_YEAR || tm->tm_mday < 1 || tm->tm_mday > 31 || tm->tm_hour < 0 || - tm->tm_hour > 24 || /* test for > 24:00:00 */ - (tm->tm_hour == 24 && (tm->tm_min > 0 || tm->tm_sec > 0)) || - tm->tm_min < 0 || tm->tm_min > 59 || - tm->tm_sec < 0 || tm->tm_sec > 60) + tm->tm_hour > HOURS_PER_DAY || /* test for > 24:00:00 */ + (tm->tm_hour == HOURS_PER_DAY && (tm->tm_min > 0 || tm->tm_sec > 0)) || + tm->tm_min < 0 || tm->tm_min > MINS_PER_HOUR - 1 || + tm->tm_sec < 0 || tm->tm_sec > SECS_PER_MINUTE) return INVALID_ABSTIME; day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - UNIX_EPOCH_JDATE; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 7cbd0222cb..ac0c53a7f3 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -226,6 +226,7 @@ static void get_coercion_expr(Node *arg, deparse_context *context, Node *parentNode); static void get_const_expr(Const *constval, deparse_context *context, int showtype); +static void get_const_collation(Const *constval, deparse_context *context); static void simple_quote_literal(StringInfo buf, const char *val); static void get_sublink_expr(SubLink *sublink, deparse_context *context); static void get_from_clause(Query *query, const char *prefix, @@ -5075,21 +5076,6 @@ get_rule_expr(Node *node, deparse_context *context, } break; - case T_CollateClause: - { - CollateClause *collate = (CollateClause *) node; - Node *arg = (Node *) collate->arg; - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren(arg, context, showimplicit, node); - appendStringInfo(buf, " COLLATE %s", - generate_collation_name(collate->collOid)); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - } - break; - case T_CoerceViaIO: { CoerceViaIO *iocoerce = (CoerceViaIO *) node; @@ -5152,6 +5138,21 @@ get_rule_expr(Node *node, deparse_context *context, } break; + case T_CollateExpr: + { + CollateExpr *collate = (CollateExpr *) node; + Node *arg = (Node *) collate->arg; + + if (!PRETTY_PAREN(context)) + appendStringInfoChar(buf, '('); + get_rule_expr_paren(arg, context, showimplicit, node); + appendStringInfo(buf, " COLLATE %s", + generate_collation_name(collate->collOid)); + if (!PRETTY_PAREN(context)) + appendStringInfoChar(buf, ')'); + } + break; + case T_CaseExpr: { CaseExpr *caseexpr = (CaseExpr *) node; @@ -5974,6 +5975,10 @@ get_coercion_expr(Node *arg, deparse_context *context, * showtype can be -1 to never show "::typename" decoration, or +1 to always * show it, or 0 to show it only if the constant wouldn't be assumed to be * the right type by default. + * + * If the Const's collation isn't default for its type, show that too. + * This can only happen in trees that have been through constant-folding. + * We assume we don't need to do this when showtype is -1. * ---------- */ static void @@ -5994,9 +5999,12 @@ get_const_expr(Const *constval, deparse_context *context, int showtype) */ appendStringInfo(buf, "NULL"); if (showtype >= 0) + { appendStringInfo(buf, "::%s", format_type_with_typemod(constval->consttype, constval->consttypmod)); + get_const_collation(constval, context); + } return; } @@ -6097,6 +6105,28 @@ get_const_expr(Const *constval, deparse_context *context, int showtype) appendStringInfo(buf, "::%s", format_type_with_typemod(constval->consttype, constval->consttypmod)); + + get_const_collation(constval, context); +} + +/* + * helper for get_const_expr: append COLLATE if needed + */ +static void +get_const_collation(Const *constval, deparse_context *context) +{ + StringInfo buf = context->buf; + + if (OidIsValid(constval->constcollid)) + { + Oid typcollation = get_typcollation(constval->consttype); + + if (constval->constcollid != typcollation) + { + appendStringInfo(buf, " COLLATE %s", + generate_collation_name(constval->constcollid)); + } + } } /* diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index f10110b1b7..5cad1b88ad 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -145,7 +145,7 @@ static double eqjoinsel_inner(Oid operator, static double eqjoinsel_semi(Oid operator, VariableStatData *vardata1, VariableStatData *vardata2); static bool convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, - Datum lobound, Datum hibound, Oid boundstypid, Oid boundscollid, + Datum lobound, Datum hibound, Oid boundstypid, double *scaledlobound, double *scaledhibound); static double convert_numeric_to_scalar(Datum value, Oid typid); static void convert_string_to_scalar(char *value, @@ -164,10 +164,10 @@ static double convert_one_string_to_scalar(char *value, int rangelo, int rangehi); static double convert_one_bytea_to_scalar(unsigned char *value, int valuelen, int rangelo, int rangehi); -static char *convert_string_datum(Datum value, Oid typid, Oid collid); +static char *convert_string_datum(Datum value, Oid typid); static double convert_timevalue_to_scalar(Datum value, Oid typid); static bool get_variable_range(PlannerInfo *root, VariableStatData *vardata, - Oid sortop, Oid collation, Datum *min, Datum *max); + Oid sortop, Datum *min, Datum *max); static bool get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop, @@ -285,6 +285,7 @@ var_eq_const(VariableStatData *vardata, Oid operator, FmgrInfo eqproc; fmgr_info(get_opcode(operator), &eqproc); + fmgr_info_collation(DEFAULT_COLLATION_OID, &eqproc); for (i = 0; i < nvalues; i++) { @@ -514,7 +515,7 @@ scalarineqsel(PlannerInfo *root, Oid operator, bool isgt, stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple); fmgr_info(get_opcode(operator), &opproc); - fmgr_info_collation(vardata->attcollation, &opproc); + fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc); /* * If we have most-common-values info, add up the fractions of the MCV @@ -839,7 +840,7 @@ ineq_histogram_selectivity(PlannerInfo *root, */ if (convert_to_scalar(constval, consttype, &val, values[i - 1], values[i], - vardata->vartype, vardata->attcollation, + vardata->vartype, &low, &high)) { if (high <= low) @@ -1700,6 +1701,7 @@ scalararraysel(PlannerInfo *root, if (!oprsel) return (Selectivity) 0.5; fmgr_info(oprsel, &oprselproc); + fmgr_info_collation(DEFAULT_COLLATION_OID, &oprselproc); /* deconstruct the expression */ Assert(list_length(clause->args) == 2); @@ -2116,6 +2118,7 @@ eqjoinsel_inner(Oid operator, nmatches; fmgr_info(get_opcode(operator), &eqproc); + fmgr_info_collation(DEFAULT_COLLATION_OID, &eqproc); hasmatch1 = (bool *) palloc0(nvalues1 * sizeof(bool)); hasmatch2 = (bool *) palloc0(nvalues2 * sizeof(bool)); @@ -2338,6 +2341,7 @@ eqjoinsel_semi(Oid operator, nmatches; fmgr_info(get_opcode(operator), &eqproc); + fmgr_info_collation(DEFAULT_COLLATION_OID, &eqproc); hasmatch1 = (bool *) palloc0(nvalues1 * sizeof(bool)); hasmatch2 = (bool *) palloc0(nvalues2 * sizeof(bool)); @@ -2588,7 +2592,7 @@ icnlikejoinsel(PG_FUNCTION_ARGS) */ void mergejoinscansel(PlannerInfo *root, Node *clause, - Oid opfamily, Oid collation, int strategy, bool nulls_first, + Oid opfamily, int strategy, bool nulls_first, Selectivity *leftstart, Selectivity *leftend, Selectivity *rightstart, Selectivity *rightend) { @@ -2757,20 +2761,20 @@ mergejoinscansel(PlannerInfo *root, Node *clause, /* Try to get ranges of both inputs */ if (!isgt) { - if (!get_variable_range(root, &leftvar, lstatop, collation, + if (!get_variable_range(root, &leftvar, lstatop, &leftmin, &leftmax)) goto fail; /* no range available from stats */ - if (!get_variable_range(root, &rightvar, rstatop, collation, + if (!get_variable_range(root, &rightvar, rstatop, &rightmin, &rightmax)) goto fail; /* no range available from stats */ } else { /* need to swap the max and min */ - if (!get_variable_range(root, &leftvar, lstatop, collation, + if (!get_variable_range(root, &leftvar, lstatop, &leftmax, &leftmin)) goto fail; /* no range available from stats */ - if (!get_variable_range(root, &rightvar, rstatop, collation, + if (!get_variable_range(root, &rightvar, rstatop, &rightmax, &rightmin)) goto fail; /* no range available from stats */ } @@ -3371,7 +3375,7 @@ estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey, double nbuckets) */ static bool convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, - Datum lobound, Datum hibound, Oid boundstypid, Oid boundscollid, + Datum lobound, Datum hibound, Oid boundstypid, double *scaledlobound, double *scaledhibound) { /* @@ -3424,9 +3428,9 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, case TEXTOID: case NAMEOID: { - char *valstr = convert_string_datum(value, valuetypid, boundscollid); - char *lostr = convert_string_datum(lobound, boundstypid, boundscollid); - char *histr = convert_string_datum(hibound, boundstypid, boundscollid); + char *valstr = convert_string_datum(value, valuetypid); + char *lostr = convert_string_datum(lobound, boundstypid); + char *histr = convert_string_datum(hibound, boundstypid); convert_string_to_scalar(valstr, scaledvalue, lostr, scaledlobound, @@ -3670,7 +3674,7 @@ convert_one_string_to_scalar(char *value, int rangelo, int rangehi) * before continuing, so as to generate correct locale-specific results. */ static char * -convert_string_datum(Datum value, Oid typid, Oid collid) +convert_string_datum(Datum value, Oid typid) { char *val; @@ -3703,7 +3707,7 @@ convert_string_datum(Datum value, Oid typid, Oid collid) return NULL; } - if (!lc_collate_is_c(collid)) + if (!lc_collate_is_c(DEFAULT_COLLATION_OID)) { char *xfrmstr; size_t xfrmlen; @@ -4102,7 +4106,6 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, vardata->rel = find_base_rel(root, var->varno); vardata->atttype = var->vartype; vardata->atttypmod = var->vartypmod; - vardata->attcollation = var->varcollid; vardata->isunique = has_unique_index(vardata->rel, var->varattno); rte = root->simple_rte_array[var->varno]; @@ -4188,7 +4191,6 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, vardata->var = node; vardata->atttype = exprType(node); vardata->atttypmod = exprTypmod(node); - vardata->attcollation = exprCollation(node); if (onerel) { @@ -4397,7 +4399,7 @@ get_variable_numdistinct(VariableStatData *vardata) * be "<" not ">", as only the former is likely to be found in pg_statistic. */ static bool -get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop, Oid collation, +get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop, Datum *min, Datum *max) { Datum tmin = 0; @@ -4482,7 +4484,7 @@ get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop, Oid FmgrInfo opproc; fmgr_info(get_opcode(sortop), &opproc); - fmgr_info_collation(collation, &opproc); + fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc); for (i = 0; i < nvalues; i++) { @@ -5109,6 +5111,7 @@ prefix_selectivity(PlannerInfo *root, VariableStatData *vardata, if (cmpopr == InvalidOid) elog(ERROR, "no >= operator for opfamily %u", opfamily); fmgr_info(get_opcode(cmpopr), &opproc); + fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc); prefixsel = ineq_histogram_selectivity(root, vardata, &opproc, true, prefixcon->constvalue, @@ -5130,6 +5133,7 @@ prefix_selectivity(PlannerInfo *root, VariableStatData *vardata, if (cmpopr == InvalidOid) elog(ERROR, "no < operator for opfamily %u", opfamily); fmgr_info(get_opcode(cmpopr), &opproc); + fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc); greaterstrcon = make_greater_string(prefixcon, &opproc); if (greaterstrcon) diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 1c2c160589..45e70029e5 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -4422,7 +4422,7 @@ timestamp_zone(PG_FUNCTION_ARGS) if (type == TZ || type == DTZ) { - tz = -(val * 60); + tz = -(val * MINS_PER_HOUR); result = dt2local(timestamp, tz); } else @@ -4596,7 +4596,7 @@ timestamptz_zone(PG_FUNCTION_ARGS) if (type == TZ || type == DTZ) { - tz = val * 60; + tz = val * MINS_PER_HOUR; result = dt2local(timestamp, tz); } else diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c index 2fab5c98fd..e553c9b9d4 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -865,6 +865,9 @@ do_stop(void) print_msg(_(" failed\n")); write_stderr(_("%s: server does not shut down\n"), progname); + if (shutdown_mode == SMART_MODE) + write_stderr(_("HINT: The \"-m fast\" option immediately disconnects sessions rather than\n" + "waiting for session-initiated disconnection.\n")); exit(1); } print_msg(_(" done\n")); @@ -952,6 +955,9 @@ do_restart(void) print_msg(_(" failed\n")); write_stderr(_("%s: server does not shut down\n"), progname); + if (shutdown_mode == SMART_MODE) + write_stderr(_("HINT: The \"-m fast\" option immediately disconnects sessions rather than\n" + "waiting for session-initiated disconnection.\n")); exit(1); } diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 0884517331..546a04c951 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -469,7 +469,10 @@ main(int argc, char **argv) break; case 'X': - /* -X is a deprecated alternative to long options */ + /* + * -X is a deprecated alternative to long options; + * no new -X options are to be added. + */ if (strcmp(optarg, "disable-dollar-quoting") == 0) disable_dollar_quoting = 1; else if (strcmp(optarg, "disable-triggers") == 0) @@ -478,10 +481,6 @@ main(int argc, char **argv) outputNoTablespaces = 1; else if (strcmp(optarg, "use-set-session-authorization") == 0) use_setsessauth = 1; - else if (strcmp(optarg, "no-security-label") == 0) - no_security_label = 1; - else if (strcmp(optarg, "no-unlogged-table-data") == 0) - no_unlogged_table_data = 1; else { fprintf(stderr, diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 2214471cea..7019123725 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -609,7 +609,7 @@ static const pgsql_thing_t words_after_create[] = { {"AGGREGATE", NULL, &Query_for_list_of_aggregates}, {"CAST", NULL, NULL}, /* Casts have complex structures for names, so * skip it */ - {"COLLATION", "SELECT pg_catalog.quote_ident(collname) FROM pg_catalog.pg_collation WHERE collencoding = pg_char_to_encoding(getdatabaseencoding()) AND substring(pg_catalog.quote_ident(collname),1,%d)='%s'"}, + {"COLLATION", "SELECT pg_catalog.quote_ident(collname) FROM pg_catalog.pg_collation WHERE collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding())) AND substring(pg_catalog.quote_ident(collname),1,%d)='%s'"}, /* * CREATE CONSTRAINT TRIGGER is not supported here because it is designed diff --git a/src/include/access/xlogdefs.h b/src/include/access/xlogdefs.h index 9078f3eb4e..2982a4799c 100644 --- a/src/include/access/xlogdefs.h +++ b/src/include/access/xlogdefs.h @@ -85,13 +85,12 @@ typedef uint32 TimeLineID; /* * Because O_DIRECT bypasses the kernel buffers, and because we never - * read those buffers except during crash recovery, it is a win to use - * it in all cases where we sync on each write(). We could allow O_DIRECT - * with fsync(), but because skipping the kernel buffer forces writes out - * quickly, it seems best just to use it for O_SYNC. It is hard to imagine - * how fsync() could be a win for O_DIRECT compared to O_SYNC and O_DIRECT. - * Also, O_DIRECT is never enough to force data to the drives, it merely - * tries to bypass the kernel cache, so we still need O_SYNC or fsync(). + * read those buffers except during crash recovery or if wal_level != minimal, + * it is a win to use it in all cases where we sync on each write(). We could + * allow O_DIRECT with fsync(), but it is unclear if fsync() could process + * writes not buffered in the kernel. Also, O_DIRECT is never enough to force + * data to the drives, it merely tries to bypass the kernel cache, so we still + * need O_SYNC/O_DSYNC. */ #ifdef O_DIRECT #define PG_O_DIRECT O_DIRECT diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 22a0b89b44..657015616c 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201103101 +#define CATALOG_VERSION_NO 201103112 #endif diff --git a/src/include/catalog/pg_collation.h b/src/include/catalog/pg_collation.h index 6decfb13b3..e90aa050f5 100644 --- a/src/include/catalog/pg_collation.h +++ b/src/include/catalog/pg_collation.h @@ -32,9 +32,9 @@ CATALOG(pg_collation,3456) { NameData collname; /* collation name */ - Oid collnamespace; /* OID of namespace containing this collation */ - Oid collowner; - int4 collencoding; /* encoding that this collation applies to */ + Oid collnamespace; /* OID of namespace containing collation */ + Oid collowner; /* owner of collation */ + int4 collencoding; /* encoding for this collation; -1 = "all" */ NameData collcollate; /* LC_COLLATE setting */ NameData collctype; /* LC_CTYPE setting */ } FormData_pg_collation; @@ -58,8 +58,8 @@ typedef FormData_pg_collation *Form_pg_collation; #define Anum_pg_collation_collcollate 5 #define Anum_pg_collation_collctype 6 -DATA(insert OID = 100 ( default PGNSP PGUID 0 "" "" )); -DESCR("placeholder for default collation"); +DATA(insert OID = 100 ( default PGNSP PGUID -1 "" "" )); +DESCR("database's default collation"); #define DEFAULT_COLLATION_OID 100 #endif /* PG_COLLATION_H */ diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index 3ad7b4bd05..cc1441372d 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -50,6 +50,10 @@ * the information to be stored in a pg_statistic row for the column. Be * careful to allocate any pointed-to data in anl_context, which will NOT * be CurrentMemoryContext when compute_stats is called. + * + * Note: for the moment, all comparisons done for statistical purposes + * should use the database's default collation (DEFAULT_COLLATION_OID). + * This might change in some future release. *---------- */ typedef struct VacAttrStats *VacAttrStatsP; @@ -66,13 +70,12 @@ typedef struct VacAttrStats * Note: do not assume that the data being analyzed has the same datatype * shown in attr, ie do not trust attr->atttypid, attlen, etc. This is * because some index opclasses store a different type than the underlying - * column/expression. Instead use attrtypid, attrtypmod, attrcollation, and attrtype for + * column/expression. Instead use attrtypid, attrtypmod, and attrtype for * information about the datatype being fed to the typanalyze function. */ Form_pg_attribute attr; /* copy of pg_attribute row for column */ Oid attrtypid; /* type of data being analyzed */ int32 attrtypmod; /* typmod of data being analyzed */ - Oid attrcollation; /* collation of the data being analyzed */ Form_pg_type attrtype; /* copy of pg_type row for attrtypid */ MemoryContext anl_context; /* where to save long-lived data */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index cbaf123ee9..8ed819b4dd 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -148,6 +148,7 @@ typedef enum NodeTag T_CoerceViaIO, T_ArrayCoerceExpr, T_ConvertRowtypeExpr, + T_CollateExpr, T_CaseExpr, T_CaseWhen, T_CaseTestExpr, @@ -169,7 +170,6 @@ typedef enum NodeTag T_JoinExpr, T_FromExpr, T_IntoClause, - T_CollateClause, /* * TAGS FOR EXPRESSION STATE NODES (execnodes.h) @@ -377,6 +377,7 @@ typedef enum NodeTag T_A_ArrayExpr, T_ResTarget, T_TypeCast, + T_CollateClause, T_SortBy, T_WindowDef, T_RangeSubselect, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 9d4515cb27..904bd5e9e1 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -266,6 +266,17 @@ typedef struct TypeCast } TypeCast; /* + * CollateClause - a COLLATE expression + */ +typedef struct CollateClause +{ + NodeTag type; + Node *arg; /* input expression */ + List *collname; /* possibly-qualified collation name */ + int location; /* token location, or -1 if unknown */ +} CollateClause; + +/* * FuncCall - a function or aggregate invocation * * agg_order (if not NIL) indicates we saw 'foo(... ORDER BY ...)'. diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 8c366df5f5..41fd56e1bf 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -653,18 +653,6 @@ typedef struct RelabelType int location; /* token location, or -1 if unknown */ } RelabelType; -/* - * CollateClause - COLLATE - */ -typedef struct CollateClause -{ - Expr xpr; - Expr *arg; /* original expression */ - List *collnames; /* assigned collation */ - Oid collOid; /* resolved collation OID */ - int location; /* token location, or -1 if unknown */ -} CollateClause; - /* ---------------- * CoerceViaIO * @@ -731,6 +719,18 @@ typedef struct ConvertRowtypeExpr } ConvertRowtypeExpr; /*---------- + * CollateExpr - COLLATE + *---------- + */ +typedef struct CollateExpr +{ + Expr xpr; + Expr *arg; /* input expression */ + Oid collOid; /* collation's OID */ + int location; /* token location, or -1 if unknown */ +} CollateExpr; + +/*---------- * CaseExpr - a CASE expression * * We support two distinct forms of CASE expression: diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h index baf6d8caf8..e9913aa049 100644 --- a/src/include/utils/selfuncs.h +++ b/src/include/utils/selfuncs.h @@ -74,7 +74,6 @@ typedef struct VariableStatData Oid vartype; /* exposed type of expression */ Oid atttype; /* type to pass to get_attstatsslot */ int32 atttypmod; /* typmod to pass to get_attstatsslot */ - Oid attcollation; /* collation of the variable */ bool isunique; /* true if matched to a unique index */ } VariableStatData; @@ -179,7 +178,7 @@ extern Selectivity rowcomparesel(PlannerInfo *root, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo); extern void mergejoinscansel(PlannerInfo *root, Node *clause, - Oid opfamily, Oid collation, int strategy, bool nulls_first, + Oid opfamily, int strategy, bool nulls_first, Selectivity *leftstart, Selectivity *leftend, Selectivity *rightstart, Selectivity *rightend); diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h index 9e51b58dde..e14285f8a4 100644 --- a/src/include/utils/timestamp.h +++ b/src/include/utils/timestamp.h @@ -81,6 +81,8 @@ typedef struct */ #define DAYS_PER_MONTH 30 /* assumes exactly 30 days per month */ #define HOURS_PER_DAY 24 /* assume no daylight savings time changes */ +#define MINS_PER_HOUR 60 /* assume no daylight savings time changes */ +#define SECS_PER_MINUTE 60 /* assume no daylight savings time changes */ /* * This doesn't adjust for uneven daylight savings time intervals or leap diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c index b0ad60e7a0..f467a7f9c9 100644 --- a/src/interfaces/ecpg/preproc/type.c +++ b/src/interfaces/ecpg/preproc/type.c @@ -245,7 +245,7 @@ ECPGdump_a_type(FILE *o, const char *name, struct ECPGtype * type, const int bra struct variable *var; if (type->type != ECPGt_descriptor && type->type != ECPGt_sqlda && - type->type != ECPGt_char_variable && + type->type != ECPGt_char_variable && type->type != ECPGt_const && brace_level >= 0) { char *str; diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 7af6eee088..689686b782 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -5350,6 +5350,9 @@ exec_simple_check_node(Node *node) case T_ConvertRowtypeExpr: return exec_simple_check_node((Node *) ((ConvertRowtypeExpr *) node)->arg); + case T_CollateExpr: + return exec_simple_check_node((Node *) ((CollateExpr *) node)->arg); + case T_CaseExpr: { CaseExpr *expr = (CaseExpr *) node; diff --git a/src/test/examples/testlibpq4.c b/src/test/examples/testlibpq4.c index dafc6aecb1..0ec04313c0 100644 --- a/src/test/examples/testlibpq4.c +++ b/src/test/examples/testlibpq4.c @@ -72,11 +72,11 @@ main(int argc, char **argv) * defaults by looking up environment variables or, failing that, using * hardwired constants */ - pghost = NULL; /* host name of the backend server */ - pgport = NULL; /* port of the backend server */ + pghost = NULL; /* host name of the backend */ + pgport = NULL; /* port of the backend */ pgoptions = NULL; /* special options to start up the backend * server */ - pgtty = NULL; /* debugging tty for the backend server */ + pgtty = NULL; /* debugging tty for the backend */ /* make a connection to the database */ conn1 = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName1); diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index 5ad5de2f00..46a8207b7e 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -18,14 +18,14 @@ CREATE TABLE collate_test_fail ( a int, b text COLLATE "ja_JP.eucjp" ); -ERROR: collation "ja_JP.eucjp" for current database encoding "UTF8" does not exist +ERROR: collation "ja_JP.eucjp" for encoding "UTF8" does not exist LINE 3: b text COLLATE "ja_JP.eucjp" ^ CREATE TABLE collate_test_fail ( a int, b text COLLATE "foo" ); -ERROR: collation "foo" for current database encoding "UTF8" does not exist +ERROR: collation "foo" for encoding "UTF8" does not exist LINE 3: b text COLLATE "foo" ^ CREATE TABLE collate_test_fail ( @@ -107,11 +107,11 @@ SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US.utf8"; ERROR: collation mismatch between explicit collations "C" and "en_US.utf8" -LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e... +LINE 1: ...ELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLL... ^ SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; ERROR: collation mismatch between explicit collations "C" and "en_US" -LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e... +LINE 1: ...ELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLL... ^ CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE.utf8"; CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE.utf8"; -- fails @@ -630,6 +630,16 @@ HINT: You can override the collation by applying the COLLATE clause to one or b CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail ERROR: no collation was derived for column "b" with collatable type text HINT: Use the COLLATE clause to set the collation explicitly. +-- 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; +ERROR: recursive query "foo" column 1 has collation "en_US" in non-recursive term but collation "de_DE" overall +LINE 2: (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + ^ +HINT: Use the COLLATE clause to set the collation of the non-recursive term. -- casting SELECT CAST('42' AS text COLLATE "C"); ERROR: syntax error at or near "COLLATE" @@ -752,7 +762,7 @@ ERROR: parameter "lc_ctype" must be specified CREATE COLLATION testx (locale = 'nonsense'); -- fail ERROR: could not create locale "nonsense": No such file or directory CREATE COLLATION test4 FROM nonsense; -ERROR: collation "nonsense" for current database encoding "UTF8" does not exist +ERROR: collation "nonsense" for encoding "UTF8" does not exist CREATE COLLATION test5 FROM test0; SELECT collname, collencoding, collcollate, collctype FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; collname | collencoding | collcollate | collctype @@ -764,9 +774,9 @@ SELECT collname, collencoding, collcollate, collctype FROM pg_collation WHERE co ALTER COLLATION test1 RENAME TO test11; ALTER COLLATION test0 RENAME TO test11; -- fail -ERROR: collation "test11" for current database encoding "UTF8" already exists in schema "public" +ERROR: collation "test11" for encoding "UTF8" already exists in schema "public" ALTER COLLATION test1 RENAME TO test22; -- fail -ERROR: collation "test1" for current database encoding "UTF8" does not exist +ERROR: collation "test1" for encoding "UTF8" does not exist ALTER COLLATION test11 OWNER TO regress_test_role; ALTER COLLATION test11 OWNER TO nonsense; ERROR: role "nonsense" does not exist @@ -785,7 +795,7 @@ SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') DROP COLLATION test0, test_schema.test11, test5; DROP COLLATION test0; -- fail -ERROR: collation "test0" for current database encoding "UTF8" does not exist +ERROR: collation "test0" for encoding "UTF8" does not exist DROP COLLATION IF EXISTS test0; NOTICE: collation "test0" does not exist, skipping SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; diff --git a/src/test/regress/sql/collate.linux.utf8.sql b/src/test/regress/sql/collate.linux.utf8.sql index c70e5cefd5..55af5091bc 100644 --- a/src/test/regress/sql/collate.linux.utf8.sql +++ b/src/test/regress/sql/collate.linux.utf8.sql @@ -190,6 +190,13 @@ SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail +-- 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 diff --git a/src/tools/git-external-diff b/src/tools/git-external-diff index fdc7080f3a..59fa36624c 100644 --- a/src/tools/git-external-diff +++ b/src/tools/git-external-diff @@ -1,6 +1,8 @@ #!/bin/bash -# Parameters: +# This script is used to produce git context diffs + +# Supplied parameters: # $1 $2 $3 $4 $5 $6 $7 # path old-file old-hash old-mode new-file new-hash new-mode # 'path' is the git-tree-relative path of the file being diff'ed |