postgresql/src/backend/parser/gram.y

5596 lines
138 KiB
Plaintext
Raw Normal View History

%{ /* -*-text-*- */
/*#define YYDEBUG 1*/
/*-------------------------------------------------------------------------
*
* gram.y--
1997-09-08 05:20:18 +02:00
* POSTGRES SQL YACC rules/actions
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.54 1999/02/08 14:14:12 wieck Exp $
*
* HISTORY
1997-09-08 05:20:18 +02:00
* AUTHOR DATE MAJOR EVENT
* Andrew Yu Sept, 1994 POSTQUEL to SQL conversion
* Andrew Yu Oct, 1994 lispy code conversion
*
* NOTES
1997-09-08 05:20:18 +02:00
* CAPITALS are used to represent terminal symbols.
* non-capitals are used to represent non-terminals.
* SQL92-specific syntax is separated from plain SQL/Postgres syntax
* to help isolate the non-extensible portions of the parser.
*
1997-09-08 05:20:18 +02:00
* if you use list, make sure the datum is a node so that the printing
* routines work
*
* WARNING
1997-09-08 05:20:18 +02:00
* sometimes we assign constants to makeStrings. Make sure we don't free
* those.
*
*-------------------------------------------------------------------------
*/
#include <string.h>
#include <ctype.h>
1996-11-08 21:46:33 +01:00
#include "postgres.h"
#include "nodes/parsenodes.h"
#include "nodes/print.h"
1996-11-08 21:46:33 +01:00
#include "parser/gramparse.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
#include "utils/palloc.h"
#include "catalog/catname.h"
#include "utils/elog.h"
#include "access/xact.h"
#include "storage/lmgr.h"
#include "utils/numeric.h"
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
#include "parser/analyze.h"
#include "catalog/pg_type.h"
#ifdef MULTIBYTE
#include "mb/pg_wchar.h"
#endif
static char saved_relname[NAMEDATALEN]; /* need this for complex attributes */
static bool QueryIsRule = FALSE;
static List *saved_In_Expr = NIL;
static Oid *param_type_info;
static int pfunc_num_args;
extern List *parsetree;
/*
1997-09-08 05:20:18 +02:00
* If you need access to certain yacc-generated variables and find that
* they're static by default, uncomment the next line. (this is not a
* problem, yet.)
*/
/*#define __YYSCLASS*/
static char *xlateSqlFunc(char *);
static char *xlateSqlType(char *);
static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr);
1998-01-17 06:01:34 +01:00
static Node *makeRowExpr(char *opr, List *largs, List *rargs);
static void mapTargetColumns(List *source, List *target);
static List *makeConstantList( A_Const *node);
static char *FlattenStringList(List *list);
static char *fmtId(char *rawid);
1997-10-31 01:50:39 +01:00
static Node *makeIndexable(char *opname, Node *lexpr, Node *rexpr);
static void param_type_init(Oid *typev, int nargs);
Oid param_type(int t); /* used in parse_expr.c */
/* old versions of flex define this as a macro */
#if defined(yywrap)
#undef yywrap
#endif /* yywrap */
%}
1997-09-08 05:20:18 +02:00
%union
{
double dval;
int ival;
char chr;
char *str;
bool boolean;
bool* pboolean; /* for pg_shadow privileges */
1997-09-08 05:20:18 +02:00
List *list;
Node *node;
Value *value;
Attr *attr;
TypeName *typnam;
DefElem *defelt;
ParamString *param;
SortGroupBy *sortgroupby;
JoinUsing *joinusing;
1997-09-08 05:20:18 +02:00
IndexElem *ielem;
RangeVar *range;
RelExpr *relexp;
A_Indices *aind;
ResTarget *target;
ParamNo *paramno;
VersionStmt *vstmt;
DefineStmt *dstmt;
RuleStmt *rstmt;
1998-01-09 21:06:08 +01:00
InsertStmt *astmt;
}
%type <node> stmt,
1997-09-08 05:20:18 +02:00
AddAttrStmt, ClosePortalStmt,
CopyStmt, CreateStmt, CreateAsStmt, CreateSeqStmt, DefineStmt, DestroyStmt,
1997-09-08 05:20:18 +02:00
ExtendStmt, FetchStmt, GrantStmt, CreateTrigStmt, DropTrigStmt,
CreatePLangStmt, DropPLangStmt,
IndexStmt, ListenStmt, UnlistenStmt, LockStmt, OptimizableStmt,
1997-11-21 19:12:58 +01:00
ProcedureStmt, RecipeStmt, RemoveAggrStmt, RemoveOperStmt,
RemoveFuncStmt, RemoveStmt,
1997-09-08 05:20:18 +02:00
RenameStmt, RevokeStmt, RuleStmt, TransactionStmt, ViewStmt, LoadStmt,
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
CreatedbStmt, DestroydbStmt, VacuumStmt, CursorStmt, SubSelect,
UpdateStmt, InsertStmt, select_w_o_sort, SelectStmt, NotifyStmt, DeleteStmt,
ClusterStmt, ExplainStmt, VariableSetStmt, VariableShowStmt, VariableResetStmt,
CreateUserStmt, AlterUserStmt, DropUserStmt, RuleActionStmt
1997-09-08 05:20:18 +02:00
%type <str> opt_database1, opt_database2, location, encoding
%type <str> opt_lmode
%type <pboolean> user_createdb_clause, user_createuser_clause
%type <str> user_passwd_clause
%type <str> user_valid_clause
%type <list> user_group_list, user_group_clause
%type <str> join_expr, join_outer, join_spec
%type <boolean> TriggerActionTime, TriggerForSpec, PLangTrusted
%type <str> TriggerEvents, TriggerFuncArg
1997-09-08 05:20:18 +02:00
%type <str> relation_name, copy_file_name, copy_delimiter, def_name,
database_name, access_method_clause, access_method, attr_name,
class, index_name, name, func_name, file_name, recipe_name, aggr_argtype
1997-08-21 03:34:44 +02:00
1997-09-08 05:20:18 +02:00
%type <str> opt_id, opt_portal_name,
1997-11-21 19:12:58 +01:00
all_Op, MathOp, opt_name, opt_unique,
OptUseOp, opt_class, SpecialRuleRelation
1997-09-08 05:20:18 +02:00
%type <str> privileges, operation_commalist, grantee
%type <chr> operation, TriggerOneEvent
%type <list> stmtblock, stmtmulti,
result, relation_name_list, OptTableElementList,
OptInherit, definition,
opt_with, func_args, func_args_list,
oper_argtypes, RuleActionList, RuleActionBlock, RuleActionMulti,
1997-09-08 05:20:18 +02:00
opt_column_list, columnList, opt_va_list, va_list,
sort_clause, sortby_list, index_params, index_list, name_list,
from_clause, from_list, opt_array_bounds, nest_array_bounds,
expr_list, attrs, res_target_list, res_target_list2,
def_list, opt_indirection, group_clause, TriggerFuncArgs,
opt_select_limit
%type <node> func_return
%type <boolean> set_opt
%type <boolean> TriggerForOpt, TriggerForType, OptTemp
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
%type <list> for_update_clause
%type <list> join_list
%type <joinusing>
join_using
%type <boolean> opt_union
%type <boolean> opt_table
%type <boolean> opt_trans
%type <node> position_expr
%type <list> extract_list, position_list
%type <list> substr_list, substr_from, substr_for, trim_list
%type <list> opt_interval
%type <boolean> opt_inh_star, opt_binary, opt_instead, opt_with_copy,
index_opt_unique, opt_verbose, opt_analyze
%type <boolean> cursor_clause, opt_cursor, opt_readonly, opt_of
1997-11-21 19:12:58 +01:00
%type <ival> copy_dirn, def_type, opt_direction, remove_type,
opt_column, event
1997-11-21 19:12:58 +01:00
%type <ival> fetch_how_many
%type <node> select_limit_value, select_offset_value
%type <list> OptSeqList
%type <defelt> OptSeqElem
%type <dstmt> def_rest
%type <astmt> insert_rest
%type <node> OptTableElement, ConstraintElem
%type <node> columnDef, alter_clause
%type <defelt> def_elem
%type <node> def_arg, columnElem, where_clause,
a_expr, a_expr_or_null, b_expr, AexprConst,
in_expr, in_expr_nodes, not_in_expr, not_in_expr_nodes,
1997-09-08 05:20:18 +02:00
having_clause
%type <list> row_descriptor, row_list, c_list, c_expr
%type <node> row_expr
%type <str> row_op
1998-12-04 16:34:49 +01:00
%type <node> case_expr, case_arg, when_clause, case_default
%type <list> when_clause_list
%type <ival> sub_type
%type <list> OptCreateAs, CreateAsList
%type <node> CreateAsElement
%type <value> NumericOnly, FloatOnly, IntegerOnly
%type <attr> event_object, attr
1997-09-08 05:20:18 +02:00
%type <sortgroupby> sortby
%type <ielem> index_elem, func_index
%type <range> from_val
%type <relexp> relation_expr
%type <target> res_target_el, res_target_el2
1997-09-08 05:20:18 +02:00
%type <paramno> ParamNo
%type <typnam> Typename, opt_type, Array, Generic, Character, Datetime, Numeric
%type <str> generic, numeric, character, datetime
%type <str> extract_arg
%type <str> opt_charset, opt_collate
%type <str> opt_float
%type <ival> opt_numeric, opt_decimal
%type <boolean> opt_varying, opt_timezone
%type <ival> Iconst
1997-09-08 05:20:18 +02:00
%type <str> Sconst
%type <str> UserId, var_value, zone_value
%type <str> ColId, ColLabel
%type <str> TypeId
%type <node> TableConstraint
%type <list> constraint_list, constraint_expr
%type <list> default_list, default_expr
%type <list> ColPrimaryKey, ColQualList, ColQualifier
%type <node> ColConstraint, ColConstraintElem
%type <list> key_actions, key_action
%type <str> key_match, key_reference
/*
* If you make any token changes, remember to:
1997-09-08 05:20:18 +02:00
* - use "yacc -d" and update parse.h
* - update the keyword table in parser/keywords.c
*/
/* Reserved word tokens
* SQL92 syntax has many type-specific constructs.
* So, go ahead and make these types reserved words,
* and call-out the syntax explicitly.
* This gets annoying when trying to also retain Postgres' nice
* type-extensible features, but we don't really have a choice.
* - thomas 1997-10-11
*/
/* Keywords (in SQL92 reserved words) */
%token ABSOLUTE, ACTION, ADD, ALL, ALTER, AND, ANY, AS, ASC,
BEGIN_TRANS, BETWEEN, BOTH, BY,
1998-12-04 16:34:49 +01:00
CASCADE, CASE, CAST, CHAR, CHARACTER, CHECK, CLOSE,
COALESCE, COLLATE, COLUMN, COMMIT,
CONSTRAINT, CREATE, CROSS, CURRENT, CURRENT_DATE, CURRENT_TIME,
CURRENT_TIMESTAMP, CURRENT_USER, CURSOR,
DAY_P, DECIMAL, DECLARE, DEFAULT, DELETE, DESC, DISTINCT, DOUBLE, DROP,
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
ELSE, END_TRANS, EXCEPT, EXECUTE, EXISTS, EXTRACT,
FALSE_P, FETCH, FLOAT, FOR, FOREIGN, FROM, FULL,
GRANT, GROUP, HAVING, HOUR_P,
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
IN, INNER_P, INSENSITIVE, INSERT, INTERSECT, INTERVAL, INTO, IS,
ISOLATION, JOIN, KEY, LANGUAGE, LEADING, LEFT, LEVEL, LIKE, LOCAL,
MATCH, MINUTE_P, MONTH_P, NAMES,
1998-12-04 16:34:49 +01:00
NATIONAL, NATURAL, NCHAR, NEXT, NO, NOT, NULLIF, NULL_P, NUMERIC,
OF, ON, ONLY, OPTION, OR, ORDER, OUTER_P,
PARTIAL, POSITION, PRECISION, PRIMARY, PRIOR, PRIVILEGES, PROCEDURE, PUBLIC,
READ, REFERENCES, RELATIVE, REVOKE, RIGHT, ROLLBACK,
SCROLL, SECOND_P, SELECT, SET, SUBSTRING,
TABLE, TEMP, THEN, TIME, TIMESTAMP, TIMEZONE_HOUR, TIMEZONE_MINUTE,
TO, TRAILING, TRANSACTION, TRIM, TRUE_P,
UNION, UNIQUE, UPDATE, USER, USING,
VALUES, VARCHAR, VARYING, VIEW,
1998-12-04 16:34:49 +01:00
WHEN, WHERE, WITH, WORK, YEAR_P, ZONE
/* Keywords (in SQL3 reserved words) */
%token TRIGGER
/* Keywords (in SQL92 non-reserved words) */
%token TYPE_P
/* Keywords for Postgres support (not in SQL92 reserved words)
*
* The CREATEDB and CREATEUSER tokens should go away
* when some sort of pg_privileges relation is introduced.
* - Todd A. Brandys 1998-01-01?
*/
%token ABORT_TRANS, AFTER, AGGREGATE, ANALYZE, BACKWARD, BEFORE, BINARY,
CACHE, CLUSTER, COPY, CREATEDB, CREATEUSER, CYCLE,
DATABASE, DELIMITERS, DO, EACH, ENCODING, EXPLAIN, EXTEND,
1997-11-21 19:12:58 +01:00
FORWARD, FUNCTION, HANDLER,
INCREMENT, INDEX, INHERITS, INSTEAD, ISNULL,
LANCOMPILER, LIMIT, LISTEN, LOAD, LOCATION, LOCK_P,
MAXVALUE, MINVALUE, MOVE,
NEW, NOCREATEDB, NOCREATEUSER, NONE, NOTHING, NOTIFY, NOTNULL,
OFFSET, OIDS, OPERATOR, PASSWORD, PROCEDURAL,
RECIPE, RENAME, RESET, RETURNS, ROW, RULE,
SEQUENCE, SERIAL, SETOF, SHOW, START, STATEMENT, STDIN, STDOUT, TRUSTED,
UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION
/* Special keywords, not in the query language - see the "lex" file */
%token <str> IDENT, SCONST, Op
%token <ival> ICONST, PARAM
%token <dval> FCONST
/* these are not real. they are here so that they get generated as #define's*/
1997-09-08 05:20:18 +02:00
%token OP
/* precedence */
%left OR
%left AND
%right NOT
%right '='
%nonassoc '<' '>'
%nonassoc LIKE
%nonassoc BETWEEN
%nonassoc IN
%nonassoc Op /* multi-character ops and user-defined operators */
%nonassoc NOTNULL
%nonassoc ISNULL
%nonassoc IS
%left '+' '-'
%left '*' '/'
%left '|' /* this is the relation union op, not logical or */
/* Unary Operators */
%right ':'
%left ';' /* end of statement or natural log */
%right UMINUS
%left '.'
%left '[' ']'
%nonassoc TYPECAST
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
%left UNION INTERSECT EXCEPT
%%
stmtblock: stmtmulti
1997-09-08 05:20:18 +02:00
{ parsetree = $1; }
| stmt
1997-09-08 05:20:18 +02:00
{ parsetree = lcons($1,NIL); }
;
stmtmulti: stmtmulti stmt ';'
1997-09-08 05:20:18 +02:00
{ $$ = lappend($1, $2); }
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
/***S*I***/
/* We comment the next rule because it seems to be redundant
* and produces 16 shift/reduce conflicts with the new SelectStmt rule
* needed for EXCEPT and INTERSECTS. So far I did not notice any
* violations by removing the rule! */
/* | stmtmulti stmt
{ $$ = lappend($1, $2); } */
| stmt ';'
1997-09-08 05:20:18 +02:00
{ $$ = lcons($1,NIL); }
;
stmt : AddAttrStmt
| AlterUserStmt
1997-09-08 05:20:18 +02:00
| ClosePortalStmt
| CopyStmt
| CreateStmt
| CreateAsStmt
1997-09-08 05:20:18 +02:00
| CreateSeqStmt
| CreatePLangStmt
1997-09-08 05:20:18 +02:00
| CreateTrigStmt
| CreateUserStmt
1997-09-08 05:20:18 +02:00
| ClusterStmt
| DefineStmt
| DestroyStmt
| DropPLangStmt
1997-09-08 05:20:18 +02:00
| DropTrigStmt
| DropUserStmt
1997-09-08 05:20:18 +02:00
| ExtendStmt
| ExplainStmt
| FetchStmt
| GrantStmt
| IndexStmt
| ListenStmt
| UnlistenStmt
| LockStmt
1997-09-08 05:20:18 +02:00
| ProcedureStmt
| RecipeStmt
| RemoveAggrStmt
| RemoveOperStmt
| RemoveFuncStmt
| RemoveStmt
| RenameStmt
| RevokeStmt
| OptimizableStmt
| RuleStmt
| TransactionStmt
| ViewStmt
| LoadStmt
| CreatedbStmt
| DestroydbStmt
| VacuumStmt
| VariableSetStmt
| VariableShowStmt
| VariableResetStmt
;
/*****************************************************************************
*
* Create a new Postgres DBMS user
*
*
*****************************************************************************/
CreateUserStmt: CREATE USER UserId user_passwd_clause user_createdb_clause
user_createuser_clause user_group_clause user_valid_clause
{
CreateUserStmt *n = makeNode(CreateUserStmt);
n->user = $3;
n->password = $4;
n->createdb = $5;
n->createuser = $6;
n->groupElts = $7;
n->validUntil = $8;
$$ = (Node *)n;
}
;
/*****************************************************************************
*
* Alter a postresql DBMS user
*
*
*****************************************************************************/
AlterUserStmt: ALTER USER UserId user_passwd_clause user_createdb_clause
user_createuser_clause user_group_clause user_valid_clause
{
AlterUserStmt *n = makeNode(AlterUserStmt);
n->user = $3;
n->password = $4;
n->createdb = $5;
n->createuser = $6;
n->groupElts = $7;
n->validUntil = $8;
$$ = (Node *)n;
}
;
/*****************************************************************************
*
* Drop a postresql DBMS user
*
*
*****************************************************************************/
DropUserStmt: DROP USER UserId
{
DropUserStmt *n = makeNode(DropUserStmt);
n->user = $3;
$$ = (Node *)n;
}
;
user_passwd_clause: WITH PASSWORD UserId { $$ = $3; }
| /*EMPTY*/ { $$ = NULL; }
;
user_createdb_clause: CREATEDB
{
bool* b;
$$ = (b = (bool*)palloc(sizeof(bool)));
*b = true;
}
| NOCREATEDB
{
bool* b;
$$ = (b = (bool*)palloc(sizeof(bool)));
*b = false;
}
| /*EMPTY*/ { $$ = NULL; }
;
user_createuser_clause: CREATEUSER
{
bool* b;
$$ = (b = (bool*)palloc(sizeof(bool)));
*b = true;
}
| NOCREATEUSER
{
bool* b;
$$ = (b = (bool*)palloc(sizeof(bool)));
*b = false;
}
| /*EMPTY*/ { $$ = NULL; }
;
user_group_list: user_group_list ',' UserId
{
$$ = lcons((void*)makeString($3), $1);
}
| UserId
{
$$ = lcons((void*)makeString($1), NIL);
}
;
user_group_clause: IN GROUP user_group_list { $$ = $3; }
| /*EMPTY*/ { $$ = NULL; }
;
user_valid_clause: VALID UNTIL SCONST { $$ = $3; }
| /*EMPTY*/ { $$ = NULL; }
;
/*****************************************************************************
*
* Set PG internal variable
* SET name TO 'var_value'
* Include SQL92 syntax (thomas 1997-10-22):
* SET TIME ZONE 'var_value'
*
*****************************************************************************/
VariableSetStmt: SET ColId TO var_value
1997-09-08 05:20:18 +02:00
{
VariableSetStmt *n = makeNode(VariableSetStmt);
n->name = $2;
n->value = $4;
$$ = (Node *) n;
}
| SET ColId '=' var_value
1997-09-08 05:20:18 +02:00
{
VariableSetStmt *n = makeNode(VariableSetStmt);
n->name = $2;
n->value = $4;
$$ = (Node *) n;
}
| SET TIME ZONE zone_value
1997-09-08 05:20:18 +02:00
{
VariableSetStmt *n = makeNode(VariableSetStmt);
n->name = "timezone";
n->value = $4;
$$ = (Node *) n;
}
| SET TRANSACTION ISOLATION LEVEL READ ColId
{
VariableSetStmt *n = makeNode(VariableSetStmt);
n->name = "XactIsoLevel";
n->value = $6;
if (strcasecmp(n->value, "COMMITTED"))
elog(ERROR,"parser: syntax error at or near \"%s\"", n->value);
$$ = (Node *) n;
}
| SET TRANSACTION ISOLATION LEVEL ColId
{
VariableSetStmt *n = makeNode(VariableSetStmt);
n->name = "XactIsoLevel";
n->value = $5;
if (strcasecmp(n->value, "SERIALIZABLE"))
elog(ERROR,"parser: syntax error at or near \"%s\"", n->value);
$$ = (Node *) n;
}
| SET NAMES encoding
{
#ifdef MB
VariableSetStmt *n = makeNode(VariableSetStmt);
n->name = "client_encoding";
n->value = $3;
$$ = (Node *) n;
#else
elog(ERROR, "SET NAMES is not supported");
#endif
}
1997-09-08 05:20:18 +02:00
;
var_value: Sconst { $$ = $1; }
| DEFAULT { $$ = NULL; }
1997-09-08 05:20:18 +02:00
;
zone_value: Sconst { $$ = $1; }
| DEFAULT { $$ = NULL; }
| LOCAL { $$ = NULL; }
1997-09-08 05:20:18 +02:00
;
VariableShowStmt: SHOW ColId
1997-09-08 05:20:18 +02:00
{
VariableShowStmt *n = makeNode(VariableShowStmt);
n->name = $2;
$$ = (Node *) n;
}
| SHOW TIME ZONE
{
VariableShowStmt *n = makeNode(VariableShowStmt);
n->name = "timezone";
$$ = (Node *) n;
}
| SHOW TRANSACTION ISOLATION LEVEL
{
VariableShowStmt *n = makeNode(VariableShowStmt);
n->name = "XactIsoLevel";
$$ = (Node *) n;
}
1997-09-08 05:20:18 +02:00
;
VariableResetStmt: RESET ColId
1997-09-08 05:20:18 +02:00
{
VariableResetStmt *n = makeNode(VariableResetStmt);
n->name = $2;
$$ = (Node *) n;
}
| RESET TIME ZONE
{
VariableResetStmt *n = makeNode(VariableResetStmt);
n->name = "timezone";
$$ = (Node *) n;
}
| RESET TRANSACTION ISOLATION LEVEL
{
VariableResetStmt *n = makeNode(VariableResetStmt);
n->name = "XactIsoLevel";
$$ = (Node *) n;
}
1997-09-08 05:20:18 +02:00
;
To: Thomas Lockhart <Thomas.G.Lockhart@jpl.nasa.gov> Subject: Re: [PATCHES] SET DateStyle patches On Tue, 22 Apr 1997, Thomas Lockhart wrote: > Some more patches! These (try to) finish implementing SET variable TO value > for "DateStyle" (changed the name from simply "date" to be more descriptive). > This is based on code from Martin and Bruce (?), which was easy to modify. > The syntax is > > SET DateStyle TO 'iso' > SET DateStyle TO 'postgres' > SET DateStyle TO 'sql' > SET DateStyle TO 'european' > SET DateStyle TO 'noneuropean' > SET DateStyle TO 'us' (same as "noneuropean") > SET DateStyle TO 'default' (current same as "postgres,us") > > ("european" is just compared for the first 4 characters, and "noneuropean" > is compared for the first 7 to allow less typing). > > Multiple arguments are allowed, so SET datestyle TO 'sql,euro' is valid. > > My mods also try to implement "SHOW variable" and "RESET variable", but > that part just core dumps at the moment. I would guess that my errors > are obvious to someone who knows what they are doing with the parser stuff, > so if someone (Bruce and/or Martin??) could have it do the right thing > we will have a more complete set of what we need. > > Also, I would like to have a floating point precision global variable to > implement "SET precision TO 10" and perhaps "SET precision TO 10,2" for > float8 and float4, but I don't know how to do that for integer types rather > than strings. If someone is fixing the SHOW and RESET code, perhaps they can > add some hooks for me to do the floats while they are at it. > > I've left some remnants of variable structures in the source code which > I did not use in the interests of getting something working for v6.1. > We'll have time to clean things up for the next release...
1997-04-23 05:18:27 +02:00
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY :
* addattr ( attr1 = type1 .. attrn = typen ) to <relname> [*]
*
*****************************************************************************/
AddAttrStmt: ALTER TABLE relation_name opt_inh_star alter_clause
1997-09-08 05:20:18 +02:00
{
AddAttrStmt *n = makeNode(AddAttrStmt);
n->relname = $3;
n->inh = $4;
n->colDef = $5;
1997-09-08 05:20:18 +02:00
$$ = (Node *)n;
}
;
alter_clause: ADD opt_column columnDef
{
$$ = $3;
}
| ADD '(' OptTableElementList ')'
{
Node *lp = lfirst($3);
if (length($3) != 1)
1998-01-09 21:06:08 +01:00
elog(ERROR,"ALTER TABLE/ADD() allows one column only");
$$ = lp;
}
| DROP opt_column ColId
1998-01-09 21:06:08 +01:00
{ elog(ERROR,"ALTER TABLE/DROP COLUMN not yet implemented"); }
| ALTER opt_column ColId SET DEFAULT default_expr
1998-01-09 21:06:08 +01:00
{ elog(ERROR,"ALTER TABLE/ALTER COLUMN/SET DEFAULT not yet implemented"); }
| ALTER opt_column ColId DROP DEFAULT
1998-01-09 21:06:08 +01:00
{ elog(ERROR,"ALTER TABLE/ALTER COLUMN/DROP DEFAULT not yet implemented"); }
| ADD ConstraintElem
1998-01-09 21:06:08 +01:00
{ elog(ERROR,"ALTER TABLE/ADD CONSTRAINT not yet implemented"); }
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY :
* close <optname>
*
*****************************************************************************/
ClosePortalStmt: CLOSE opt_id
1997-09-08 05:20:18 +02:00
{
ClosePortalStmt *n = makeNode(ClosePortalStmt);
n->portalname = $2;
$$ = (Node *)n;
}
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY :
* COPY [BINARY] <relname> FROM/TO
* [USING DELIMITERS <delimiter>]
*
*****************************************************************************/
CopyStmt: COPY opt_binary relation_name opt_with_copy copy_dirn copy_file_name copy_delimiter
1997-09-08 05:20:18 +02:00
{
CopyStmt *n = makeNode(CopyStmt);
n->binary = $2;
n->relname = $3;
n->oids = $4;
n->direction = $5;
n->filename = $6;
n->delimiter = $7;
$$ = (Node *)n;
}
;
copy_dirn: TO
{ $$ = TO; }
| FROM
1997-09-08 05:20:18 +02:00
{ $$ = FROM; }
;
/*
* copy_file_name NULL indicates stdio is used. Whether stdin or stdout is
* used depends on the direction. (It really doesn't make sense to copy from
1997-09-08 05:20:18 +02:00
* stdout. We silently correct the "typo". - AY 9/94
*/
1997-09-08 05:20:18 +02:00
copy_file_name: Sconst { $$ = $1; }
| STDIN { $$ = NULL; }
| STDOUT { $$ = NULL; }
;
opt_binary: BINARY { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
1997-09-08 05:20:18 +02:00
;
1997-09-08 05:20:18 +02:00
opt_with_copy: WITH OIDS { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
1997-09-08 05:20:18 +02:00
;
/*
* the default copy delimiter is tab but the user can configure it
*/
copy_delimiter: USING DELIMITERS Sconst { $$ = $3; }
| /*EMPTY*/ { $$ = "\t"; }
1997-09-08 05:20:18 +02:00
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY :
* CREATE relname
*
*****************************************************************************/
CreateStmt: CREATE OptTemp TABLE relation_name '(' OptTableElementList ')'
OptInherit
1997-09-08 05:20:18 +02:00
{
CreateStmt *n = makeNode(CreateStmt);
n->istemp = $2;
n->relname = $4;
n->tableElts = $6;
n->inhRelnames = $8;
n->constraints = NIL;
1997-09-08 05:20:18 +02:00
$$ = (Node *)n;
}
;
OptTemp: TEMP { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
;
OptTableElementList: OptTableElementList ',' OptTableElement
{
if ($3 != NULL)
$$ = lappend($1, $3);
else
$$ = $1;
}
| OptTableElement
{
if ($1 != NULL)
$$ = lcons($1, NIL);
else
$$ = NULL;
}
| /*EMPTY*/ { $$ = NULL; }
1997-09-08 05:20:18 +02:00
;
OptTableElement: columnDef { $$ = $1; }
| TableConstraint { $$ = $1; }
1997-09-08 05:20:18 +02:00
;
columnDef: ColId Typename ColQualifier
{
ColumnDef *n = makeNode(ColumnDef);
n->colname = $1;
n->typename = $2;
n->defval = NULL;
n->is_not_null = FALSE;
n->constraints = $3;
$$ = (Node *)n;
}
| ColId SERIAL ColPrimaryKey
{
ColumnDef *n = makeNode(ColumnDef);
n->colname = $1;
n->typename = makeNode(TypeName);
n->typename->name = xlateSqlType("integer");
n->defval = NULL;
n->is_not_null = TRUE;
n->is_sequence = TRUE;
n->constraints = $3;
$$ = (Node *)n;
}
;
ColQualifier: ColQualList { $$ = $1; }
| /*EMPTY*/ { $$ = NULL; }
1997-09-08 05:20:18 +02:00
;
ColQualList: ColQualList ColConstraint
{
if ($2 != NULL)
$$ = lappend($1, $2);
else
$$ = $1;
}
| ColConstraint
{
if ($1 != NULL)
$$ = lcons($1, NIL);
else
$$ = NULL;
}
;
ColPrimaryKey: PRIMARY KEY
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_PRIMARY;
n->name = NULL;
n->def = NULL;
n->keys = NULL;
$$ = lcons((Node *)n, NIL);
}
| /*EMPTY*/ { $$ = NULL; }
;
ColConstraint:
CONSTRAINT name ColConstraintElem
{
Constraint *n = (Constraint *)$3;
if (n != NULL) n->name = fmtId($2);
$$ = $3;
}
| ColConstraintElem
{ $$ = $1; }
1997-09-08 05:20:18 +02:00
;
/* The column constraint WITH NULL gives a shift/reduce error
* because it requires yacc to look more than one token ahead to
* resolve WITH TIME ZONE and WITH NULL.
* So, leave it out of the syntax for now.
| WITH NULL_P
{
$$ = NULL;
}
* - thomas 1998-09-12
*
* DEFAULT NULL is already the default for Postgres.
* Bue define it here and carry it forward into the system
* to make it explicit.
* - thomas 1998-09-13
*/
ColConstraintElem: CHECK '(' constraint_expr ')'
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_CHECK;
n->name = NULL;
n->def = FlattenStringList($3);
n->keys = NULL;
$$ = (Node *)n;
}
| DEFAULT NULL_P
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_DEFAULT;
n->name = NULL;
n->def = NULL;
n->keys = NULL;
$$ = (Node *)n;
}
| DEFAULT default_expr
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_DEFAULT;
n->name = NULL;
n->def = FlattenStringList($2);
n->keys = NULL;
$$ = (Node *)n;
}
| NOT NULL_P
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_NOTNULL;
n->name = NULL;
n->def = NULL;
n->keys = NULL;
$$ = (Node *)n;
}
| UNIQUE
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_UNIQUE;
n->name = NULL;
n->def = NULL;
n->keys = NULL;
$$ = (Node *)n;
}
| PRIMARY KEY
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_PRIMARY;
n->name = NULL;
n->def = NULL;
n->keys = NULL;
$$ = (Node *)n;
}
| REFERENCES ColId opt_column_list key_match key_actions
{
1998-01-09 21:06:08 +01:00
elog(NOTICE,"CREATE TABLE/FOREIGN KEY clause ignored; not yet implemented");
$$ = NULL;
}
1997-09-08 05:20:18 +02:00
;
1997-08-20 03:12:38 +02:00
default_list: default_list ',' default_expr
{
$$ = lappend($1,makeString(","));
$$ = nconc($$, $3);
}
| default_expr
{
$$ = $1;
}
;
/* The Postgres default column value is NULL.
* Rather than carrying DEFAULT NULL forward as a clause,
* let's just have it be a no-op.
| NULL_P
{ $$ = lcons( makeString("NULL"), NIL); }
* - thomas 1998-09-13
*/
default_expr: AexprConst
{ $$ = makeConstantList((A_Const *) $1); }
| '-' default_expr %prec UMINUS
{ $$ = lcons( makeString( "-"), $2); }
| default_expr '+' default_expr
{ $$ = nconc( $1, lcons( makeString( "+"), $3)); }
| default_expr '-' default_expr
{ $$ = nconc( $1, lcons( makeString( "-"), $3)); }
| default_expr '/' default_expr
{ $$ = nconc( $1, lcons( makeString( "/"), $3)); }
| default_expr '*' default_expr
{ $$ = nconc( $1, lcons( makeString( "*"), $3)); }
| default_expr '=' default_expr
1998-01-09 21:06:08 +01:00
{ elog(ERROR,"boolean expressions not supported in DEFAULT"); }
| default_expr '<' default_expr
1998-01-09 21:06:08 +01:00
{ elog(ERROR,"boolean expressions not supported in DEFAULT"); }
| default_expr '>' default_expr
1998-01-09 21:06:08 +01:00
{ elog(ERROR,"boolean expressions not supported in DEFAULT"); }
| ':' default_expr
{ $$ = lcons( makeString( ":"), $2); }
| ';' default_expr
{ $$ = lcons( makeString( ";"), $2); }
| '|' default_expr
{ $$ = lcons( makeString( "|"), $2); }
| default_expr TYPECAST Typename
{
$3->name = fmtId($3->name);
$$ = nconc( lcons( makeString( "CAST"), $1), makeList( makeString("AS"), $3, -1));
}
| CAST '(' default_expr AS Typename ')'
{
$5->name = fmtId($5->name);
$$ = nconc( lcons( makeString( "CAST"), $3), makeList( makeString("AS"), $5, -1));
}
| '(' default_expr ')'
{ $$ = lappend( lcons( makeString( "("), $2), makeString( ")")); }
| func_name '(' ')'
{
$$ = makeList( makeString($1), makeString("("), -1);
$$ = lappend( $$, makeString(")"));
}
| func_name '(' default_list ')'
{
$$ = makeList( makeString($1), makeString("("), -1);
$$ = nconc( $$, $3);
$$ = lappend( $$, makeString(")"));
}
| default_expr Op default_expr
{
if (!strcmp("<=", $2) || !strcmp(">=", $2))
1998-01-09 21:06:08 +01:00
elog(ERROR,"boolean expressions not supported in DEFAULT");
$$ = nconc( $1, lcons( makeString( $2), $3));
}
| Op default_expr
{ $$ = lcons( makeString( $1), $2); }
| default_expr Op
{ $$ = lappend( $1, makeString( $2)); }
/* XXX - thomas 1997-10-07 v6.2 function-specific code to be changed */
| CURRENT_DATE
{ $$ = lcons( makeString( "date( 'current'::datetime + '0 sec')"), NIL); }
| CURRENT_TIME
{ $$ = lcons( makeString( "'now'::time"), NIL); }
| CURRENT_TIME '(' Iconst ')'
{
if ($3 != 0)
elog(NOTICE,"CURRENT_TIME(%d) precision not implemented; zero used instead",$3);
$$ = lcons( makeString( "'now'::time"), NIL);
}
| CURRENT_TIMESTAMP
{ $$ = lcons( makeString( "now()"), NIL); }
| CURRENT_TIMESTAMP '(' Iconst ')'
{
if ($3 != 0)
elog(NOTICE,"CURRENT_TIMESTAMP(%d) precision not implemented; zero used instead",$3);
$$ = lcons( makeString( "now()"), NIL);
}
| CURRENT_USER
{ $$ = lcons( makeString( "CURRENT_USER"), NIL); }
| USER
{ $$ = lcons( makeString( "USER"), NIL); }
1997-09-08 05:20:18 +02:00
;
/* ConstraintElem specifies constraint syntax which is not embedded into
* a column definition. ColConstraintElem specifies the embedded form.
* - thomas 1997-12-03
*/
TableConstraint: CONSTRAINT name ConstraintElem
1997-09-08 05:20:18 +02:00
{
Constraint *n = (Constraint *)$3;
if (n != NULL) n->name = fmtId($2);
1997-09-08 05:20:18 +02:00
$$ = $3;
}
| ConstraintElem
{ $$ = $1; }
1997-09-08 05:20:18 +02:00
;
ConstraintElem: CHECK '(' constraint_expr ')'
1997-09-08 05:20:18 +02:00
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_CHECK;
n->name = NULL;
n->def = FlattenStringList($3);
$$ = (Node *)n;
1997-09-08 05:20:18 +02:00
}
| UNIQUE '(' columnList ')'
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_UNIQUE;
n->name = NULL;
n->def = NULL;
n->keys = $3;
$$ = (Node *)n;
}
| PRIMARY KEY '(' columnList ')'
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_PRIMARY;
n->name = NULL;
n->def = NULL;
n->keys = $4;
$$ = (Node *)n;
}
| FOREIGN KEY '(' columnList ')' REFERENCES ColId opt_column_list key_match key_actions
{
elog(NOTICE,"CREATE TABLE/FOREIGN KEY clause ignored; not yet implemented");
$$ = NULL;
}
1997-09-08 05:20:18 +02:00
;
constraint_list: constraint_list ',' constraint_expr
{
$$ = lappend($1,makeString(","));
$$ = nconc($$, $3);
}
| constraint_expr
{
$$ = $1;
}
;
constraint_expr: AexprConst
{ $$ = makeConstantList((A_Const *) $1); }
| NULL_P
{ $$ = lcons( makeString("NULL"), NIL); }
| ColId
{
$$ = lcons( makeString(fmtId($1)), NIL);
}
| '-' constraint_expr %prec UMINUS
{ $$ = lcons( makeString( "-"), $2); }
| constraint_expr '+' constraint_expr
{ $$ = nconc( $1, lcons( makeString( "+"), $3)); }
| constraint_expr '-' constraint_expr
{ $$ = nconc( $1, lcons( makeString( "-"), $3)); }
| constraint_expr '/' constraint_expr
{ $$ = nconc( $1, lcons( makeString( "/"), $3)); }
| constraint_expr '*' constraint_expr
{ $$ = nconc( $1, lcons( makeString( "*"), $3)); }
| constraint_expr '=' constraint_expr
{ $$ = nconc( $1, lcons( makeString( "="), $3)); }
| constraint_expr '<' constraint_expr
{ $$ = nconc( $1, lcons( makeString( "<"), $3)); }
| constraint_expr '>' constraint_expr
{ $$ = nconc( $1, lcons( makeString( ">"), $3)); }
| ':' constraint_expr
{ $$ = lcons( makeString( ":"), $2); }
| ';' constraint_expr
{ $$ = lcons( makeString( ";"), $2); }
| '|' constraint_expr
{ $$ = lcons( makeString( "|"), $2); }
| constraint_expr TYPECAST Typename
{
$3->name = fmtId($3->name);
$$ = nconc( lcons( makeString( "CAST"), $1), makeList( makeString("AS"), $3, -1));
}
| CAST '(' constraint_expr AS Typename ')'
{
$5->name = fmtId($5->name);
$$ = nconc( lcons( makeString( "CAST"), $3), makeList( makeString("AS"), $5, -1));
}
| '(' constraint_expr ')'
{ $$ = lappend( lcons( makeString( "("), $2), makeString( ")")); }
| func_name '(' ')'
{
$$ = makeList( makeString($1), makeString("("), -1);
$$ = lappend( $$, makeString(")"));
}
| func_name '(' constraint_list ')'
{
$$ = makeList( makeString($1), makeString("("), -1);
$$ = nconc( $$, $3);
$$ = lappend( $$, makeString(")"));
}
| constraint_expr Op constraint_expr
{ $$ = nconc( $1, lcons( makeString( $2), $3)); }
| constraint_expr LIKE constraint_expr
{ $$ = nconc( $1, lcons( makeString( "LIKE"), $3)); }
| constraint_expr NOT LIKE constraint_expr
{ $$ = nconc( $1, lcons( makeString( "NOT LIKE"), $4)); }
| constraint_expr AND constraint_expr
{ $$ = nconc( $1, lcons( makeString( "AND"), $3)); }
| constraint_expr OR constraint_expr
{ $$ = nconc( $1, lcons( makeString( "OR"), $3)); }
| NOT constraint_expr
{ $$ = lcons( makeString( "NOT"), $2); }
| Op constraint_expr
{ $$ = lcons( makeString( $1), $2); }
| constraint_expr Op
{ $$ = lappend( $1, makeString( $2)); }
| constraint_expr ISNULL
{ $$ = lappend( $1, makeString( "IS NULL")); }
| constraint_expr IS NULL_P
{ $$ = lappend( $1, makeString( "IS NULL")); }
| constraint_expr NOTNULL
{ $$ = lappend( $1, makeString( "IS NOT NULL")); }
| constraint_expr IS NOT NULL_P
{ $$ = lappend( $1, makeString( "IS NOT NULL")); }
| constraint_expr IS TRUE_P
{ $$ = lappend( $1, makeString( "IS TRUE")); }
| constraint_expr IS FALSE_P
{ $$ = lappend( $1, makeString( "IS FALSE")); }
| constraint_expr IS NOT TRUE_P
{ $$ = lappend( $1, makeString( "IS NOT TRUE")); }
| constraint_expr IS NOT FALSE_P
{ $$ = lappend( $1, makeString( "IS NOT FALSE")); }
| constraint_expr IN '(' c_list ')'
{
$$ = lappend( $1, makeString("IN"));
$$ = lappend( $$, makeString("("));
$$ = nconc( $$, $4);
$$ = lappend( $$, makeString(")"));
}
| constraint_expr NOT IN '(' c_list ')'
{
$$ = lappend( $1, makeString("NOT IN"));
$$ = lappend( $$, makeString("("));
$$ = nconc( $$, $5);
$$ = lappend( $$, makeString(")"));
}
| constraint_expr BETWEEN c_expr AND c_expr
{
$$ = lappend( $1, makeString("BETWEEN"));
$$ = nconc( $$, $3);
$$ = lappend( $$, makeString("AND"));
$$ = nconc( $$, $5);
}
| constraint_expr NOT BETWEEN c_expr AND c_expr
{
$$ = lappend( $1, makeString("NOT BETWEEN"));
$$ = nconc( $$, $4);
$$ = lappend( $$, makeString("AND"));
$$ = nconc( $$, $6);
}
;
c_list: c_list ',' c_expr
{
$$ = lappend($1, makeString(","));
$$ = nconc($$, $3);
}
| c_expr
{
$$ = $1;
}
;
c_expr: AexprConst
{ $$ = makeConstantList((A_Const *) $1); }
;
key_match: MATCH FULL { $$ = NULL; }
| MATCH PARTIAL { $$ = NULL; }
| /*EMPTY*/ { $$ = NULL; }
;
key_actions: key_action key_action { $$ = NIL; }
| key_action { $$ = NIL; }
| /*EMPTY*/ { $$ = NIL; }
;
key_action: ON DELETE key_reference { $$ = NIL; }
| ON UPDATE key_reference { $$ = NIL; }
;
key_reference: NO ACTION { $$ = NULL; }
| CASCADE { $$ = NULL; }
| SET DEFAULT { $$ = NULL; }
| SET NULL_P { $$ = NULL; }
;
OptInherit: INHERITS '(' relation_name_list ')' { $$ = $3; }
| /*EMPTY*/ { $$ = NIL; }
;
CreateAsStmt: CREATE OptTemp TABLE relation_name OptCreateAs AS SubSelect
{
SelectStmt *n = (SelectStmt *)$7;
if ($5 != NIL)
mapTargetColumns($5, n->targetList);
n->istemp = $2;
n->into = $4;
$$ = (Node *)n;
}
;
OptCreateAs: '(' CreateAsList ')' { $$ = $2; }
| /*EMPTY*/ { $$ = NULL; }
;
CreateAsList: CreateAsList ',' CreateAsElement { $$ = lappend($1, $3); }
| CreateAsElement { $$ = lcons($1, NIL); }
;
CreateAsElement: ColId
{
ColumnDef *n = makeNode(ColumnDef);
n->colname = $1;
n->typename = NULL;
n->defval = NULL;
n->is_not_null = FALSE;
n->constraints = NULL;
$$ = (Node *)n;
}
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY :
* CREATE SEQUENCE seqname
*
*****************************************************************************/
CreateSeqStmt: CREATE SEQUENCE relation_name OptSeqList
1997-09-08 05:20:18 +02:00
{
CreateSeqStmt *n = makeNode(CreateSeqStmt);
n->seqname = $3;
n->options = $4;
$$ = (Node *)n;
}
;
OptSeqList: OptSeqList OptSeqElem
1997-09-08 05:20:18 +02:00
{ $$ = lappend($1, $2); }
| { $$ = NIL; }
1997-09-08 05:20:18 +02:00
;
OptSeqElem: CACHE IntegerOnly
1997-09-08 05:20:18 +02:00
{
$$ = makeNode(DefElem);
$$->defname = "cache";
1997-09-08 05:20:18 +02:00
$$->arg = (Node *)$2;
}
| CYCLE
1997-09-08 05:20:18 +02:00
{
$$ = makeNode(DefElem);
$$->defname = "cycle";
1997-09-08 05:20:18 +02:00
$$->arg = (Node *)NULL;
}
| INCREMENT IntegerOnly
{
$$ = makeNode(DefElem);
$$->defname = "increment";
$$->arg = (Node *)$2;
}
| MAXVALUE IntegerOnly
{
$$ = makeNode(DefElem);
$$->defname = "maxvalue";
$$->arg = (Node *)$2;
}
| MINVALUE IntegerOnly
{
$$ = makeNode(DefElem);
$$->defname = "minvalue";
$$->arg = (Node *)$2;
}
| START IntegerOnly
{
$$ = makeNode(DefElem);
$$->defname = "start";
$$->arg = (Node *)$2;
}
;
NumericOnly: FloatOnly { $$ = $1; }
| IntegerOnly { $$ = $1; }
FloatOnly: FCONST
{
$$ = makeFloat($1);
}
| '-' FCONST
{
$$ = makeFloat($2);
$$->val.dval = - $$->val.dval;
}
;
IntegerOnly: Iconst
{
$$ = makeInteger($1);
}
| '-' Iconst
{
$$ = makeInteger($2);
$$->val.ival = - $$->val.ival;
}
1997-09-08 05:20:18 +02:00
;
/*****************************************************************************
*
* QUERIES :
* CREATE PROCEDURAL LANGUAGE ...
* DROP PROCEDURAL LANGUAGE ...
*
*****************************************************************************/
CreatePLangStmt: CREATE PLangTrusted PROCEDURAL LANGUAGE Sconst
HANDLER def_name LANCOMPILER Sconst
{
CreatePLangStmt *n = makeNode(CreatePLangStmt);
n->plname = $5;
n->plhandler = $7;
n->plcompiler = $9;
n->pltrusted = $2;
$$ = (Node *)n;
}
;
PLangTrusted: TRUSTED { $$ = TRUE; }
| { $$ = FALSE; }
DropPLangStmt: DROP PROCEDURAL LANGUAGE Sconst
{
DropPLangStmt *n = makeNode(DropPLangStmt);
n->plname = $4;
$$ = (Node *)n;
}
;
1997-09-04 15:24:26 +02:00
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERIES :
* CREATE TRIGGER ...
* DROP TRIGGER ...
1997-09-04 15:24:26 +02:00
*
*****************************************************************************/
CreateTrigStmt: CREATE TRIGGER name TriggerActionTime TriggerEvents ON
1997-09-08 05:20:18 +02:00
relation_name TriggerForSpec EXECUTE PROCEDURE
name '(' TriggerFuncArgs ')'
{
CreateTrigStmt *n = makeNode(CreateTrigStmt);
n->trigname = $3;
n->relname = $7;
n->funcname = $11;
n->args = $13;
n->before = $4;
n->row = $8;
memcpy (n->actions, $5, 4);
$$ = (Node *)n;
}
;
TriggerActionTime: BEFORE { $$ = TRUE; }
| AFTER { $$ = FALSE; }
1997-09-08 05:20:18 +02:00
;
TriggerEvents: TriggerOneEvent
{
char *e = palloc (4);
e[0] = $1; e[1] = 0; $$ = e;
}
| TriggerOneEvent OR TriggerOneEvent
{
char *e = palloc (4);
e[0] = $1; e[1] = $3; e[2] = 0; $$ = e;
}
| TriggerOneEvent OR TriggerOneEvent OR TriggerOneEvent
{
char *e = palloc (4);
e[0] = $1; e[1] = $3; e[2] = $5; e[3] = 0;
$$ = e;
}
1997-09-08 05:20:18 +02:00
;
TriggerOneEvent: INSERT { $$ = 'i'; }
| DELETE { $$ = 'd'; }
| UPDATE { $$ = 'u'; }
1997-09-08 05:20:18 +02:00
;
TriggerForSpec: FOR TriggerForOpt TriggerForType
1997-09-08 05:20:18 +02:00
{
$$ = $3;
1997-09-08 05:20:18 +02:00
}
;
1997-09-04 15:24:26 +02:00
TriggerForOpt: EACH { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
;
TriggerForType: ROW { $$ = TRUE; }
| STATEMENT { $$ = FALSE; }
;
TriggerFuncArgs: TriggerFuncArg
1997-09-08 05:20:18 +02:00
{ $$ = lcons($1, NIL); }
| TriggerFuncArgs ',' TriggerFuncArg
1997-09-08 05:20:18 +02:00
{ $$ = lappend($1, $3); }
| /*EMPTY*/
{ $$ = NIL; }
1997-09-08 05:20:18 +02:00
;
TriggerFuncArg: ICONST
{
char *s = (char *) palloc (256);
sprintf (s, "%d", $1);
$$ = s;
}
| FCONST
{
char *s = (char *) palloc (256);
sprintf (s, "%g", $1);
$$ = s;
}
| Sconst { $$ = $1; }
| IDENT { $$ = $1; }
1997-09-08 05:20:18 +02:00
;
1997-09-04 15:24:26 +02:00
DropTrigStmt: DROP TRIGGER name ON relation_name
1997-09-08 05:20:18 +02:00
{
DropTrigStmt *n = makeNode(DropTrigStmt);
n->trigname = $3;
n->relname = $5;
$$ = (Node *) n;
}
;
1997-09-04 15:24:26 +02:00
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY :
* define (type,operator,aggregate)
*
*****************************************************************************/
DefineStmt: CREATE def_type def_rest
1997-09-08 05:20:18 +02:00
{
$3->defType = $2;
$$ = (Node *)$3;
}
;
def_rest: def_name definition
1997-09-08 05:20:18 +02:00
{
$$ = makeNode(DefineStmt);
$$->defname = $1;
$$->definition = $2;
}
;
def_type: OPERATOR { $$ = OPERATOR; }
| TYPE_P { $$ = TYPE_P; }
| AGGREGATE { $$ = AGGREGATE; }
1997-09-08 05:20:18 +02:00
;
def_name: PROCEDURE { $$ = "procedure"; }
| JOIN { $$ = "join"; }
| ColId { $$ = $1; }
| MathOp { $$ = $1; }
| Op { $$ = $1; }
1997-09-08 05:20:18 +02:00
;
definition: '(' def_list ')' { $$ = $2; }
1997-09-08 05:20:18 +02:00
;
def_list: def_elem { $$ = lcons($1, NIL); }
| def_list ',' def_elem { $$ = lappend($1, $3); }
1997-09-08 05:20:18 +02:00
;
def_elem: def_name '=' def_arg
1997-09-08 05:20:18 +02:00
{
$$ = makeNode(DefElem);
$$->defname = $1;
$$->arg = (Node *)$3;
}
| def_name
1997-09-08 05:20:18 +02:00
{
$$ = makeNode(DefElem);
$$->defname = $1;
$$->arg = (Node *)NULL;
}
| DEFAULT '=' def_arg
1997-09-08 05:20:18 +02:00
{
$$ = makeNode(DefElem);
$$->defname = "default";
1997-09-08 05:20:18 +02:00
$$->arg = (Node *)$3;
}
;
def_arg: ColId { $$ = (Node *)makeString($1); }
| all_Op { $$ = (Node *)makeString($1); }
| NumericOnly { $$ = (Node *)$1; }
| Sconst { $$ = (Node *)makeString($1); }
| SETOF ColId
{
TypeName *n = makeNode(TypeName);
n->name = $2;
n->setof = TRUE;
n->arrayBounds = NULL;
n->typmod = -1;
$$ = (Node *)n;
}
1997-09-08 05:20:18 +02:00
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* destroy <relname1> [, <relname2> .. <relnameN> ]
*
*****************************************************************************/
DestroyStmt: DROP TABLE relation_name_list
1997-09-08 05:20:18 +02:00
{
DestroyStmt *n = makeNode(DestroyStmt);
n->relNames = $3;
n->sequence = FALSE;
1997-09-08 05:20:18 +02:00
$$ = (Node *)n;
}
| DROP SEQUENCE relation_name_list
1997-09-08 05:20:18 +02:00
{
DestroyStmt *n = makeNode(DestroyStmt);
n->relNames = $3;
n->sequence = TRUE;
1997-09-08 05:20:18 +02:00
$$ = (Node *)n;
}
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* fetch/move [forward | backward] [ # | all ] [ in <portalname> ]
* fetch [ forward | backward | absolute | relative ]
* [ # | all | next | prior ] [ [ in | from ] <portalname> ]
*
*****************************************************************************/
1997-09-08 05:20:18 +02:00
FetchStmt: FETCH opt_direction fetch_how_many opt_portal_name
{
FetchStmt *n = makeNode(FetchStmt);
if ($2 == RELATIVE)
{
if ($3 == 0)
elog(ERROR,"FETCH/RELATIVE at current position is not supported");
$2 = FORWARD;
}
if ($3 < 0)
{
$3 = -$3;
$2 = (($2 == FORWARD)? BACKWARD: FORWARD);
}
1997-09-08 05:20:18 +02:00
n->direction = $2;
n->howMany = $3;
n->portalname = $4;
1997-09-29 07:59:16 +02:00
n->ismove = false;
$$ = (Node *)n;
}
| MOVE opt_direction fetch_how_many opt_portal_name
{
FetchStmt *n = makeNode(FetchStmt);
if ($3 < 0)
{
$3 = -$3;
$2 = (($2 == FORWARD)? BACKWARD: FORWARD);
}
1997-09-29 07:59:16 +02:00
n->direction = $2;
n->howMany = $3;
n->portalname = $4;
n->ismove = TRUE;
1997-09-08 05:20:18 +02:00
$$ = (Node *)n;
}
;
opt_direction: FORWARD { $$ = FORWARD; }
| BACKWARD { $$ = BACKWARD; }
| RELATIVE { $$ = RELATIVE; }
| ABSOLUTE
{
elog(NOTICE,"FETCH/ABSOLUTE not supported, using RELATIVE");
$$ = RELATIVE;
}
| /*EMPTY*/ { $$ = FORWARD; /* default */ }
1997-09-08 05:20:18 +02:00
;
fetch_how_many: Iconst { $$ = $1; }
| '-' Iconst { $$ = - $2; }
| ALL { $$ = 0; /* 0 means fetch all tuples*/ }
| NEXT { $$ = 1; }
| PRIOR { $$ = -1; }
| /*EMPTY*/ { $$ = 1; /*default*/ }
1997-09-08 05:20:18 +02:00
;
opt_portal_name: IN name { $$ = $2; }
| FROM name { $$ = $2; }
1997-09-29 07:59:16 +02:00
| /*EMPTY*/ { $$ = NULL; }
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* GRANT [privileges] ON [relation_name_list] TO [GROUP] grantee
*
*****************************************************************************/
GrantStmt: GRANT privileges ON relation_name_list TO grantee opt_with_grant
1997-09-08 05:20:18 +02:00
{
$$ = (Node*)makeAclStmt($2,$4,$6,'+');
}
;
privileges: ALL PRIVILEGES
1997-09-08 05:20:18 +02:00
{
$$ = aclmakepriv("rwaR",0);
}
| ALL
{
$$ = aclmakepriv("rwaR",0);
}
| operation_commalist
{
$$ = $1;
}
;
operation_commalist: operation
1997-09-08 05:20:18 +02:00
{
$$ = aclmakepriv("",$1);
}
| operation_commalist ',' operation
{
$$ = aclmakepriv($1,$3);
}
;
operation: SELECT
1997-09-08 05:20:18 +02:00
{
$$ = ACL_MODE_RD_CHR;
}
| INSERT
{
$$ = ACL_MODE_AP_CHR;
}
| UPDATE
{
$$ = ACL_MODE_WR_CHR;
}
| DELETE
{
$$ = ACL_MODE_WR_CHR;
}
| RULE
1997-09-08 05:20:18 +02:00
{
$$ = ACL_MODE_RU_CHR;
}
;
grantee: PUBLIC
{
$$ = aclmakeuser("A","");
}
| GROUP ColId
1997-09-08 05:20:18 +02:00
{
$$ = aclmakeuser("G",$2);
}
| ColId
1997-09-08 05:20:18 +02:00
{
$$ = aclmakeuser("U",$1);
}
;
opt_with_grant: WITH GRANT OPTION
1997-09-08 05:20:18 +02:00
{
yyerror("WITH GRANT OPTION is not supported. Only relation owners can set privileges");
}
| /*EMPTY*/
1997-09-08 05:20:18 +02:00
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* REVOKE [privileges] ON [relation_name] FROM [user]
*
*****************************************************************************/
RevokeStmt: REVOKE privileges ON relation_name_list FROM grantee
1997-09-08 05:20:18 +02:00
{
$$ = (Node*)makeAclStmt($2,$4,$6,'-');
}
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
1997-11-21 19:12:58 +01:00
* create index <indexname> on <relname>
1997-09-08 05:20:18 +02:00
* using <access> "(" (<col> with <op>)+ ")" [with
* <target_list>]
*
1997-09-08 05:20:18 +02:00
* [where <qual>] is not supported anymore
*****************************************************************************/
1997-09-08 05:20:18 +02:00
IndexStmt: CREATE index_opt_unique INDEX index_name ON relation_name
access_method_clause '(' index_params ')' opt_with
{
/* should check that access_method is valid,
etc ... but doesn't */
IndexStmt *n = makeNode(IndexStmt);
n->unique = $2;
n->idxname = $4;
n->relname = $6;
n->accessMethod = $7;
n->indexParams = $9;
n->withClause = $11;
n->whereClause = NULL;
$$ = (Node *)n;
}
;
index_opt_unique: UNIQUE { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
1997-09-08 05:20:18 +02:00
;
access_method_clause: USING access_method { $$ = $2; }
| /*EMPTY*/ { $$ = "btree"; }
1997-09-08 05:20:18 +02:00
;
index_params: index_list { $$ = $1; }
| func_index { $$ = lcons($1,NIL); }
;
index_list: index_list ',' index_elem { $$ = lappend($1, $3); }
| index_elem { $$ = lcons($1, NIL); }
;
func_index: func_name '(' name_list ')' opt_type opt_class
{
$$ = makeNode(IndexElem);
$$->name = $1;
$$->args = $3;
$$->class = $6;
1998-08-26 07:22:58 +02:00
$$->typename = $5;
}
;
index_elem: attr_name opt_type opt_class
{
$$ = makeNode(IndexElem);
$$->name = $1;
$$->args = NIL;
$$->class = $3;
1998-08-26 07:22:58 +02:00
$$->typename = $2;
}
;
opt_type: ':' Typename { $$ = $2; }
| FOR Typename { $$ = $2; }
| /*EMPTY*/ { $$ = NULL; }
;
/* opt_class "WITH class" conflicts with preceeding opt_type
* for Typename of "TIMESTAMP WITH TIME ZONE"
* So, remove "WITH class" from the syntax. OK??
* - thomas 1997-10-12
* | WITH class { $$ = $2; }
*/
opt_class: class { $$ = $1; }
| USING class { $$ = $2; }
| /*EMPTY*/ { $$ = NULL; }
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* extend index <indexname> [where <qual>]
*
*****************************************************************************/
ExtendStmt: EXTEND INDEX index_name where_clause
1997-09-08 05:20:18 +02:00
{
ExtendStmt *n = makeNode(ExtendStmt);
n->idxname = $3;
n->whereClause = $4;
$$ = (Node *)n;
}
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* execute recipe <recipeName>
*
*****************************************************************************/
RecipeStmt: EXECUTE RECIPE recipe_name
1997-09-08 05:20:18 +02:00
{
RecipeStmt *n;
if (!IsTransactionBlock())
1998-01-09 21:06:08 +01:00
elog(ERROR,"EXECUTE RECIPE may only be used in begin/end transaction blocks");
1997-09-08 05:20:18 +02:00
n = makeNode(RecipeStmt);
n->recipeName = $3;
$$ = (Node *)n;
}
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* define function <fname>
* (language = <lang>, returntype = <typename>
* [, arch_pct = <percentage | pre-defined>]
* [, disk_pct = <percentage | pre-defined>]
* [, byte_pct = <percentage | pre-defined>]
* [, perbyte_cpu = <int | pre-defined>]
* [, percall_cpu = <int | pre-defined>]
* [, iscachable])
* [arg is (<type-1> { , <type-n>})]
* as <filename or code in language as appropriate>
*
*****************************************************************************/
ProcedureStmt: CREATE FUNCTION func_name func_args
RETURNS func_return opt_with AS Sconst LANGUAGE Sconst
1997-09-08 05:20:18 +02:00
{
ProcedureStmt *n = makeNode(ProcedureStmt);
n->funcname = $3;
n->defArgs = $4;
n->returnType = $6;
1997-09-08 05:20:18 +02:00
n->withClause = $7;
n->as = $9;
n->language = $11;
$$ = (Node *)n;
};
opt_with: WITH definition { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; }
1997-09-08 05:20:18 +02:00
;
func_args: '(' func_args_list ')' { $$ = $2; }
| '(' ')' { $$ = NIL; }
1997-09-08 05:20:18 +02:00
;
func_args_list: TypeId
{ $$ = lcons(makeString($1),NIL); }
| func_args_list ',' TypeId
{ $$ = lappend($1,makeString($3)); }
;
func_return: set_opt TypeId
{
TypeName *n = makeNode(TypeName);
n->name = $2;
n->setof = $1;
n->arrayBounds = NULL;
$$ = (Node *)n;
}
;
set_opt: SETOF { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
*
1997-09-08 05:20:18 +02:00
* remove function <funcname>
* (REMOVE FUNCTION "funcname" (arg1, arg2, ...))
* remove aggregate <aggname>
* (REMOVE AGGREGATE "aggname" "aggtype")
* remove operator <opname>
* (REMOVE OPERATOR "opname" (leftoperand_typ rightoperand_typ))
* remove type <typename>
* (REMOVE TYPE "typename")
* remove rule <rulename>
* (REMOVE RULE "rulename")
*
*****************************************************************************/
RemoveStmt: DROP remove_type name
1997-09-08 05:20:18 +02:00
{
RemoveStmt *n = makeNode(RemoveStmt);
n->removeType = $2;
n->name = $3;
$$ = (Node *)n;
}
;
remove_type: TYPE_P { $$ = TYPE_P; }
| INDEX { $$ = INDEX; }
| RULE { $$ = RULE; }
| VIEW { $$ = VIEW; }
1997-09-08 05:20:18 +02:00
;
RemoveAggrStmt: DROP AGGREGATE name aggr_argtype
1997-09-08 05:20:18 +02:00
{
RemoveAggrStmt *n = makeNode(RemoveAggrStmt);
n->aggname = $3;
n->aggtype = $4;
$$ = (Node *)n;
}
;
aggr_argtype: name { $$ = $1; }
| '*' { $$ = NULL; }
1997-09-08 05:20:18 +02:00
;
RemoveFuncStmt: DROP FUNCTION func_name func_args
1997-09-08 05:20:18 +02:00
{
RemoveFuncStmt *n = makeNode(RemoveFuncStmt);
n->funcname = $3;
n->args = $4;
1997-09-08 05:20:18 +02:00
$$ = (Node *)n;
}
;
RemoveOperStmt: DROP OPERATOR all_Op '(' oper_argtypes ')'
1997-09-08 05:20:18 +02:00
{
RemoveOperStmt *n = makeNode(RemoveOperStmt);
n->opname = $3;
n->args = $5;
$$ = (Node *)n;
}
;
all_Op: Op | MathOp;
MathOp: '+' { $$ = "+"; }
| '-' { $$ = "-"; }
| '*' { $$ = "*"; }
| '/' { $$ = "/"; }
| '<' { $$ = "<"; }
| '>' { $$ = ">"; }
| '=' { $$ = "="; }
1997-09-08 05:20:18 +02:00
;
oper_argtypes: name
{
1998-01-09 21:06:08 +01:00
elog(ERROR,"parser: argument type missing (use NONE for unary operators)");
1997-09-08 05:20:18 +02:00
}
| name ',' name
{ $$ = makeList(makeString($1), makeString($3), -1); }
| NONE ',' name /* left unary */
{ $$ = makeList(NULL, makeString($3), -1); }
| name ',' NONE /* right unary */
{ $$ = makeList(makeString($1), NULL, -1); }
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* rename <attrname1> in <relname> [*] to <attrname2>
* rename <relname1> to <relname2>
*
*****************************************************************************/
RenameStmt: ALTER TABLE relation_name opt_inh_star
1997-09-08 05:20:18 +02:00
RENAME opt_column opt_name TO name
{
RenameStmt *n = makeNode(RenameStmt);
n->relname = $3;
n->inh = $4;
n->column = $7;
n->newname = $9;
$$ = (Node *)n;
}
;
opt_name: name { $$ = $1; }
| /*EMPTY*/ { $$ = NULL; }
1997-09-08 05:20:18 +02:00
;
opt_column: COLUMN { $$ = COLUMN; }
| /*EMPTY*/ { $$ = 0; }
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY: Define Rewrite Rule , Define Tuple Rule
* Define Rule <old rules >
*
1997-09-08 05:20:18 +02:00
* only rewrite rule is supported -- ay 9/94
*
*****************************************************************************/
RuleStmt: CREATE RULE name AS
1997-09-08 05:20:18 +02:00
{ QueryIsRule=TRUE; }
ON event TO event_object where_clause
DO opt_instead RuleActionList
1997-09-08 05:20:18 +02:00
{
RuleStmt *n = makeNode(RuleStmt);
n->rulename = $3;
n->event = $7;
n->object = $9;
n->whereClause = $10;
n->instead = $12;
n->actions = $13;
$$ = (Node *)n;
}
;
RuleActionList: NOTHING { $$ = NIL; }
| SelectStmt { $$ = lcons($1, NIL); }
| RuleActionStmt { $$ = lcons($1, NIL); }
| '[' RuleActionBlock ']' { $$ = $2; }
| '(' RuleActionBlock ')' { $$ = $2; }
1997-09-08 05:20:18 +02:00
;
RuleActionBlock: RuleActionMulti { $$ = $1; }
| RuleActionStmt { $$ = lcons($1, NIL); }
1997-09-08 05:20:18 +02:00
;
RuleActionMulti: RuleActionMulti RuleActionStmt
1997-09-08 05:20:18 +02:00
{ $$ = lappend($1, $2); }
| RuleActionMulti RuleActionStmt ';'
{ $$ = lappend($1, $2); }
| RuleActionStmt ';'
1997-09-08 05:20:18 +02:00
{ $$ = lcons($1, NIL); }
;
RuleActionStmt: InsertStmt
| UpdateStmt
| DeleteStmt
| NotifyStmt
;
event_object: relation_name '.' attr_name
1997-09-08 05:20:18 +02:00
{
$$ = makeNode(Attr);
$$->relname = $1;
$$->paramNo = NULL;
$$->attrs = lcons(makeString($3), NIL);
$$->indirection = NIL;
}
| relation_name
{
$$ = makeNode(Attr);
$$->relname = $1;
$$->paramNo = NULL;
$$->attrs = NIL;
$$->indirection = NIL;
}
;
/* change me to select, update, etc. some day */
1997-09-08 05:20:18 +02:00
event: SELECT { $$ = CMD_SELECT; }
| UPDATE { $$ = CMD_UPDATE; }
| DELETE { $$ = CMD_DELETE; }
| INSERT { $$ = CMD_INSERT; }
;
1997-09-08 05:20:18 +02:00
opt_instead: INSTEAD { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
1997-09-08 05:20:18 +02:00
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* NOTIFY <relation_name> can appear both in rule bodies and
* as a query-level command
*
*****************************************************************************/
NotifyStmt: NOTIFY relation_name
1997-09-08 05:20:18 +02:00
{
NotifyStmt *n = makeNode(NotifyStmt);
n->relname = $2;
$$ = (Node *)n;
}
;
ListenStmt: LISTEN relation_name
1997-09-08 05:20:18 +02:00
{
ListenStmt *n = makeNode(ListenStmt);
n->relname = $2;
$$ = (Node *)n;
}
;
UnlistenStmt: UNLISTEN relation_name
{
UnlistenStmt *n = makeNode(UnlistenStmt);
n->relname = $2;
$$ = (Node *)n;
}
| UNLISTEN '*'
{
UnlistenStmt *n = makeNode(UnlistenStmt);
n->relname = "*";
$$ = (Node *)n;
}
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* Transactions:
*
1997-09-08 05:20:18 +02:00
* abort transaction
* (ABORT)
* begin transaction
* (BEGIN)
* end transaction
* (END)
*
*****************************************************************************/
TransactionStmt: ABORT_TRANS opt_trans
1997-09-08 05:20:18 +02:00
{
TransactionStmt *n = makeNode(TransactionStmt);
n->command = ABORT_TRANS;
$$ = (Node *)n;
}
| BEGIN_TRANS opt_trans
1997-09-08 05:20:18 +02:00
{
TransactionStmt *n = makeNode(TransactionStmt);
n->command = BEGIN_TRANS;
$$ = (Node *)n;
}
| COMMIT opt_trans
1997-09-08 05:20:18 +02:00
{
TransactionStmt *n = makeNode(TransactionStmt);
n->command = END_TRANS;
$$ = (Node *)n;
}
| END_TRANS opt_trans
1997-09-08 05:20:18 +02:00
{
TransactionStmt *n = makeNode(TransactionStmt);
n->command = END_TRANS;
$$ = (Node *)n;
}
| ROLLBACK opt_trans
1997-09-08 05:20:18 +02:00
{
TransactionStmt *n = makeNode(TransactionStmt);
n->command = ABORT_TRANS;
$$ = (Node *)n;
}
;
1997-09-08 05:20:18 +02:00
opt_trans: WORK { $$ = TRUE; }
| TRANSACTION { $$ = TRUE; }
| /*EMPTY*/ { $$ = TRUE; }
1997-09-08 05:20:18 +02:00
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* define view <viewname> '('target-list ')' [where <quals> ]
*
*****************************************************************************/
1998-01-09 21:06:08 +01:00
ViewStmt: CREATE VIEW name AS SelectStmt
1997-09-08 05:20:18 +02:00
{
ViewStmt *n = makeNode(ViewStmt);
n->viewname = $3;
n->query = (Query *)$5;
1998-01-11 21:02:32 +01:00
if (((SelectStmt *)n->query)->sortClause != NULL)
elog(ERROR,"Order by and Distinct on views is not implemented.");
1998-01-09 22:26:12 +01:00
if (((SelectStmt *)n->query)->unionClause != NULL)
1998-01-09 21:06:08 +01:00
elog(ERROR,"Views on unions not implemented.");
1999-01-05 16:46:25 +01:00
if (((SelectStmt *)n->query)->forUpdate != NULL)
elog(ERROR, "SELECT FOR UPDATE is not allowed in CREATE VIEW");
1997-09-08 05:20:18 +02:00
$$ = (Node *)n;
}
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* load "filename"
*
*****************************************************************************/
LoadStmt: LOAD file_name
1997-09-08 05:20:18 +02:00
{
LoadStmt *n = makeNode(LoadStmt);
n->filename = $2;
$$ = (Node *)n;
}
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* createdb dbname
*
*****************************************************************************/
CreatedbStmt: CREATE DATABASE database_name WITH opt_database1 opt_database2
1997-09-08 05:20:18 +02:00
{
CreatedbStmt *n = makeNode(CreatedbStmt);
if ($5 == NULL && $6 == NULL) {
elog(ERROR, "CREATE DATABASE WITH requires at least an option");
}
1997-09-08 05:20:18 +02:00
n->dbname = $3;
n->dbpath = $5;
#ifdef MULTIBYTE
if ($6 != NULL) {
n->encoding = pg_char_to_encoding($6);
if (n->encoding < 0) {
elog(ERROR, "invalid encoding name %s", $6);
}
} else {
n->encoding = GetTemplateEncoding();
}
#else
1998-08-04 19:37:48 +02:00
if ($6 != NULL)
elog(ERROR, "WITH ENCODING is not supported");
n->encoding = 0;
#endif
1997-09-08 05:20:18 +02:00
$$ = (Node *)n;
}
| CREATE DATABASE database_name
{
CreatedbStmt *n = makeNode(CreatedbStmt);
n->dbname = $3;
n->dbpath = NULL;
#ifdef MULTIBYTE
n->encoding = GetTemplateEncoding();
#else
n->encoding = 0;
#endif
$$ = (Node *)n;
}
;
opt_database1: LOCATION '=' location { $$ = $3; }
| /*EMPTY*/ { $$ = NULL; }
1997-09-08 05:20:18 +02:00
;
opt_database2: ENCODING '=' encoding { $$ = $3; }
| /*EMPTY*/ { $$ = NULL; }
;
location: Sconst { $$ = $1; }
| DEFAULT { $$ = NULL; }
| /*EMPTY*/ { $$ = NULL; }
;
encoding: Sconst { $$ = $1; }
| DEFAULT { $$ = NULL; }
| /*EMPTY*/ { $$ = NULL; }
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* destroydb dbname
*
*****************************************************************************/
1997-09-08 05:20:18 +02:00
DestroydbStmt: DROP DATABASE database_name
{
DestroydbStmt *n = makeNode(DestroydbStmt);
n->dbname = $3;
$$ = (Node *)n;
}
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* cluster <index_name> on <relation_name>
*
*****************************************************************************/
ClusterStmt: CLUSTER index_name ON relation_name
1997-09-08 05:20:18 +02:00
{
ClusterStmt *n = makeNode(ClusterStmt);
n->relname = $4;
n->indexname = $2;
$$ = (Node*)n;
}
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* vacuum
*
*****************************************************************************/
VacuumStmt: VACUUM opt_verbose opt_analyze
1997-09-08 05:20:18 +02:00
{
VacuumStmt *n = makeNode(VacuumStmt);
n->verbose = $2;
n->analyze = $3;
n->vacrel = NULL;
n->va_spec = NIL;
$$ = (Node *)n;
}
1997-12-02 17:09:15 +01:00
| VACUUM opt_verbose opt_analyze relation_name opt_va_list
1997-09-08 05:20:18 +02:00
{
VacuumStmt *n = makeNode(VacuumStmt);
n->verbose = $2;
1997-12-02 17:09:15 +01:00
n->analyze = $3;
n->vacrel = $4;
1997-09-08 05:20:18 +02:00
n->va_spec = $5;
if ( $5 != NIL && !$4 )
1998-01-09 21:06:08 +01:00
elog(ERROR,"parser: syntax error at or near \"(\"");
1997-09-08 05:20:18 +02:00
$$ = (Node *)n;
}
;
opt_verbose: VERBOSE { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
1997-09-08 05:20:18 +02:00
;
opt_analyze: ANALYZE { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
1997-09-08 05:20:18 +02:00
;
opt_va_list: '(' va_list ')' { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; }
1997-09-08 05:20:18 +02:00
;
va_list: name
1997-09-08 05:20:18 +02:00
{ $$=lcons($1,NIL); }
| va_list ',' name
{ $$=lappend($1,$3); }
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* EXPLAIN query
*
*****************************************************************************/
ExplainStmt: EXPLAIN opt_verbose OptimizableStmt
1997-09-08 05:20:18 +02:00
{
ExplainStmt *n = makeNode(ExplainStmt);
n->verbose = $2;
n->query = (Query*)$3;
$$ = (Node *)n;
}
;
/*****************************************************************************
1997-09-08 05:20:18 +02:00
* *
* Optimizable Stmts: *
* *
* one of the five queries processed by the planner *
* *
* [ultimately] produces query-trees as specified *
* in the query-spec document in ~postgres/ref *
* *
*****************************************************************************/
1998-01-09 21:06:08 +01:00
OptimizableStmt: SelectStmt
1997-09-08 05:20:18 +02:00
| CursorStmt
1998-01-09 21:06:08 +01:00
| UpdateStmt
| InsertStmt
1997-09-08 05:20:18 +02:00
| NotifyStmt
| DeleteStmt /* by default all are $$=$1 */
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* INSERT STATEMENTS
*
*****************************************************************************/
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
/***S*I***/
/* This rule used 'opt_column_list' between 'relation_name' and 'insert_rest'
* originally. When the second rule of 'insert_rest' was changed to use
* the new 'SelectStmt' rule (for INTERSECT and EXCEPT) it produced a shift/reduce
* conflict. So I just changed the rules 'InsertStmt' and 'insert_rest' to accept
* the same statements without any shift/reduce conflicts */
InsertStmt: INSERT INTO relation_name insert_rest
1997-09-08 05:20:18 +02:00
{
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
$4->relname = $3;
$$ = (Node *)$4;
1997-09-08 05:20:18 +02:00
}
;
insert_rest: VALUES '(' res_target_list2 ')'
1997-09-08 05:20:18 +02:00
{
1998-01-09 21:06:08 +01:00
$$ = makeNode(InsertStmt);
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
$$->cols = NULL;
1998-01-11 04:41:57 +01:00
$$->unique = NULL;
1997-09-08 05:20:18 +02:00
$$->targetList = $3;
$$->fromClause = NIL;
$$->whereClause = NULL;
1998-01-11 04:41:57 +01:00
$$->groupClause = NIL;
$$->havingClause = NULL;
1998-01-11 04:41:57 +01:00
$$->unionClause = NIL;
1997-09-08 05:20:18 +02:00
}
| DEFAULT VALUES
{
$$ = makeNode(InsertStmt);
$$->unique = NULL;
$$->targetList = NIL;
$$->fromClause = NIL;
$$->whereClause = NULL;
$$->groupClause = NIL;
$$->havingClause = NULL;
$$->unionClause = NIL;
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
/***S*I***/
$$->intersectClause = NIL;
}
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
/***S*I***/
/* We want the full power of SelectStatements including INTERSECT and EXCEPT
* for insertion */
| SelectStmt
1997-09-08 05:20:18 +02:00
{
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
SelectStmt *n;
n = (SelectStmt *)$1;
1998-01-09 21:06:08 +01:00
$$ = makeNode(InsertStmt);
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
$$->cols = NULL;
$$->unique = n->unique;
$$->targetList = n->targetList;
$$->fromClause = n->fromClause;
$$->whereClause = n->whereClause;
$$->groupClause = n->groupClause;
$$->havingClause = n->havingClause;
$$->unionClause = n->unionClause;
$$->intersectClause = n->intersectClause;
1999-01-21 17:08:55 +01:00
$$->forUpdate = n->forUpdate;
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
}
| '(' columnList ')' VALUES '(' res_target_list2 ')'
{
$$ = makeNode(InsertStmt);
$$->cols = $2;
$$->unique = NULL;
$$->targetList = $6;
$$->fromClause = NIL;
$$->whereClause = NULL;
$$->groupClause = NIL;
$$->havingClause = NULL;
$$->unionClause = NIL;
/***S*I***/
$$->intersectClause = NIL;
}
| '(' columnList ')' SelectStmt
{
SelectStmt *n;
n = (SelectStmt *)$4;
$$ = makeNode(InsertStmt);
$$->cols = $2;
$$->unique = n->unique;
$$->targetList = n->targetList;
$$->fromClause = n->fromClause;
$$->whereClause = n->whereClause;
$$->groupClause = n->groupClause;
$$->havingClause = n->havingClause;
$$->unionClause = n->unionClause;
$$->intersectClause = n->intersectClause;
1997-09-08 05:20:18 +02:00
}
;
opt_column_list: '(' columnList ')' { $$ = $2; }
1997-09-08 05:20:18 +02:00
| /*EMPTY*/ { $$ = NIL; }
;
columnList:
1997-09-08 05:20:18 +02:00
columnList ',' columnElem
{ $$ = lappend($1, $3); }
| columnElem
{ $$ = lcons($1, NIL); }
;
columnElem: ColId opt_indirection
1997-09-08 05:20:18 +02:00
{
Ident *id = makeNode(Ident);
id->name = $1;
id->indirection = $2;
$$ = (Node *)id;
}
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* DELETE STATEMENTS
*
*****************************************************************************/
DeleteStmt: DELETE FROM relation_name
1997-09-08 05:20:18 +02:00
where_clause
{
DeleteStmt *n = makeNode(DeleteStmt);
n->relname = $3;
n->whereClause = $4;
$$ = (Node *)n;
}
;
LockStmt: LOCK_P opt_table relation_name
{
LockStmt *n = makeNode(LockStmt);
n->relname = $3;
n->mode = AccessExclusiveLock;
$$ = (Node *)n;
}
| LOCK_P opt_table relation_name IN opt_lmode ROW IDENT IDENT
{
LockStmt *n = makeNode(LockStmt);
n->relname = $3;
if (strcasecmp($8, "MODE"))
elog(ERROR,"parser: syntax error at or near \"%s\"", $8);
if ($5 != NULL)
{
if (strcasecmp($5, "SHARE"))
elog(ERROR,"parser: syntax error at or near \"%s\"", $5);
if (strcasecmp($7, "EXCLUSIVE"))
elog(ERROR,"parser: syntax error at or near \"%s\"", $7);
n->mode = ShareRowExclusiveLock;
}
else
{
if (strcasecmp($7, "SHARE") == 0)
n->mode = RowShareLock;
else if (strcasecmp($7, "EXCLUSIVE") == 0)
n->mode = RowExclusiveLock;
else
elog(ERROR,"parser: syntax error at or near \"%s\"", $7);
}
$$ = (Node *)n;
}
| LOCK_P opt_table relation_name IN IDENT IDENT IDENT
{
LockStmt *n = makeNode(LockStmt);
n->relname = $3;
if (strcasecmp($7, "MODE"))
elog(ERROR,"parser: syntax error at or near \"%s\"", $7);
if (strcasecmp($5, "ACCESS"))
elog(ERROR,"parser: syntax error at or near \"%s\"", $5);
if (strcasecmp($6, "SHARE") == 0)
n->mode = AccessShareLock;
else if (strcasecmp($6, "EXCLUSIVE") == 0)
n->mode = AccessExclusiveLock;
else
elog(ERROR,"parser: syntax error at or near \"%s\"", $6);
$$ = (Node *)n;
}
| LOCK_P opt_table relation_name IN IDENT IDENT
{
LockStmt *n = makeNode(LockStmt);
n->relname = $3;
if (strcasecmp($6, "MODE"))
elog(ERROR,"parser: syntax error at or near \"%s\"", $6);
if (strcasecmp($5, "SHARE") == 0)
n->mode = ShareLock;
else if (strcasecmp($5, "EXCLUSIVE") == 0)
n->mode = ExclusiveLock;
else
elog(ERROR,"parser: syntax error at or near \"%s\"", $5);
$$ = (Node *)n;
}
;
opt_lmode: IDENT { $$ = $1; }
| /*EMPTY*/ { $$ = NULL; }
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
1998-01-09 21:06:08 +01:00
* UpdateStmt (UPDATE)
*
*****************************************************************************/
1998-01-09 21:06:08 +01:00
UpdateStmt: UPDATE relation_name
1997-09-08 05:20:18 +02:00
SET res_target_list
from_clause
where_clause
{
1998-01-09 21:06:08 +01:00
UpdateStmt *n = makeNode(UpdateStmt);
1997-09-08 05:20:18 +02:00
n->relname = $2;
n->targetList = $4;
n->fromClause = $5;
n->whereClause = $6;
$$ = (Node *)n;
}
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* CURSOR STATEMENTS
*
*****************************************************************************/
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
/***S*I***/
CursorStmt: DECLARE name opt_cursor CURSOR FOR SelectStmt cursor_clause
{
SelectStmt *n;
n= (SelectStmt *)$6;
/* from PORTAL name */
/*
* 15 august 1991 -- since 3.0 postgres does locking
1997-09-08 05:20:18 +02:00
* right, we discovered that portals were violating
* locking protocol. portal locks cannot span xacts.
* as a short-term fix, we installed the check here.
* -- mao
*/
if (!IsTransactionBlock())
1998-01-09 21:06:08 +01:00
elog(ERROR,"Named portals may only be used in begin/end transaction blocks");
1997-09-08 05:20:18 +02:00
n->portalname = $2;
n->binary = $3;
$$ = (Node *)n;
}
;
opt_cursor: BINARY { $$ = TRUE; }
| INSENSITIVE { $$ = FALSE; }
| SCROLL { $$ = FALSE; }
| INSENSITIVE SCROLL { $$ = FALSE; }
| /*EMPTY*/ { $$ = FALSE; }
;
cursor_clause: FOR opt_readonly { $$ = $2; }
| /*EMPTY*/ { $$ = FALSE; }
;
opt_readonly: READ ONLY { $$ = TRUE; }
| UPDATE opt_of
{
elog(ERROR,"DECLARE/UPDATE not supported;"
" Cursors must be READ ONLY.");
$$ = FALSE;
}
;
opt_of: OF columnList
{
$$ = FALSE;
}
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* QUERY:
* SELECT STATEMENTS
*
*****************************************************************************/
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
/***S*I***/
/* The new 'SelectStmt' rule adapted for the optional use of INTERSECT EXCEPT and UNION
* accepts the use of '(' and ')' to select an order of set operations.
*
* The rule returns a SelectStmt Node having the set operations attached to
* unionClause and intersectClause (NIL if no set operations were present) */
SelectStmt: select_w_o_sort sort_clause for_update_clause opt_select_limit
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
{
/* There were no set operations, so just attach the sortClause */
if IsA($1, SelectStmt)
{
SelectStmt *n = (SelectStmt *)$1;
n->sortClause = $2;
n->forUpdate = $3;
n->limitOffset = nth(0, $4);
n->limitCount = nth(1, $4);
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
$$ = (Node *)n;
}
/* There were set operations: The root of the operator tree
* is delivered by $1 but we cannot hand back an A_Expr Node.
* So we search for the leftmost 'SelectStmt' in the operator
* tree $1 (which is the first Select Statement in the query
* typed in by the user or where ever it came from).
*
* Then we attach the whole operator tree to 'intersectClause',
* and a list of all 'SelectStmt' Nodes to 'unionClause' and
* hand back the leftmost 'SelectStmt' Node. (We do it this way
* because the following functions (e.g. parse_analyze etc.)
* excpect a SelectStmt node and not an operator tree! The whole
* tree attached to 'intersectClause' won't be touched by
* parse_analyze() etc. until the function
* Except_Intersect_Rewrite() (in rewriteHandler.c) which performs
* the necessary steps to be able create a plan!) */
else
{
List *select_list = NIL;
SelectStmt *first_select;
Node *op = (Node *) $1;
bool intersect_present = FALSE, unionall_present = FALSE;
/* Take the operator tree as an argument and
* create a list of all SelectStmt Nodes found in the tree.
*
* If one of the SelectStmt Nodes has the 'unionall' flag
* set to true the 'unionall_present' flag is also set to
* true */
create_select_list((Node *)op, &select_list, &unionall_present);
/* Replace all the A_Expr Nodes in the operator tree by
* Expr Nodes.
*
* If an INTERSECT or an EXCEPT is present, the
* 'intersect_present' flag is set to true */
op = A_Expr_to_Expr(op, &intersect_present);
/* If both flags are set to true we have a UNION ALL
* statement mixed up with INTERSECT or EXCEPT
* which can not be handled at the moment */
if (intersect_present && unionall_present)
{
elog(ERROR,"UNION ALL not allowed in mixed set operations!");
}
/* Get the leftmost SeletStmt Node (which automatically
* represents the first Select Statement of the query!) */
first_select = (SelectStmt *)lfirst(select_list);
/* Attach the list of all SeletStmt Nodes to unionClause */
first_select->unionClause = select_list;
/* Attach the whole operator tree to intersectClause */
first_select->intersectClause = (List *) op;
/* finally attach the sort clause */
first_select->sortClause = $2;
1999-01-18 07:32:27 +01:00
first_select->forUpdate = $3;
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
$$ = (Node *)first_select;
}
1999-01-25 13:01:19 +01:00
if (((SelectStmt *)$$)->forUpdate != NULL && QueryIsRule)
elog(ERROR, "SELECT FOR UPDATE is not allowed in RULES");
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
}
;
/***S*I***/
/* This rule parses Select statements including UNION INTERSECT and EXCEPT.
* '(' and ')' can be used to specify the order of the operations
* (UNION EXCEPT INTERSECT). Without the use of '(' and ')' we want the
* operations to be left associative.
*
* The sort_clause is not handled here!
*
* The rule builds up an operator tree using A_Expr Nodes. AND Nodes represent
* INTERSECTs OR Nodes represent UNIONs and AND NOT nodes represent EXCEPTs.
* The SelectStatements to be connected are the left and right arguments to
* the A_Expr Nodes.
* If no set operations show up in the query the tree consists only of one
* SelectStmt Node */
select_w_o_sort: '(' select_w_o_sort ')'
{
$$ = $2;
}
| SubSelect
{
$$ = $1;
}
| select_w_o_sort EXCEPT select_w_o_sort
{
$$ = (Node *)makeA_Expr(AND,NULL,$1,
makeA_Expr(NOT,NULL,NULL,$3));
}
| select_w_o_sort UNION opt_union select_w_o_sort
{
if (IsA($4, SelectStmt))
{
SelectStmt *n = (SelectStmt *)$4;
n->unionall = $3;
}
$$ = (Node *)makeA_Expr(OR,NULL,$1,$4);
}
| select_w_o_sort INTERSECT select_w_o_sort
{
$$ = (Node *)makeA_Expr(AND,NULL,$1,$3);
}
;
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
/***S*I***/
SubSelect: SELECT opt_unique res_target_list2
result from_clause where_clause
group_clause having_clause
1997-09-08 05:20:18 +02:00
{
1998-01-09 21:06:08 +01:00
SelectStmt *n = makeNode(SelectStmt);
1997-09-08 05:20:18 +02:00
n->unique = $2;
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
n->unionall = FALSE;
1997-09-08 05:20:18 +02:00
n->targetList = $3;
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
/***S*I***/
/* This is new: Subselects support the INTO clause
* which allows queries that are not part of the
* SQL92 standard and should not be formulated!
* We need it for INTERSECT and EXCEPT and I did not
* want to create a new rule 'SubSelect1' including the
* feature. If it makes troubles we will have to add
* a new rule and change this to prevent INTOs in
* Subselects again */
n->istemp = (bool) ((Value *) lfirst($4))->val.ival;
n->into = (char *) lnext($4);
Hi! INTERSECT and EXCEPT is available for postgresql-v6.4! The patch against v6.4 is included at the end of the current text (in uuencoded form!) I also included the text of my Master's Thesis. (a postscript version). I hope that you find something of it useful and would be happy if parts of it find their way into the PostgreSQL documentation project (If so, tell me, then I send the sources of the document!) The contents of the document are: -) The first chapter might be of less interest as it gives only an overview on SQL. -) The second chapter gives a description on much of PostgreSQL's features (like user defined types etc. and how to use these features) -) The third chapter starts with an overview of PostgreSQL's internal structure with focus on the stages a query has to pass (i.e. parser, planner/optimizer, executor). Then a detailed description of the implementation of the Having clause and the Intersect/Except logic is given. Originally I worked on v6.3.2 but never found time enough to prepare and post a patch. Now I applied the changes to v6.4 to get Intersect and Except working with the new version. Chapter 3 of my documentation deals with the changes against v6.3.2, so keep that in mind when comparing the parts of the code printed there with the patched sources of v6.4. Here are some remarks on the patch. There are some things that have still to be done but at the moment I don't have time to do them myself. (I'm doing my military service at the moment) Sorry for that :-( -) I used a rewrite technique for the implementation of the Except/Intersect logic which rewrites the query to a semantically equivalent query before it is handed to the rewrite system (for views, rules etc.), planner, executor etc. -) In v6.3.2 the types of the attributes of two select statements connected by the UNION keyword had to match 100%. In v6.4 the types only need to be familiar (i.e. int and float can be mixed). Since this feature did not exist when I worked on Intersect/Except it does not work correctly for Except/Intersect queries WHEN USED IN COMBINATION WITH UNIONS! (i.e. sometimes the wrong type is used for the resulting table. This is because until now the types of the attributes of the first select statement have been used for the resulting table. When Intersects and/or Excepts are used in combination with Unions it might happen, that the first select statement of the original query appears at another position in the query which will be executed. The reason for this is the technique used for the implementation of Except/Intersect which does a query rewrite!) NOTE: It is NOT broken for pure UNION queries and pure INTERSECT/EXCEPT queries!!! -) I had to add the field intersect_clause to some data structures but did not find time to implement printfuncs for the new field. This does NOT break the debug modes but when an Except/Intersect is used the query debug output will be the already rewritten query. -) Massive changes to the grammar rules for SELECT and INSERT statements have been necessary (see comments in gram.y and documentation for deatails) in order to be able to use mixed queries like (SELECT ... UNION (SELECT ... EXCEPT SELECT)) INTERSECT SELECT...; -) When using UNION/EXCEPT/INTERSECT you will get: NOTICE: equal: "Don't know if nodes of type xxx are equal". I did not have time to add comparsion support for all the needed nodes, but the default behaviour of the function equal met my requirements. I did not dare to supress this message! That's the reason why the regression test for union will fail: These messages are also included in the union.out file! -) Somebody of you changed the union_planner() function for v6.4 (I copied the targetlist to new_tlist and that was removed and replaced by a cleanup of the original targetlist). These chnages violated some having queries executed against views so I changed it back again. I did not have time to examine the differences between the two versions but now it works :-) If you want to find out, try the file queries/view_having.sql on both versions and compare the results . Two queries won't produce a correct result with your version. regards Stefan
1999-01-18 01:10:17 +01:00
1997-09-08 05:20:18 +02:00
n->fromClause = $5;
n->whereClause = $6;
n->groupClause = $7;
n->havingClause = $8;
$$ = (Node *)n;
}
;
/* easy way to return two values. Can someone improve this? bjm */
result: INTO OptTemp opt_table relation_name { $$ = lcons(makeInteger($2), (List *)$4); }
| /*EMPTY*/ { $$ = lcons(makeInteger(false), NIL); }
;
opt_table: TABLE { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
1997-09-08 05:20:18 +02:00
;
opt_union: ALL { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
;
opt_unique: DISTINCT { $$ = "*"; }
| DISTINCT ON ColId { $$ = $3; }
| ALL { $$ = NULL; }
| /*EMPTY*/ { $$ = NULL; }
1997-09-08 05:20:18 +02:00
;
1997-09-08 05:20:18 +02:00
sort_clause: ORDER BY sortby_list { $$ = $3; }
| /*EMPTY*/ { $$ = NIL; }
1997-09-08 05:20:18 +02:00
;
sortby_list: sortby { $$ = lcons($1, NIL); }
| sortby_list ',' sortby { $$ = lappend($1, $3); }
1997-09-08 05:20:18 +02:00
;
sortby: a_expr OptUseOp
1997-09-08 05:20:18 +02:00
{
$$ = makeNode(SortGroupBy);
$$->node = $1;
1997-09-08 05:20:18 +02:00
$$->useOp = $2;
}
;
OptUseOp: USING Op { $$ = $2; }
| USING '<' { $$ = "<"; }
| USING '>' { $$ = ">"; }
| ASC { $$ = "<"; }
| DESC { $$ = ">"; }
| /*EMPTY*/ { $$ = "<"; /*default*/ }
1997-09-08 05:20:18 +02:00
;
opt_select_limit: LIMIT select_limit_value ',' select_offset_value
{ $$ = lappend(lappend(NIL, $4), $2); }
| LIMIT select_limit_value OFFSET select_offset_value
{ $$ = lappend(lappend(NIL, $4), $2); }
| LIMIT select_limit_value
{ $$ = lappend(lappend(NIL, NULL), $2); }
| OFFSET select_offset_value LIMIT select_limit_value
{ $$ = lappend(lappend(NIL, $2), $4); }
| OFFSET select_offset_value
{ $$ = lappend(lappend(NIL, $2), NULL); }
| /* EMPTY */
{ $$ = lappend(lappend(NIL, NULL), NULL); }
;
select_limit_value: Iconst
{
Const *n = makeNode(Const);
if ($1 < 1)
elog(ERROR, "selection limit must be ALL or a positive integer value > 0");
n->consttype = INT4OID;
n->constlen = sizeof(int4);
n->constvalue = (Datum)$1;
n->constisnull = FALSE;
n->constbyval = TRUE;
n->constisset = FALSE;
n->constiscast = FALSE;
$$ = (Node *)n;
}
| ALL
{
Const *n = makeNode(Const);
n->consttype = INT4OID;
n->constlen = sizeof(int4);
n->constvalue = (Datum)0;
n->constisnull = FALSE;
n->constbyval = TRUE;
n->constisset = FALSE;
n->constiscast = FALSE;
$$ = (Node *)n;
}
| PARAM
{
Param *n = makeNode(Param);
n->paramkind = PARAM_NUM;
n->paramid = $1;
n->paramtype = INT4OID;
$$ = (Node *)n;
}
;
select_offset_value: Iconst
{
Const *n = makeNode(Const);
n->consttype = INT4OID;
n->constlen = sizeof(int4);
n->constvalue = (Datum)$1;
n->constisnull = FALSE;
n->constbyval = TRUE;
n->constisset = FALSE;
n->constiscast = FALSE;
$$ = (Node *)n;
}
| PARAM
{
Param *n = makeNode(Param);
n->paramkind = PARAM_NUM;
n->paramid = $1;
n->paramtype = INT4OID;
$$ = (Node *)n;
}
;
/*
1997-09-08 05:20:18 +02:00
* jimmy bell-style recursive queries aren't supported in the
* current system.
*
1997-09-08 05:20:18 +02:00
* ...however, recursive addattr and rename supported. make special
* cases for these.
*/
opt_inh_star: '*' { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
1997-09-08 05:20:18 +02:00
;
relation_name_list: name_list;
name_list: name
{ $$ = lcons(makeString($1),NIL); }
1997-09-08 05:20:18 +02:00
| name_list ',' name
{ $$ = lappend($1,makeString($3)); }
1997-09-08 05:20:18 +02:00
;
group_clause: GROUP BY expr_list { $$ = $3; }
1997-09-08 05:20:18 +02:00
| /*EMPTY*/ { $$ = NIL; }
;
1998-01-20 06:05:08 +01:00
having_clause: HAVING a_expr
{
$$ = $2;
1998-01-20 06:05:08 +01:00
}
1997-09-08 05:20:18 +02:00
| /*EMPTY*/ { $$ = NULL; }
;
1999-01-05 16:46:25 +01:00
for_update_clause:
FOR UPDATE
{
$$ = lcons(NULL, NULL);
}
| FOR UPDATE OF va_list
{
$$ = $4;
}
| /* EMPTY */
{
$$ = NULL;
}
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* clauses common to all Optimizable Stmts:
* from_clause -
* where_clause -
*
*****************************************************************************/
from_clause: FROM '(' relation_expr join_expr JOIN relation_expr join_spec ')'
1997-09-08 05:20:18 +02:00
{
$$ = NIL;
1998-01-09 21:06:08 +01:00
elog(ERROR,"JOIN not yet implemented");
1997-09-08 05:20:18 +02:00
}
| FROM from_list { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; }
;
from_list: from_list ',' from_val
{ $$ = lappend($1, $3); }
| from_val CROSS JOIN from_val
1998-01-09 21:06:08 +01:00
{ elog(ERROR,"CROSS JOIN not yet implemented"); }
| from_val
1997-09-08 05:20:18 +02:00
{ $$ = lcons($1, NIL); }
;
from_val: relation_expr AS ColLabel
1997-09-08 05:20:18 +02:00
{
$$ = makeNode(RangeVar);
$$->relExpr = $1;
$$->name = $3;
}
| relation_expr ColId
1997-09-08 05:20:18 +02:00
{
$$ = makeNode(RangeVar);
$$->relExpr = $1;
$$->name = $2;
}
| relation_expr
{
$$ = makeNode(RangeVar);
$$->relExpr = $1;
$$->name = NULL;
}
;
join_expr: NATURAL join_expr { $$ = NULL; }
| FULL join_outer
1998-01-09 21:06:08 +01:00
{ elog(ERROR,"FULL OUTER JOIN not yet implemented"); }
1997-09-08 05:20:18 +02:00
| LEFT join_outer
1998-01-09 21:06:08 +01:00
{ elog(ERROR,"LEFT OUTER JOIN not yet implemented"); }
1997-09-08 05:20:18 +02:00
| RIGHT join_outer
1998-01-09 21:06:08 +01:00
{ elog(ERROR,"RIGHT OUTER JOIN not yet implemented"); }
| OUTER_P
1998-01-09 21:06:08 +01:00
{ elog(ERROR,"OUTER JOIN not yet implemented"); }
| INNER_P
1998-01-09 21:06:08 +01:00
{ elog(ERROR,"INNER JOIN not yet implemented"); }
1997-09-08 05:20:18 +02:00
| UNION
1998-01-09 21:06:08 +01:00
{ elog(ERROR,"UNION JOIN not yet implemented"); }
| /*EMPTY*/
1998-01-09 21:06:08 +01:00
{ elog(ERROR,"INNER JOIN not yet implemented"); }
1997-09-08 05:20:18 +02:00
;
join_outer: OUTER_P { $$ = NULL; }
| /*EMPTY*/ { $$ = NULL; /* no qualifiers */ }
1997-09-08 05:20:18 +02:00
;
join_spec: ON '(' a_expr ')' { $$ = NULL; }
| USING '(' join_list ')' { $$ = NULL; }
| /*EMPTY*/ { $$ = NULL; /* no qualifiers */ }
1997-09-08 05:20:18 +02:00
;
join_list: join_using { $$ = lcons($1, NIL); }
1997-09-08 05:20:18 +02:00
| join_list ',' join_using { $$ = lappend($1, $3); }
;
join_using: ColId
/* Changed from SortGroupBy parse node to new JoinUsing node.
* SortGroupBy no longer needs these structure members.
*
* Once, acknowledged, this comment can be removed by the
* developer(s) working on the JOIN clause.
*
* - daveh@insightdist.com 1998-07-31
*/
{
$$ = makeNode(JoinUsing);
1997-09-08 05:20:18 +02:00
$$->resno = 0;
$$->range = NULL;
$$->name = $1;
}
| ColId '.' ColId
1997-09-08 05:20:18 +02:00
{
$$ = makeNode(JoinUsing);
1997-09-08 05:20:18 +02:00
$$->resno = 0;
$$->range = $1;
$$->name = $3;
}
| Iconst
{
$$ = makeNode(JoinUsing);
1997-09-08 05:20:18 +02:00
$$->resno = $1;
$$->range = NULL;
$$->name = NULL;
}
;
where_clause: WHERE a_expr { $$ = $2; }
| /*EMPTY*/ { $$ = NULL; /* no qualifiers */ }
1997-09-08 05:20:18 +02:00
;
relation_expr: relation_name
{
/* normal relations */
$$ = makeNode(RelExpr);
$$->relname = $1;
$$->inh = FALSE;
}
| relation_name '*' %prec '='
{
/* inheritance query */
1997-09-08 05:20:18 +02:00
$$ = makeNode(RelExpr);
$$->relname = $1;
$$->inh = TRUE;
}
opt_array_bounds: '[' ']' nest_array_bounds
1997-09-08 05:20:18 +02:00
{ $$ = lcons(makeInteger(-1), $3); }
| '[' Iconst ']' nest_array_bounds
{ $$ = lcons(makeInteger($2), $4); }
1998-12-04 16:34:49 +01:00
| /*EMPTY*/
1997-09-08 05:20:18 +02:00
{ $$ = NIL; }
;
nest_array_bounds: '[' ']' nest_array_bounds
{ $$ = lcons(makeInteger(-1), $3); }
| '[' Iconst ']' nest_array_bounds
{ $$ = lcons(makeInteger($2), $4); }
| /*EMPTY*/
{ $$ = NIL; }
;
/*****************************************************************************
*
* Type syntax
* SQL92 introduces a large amount of type-specific syntax.
* Define individual clauses to handle these cases, and use
* the generic case to handle regular type-extensible Postgres syntax.
* - thomas 1997-10-10
*
*****************************************************************************/
Typename: Array opt_array_bounds
{
$$ = $1;
$$->arrayBounds = $2;
1997-09-08 05:20:18 +02:00
/* Is this the name of a complex type? If so, implement
* it as a set.
*/
if (!strcmp(saved_relname, $$->name))
1997-09-08 05:20:18 +02:00
/* This attr is the same type as the relation
* being defined. The classic example: create
* emp(name=text,mgr=emp)
*/
$$->setof = TRUE;
else if (typeTypeRelid(typenameType($$->name)) != InvalidOid)
1997-09-08 05:20:18 +02:00
/* (Eventually add in here that the set can only
* contain one element.)
*/
$$->setof = TRUE;
else
$$->setof = FALSE;
}
| Character
| SETOF Array
1997-09-08 05:20:18 +02:00
{
$$ = $2;
1997-09-08 05:20:18 +02:00
$$->setof = TRUE;
}
;
Array: Generic
| Datetime
| Numeric
1997-09-08 05:20:18 +02:00
;
Generic: generic
1997-09-08 05:20:18 +02:00
{
$$ = makeNode(TypeName);
$$->name = xlateSqlType($1);
$$->typmod = -1;
}
;
generic: IDENT { $$ = $1; }
| TYPE_P { $$ = xlateSqlType("type"); }
;
/* SQL92 numeric data types
* Check FLOAT() precision limits assuming IEEE floating types.
* Provide real DECIMAL() and NUMERIC() implementations now - Jan 1998-12-30
* - thomas 1997-09-18
*/
Numeric: FLOAT opt_float
{
$$ = makeNode(TypeName);
$$->name = xlateSqlType($2);
$$->typmod = -1;
1997-09-08 05:20:18 +02:00
}
| DOUBLE PRECISION
{
$$ = makeNode(TypeName);
$$->name = xlateSqlType("float");
}
| DECIMAL opt_decimal
1997-09-08 05:20:18 +02:00
{
$$ = makeNode(TypeName);
$$->name = xlateSqlType("numeric");
$$->typmod = -1;
}
| NUMERIC opt_numeric
{
$$ = makeNode(TypeName);
$$->name = xlateSqlType("numeric");
$$->typmod = $2;
}
;
1997-09-08 05:20:18 +02:00
numeric: FLOAT
{ $$ = xlateSqlType("float8"); }
| DOUBLE PRECISION
{ $$ = xlateSqlType("float8"); }
| DECIMAL
{ $$ = xlateSqlType("numeric"); }
| NUMERIC
{ $$ = xlateSqlType("numeric"); }
;
opt_float: '(' Iconst ')'
{
if ($2 < 1)
1998-01-09 21:06:08 +01:00
elog(ERROR,"precision for FLOAT must be at least 1");
else if ($2 < 7)
$$ = xlateSqlType("float4");
else if ($2 < 16)
$$ = xlateSqlType("float8");
else
1998-01-09 21:06:08 +01:00
elog(ERROR,"precision for FLOAT must be less than 16");
}
| /*EMPTY*/
{
$$ = xlateSqlType("float8");
}
;
opt_numeric: '(' Iconst ',' Iconst ')'
{
if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION)
elog(ERROR,"NUMERIC precision %d must be beween 1 and %d",
$2, NUMERIC_MAX_PRECISION);
if ($4 < 0 || $4 > $2)
elog(ERROR,"NUMERIC scale %d must be between 0 and precision %d",
$4,$2);
$$ = (($2 << 16) | $4) + VARHDRSZ;
}
| '(' Iconst ')'
{
if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION)
elog(ERROR,"NUMERIC precision %d must be beween 1 and %d",
$2, NUMERIC_MAX_PRECISION);
$$ = ($2 << 16) + VARHDRSZ;
}
| /*EMPTY*/
{
$$ = ((NUMERIC_DEFAULT_PRECISION << 16) | NUMERIC_DEFAULT_SCALE) + VARHDRSZ;
}
;
opt_decimal: '(' Iconst ',' Iconst ')'
{
if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION)
elog(ERROR,"DECIMAL precision %d must be beween 1 and %d",
$2, NUMERIC_MAX_PRECISION);
if ($4 < 0 || $4 > $2)
elog(ERROR,"DECIMAL scale %d must be between 0 and precision %d",
$4,$2);
$$ = (($2 << 16) | $4) + VARHDRSZ;
}
| '(' Iconst ')'
{
if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION)
elog(ERROR,"DECIMAL precision %d must be beween 1 and %d",
$2, NUMERIC_MAX_PRECISION);
$$ = ($2 << 16) + VARHDRSZ;
}
| /*EMPTY*/
{
$$ = ((NUMERIC_DEFAULT_PRECISION << 16) | NUMERIC_DEFAULT_SCALE) + VARHDRSZ;
}
;
/* SQL92 character data types
* The following implements CHAR() and VARCHAR().
* We do it here instead of the 'Generic' production
* because we don't want to allow arrays of VARCHAR().
* I haven't thought about whether that will work or not.
* - ay 6/95
*/
Character: character '(' Iconst ')'
{
$$ = makeNode(TypeName);
if (strcasecmp($1, "char") == 0)
$$->name = xlateSqlType("bpchar");
else if (strcasecmp($1, "varchar") == 0)
$$->name = xlateSqlType("varchar");
else
yyerror("internal parsing error; unrecognized character type");
if ($3 < 1)
elog(ERROR,"length for '%s' type must be at least 1",$1);
else if ($3 > 4096)
/* we can store a char() of length up to the size
* of a page (8KB) - page headers and friends but
* just to be safe here... - ay 6/95
* XXX note this hardcoded limit - thomas 1997-07-13
1997-09-08 05:20:18 +02:00
*/
elog(ERROR,"length for type '%s' cannot exceed 4096",$1);
/* we actually implement this sort of like a varlen, so
* the first 4 bytes is the length. (the difference
* between this and "text" is that we blank-pad and
* truncate where necessary
*/
$$->typmod = VARHDRSZ + $3;
1997-09-08 05:20:18 +02:00
}
| character
{
$$ = makeNode(TypeName);
/* Let's try to make all single-character types into bpchar(1)
* - thomas 1998-05-07
*/
if (strcasecmp($1, "char") == 0)
{
$$->name = xlateSqlType("bpchar");
$$->typmod = VARHDRSZ + 1;
}
else
{
$$->name = xlateSqlType($1);
$$->typmod = -1;
}
}
;
character: CHARACTER opt_varying opt_charset opt_collate
{
char *type, *c;
if (($3 == NULL) || (strcasecmp($3, "sql_text") == 0)) {
if ($2) type = xlateSqlType("varchar");
else type = xlateSqlType("char");
} else {
if ($2) {
c = palloc(strlen("var") + strlen($3) + 1);
strcpy(c, "var");
strcat(c, $3);
type = xlateSqlType(c);
} else {
type = xlateSqlType($3);
}
};
if ($4 != NULL)
elog(NOTICE,"COLLATE %s not yet implemented; clause ignored",$4);
$$ = type;
}
| CHAR opt_varying { $$ = xlateSqlType($2? "varchar": "char"); }
| VARCHAR { $$ = xlateSqlType("varchar"); }
| NATIONAL CHARACTER opt_varying { $$ = xlateSqlType($3? "varchar": "char"); }
| NCHAR opt_varying { $$ = xlateSqlType($2? "varchar": "char"); }
;
opt_varying: VARYING { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
;
opt_charset: CHARACTER SET ColId { $$ = $3; }
| /*EMPTY*/ { $$ = NULL; }
;
opt_collate: COLLATE ColId { $$ = $2; }
| /*EMPTY*/ { $$ = NULL; }
;
Datetime: datetime
{
$$ = makeNode(TypeName);
$$->name = xlateSqlType($1);
$$->typmod = -1;
}
| TIMESTAMP opt_timezone
{
$$ = makeNode(TypeName);
$$->name = xlateSqlType("timestamp");
$$->timezone = $2;
$$->typmod = -1;
}
| TIME
{
$$ = makeNode(TypeName);
$$->name = xlateSqlType("time");
$$->typmod = -1;
}
| INTERVAL opt_interval
{
$$ = makeNode(TypeName);
$$->name = xlateSqlType("interval");
$$->typmod = -1;
}
1997-09-08 05:20:18 +02:00
;
datetime: YEAR_P { $$ = "year"; }
| MONTH_P { $$ = "month"; }
| DAY_P { $$ = "day"; }
| HOUR_P { $$ = "hour"; }
| MINUTE_P { $$ = "minute"; }
| SECOND_P { $$ = "second"; }
;
opt_timezone: WITH TIME ZONE { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
;
opt_interval: datetime { $$ = lcons($1, NIL); }
| YEAR_P TO MONTH_P { $$ = NIL; }
| DAY_P TO HOUR_P { $$ = NIL; }
| DAY_P TO MINUTE_P { $$ = NIL; }
| DAY_P TO SECOND_P { $$ = NIL; }
| HOUR_P TO MINUTE_P { $$ = NIL; }
| HOUR_P TO SECOND_P { $$ = NIL; }
| MINUTE_P TO SECOND_P { $$ = NIL; }
| /*EMPTY*/ { $$ = NIL; }
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* expression grammar, still needs some cleanup
*
*****************************************************************************/
a_expr_or_null: a_expr
{ $$ = $1; }
| NULL_P
1997-09-08 05:20:18 +02:00
{
A_Const *n = makeNode(A_Const);
n->val.type = T_Null;
$$ = (Node *)n;
}
;
/* Expressions using row descriptors
* Define row_descriptor to allow yacc to break the reduce/reduce conflict
* with singleton expressions.
* Eliminated lots of code by defining row_op and sub_type clauses.
* However, can not consolidate EXPR_LINK case with others subselects
* due to shift/reduce conflict with the non-subselect clause (the parser
* would have to look ahead more than one token to resolve the conflict).
* - thomas 1998-05-09
*/
row_expr: '(' row_descriptor ')' IN '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = $2;
n->oper = lcons("=",NIL);
n->useor = false;
n->subLinkType = ANY_SUBLINK;
n->subselect = $6;
$$ = (Node *)n;
}
| '(' row_descriptor ')' NOT IN '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = $2;
n->oper = lcons("<>",NIL);
n->useor = true;
n->subLinkType = ALL_SUBLINK;
n->subselect = $7;
$$ = (Node *)n;
}
| '(' row_descriptor ')' row_op sub_type '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = $2;
n->oper = lcons($4, NIL);
if (strcmp($4,"<>") == 0)
n->useor = true;
else
n->useor = false;
n->subLinkType = $5;
n->subselect = $7;
$$ = (Node *)n;
}
| '(' row_descriptor ')' row_op '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = $2;
n->oper = lcons($4, NIL);
1998-02-18 04:26:54 +01:00
if (strcmp($4,"<>") == 0)
n->useor = true;
else
n->useor = false;
n->subLinkType = EXPR_SUBLINK;
n->subselect = $6;
$$ = (Node *)n;
}
| '(' row_descriptor ')' row_op '(' row_descriptor ')'
{
1998-01-17 06:01:34 +01:00
$$ = makeRowExpr($4, $2, $6);
}
;
row_descriptor: row_list ',' a_expr
{
$$ = lappend($1, $3);
}
;
row_list: row_list ',' a_expr
{
$$ = lappend($1, $3);
}
| a_expr
{
$$ = lcons($1, NIL);
}
;
row_op: Op { $$ = $1; }
| '<' { $$ = "<"; }
| '=' { $$ = "="; }
| '>' { $$ = ">"; }
| '+' { $$ = "+"; }
| '-' { $$ = "-"; }
| '*' { $$ = "*"; }
| '/' { $$ = "/"; }
;
sub_type: ANY { $$ = ANY_SUBLINK; }
| ALL { $$ = ALL_SUBLINK; }
;
1998-12-04 16:34:49 +01:00
/* General expressions
* This is the heart of the expression syntax.
* Note that the BETWEEN clause looks similar to a boolean expression
* and so we must define b_expr which is almost the same as a_expr
* but without the boolean expressions.
1998-12-04 16:34:49 +01:00
* All operations/expressions are allowed in a BETWEEN clause
* if surrounded by parens.
*/
a_expr: attr opt_indirection
1997-09-08 05:20:18 +02:00
{
$1->indirection = $2;
$$ = (Node *)$1;
}
| row_expr
{ $$ = $1; }
1997-09-08 05:20:18 +02:00
| AexprConst
{ $$ = $1; }
| ColId
{
/* could be a column name or a relation_name */
Ident *n = makeNode(Ident);
n->name = $1;
n->indirection = NULL;
$$ = (Node *)n;
}
1997-09-08 05:20:18 +02:00
| '-' a_expr %prec UMINUS
{ $$ = makeA_Expr(OP, "-", NULL, $2); }
| a_expr '+' a_expr
{ $$ = makeA_Expr(OP, "+", $1, $3); }
| a_expr '-' a_expr
{ $$ = makeA_Expr(OP, "-", $1, $3); }
| a_expr '/' a_expr
{ $$ = makeA_Expr(OP, "/", $1, $3); }
| a_expr '*' a_expr
{ $$ = makeA_Expr(OP, "*", $1, $3); }
| a_expr '<' a_expr
{ $$ = makeA_Expr(OP, "<", $1, $3); }
| a_expr '>' a_expr
{ $$ = makeA_Expr(OP, ">", $1, $3); }
| a_expr '=' a_expr
{ $$ = makeA_Expr(OP, "=", $1, $3); }
| ':' a_expr
{ $$ = makeA_Expr(OP, ":", NULL, $2); }
| ';' a_expr
{ $$ = makeA_Expr(OP, ";", NULL, $2); }
| '|' a_expr
{ $$ = makeA_Expr(OP, "|", NULL, $2); }
| a_expr TYPECAST Typename
1997-09-08 05:20:18 +02:00
{
$$ = (Node *)$1;
1997-09-08 05:20:18 +02:00
/* AexprConst can be either A_Const or ParamNo */
if (nodeTag($1) == T_A_Const) {
1997-09-08 05:20:18 +02:00
((A_Const *)$1)->typename = $3;
} else if (nodeTag($1) == T_Param) {
1997-09-08 05:20:18 +02:00
((ParamNo *)$1)->typename = $3;
/* otherwise, try to transform to a function call */
} else {
FuncCall *n = makeNode(FuncCall);
n->funcname = $3->name;
n->args = lcons($1,NIL);
$$ = (Node *)n;
}
1997-09-08 05:20:18 +02:00
}
| CAST '(' a_expr AS Typename ')'
1997-09-08 05:20:18 +02:00
{
$$ = (Node *)$3;
1997-09-08 05:20:18 +02:00
/* AexprConst can be either A_Const or ParamNo */
if (nodeTag($3) == T_A_Const) {
((A_Const *)$3)->typename = $5;
} else if (nodeTag($5) == T_Param) {
((ParamNo *)$3)->typename = $5;
/* otherwise, try to transform to a function call */
} else {
FuncCall *n = makeNode(FuncCall);
n->funcname = $5->name;
n->args = lcons($3,NIL);
$$ = (Node *)n;
}
1997-09-08 05:20:18 +02:00
}
| '(' a_expr_or_null ')'
{ $$ = $2; }
| a_expr Op a_expr
1997-10-31 01:50:39 +01:00
{ $$ = makeIndexable($2,$1,$3); }
1997-09-08 05:20:18 +02:00
| a_expr LIKE a_expr
1997-10-31 01:50:39 +01:00
{ $$ = makeIndexable("~~", $1, $3); }
1997-09-08 05:20:18 +02:00
| a_expr NOT LIKE a_expr
{ $$ = makeA_Expr(OP, "!~~", $1, $4); }
| Op a_expr
{ $$ = makeA_Expr(OP, $1, NULL, $2); }
| a_expr Op
{ $$ = makeA_Expr(OP, $2, $1, NULL); }
| func_name '(' '*' ')'
1997-09-08 05:20:18 +02:00
{
/* cheap hack for aggregate (eg. count) */
1997-09-08 05:20:18 +02:00
FuncCall *n = makeNode(FuncCall);
A_Const *star = makeNode(A_Const);
1997-09-08 05:20:18 +02:00
star->val.type = T_String;
star->val.val.str = "";
1997-09-08 05:20:18 +02:00
n->funcname = $1;
n->args = lcons(star, NIL);
$$ = (Node *)n;
}
| func_name '(' ')'
1997-09-08 05:20:18 +02:00
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
n->args = NIL;
$$ = (Node *)n;
}
| func_name '(' expr_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
n->args = $3;
$$ = (Node *)n;
}
| CURRENT_DATE
{
A_Const *n = makeNode(A_Const);
TypeName *t = makeNode(TypeName);
n->val.type = T_String;
n->val.val.str = "now";
n->typename = t;
t->name = xlateSqlType("date");
t->setof = FALSE;
t->typmod = -1;
$$ = (Node *)n;
}
| CURRENT_TIME
{
A_Const *n = makeNode(A_Const);
TypeName *t = makeNode(TypeName);
n->val.type = T_String;
n->val.val.str = "now";
n->typename = t;
t->name = xlateSqlType("time");
t->setof = FALSE;
t->typmod = -1;
$$ = (Node *)n;
}
| CURRENT_TIME '(' Iconst ')'
{
FuncCall *n = makeNode(FuncCall);
A_Const *s = makeNode(A_Const);
TypeName *t = makeNode(TypeName);
n->funcname = xlateSqlType("time");
n->args = lcons(s, NIL);
s->val.type = T_String;
s->val.val.str = "now";
s->typename = t;
t->name = xlateSqlType("time");
t->setof = FALSE;
t->typmod = -1;
if ($3 != 0)
elog(NOTICE,"CURRENT_TIME(%d) precision not implemented; zero used instead",$3);
$$ = (Node *)n;
}
| CURRENT_TIMESTAMP
{
A_Const *n = makeNode(A_Const);
TypeName *t = makeNode(TypeName);
n->val.type = T_String;
n->val.val.str = "now";
n->typename = t;
t->name = xlateSqlType("timestamp");
t->setof = FALSE;
t->typmod = -1;
$$ = (Node *)n;
}
| CURRENT_TIMESTAMP '(' Iconst ')'
{
FuncCall *n = makeNode(FuncCall);
A_Const *s = makeNode(A_Const);
TypeName *t = makeNode(TypeName);
n->funcname = xlateSqlType("timestamp");
n->args = lcons(s, NIL);
s->val.type = T_String;
s->val.val.str = "now";
s->typename = t;
t->name = xlateSqlType("timestamp");
t->setof = FALSE;
t->typmod = -1;
if ($3 != 0)
elog(NOTICE,"CURRENT_TIMESTAMP(%d) precision not implemented; zero used instead",$3);
1997-09-08 05:20:18 +02:00
$$ = (Node *)n;
}
| CURRENT_USER
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "getpgusername";
n->args = NIL;
$$ = (Node *)n;
}
| USER
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "getpgusername";
n->args = NIL;
$$ = (Node *)n;
}
1997-09-08 05:20:18 +02:00
| EXISTS '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = NIL;
n->useor = false;
n->oper = NIL;
n->subLinkType = EXISTS_SUBLINK;
n->subselect = $3;
$$ = (Node *)n;
1997-09-08 05:20:18 +02:00
}
| EXTRACT '(' extract_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "date_part";
n->args = $3;
$$ = (Node *)n;
}
| POSITION '(' position_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "strpos";
n->args = $3;
$$ = (Node *)n;
}
| SUBSTRING '(' substr_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "substr";
n->args = $3;
$$ = (Node *)n;
}
/* various trim expressions are defined in SQL92 - thomas 1997-07-19 */
| TRIM '(' BOTH trim_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "btrim";
n->args = $4;
$$ = (Node *)n;
}
| TRIM '(' LEADING trim_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "ltrim";
n->args = $4;
$$ = (Node *)n;
}
| TRIM '(' TRAILING trim_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "rtrim";
n->args = $4;
$$ = (Node *)n;
}
| TRIM '(' trim_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "btrim";
n->args = $3;
$$ = (Node *)n;
}
| a_expr ISNULL
{ $$ = makeA_Expr(ISNULL, NULL, $1, NULL); }
| a_expr IS NULL_P
1997-09-08 05:20:18 +02:00
{ $$ = makeA_Expr(ISNULL, NULL, $1, NULL); }
| a_expr NOTNULL
{ $$ = makeA_Expr(NOTNULL, NULL, $1, NULL); }
| a_expr IS NOT NULL_P
1997-09-08 05:20:18 +02:00
{ $$ = makeA_Expr(NOTNULL, NULL, $1, NULL); }
/* IS TRUE, IS FALSE, etc used to be function calls
* but let's make them expressions to allow the optimizer
* a chance to eliminate them if a_expr is a constant string.
* - thomas 1997-12-22
*/
| a_expr IS TRUE_P
{
A_Const *n = makeNode(A_Const);
n->val.type = T_String;
n->val.val.str = "t";
n->typename = makeNode(TypeName);
n->typename->name = xlateSqlType("bool");
n->typename->typmod = -1;
$$ = makeA_Expr(OP, "=", $1,(Node *)n);
}
| a_expr IS NOT FALSE_P
{
A_Const *n = makeNode(A_Const);
n->val.type = T_String;
n->val.val.str = "t";
n->typename = makeNode(TypeName);
n->typename->name = xlateSqlType("bool");
n->typename->typmod = -1;
$$ = makeA_Expr(OP, "=", $1,(Node *)n);
}
| a_expr IS FALSE_P
{
A_Const *n = makeNode(A_Const);
n->val.type = T_String;
n->val.val.str = "f";
n->typename = makeNode(TypeName);
n->typename->name = xlateSqlType("bool");
n->typename->typmod = -1;
$$ = makeA_Expr(OP, "=", $1,(Node *)n);
}
| a_expr IS NOT TRUE_P
{
A_Const *n = makeNode(A_Const);
n->val.type = T_String;
n->val.val.str = "f";
n->typename = makeNode(TypeName);
n->typename->name = xlateSqlType("bool");
n->typename->typmod = -1;
$$ = makeA_Expr(OP, "=", $1,(Node *)n);
}
| a_expr BETWEEN b_expr AND b_expr
1997-09-08 05:20:18 +02:00
{
$$ = makeA_Expr(AND, NULL,
makeA_Expr(OP, ">=", $1, $3),
makeA_Expr(OP, "<=", $1, $5));
}
| a_expr NOT BETWEEN b_expr AND b_expr
1997-09-08 05:20:18 +02:00
{
$$ = makeA_Expr(OR, NULL,
makeA_Expr(OP, "<", $1, $4),
makeA_Expr(OP, ">", $1, $6));
}
| a_expr IN { saved_In_Expr = lcons($1,saved_In_Expr); } '(' in_expr ')'
{
saved_In_Expr = lnext(saved_In_Expr);
if (nodeTag($5) == T_SubLink)
{
SubLink *n = (SubLink *)$5;
n->lefthand = lcons($1, NIL);
n->oper = lcons("=",NIL);
n->useor = false;
n->subLinkType = ANY_SUBLINK;
$$ = (Node *)n;
}
else $$ = $5;
}
| a_expr NOT IN { saved_In_Expr = lcons($1,saved_In_Expr); } '(' not_in_expr ')'
{
saved_In_Expr = lnext(saved_In_Expr);
if (nodeTag($6) == T_SubLink)
{
SubLink *n = (SubLink *)$6;
n->lefthand = lcons($1, NIL);
n->oper = lcons("<>",NIL);
n->useor = false;
n->subLinkType = ALL_SUBLINK;
$$ = (Node *)n;
}
else $$ = $6;
}
| a_expr Op '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1, NULL);
n->oper = lcons($2,NIL);
n->useor = false;
n->subLinkType = EXPR_SUBLINK;
n->subselect = $4;
$$ = (Node *)n;
}
| a_expr '+' '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1, NULL);
n->oper = lcons("+",NIL);
n->useor = false;
n->subLinkType = EXPR_SUBLINK;
n->subselect = $4;
$$ = (Node *)n;
}
| a_expr '-' '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1, NULL);
n->oper = lcons("-",NIL);
n->useor = false;
n->subLinkType = EXPR_SUBLINK;
n->subselect = $4;
$$ = (Node *)n;
}
| a_expr '/' '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1, NULL);
n->oper = lcons("/",NIL);
n->useor = false;
n->subLinkType = EXPR_SUBLINK;
n->subselect = $4;
$$ = (Node *)n;
}
| a_expr '*' '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1, NULL);
n->oper = lcons("*",NIL);
n->useor = false;
n->subLinkType = EXPR_SUBLINK;
n->subselect = $4;
$$ = (Node *)n;
}
| a_expr '<' '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1, NULL);
n->oper = lcons("<",NIL);
n->useor = false;
n->subLinkType = EXPR_SUBLINK;
n->subselect = $4;
$$ = (Node *)n;
}
| a_expr '>' '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1, NULL);
n->oper = lcons(">",NIL);
n->useor = false;
n->subLinkType = EXPR_SUBLINK;
n->subselect = $4;
$$ = (Node *)n;
}
| a_expr '=' '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1, NULL);
n->oper = lcons("=",NIL);
n->useor = false;
n->subLinkType = EXPR_SUBLINK;
n->subselect = $4;
$$ = (Node *)n;
}
| a_expr Op ANY '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1,NIL);
n->oper = lcons($2,NIL);
n->useor = false;
n->subLinkType = ANY_SUBLINK;
n->subselect = $5;
$$ = (Node *)n;
}
| a_expr '+' ANY '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1,NIL);
n->oper = lcons("+",NIL);
n->useor = false;
n->subLinkType = ANY_SUBLINK;
n->subselect = $5;
$$ = (Node *)n;
}
| a_expr '-' ANY '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1,NIL);
n->oper = lcons("-",NIL);
n->useor = false;
n->subLinkType = ANY_SUBLINK;
n->subselect = $5;
$$ = (Node *)n;
}
| a_expr '/' ANY '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1,NIL);
n->oper = lcons("/",NIL);
n->useor = false;
n->subLinkType = ANY_SUBLINK;
n->subselect = $5;
$$ = (Node *)n;
}
| a_expr '*' ANY '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1,NIL);
n->oper = lcons("*",NIL);
n->useor = false;
n->subLinkType = ANY_SUBLINK;
n->subselect = $5;
$$ = (Node *)n;
}
| a_expr '<' ANY '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1,NIL);
n->oper = lcons("<",NIL);
n->useor = false;
n->subLinkType = ANY_SUBLINK;
n->subselect = $5;
$$ = (Node *)n;
}
| a_expr '>' ANY '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1,NIL);
n->oper = lcons(">",NIL);
n->useor = false;
n->subLinkType = ANY_SUBLINK;
n->subselect = $5;
$$ = (Node *)n;
}
| a_expr '=' ANY '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1,NIL);
n->oper = lcons("=",NIL);
n->useor = false;
n->subLinkType = ANY_SUBLINK;
n->subselect = $5;
$$ = (Node *)n;
}
| a_expr Op ALL '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1, NULL);
n->oper = lcons($2,NIL);
n->useor = false;
n->subLinkType = ALL_SUBLINK;
n->subselect = $5;
$$ = (Node *)n;
}
| a_expr '+' ALL '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1, NULL);
n->oper = lcons("+",NIL);
n->useor = false;
n->subLinkType = ALL_SUBLINK;
n->subselect = $5;
$$ = (Node *)n;
}
| a_expr '-' ALL '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1, NULL);
n->oper = lcons("-",NIL);
n->useor = false;
n->subLinkType = ALL_SUBLINK;
n->subselect = $5;
$$ = (Node *)n;
}
| a_expr '/' ALL '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1, NULL);
n->oper = lcons("/",NIL);
n->useor = false;
n->subLinkType = ALL_SUBLINK;
n->subselect = $5;
$$ = (Node *)n;
}
| a_expr '*' ALL '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1, NULL);
n->oper = lcons("*",NIL);
n->useor = false;
n->subLinkType = ALL_SUBLINK;
n->subselect = $5;
$$ = (Node *)n;
}
| a_expr '<' ALL '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1, NULL);
n->oper = lcons("<",NIL);
n->useor = false;
n->subLinkType = ALL_SUBLINK;
n->subselect = $5;
$$ = (Node *)n;
}
| a_expr '>' ALL '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1, NULL);
n->oper = lcons(">",NIL);
n->useor = false;
n->subLinkType = ALL_SUBLINK;
n->subselect = $5;
$$ = (Node *)n;
}
| a_expr '=' ALL '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1, NULL);
n->oper = lcons("=",NIL);
n->useor = false;
n->subLinkType = ALL_SUBLINK;
n->subselect = $5;
$$ = (Node *)n;
}
1997-09-08 05:20:18 +02:00
| a_expr AND a_expr
{ $$ = makeA_Expr(AND, NULL, $1, $3); }
| a_expr OR a_expr
{ $$ = makeA_Expr(OR, NULL, $1, $3); }
| NOT a_expr
{ $$ = makeA_Expr(NOT, NULL, NULL, $2); }
1998-12-04 16:34:49 +01:00
| case_expr
{ $$ = $1; }
1997-09-08 05:20:18 +02:00
;
1998-12-04 16:34:49 +01:00
/* Restricted expressions
* b_expr is a subset of the complete expression syntax
* defined by a_expr. b_expr is used in BETWEEN clauses
* to eliminate parser ambiguities stemming from the AND keyword.
*/
b_expr: attr opt_indirection
{
$1->indirection = $2;
$$ = (Node *)$1;
}
| AexprConst
{ $$ = $1; }
| ColId
{
/* could be a column name or a relation_name */
Ident *n = makeNode(Ident);
n->name = $1;
n->indirection = NULL;
$$ = (Node *)n;
}
| '-' b_expr %prec UMINUS
{ $$ = makeA_Expr(OP, "-", NULL, $2); }
| b_expr '+' b_expr
{ $$ = makeA_Expr(OP, "+", $1, $3); }
| b_expr '-' b_expr
{ $$ = makeA_Expr(OP, "-", $1, $3); }
| b_expr '/' b_expr
{ $$ = makeA_Expr(OP, "/", $1, $3); }
| b_expr '*' b_expr
{ $$ = makeA_Expr(OP, "*", $1, $3); }
| ':' b_expr
{ $$ = makeA_Expr(OP, ":", NULL, $2); }
| ';' b_expr
{ $$ = makeA_Expr(OP, ";", NULL, $2); }
| '|' b_expr
{ $$ = makeA_Expr(OP, "|", NULL, $2); }
| b_expr TYPECAST Typename
{
$$ = (Node *)$1;
/* AexprConst can be either A_Const or ParamNo */
if (nodeTag($1) == T_A_Const) {
((A_Const *)$1)->typename = $3;
} else if (nodeTag($1) == T_Param) {
((ParamNo *)$1)->typename = $3;
/* otherwise, try to transform to a function call */
} else {
FuncCall *n = makeNode(FuncCall);
n->funcname = $3->name;
n->args = lcons($1,NIL);
$$ = (Node *)n;
}
}
| CAST '(' b_expr AS Typename ')'
{
$$ = (Node *)$3;
/* AexprConst can be either A_Const or ParamNo */
if (nodeTag($3) == T_A_Const) {
((A_Const *)$3)->typename = $5;
} else if (nodeTag($3) == T_Param) {
((ParamNo *)$3)->typename = $5;
/* otherwise, try to transform to a function call */
} else {
FuncCall *n = makeNode(FuncCall);
n->funcname = $5->name;
n->args = lcons($3,NIL);
$$ = (Node *)n;
}
}
| '(' a_expr ')'
{ $$ = $2; }
| b_expr Op b_expr
{ $$ = makeIndexable($2,$1,$3); }
| Op b_expr
{ $$ = makeA_Expr(OP, $1, NULL, $2); }
| b_expr Op
{ $$ = makeA_Expr(OP, $2, $1, NULL); }
| func_name '(' ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
n->args = NIL;
$$ = (Node *)n;
}
| func_name '(' expr_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
n->args = $3;
$$ = (Node *)n;
}
| CURRENT_DATE
{
A_Const *n = makeNode(A_Const);
TypeName *t = makeNode(TypeName);
n->val.type = T_String;
n->val.val.str = "now";
n->typename = t;
t->name = xlateSqlType("date");
t->setof = FALSE;
t->typmod = -1;
$$ = (Node *)n;
}
| CURRENT_TIME
{
A_Const *n = makeNode(A_Const);
TypeName *t = makeNode(TypeName);
n->val.type = T_String;
n->val.val.str = "now";
n->typename = t;
t->name = xlateSqlType("time");
t->setof = FALSE;
t->typmod = -1;
$$ = (Node *)n;
}
| CURRENT_TIME '(' Iconst ')'
{
FuncCall *n = makeNode(FuncCall);
A_Const *s = makeNode(A_Const);
TypeName *t = makeNode(TypeName);
n->funcname = xlateSqlType("time");
n->args = lcons(s, NIL);
s->val.type = T_String;
s->val.val.str = "now";
s->typename = t;
t->name = xlateSqlType("time");
t->setof = FALSE;
t->typmod = -1;
if ($3 != 0)
elog(NOTICE,"CURRENT_TIME(%d) precision not implemented; zero used instead",$3);
$$ = (Node *)n;
}
| CURRENT_TIMESTAMP
{
A_Const *n = makeNode(A_Const);
TypeName *t = makeNode(TypeName);
n->val.type = T_String;
n->val.val.str = "now";
n->typename = t;
t->name = xlateSqlType("timestamp");
t->setof = FALSE;
t->typmod = -1;
$$ = (Node *)n;
}
| CURRENT_TIMESTAMP '(' Iconst ')'
{
FuncCall *n = makeNode(FuncCall);
A_Const *s = makeNode(A_Const);
TypeName *t = makeNode(TypeName);
n->funcname = xlateSqlType("timestamp");
n->args = lcons(s, NIL);
s->val.type = T_String;
s->val.val.str = "now";
s->typename = t;
t->name = xlateSqlType("timestamp");
t->setof = FALSE;
t->typmod = -1;
if ($3 != 0)
elog(NOTICE,"CURRENT_TIMESTAMP(%d) precision not implemented; zero used instead",$3);
$$ = (Node *)n;
}
| CURRENT_USER
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "getpgusername";
n->args = NIL;
$$ = (Node *)n;
}
| USER
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "getpgusername";
n->args = NIL;
$$ = (Node *)n;
}
| POSITION '(' position_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "strpos";
n->args = $3;
$$ = (Node *)n;
}
| SUBSTRING '(' substr_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "substr";
n->args = $3;
$$ = (Node *)n;
}
/* various trim expressions are defined in SQL92 - thomas 1997-07-19 */
| TRIM '(' BOTH trim_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "btrim";
n->args = $4;
$$ = (Node *)n;
}
| TRIM '(' LEADING trim_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "ltrim";
n->args = $4;
$$ = (Node *)n;
}
| TRIM '(' TRAILING trim_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "rtrim";
n->args = $4;
$$ = (Node *)n;
}
| TRIM '(' trim_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "btrim";
n->args = $3;
$$ = (Node *)n;
}
;
opt_indirection: '[' a_expr ']' opt_indirection
1997-09-08 05:20:18 +02:00
{
A_Indices *ai = makeNode(A_Indices);
ai->lidx = NULL;
ai->uidx = $2;
$$ = lcons(ai, $4);
}
| '[' a_expr ':' a_expr ']' opt_indirection
{
A_Indices *ai = makeNode(A_Indices);
ai->lidx = $2;
ai->uidx = $4;
$$ = lcons(ai, $6);
}
1998-12-04 16:34:49 +01:00
| /*EMPTY*/
1997-09-08 05:20:18 +02:00
{ $$ = NIL; }
;
expr_list: a_expr_or_null
1997-09-08 05:20:18 +02:00
{ $$ = lcons($1, NIL); }
| expr_list ',' a_expr_or_null
1997-09-08 05:20:18 +02:00
{ $$ = lappend($1, $3); }
| expr_list USING a_expr
1997-09-08 05:20:18 +02:00
{ $$ = lappend($1, $3); }
;
extract_list: extract_arg FROM a_expr
1997-09-08 05:20:18 +02:00
{
A_Const *n = makeNode(A_Const);
n->val.type = T_String;
n->val.val.str = $1;
$$ = lappend(lcons((Node *)n,NIL), $3);
}
1998-12-04 16:34:49 +01:00
| /*EMPTY*/
1997-09-08 05:20:18 +02:00
{ $$ = NIL; }
;
extract_arg: datetime { $$ = $1; }
| TIMEZONE_HOUR { $$ = "tz_hour"; }
| TIMEZONE_MINUTE { $$ = "tz_minute"; }
;
position_list: position_expr IN position_expr
{ $$ = makeList($3, $1, -1); }
1998-12-04 16:34:49 +01:00
| /*EMPTY*/
1997-09-08 05:20:18 +02:00
{ $$ = NIL; }
;
position_expr: attr opt_indirection
{
$1->indirection = $2;
$$ = (Node *)$1;
}
| AexprConst
{ $$ = $1; }
| '-' position_expr %prec UMINUS
{ $$ = makeA_Expr(OP, "-", NULL, $2); }
| position_expr '+' position_expr
{ $$ = makeA_Expr(OP, "+", $1, $3); }
| position_expr '-' position_expr
{ $$ = makeA_Expr(OP, "-", $1, $3); }
| position_expr '/' position_expr
{ $$ = makeA_Expr(OP, "/", $1, $3); }
| position_expr '*' position_expr
{ $$ = makeA_Expr(OP, "*", $1, $3); }
| '|' position_expr
{ $$ = makeA_Expr(OP, "|", NULL, $2); }
| position_expr TYPECAST Typename
{
$$ = (Node *)$1;
/* AexprConst can be either A_Const or ParamNo */
if (nodeTag($1) == T_A_Const) {
((A_Const *)$1)->typename = $3;
} else if (nodeTag($1) == T_Param) {
((ParamNo *)$1)->typename = $3;
/* otherwise, try to transform to a function call */
} else {
FuncCall *n = makeNode(FuncCall);
n->funcname = $3->name;
n->args = lcons($1,NIL);
$$ = (Node *)n;
}
}
| CAST '(' position_expr AS Typename ')'
{
$$ = (Node *)$3;
/* AexprConst can be either A_Const or ParamNo */
if (nodeTag($3) == T_A_Const) {
((A_Const *)$3)->typename = $5;
} else if (nodeTag($3) == T_Param) {
((ParamNo *)$3)->typename = $5;
/* otherwise, try to transform to a function call */
} else {
FuncCall *n = makeNode(FuncCall);
n->funcname = $5->name;
n->args = lcons($3,NIL);
$$ = (Node *)n;
}
}
| '(' position_expr ')'
{ $$ = $2; }
| position_expr Op position_expr
{ $$ = makeA_Expr(OP, $2, $1, $3); }
| Op position_expr
{ $$ = makeA_Expr(OP, $1, NULL, $2); }
| position_expr Op
{ $$ = makeA_Expr(OP, $2, $1, NULL); }
| ColId
{
/* could be a column name or a relation_name */
Ident *n = makeNode(Ident);
n->name = $1;
n->indirection = NULL;
$$ = (Node *)n;
}
| func_name '(' ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
n->args = NIL;
$$ = (Node *)n;
}
| func_name '(' expr_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
n->args = $3;
$$ = (Node *)n;
}
| POSITION '(' position_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "strpos";
n->args = $3;
$$ = (Node *)n;
}
| SUBSTRING '(' substr_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "substr";
n->args = $3;
$$ = (Node *)n;
}
/* various trim expressions are defined in SQL92 - thomas 1997-07-19 */
| TRIM '(' BOTH trim_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "btrim";
n->args = $4;
$$ = (Node *)n;
}
| TRIM '(' LEADING trim_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "ltrim";
n->args = $4;
$$ = (Node *)n;
}
| TRIM '(' TRAILING trim_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "rtrim";
n->args = $4;
$$ = (Node *)n;
}
| TRIM '(' trim_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "btrim";
n->args = $3;
$$ = (Node *)n;
}
;
substr_list: expr_list substr_from substr_for
1997-09-08 05:20:18 +02:00
{
$$ = nconc(nconc($1,$2),$3);
1997-09-08 05:20:18 +02:00
}
1998-12-04 16:34:49 +01:00
| /*EMPTY*/
1997-09-08 05:20:18 +02:00
{ $$ = NIL; }
;
substr_from: FROM expr_list
1997-09-08 05:20:18 +02:00
{ $$ = $2; }
1998-12-04 16:34:49 +01:00
| /*EMPTY*/
{
A_Const *n = makeNode(A_Const);
n->val.type = T_Integer;
n->val.val.ival = 1;
$$ = lcons((Node *)n,NIL);
}
1997-09-08 05:20:18 +02:00
;
substr_for: FOR expr_list
1997-09-08 05:20:18 +02:00
{ $$ = $2; }
1998-12-04 16:34:49 +01:00
| /*EMPTY*/
1997-09-08 05:20:18 +02:00
{ $$ = NIL; }
;
trim_list: a_expr FROM expr_list
1997-09-08 05:20:18 +02:00
{ $$ = lappend($3, $1); }
| FROM expr_list
1997-09-08 05:20:18 +02:00
{ $$ = $2; }
| expr_list
1997-09-08 05:20:18 +02:00
{ $$ = $1; }
;
in_expr: SubSelect
{
SubLink *n = makeNode(SubLink);
n->subselect = $1;
$$ = (Node *)n;
}
| in_expr_nodes
{ $$ = $1; }
;
in_expr_nodes: AexprConst
{ $$ = makeA_Expr(OP, "=", lfirst(saved_In_Expr), $1); }
| in_expr_nodes ',' AexprConst
1997-09-08 05:20:18 +02:00
{ $$ = makeA_Expr(OR, NULL, $1,
makeA_Expr(OP, "=", lfirst(saved_In_Expr), $3));
1997-09-08 05:20:18 +02:00
}
;
not_in_expr: SubSelect
{
SubLink *n = makeNode(SubLink);
n->subselect = $1;
$$ = (Node *)n;
}
| not_in_expr_nodes
{ $$ = $1; }
;
not_in_expr_nodes: AexprConst
{ $$ = makeA_Expr(OP, "<>", lfirst(saved_In_Expr), $1); }
| not_in_expr_nodes ',' AexprConst
1997-09-08 05:20:18 +02:00
{ $$ = makeA_Expr(AND, NULL, $1,
makeA_Expr(OP, "<>", lfirst(saved_In_Expr), $3));
1997-09-08 05:20:18 +02:00
}
;
1998-12-04 16:34:49 +01:00
/* Case clause
* Define SQL92-style case clause.
* Allow all four forms described in the standard:
* - Full specification
* CASE WHEN a = b THEN c ... ELSE d END
* - Implicit argument
* CASE a WHEN b THEN c ... ELSE d END
* - Conditional NULL
* NULLIF(x,y)
* same as CASE WHEN x = y THEN NULL ELSE x END
* - Conditional substitution from list, use first non-null argument
* COALESCE(a,b,...)
* same as CASE WHEN a IS NOT NULL THEN a WHEN b IS NOT NULL THEN b ... END
* - thomas 1998-11-09
*/
case_expr: CASE case_arg when_clause_list case_default END_TRANS
{
CaseExpr *c = makeNode(CaseExpr);
c->arg = $2;
c->args = $3;
c->defresult = $4;
$$ = (Node *)c;
}
| NULLIF '(' a_expr ',' a_expr ')'
{
CaseExpr *c = makeNode(CaseExpr);
CaseWhen *w = makeNode(CaseWhen);
c->args = lcons(w, NIL);
c->defresult = $3;
w->expr = makeA_Expr(OP, "=", $3, $5);
$$ = (Node *)c;
elog(NOTICE,"NULLIF() not yet fully implemented");
}
| COALESCE '(' expr_list ')'
{
CaseExpr *c = makeNode(CaseExpr);
CaseWhen *w;
List *l;
foreach (l,$3)
{
w = makeNode(CaseWhen);
w->expr = makeA_Expr(NOTNULL, NULL, lfirst(l), NULL);
w->result = lfirst(l);
c->args = lappend(c->args, w);
}
$$ = (Node *)c;
elog(NOTICE,"COALESCE() not yet fully implemented");
}
;
when_clause_list: when_clause_list when_clause
{ $$ = lappend($1, $2); }
| when_clause
{ $$ = lcons($1, NIL); }
;
when_clause: WHEN a_expr THEN a_expr_or_null
{
CaseWhen *w = makeNode(CaseWhen);
w->expr = $2;
w->result = $4;
$$ = (Node *)w;
}
;
case_default: ELSE a_expr_or_null { $$ = $2; }
| /*EMPTY*/ { $$ = NULL; }
;
case_arg: attr opt_indirection
{
$1->indirection = $2;
$$ = (Node *)$1;
}
| ColId
{
/* could be a column name or a relation_name */
Ident *n = makeNode(Ident);
n->name = $1;
n->indirection = NULL;
$$ = (Node *)n;
}
| /*EMPTY*/
{ $$ = NULL; }
;
attr: relation_name '.' attrs
1997-09-08 05:20:18 +02:00
{
$$ = makeNode(Attr);
$$->relname = $1;
$$->paramNo = NULL;
$$->attrs = $3;
$$->indirection = NULL;
}
| ParamNo '.' attrs
{
$$ = makeNode(Attr);
$$->relname = NULL;
$$->paramNo = $1;
$$->attrs = $3;
$$->indirection = NULL;
}
;
attrs: attr_name
{ $$ = lcons(makeString($1), NIL); }
| attrs '.' attr_name
{ $$ = lappend($1, makeString($3)); }
| attrs '.' '*'
{ $$ = lappend($1, makeString("*")); }
;
/*****************************************************************************
*
1997-09-08 05:20:18 +02:00
* target lists
*
*****************************************************************************/
res_target_list: res_target_list ',' res_target_el
1997-09-08 05:20:18 +02:00
{ $$ = lappend($1,$3); }
| res_target_el
{ $$ = lcons($1, NIL); }
| '*'
{
ResTarget *rt = makeNode(ResTarget);
Attr *att = makeNode(Attr);
att->relname = "*";
att->paramNo = NULL;
att->attrs = NULL;
att->indirection = NIL;
rt->name = NULL;
rt->indirection = NULL;
rt->val = (Node *)att;
$$ = lcons(rt, NIL);
}
;
res_target_el: ColId opt_indirection '=' a_expr_or_null
1997-09-08 05:20:18 +02:00
{
$$ = makeNode(ResTarget);
$$->name = $1;
$$->indirection = $2;
$$->val = (Node *)$4;
}
| attr opt_indirection
{
$$ = makeNode(ResTarget);
$$->name = NULL;
$$->indirection = $2;
$$->val = (Node *)$1;
}
| relation_name '.' '*'
{
Attr *att = makeNode(Attr);
att->relname = $1;
att->paramNo = NULL;
att->attrs = lcons(makeString("*"), NIL);
att->indirection = NIL;
$$ = makeNode(ResTarget);
$$->name = NULL;
$$->indirection = NULL;
$$->val = (Node *)att;
}
;
/*
** target list for select.
1998-01-20 06:05:08 +01:00
** should get rid of the other but is still needed by the defunct select into
** and update (uses a subset)
*/
res_target_list2: res_target_list2 ',' res_target_el2
1997-09-08 05:20:18 +02:00
{ $$ = lappend($1, $3); }
| res_target_el2
{ $$ = lcons($1, NIL); }
;
/* AS is not optional because shift/red conflict with unary ops */
res_target_el2: a_expr_or_null AS ColLabel
1997-09-08 05:20:18 +02:00
{
$$ = makeNode(ResTarget);
$$->name = $3;
$$->indirection = NULL;
$$->val = (Node *)$1;
}
| a_expr_or_null
{
$$ = makeNode(ResTarget);
$$->name = NULL;
$$->indirection = NULL;
$$->val = (Node *)$1;
}
| relation_name '.' '*'
{
Attr *att = makeNode(Attr);
att->relname = $1;
att->paramNo = NULL;
att->attrs = lcons(makeString("*"), NIL);
att->indirection = NIL;
$$ = makeNode(ResTarget);
$$->name = NULL;
$$->indirection = NULL;
$$->val = (Node *)att;
}
| '*'
{
Attr *att = makeNode(Attr);
att->relname = "*";
att->paramNo = NULL;
att->attrs = NULL;
att->indirection = NIL;
$$ = makeNode(ResTarget);
$$->name = NULL;
$$->indirection = NULL;
$$->val = (Node *)att;
}
;
opt_id: ColId { $$ = $1; }
1998-12-04 16:34:49 +01:00
| /*EMPTY*/ { $$ = NULL; }
1997-09-08 05:20:18 +02:00
;
relation_name: SpecialRuleRelation
{
$$ = $1;
StrNCpy(saved_relname, $1, NAMEDATALEN);
1997-09-08 05:20:18 +02:00
}
| ColId
{
/* disallow refs to variable system tables */
1997-09-08 05:20:18 +02:00
if (strcmp(LogRelationName, $1) == 0
|| strcmp(VariableRelationName, $1) == 0)
elog(ERROR,"%s cannot be accessed by users",$1);
1997-09-08 05:20:18 +02:00
else
$$ = $1;
StrNCpy(saved_relname, $1, NAMEDATALEN);
1997-09-08 05:20:18 +02:00
}
;
database_name: ColId { $$ = $1; };
access_method: IDENT { $$ = $1; };
1997-09-08 05:20:18 +02:00
attr_name: ColId { $$ = $1; };
class: IDENT { $$ = $1; };
index_name: ColId { $$ = $1; };
/* Functions
* Include date/time keywords as SQL92 extension.
* Include TYPE as a SQL92 unreserved keyword. - thomas 1997-10-05
*/
name: ColId { $$ = $1; };
func_name: ColId { $$ = xlateSqlFunc($1); };
1997-09-08 05:20:18 +02:00
file_name: Sconst { $$ = $1; };
recipe_name: IDENT { $$ = $1; };
/* Constants
* Include TRUE/FALSE for SQL3 support. - thomas 1997-10-24
*/
AexprConst: Iconst
1997-09-08 05:20:18 +02:00
{
A_Const *n = makeNode(A_Const);
n->val.type = T_Integer;
n->val.val.ival = $1;
$$ = (Node *)n;
}
| FCONST
{
A_Const *n = makeNode(A_Const);
n->val.type = T_Float;
n->val.val.dval = $1;
$$ = (Node *)n;
}
| Sconst
{
A_Const *n = makeNode(A_Const);
n->val.type = T_String;
n->val.val.str = $1;
$$ = (Node *)n;
}
| Typename Sconst
{
A_Const *n = makeNode(A_Const);
n->typename = $1;
n->val.type = T_String;
n->val.val.str = $2;
$$ = (Node *)n;
}
1997-09-08 05:20:18 +02:00
| ParamNo
{ $$ = (Node *)$1; }
| TRUE_P
{
A_Const *n = makeNode(A_Const);
n->val.type = T_String;
n->val.val.str = "t";
n->typename = makeNode(TypeName);
n->typename->name = xlateSqlType("bool");
n->typename->typmod = -1;
$$ = (Node *)n;
}
| FALSE_P
{
A_Const *n = makeNode(A_Const);
n->val.type = T_String;
n->val.val.str = "f";
n->typename = makeNode(TypeName);
n->typename->name = xlateSqlType("bool");
n->typename->typmod = -1;
$$ = (Node *)n;
}
1997-09-08 05:20:18 +02:00
;
ParamNo: PARAM opt_indirection
1997-09-08 05:20:18 +02:00
{
$$ = makeNode(ParamNo);
$$->number = $1;
$$->indirection = $2;
1997-09-08 05:20:18 +02:00
}
;
1997-09-08 05:20:18 +02:00
Iconst: ICONST { $$ = $1; };
Sconst: SCONST { $$ = $1; };
UserId: IDENT { $$ = $1; };
/* Column and type identifier
* Does not include explicit datetime types
* since these must be decoupled in Typename syntax.
* Use ColId for most identifiers. - thomas 1997-10-21
*/
TypeId: ColId
{ $$ = xlateSqlType($1); }
| numeric
{ $$ = xlateSqlType($1); }
| character
{ $$ = xlateSqlType($1); }
;
/* Column identifier
* Include date/time keywords as SQL92 extension.
* Include TYPE as a SQL92 unreserved keyword. - thomas 1997-10-05
* Add other keywords. Note that as the syntax expands,
* some of these keywords will have to be removed from this
* list due to shift/reduce conflicts in yacc. If so, move
* down to the ColLabel entity. - thomas 1997-11-06
*/
ColId: IDENT { $$ = $1; }
| datetime { $$ = $1; }
| ABSOLUTE { $$ = "absolute"; }
| ACTION { $$ = "action"; }
| AFTER { $$ = "after"; }
| AGGREGATE { $$ = "aggregate"; }
| BACKWARD { $$ = "backward"; }
| BEFORE { $$ = "before"; }
| CACHE { $$ = "cache"; }
| CREATEDB { $$ = "createdb"; }
| CREATEUSER { $$ = "createuser"; }
| CYCLE { $$ = "cycle"; }
| DATABASE { $$ = "database"; }
| DELIMITERS { $$ = "delimiters"; }
| DOUBLE { $$ = "double"; }
| EACH { $$ = "each"; }
| ENCODING { $$ = "encoding"; }
| FORWARD { $$ = "forward"; }
| FUNCTION { $$ = "function"; }
| HANDLER { $$ = "handler"; }
| INCREMENT { $$ = "increment"; }
| INDEX { $$ = "index"; }
| INHERITS { $$ = "inherits"; }
| INSENSITIVE { $$ = "insensitive"; }
| INSTEAD { $$ = "instead"; }
| ISNULL { $$ = "isnull"; }
| KEY { $$ = "key"; }
| LANGUAGE { $$ = "language"; }
| LANCOMPILER { $$ = "lancompiler"; }
| LOCATION { $$ = "location"; }
| MATCH { $$ = "match"; }
| MAXVALUE { $$ = "maxvalue"; }
| MINVALUE { $$ = "minvalue"; }
| NEXT { $$ = "next"; }
| NOCREATEDB { $$ = "nocreatedb"; }
| NOCREATEUSER { $$ = "nocreateuser"; }
| NOTHING { $$ = "nothing"; }
| NOTNULL { $$ = "notnull"; }
| OF { $$ = "of"; }
| OIDS { $$ = "oids"; }
| ONLY { $$ = "only"; }
| OPERATOR { $$ = "operator"; }
| OPTION { $$ = "option"; }
| PASSWORD { $$ = "password"; }
| PRIOR { $$ = "prior"; }
| PRIVILEGES { $$ = "privileges"; }
| PROCEDURAL { $$ = "procedural"; }
| READ { $$ = "read"; }
| RECIPE { $$ = "recipe"; }
| RELATIVE { $$ = "relative"; }
| RENAME { $$ = "rename"; }
| RETURNS { $$ = "returns"; }
| ROW { $$ = "row"; }
| RULE { $$ = "rule"; }
| SCROLL { $$ = "scroll"; }
| SEQUENCE { $$ = "sequence"; }
| SERIAL { $$ = "serial"; }
| START { $$ = "start"; }
| STATEMENT { $$ = "statement"; }
| STDIN { $$ = "stdin"; }
| STDOUT { $$ = "stdout"; }
| TIME { $$ = "time"; }
| TIMESTAMP { $$ = "timestamp"; }
| TIMEZONE_HOUR { $$ = "timezone_hour"; }
| TIMEZONE_MINUTE { $$ = "timezone_minute"; }
| TRIGGER { $$ = "trigger"; }
| TRUSTED { $$ = "trusted"; }
| TYPE_P { $$ = "type"; }
| VALID { $$ = "valid"; }
| VERSION { $$ = "version"; }
| ZONE { $$ = "zone"; }
;
/* Column label
* Allowed labels in "AS" clauses.
* Include TRUE/FALSE SQL3 reserved words for Postgres backward
* compatibility. Cannot allow this for column names since the
* syntax would not distinguish between the constant value and
* a column name. - thomas 1997-10-24
* Add other keywords to this list. Note that they appear here
* rather than in ColId if there was a shift/reduce conflict
* when used as a full identifier. - thomas 1997-11-06
*/
ColLabel: ColId { $$ = $1; }
| ABORT_TRANS { $$ = "abort"; }
| ANALYZE { $$ = "analyze"; }
| BINARY { $$ = "binary"; }
1998-12-04 16:34:49 +01:00
| CASE { $$ = "case"; }
| CLUSTER { $$ = "cluster"; }
1998-12-04 16:34:49 +01:00
| COALESCE { $$ = "coalesce"; }
| CONSTRAINT { $$ = "constraint"; }
| COPY { $$ = "copy"; }
| CROSS { $$ = "cross"; }
| CURRENT { $$ = "current"; }
| DO { $$ = "do"; }
1998-12-04 16:34:49 +01:00
| ELSE { $$ = "else"; }
| END_TRANS { $$ = "end"; }
| EXPLAIN { $$ = "explain"; }
| EXTEND { $$ = "extend"; }
| FALSE_P { $$ = "false"; }
| FOREIGN { $$ = "foreign"; }
| GROUP { $$ = "group"; }
| LISTEN { $$ = "listen"; }
| LOAD { $$ = "load"; }
| LOCK_P { $$ = "lock"; }
| MOVE { $$ = "move"; }
| NEW { $$ = "new"; }
| NONE { $$ = "none"; }
1998-12-04 16:34:49 +01:00
| NULLIF { $$ = "nullif"; }
| ORDER { $$ = "order"; }
| POSITION { $$ = "position"; }
| PRECISION { $$ = "precision"; }
| RESET { $$ = "reset"; }
| SETOF { $$ = "setof"; }
| SHOW { $$ = "show"; }
| TABLE { $$ = "table"; }
1998-12-04 16:34:49 +01:00
| THEN { $$ = "then"; }
| TRANSACTION { $$ = "transaction"; }
| TRUE_P { $$ = "true"; }
| VACUUM { $$ = "vacuum"; }
| VERBOSE { $$ = "verbose"; }
1998-12-04 16:34:49 +01:00
| WHEN { $$ = "when"; }
1997-09-08 05:20:18 +02:00
;
SpecialRuleRelation: CURRENT
1997-09-08 05:20:18 +02:00
{
if (QueryIsRule)
$$ = "*CURRENT*";
else
1998-01-09 21:06:08 +01:00
elog(ERROR,"CURRENT used in non-rule query");
1997-09-08 05:20:18 +02:00
}
| NEW
{
if (QueryIsRule)
$$ = "*NEW*";
else
1998-01-09 21:06:08 +01:00
elog(ERROR,"NEW used in non-rule query");
1997-09-08 05:20:18 +02:00
}
;
%%
static Node *
makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr)
{
1997-09-08 05:20:18 +02:00
A_Expr *a = makeNode(A_Expr);
a->oper = oper;
a->opname = opname;
a->lexpr = lexpr;
a->rexpr = rexpr;
return (Node *)a;
}
1998-01-17 06:01:34 +01:00
/* makeRowExpr()
* Generate separate operator nodes for a single row descriptor expression.
* Perhaps this should go deeper in the parser someday... - thomas 1997-12-22
*/
static Node *
makeRowExpr(char *opr, List *largs, List *rargs)
{
Node *expr = NULL;
Node *larg, *rarg;
if (length(largs) != length(rargs))
elog(ERROR,"Unequal number of entries in row expression");
if (lnext(largs) != NIL)
expr = makeRowExpr(opr,lnext(largs),lnext(rargs));
larg = lfirst(largs);
rarg = lfirst(rargs);
if ((strcmp(opr, "=") == 0)
|| (strcmp(opr, "<") == 0)
|| (strcmp(opr, "<=") == 0)
|| (strcmp(opr, ">") == 0)
|| (strcmp(opr, ">=") == 0))
{
if (expr == NULL)
expr = makeA_Expr(OP, opr, larg, rarg);
else
expr = makeA_Expr(AND, NULL, expr, makeA_Expr(OP, opr, larg, rarg));
}
else if (strcmp(opr, "<>") == 0)
{
if (expr == NULL)
expr = makeA_Expr(OP, opr, larg, rarg);
else
expr = makeA_Expr(OR, NULL, expr, makeA_Expr(OP, opr, larg, rarg));
}
else
{
elog(ERROR,"Operator '%s' not implemented for row expressions",opr);
}
#if FALSE
while ((largs != NIL) && (rargs != NIL))
{
larg = lfirst(largs);
rarg = lfirst(rargs);
if (expr == NULL)
expr = makeA_Expr(OP, opr, larg, rarg);
else
expr = makeA_Expr(AND, NULL, expr, makeA_Expr(OP, opr, larg, rarg));
largs = lnext(largs);
rargs = lnext(rargs);
}
pprint(expr);
#endif
return expr;
}
static void
mapTargetColumns(List *src, List *dst)
{
ColumnDef *s;
ResTarget *d;
if (length(src) != length(dst))
1998-01-09 21:06:08 +01:00
elog(ERROR,"CREATE TABLE/AS SELECT has mismatched column count");
while ((src != NIL) && (dst != NIL))
{
s = (ColumnDef *)lfirst(src);
d = (ResTarget *)lfirst(dst);
d->name = s->colname;
src = lnext(src);
dst = lnext(dst);
}
return;
} /* mapTargetColumns() */
1997-10-31 01:50:39 +01:00
static Node *makeIndexable(char *opname, Node *lexpr, Node *rexpr)
{
Node *result = NULL;
/* we do this so indexes can be used */
if (strcmp(opname,"~") == 0 ||
strcmp(opname,"~*") == 0)
1997-10-31 01:50:39 +01:00
{
if (nodeTag(rexpr) == T_A_Const &&
((A_Const *)rexpr)->val.type == T_String &&
((A_Const *)rexpr)->val.val.str[0] == '^')
{
A_Const *n = (A_Const *)rexpr;
char *match_least = palloc(strlen(n->val.val.str)+2);
char *match_most = palloc(strlen(n->val.val.str)+2);
int pos, match_pos=0;
/* skip leading ^ */
for (pos = 1; n->val.val.str[pos]; pos++)
{
if (n->val.val.str[pos] == '.' ||
n->val.val.str[pos] == '?' ||
n->val.val.str[pos] == '*' ||
n->val.val.str[pos] == '[' ||
n->val.val.str[pos] == '$' ||
(strcmp(opname,"~*") == 0 && isalpha(n->val.val.str[pos])))
1997-10-31 01:50:39 +01:00
break;
if (n->val.val.str[pos] == '\\')
pos++;
match_least[match_pos] = n->val.val.str[pos];
match_most[match_pos++] = n->val.val.str[pos];
}
if (match_pos != 0)
{
A_Const *least = makeNode(A_Const);
A_Const *most = makeNode(A_Const);
/* make strings to be used in index use */
match_least[match_pos] = '\0';
match_most[match_pos] = '\377';
match_most[match_pos+1] = '\0';
least->val.type = T_String;
least->val.val.str = match_least;
most->val.type = T_String;
most->val.val.str = match_most;
#ifdef USE_LOCALE
result = makeA_Expr(AND, NULL,
makeA_Expr(OP, "~", lexpr, rexpr),
makeA_Expr(OP, ">=", lexpr, (Node *)least));
#else
1997-10-31 01:50:39 +01:00
result = makeA_Expr(AND, NULL,
makeA_Expr(OP, "~", lexpr, rexpr),
makeA_Expr(AND, NULL,
makeA_Expr(OP, ">=", lexpr, (Node *)least),
makeA_Expr(OP, "<=", lexpr, (Node *)most)));
#endif
1997-10-31 01:50:39 +01:00
}
}
}
else if (strcmp(opname,"~~") == 0)
{
if (nodeTag(rexpr) == T_A_Const &&
((A_Const *)rexpr)->val.type == T_String)
{
A_Const *n = (A_Const *)rexpr;
char *match_least = palloc(strlen(n->val.val.str)+2);
char *match_most = palloc(strlen(n->val.val.str)+2);
int pos, match_pos=0;
for (pos = 0; n->val.val.str[pos]; pos++)
{
if (n->val.val.str[pos] == '%' &&
n->val.val.str[pos+1] != '%')
break;
if(n->val.val.str[pos] == '_')
break;
if (n->val.val.str[pos] == '\\' ||
n->val.val.str[pos] == '%')
1997-10-31 01:50:39 +01:00
pos++;
if (n->val.val.str[pos] == '\0')
break;
1997-10-31 01:50:39 +01:00
match_least[match_pos] = n->val.val.str[pos];
match_most[match_pos++] = n->val.val.str[pos];
}
if (match_pos != 0)
{
A_Const *least = makeNode(A_Const);
A_Const *most = makeNode(A_Const);
/* make strings to be used in index use */
match_least[match_pos] = '\0';
match_most[match_pos] = '\377';
match_most[match_pos+1] = '\0';
least->val.type = T_String;
least->val.val.str = match_least;
most->val.type = T_String;
most->val.val.str = match_most;
#ifdef USE_LOCALE
result = makeA_Expr(AND, NULL,
makeA_Expr(OP, "~~", lexpr, rexpr),
makeA_Expr(OP, ">=", lexpr, (Node *)least));
#else
1997-10-31 01:50:39 +01:00
result = makeA_Expr(AND, NULL,
makeA_Expr(OP, "~~", lexpr, rexpr),
makeA_Expr(AND, NULL,
makeA_Expr(OP, ">=", lexpr, (Node *)least),
makeA_Expr(OP, "<=", lexpr, (Node *)most)));
#endif
1997-10-31 01:50:39 +01:00
}
}
}
if (result == NULL)
result = makeA_Expr(OP, opname, lexpr, rexpr);
return result;
} /* makeIndexable() */
1997-10-31 01:50:39 +01:00
/* xlateSqlFunc()
* Convert alternate type names to internal Postgres types.
* Do not convert "float", since that is handled elsewhere
* for FLOAT(p) syntax.
*/
static char *
xlateSqlFunc(char *name)
{
if (!strcasecmp(name,"character_length")
|| !strcasecmp(name,"char_length"))
return "length";
else
return name;
} /* xlateSqlFunc() */
/* xlateSqlType()
* Convert alternate type names to internal Postgres types.
*/
static char *
xlateSqlType(char *name)
{
if (!strcasecmp(name,"int")
|| !strcasecmp(name,"integer"))
return "int4";
1997-09-08 05:20:18 +02:00
else if (!strcasecmp(name, "smallint"))
return "int2";
else if (!strcasecmp(name, "real")
|| !strcasecmp(name, "float"))
1997-09-08 05:20:18 +02:00
return "float8";
else if (!strcasecmp(name, "interval"))
return "timespan";
else if (!strcasecmp(name, "boolean"))
return "bool";
1997-09-08 05:20:18 +02:00
else
return name;
} /* xlateSqlType() */
void parser_init(Oid *typev, int nargs)
{
QueryIsRule = FALSE;
1997-09-08 05:20:18 +02:00
saved_relname[0]= '\0';
saved_In_Expr = NULL;
1997-09-08 05:20:18 +02:00
param_type_init(typev, nargs);
}
/* FlattenStringList()
* Traverse list of string nodes and convert to a single string.
* Used for reconstructing string form of complex expressions.
*
* Allocate at least one byte for terminator.
*/
static char *
FlattenStringList(List *list)
{
List *l;
Value *v;
char *s;
char *sp;
int nlist, len = 0;
nlist = length(list);
l = list;
while(l != NIL) {
v = (Value *)lfirst(l);
sp = v->val.str;
l = lnext(l);
len += strlen(sp);
};
len += nlist;
s = (char*) palloc(len+1);
*s = '\0';
l = list;
while(l != NIL) {
v = (Value *)lfirst(l);
sp = v->val.str;
l = lnext(l);
strcat(s,sp);
if (l != NIL) strcat(s," ");
};
*(s+len) = '\0';
1998-09-01 05:29:17 +02:00
return s;
} /* FlattenStringList() */
/* makeConstantList()
* Convert constant value node into string node.
*/
static List *
makeConstantList( A_Const *n)
{
List *result = NIL;
char *typval = NULL;
char *defval = NULL;
if (nodeTag(n) != T_A_Const) {
1998-01-09 21:06:08 +01:00
elog(ERROR,"Cannot handle non-constant parameter");
} else if (n->val.type == T_Float) {
defval = (char*) palloc(20+1);
sprintf( defval, "%g", n->val.val.dval);
result = lcons( makeString(defval), NIL);
} else if (n->val.type == T_Integer) {
defval = (char*) palloc(20+1);
sprintf( defval, "%ld", n->val.val.ival);
result = lcons( makeString(defval), NIL);
} else if (n->val.type == T_String) {
defval = (char*) palloc(strlen( ((A_Const *) n)->val.val.str) + 3);
strcpy( defval, "'");
strcat( defval, ((A_Const *) n)->val.val.str);
strcat( defval, "'");
if (n->typename != NULL)
{
typval = (char*) palloc(strlen( n->typename->name) + 1);
strcpy(typval, n->typename->name);
result = lappend( lcons( makeString(typval), NIL), makeString(defval));
}
else
{
result = lcons( makeString(defval), NIL);
}
} else {
1998-01-09 21:06:08 +01:00
elog(ERROR,"Internal error in makeConstantList(): cannot encode node");
};
1998-09-01 05:29:17 +02:00
return result;
} /* makeConstantList() */
/* fmtId()
* Check input string for non-lowercase/non-numeric characters.
* Returns either input string or input surrounded by double quotes.
*/
static char *
fmtId(char *rawid)
{
static char *cp;
for (cp = rawid; *cp != '\0'; cp++)
if (! (islower(*cp) || isdigit(*cp) || (*cp == '_'))) break;
if (*cp != '\0') {
cp = palloc(strlen(rawid)+3);
strcpy(cp,"\"");
strcat(cp,rawid);
strcat(cp,"\"");
} else {
cp = rawid;
};
1998-09-01 05:29:17 +02:00
return cp;
}
/*
* param_type_init()
*
* keep enough information around fill out the type of param nodes
* used in postquel functions
*/
static void
param_type_init(Oid *typev, int nargs)
{
pfunc_num_args = nargs;
param_type_info = typev;
}
Oid param_type(int t)
{
if ((t > pfunc_num_args) || (t == 0))
return InvalidOid;
return param_type_info[t - 1];
}