diff options
author | Bernd Helmle | 2011-06-14 12:05:39 +0000 |
---|---|---|
committer | Bernd Helmle | 2011-06-14 12:05:39 +0000 |
commit | a84046d724aedec5d8ef88e793efa3733f9c52da (patch) | |
tree | 8bb5f1566417d80db65d1d38b48d79abcb753bbd | |
parent | c7be7949ddd9549c2dd359fef2856d407399534e (diff) | |
parent | b81831acbc671445061ed41a55fb1cc21d8e2979 (diff) |
Merge branch 'master' of ../bernd_pg into notnull_constraint
-rw-r--r-- | doc/src/sgml/client-auth.sgml | 2 | ||||
-rw-r--r-- | doc/src/sgml/config.sgml | 16 | ||||
-rw-r--r-- | doc/src/sgml/ddl.sgml | 9 | ||||
-rw-r--r-- | doc/src/sgml/fdwhandler.sgml | 11 | ||||
-rw-r--r-- | doc/src/sgml/queries.sgml | 4 | ||||
-rw-r--r-- | doc/src/sgml/ref/alter_extension.sgml | 12 | ||||
-rw-r--r-- | doc/src/sgml/ref/create_index.sgml | 11 | ||||
-rw-r--r-- | src/backend/catalog/pg_shdepend.c | 3 | ||||
-rw-r--r-- | src/include/access/twophase_rmgr.h | 8 | ||||
-rw-r--r-- | src/interfaces/ecpg/preproc/Makefile | 4 | ||||
-rw-r--r-- | src/interfaces/ecpg/preproc/README.parser | 4 | ||||
-rw-r--r-- | src/interfaces/ecpg/preproc/ecpg.trailer | 2 | ||||
-rw-r--r-- | src/interfaces/ecpg/preproc/parse.pl | 1021 | ||||
-rw-r--r-- | src/interfaces/ecpg/preproc/parse2.pl | 648 |
14 files changed, 649 insertions, 1106 deletions
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index 77c541b6cb..575eb3be44 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -231,7 +231,7 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> An IP address is specified in standard dotted decimal notation with a <acronym>CIDR</> mask length. The mask length indicates the number of high-order bits of the client - IP address that must match. Bits to the right of this must + IP address that must match. Bits to the right of this should be zero in the given IP address. There must not be any white space between the IP address, the <literal>/</literal>, and the CIDR mask length. diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 39819695d1..e835e4bc90 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -2107,6 +2107,10 @@ SET ENABLE_SEQSCAN TO OFF; <xref linkend="guc-synchronous-commit"> parameter to <literal>local</> or <literal>off</>. </para> + <para> + This parameter can only be set in the <filename>postgresql.conf</> + file or on the server command line. + </para> </listitem> </varlistentry> @@ -2239,8 +2243,10 @@ SET ENABLE_SEQSCAN TO OFF; about queries currently executing on the standby. This parameter can be used to eliminate query cancels caused by cleanup records, but can cause database bloat on the primary for some workloads. - The default value is <literal>off</literal>. Feedback messages will not - be sent more frequently than once per <varname>wal_receiver_status_interval</>. + Feedback messages will not be sent more frequently than once per + <varname>wal_receiver_status_interval</>. The default value is + <literal>off</literal>. This parameter can only be set in the + <filename>postgresql.conf</> file or on the server command line. </para> </listitem> </varlistentry> @@ -4485,9 +4491,11 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv; <para> The current effective value of the search path can be examined via the <acronym>SQL</acronym> function - <function>current_schemas()</>. This is not quite the same as + <function>current_schemas</> + (see <xref linkend="functions-info">). + This is not quite the same as examining the value of <varname>search_path</varname>, since - <function>current_schemas()</> shows how the items + <function>current_schemas</> shows how the items appearing in <varname>search_path</varname> were resolved. </para> diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml index 9709dd613f..4c9fc5d57a 100644 --- a/doc/src/sgml/ddl.sgml +++ b/doc/src/sgml/ddl.sgml @@ -3021,10 +3021,11 @@ ANALYZE measurement; <firstterm>foreign data wrapper</firstterm>. A foreign data wrapper is a library that can communicate with an external data source, hiding the details of connecting to the data source and fetching data from it. There - are several foreign data wrappers available, which can for example read - plain data files residing on the server, or connect to another PostgreSQL - instance. If none of the existing foreign data wrappers suit your needs, - you can write your own; see <xref linkend="fdwhandler">. + is a foreign data wrapper available as a <filename>contrib</> module, + which can read plain data files residing on the server. Other kind of + foreign data wrappers might be found as third party products. If none of + the existing foreign data wrappers suit your needs, you can write your + own; see <xref linkend="fdwhandler">. </para> <para> diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml index fc07f129b7..c8ae861852 100644 --- a/doc/src/sgml/fdwhandler.sgml +++ b/doc/src/sgml/fdwhandler.sgml @@ -180,6 +180,17 @@ IterateForeignScan (ForeignScanState *node); </para> <para> + Note that <productname>PostgreSQL</productname>'s executor doesn't care + whether the rows returned violate the <literal>NOT NULL</literal> + constraints which were defined on the foreign table columns - but the + planner does care, and may optimize queries incorrectly if + <literal>NULL</> values are present in a column declared not to contain + them. If a <literal>NULL</> value is encountered when the user has + declared that none should be present, it may be appropriate to raise an + error (just as you would need to do in the case of a data type mismatch). + </para> + + <para> <programlisting> void ReScanForeignScan (ForeignScanState *node); diff --git a/doc/src/sgml/queries.sgml b/doc/src/sgml/queries.sgml index b3cf39a39b..7ca640e697 100644 --- a/doc/src/sgml/queries.sgml +++ b/doc/src/sgml/queries.sgml @@ -266,7 +266,9 @@ FROM <replaceable>table_reference</replaceable> <optional>, <replaceable>table_r <literal>USING</>: it forms a <literal>USING</> list consisting of all column names that appear in both input tables. As with <literal>USING</>, these columns appear - only once in the output table. + only once in the output table. If there are no common + columns, <literal>NATURAL</literal> behaves like + <literal>CROSS JOIN</literal>. </para> <para> diff --git a/doc/src/sgml/ref/alter_extension.sgml b/doc/src/sgml/ref/alter_extension.sgml index e8c9608666..67e2c3be49 100644 --- a/doc/src/sgml/ref/alter_extension.sgml +++ b/doc/src/sgml/ref/alter_extension.sgml @@ -242,6 +242,18 @@ ALTER EXTENSION <replaceable class="PARAMETER">extension_name</replaceable> DROP </varlistentry> <varlistentry> + <term><replaceable class="parameter">left_type</replaceable></term> + <term><replaceable class="parameter">right_type</replaceable></term> + <listitem> + <para> + The data type(s) of the operator's arguments (optionally + schema-qualified). Write <literal>NONE</> for the missing argument + of a prefix or postfix operator. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><literal>PROCEDURAL</literal></term> <listitem> diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 43b6499603..f7f360875c 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -381,7 +381,16 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ <replaceable class="parameter">name</ <para> In a concurrent index build, the index is actually entered into the system catalogs in one transaction, then the two table scans occur in a - second and third transaction. + second and third transaction. All active transactions at the time the + second table scan starts, not just ones that already involve the table, + have the potential to block the concurrent index creation until they + finish. When checking for transactions that could still use the original + index, concurrent index creation advances through potentially interfering + older transactions one at a time, obtaining shared locks on their virtual + transaction identifiers to wait for them to complete. + </para> + + <para> If a problem arises while scanning the table, such as a uniqueness violation in a unique index, the <command>CREATE INDEX</> command will fail but leave behind an <quote>invalid</> index. This index diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c index 8c8e7b276d..591352d4b4 100644 --- a/src/backend/catalog/pg_shdepend.c +++ b/src/backend/catalog/pg_shdepend.c @@ -1284,8 +1284,7 @@ shdepReassignOwned(List *roleids, Oid newrole) ereport(ERROR, (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), - errmsg("cannot drop objects owned by %s because they are " - "required by the database system", + errmsg("cannot reassign ownership of objects owned by %s because they are required by the database system", getObjectDescription(&obj)))); /* diff --git a/src/include/access/twophase_rmgr.h b/src/include/access/twophase_rmgr.h index 1c7d8bb4c0..090a2bc5db 100644 --- a/src/include/access/twophase_rmgr.h +++ b/src/include/access/twophase_rmgr.h @@ -23,10 +23,10 @@ typedef uint8 TwoPhaseRmgrId; */ #define TWOPHASE_RM_END_ID 0 #define TWOPHASE_RM_LOCK_ID 1 -#define TWOPHASE_RM_PREDICATELOCK_ID 2 -#define TWOPHASE_RM_PGSTAT_ID 3 -#define TWOPHASE_RM_MULTIXACT_ID 4 -#define TWOPHASE_RM_MAX_ID TWOPHASE_RM_MULTIXACT_ID +#define TWOPHASE_RM_PGSTAT_ID 2 +#define TWOPHASE_RM_MULTIXACT_ID 3 +#define TWOPHASE_RM_PREDICATELOCK_ID 4 +#define TWOPHASE_RM_MAX_ID TWOPHASE_RM_PREDICATELOCK_ID extern const TwoPhaseCallback twophase_recover_callbacks[]; extern const TwoPhaseCallback twophase_postcommit_callbacks[]; diff --git a/src/interfaces/ecpg/preproc/Makefile b/src/interfaces/ecpg/preproc/Makefile index 36b3c26ada..8e85fe9e5a 100644 --- a/src/interfaces/ecpg/preproc/Makefile +++ b/src/interfaces/ecpg/preproc/Makefile @@ -57,8 +57,8 @@ else @$(missing) flex $< $@ endif -preproc.y: ../../../backend/parser/gram.y parse2.pl ecpg.addons ecpg.header ecpg.tokens ecpg.trailer ecpg.type - $(PERL) $(srcdir)/parse2.pl $(srcdir) < $< > $@ +preproc.y: ../../../backend/parser/gram.y parse.pl ecpg.addons ecpg.header ecpg.tokens ecpg.trailer ecpg.type + $(PERL) $(srcdir)/parse.pl $(srcdir) < $< > $@ $(PERL) $(srcdir)/check_rules.pl $(srcdir) $< ecpg_keywords.o c_keywords.o keywords.o preproc.o parser.o: preproc.h diff --git a/src/interfaces/ecpg/preproc/README.parser b/src/interfaces/ecpg/preproc/README.parser index 6d6daac4ba..ddc3061d48 100644 --- a/src/interfaces/ecpg/preproc/README.parser +++ b/src/interfaces/ecpg/preproc/README.parser @@ -3,7 +3,7 @@ ECPG modifies and extends the core grammar in a way that defined in ecpg.tokens, types are defined in ecpg.type 2) most tokens from the core grammar are simply converted to literals concatenated together to form the SQL string - passed to the server, this is done by parse2.pl. + passed to the server, this is done by parse.pl. 3) some rules need side-effects, actions are either added or completely overridden (compared to the basic token concatenation) for them, these are defined in ecpg.addons, @@ -20,7 +20,7 @@ rules concatenated together. e.g. if gram.y has this: ruleA: tokenA tokenB tokenC {...} then "dumpedtokens" is "ruleAtokenAtokenBtokenC". "postfix" above can be: -a) "block" - the automatic rule created by parse2.pl is completely +a) "block" - the automatic rule created by parse.pl is completely overridden, the code block has to be written completely as it were in a plain bison grammar b) "rule" - the automatic rule is extended on, so new syntaxes diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer index 3a941ce1ab..4e0c52caae 100644 --- a/src/interfaces/ecpg/preproc/ecpg.trailer +++ b/src/interfaces/ecpg/preproc/ecpg.trailer @@ -1591,7 +1591,7 @@ ECPGCKeywords: S_AUTO { $$ = mm_strdup("auto"); } * CONNECTION can be added back in all_unreserved_keyword, but CURRENT and * INPUT are reserved for ecpg purposes. * - * The mentioned exclusions are done by $replace_line settings in parse2.pl. + * The mentioned exclusions are done by $replace_line settings in parse.pl. */ all_unreserved_keyword: unreserved_keyword { $$ = $1; } | ECPGunreserved_interval { $$ = $1; } diff --git a/src/interfaces/ecpg/preproc/parse.pl b/src/interfaces/ecpg/preproc/parse.pl index 2328010203..515470e9d1 100644 --- a/src/interfaces/ecpg/preproc/parse.pl +++ b/src/interfaces/ecpg/preproc/parse.pl @@ -1,499 +1,648 @@ #!/usr/bin/perl # src/interfaces/ecpg/preproc/parse.pl -# parser generater for ecpg +# parser generater for ecpg version 2 # call with backend parser as stdin # # Copyright (c) 2007-2011, PostgreSQL Global Development Group # # Written by Mike Aubury <[email protected]> # Michael Meskes <[email protected]> +# Andy Colson <[email protected]> # # Placed under the same license as PostgreSQL. # -if (@ARGV) { - $path = $ARGV[0]; - shift @ARGV; -} +use strict; +use warnings; +no warnings 'uninitialized'; + +my $path = shift @ARGV; +$path = "." unless $path; -if ($path eq '') { $path = "."; } +my $copymode = 0; +my $brace_indent = 0; +my $yaccmode = 0; +my $header_included = 0; +my $feature_not_supported = 0; +my $tokenmode = 0; -$[ = 1; # set array base to 1 -$, = ' '; # set output field separator -$\ = "\n"; # set output record separator +my(%buff, $infield, $comment, %tokens, %addons ); +my($stmt_mode, @fields); +my($line, $non_term_id); -$copymode = 'off'; -$brace_indent = 0; -$yaccmode = 0; -$header_included = 0; -$feature_not_supported = 0; -$tokenmode = 0; # some token have to be replaced by other symbols # either in the rule -$replace_token{'BCONST'} = 'ecpg_bconst'; -$replace_token{'FCONST'} = 'ecpg_fconst'; -$replace_token{'Sconst'} = 'ecpg_sconst'; -$replace_token{'IDENT'} = 'ecpg_ident'; -$replace_token{'PARAM'} = 'ecpg_param'; +my %replace_token = ( + 'BCONST' => 'ecpg_bconst', + 'FCONST' => 'ecpg_fconst', + 'Sconst' => 'ecpg_sconst', + 'IDENT' => 'ecpg_ident', + 'PARAM' => 'ecpg_param', +); + # or in the block -$replace_string{'WITH_TIME'} = 'with time'; -$replace_string{'NULLS_FIRST'} = 'nulls first'; -$replace_string{'NULLS_LAST'} = 'nulls last'; -$replace_string{'TYPECAST'} = '::'; -$replace_string{'DOT_DOT'} = '..'; -$replace_string{'COLON_EQUALS'} = ':='; +my %replace_string = ( + 'WITH_TIME' => 'with time', + 'NULLS_FIRST' => 'nulls first', + 'NULLS_LAST' => 'nulls last', + 'TYPECAST' => '::', + 'DOT_DOT' => '..', + 'COLON_EQUALS' => ':=', +); # specific replace_types for specific non-terminals - never include the ':' # ECPG-only replace_types are defined in ecpg-replace_types -$replace_types{'PrepareStmt'} = '<prep>'; -$replace_types{'opt_array_bounds'} = '<index>'; -# "ignore" means: do not create type and rules for this non-term-id -$replace_types{'stmtblock'} = 'ignore'; -$replace_types{'stmtmulti'} = 'ignore'; -$replace_types{'CreateAsStmt'} = 'ignore'; -$replace_types{'DeallocateStmt'} = 'ignore'; -$replace_types{'ColId'} = 'ignore'; -$replace_types{'type_function_name'} = 'ignore'; -$replace_types{'ColLabel'} = 'ignore'; -$replace_types{'Sconst'} = 'ignore'; +my %replace_types = ( + 'PrepareStmt' => '<prep>', + 'opt_array_bounds' => '<index>', + + # "ignore" means: do not create type and rules for this non-term-id + 'stmtblock' => 'ignore', + 'stmtmulti' => 'ignore', + 'CreateAsStmt' => 'ignore', + 'DeallocateStmt' => 'ignore', + 'ColId' => 'ignore', + 'type_function_name' => 'ignore', + 'ColLabel' => 'ignore', + 'Sconst' => 'ignore', +); # these replace_line commands excise certain keywords from the core keyword # lists. Be sure to account for these in ColLabel and related productions. -$replace_line{'unreserved_keywordCONNECTION'} = 'ignore'; -$replace_line{'unreserved_keywordCURRENT_P'} = 'ignore'; -$replace_line{'unreserved_keywordDAY_P'} = 'ignore'; -$replace_line{'unreserved_keywordHOUR_P'} = 'ignore'; -$replace_line{'unreserved_keywordINPUT_P'} = 'ignore'; -$replace_line{'unreserved_keywordMINUTE_P'} = 'ignore'; -$replace_line{'unreserved_keywordMONTH_P'} = 'ignore'; -$replace_line{'unreserved_keywordSECOND_P'} = 'ignore'; -$replace_line{'unreserved_keywordYEAR_P'} = 'ignore'; -$replace_line{'col_name_keywordCHAR_P'} = 'ignore'; -$replace_line{'col_name_keywordINT_P'} = 'ignore'; -$replace_line{'col_name_keywordVALUES'} = 'ignore'; -$replace_line{'reserved_keywordTO'} = 'ignore'; -$replace_line{'reserved_keywordUNION'} = 'ignore'; - -# some other production rules have to be ignored or replaced -$replace_line{'fetch_argsFORWARDopt_from_incursor_name'} = 'ignore'; -$replace_line{'fetch_argsBACKWARDopt_from_incursor_name'} = 'ignore'; -$replace_line{"opt_array_boundsopt_array_bounds'['Iconst']'"} = 'ignore'; -$replace_line{'VariableShowStmtSHOWvar_name'} = 'SHOW var_name ecpg_into'; -$replace_line{'VariableShowStmtSHOWTIMEZONE'} = 'SHOW TIME ZONE ecpg_into'; -$replace_line{'VariableShowStmtSHOWTRANSACTIONISOLATIONLEVEL'} = 'SHOW TRANSACTION ISOLATION LEVEL ecpg_into'; -$replace_line{'VariableShowStmtSHOWSESSIONAUTHORIZATION'} = 'SHOW SESSION AUTHORIZATION ecpg_into'; -$replace_line{'returning_clauseRETURNINGtarget_list'} = 'RETURNING target_list ecpg_into'; -$replace_line{'ExecuteStmtEXECUTEnameexecute_param_clause'} = 'EXECUTE prepared_name execute_param_clause execute_rest'; -$replace_line{'ExecuteStmtCREATEOptTempTABLEcreate_as_targetASEXECUTEnameexecute_param_clause'} = 'CREATE OptTemp TABLE create_as_target AS EXECUTE prepared_name execute_param_clause'; -$replace_line{'PrepareStmtPREPAREnameprep_type_clauseASPreparableStmt'} = 'PREPARE prepared_name prep_type_clause AS PreparableStmt'; -$replace_line{'var_nameColId'} = 'ECPGColId'; - -line: while (<>) { - chomp; # strip record separator - @Fld = split(' ', $_, -1); - - # Dump the action for a rule - - # mode indicates if we are processing the 'stmt:' rule (mode==0 means normal, mode==1 means stmt:) - # flds are the fields to use. These may start with a '$' - in which case they are the result of a previous non-terminal - # if they dont start with a '$' then they are token name - # - # len is the number of fields in flds... - # leadin is the padding to apply at the beginning (just use for formatting) - - if (/ERRCODE_FEATURE_NOT_SUPPORTED/) { - $feature_not_supported = 1; - next line; - } - - if (/^%%/) { - $tokenmode = 2; - $copymode = 'on'; - $yaccmode++; - $infield = 0; - $fieldcount = 0; - } - - $S = $_; - $prec = 0; - # Make sure any braces are split - $s = '{', $S =~ s/$s/ { /g; - $s = '}', $S =~ s/$s/ } /g; - # Any comments are split - $s = '[/][*]', $S =~ s#$s# /* #g; - $s = '[*][/]', $S =~ s#$s# */ #g; - - # Now split the line into individual fields - $n = (@arr = split(' ', $S)); - - if ($arr[1] eq '%token' && $tokenmode == 0) { - $tokenmode = 1; - &include_stuff('tokens', 'ecpg.tokens', '', 1, 0); - $type = 1; - } - elsif ($arr[1] eq '%type' && $header_included == 0) { - &include_stuff('header', 'ecpg.header', '', 1, 0); - &include_stuff('ecpgtype', 'ecpg.type', '', 1, 0); - $header_included = 1; - } - - if ($tokenmode == 1) { - $str = ''; - for ($a = 1; $a <= $n; $a++) { - if ($arr[$a] eq '/*') { - $comment++; - next; - } - if ($arr[$a] eq '*/') { - $comment--; - next; - } - if ($comment) { - next; - } - if (substr($arr[$a], 1, 1) eq '<') { - next; - # its a type - } - $tokens{$arr[$a]} = 1; - - $str = $str . ' ' . $arr[$a]; - if ($arr[$a] eq 'IDENT' && $arr[$a - 1] eq '%nonassoc') { - # add two more tokens to the list - $str = $str . "\n%nonassoc CSTRING\n%nonassoc UIDENT"; - } - } - &add_to_buffer('orig_tokens', $str); - next line; - } - - # Dont worry about anything if we're not in the right section of gram.y - if ($yaccmode != 1) { - next line; - } - - # Go through each field in turn - for ($fieldIndexer = 1; $fieldIndexer <= $n; $fieldIndexer++) { - if ($arr[$fieldIndexer] eq '*/' && $comment) { - $comment = 0; - next; - } - elsif ($comment) { - next; - } - elsif ($arr[$fieldIndexer] eq '/*') { - # start of a multiline comment - $comment = 1; - next; - } - elsif ($arr[$fieldIndexer] eq '//') { - next line; - } - elsif ($arr[$fieldIndexer] eq '}') { - $brace_indent--; - next; - } - elsif ($arr[$fieldIndexer] eq '{') { - $brace_indent++; - next; - } - - if ($brace_indent > 0) { - next; - } - if ($arr[$fieldIndexer] eq ';') { - if ($copymode eq 'on') { - if ($infield && $includetype eq '') { - &dump_line($stmt_mode, $fields, $field_count); +my %replace_line = ( + 'unreserved_keywordCONNECTION' => 'ignore', + 'unreserved_keywordCURRENT_P' => 'ignore', + 'unreserved_keywordDAY_P' => 'ignore', + 'unreserved_keywordHOUR_P' => 'ignore', + 'unreserved_keywordINPUT_P' => 'ignore', + 'unreserved_keywordMINUTE_P' => 'ignore', + 'unreserved_keywordMONTH_P' => 'ignore', + 'unreserved_keywordSECOND_P' => 'ignore', + 'unreserved_keywordYEAR_P' => 'ignore', + 'col_name_keywordCHAR_P' => 'ignore', + 'col_name_keywordINT_P' => 'ignore', + 'col_name_keywordVALUES' => 'ignore', + 'reserved_keywordTO' => 'ignore', + 'reserved_keywordUNION' => 'ignore', + + # some other production rules have to be ignored or replaced + 'fetch_argsFORWARDopt_from_incursor_name' => 'ignore', + 'fetch_argsBACKWARDopt_from_incursor_name' => 'ignore', + "opt_array_boundsopt_array_bounds'['Iconst']'" => 'ignore', + 'VariableShowStmtSHOWvar_name' => 'SHOW var_name ecpg_into', + 'VariableShowStmtSHOWTIMEZONE' => 'SHOW TIME ZONE ecpg_into', + 'VariableShowStmtSHOWTRANSACTIONISOLATIONLEVEL' => 'SHOW TRANSACTION ISOLATION LEVEL ecpg_into', + 'VariableShowStmtSHOWSESSIONAUTHORIZATION' => 'SHOW SESSION AUTHORIZATION ecpg_into', + 'returning_clauseRETURNINGtarget_list' => 'RETURNING target_list ecpg_into', + 'ExecuteStmtEXECUTEnameexecute_param_clause' => 'EXECUTE prepared_name execute_param_clause execute_rest', + 'ExecuteStmtCREATEOptTempTABLEcreate_as_targetASEXECUTEnameexecute_param_clause' => + 'CREATE OptTemp TABLE create_as_target AS EXECUTE prepared_name execute_param_clause', + 'PrepareStmtPREPAREnameprep_type_clauseASPreparableStmt' => + 'PREPARE prepared_name prep_type_clause AS PreparableStmt', + 'var_nameColId' => 'ECPGColId', +); + +preload_addons(); + +main(); + +dump_buffer('header'); +dump_buffer('tokens'); +dump_buffer('types'); +dump_buffer('ecpgtype'); +dump_buffer('orig_tokens'); +print '%%', "\n"; +print 'prog: statements;', "\n"; +dump_buffer('rules'); +include_file( 'trailer', 'ecpg.trailer' ); +dump_buffer('trailer'); + +sub main +{ + line: while (<>) + { + if (/ERRCODE_FEATURE_NOT_SUPPORTED/) + { + $feature_not_supported = 1; + next line; } - &add_to_buffer('rules', ";\n\n"); - } - else { - $copymode = 'on'; - } - $field_count = 0; - $infield = 0; - $line = ''; - $includetype = ''; - next; - } - if ($arr[$fieldIndexer] eq '|') { - if ($copymode eq 'on') { - if ($infield && $includetype eq '') { - $infield = $infield + &dump_line($stmt_mode, $fields, $field_count); - } - if ($infield > 1) { - $line = '| '; + chomp; + + # comment out the line below to make the result file match (blank line wise) + # the prior version. + #next if ($_ eq ''); + + # Dump the action for a rule - + # stmt_mode indicates if we are processing the 'stmt:' + # rule (mode==0 means normal, mode==1 means stmt:) + # flds are the fields to use. These may start with a '$' - in + # which case they are the result of a previous non-terminal + # + # if they dont start with a '$' then they are token name + # + # len is the number of fields in flds... + # leadin is the padding to apply at the beginning (just use for formatting) + + if (/^%%/) { + $tokenmode = 2; + $copymode = 1; + $yaccmode++; + $infield = 0; } - } - $field_count = 0; - $includetype = ''; - next; - } - if ($replace_token{$arr[$fieldIndexer]}) { - $arr[$fieldIndexer] = $replace_token{$arr[$fieldIndexer]}; - } + my $prec = 0; - # Are we looking at a declaration of a non-terminal ? - if (($arr[$fieldIndexer] =~ '[A-Za-z0-9]+:') || $arr[$fieldIndexer + 1] eq ':') { - $non_term_id = $arr[$fieldIndexer]; - $s = ':', $non_term_id =~ s/$s//g; - - if ($replace_types{$non_term_id} eq '') { - $replace_types{$non_term_id} = '<str>'; - } - if ($replace_types{$non_term_id} eq 'ignore') { - $copymode = ';'; - $line = ''; - next line; - } - else { - $copymode = 'on'; - } - $line = $line . ' ' . $arr[$fieldIndexer]; - # Do we have the : attached already ? - # If yes, we'll have already printed the ':' - if (!($arr[$fieldIndexer] =~ '[A-Za-z0-9]+:')) { - # Consume the ':' which is next... - $line = $line . ':'; - $fieldIndexer++; - } - - # Special mode? - if ($non_term_id eq 'stmt') { - $stmt_mode = 1; - } - else { - $stmt_mode = 0; - } - $tstr = '%type ' . $replace_types{$non_term_id} . ' ' . $non_term_id; - &add_to_buffer('types', $tstr); - - if ($copymode eq 'on') { - &add_to_buffer('rules', $line); - } - $line = ''; - $field_count = 0; - $infield = 1; - next; - } - elsif ($copymode eq 'on') { - $line = $line . ' ' . $arr[$fieldIndexer]; - } - if ($arr[$fieldIndexer] eq '%prec') { - $prec = 1; - next; - } + # Make sure any braces are split + s/{/ { /g; + s/}/ } /g; + + # Any comments are split + s|\/\*| /* |g; + s|\*\/| */ |g; + + # Now split the line into individual fields + my @arr = split(' '); - if ($copymode eq 'on' && !$prec && !$comment && $arr[$fieldIndexer] ne '/*EMPTY*/' && length($arr[$fieldIndexer]) && $infield) { - $nfield = $field_count + 1; - if ($arr[$fieldIndexer] ne 'Op' && ($tokens{$arr[$fieldIndexer]} > 0 || $arr[$fieldIndexer] =~ "'.+'") || $stmt_mode == 1) { - if ($replace_string{$arr[$fieldIndexer]}) { - $S = $replace_string{$arr[$fieldIndexer]}; + if ( $arr[0] eq '%token' && $tokenmode == 0 ) + { + $tokenmode = 1; + include_file( 'tokens', 'ecpg.tokens' ); } - else { - $S = $arr[$fieldIndexer]; + elsif ( $arr[0] eq '%type' && $header_included == 0 ) + { + include_file( 'header', 'ecpg.header' ); + include_file( 'ecpgtype', 'ecpg.type' ); + $header_included = 1; } - $s = '_P', $S =~ s/$s//g; - $s = "'", $S =~ s/$s//g; - if ($stmt_mode == 1) { - $fields{$field_count++} = $S; + + if ( $tokenmode == 1 ) + { + my $str = ''; + my $prior = ''; + for my $a (@arr) + { + if ( $a eq '/*' ) + { + $comment++; + next; + } + if ( $a eq '*/' ) + { + $comment--; + next; + } + if ($comment) + { + next; + } + if ( substr( $a, 0, 1 ) eq '<' ) { + next; + + # its a type + } + $tokens{ $a } = 1; + + $str = $str . ' ' . $a; + if ( $a eq 'IDENT' && $prior eq '%nonassoc' ) + { + # add two more tokens to the list + $str = $str . "\n%nonassoc CSTRING\n%nonassoc UIDENT"; + } + $prior = $a; + } + add_to_buffer( 'orig_tokens', $str ); + next line; + } + + # Dont worry about anything if we're not in the right section of gram.y + if ( $yaccmode != 1 ) + { + next line; } - else { - $fields{$field_count++} = lc($S); + + + # Go through each field in turn + for (my $fieldIndexer = 0 ; $fieldIndexer < scalar(@arr) ; $fieldIndexer++ ) + { + if ( $arr[$fieldIndexer] eq '*/' && $comment ) + { + $comment = 0; + next; + } + elsif ($comment) + { + next; + } + elsif ( $arr[$fieldIndexer] eq '/*' ) + { + # start of a multiline comment + $comment = 1; + next; + } + elsif ( $arr[$fieldIndexer] eq '//' ) + { + next line; + } + elsif ( $arr[$fieldIndexer] eq '}' ) + { + $brace_indent--; + next; + } + elsif ( $arr[$fieldIndexer] eq '{' ) + { + $brace_indent++; + next; + } + + if ( $brace_indent > 0 ) + { + next; + } + if ( $arr[$fieldIndexer] eq ';' ) + { + if ($copymode) + { + if ( $infield ) + { + dump_line( $stmt_mode, \@fields ); + } + add_to_buffer( 'rules', ";\n\n" ); + } + else + { + $copymode = 1; + } + @fields = (); + $infield = 0; + $line = ''; + next; + } + + if ( $arr[$fieldIndexer] eq '|' ) + { + if ($copymode) + { + if ( $infield ) + { + $infield = $infield + dump_line( $stmt_mode, \@fields ); + } + if ( $infield > 1 ) + { + $line = '| '; + } + } + @fields = (); + next; + } + + if ( exists $replace_token{ $arr[$fieldIndexer] } ) + { + $arr[$fieldIndexer] = $replace_token{ $arr[$fieldIndexer] }; + } + + # Are we looking at a declaration of a non-terminal ? + if ( ( $arr[$fieldIndexer] =~ /[A-Za-z0-9]+:/ ) + || $arr[ $fieldIndexer + 1 ] eq ':' ) + { + $non_term_id = $arr[$fieldIndexer]; + $non_term_id =~ tr/://d; + + if ( not defined $replace_types{$non_term_id} ) + { + $replace_types{$non_term_id} = '<str>'; + $copymode = 1; + } + elsif ( $replace_types{$non_term_id} eq 'ignore' ) + { + $copymode = 0; + $line = ''; + next line; + } + $line = $line . ' ' . $arr[$fieldIndexer]; + + # Do we have the : attached already ? + # If yes, we'll have already printed the ':' + if ( !( $arr[$fieldIndexer] =~ '[A-Za-z0-9]+:' ) ) + { + # Consume the ':' which is next... + $line = $line . ':'; + $fieldIndexer++; + } + + # Special mode? + if ( $non_term_id eq 'stmt' ) + { + $stmt_mode = 1; + } + else + { + $stmt_mode = 0; + } + my $tstr = '%type ' . $replace_types{$non_term_id} . ' ' . $non_term_id; + add_to_buffer( 'types', $tstr ); + + if ($copymode) + { + add_to_buffer( 'rules', $line ); + } + $line = ''; + @fields = (); + $infield = 1; + next; + } + elsif ($copymode) { + $line = $line . ' ' . $arr[$fieldIndexer]; + } + if ( $arr[$fieldIndexer] eq '%prec' ) + { + $prec = 1; + next; + } + + if ( $copymode + && !$prec + && !$comment + && length( $arr[$fieldIndexer] ) + && $infield ) + { + if ( + $arr[$fieldIndexer] ne 'Op' + && ( $tokens{ $arr[$fieldIndexer] } > 0 || $arr[$fieldIndexer] =~ /'.+'/ ) + || $stmt_mode == 1 + ) + { + my $S; + if ( exists $replace_string{ $arr[$fieldIndexer] } ) + { + $S = $replace_string{ $arr[$fieldIndexer] }; + } + else + { + $S = $arr[$fieldIndexer]; + } + $S =~ s/_P//g; + $S =~ tr/'//d; + if ( $stmt_mode == 1 ) + { + push(@fields, $S); + } + else + { + push(@fields, lc($S)); + } + } + else + { + push(@fields, '$' . (scalar(@fields)+1)); + } + } } - } - else { - $fields{$field_count++} = "\$" . $nfield; - } } - } } -&dump('header'); -&dump('tokens'); -&dump('types'); -&dump('ecpgtype'); -&dump('orig_tokens'); -print '%%'; -print 'prog: statements;'; -&dump('rules'); -&include_stuff('trailer', 'ecpg.trailer', '', 1, 0); -&dump('trailer'); - -sub include_stuff { - local($includestream, $includefilename, $includeblock, $copy, $field_count) = @_; - $copied = 0; - $inblock = 0; - $filename = $path . "/" . $includefilename; - while (($_ = &Getline2($filename),$getline_ok)) { - if ($includeblock ne '' && $Fld[1] eq 'ECPG:' && $inblock == 0) { - if ($Fld[2] eq $includeblock) { - $copy = 1; - $inblock = 1; - $includetype = $Fld[3]; - if ($includetype eq 'rule') { - &dump_fields($stmt_mode, *fields, $field_count, ' { '); - } - elsif ($includetype eq 'addon') { - &add_to_buffer('rules', ' { '); - } - } - else { - $copy = 0; - } + +# append a file onto a buffer. +# Arguments: buffer_name, filename (without path) +sub include_file +{ + my ($buffer, $filename) = @_; + my $full = "$path/$filename"; + open(my $fh, '<', $full) or die; + while ( <$fh> ) + { + chomp; + add_to_buffer( $buffer, $_ ); } - else { - if ($copy == 1 && $Fld[1] ne 'ECPG:') { - &add_to_buffer($includestream, $_); - $copied = 1; - $inblock = 0; - } + close($fh); +} + +sub include_addon +{ + my($buffer, $block, $fields, $stmt_mode) = @_; + my $rec = $addons{$block}; + return 0 unless $rec; + + if ( $rec->{type} eq 'rule' ) + { + dump_fields( $stmt_mode, $fields, ' { ' ); } - } - delete $opened{$filename} && close($filename); - if ($includetype eq 'addon') { - &dump_fields($stmt_mode, *fields, $field_count, ''); - } - if ($copied == 1) { - $field_count = 0; - $line = ''; - } - $copied; + elsif ( $rec->{type} eq 'addon' ) + { + add_to_buffer( 'rules', ' { ' ); + } + + #add_to_buffer( $stream, $_ ); + #We have an array to add to the buffer, we'll add it ourself instead of + #calling add_to_buffer, which does not know about arrays + + push( @{ $buff{$buffer} }, @{ $rec->{lines} } ); + + if ( $rec->{type} eq 'addon' ) + { + dump_fields( $stmt_mode, $fields, '' ); + } + + + # if we added something (ie there are lines in our array), return 1 + return 1 if (scalar(@{ $rec->{lines} }) > 0); + return 0; } -sub add_to_buffer { - local($buffer, $str) = @_; - $buff{$buffer, $buffcnt{$buffer}++} = $str; + +# include_addon does this same thing, but does not call this +# sub... so if you change this, you need to fix include_addon too +# Pass: buffer_name, string_to_append +sub add_to_buffer +{ + push( @{ $buff{$_[0]} }, "$_[1]\n" ); } -sub dump { - local($buffer) = @_; - print '/* ' . $buffer . ' */'; - for ($a = 0; $a < $buffcnt{$buffer}; $a++) { - print $buff{$buffer, $a}; - } +sub dump_buffer +{ + my($buffer) = @_; + print '/* ', $buffer, ' */',"\n"; + my $ref = $buff{$buffer}; + print @$ref; } -sub dump_fields { - local($mode, *flds, $len, $ln) = @_; - if ($mode == 0) { - #Normal - &add_to_buffer('rules', $ln); - if ($feature_not_supported == 1) { - # we found an unsupported feature, but we have to - # filter out ExecuteStmt: CREATE OptTemp TABLE ... - # because the warning there is only valid in some situations - if ($flds{0} ne 'create' || $flds{2} ne 'table') { - &add_to_buffer('rules', "mmerror(PARSE_ERROR, ET_WARNING, \"unsupported feature will be passed to server\");"); - } - $feature_not_supported = 0; - } +sub dump_fields +{ + my ( $mode, $flds, $ln ) = @_; + my $len = scalar(@$flds); + + if ( $mode == 0 ) + { + #Normal + add_to_buffer( 'rules', $ln ); + if ( $feature_not_supported == 1 ) + { + # we found an unsupported feature, but we have to + # filter out ExecuteStmt: CREATE OptTemp TABLE ... + # because the warning there is only valid in some situations + if ( $flds->[0] ne 'create' || $flds->[2] ne 'table' ) + { + add_to_buffer( 'rules', + 'mmerror(PARSE_ERROR, ET_WARNING, "unsupported feature will be passed to server");' + ); + } + $feature_not_supported = 0; + } - if ($len == 0) { - # We have no fields ? - &add_to_buffer('rules', " \$\$=EMPTY; }"); + if ( $len == 0 ) + { + # We have no fields ? + add_to_buffer( 'rules', ' $$=EMPTY; }' ); + } + else + { + # Go through each field and try to 'aggregate' the tokens + # into a single 'mm_strdup' where possible + my @flds_new; + my $str; + for ( my $z = 0 ; $z < $len ; $z++ ) + { + if ( substr( $flds->[$z], 0, 1 ) eq '$' ) + { + push(@flds_new, $flds->[$z]); + next; + } + + $str = $flds->[$z]; + + while (1) + { + if ( $z >= $len - 1 || substr( $flds->[ $z + 1 ], 0, 1 ) eq '$' ) + { + # We're at the end... + push(@flds_new, "mm_strdup(\"$str\")"); + last; + } + $z++; + $str = $str . ' ' . $flds->[$z]; + } + } + + # So - how many fields did we end up with ? + $len = scalar(@flds_new); + if ( $len == 1 ) + { + # Straight assignement + $str = ' $$ = ' . $flds_new[0] . ';'; + add_to_buffer( 'rules', $str ); + } + else + { + # Need to concatenate the results to form + # our final string + $str = ' $$ = cat_str(' . $len . ',' . join(',', @flds_new) . ');'; + add_to_buffer( 'rules', $str ); + } + add_to_buffer( 'rules', '}' ); + } } - else { - # Go through each field and try to 'aggregate' the tokens into a single 'mm_strdup' where possible - $cnt = 0; - for ($z = 0; $z < $len; $z++) { - if (substr($flds{$z}, 1, 1) eq "\$") { - $flds_new{$cnt++} = $flds{$z}; - next; + else + { + # we're in the stmt: rule + if ($len) + { + # or just the statement ... + add_to_buffer( 'rules', ' { output_statement($1, 0, ECPGst_normal); }' ); } + else + { + add_to_buffer( 'rules', ' { $$ = NULL; }' ); + } + } +} - $str = $flds{$z}; - while (1) { - if ($z >= $len - 1 || substr($flds{$z + 1}, 1, 1) eq "\$") { - # We're at the end... - $flds_new{$cnt++} = "mm_strdup(\"" . $str . "\")"; - last; - } - $z++; - $str = $str . ' ' . $flds{$z}; +sub dump_line +{ + my($stmt_mode, $fields) = @_; + my $block = $non_term_id . $line; + $block =~ tr/ |//d; + my $rep = $replace_line{$block}; + if ($rep) + { + if ($rep eq 'ignore' ) + { + return 0; } - } - - # So - how many fields did we end up with ? - if ($cnt == 1) { - # Straight assignement - $str = " \$\$ = " . $flds_new{0} . ';'; - &add_to_buffer('rules', $str); - } - else { - # Need to concatenate the results to form - # our final string - $str = " \$\$ = cat_str(" . $cnt; - - for ($z = 0; $z < $cnt; $z++) { - $str = $str . ',' . $flds_new{$z}; + + if ( index( $line, '|' ) != -1 ) + { + $line = '| ' . $rep; } - $str = $str . ');'; - &add_to_buffer('rules', $str); - } - if ($literal_mode == 0) { - &add_to_buffer('rules', '}'); - } - } - } - else { - # we're in the stmt: rule - if ($len) { - # or just the statement ... - &add_to_buffer('rules', " { output_statement(\$1, 0, ECPGst_normal); }"); + else + { + $line = $rep; + } + $block = $non_term_id . $line; + $block =~ tr/ |//d; } - else { - &add_to_buffer('rules', " { \$\$ = NULL; }"); + add_to_buffer( 'rules', $line ); + my $i = include_addon( 'rules', $block, $fields, $stmt_mode); + if ( $i == 0 ) + { + dump_fields( $stmt_mode, $fields, ' { ' ); } - } + return 1; } -sub generate_block { - local($line) = @_; - $block = $non_term_id . $line; - $s = ' ', $block =~ s/$s//g; - $s = "\\|", $block =~ s/$s//g; - return $block; -} +=top + load addons into cache + %addons = { + stmtClosePortalStmt => { 'type' => 'block', 'lines' => [ "{", "if (INFORMIX_MODE)" ..., "}" ] }, + stmtViewStmt => { 'type' => 'rule', 'lines' => [ "| ECPGAllocateDescr", ... ] } + } -sub dump_line { - local($stmt_mode, $fields, $field_count) = @_; - $block = &generate_block($line); - if ($replace_line{$block} eq 'ignore') { - return 0; - } - elsif ($replace_line{$block}) { - if (index($line, '|') != 0) { - $line = '| ' . $replace_line{$block}; +=cut +sub preload_addons +{ + my $filename = $path . "/ecpg.addons"; + open(my $fh, '<', $filename) or die; + # there may be multple lines starting ECPG: and then multiple lines of code. + # the code need to be add to all prior ECPG records. + my (@needsRules, @code, $record); + # there may be comments before the first ECPG line, skip them + my $skip = 1; + while ( <$fh> ) + { + if (/^ECPG:\s(\S+)\s?(\w+)?/) + { + $skip = 0; + if (@code) + { + for my $x (@needsRules) + { + push(@{ $x->{lines} }, @code); + } + @code = (); + @needsRules = (); + } + $record = {}; + $record->{type} = $2; + $record->{lines} = []; + if (exists $addons{$1}) { die "Ga! there are dups!\n"; } + $addons{$1} = $record; + push(@needsRules, $record); + } + else + { + next if $skip; + push(@code, $_); + } } - else { - $line = $replace_line{$block}; + close($fh); + if (@code) + { + for my $x (@needsRules) + { + push(@{ $x->{lines} }, @code); + } } - $block = &generate_block($line); - } - &add_to_buffer('rules', $line); - $i = &include_stuff('rules', 'ecpg.addons', $block, 0, $field_count); - if ($i == 0) { - &dump_fields($stmt_mode, *fields, $field_count, ' { '); - } - return 1; } -sub Getline2 { - &Pick('',@_); - if ($getline_ok = (($_ = <$fh>) ne '')) { - chomp; # strip record separator - @Fld = split(' ', $_, -1); - } - $_; -} -sub Pick { - local($mode,$name,$pipe) = @_; - $fh = $name; - open($name,$mode.$name.$pipe) unless $opened{$name}++; -} diff --git a/src/interfaces/ecpg/preproc/parse2.pl b/src/interfaces/ecpg/preproc/parse2.pl deleted file mode 100644 index dd509373a5..0000000000 --- a/src/interfaces/ecpg/preproc/parse2.pl +++ /dev/null @@ -1,648 +0,0 @@ -#!/usr/bin/perl -# src/interfaces/ecpg/preproc/parse2.pl -# parser generater for ecpg version 2 -# call with backend parser as stdin -# -# Copyright (c) 2007-2011, PostgreSQL Global Development Group -# -# Written by Mike Aubury <[email protected]> -# Michael Meskes <[email protected]> -# Andy Colson <[email protected]> -# -# Placed under the same license as PostgreSQL. -# - -use strict; -use warnings; -no warnings 'uninitialized'; - -my $path = shift @ARGV; -$path = "." unless $path; - -my $copymode = 0; -my $brace_indent = 0; -my $yaccmode = 0; -my $header_included = 0; -my $feature_not_supported = 0; -my $tokenmode = 0; - -my(%buff, $infield, $comment, %tokens, %addons ); -my($stmt_mode, @fields); -my($line, $non_term_id); - - -# some token have to be replaced by other symbols -# either in the rule -my %replace_token = ( - 'BCONST' => 'ecpg_bconst', - 'FCONST' => 'ecpg_fconst', - 'Sconst' => 'ecpg_sconst', - 'IDENT' => 'ecpg_ident', - 'PARAM' => 'ecpg_param', -); - -# or in the block -my %replace_string = ( - 'WITH_TIME' => 'with time', - 'NULLS_FIRST' => 'nulls first', - 'NULLS_LAST' => 'nulls last', - 'TYPECAST' => '::', - 'DOT_DOT' => '..', - 'COLON_EQUALS' => ':=', -); - -# specific replace_types for specific non-terminals - never include the ':' -# ECPG-only replace_types are defined in ecpg-replace_types -my %replace_types = ( - 'PrepareStmt' => '<prep>', - 'opt_array_bounds' => '<index>', - - # "ignore" means: do not create type and rules for this non-term-id - 'stmtblock' => 'ignore', - 'stmtmulti' => 'ignore', - 'CreateAsStmt' => 'ignore', - 'DeallocateStmt' => 'ignore', - 'ColId' => 'ignore', - 'type_function_name' => 'ignore', - 'ColLabel' => 'ignore', - 'Sconst' => 'ignore', -); - -# these replace_line commands excise certain keywords from the core keyword -# lists. Be sure to account for these in ColLabel and related productions. -my %replace_line = ( - 'unreserved_keywordCONNECTION' => 'ignore', - 'unreserved_keywordCURRENT_P' => 'ignore', - 'unreserved_keywordDAY_P' => 'ignore', - 'unreserved_keywordHOUR_P' => 'ignore', - 'unreserved_keywordINPUT_P' => 'ignore', - 'unreserved_keywordMINUTE_P' => 'ignore', - 'unreserved_keywordMONTH_P' => 'ignore', - 'unreserved_keywordSECOND_P' => 'ignore', - 'unreserved_keywordYEAR_P' => 'ignore', - 'col_name_keywordCHAR_P' => 'ignore', - 'col_name_keywordINT_P' => 'ignore', - 'col_name_keywordVALUES' => 'ignore', - 'reserved_keywordTO' => 'ignore', - 'reserved_keywordUNION' => 'ignore', - - # some other production rules have to be ignored or replaced - 'fetch_argsFORWARDopt_from_incursor_name' => 'ignore', - 'fetch_argsBACKWARDopt_from_incursor_name' => 'ignore', - "opt_array_boundsopt_array_bounds'['Iconst']'" => 'ignore', - 'VariableShowStmtSHOWvar_name' => 'SHOW var_name ecpg_into', - 'VariableShowStmtSHOWTIMEZONE' => 'SHOW TIME ZONE ecpg_into', - 'VariableShowStmtSHOWTRANSACTIONISOLATIONLEVEL' => 'SHOW TRANSACTION ISOLATION LEVEL ecpg_into', - 'VariableShowStmtSHOWSESSIONAUTHORIZATION' => 'SHOW SESSION AUTHORIZATION ecpg_into', - 'returning_clauseRETURNINGtarget_list' => 'RETURNING target_list ecpg_into', - 'ExecuteStmtEXECUTEnameexecute_param_clause' => 'EXECUTE prepared_name execute_param_clause execute_rest', - 'ExecuteStmtCREATEOptTempTABLEcreate_as_targetASEXECUTEnameexecute_param_clause' => - 'CREATE OptTemp TABLE create_as_target AS EXECUTE prepared_name execute_param_clause', - 'PrepareStmtPREPAREnameprep_type_clauseASPreparableStmt' => - 'PREPARE prepared_name prep_type_clause AS PreparableStmt', - 'var_nameColId' => 'ECPGColId', -); - -preload_addons(); - -main(); - -dump_buffer('header'); -dump_buffer('tokens'); -dump_buffer('types'); -dump_buffer('ecpgtype'); -dump_buffer('orig_tokens'); -print '%%', "\n"; -print 'prog: statements;', "\n"; -dump_buffer('rules'); -include_file( 'trailer', 'ecpg.trailer' ); -dump_buffer('trailer'); - -sub main -{ - line: while (<>) - { - if (/ERRCODE_FEATURE_NOT_SUPPORTED/) - { - $feature_not_supported = 1; - next line; - } - - chomp; - - # comment out the line below to make the result file match (blank line wise) - # the prior version. - #next if ($_ eq ''); - - # Dump the action for a rule - - # stmt_mode indicates if we are processing the 'stmt:' - # rule (mode==0 means normal, mode==1 means stmt:) - # flds are the fields to use. These may start with a '$' - in - # which case they are the result of a previous non-terminal - # - # if they dont start with a '$' then they are token name - # - # len is the number of fields in flds... - # leadin is the padding to apply at the beginning (just use for formatting) - - if (/^%%/) { - $tokenmode = 2; - $copymode = 1; - $yaccmode++; - $infield = 0; - } - - my $prec = 0; - - # Make sure any braces are split - s/{/ { /g; - s/}/ } /g; - - # Any comments are split - s|\/\*| /* |g; - s|\*\/| */ |g; - - # Now split the line into individual fields - my @arr = split(' '); - - if ( $arr[0] eq '%token' && $tokenmode == 0 ) - { - $tokenmode = 1; - include_file( 'tokens', 'ecpg.tokens' ); - } - elsif ( $arr[0] eq '%type' && $header_included == 0 ) - { - include_file( 'header', 'ecpg.header' ); - include_file( 'ecpgtype', 'ecpg.type' ); - $header_included = 1; - } - - if ( $tokenmode == 1 ) - { - my $str = ''; - my $prior = ''; - for my $a (@arr) - { - if ( $a eq '/*' ) - { - $comment++; - next; - } - if ( $a eq '*/' ) - { - $comment--; - next; - } - if ($comment) - { - next; - } - if ( substr( $a, 0, 1 ) eq '<' ) { - next; - - # its a type - } - $tokens{ $a } = 1; - - $str = $str . ' ' . $a; - if ( $a eq 'IDENT' && $prior eq '%nonassoc' ) - { - # add two more tokens to the list - $str = $str . "\n%nonassoc CSTRING\n%nonassoc UIDENT"; - } - $prior = $a; - } - add_to_buffer( 'orig_tokens', $str ); - next line; - } - - # Dont worry about anything if we're not in the right section of gram.y - if ( $yaccmode != 1 ) - { - next line; - } - - - # Go through each field in turn - for (my $fieldIndexer = 0 ; $fieldIndexer < scalar(@arr) ; $fieldIndexer++ ) - { - if ( $arr[$fieldIndexer] eq '*/' && $comment ) - { - $comment = 0; - next; - } - elsif ($comment) - { - next; - } - elsif ( $arr[$fieldIndexer] eq '/*' ) - { - # start of a multiline comment - $comment = 1; - next; - } - elsif ( $arr[$fieldIndexer] eq '//' ) - { - next line; - } - elsif ( $arr[$fieldIndexer] eq '}' ) - { - $brace_indent--; - next; - } - elsif ( $arr[$fieldIndexer] eq '{' ) - { - $brace_indent++; - next; - } - - if ( $brace_indent > 0 ) - { - next; - } - if ( $arr[$fieldIndexer] eq ';' ) - { - if ($copymode) - { - if ( $infield ) - { - dump_line( $stmt_mode, \@fields ); - } - add_to_buffer( 'rules', ";\n\n" ); - } - else - { - $copymode = 1; - } - @fields = (); - $infield = 0; - $line = ''; - next; - } - - if ( $arr[$fieldIndexer] eq '|' ) - { - if ($copymode) - { - if ( $infield ) - { - $infield = $infield + dump_line( $stmt_mode, \@fields ); - } - if ( $infield > 1 ) - { - $line = '| '; - } - } - @fields = (); - next; - } - - if ( exists $replace_token{ $arr[$fieldIndexer] } ) - { - $arr[$fieldIndexer] = $replace_token{ $arr[$fieldIndexer] }; - } - - # Are we looking at a declaration of a non-terminal ? - if ( ( $arr[$fieldIndexer] =~ /[A-Za-z0-9]+:/ ) - || $arr[ $fieldIndexer + 1 ] eq ':' ) - { - $non_term_id = $arr[$fieldIndexer]; - $non_term_id =~ tr/://d; - - if ( not defined $replace_types{$non_term_id} ) - { - $replace_types{$non_term_id} = '<str>'; - $copymode = 1; - } - elsif ( $replace_types{$non_term_id} eq 'ignore' ) - { - $copymode = 0; - $line = ''; - next line; - } - $line = $line . ' ' . $arr[$fieldIndexer]; - - # Do we have the : attached already ? - # If yes, we'll have already printed the ':' - if ( !( $arr[$fieldIndexer] =~ '[A-Za-z0-9]+:' ) ) - { - # Consume the ':' which is next... - $line = $line . ':'; - $fieldIndexer++; - } - - # Special mode? - if ( $non_term_id eq 'stmt' ) - { - $stmt_mode = 1; - } - else - { - $stmt_mode = 0; - } - my $tstr = '%type ' . $replace_types{$non_term_id} . ' ' . $non_term_id; - add_to_buffer( 'types', $tstr ); - - if ($copymode) - { - add_to_buffer( 'rules', $line ); - } - $line = ''; - @fields = (); - $infield = 1; - next; - } - elsif ($copymode) { - $line = $line . ' ' . $arr[$fieldIndexer]; - } - if ( $arr[$fieldIndexer] eq '%prec' ) - { - $prec = 1; - next; - } - - if ( $copymode - && !$prec - && !$comment - && length( $arr[$fieldIndexer] ) - && $infield ) - { - if ( - $arr[$fieldIndexer] ne 'Op' - && ( $tokens{ $arr[$fieldIndexer] } > 0 || $arr[$fieldIndexer] =~ /'.+'/ ) - || $stmt_mode == 1 - ) - { - my $S; - if ( exists $replace_string{ $arr[$fieldIndexer] } ) - { - $S = $replace_string{ $arr[$fieldIndexer] }; - } - else - { - $S = $arr[$fieldIndexer]; - } - $S =~ s/_P//g; - $S =~ tr/'//d; - if ( $stmt_mode == 1 ) - { - push(@fields, $S); - } - else - { - push(@fields, lc($S)); - } - } - else - { - push(@fields, '$' . (scalar(@fields)+1)); - } - } - } - } -} - - -# append a file onto a buffer. -# Arguments: buffer_name, filename (without path) -sub include_file -{ - my ($buffer, $filename) = @_; - my $full = "$path/$filename"; - open(my $fh, '<', $full) or die; - while ( <$fh> ) - { - chomp; - add_to_buffer( $buffer, $_ ); - } - close($fh); -} - -sub include_addon -{ - my($buffer, $block, $fields, $stmt_mode) = @_; - my $rec = $addons{$block}; - return 0 unless $rec; - - if ( $rec->{type} eq 'rule' ) - { - dump_fields( $stmt_mode, $fields, ' { ' ); - } - elsif ( $rec->{type} eq 'addon' ) - { - add_to_buffer( 'rules', ' { ' ); - } - - #add_to_buffer( $stream, $_ ); - #We have an array to add to the buffer, we'll add it ourself instead of - #calling add_to_buffer, which does not know about arrays - - push( @{ $buff{$buffer} }, @{ $rec->{lines} } ); - - if ( $rec->{type} eq 'addon' ) - { - dump_fields( $stmt_mode, $fields, '' ); - } - - - # if we added something (ie there are lines in our array), return 1 - return 1 if (scalar(@{ $rec->{lines} }) > 0); - return 0; -} - - -# include_addon does this same thing, but does not call this -# sub... so if you change this, you need to fix include_addon too -# Pass: buffer_name, string_to_append -sub add_to_buffer -{ - push( @{ $buff{$_[0]} }, "$_[1]\n" ); -} - -sub dump_buffer -{ - my($buffer) = @_; - print '/* ', $buffer, ' */',"\n"; - my $ref = $buff{$buffer}; - print @$ref; -} - -sub dump_fields -{ - my ( $mode, $flds, $ln ) = @_; - my $len = scalar(@$flds); - - if ( $mode == 0 ) - { - #Normal - add_to_buffer( 'rules', $ln ); - if ( $feature_not_supported == 1 ) - { - # we found an unsupported feature, but we have to - # filter out ExecuteStmt: CREATE OptTemp TABLE ... - # because the warning there is only valid in some situations - if ( $flds->[0] ne 'create' || $flds->[2] ne 'table' ) - { - add_to_buffer( 'rules', - 'mmerror(PARSE_ERROR, ET_WARNING, "unsupported feature will be passed to server");' - ); - } - $feature_not_supported = 0; - } - - if ( $len == 0 ) - { - # We have no fields ? - add_to_buffer( 'rules', ' $$=EMPTY; }' ); - } - else - { - # Go through each field and try to 'aggregate' the tokens - # into a single 'mm_strdup' where possible - my @flds_new; - my $str; - for ( my $z = 0 ; $z < $len ; $z++ ) - { - if ( substr( $flds->[$z], 0, 1 ) eq '$' ) - { - push(@flds_new, $flds->[$z]); - next; - } - - $str = $flds->[$z]; - - while (1) - { - if ( $z >= $len - 1 || substr( $flds->[ $z + 1 ], 0, 1 ) eq '$' ) - { - # We're at the end... - push(@flds_new, "mm_strdup(\"$str\")"); - last; - } - $z++; - $str = $str . ' ' . $flds->[$z]; - } - } - - # So - how many fields did we end up with ? - $len = scalar(@flds_new); - if ( $len == 1 ) - { - # Straight assignement - $str = ' $$ = ' . $flds_new[0] . ';'; - add_to_buffer( 'rules', $str ); - } - else - { - # Need to concatenate the results to form - # our final string - $str = ' $$ = cat_str(' . $len . ',' . join(',', @flds_new) . ');'; - add_to_buffer( 'rules', $str ); - } - add_to_buffer( 'rules', '}' ); - } - } - else - { - # we're in the stmt: rule - if ($len) - { - # or just the statement ... - add_to_buffer( 'rules', ' { output_statement($1, 0, ECPGst_normal); }' ); - } - else - { - add_to_buffer( 'rules', ' { $$ = NULL; }' ); - } - } -} - - -sub dump_line -{ - my($stmt_mode, $fields) = @_; - my $block = $non_term_id . $line; - $block =~ tr/ |//d; - my $rep = $replace_line{$block}; - if ($rep) - { - if ($rep eq 'ignore' ) - { - return 0; - } - - if ( index( $line, '|' ) != -1 ) - { - $line = '| ' . $rep; - } - else - { - $line = $rep; - } - $block = $non_term_id . $line; - $block =~ tr/ |//d; - } - add_to_buffer( 'rules', $line ); - my $i = include_addon( 'rules', $block, $fields, $stmt_mode); - if ( $i == 0 ) - { - dump_fields( $stmt_mode, $fields, ' { ' ); - } - return 1; -} - -=top - load addons into cache - %addons = { - stmtClosePortalStmt => { 'type' => 'block', 'lines' => [ "{", "if (INFORMIX_MODE)" ..., "}" ] }, - stmtViewStmt => { 'type' => 'rule', 'lines' => [ "| ECPGAllocateDescr", ... ] } - } - -=cut -sub preload_addons -{ - my $filename = $path . "/ecpg.addons"; - open(my $fh, '<', $filename) or die; - # there may be multple lines starting ECPG: and then multiple lines of code. - # the code need to be add to all prior ECPG records. - my (@needsRules, @code, $record); - # there may be comments before the first ECPG line, skip them - my $skip = 1; - while ( <$fh> ) - { - if (/^ECPG:\s(\S+)\s?(\w+)?/) - { - $skip = 0; - if (@code) - { - for my $x (@needsRules) - { - push(@{ $x->{lines} }, @code); - } - @code = (); - @needsRules = (); - } - $record = {}; - $record->{type} = $2; - $record->{lines} = []; - if (exists $addons{$1}) { die "Ga! there are dups!\n"; } - $addons{$1} = $record; - push(@needsRules, $record); - } - else - { - next if $skip; - push(@code, $_); - } - } - close($fh); - if (@code) - { - for my $x (@needsRules) - { - push(@{ $x->{lines} }, @code); - } - } -} - - |