mirror of
https://github.com/postgres/postgres.git
synced 2025-07-20 05:03:10 +03:00
Remove all time travel stuff. Small parser cleanup.
This commit is contained in:
@ -4,7 +4,7 @@
|
||||
# Makefile for parser
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/parser/Makefile,v 1.9 1997/11/14 15:48:19 thomas Exp $
|
||||
# $Header: /cvsroot/pgsql/src/backend/parser/Makefile,v 1.10 1997/11/20 23:22:05 momjian Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@ -23,7 +23,7 @@ endif
|
||||
|
||||
|
||||
OBJS= analyze.o catalog_utils.o dbcommands.o gram.o \
|
||||
keywords.o parser.o parse_query.o scan.o scansup.o sysfunc.o
|
||||
keywords.o parser.o parse_query.o scan.o scansup.o
|
||||
|
||||
all: SUBSYS.o
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.48 1997/10/30 16:34:22 thomas Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.49 1997/11/20 23:22:11 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -21,6 +21,7 @@
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "parse.h" /* for AND, OR, etc. */
|
||||
#include "catalog/pg_aggregate.h"
|
||||
#include "catalog/pg_type.h" /* for INT4OID, etc. */
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "utils/elog.h"
|
||||
@ -66,33 +67,31 @@ static List *expandAllTables(ParseState *pstate);
|
||||
static char *figureColname(Node *expr, Node *resval);
|
||||
static List *makeTargetNames(ParseState *pstate, List *cols);
|
||||
static List *transformTargetList(ParseState *pstate, List *targetlist);
|
||||
static TargetEntry *
|
||||
make_targetlist_expr(ParseState *pstate,
|
||||
static TargetEntry *make_targetlist_expr(ParseState *pstate,
|
||||
char *colname, Node *expr,
|
||||
List *arrayRef);
|
||||
static bool inWhereClause = false;
|
||||
static Node *transformWhereClause(ParseState *pstate, Node *a_expr);
|
||||
static List *
|
||||
transformGroupClause(ParseState *pstate, List *grouplist,
|
||||
static List *transformGroupClause(ParseState *pstate, List *grouplist,
|
||||
List *targetlist);
|
||||
static List *
|
||||
transformSortClause(ParseState *pstate,
|
||||
static List *transformSortClause(ParseState *pstate,
|
||||
List *orderlist, List *targetlist,
|
||||
char *uniqueFlag);
|
||||
|
||||
static void parseFromClause(ParseState *pstate, List *frmList);
|
||||
static Node *
|
||||
ParseFunc(ParseState *pstate, char *funcname,
|
||||
static Node *ParseFunc(ParseState *pstate, char *funcname,
|
||||
List *fargs, int *curr_resno);
|
||||
static List *setup_tlist(char *attname, Oid relid);
|
||||
static List *setup_base_tlist(Oid typeid);
|
||||
static void
|
||||
make_arguments(int nargs, List *fargs, Oid *input_typeids,
|
||||
static void make_arguments(int nargs, List *fargs, Oid *input_typeids,
|
||||
Oid *function_typeids);
|
||||
static void AddAggToParseState(ParseState *pstate, Aggreg *aggreg);
|
||||
static void finalizeAggregates(ParseState *pstate, Query *qry);
|
||||
static void parseCheckAggregates(ParseState *pstate, Query *qry);
|
||||
static ParseState *makeParseState(void);
|
||||
static Node *parser_typecast(Value *expr, TypeName *typename, int typlen);
|
||||
static Node *parser_typecast2(Node *expr, Oid exprType, Type tp, int typlen);
|
||||
static Aggreg *ParseAgg(char *aggname, Oid basetype, Node *target);
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
@ -464,9 +463,9 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
|
||||
* equal to 2.
|
||||
*/
|
||||
addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*",
|
||||
FALSE, FALSE, NULL);
|
||||
FALSE, FALSE);
|
||||
addRangeTableEntry(pstate, stmt->object->relname, "*NEW*",
|
||||
FALSE, FALSE, NULL);
|
||||
FALSE, FALSE);
|
||||
|
||||
pstate->p_last_resno = 1;
|
||||
pstate->p_is_rule = true; /* for expand all */
|
||||
@ -947,8 +946,7 @@ parseFromClause(ParseState *pstate, List *frmList)
|
||||
* eg. select * from foo f where f.x = 1; will generate wrong answer
|
||||
* if we expand * to foo.x.
|
||||
*/
|
||||
rte = addRangeTableEntry(pstate, relname, refname, baserel->inh, TRUE,
|
||||
baserel->timeRange);
|
||||
rte = addRangeTableEntry(pstate, relname, refname, baserel->inh, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -968,7 +966,7 @@ makeRangeTable(ParseState *pstate, char *relname, List *frmList)
|
||||
return;
|
||||
|
||||
if (refnameRangeTablePosn(pstate->p_rtable, relname) < 1)
|
||||
rte = addRangeTableEntry(pstate, relname, relname, FALSE, FALSE, NULL);
|
||||
rte = addRangeTableEntry(pstate, relname, relname, FALSE, FALSE);
|
||||
else
|
||||
rte = refnameRangeTableEntry(pstate->p_rtable, relname);
|
||||
|
||||
@ -2321,7 +2319,7 @@ ParseFunc(ParseState *pstate, char *funcname, List *fargs, int *curr_resno)
|
||||
|
||||
rte = refnameRangeTableEntry(pstate->p_rtable, refname);
|
||||
if (rte == NULL)
|
||||
rte = addRangeTableEntry(pstate, refname, refname, FALSE, FALSE, NULL);
|
||||
rte = addRangeTableEntry(pstate, refname, refname, FALSE, FALSE);
|
||||
|
||||
relname = rte->relname;
|
||||
relid = rte->relid;
|
||||
@ -2443,7 +2441,7 @@ ParseFunc(ParseState *pstate, char *funcname, List *fargs, int *curr_resno)
|
||||
rte = refnameRangeTableEntry(pstate->p_rtable, refname);
|
||||
if (rte == NULL)
|
||||
rte = addRangeTableEntry(pstate, refname, refname,
|
||||
FALSE, FALSE, NULL);
|
||||
FALSE, FALSE);
|
||||
relname = rte->relname;
|
||||
|
||||
vnum = refnameRangeTablePosn(pstate->p_rtable, rte->refname);
|
||||
@ -2862,3 +2860,340 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/* not used
|
||||
#define PSIZE(PTR) (*((int32 *)(PTR) - 1))
|
||||
*/
|
||||
|
||||
static Node *
|
||||
parser_typecast(Value *expr, TypeName *typename, int typlen)
|
||||
{
|
||||
/* check for passing non-ints */
|
||||
Const *adt;
|
||||
Datum lcp;
|
||||
Type tp;
|
||||
char type_string[NAMEDATALEN];
|
||||
int32 len;
|
||||
char *cp = NULL;
|
||||
char *const_string = NULL;
|
||||
bool string_palloced = false;
|
||||
|
||||
switch (nodeTag(expr))
|
||||
{
|
||||
case T_String:
|
||||
const_string = DatumGetPointer(expr->val.str);
|
||||
break;
|
||||
case T_Integer:
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%ld", expr->val.ival);
|
||||
break;
|
||||
default:
|
||||
elog(WARN,
|
||||
"parser_typecast: cannot cast this expression to type \"%s\"",
|
||||
typename->name);
|
||||
}
|
||||
|
||||
if (typename->arrayBounds != NIL)
|
||||
{
|
||||
sprintf(type_string, "_%s", typename->name);
|
||||
tp = (Type) type(type_string);
|
||||
}
|
||||
else
|
||||
{
|
||||
tp = (Type) type(typename->name);
|
||||
}
|
||||
|
||||
len = tlen(tp);
|
||||
|
||||
#if 0 /* fix me */
|
||||
switch (CInteger(lfirst(expr)))
|
||||
{
|
||||
case INT4OID: /* int4 */
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%d", ((Const *) lnext(expr))->constvalue);
|
||||
break;
|
||||
|
||||
case NAMEOID: /* char16 */
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%s", ((Const *) lnext(expr))->constvalue);
|
||||
break;
|
||||
|
||||
case CHAROID: /* char */
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%c", ((Const) lnext(expr))->constvalue);
|
||||
break;
|
||||
|
||||
case FLOAT8OID: /* float8 */
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%f", ((Const) lnext(expr))->constvalue);
|
||||
break;
|
||||
|
||||
case CASHOID: /* money */
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%d",
|
||||
(int) ((Const *) expr)->constvalue);
|
||||
break;
|
||||
|
||||
case TEXTOID: /* text */
|
||||
const_string = DatumGetPointer(((Const) lnext(expr))->constvalue);
|
||||
const_string = (char *) textout((struct varlena *) const_string);
|
||||
break;
|
||||
|
||||
case UNKNOWNOID: /* unknown */
|
||||
const_string = DatumGetPointer(((Const) lnext(expr))->constvalue);
|
||||
const_string = (char *) textout((struct varlena *) const_string);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(WARN, "unknown type %d", CInteger(lfirst(expr)));
|
||||
}
|
||||
#endif
|
||||
|
||||
cp = instr2(tp, const_string, typlen);
|
||||
|
||||
if (!tbyvalue(tp))
|
||||
{
|
||||
/*
|
||||
if (len >= 0 && len != PSIZE(cp)) {
|
||||
char *pp;
|
||||
pp = (char *) palloc(len);
|
||||
memmove(pp, cp, len);
|
||||
cp = pp;
|
||||
}
|
||||
*/
|
||||
lcp = PointerGetDatum(cp);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (len)
|
||||
{
|
||||
case 1:
|
||||
lcp = Int8GetDatum(cp);
|
||||
break;
|
||||
case 2:
|
||||
lcp = Int16GetDatum(cp);
|
||||
break;
|
||||
case 4:
|
||||
lcp = Int32GetDatum(cp);
|
||||
break;
|
||||
default:
|
||||
lcp = PointerGetDatum(cp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
adt = makeConst(typeid(tp),
|
||||
len,
|
||||
(Datum) lcp,
|
||||
false,
|
||||
tbyvalue(tp),
|
||||
false, /* not a set */
|
||||
true /* is cast */ );
|
||||
|
||||
if (string_palloced)
|
||||
pfree(const_string);
|
||||
|
||||
return (Node *) adt;
|
||||
}
|
||||
|
||||
static Node *
|
||||
parser_typecast2(Node *expr, Oid exprType, Type tp, int typlen)
|
||||
{
|
||||
/* check for passing non-ints */
|
||||
Const *adt;
|
||||
Datum lcp;
|
||||
int32 len = tlen(tp);
|
||||
char *cp = NULL;
|
||||
|
||||
char *const_string = NULL;
|
||||
bool string_palloced = false;
|
||||
|
||||
Assert(IsA(expr, Const));
|
||||
|
||||
switch (exprType)
|
||||
{
|
||||
case 0: /* NULL */
|
||||
break;
|
||||
case INT4OID: /* int4 */
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%d",
|
||||
(int) ((Const *) expr)->constvalue);
|
||||
break;
|
||||
case NAMEOID: /* char16 */
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%s",
|
||||
(char *) ((Const *) expr)->constvalue);
|
||||
break;
|
||||
case CHAROID: /* char */
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%c",
|
||||
(char) ((Const *) expr)->constvalue);
|
||||
break;
|
||||
case FLOAT4OID: /* float4 */
|
||||
{
|
||||
float32 floatVal =
|
||||
DatumGetFloat32(((Const *) expr)->constvalue);
|
||||
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%f", *floatVal);
|
||||
break;
|
||||
}
|
||||
case FLOAT8OID: /* float8 */
|
||||
{
|
||||
float64 floatVal =
|
||||
DatumGetFloat64(((Const *) expr)->constvalue);
|
||||
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%f", *floatVal);
|
||||
break;
|
||||
}
|
||||
case CASHOID: /* money */
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%ld",
|
||||
(long) ((Const *) expr)->constvalue);
|
||||
break;
|
||||
case TEXTOID: /* text */
|
||||
const_string =
|
||||
DatumGetPointer(((Const *) expr)->constvalue);
|
||||
const_string = (char *) textout((struct varlena *) const_string);
|
||||
break;
|
||||
case UNKNOWNOID: /* unknown */
|
||||
const_string =
|
||||
DatumGetPointer(((Const *) expr)->constvalue);
|
||||
const_string = (char *) textout((struct varlena *) const_string);
|
||||
break;
|
||||
default:
|
||||
elog(WARN, "unknown type %u ", exprType);
|
||||
}
|
||||
|
||||
if (!exprType)
|
||||
{
|
||||
adt = makeConst(typeid(tp),
|
||||
(Size) 0,
|
||||
(Datum) NULL,
|
||||
true, /* isnull */
|
||||
false, /* was omitted */
|
||||
false, /* not a set */
|
||||
true /* is cast */ );
|
||||
return ((Node *) adt);
|
||||
}
|
||||
|
||||
cp = instr2(tp, const_string, typlen);
|
||||
|
||||
|
||||
if (!tbyvalue(tp))
|
||||
{
|
||||
/*
|
||||
if (len >= 0 && len != PSIZE(cp)) {
|
||||
char *pp;
|
||||
pp = (char *) palloc(len);
|
||||
memmove(pp, cp, len);
|
||||
cp = pp;
|
||||
}
|
||||
*/
|
||||
lcp = PointerGetDatum(cp);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (len)
|
||||
{
|
||||
case 1:
|
||||
lcp = Int8GetDatum(cp);
|
||||
break;
|
||||
case 2:
|
||||
lcp = Int16GetDatum(cp);
|
||||
break;
|
||||
case 4:
|
||||
lcp = Int32GetDatum(cp);
|
||||
break;
|
||||
default:
|
||||
lcp = PointerGetDatum(cp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
adt = makeConst(typeid(tp),
|
||||
(Size) len,
|
||||
(Datum) lcp,
|
||||
false,
|
||||
false, /* was omitted */
|
||||
false, /* not a set */
|
||||
true /* is cast */ );
|
||||
|
||||
/*
|
||||
* printf("adt %s : %u %d %d\n",CString(expr),typeid(tp) , len,cp);
|
||||
*/
|
||||
if (string_palloced)
|
||||
pfree(const_string);
|
||||
|
||||
return ((Node *) adt);
|
||||
}
|
||||
|
||||
static Aggreg *
|
||||
ParseAgg(char *aggname, Oid basetype, Node *target)
|
||||
{
|
||||
Oid fintype;
|
||||
Oid vartype;
|
||||
Oid xfn1;
|
||||
Form_pg_aggregate aggform;
|
||||
Aggreg *aggreg;
|
||||
HeapTuple theAggTuple;
|
||||
|
||||
theAggTuple = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggname),
|
||||
ObjectIdGetDatum(basetype),
|
||||
0, 0);
|
||||
if (!HeapTupleIsValid(theAggTuple))
|
||||
{
|
||||
elog(WARN, "aggregate %s does not exist", aggname);
|
||||
}
|
||||
|
||||
aggform = (Form_pg_aggregate) GETSTRUCT(theAggTuple);
|
||||
fintype = aggform->aggfinaltype;
|
||||
xfn1 = aggform->aggtransfn1;
|
||||
|
||||
if (nodeTag(target) != T_Var && nodeTag(target) != T_Expr)
|
||||
elog(WARN, "parser: aggregate can only be applied on an attribute or expression");
|
||||
|
||||
/* only aggregates with transfn1 need a base type */
|
||||
if (OidIsValid(xfn1))
|
||||
{
|
||||
basetype = aggform->aggbasetype;
|
||||
if (nodeTag(target) == T_Var)
|
||||
vartype = ((Var *) target)->vartype;
|
||||
else
|
||||
vartype = ((Expr *) target)->typeOid;
|
||||
|
||||
if (basetype != vartype)
|
||||
{
|
||||
Type tp1,
|
||||
tp2;
|
||||
|
||||
tp1 = get_id_type(basetype);
|
||||
tp2 = get_id_type(vartype);
|
||||
elog(NOTICE, "Aggregate type mismatch:");
|
||||
elog(WARN, "%s works on %s, not %s", aggname,
|
||||
tname(tp1), tname(tp2));
|
||||
}
|
||||
}
|
||||
|
||||
aggreg = makeNode(Aggreg);
|
||||
aggreg->aggname = pstrdup(aggname);
|
||||
aggreg->basetype = aggform->aggbasetype;
|
||||
aggreg->aggtype = fintype;
|
||||
|
||||
aggreg->target = target;
|
||||
|
||||
return aggreg;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/Attic/catalog_utils.c,v 1.29 1997/11/02 15:25:19 vadim Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/Attic/catalog_utils.c,v 1.30 1997/11/20 23:22:14 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -347,7 +347,7 @@ binary_oper_get_candidates(char *opname,
|
||||
pg_operator_desc = heap_openr(OperatorRelationName);
|
||||
pg_operator_scan = heap_beginscan(pg_operator_desc,
|
||||
0,
|
||||
SelfTimeQual,
|
||||
true,
|
||||
nkeys,
|
||||
opKey);
|
||||
|
||||
@ -642,7 +642,7 @@ unary_oper_get_candidates(char *op,
|
||||
pg_operator_desc = heap_openr(OperatorRelationName);
|
||||
pg_operator_scan = heap_beginscan(pg_operator_desc,
|
||||
0,
|
||||
SelfTimeQual,
|
||||
true,
|
||||
2,
|
||||
opKey);
|
||||
|
||||
@ -1004,7 +1004,7 @@ func_get_candidates(char *funcname, int nargs)
|
||||
ItemPointer iptr;
|
||||
|
||||
iptr = &indexRes->heap_iptr;
|
||||
tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer);
|
||||
tuple = heap_fetch(heapRelation, false, iptr, &buffer);
|
||||
pfree(indexRes);
|
||||
if (HeapTupleIsValid(tuple))
|
||||
{
|
||||
@ -1352,7 +1352,7 @@ findsupers(Oid relid, Oid **supervec)
|
||||
ObjectIdEqualRegProcedure,
|
||||
ObjectIdGetDatum(relid));
|
||||
|
||||
inhscan = heap_beginscan(inhrel, 0, NowTimeQual, 1, &skey);
|
||||
inhscan = heap_beginscan(inhrel, 0, false, 1, &skey);
|
||||
|
||||
while (HeapTupleIsValid(inhtup = heap_getnext(inhscan, 0, &buf)))
|
||||
{
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/Attic/dbcommands.c,v 1.11 1997/11/10 15:17:44 thomas Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/Attic/dbcommands.c,v 1.12 1997/11/20 23:22:16 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -159,7 +159,7 @@ get_pg_dbtup(char *command, char *dbname, Relation dbrel)
|
||||
ScanKeyEntryInitialize(&scanKey, 0, Anum_pg_database_datname,
|
||||
NameEqualRegProcedure, NameGetDatum(dbname));
|
||||
|
||||
scan = heap_beginscan(dbrel, 0, NowTimeQual, 1, &scanKey);
|
||||
scan = heap_beginscan(dbrel, 0, false, 1, &scanKey);
|
||||
if (!HeapScanIsValid(scan))
|
||||
elog(WARN, "%s: cannot begin scan of pg_database.", command);
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 1.68 1997/11/17 16:37:24 thomas Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 1.69 1997/11/20 23:22:19 momjian Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -95,7 +95,6 @@ static Node *makeIndexable(char *opname, Node *lexpr, Node *rexpr);
|
||||
IndexElem *ielem;
|
||||
RangeVar *range;
|
||||
RelExpr *relexp;
|
||||
TimeRange *trange;
|
||||
A_Indices *aind;
|
||||
ResTarget *target;
|
||||
ParamNo *paramno;
|
||||
@ -134,8 +133,7 @@ static Node *makeIndexable(char *opname, Node *lexpr, Node *rexpr);
|
||||
|
||||
%type <str> opt_id, opt_portal_name,
|
||||
before_clause, after_clause, all_Op, MathOp, opt_name, opt_unique,
|
||||
result, OptUseOp, opt_class, opt_range_start, opt_range_end,
|
||||
SpecialRuleRelation
|
||||
result, OptUseOp, opt_class, SpecialRuleRelation
|
||||
|
||||
%type <str> privileges, operation_commalist, grantee
|
||||
%type <chr> operation, TriggerOneEvent
|
||||
@ -190,7 +188,6 @@ static Node *makeIndexable(char *opname, Node *lexpr, Node *rexpr);
|
||||
%type <ielem> index_elem, func_index
|
||||
%type <range> from_val
|
||||
%type <relexp> relation_expr
|
||||
%type <trange> time_range
|
||||
%type <target> res_target_el, res_target_el2
|
||||
%type <paramno> ParamNo
|
||||
|
||||
@ -2353,7 +2350,6 @@ relation_expr: relation_name
|
||||
$$ = makeNode(RelExpr);
|
||||
$$->relname = $1;
|
||||
$$->inh = FALSE;
|
||||
$$->timeRange = NULL;
|
||||
}
|
||||
| relation_name '*' %prec '='
|
||||
{
|
||||
@ -2361,44 +2357,7 @@ relation_expr: relation_name
|
||||
$$ = makeNode(RelExpr);
|
||||
$$->relname = $1;
|
||||
$$->inh = TRUE;
|
||||
$$->timeRange = NULL;
|
||||
}
|
||||
| relation_name time_range
|
||||
{
|
||||
/* time-qualified query */
|
||||
$$ = makeNode(RelExpr);
|
||||
$$->relname = $1;
|
||||
$$->inh = FALSE;
|
||||
$$->timeRange = $2;
|
||||
}
|
||||
;
|
||||
|
||||
/* Time travel
|
||||
* Range specification clause.
|
||||
*/
|
||||
time_range: '[' opt_range_start ',' opt_range_end ']'
|
||||
{
|
||||
$$ = makeNode(TimeRange);
|
||||
$$->startDate = $2;
|
||||
$$->endDate = $4;
|
||||
elog (WARN, "time travel is no longer available");
|
||||
}
|
||||
| '[' date ']'
|
||||
{
|
||||
$$ = makeNode(TimeRange);
|
||||
$$->startDate = $2;
|
||||
$$->endDate = NULL;
|
||||
elog (WARN, "time travel is no longer available");
|
||||
}
|
||||
;
|
||||
|
||||
opt_range_start: date
|
||||
| /*EMPTY*/ { $$ = "epoch"; }
|
||||
;
|
||||
|
||||
opt_range_end: date
|
||||
| /*EMPTY*/ { $$ = "now"; }
|
||||
;
|
||||
|
||||
opt_array_bounds: '[' ']' nest_array_bounds
|
||||
{ $$ = lcons(makeInteger(-1), $3); }
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/Attic/parse_query.c,v 1.22 1997/11/02 15:25:30 vadim Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/Attic/parse_query.c,v 1.23 1997/11/20 23:22:22 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -127,8 +127,8 @@ RangeTblEntry *
|
||||
addRangeTableEntry(ParseState *pstate,
|
||||
char *relname,
|
||||
char *refname,
|
||||
bool inh, bool inFromCl,
|
||||
TimeRange *timeRange)
|
||||
bool inh,
|
||||
bool inFromCl)
|
||||
{
|
||||
Relation relation;
|
||||
RangeTblEntry *rte = makeNode(RangeTblEntry);
|
||||
@ -154,8 +154,6 @@ addRangeTableEntry(ParseState *pstate,
|
||||
*/
|
||||
rte->inh = inh;
|
||||
|
||||
rte->timeRange = timeRange;
|
||||
|
||||
/* RelOID */
|
||||
rte->relid = RelationGetRelationId(relation);
|
||||
|
||||
@ -194,7 +192,7 @@ expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno)
|
||||
|
||||
rte = refnameRangeTableEntry(pstate->p_rtable, refname);
|
||||
if (rte == NULL)
|
||||
rte = addRangeTableEntry(pstate, relname, refname, FALSE, FALSE, NULL);
|
||||
rte = addRangeTableEntry(pstate, relname, refname, FALSE, FALSE);
|
||||
|
||||
rdesc = heap_open(rte->relid);
|
||||
|
||||
@ -475,7 +473,7 @@ make_var(ParseState *pstate, char *refname, char *attrname, Oid *type_id)
|
||||
|
||||
rte = refnameRangeTableEntry(pstate->p_rtable, refname);
|
||||
if (rte == NULL)
|
||||
rte = addRangeTableEntry(pstate, refname, refname, FALSE, FALSE, NULL);
|
||||
rte = addRangeTableEntry(pstate, refname, refname, FALSE, FALSE);
|
||||
|
||||
vnum = refnameRangeTablePosn(pstate->p_rtable, refname);
|
||||
|
||||
|
@ -6,45 +6,20 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.27 1997/11/17 16:59:08 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.28 1997/11/20 23:22:24 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/param.h> /* for MAXPATHLEN */
|
||||
|
||||
#include "postgres.h"
|
||||
#include "parser/catalog_utils.h"
|
||||
#include "parser/gramparse.h"
|
||||
#include "parser/parse_query.h"
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/execnodes.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/primnodes.h"
|
||||
#include "nodes/plannodes.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/exc.h"
|
||||
#include "utils/excid.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/palloc.h"
|
||||
#include "utils/syscache.h"
|
||||
#include "catalog/pg_aggregate.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "access/heapam.h"
|
||||
#include "optimizer/clauses.h"
|
||||
|
||||
void init_io(); /* from scan.l */
|
||||
void parser_init(Oid *typev, int nargs); /* from gram.y */
|
||||
int yyparse(); /* from gram.c */
|
||||
|
||||
char *parseString; /* the char* which holds the string to be
|
||||
* parsed */
|
||||
char *parseCh; /* a pointer used during parsing to walk
|
||||
* down ParseString */
|
||||
|
||||
List *parsetree = NIL;
|
||||
|
||||
#ifdef SETS_FIXED
|
||||
@ -70,9 +45,7 @@ parser(char *str, Oid *typev, int nargs)
|
||||
|
||||
init_io();
|
||||
|
||||
/* Set things up to read from the string, if there is one */
|
||||
parseString = (char *) palloc(strlen(str) + 1);
|
||||
memmove(parseString, str, strlen(str) + 1);
|
||||
parseString = pstrdup(str);
|
||||
|
||||
parser_init(typev, nargs);
|
||||
yyresult = yyparse();
|
||||
@ -83,10 +56,8 @@ parser(char *str, Oid *typev, int nargs)
|
||||
|
||||
clearerr(stdin);
|
||||
|
||||
if (yyresult)
|
||||
{ /* error */
|
||||
if (yyresult) /* error */
|
||||
return ((QueryTreeList *) NULL);
|
||||
}
|
||||
|
||||
queryList = parse_analyze(parsetree);
|
||||
|
||||
@ -205,339 +176,3 @@ define_sets(Node *clause)
|
||||
|
||||
#endif
|
||||
|
||||
/* not used
|
||||
#define PSIZE(PTR) (*((int32 *)(PTR) - 1))
|
||||
*/
|
||||
|
||||
Node *
|
||||
parser_typecast(Value *expr, TypeName *typename, int typlen)
|
||||
{
|
||||
/* check for passing non-ints */
|
||||
Const *adt;
|
||||
Datum lcp;
|
||||
Type tp;
|
||||
char type_string[NAMEDATALEN];
|
||||
int32 len;
|
||||
char *cp = NULL;
|
||||
char *const_string = NULL;
|
||||
bool string_palloced = false;
|
||||
|
||||
switch (nodeTag(expr))
|
||||
{
|
||||
case T_String:
|
||||
const_string = DatumGetPointer(expr->val.str);
|
||||
break;
|
||||
case T_Integer:
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%ld", expr->val.ival);
|
||||
break;
|
||||
default:
|
||||
elog(WARN,
|
||||
"parser_typecast: cannot cast this expression to type \"%s\"",
|
||||
typename->name);
|
||||
}
|
||||
|
||||
if (typename->arrayBounds != NIL)
|
||||
{
|
||||
sprintf(type_string, "_%s", typename->name);
|
||||
tp = (Type) type(type_string);
|
||||
}
|
||||
else
|
||||
{
|
||||
tp = (Type) type(typename->name);
|
||||
}
|
||||
|
||||
len = tlen(tp);
|
||||
|
||||
#if 0 /* fix me */
|
||||
switch (CInteger(lfirst(expr)))
|
||||
{
|
||||
case INT4OID: /* int4 */
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%d", ((Const *) lnext(expr))->constvalue);
|
||||
break;
|
||||
|
||||
case NAMEOID: /* char16 */
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%s", ((Const *) lnext(expr))->constvalue);
|
||||
break;
|
||||
|
||||
case CHAROID: /* char */
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%c", ((Const) lnext(expr))->constvalue);
|
||||
break;
|
||||
|
||||
case FLOAT8OID: /* float8 */
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%f", ((Const) lnext(expr))->constvalue);
|
||||
break;
|
||||
|
||||
case CASHOID: /* money */
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%d",
|
||||
(int) ((Const *) expr)->constvalue);
|
||||
break;
|
||||
|
||||
case TEXTOID: /* text */
|
||||
const_string = DatumGetPointer(((Const) lnext(expr))->constvalue);
|
||||
const_string = (char *) textout((struct varlena *) const_string);
|
||||
break;
|
||||
|
||||
case UNKNOWNOID: /* unknown */
|
||||
const_string = DatumGetPointer(((Const) lnext(expr))->constvalue);
|
||||
const_string = (char *) textout((struct varlena *) const_string);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(WARN, "unknown type %d", CInteger(lfirst(expr)));
|
||||
}
|
||||
#endif
|
||||
|
||||
cp = instr2(tp, const_string, typlen);
|
||||
|
||||
if (!tbyvalue(tp))
|
||||
{
|
||||
/*
|
||||
if (len >= 0 && len != PSIZE(cp)) {
|
||||
char *pp;
|
||||
pp = (char *) palloc(len);
|
||||
memmove(pp, cp, len);
|
||||
cp = pp;
|
||||
}
|
||||
*/
|
||||
lcp = PointerGetDatum(cp);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (len)
|
||||
{
|
||||
case 1:
|
||||
lcp = Int8GetDatum(cp);
|
||||
break;
|
||||
case 2:
|
||||
lcp = Int16GetDatum(cp);
|
||||
break;
|
||||
case 4:
|
||||
lcp = Int32GetDatum(cp);
|
||||
break;
|
||||
default:
|
||||
lcp = PointerGetDatum(cp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
adt = makeConst(typeid(tp),
|
||||
len,
|
||||
(Datum) lcp,
|
||||
false,
|
||||
tbyvalue(tp),
|
||||
false, /* not a set */
|
||||
true /* is cast */ );
|
||||
|
||||
if (string_palloced)
|
||||
pfree(const_string);
|
||||
|
||||
return (Node *) adt;
|
||||
}
|
||||
|
||||
Node *
|
||||
parser_typecast2(Node *expr, Oid exprType, Type tp, int typlen)
|
||||
{
|
||||
/* check for passing non-ints */
|
||||
Const *adt;
|
||||
Datum lcp;
|
||||
int32 len = tlen(tp);
|
||||
char *cp = NULL;
|
||||
|
||||
char *const_string = NULL;
|
||||
bool string_palloced = false;
|
||||
|
||||
Assert(IsA(expr, Const));
|
||||
|
||||
switch (exprType)
|
||||
{
|
||||
case 0: /* NULL */
|
||||
break;
|
||||
case INT4OID: /* int4 */
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%d",
|
||||
(int) ((Const *) expr)->constvalue);
|
||||
break;
|
||||
case NAMEOID: /* char16 */
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%s",
|
||||
(char *) ((Const *) expr)->constvalue);
|
||||
break;
|
||||
case CHAROID: /* char */
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%c",
|
||||
(char) ((Const *) expr)->constvalue);
|
||||
break;
|
||||
case FLOAT4OID: /* float4 */
|
||||
{
|
||||
float32 floatVal =
|
||||
DatumGetFloat32(((Const *) expr)->constvalue);
|
||||
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%f", *floatVal);
|
||||
break;
|
||||
}
|
||||
case FLOAT8OID: /* float8 */
|
||||
{
|
||||
float64 floatVal =
|
||||
DatumGetFloat64(((Const *) expr)->constvalue);
|
||||
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%f", *floatVal);
|
||||
break;
|
||||
}
|
||||
case CASHOID: /* money */
|
||||
const_string = (char *) palloc(256);
|
||||
string_palloced = true;
|
||||
sprintf(const_string, "%ld",
|
||||
(long) ((Const *) expr)->constvalue);
|
||||
break;
|
||||
case TEXTOID: /* text */
|
||||
const_string =
|
||||
DatumGetPointer(((Const *) expr)->constvalue);
|
||||
const_string = (char *) textout((struct varlena *) const_string);
|
||||
break;
|
||||
case UNKNOWNOID: /* unknown */
|
||||
const_string =
|
||||
DatumGetPointer(((Const *) expr)->constvalue);
|
||||
const_string = (char *) textout((struct varlena *) const_string);
|
||||
break;
|
||||
default:
|
||||
elog(WARN, "unknown type %u ", exprType);
|
||||
}
|
||||
|
||||
if (!exprType)
|
||||
{
|
||||
adt = makeConst(typeid(tp),
|
||||
(Size) 0,
|
||||
(Datum) NULL,
|
||||
true, /* isnull */
|
||||
false, /* was omitted */
|
||||
false, /* not a set */
|
||||
true /* is cast */ );
|
||||
return ((Node *) adt);
|
||||
}
|
||||
|
||||
cp = instr2(tp, const_string, typlen);
|
||||
|
||||
|
||||
if (!tbyvalue(tp))
|
||||
{
|
||||
/*
|
||||
if (len >= 0 && len != PSIZE(cp)) {
|
||||
char *pp;
|
||||
pp = (char *) palloc(len);
|
||||
memmove(pp, cp, len);
|
||||
cp = pp;
|
||||
}
|
||||
*/
|
||||
lcp = PointerGetDatum(cp);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (len)
|
||||
{
|
||||
case 1:
|
||||
lcp = Int8GetDatum(cp);
|
||||
break;
|
||||
case 2:
|
||||
lcp = Int16GetDatum(cp);
|
||||
break;
|
||||
case 4:
|
||||
lcp = Int32GetDatum(cp);
|
||||
break;
|
||||
default:
|
||||
lcp = PointerGetDatum(cp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
adt = makeConst(typeid(tp),
|
||||
(Size) len,
|
||||
(Datum) lcp,
|
||||
false,
|
||||
false, /* was omitted */
|
||||
false, /* not a set */
|
||||
true /* is cast */ );
|
||||
|
||||
/*
|
||||
* printf("adt %s : %u %d %d\n",CString(expr),typeid(tp) , len,cp);
|
||||
*/
|
||||
if (string_palloced)
|
||||
pfree(const_string);
|
||||
|
||||
return ((Node *) adt);
|
||||
}
|
||||
|
||||
Aggreg *
|
||||
ParseAgg(char *aggname, Oid basetype, Node *target)
|
||||
{
|
||||
Oid fintype;
|
||||
Oid vartype;
|
||||
Oid xfn1;
|
||||
Form_pg_aggregate aggform;
|
||||
Aggreg *aggreg;
|
||||
HeapTuple theAggTuple;
|
||||
|
||||
theAggTuple = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggname),
|
||||
ObjectIdGetDatum(basetype),
|
||||
0, 0);
|
||||
if (!HeapTupleIsValid(theAggTuple))
|
||||
{
|
||||
elog(WARN, "aggregate %s does not exist", aggname);
|
||||
}
|
||||
|
||||
aggform = (Form_pg_aggregate) GETSTRUCT(theAggTuple);
|
||||
fintype = aggform->aggfinaltype;
|
||||
xfn1 = aggform->aggtransfn1;
|
||||
|
||||
if (nodeTag(target) != T_Var && nodeTag(target) != T_Expr)
|
||||
elog(WARN, "parser: aggregate can only be applied on an attribute or expression");
|
||||
|
||||
/* only aggregates with transfn1 need a base type */
|
||||
if (OidIsValid(xfn1))
|
||||
{
|
||||
basetype = aggform->aggbasetype;
|
||||
if (nodeTag(target) == T_Var)
|
||||
vartype = ((Var *) target)->vartype;
|
||||
else
|
||||
vartype = ((Expr *) target)->typeOid;
|
||||
|
||||
if (basetype != vartype)
|
||||
{
|
||||
Type tp1,
|
||||
tp2;
|
||||
|
||||
tp1 = get_id_type(basetype);
|
||||
tp2 = get_id_type(vartype);
|
||||
elog(NOTICE, "Aggregate type mismatch:");
|
||||
elog(WARN, "%s works on %s, not %s", aggname,
|
||||
tname(tp1), tname(tp2));
|
||||
}
|
||||
}
|
||||
|
||||
aggreg = makeNode(Aggreg);
|
||||
aggreg->aggname = pstrdup(aggname);
|
||||
aggreg->basetype = aggform->aggbasetype;
|
||||
aggreg->aggtype = fintype;
|
||||
|
||||
aggreg->target = target;
|
||||
|
||||
return aggreg;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/scan.l,v 1.29 1997/11/17 16:31:39 thomas Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/scan.l,v 1.30 1997/11/20 23:22:25 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -29,12 +29,11 @@
|
||||
#include "parser/gramparse.h"
|
||||
#include "parser/keywords.h"
|
||||
#include "parser/scansup.h"
|
||||
#include "parser/sysfunc.h"
|
||||
#include "parse.h"
|
||||
#include "utils/builtins.h"
|
||||
|
||||
extern char *parseString;
|
||||
extern char *parseCh;
|
||||
static char *parseCh;
|
||||
|
||||
/* some versions of lex define this as a macro */
|
||||
#if defined(yywrap)
|
||||
@ -127,8 +126,6 @@ number [-+.0-9Ee]
|
||||
letter [\200-\377_A-Za-z]
|
||||
letter_or_digit [\200-\377_A-Za-z0-9]
|
||||
|
||||
sysfunc SYS_{letter}{letter_or_digit}*
|
||||
|
||||
identifier {letter}{letter_or_digit}*
|
||||
|
||||
typecast "::"
|
||||
@ -278,11 +275,6 @@ other .
|
||||
}
|
||||
|
||||
|
||||
{sysfunc} {
|
||||
yylval.str = pstrdup(SystemFunctionHandler((char *)yytext));
|
||||
return (SCONST);
|
||||
}
|
||||
|
||||
{typecast} { return TYPECAST; }
|
||||
|
||||
{self}/-[\.0-9] {
|
||||
|
@ -1,86 +0,0 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* sysfunc.c--
|
||||
* process system functions and return a string result
|
||||
*
|
||||
* Notes:
|
||||
* 1) I return a string result because most of the functions cannot return any
|
||||
* normal type anyway (e.g. SYS_DATE, SYS_TIME, etc...), and the few that
|
||||
* might (SYS_UID or whatever) can just return it as a string - no problem.
|
||||
* This keeps the function flexible enough to be of good use.
|
||||
*
|
||||
* Written by Chad Robinson, chadr@brttech.com
|
||||
* Last modified: 04/27/1996
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <config.h>
|
||||
#include <postgres.h>
|
||||
#include <miscadmin.h>
|
||||
#include <parser/sysfunc.h>
|
||||
|
||||
/*
|
||||
* Can't get much more obvious than this. Might need to replace localtime()
|
||||
* on older systems...
|
||||
*/
|
||||
static char *
|
||||
Sysfunc_system_date(void)
|
||||
{
|
||||
time_t cur_time_secs;
|
||||
struct tm *cur_time_expanded;
|
||||
static char buf[12]; /* Just for safety, y'understand... */
|
||||
|
||||
time(&cur_time_secs);
|
||||
cur_time_expanded = localtime(&cur_time_secs);
|
||||
if (EuroDates == 1)
|
||||
sprintf(buf, "%2.2d-%2.2d-%4.4d", cur_time_expanded->tm_mday,
|
||||
cur_time_expanded->tm_mon + 1, cur_time_expanded->tm_year + 1900);
|
||||
else
|
||||
sprintf(buf, "%2.2d-%2.2d-%4.4d", cur_time_expanded->tm_mon + 1,
|
||||
cur_time_expanded->tm_mday, cur_time_expanded->tm_year + 1900);
|
||||
|
||||
return &buf[0];
|
||||
}
|
||||
|
||||
static char *
|
||||
Sysfunc_system_time(void)
|
||||
{
|
||||
time_t cur_time_secs;
|
||||
struct tm *cur_time_expanded;
|
||||
static char buf[10]; /* Just for safety, y'understand... */
|
||||
|
||||
time(&cur_time_secs);
|
||||
cur_time_expanded = localtime(&cur_time_secs);
|
||||
sprintf(buf, "%2.2d:%2.2d:%2.2d", cur_time_expanded->tm_hour,
|
||||
cur_time_expanded->tm_min, cur_time_expanded->tm_sec);
|
||||
|
||||
return &buf[0];
|
||||
}
|
||||
|
||||
char *
|
||||
SystemFunctionHandler(char *funct)
|
||||
{
|
||||
if (!strcmp(funct, "SYS_DATE"))
|
||||
return Sysfunc_system_date();
|
||||
if (!strcmp(funct, "SYS_TIME"))
|
||||
return Sysfunc_system_time();
|
||||
return "*unknown function*";
|
||||
}
|
||||
|
||||
#ifdef SYSFUNC_TEST
|
||||
/*
|
||||
* Chad's rule of coding #4 - never delete a test function, even a stupid
|
||||
* one - you always need it 10 minutes after you delete it.
|
||||
*/
|
||||
void
|
||||
main(void)
|
||||
{
|
||||
printf("Current system date: %s\n", SystemFunctionHandler("SYS_DATE"));
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user