Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add support for CURRENT_TIMESTAMP and friends.

Patch provided by Akio Ishida.
  • Loading branch information...
commit e913eb48ae0b370249b4b4262291f00e753c323f 1 parent cbf2a19
t-ishii authored
View
7 Makefile.am
@@ -10,6 +10,7 @@ pgpool_SOURCES = pool.h pool_type.h version.h pgpool.conf.sample \
pool_query_cache.c pool_hba.conf.sample sample/pgpool.pam\
pool_hba.c pool_path.h pool_path.c pool_ip.h pool_ip.c pool_type.h \
ps_status.c strlcpy.c recovery.c pool_relcache.c \
+ pool_timestamp.c pool_timestamp.h \
pool_proto_modules.c pool_proto_modules.h
pg_md5_SOURCES = pg_md5.c md5.c md5.h
@@ -67,6 +68,12 @@ EXTRA_DIST = pgpool.8.in sql/system_db.sql sample/pgpool.pam doc/pgpool-ja.html
test/jdbc/expected/autocommit test/jdbc/expected/batch \
test/jdbc/expected/column test/jdbc/expected/lock test/jdbc/expected/select \
test/jdbc/expected/update test/jdbc/expected/insert \
+ test/timestamp/Makefile test/timestamp/input/insert.sql \
+ test/timestamp/input/update.sql test/timestamp/input/misc.sql \
+ test/timestamp/expected/insert.out test/timestamp/expected/update.out \
+ test/timestamp/expected/misc.out test/timestamp/main.c \
+ test/timestamp/parse_schedule test/timestamp/run-test \
redhat/pgpool.init redhat/pgpool.sysconfig
+
SUBDIRS = parser pcp
View
11 Makefile.in
@@ -71,7 +71,7 @@ am_pgpool_OBJECTS = main.$(OBJEXT) child.$(OBJEXT) pool_auth.$(OBJEXT) \
pool_query_cache.$(OBJEXT) pool_hba.$(OBJEXT) \
pool_path.$(OBJEXT) pool_ip.$(OBJEXT) ps_status.$(OBJEXT) \
strlcpy.$(OBJEXT) recovery.$(OBJEXT) pool_relcache.$(OBJEXT) \
- pool_proto_modules.$(OBJEXT)
+ pool_timestamp.$(OBJEXT) pool_proto_modules.$(OBJEXT)
pgpool_OBJECTS = $(am_pgpool_OBJECTS)
pgpool_DEPENDENCIES = parser/libsql-parser.a pcp/libpcp.la \
parser/nodes.o
@@ -239,6 +239,7 @@ pgpool_SOURCES = pool.h pool_type.h version.h pgpool.conf.sample \
pool_query_cache.c pool_hba.conf.sample sample/pgpool.pam\
pool_hba.c pool_path.h pool_path.c pool_ip.h pool_ip.c pool_type.h \
ps_status.c strlcpy.c recovery.c pool_relcache.c \
+ pool_timestamp.c pool_timestamp.h \
pool_proto_modules.c pool_proto_modules.h
pg_md5_SOURCES = pg_md5.c md5.c md5.h
@@ -281,6 +282,11 @@ EXTRA_DIST = pgpool.8.in sql/system_db.sql sample/pgpool.pam doc/pgpool-ja.html
test/jdbc/expected/autocommit test/jdbc/expected/batch \
test/jdbc/expected/column test/jdbc/expected/lock test/jdbc/expected/select \
test/jdbc/expected/update test/jdbc/expected/insert \
+ test/timestamp/Makefile test/timestamp/input/insert.sql \
+ test/timestamp/input/update.sql test/timestamp/input/misc.sql \
+ test/timestamp/expected/insert.out test/timestamp/expected/update.out \
+ test/timestamp/expected/misc.out test/timestamp/main.c \
+ test/timestamp/parse_schedule test/timestamp/run-test \
redhat/pgpool.init redhat/pgpool.sysconfig
SUBDIRS = parser pcp
@@ -404,6 +410,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pool_signal.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pool_stream.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pool_system.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pool_timestamp.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ps_status.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/recovery.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strlcpy.Po@am__quote@
@@ -658,7 +665,7 @@ distclean-tags:
distdir: $(DISTFILES)
$(am__remove_distdir)
mkdir $(distdir)
- $(mkdir_p) $(distdir)/doc $(distdir)/redhat $(distdir)/sample $(distdir)/sql $(distdir)/sql/pgpool-recovery $(distdir)/test/jdbc $(distdir)/test/jdbc/expected $(distdir)/test/parser $(distdir)/test/parser/expected $(distdir)/test/parser/input
+ $(mkdir_p) $(distdir)/doc $(distdir)/redhat $(distdir)/sample $(distdir)/sql $(distdir)/sql/pgpool-recovery $(distdir)/test/jdbc $(distdir)/test/jdbc/expected $(distdir)/test/parser $(distdir)/test/parser/expected $(distdir)/test/parser/input $(distdir)/test/timestamp $(distdir)/test/timestamp/expected $(distdir)/test/timestamp/input
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
list='$(DISTFILES)'; for file in $$list; do \
View
69 pool_process_query.c
@@ -1,6 +1,6 @@
/* -*-pgsql-c-*- */
/*
- * $Header: /cvsroot/pgpool/pgpool-II/pool_process_query.c,v 1.170 2009/11/04 14:01:28 t-ishii Exp $
+ * $Header: /cvsroot/pgpool/pgpool-II/pool_process_query.c,v 1.171 2009/11/10 10:03:10 t-ishii Exp $
*
* pgpool: a language independent connection pool server for PostgreSQL
* written by Tatsuo Ishii
@@ -43,6 +43,7 @@
#include "pool.h"
#include "pool_signal.h"
+#include "pool_timestamp.h"
#include "pool_proto_modules.h"
#ifndef FD_SETSIZE
@@ -1895,17 +1896,9 @@ POOL_STATUS SimpleForwardToBackend(char kind, POOL_CONNECTION *frontend, POOL_CO
char *p;
int i;
char *name;
+ char *rewrite_msg = NULL;
POOL_STATUS ret;
- for (i=0;i<NUM_BACKENDS;i++)
- {
- if (VALID_BACKEND(i))
- {
- if (pool_write(CONNECTION(backend, i), &kind, 1))
- return POOL_END;
- }
- }
-
if (pool_read(frontend, &sendlen, sizeof(sendlen)))
{
return POOL_END;
@@ -1913,17 +1906,20 @@ POOL_STATUS SimpleForwardToBackend(char kind, POOL_CONNECTION *frontend, POOL_CO
len = ntohl(sendlen) - 4;
- for (i=0;i<NUM_BACKENDS;i++)
+ if (len == 0)
{
- if (VALID_BACKEND(i))
+ for (i=0;i<NUM_BACKENDS;i++)
{
- if (pool_write(CONNECTION(backend,i), &sendlen, sizeof(sendlen)))
- return POOL_END;
+ if (VALID_BACKEND(i))
+ {
+ if (pool_write(CONNECTION(backend, i), &kind, 1))
+ return POOL_END;
+ if (pool_write(CONNECTION(backend,i), &sendlen, sizeof(sendlen)))
+ return POOL_END;
+ }
}
- }
-
- if (len == 0)
return POOL_CONTINUE;
+ }
else if (len < 0)
{
pool_error("SimpleForwardToBackend: invalid message length");
@@ -1934,14 +1930,53 @@ POOL_STATUS SimpleForwardToBackend(char kind, POOL_CONNECTION *frontend, POOL_CO
if (p == NULL)
return POOL_END;
+ if (kind == 'B')
+ {
+ Portal *portal = NULL;
+ char *stmt_name, *portal_name;
+
+ portal_name = p;
+ stmt_name = p + strlen(portal_name) + 1;
+
+ if (*stmt_name == '\0')
+ portal = unnamed_statement;
+ else
+ {
+ portal = lookup_prepared_statement_by_statement(&prepared_list, stmt_name);
+ }
+
+ /* rewrite bind message */
+ if (REPLICATION && portal && portal->num_tsparams > 0)
+ {
+ p = rewrite_msg = bind_rewrite_timestamp(backend, portal, p, &len);
+ sendlen = htonl(len + 4);
+ }
+ }
+
for (i=0;i<NUM_BACKENDS;i++)
{
if (VALID_BACKEND(i))
{
+ if (pool_write(CONNECTION(backend, i), &kind, 1))
+ {
+ free(rewrite_msg);
+ return POOL_END;
+ }
+
+ if (pool_write(CONNECTION(backend,i), &sendlen, sizeof(sendlen)))
+ {
+ free(rewrite_msg);
+ return POOL_END;
+ }
+
if (pool_write_and_flush(CONNECTION(backend, i), p, len))
+ {
+ free(rewrite_msg);
return POOL_END;
+ }
}
}
+ free(rewrite_msg);
if (kind == 'B') /* Bind message */
{
View
91 pool_proto_modules.c
@@ -1,6 +1,6 @@
/* -*-pgsql-c-*- */
/*
- * $Header: /cvsroot/pgpool/pgpool-II/pool_proto_modules.c,v 1.22 2009/10/30 05:11:20 t-ishii Exp $
+ * $Header: /cvsroot/pgpool/pgpool-II/pool_proto_modules.c,v 1.23 2009/11/10 10:03:10 t-ishii Exp $
*
* pgpool: a language independent connection pool server for PostgreSQL
* written by Tatsuo Ishii
@@ -45,6 +45,7 @@
#include "pool.h"
#include "pool_signal.h"
+#include "pool_timestamp.h"
#include "pool_proto_modules.h"
#include "parser/pool_string.h"
@@ -525,17 +526,44 @@ POOL_STATUS NotificationResponse(POOL_CONNECTION *frontend,
{
/* check if query is "COMMIT" or "ROLLBACK" */
commit = is_commit_query(node);
- free_parser();
/*
* Query is not commit/rollback
*/
if (!commit)
{
+ char *rewrite_query;
+
+ if (node)
+ {
+ Portal *portal = NULL;
+
+ if (IsA(node, PrepareStmt))
+ {
+ portal = pending_prepared_portal;
+ portal->num_tsparams = 0;
+ }
+ else if (IsA(node, ExecuteStmt))
+ portal = lookup_prepared_statement_by_statement(
+ &prepared_list, ((ExecuteStmt *) node)->name);
+
+ /* rewrite `now()' to timestamp literal */
+ rewrite_query = rewrite_timestamp(backend, node, false, portal);
+ if (rewrite_query != NULL)
+ {
+ string = rewrite_query;
+ len = strlen(string) + 1;
+ }
+
+ }
+
/* Send the query to master node */
if (send_simplequery_message(MASTER(backend), len, string, MAJOR(backend)) != POOL_CONTINUE)
+ {
+ free_parser();
return POOL_END;
+ }
if (wait_for_query_response(frontend, MASTER(backend), string, MAJOR(backend)) != POOL_CONTINUE)
{
@@ -547,6 +575,7 @@ POOL_STATUS NotificationResponse(POOL_CONNECTION *frontend,
cancel_packet.key= MASTER_CONNECTION(backend)->key;
cancel_request(&cancel_packet);
+ free_parser();
return POOL_END;
}
@@ -570,7 +599,10 @@ POOL_STATUS NotificationResponse(POOL_CONNECTION *frontend,
continue;
if (send_simplequery_message(CONNECTION(backend, i), len, string, MAJOR(backend)) != POOL_CONTINUE)
+ {
+ free_parser();
return POOL_END;
+ }
}
/* Wait for nodes othan than the master node */
@@ -589,6 +621,7 @@ POOL_STATUS NotificationResponse(POOL_CONNECTION *frontend,
cancel_packet.key= MASTER_CONNECTION(backend)->key;
cancel_request(&cancel_packet);
+ free_parser();
return POOL_END;
}
}
@@ -597,7 +630,10 @@ POOL_STATUS NotificationResponse(POOL_CONNECTION *frontend,
if (commit)
{
if (send_simplequery_message(MASTER(backend), len, string, MAJOR(backend)) != POOL_CONTINUE)
+ {
+ free_parser();
return POOL_END;
+ }
if (wait_for_query_response(frontend, MASTER(backend), string, MAJOR(backend)) != POOL_CONTINUE)
{
@@ -609,12 +645,14 @@ POOL_STATUS NotificationResponse(POOL_CONNECTION *frontend,
cancel_packet.key= MASTER_CONNECTION(backend)->key;
cancel_request(&cancel_packet);
+ free_parser();
return POOL_END;
}
TSTATE(backend) = 'I';
}
+ free_parser();
}
else
{
@@ -919,7 +957,8 @@ POOL_STATUS Parse(POOL_CONNECTION *frontend,
parse_tree_list = raw_parser(stmt);
if (parse_tree_list == NIL)
{
- free_parser();
+ /* free_parser(); */
+ ;
}
else
{
@@ -953,6 +992,7 @@ POOL_STATUS Parse(POOL_CONNECTION *frontend,
if (portal == NULL)
{
pool_error("Parse: create_portal() failed");
+ free_parser();
return POOL_END;
}
@@ -987,6 +1027,37 @@ POOL_STATUS Parse(POOL_CONNECTION *frontend,
if (REPLICATION)
{
+ char *rewrite_query;
+ bool rewrite_to_params = true;
+
+ /*
+ * rewrite `now()'.
+ * if stmt is unnamed, we rewrite `now()' to timestamp constant.
+ * else we rewrite `now()' to params and expand that at Bind
+ * message.
+ */
+ if (*name == '\0')
+ rewrite_to_params = false;
+ portal->num_tsparams = 0;
+ rewrite_query = rewrite_timestamp(backend, node, rewrite_to_params, portal);
+ if (rewrite_query != NULL)
+ {
+ string = palloc(strlen(name) + strlen(rewrite_query) + 2);
+ strcpy(string, name);
+ strcpy(string + strlen(name) + 1, rewrite_query);
+ memcpy(string + strlen(name) + strlen(rewrite_query) + 2,
+ stmt + strlen(stmt) + 1,
+ len - (strlen(name) + strlen(stmt) + 2));
+
+ len = len - strlen(stmt) + strlen(rewrite_query);
+ name = string;
+ stmt = string + strlen(name) + 1;
+ pool_debug("rewrite query %s %s len=%d", name, stmt, len);
+ }
+ }
+
+ if (REPLICATION)
+ {
char kind;
if (TSTATE(backend) != 'T')
@@ -1003,9 +1074,16 @@ POOL_STATUS Parse(POOL_CONNECTION *frontend,
kind = pool_read_kind(backend);
if (kind != 'Z')
+ {
+ free_parser();
return POOL_END;
+ }
+
if (ReadyForQuery(frontend, backend, 0) != POOL_CONTINUE)
+ {
+ free_parser();
return POOL_END;
+ }
}
if (is_strict_query(node))
@@ -1017,17 +1095,22 @@ POOL_STATUS Parse(POOL_CONNECTION *frontend,
status = insert_lock(frontend, backend, stmt, (InsertStmt *)node);
if (status != POOL_CONTINUE)
{
+ free_parser();
return status;
}
}
}
- free_parser();
}
/* send to master node */
if (send_extended_protocol_message(backend, MASTER_NODE_ID,
"P", len, string))
+ {
+ free_parser();
return POOL_END;
+ }
+
+ free_parser();
if (REPLICATION || PARALLEL_MODE || MASTER_SLAVE)
{
View
3  pool_proto_modules.h
@@ -1,7 +1,7 @@
/* -*-pgsql-c-*- */
/*
*
- * $Header: /cvsroot/pgpool/pgpool-II/pool_proto_modules.h,v 1.6 2009/10/03 11:57:00 t-ishii Exp $
+ * $Header: /cvsroot/pgpool/pgpool-II/pool_proto_modules.h,v 1.7 2009/11/10 10:03:10 t-ishii Exp $
*
* pgpool: a language independent connection pool server for PostgreSQL
* written by Tatsuo Ishii
@@ -42,6 +42,7 @@ typedef struct {
Node *stmt; /* parse tree for prepared statement */
char *sql_string; /* original SQL statement */
POOL_MEMORY_POOL *prepare_ctxt; /* memory context for parse tree */
+ int num_tsparams;
} Portal;
/*
View
1,056 pool_timestamp.c
@@ -0,0 +1,1056 @@
+/* -*-pgsql-c-*- */
+/*
+ * $Header: /cvsroot/pgpool/pgpool-II/pool_timestamp.c,v 1.1 2009/11/10 10:03:10 t-ishii Exp $
+ *
+ * pgpool: a language independent connection pool server for PostgreSQL
+ * written by Tatsuo Ishii
+ *
+ * Copyright (c) 2003-2009 PgPool Global Development Group
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that copyright notice and this permission
+ * notice appear in supporting documentation, and that the name of the
+ * author not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The author makes no representations about the
+ * suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ */
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pool.h"
+#include "pool_timestamp.h"
+#include "parser/parsenodes.h"
+#include "parser/pool_memory.h"
+
+#define Assert(x)
+
+typedef struct {
+ char *attrname;
+ int use_timestamp;
+} TSAttr;
+
+typedef struct {
+ int relnatts;
+ TSAttr attr[1];
+} TSRel;
+
+typedef struct {
+ A_Const *ts_const;
+ POOL_CONNECTION_POOL *backend;
+ char *relname;
+ int num_params; /* num of original params (for Parse) */
+ bool rewrite_to_params;
+ bool rewrite; /* has rewritten? */
+ List *params; /* list of additional params */
+} TSRewriteContext;
+
+static void *ts_register_func(POOL_SELECT_RESULT *res);
+static void *ts_unregister_func(void *data);
+static TSRel *relcache_lookup(TSRewriteContext *ctx);
+static bool isStringConst(Node *node, const char *str);
+static bool isSystemType(Node *node, const char *name);
+static bool rewrite_timestamp_walker(Node *node, void *context);
+static bool rewrite_timestamp_insert(InsertStmt *i_stmt, TSRewriteContext *ctx);
+static bool rewrite_timestamp_update(UpdateStmt *u_stmt, TSRewriteContext *ctx);
+static char *get_current_timestamp(POOL_CONNECTION_POOL *backend);
+static Node *makeTsExpr(TSRewriteContext *ctx);
+bool raw_expression_tree_walker(Node *node, bool (*walker) (), void *context);
+
+#define MAX_RELCACHE 32
+POOL_RELCACHE *ts_relcache;
+
+
+static void *
+ts_register_func(POOL_SELECT_RESULT *res)
+{
+ TSRel *rel;
+ int i;
+
+ if (res->numrows == 0)
+ return NULL;
+
+ rel = (TSRel *) malloc(sizeof(TSRel) + sizeof(TSAttr) * (res->numrows - 1));
+
+ for (i = 0; i < res->numrows; i++)
+ {
+ rel->attr[i].attrname = strdup(res->data[i * 2]);
+ rel->attr[i].use_timestamp = *(res->data[i * 2 + 1]) == 't';
+ pool_debug("attrname %s use_timestamp = %d",
+ rel->attr[i].attrname, rel->attr[i].use_timestamp);
+ }
+
+ rel->relnatts = res->numrows;
+ return (void *) rel;
+}
+
+
+static void *
+ts_unregister_func(void *data)
+{
+ TSRel *rel = (TSRel *) data;
+
+ if (rel == NULL)
+ return NULL;
+
+ free(rel);
+ return rel;
+}
+
+
+static TSRel*
+relcache_lookup(TSRewriteContext *ctx)
+{
+#define ATTRDEFQUERY "SELECT attname, coalesce(d.adsrc = 'now()' OR d.adsrc LIKE '%%''now''::text%%', false)" \
+ " FROM pg_catalog.pg_class c, pg_catalog.pg_attribute a " \
+ " LEFT JOIN pg_catalog.pg_attrdef d ON (a.attrelid = d.adrelid AND a.attnum = d.adnum)" \
+ " WHERE c.oid = a.attrelid AND a.attnum >= 1 AND a.attisdropped = 'f' AND c.relname = '%s'" \
+ " ORDER BY a.attnum"
+
+ if (!ts_relcache)
+ {
+ ts_relcache = pool_create_relcache(MAX_RELCACHE, ATTRDEFQUERY, ts_register_func, ts_unregister_func, false);
+
+ if (ts_relcache == NULL)
+ {
+ pool_error("relcache_lookup: pool_create_relcache error");
+ return NULL;
+ }
+ }
+
+ return (TSRel *) pool_search_relcache(ts_relcache, ctx->backend, ctx->relname);
+}
+
+
+static Node *
+makeTsExpr(TSRewriteContext *ctx)
+{
+ ParamRef *param;
+
+ if (!ctx->rewrite_to_params)
+ return (Node *) ctx->ts_const;
+
+ param = makeNode(ParamRef);
+ param->number = 0;
+ ctx->params = lappend(ctx->params, param);
+ return (Node *) param;
+}
+
+
+static bool
+isStringConst(Node *node, const char *str)
+{
+ A_Const *a_const;
+ Value val;
+
+ if (!IsA(node, A_Const))
+ return false;
+
+ a_const = (A_Const *) node;
+ val = a_const->val;
+
+ if (val.type == T_String && strcmp(str, val.val.str) == 0)
+ return true;
+
+ return false;
+}
+
+
+static bool
+isSystemType(Node *node, const char *name)
+{
+ TypeName *typename;
+
+ if (!IsA(node, TypeName))
+ return false;
+
+ typename = (TypeName *) node;
+ if (list_length(typename->names) == 2 &&
+ strcmp("pg_catalog", strVal(linitial(typename->names))) == 0 &&
+ strcmp(name, strVal(lsecond(typename->names))) == 0)
+ return true;
+
+ return false;
+}
+
+
+static bool
+isSystemTypeCast(Node *node, const char *name)
+{
+ TypeCast *typecast;
+
+ if (!IsA(node, TypeCast))
+ return false;
+
+ typecast = (TypeCast *) node;
+ return isSystemType((Node *) typecast->typename, name);
+}
+
+/*
+ * walker function for raw_expression_tree_walker
+ */
+static bool
+rewrite_timestamp_walker(Node *node, void *context)
+{
+ TSRewriteContext *ctx = (TSRewriteContext *) context;
+
+ if (node == NULL)
+ return false;
+
+ if (nodeTag(node) == T_FuncCall)
+ {
+ /* `now()' FuncCall */
+ FuncCall *fcall = (FuncCall *) node;
+
+ if (list_length(fcall->funcname) == 1 &&
+ strcmp("now", strVal(linitial(fcall->funcname))) == 0)
+ {
+ extern List *SystemFuncName(char *name);
+
+ fcall->funcname = SystemFuncName("timestamptz");
+ fcall->args = list_make1(makeTsExpr(ctx));
+ ctx->rewrite = true;
+ }
+ }
+ else if (IsA(node, TypeCast))
+ {
+ /* CURRENT_DATE, CURRENT_TIME, LOCALTIMESTAMP, LOCALTIME etc.*/
+ TypeCast *tc = (TypeCast *) node;
+
+ if ((isSystemType((Node *) tc->typename, "date") ||
+ isSystemType((Node *) tc->typename, "timestamp") ||
+ isSystemType((Node *) tc->typename, "timestamptz") ||
+ isSystemType((Node *) tc->typename, "time") ||
+ isSystemType((Node *) tc->typename, "timetz")))
+ {
+ /* rewrite `'now'::timestamp' and `'now'::text::timestamp' both */
+ if (isSystemTypeCast(tc->arg, "text"))
+ tc = (TypeCast *) tc->arg;
+
+ if (isStringConst(tc->arg, "now"))
+ {
+ tc->arg = (Node *) makeTsExpr(ctx);
+ ctx->rewrite = true;
+ }
+ }
+ }
+ else if (IsA(node, ParamRef))
+ {
+ ParamRef *param = (ParamRef *) node;
+
+ /* count how many params in original query */
+ if (ctx->num_params < param->number)
+ ctx->num_params = param->number;
+ }
+
+ return raw_expression_tree_walker(node, rewrite_timestamp_walker, context);
+}
+
+
+/*
+ * Get `now()' from MASTER node
+ */
+static char *
+get_current_timestamp(POOL_CONNECTION_POOL *backend)
+{
+ POOL_SELECT_RESULT *res;
+ POOL_STATUS status;
+ static char timestamp[32];
+
+ status = do_query(MASTER(backend), "SELECT now()", &res);
+ if (status != POOL_CONTINUE)
+ {
+ pool_error("get_current_timestamp: do_query faild");
+ return NULL;
+ }
+
+ if (res->numrows != 1)
+ {
+ free_select_result(res);
+ return NULL;
+ }
+
+ strlcpy(timestamp, res->data[0], sizeof(timestamp));
+
+ free_select_result(res);
+ return timestamp;
+}
+
+
+/*
+ * rewrite InsertStmt
+ */
+static bool
+rewrite_timestamp_insert(InsertStmt *i_stmt, TSRewriteContext *ctx)
+{
+ int i;
+ bool rewrite = false;
+ TSRel *relcache;
+
+
+ if (i_stmt->selectStmt == NULL)
+ {
+ List *values = NIL;
+ SelectStmt *selectStmt = makeNode(SelectStmt);
+
+ relcache = relcache_lookup(ctx);
+ if (relcache == NULL)
+ return false;
+
+ /*
+ * INSERT INTO rel DEFAULT VALUES
+ * rewrite to:
+ * INSERT INTO rel VALUES (DEFAULT, '2009-..',...)
+ */
+
+ for (i = 0; i < relcache->relnatts; i++)
+ {
+ if (relcache->attr[i].use_timestamp)
+ {
+ rewrite = true;
+ values = lappend(values, makeTsExpr(ctx));
+ }
+ else
+ values = lappend(values, makeNode(SetToDefault));
+ }
+ if (rewrite)
+ {
+ selectStmt->valuesLists = list_make1(values);
+ i_stmt->selectStmt = (Node *) selectStmt;
+ }
+
+ return rewrite;
+ }
+ else if (IsA(i_stmt->selectStmt, SelectStmt))
+ {
+ SelectStmt *selectStmt = (SelectStmt *) i_stmt->selectStmt;
+ ListCell *lc_row, *lc_val, *lc_col;
+
+ /*
+ * Rewrite `now()' call to timestamp literal.
+ */
+ raw_expression_tree_walker(
+ (Node *) selectStmt,
+ rewrite_timestamp_walker, (void *) ctx);
+
+ rewrite = ctx->rewrite;
+
+ /*
+ * if `INSERT INTO rel SELECT ...'
+ */
+ if (selectStmt->valuesLists == NIL)
+ return rewrite;
+
+ relcache = relcache_lookup(ctx);
+ if (relcache == NULL)
+ return false;
+
+ if (i_stmt->cols == NIL)
+ {
+ /*
+ * INSERT INTO rel VALUES (...)
+ *
+ * CREATE TABLE r1 (
+ * i1 int,
+ * t1 timestamp default now(),
+ * i2 int,
+ * t2 timestamp default now(),
+ * )
+ *
+ * INSERT INTO r1 VALUES (1, DEFAULT);
+ * rewrite to:
+ * INSERT INTO r1 VALUES (1, '20xx-xx-xx...', DEFAULT, '2..')
+ */
+ foreach (lc_row, selectStmt->valuesLists)
+ {
+ List *values = lfirst(lc_row);
+
+ i = 0;
+ foreach (lc_val, values)
+ {
+ if (relcache->attr[i].use_timestamp == true && IsA(lfirst(lc_val), SetToDefault))
+ {
+ rewrite = true;
+ lfirst(lc_val) = makeTsExpr(ctx);
+ }
+ i++;
+ }
+
+ /* fill rest columns */
+ for (; i < relcache->relnatts; i++)
+ {
+ if (relcache->attr[i].use_timestamp == true)
+ {
+ rewrite = true;
+ values = lappend(values, makeTsExpr(ctx));
+ }
+ else
+ values = lappend(values, makeNode(SetToDefault));
+ }
+ }
+ }
+ else
+ {
+ /*
+ * INSERT INTO rel(col1, col2) VALUES (val, val2)
+ *
+ * if timestamp column does not given by column list
+ * add colname to column list and add timestamp to values list.
+ */
+ int append_columns = 0;
+ ResTarget *col;
+
+ for (i = 0; i < relcache->relnatts; i++)
+ {
+ if (relcache->attr[i].use_timestamp == false)
+ continue;
+
+ foreach (lc_col, i_stmt->cols)
+ {
+ col = lfirst(lc_col);
+
+ if (strcmp(relcache->attr[i].attrname, col->name) == 0)
+ break;
+ }
+
+ if (lc_col == NULL)
+ {
+ /* column not found in query, append it.*/
+ rewrite = true;
+ col = makeNode(ResTarget);
+ col->name = relcache->attr[i].attrname;
+ col->indirection = NIL;
+ col->val = NULL;
+ i_stmt->cols = lappend(i_stmt->cols, col);
+ append_columns++;
+ }
+ }
+
+ foreach (lc_row, selectStmt->valuesLists)
+ {
+ List *values = lfirst(lc_row);
+
+ /* replace DEFAULT to literal */
+ forboth (lc_col, i_stmt->cols, lc_val, values)
+ {
+ col = lfirst(lc_col);
+ for (i = 0; i < relcache->relnatts; i++)
+ {
+ if (strcmp(relcache->attr[i].attrname, col->name) == 0)
+ break;
+ }
+
+ if (relcache->attr[i].use_timestamp == true && IsA(lfirst(lc_val), SetToDefault))
+ {
+ rewrite = true;
+ lfirst(lc_val) = makeTsExpr(ctx);
+ }
+ }
+
+ /* add ts_const to values list */
+ for (i = 0; i < append_columns; i++)
+ {
+ values = lappend(values, makeTsExpr(ctx));
+ }
+ }
+ }
+ }
+
+ return rewrite;
+}
+
+
+/*
+ * rewrite UpdateStmt
+ */
+static bool
+rewrite_timestamp_update(UpdateStmt *u_stmt, TSRewriteContext *ctx)
+{
+ TSRel *relcache = NULL;
+ ListCell *lc;
+ bool rewrite;
+
+ /* rewrite "update ... set col1 = now() */
+ raw_expression_tree_walker(
+ (Node *) u_stmt->targetList,
+ rewrite_timestamp_walker, (void *) ctx);
+
+ raw_expression_tree_walker(
+ (Node *) u_stmt->whereClause,
+ rewrite_timestamp_walker, (void *) ctx);
+
+ raw_expression_tree_walker(
+ (Node *) u_stmt->fromClause,
+ rewrite_timestamp_walker, (void *) ctx);
+
+ rewrite = ctx->rewrite;
+
+ /* rewrite "update ... set col1 = default" */
+ foreach (lc, u_stmt->targetList)
+ {
+ ResTarget *res = (ResTarget *) lfirst(lc);
+ int i;
+
+ if (IsA(res->val, SetToDefault))
+ {
+ relcache = relcache_lookup(ctx);
+ if (relcache == NULL)
+ return false;
+
+ for (i = 0; i < relcache->relnatts; i++)
+ {
+ if (strcmp(relcache->attr[i].attrname, res->name) == 0)
+ {
+ if (relcache->attr[i].use_timestamp)
+ {
+ res->val = (Node *) makeTsExpr(ctx);
+ rewrite = true;
+ }
+ break;
+ }
+ }
+ }
+ }
+ return rewrite;
+}
+
+/*
+ * Rewrite `now()' to timestamp literal.
+ * returns query string as palloced string, or NULL if not to need rewrite.
+ */
+char *
+rewrite_timestamp(POOL_CONNECTION_POOL *backend, Node *node,
+ bool rewrite_to_params, Portal *portal)
+{
+ TSRewriteContext ctx;
+ Node *stmt;
+ bool rewrite = false;
+ char *timestamp;
+ char *rewrite_query;
+
+ if (node == NULL)
+ return NULL;
+
+ if (!REPLICATION)
+ return NULL;
+
+ /* init context */
+ ctx.ts_const = makeNode(A_Const);
+ ctx.ts_const->val.type = T_String;
+ ctx.rewrite_to_params = rewrite_to_params;
+ ctx.backend = backend;
+ ctx.num_params = 0;
+ ctx.rewrite = false;
+ ctx.params = NIL;
+
+ /*
+ * Prepare?
+ */
+ if (IsA(node, PrepareStmt))
+ {
+ stmt = ((PrepareStmt *) node)->query;
+ ctx.rewrite_to_params = true;
+ }
+ else
+ stmt = node;
+
+ if (IsA(stmt, InsertStmt))
+ {
+ InsertStmt *i_stmt = (InsertStmt *) stmt;
+ ctx.relname = nodeToString(i_stmt->relation);
+ rewrite = rewrite_timestamp_insert(i_stmt, &ctx);
+ }
+ else if (IsA(stmt, UpdateStmt))
+ {
+ UpdateStmt *u_stmt = (UpdateStmt *) stmt;
+ ctx.relname = nodeToString(u_stmt->relation);
+ rewrite = rewrite_timestamp_update(u_stmt, &ctx);
+ }
+ else if (IsA(stmt, DeleteStmt))
+ {
+ DeleteStmt *d_stmt = (DeleteStmt *) stmt;
+ raw_expression_tree_walker(
+ (Node *) d_stmt->usingClause,
+ rewrite_timestamp_walker, (void *) &ctx);
+
+ raw_expression_tree_walker(
+ (Node *) d_stmt->whereClause,
+ rewrite_timestamp_walker, (void *) &ctx);
+ rewrite = ctx.rewrite;
+ }
+ else if (IsA(stmt, ExecuteStmt))
+ {
+ ExecuteStmt *e_stmt = (ExecuteStmt *) stmt;
+
+ /* rewrite params */
+ raw_expression_tree_walker(
+ (Node *) e_stmt->params,
+ rewrite_timestamp_walker, (void *) &ctx);
+
+ rewrite = ctx.rewrite;
+
+ /* add params */
+ if (portal)
+ {
+ int i;
+
+ for (i = 0; i < portal->num_tsparams; i++)
+ {
+ e_stmt->params = lappend(e_stmt->params, ctx.ts_const);
+ rewrite = true;
+ }
+ }
+ }
+ else
+ ;
+
+ if (!rewrite)
+ return NULL;
+
+ if (ctx.rewrite_to_params && portal)
+ {
+ ListCell *lc;
+ int num = ctx.num_params + 1;
+
+ /* renumber params */
+ foreach (lc, ctx.params)
+ {
+ ParamRef *param = (ParamRef *) lfirst(lc);
+ param->number = num++;
+ }
+
+ /* save to portal */
+ portal->num_tsparams = list_length(ctx.params);
+ }
+ else
+ {
+ timestamp = get_current_timestamp(backend);
+ if (timestamp == NULL)
+ {
+ pool_error("rewrite_timestamp: could not get current timestamp");
+ return NULL;
+ }
+
+ ctx.ts_const->val.val.str = timestamp;
+ }
+ rewrite_query = nodeToString(node);
+
+ return rewrite_query;
+}
+
+
+/*
+ * rewrite Bind message to add parameter
+ */
+char *
+bind_rewrite_timestamp(POOL_CONNECTION_POOL *backend, Portal *portal,
+ const char *orig_msg, int *len)
+{
+ int16 tmp2,
+ num_params;
+ int32 tmp4;
+ int i,
+ ts_len,
+ copy_len;
+ const char *copy_from;
+ char *ts,
+ *copy_to,
+ *new_msg;
+
+ ts = get_current_timestamp(backend);
+ if (ts == NULL)
+ {
+ pool_error("bind_rewrite_timestamp: could not get current timestamp");
+ return NULL;
+ }
+
+ ts_len = strlen(ts);
+
+ *len += (strlen(ts) + sizeof(int32)) * portal->num_tsparams;
+ /*sendlen = htonl(*len + 4);*/
+ new_msg = copy_to = (char *) malloc(*len); /* XXX how to free */
+ copy_from = orig_msg;
+
+ /* portal_name */
+ copy_len = strlen(copy_from) + 1;
+ memcpy(copy_to, copy_from, copy_len);
+ copy_to += copy_len; copy_from += copy_len;
+
+ /* stmt_name */
+ copy_len = strlen(copy_from) + 1;
+ memcpy(copy_to, copy_from, copy_len);
+ copy_to += copy_len; copy_from += copy_len;
+
+ /* format code */
+ memcpy(&tmp2, copy_from, sizeof(int16));
+ copy_len = sizeof(int16);
+ tmp2 = ntohs(tmp2);
+ copy_len += tmp2 * sizeof(int16);
+ memcpy(copy_to, copy_from, copy_len);
+ copy_to += copy_len; copy_from += copy_len;
+
+ /* num params */
+ memcpy(&tmp2, copy_from, sizeof(int16));
+ copy_len = sizeof(int16);
+ num_params = ntohs(tmp2);
+ tmp2 = htons(num_params + portal->num_tsparams);
+ memcpy(copy_to, &tmp2, sizeof(int16));
+ copy_to += copy_len; copy_from += copy_len;
+
+ /* params */
+ copy_len = 0;
+ for (i = 0; i < num_params; i++)
+ {
+ memcpy(&tmp4, copy_from + copy_len, sizeof(int32));
+ copy_len += sizeof(int32);
+ copy_len += ntohl(tmp4);
+ }
+ memcpy(copy_to, copy_from, copy_len);
+ copy_to += copy_len; copy_from += copy_len;
+
+ tmp4 = htonl(ts_len);
+ for (i = 0; i < portal->num_tsparams; i++)
+ {
+ memcpy(copy_to, &tmp4, sizeof(int32));
+ copy_to += sizeof(int32);
+ memcpy(copy_to, ts, ts_len);
+ copy_to += ts_len;
+ }
+
+ /* result format code */
+ memcpy(&tmp2, copy_from, sizeof(int16));
+ copy_len = sizeof(int16);
+ copy_len += sizeof(int16) * ntohs(tmp2);
+ memcpy(copy_to, copy_from, copy_len);
+
+ return new_msg;
+}
+
+
+/* from nodeFuncs.c start */
+
+/*
+ * raw_expression_tree_walker --- walk raw parse trees
+ *
+ * This has exactly the same API as expression_tree_walker, but instead of
+ * walking post-analysis parse trees, it knows how to walk the node types
+ * found in raw grammar output. (There is not currently any need for a
+ * combined walker, so we keep them separate in the name of efficiency.)
+ * Unlike expression_tree_walker, there is no special rule about query
+ * boundaries: we descend to everything that's possibly interesting.
+ *
+ * Currently, the node type coverage extends to SelectStmt and everything
+ * that could appear under it, but not other statement types.
+ */
+bool
+ raw_expression_tree_walker(Node *node, bool (*walker) (), void *context)
+{
+ ListCell *temp;
+
+ /*
+ * The walker has already visited the current node, and so we need only
+ * recurse into any sub-nodes it has.
+ */
+ if (node == NULL)
+ return false;
+
+ /* Guard against stack overflow due to overly complex expressions */
+ /*
+ check_stack_depth();
+ */
+
+ switch (nodeTag(node))
+ {
+ case T_SetToDefault:
+ case T_CurrentOfExpr:
+ case T_Integer:
+ case T_Float:
+ case T_String:
+ case T_BitString:
+ case T_Null:
+ case T_ParamRef:
+ case T_A_Const:
+ case T_A_Star:
+ /* primitive node types with no subnodes */
+ break;
+ case T_Alias:
+ /* we assume the colnames list isn't interesting */
+ break;
+ case T_RangeVar:
+ return walker(((RangeVar *) node)->alias, context);
+ case T_SubLink:
+ {
+ SubLink *sublink = (SubLink *) node;
+
+ if (walker(sublink->testexpr, context))
+ return true;
+ /* we assume the operName is not interesting */
+ if (walker(sublink->subselect, context))
+ return true;
+ }
+ break;
+ case T_CaseExpr:
+ {
+ CaseExpr *caseexpr = (CaseExpr *) node;
+
+ if (walker(caseexpr->arg, context))
+ return true;
+ /* we assume walker doesn't care about CaseWhens, either */
+ foreach(temp, caseexpr->args)
+ {
+ CaseWhen *when = (CaseWhen *) lfirst(temp);
+
+ Assert(IsA(when, CaseWhen));
+ if (walker(when->expr, context))
+ return true;
+ if (walker(when->result, context))
+ return true;
+ }
+ if (walker(caseexpr->defresult, context))
+ return true;
+ }
+ break;
+ case T_RowExpr:
+ /* Assume colnames isn't interesting */
+ return walker(((RowExpr *) node)->args, context);
+ case T_CoalesceExpr:
+ return walker(((CoalesceExpr *) node)->args, context);
+ case T_MinMaxExpr:
+ return walker(((MinMaxExpr *) node)->args, context);
+ case T_XmlExpr:
+ {
+ XmlExpr *xexpr = (XmlExpr *) node;
+
+ if (walker(xexpr->named_args, context))
+ return true;
+ /* we assume walker doesn't care about arg_names */
+ if (walker(xexpr->args, context))
+ return true;
+ }
+ break;
+ case T_NullTest:
+ return walker(((NullTest *) node)->arg, context);
+ case T_BooleanTest:
+ return walker(((BooleanTest *) node)->arg, context);
+ case T_JoinExpr:
+ {
+ JoinExpr *join = (JoinExpr *) node;
+
+ if (walker(join->larg, context))
+ return true;
+ if (walker(join->rarg, context))
+ return true;
+ if (walker(join->quals, context))
+ return true;
+ if (walker(join->alias, context))
+ return true;
+ /* using list is deemed uninteresting */
+ }
+ break;
+ case T_IntoClause:
+ {
+ IntoClause *into = (IntoClause *) node;
+
+ if (walker(into->rel, context))
+ return true;
+ /* colNames, options are deemed uninteresting */
+ }
+ break;
+ case T_List:
+ foreach(temp, (List *) node)
+ {
+ if (walker((Node *) lfirst(temp), context))
+ return true;
+ }
+ break;
+ case T_SelectStmt:
+ {
+ SelectStmt *stmt = (SelectStmt *) node;
+
+ if (walker(stmt->distinctClause, context))
+ return true;
+ if (walker(stmt->intoClause, context))
+ return true;
+ if (walker(stmt->targetList, context))
+ return true;
+ if (walker(stmt->fromClause, context))
+ return true;
+ if (walker(stmt->whereClause, context))
+ return true;
+ if (walker(stmt->groupClause, context))
+ return true;
+ if (walker(stmt->havingClause, context))
+ return true;
+ if (walker(stmt->windowClause, context))
+ return true;
+ if (walker(stmt->withClause, context))
+ return true;
+ if (walker(stmt->valuesLists, context))
+ return true;
+ if (walker(stmt->sortClause, context))
+ return true;
+ if (walker(stmt->limitOffset, context))
+ return true;
+ if (walker(stmt->limitCount, context))
+ return true;
+ if (walker(stmt->lockingClause, context))
+ return true;
+ if (walker(stmt->larg, context))
+ return true;
+ if (walker(stmt->rarg, context))
+ return true;
+ }
+ break;
+ case T_A_Expr:
+ {
+ A_Expr *expr = (A_Expr *) node;
+
+ if (walker(expr->lexpr, context))
+ return true;
+ if (walker(expr->rexpr, context))
+ return true;
+ /* operator name is deemed uninteresting */
+ }
+ break;
+ case T_ColumnRef:
+ /* we assume the fields contain nothing interesting */
+ break;
+ case T_FuncCall:
+ {
+ FuncCall *fcall = (FuncCall *) node;
+
+ if (walker(fcall->args, context))
+ return true;
+ if (walker(fcall->over, context))
+ return true;
+ /* function name is deemed uninteresting */
+ }
+ break;
+ case T_A_Indices:
+ {
+ A_Indices *indices = (A_Indices *) node;
+
+ if (walker(indices->lidx, context))
+ return true;
+ if (walker(indices->uidx, context))
+ return true;
+ }
+ break;
+ case T_A_Indirection:
+ {
+ A_Indirection *indir = (A_Indirection *) node;
+
+ if (walker(indir->arg, context))
+ return true;
+ if (walker(indir->indirection, context))
+ return true;
+ }
+ break;
+ case T_A_ArrayExpr:
+ return walker(((A_ArrayExpr *) node)->elements, context);
+ case T_ResTarget:
+ {
+ ResTarget *rt = (ResTarget *) node;
+
+ if (walker(rt->indirection, context))
+ return true;
+ if (walker(rt->val, context))
+ return true;
+ }
+ break;
+ case T_TypeCast:
+ {
+ TypeCast *tc = (TypeCast *) node;
+
+ if (walker(tc->arg, context))
+ return true;
+ if (walker(tc->typename, context))
+ return true;
+ }
+ break;
+ case T_SortBy:
+ return walker(((SortBy *) node)->node, context);
+ case T_WindowDef:
+ {
+ WindowDef *wd = (WindowDef *) node;
+
+ if (walker(wd->partitionClause, context))
+ return true;
+ if (walker(wd->orderClause, context))
+ return true;
+ }
+ break;
+ case T_RangeSubselect:
+ {
+ RangeSubselect *rs = (RangeSubselect *) node;
+
+ if (walker(rs->subquery, context))
+ return true;
+ if (walker(rs->alias, context))
+ return true;
+ }
+ break;
+ case T_RangeFunction:
+ {
+ RangeFunction *rf = (RangeFunction *) node;
+
+ if (walker(rf->funccallnode, context))
+ return true;
+ if (walker(rf->alias, context))
+ return true;
+ }
+ break;
+ case T_TypeName:
+ {
+ TypeName *tn = (TypeName *) node;
+
+ if (walker(tn->typmods, context))
+ return true;
+ if (walker(tn->arrayBounds, context))
+ return true;
+ /* type name itself is deemed uninteresting */
+ }
+ break;
+ case T_ColumnDef:
+ {
+ ColumnDef *coldef = (ColumnDef *) node;
+
+ if (walker(coldef->typename, context))
+ return true;
+ if (walker(coldef->raw_default, context))
+ return true;
+ /* for now, constraints are ignored */
+ }
+ break;
+ case T_LockingClause:
+ return walker(((LockingClause *) node)->lockedRels, context);
+ case T_XmlSerialize:
+ {
+ XmlSerialize *xs = (XmlSerialize *) node;
+
+ if (walker(xs->expr, context))
+ return true;
+ if (walker(xs->typename, context))
+ return true;
+ }
+ break;
+ case T_WithClause:
+ return walker(((WithClause *) node)->ctes, context);
+ case T_CommonTableExpr:
+ return walker(((CommonTableExpr *) node)->ctequery, context);
+ default:
+ /*
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(node));
+ */
+ break;
+ }
+ return false;
+}
+/* from nodeFuncs.c end */
+
View
10 pool_timestamp.h
@@ -0,0 +1,10 @@
+#ifndef POOL_TIMESTAMP_H
+#define POOL_TIMESTAMP_H
+#include "pool.h"
+#include "pool_proto_modules.h"
+#include "parser/nodes.h"
+
+char *rewrite_timestamp(POOL_CONNECTION_POOL *backend, Node *node, bool rewrite_to_params, Portal *portal);
+char *bind_rewrite_timestamp(POOL_CONNECTION_POOL *backend, Portal *portal, const char *orig_msg, int *len);
+
+#endif /* POOL_TIMESTAMP_H */
View
31 test/timestamp/Makefile
@@ -0,0 +1,31 @@
+
+PROGRAM=timestamp-test
+topsrc_dir=../..
+CPPFLAGS=-I$(topsrc_dir) -I$(shell pg_config --includedir)
+CFLAGS=-Wall -O0 -g
+
+OBJS=main.o \
+ $(topsrc_dir)/pool_timestamp.o \
+ $(topsrc_dir)/parser/libsql-parser.a
+
+all: all-pre $(PROGRAM)
+
+all-pre:
+ $(MAKE) -C $(topsrc_dir)/parser
+ $(MAKE) -C $(topsrc_dir) pool_timestamp.o
+
+$(PROGRAM): $(OBJS)
+ $(CC) $(OBJS) -o $(PROGRAM)
+
+main.o: main.c
+
+test: $(PROGRAM)
+ ./run-test parse_schedule
+
+clean:
+ -rm *.o
+ -rm $(PROGRAM)
+ -rm result/*.out
+ -rm test.diff
+
+.PHONY: all all-pre test clean
View
15 test/timestamp/expected/insert.out
@@ -0,0 +1,15 @@
+INSERT INTO "rel1" VALUES (DEFAULT,'2009-01-01 23:59:59.123456+09',DEFAULT,'2009-01-01 23:59:59.123456+09')
+INSERT INTO rel2 DEFAULT VALUES
+INSERT INTO rel2(c1) VALUES(1)
+INSERT INTO "rel1" VALUES (1,"timestamptz"('2009-01-01 23:59:59.123456+09'),2,'2009-01-01 23:59:59.123456+09'::text::date)
+INSERT INTO "rel1" VALUES (3,"timestamptz"('2009-01-01 23:59:59.123456+09'),4,'2009-01-01 23:59:59.123456+09'::text::timetz)
+INSERT INTO "rel1" VALUES (5,'2009-01-01 23:59:59.123456+09'::text::timestamptz(0),6,'2009-01-01 23:59:59.123456+09'::text::timetz(0))
+INSERT INTO "rel1" VALUES (7,'2009-01-01 23:59:59.123456+09'::text::timestamp,8,'2009-01-01 23:59:59.123456+09'::text::time)
+INSERT INTO "rel1" VALUES (9,'2009-01-01 23:59:59.123456+09'::text::timestamp(0),10,'2009-01-01 23:59:59.123456+09'::text::time(0))
+INSERT INTO "rel1" VALUES (11,'2009-01-01 23:59:59.123456+09',DEFAULT,'2009-01-01 23:59:59.123456+09')
+INSERT INTO "rel1"("c3", "c2", "c4") VALUES (1,'2009-01-01 23:59:59.123456+09','2009-01-01 23:59:59.123456+09')
+INSERT INTO "rel1"("c2", "c1", "c4") VALUES ('2000-1-1',1,'2009-01-01 23:59:59.123456+09')
+INSERT INTO "rel1"("c2", "c1", "c4") VALUES ('2009-01-01 23:59:59.123456+09',2,'2009-01-01 23:59:59.123456+09')
+INSERT INTO "rel1"("c2", "c1", "c4") VALUES ('2009-01-01 23:59:59.123456+09',3,'2009-01-01 23:59:59.123456+09'), ('2009-01-01 23:59:59.123456+09',4,'2009-01-01 23:59:59.123456+09'), ('2009-1-1',5,'2009-01-01 23:59:59.123456+09')
+INSERT INTO rel1(c1, c2, c4) VALUES(1, '2009-1-1', '2009-2-2')
+PREPARE "q" (int4) AS INSERT INTO "rel1"("c3", "c2", "c4") VALUES ($1,$2,$3)
View
3  test/timestamp/expected/misc.out
@@ -0,0 +1,3 @@
+DELETE FROM "rel1" WHERE ("c1"='2009-01-01 23:59:59.123456+09'::text::date )
+PREPARE "q" ("date") AS DELETE FROM "rel1" WHERE ( ("c1"=$1 ) AND ("c3"=$2::text::date ))
+EXECUTE "q" ("timestamptz"('2009-01-01 23:59:59.123456+09'))
View
6 test/timestamp/expected/update.out
@@ -0,0 +1,6 @@
+UPDATE "rel1" SET "c1" = DEFAULT, "c2" = '2009-01-01 23:59:59.123456+09'
+UPDATE rel2 SET c1 = DEFAULT, c2 = DEFAULT
+UPDATE "rel1" SET "c1" = "timestamptz"('2009-01-01 23:59:59.123456+09'), "c2" = '2009-01-01 23:59:59.123456+09'::text::date
+UPDATE "rel1" SET "c3" = "timestamptz"('2009-01-01 23:59:59.123456+09'), "c4" = '2009-01-01 23:59:59.123456+09'::text::timetz
+UPDATE "rel1" SET "c1" = '2009-01-01 23:59:59.123456+09'::text::timestamp, "c2" = '2009-01-01 23:59:59.123456+09'::text::time
+PREPARE "q" (int4) AS UPDATE "rel1" SET "c1" = $1, "c2" = $2::text::date
View
15 test/timestamp/input/insert.sql
@@ -0,0 +1,15 @@
+INSERT INTO rel1 DEFAULT VALUES
+INSERT INTO rel2 DEFAULT VALUES
+INSERT INTO rel2(c1) VALUES(1)
+INSERT INTO rel1 VALUES(1, now(), 2, CURRENT_DATE)
+INSERT INTO rel1 VALUES(3, CURRENT_TIMESTAMP, 4, CURRENT_TIME)
+INSERT INTO rel1 VALUES(5, CURRENT_TIMESTAMP(0), 6, CURRENT_TIME(0))
+INSERT INTO rel1 VALUES(7, LOCALTIMESTAMP, 8, LOCALTIME)
+INSERT INTO rel1 VALUES(9, LOCALTIMESTAMP(0), 10, LOCALTIME(0))
+INSERT INTO rel1 VALUES(11, DEFAULT);
+INSERT INTO rel1(c3) VALUES(1)
+INSERT INTO rel1(c2, c1) VALUES('2000-1-1', 1)
+INSERT INTO rel1(c2, c1) VALUES(DEFAULT, 2)
+INSERT INTO rel1(c2, c1) VALUES(DEFAULT, 3), (DEFAULT, 4), ('2009-1-1', 5)
+INSERT INTO rel1(c1, c2, c4) VALUES(1, '2009-1-1', '2009-2-2')
+PREPARE q(int) AS INSERT INTO rel1(c3) VALUES($1)
View
3  test/timestamp/input/misc.sql
@@ -0,0 +1,3 @@
+DELETE FROM rel1 WHERE c1 = CURRENT_DATE
+PREPARE q(date) AS DELETE FROM rel1 WHERE c1 = $1 AND c3 = CURRENT_DATE
+EXECUTE q(now())
View
6 test/timestamp/input/update.sql
@@ -0,0 +1,6 @@
+UPDATE rel1 SET c1 = DEFAULT, c2 = DEFAULT
+UPDATE rel2 SET c1 = DEFAULT, c2 = DEFAULT
+UPDATE rel1 SET c1 = now(), c2 = CURRENT_DATE
+UPDATE rel1 SET (c3, c4) = (CURRENT_TIMESTAMP, CURRENT_TIME)
+UPDATE rel1 SET c1 = LOCALTIMESTAMP, c2 = LOCALTIME
+PREPARE q(int) AS UPDATE rel1 SET c1 = $1, c2 = CURRENT_DATE
View
117 test/timestamp/main.c
@@ -0,0 +1,117 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "pool.h"
+#include "pool_proto_modules.h"
+#include "pool_timestamp.h"
+#include "parser/parser.h"
+
+/* for get_current_timestamp() (MASTER() macro) */
+POOL_REQUEST_INFO _req_info;
+POOL_REQUEST_INFO *Req_info = &_req_info;
+int selected_slot = 0; /* selected DB node */
+int in_load_balance = 1; /* non 0 if in load balance mode */
+POOL_CONFIG _pool_config;
+POOL_CONFIG *pool_config = &_pool_config;
+
+typedef struct {
+ char *attrname;
+ int use_timestamp;
+} TSAttr;
+
+typedef struct {
+ int relnatts;
+ TSAttr attr[4];
+} TSRel;
+
+
+TSRel rc[2] = {
+ { 4, {
+ { "c1", 0 },
+ { "c2", 1 },
+ { "c3", 0 },
+ { "c4", 1 }
+ } },
+ { 4, {
+ { "c1", 0 },
+ { "c2", 0 },
+ { "c3", 0 },
+ { "c4", 0 }
+ } }
+};
+
+POOL_RELCACHE *
+pool_create_relcache(int cachesize, char *sql, func_ptr register_func, func_ptr unregister_func, bool issessionlocal)
+{
+ return (POOL_RELCACHE *) 1;
+}
+
+void *
+pool_search_relcache(POOL_RELCACHE *relcache, POOL_CONNECTION_POOL *backend, char *table)
+{
+ if (strcmp(table, "\"rel1\"") == 0)
+ return (void *) &(rc[0]);
+ else
+ return (void *) &(rc[1]);
+}
+
+POOL_STATUS
+do_query(POOL_CONNECTION *backend, char *query, POOL_SELECT_RESULT **result) {
+ static POOL_SELECT_RESULT res;
+ static char *data[1] = {
+ "2009-01-01 23:59:59.123456+09"
+ };
+
+ res.numrows = 1;
+ res.data = data;
+
+ *result = &res;
+ return POOL_CONTINUE;
+}
+
+int
+main(int argc, char **argv)
+{
+ char *query;
+ List *tree;
+ ListCell *l;
+ Portal portal;
+ POOL_CONNECTION_POOL backend;
+ POOL_CONNECTION_POOL_SLOT slot;
+ backend.slots[0] = &slot;
+
+ pool_config->replication_enabled = 1;
+
+ if (argc != 2)
+ {
+ fprintf(stderr, "./timestmp-test query\n");
+ exit(1);
+ }
+
+ tree = raw_parser(argv[1]);
+ if (tree == NULL)
+ {
+ printf("syntax error: %s\n", argv[1]);
+ }
+ else
+ {
+ foreach(l, tree)
+ {
+ portal.num_tsparams = 0;
+ Node *node = (Node *) lfirst(l);
+ query = rewrite_timestamp(&backend, node, false, &portal);
+ if (query)
+ printf("%s\n", query);
+ else
+ printf("%s\n", argv[1]);
+
+ }
+ }
+ return 0;
+}
+
+void child_exit(int code) { exit (code); }
+void pool_error(const char *fmt,...) {}
+void pool_debug(const char *fmt,...) {}
+void pool_log(const char *fmt,...) {}
+void free_select_result(POOL_SELECT_RESULT *result) {}
View
3  test/timestamp/parse_schedule
@@ -0,0 +1,3 @@
+insert
+update
+misc
View
69 test/timestamp/run-test
@@ -0,0 +1,69 @@
+#! /usr/bin/env ruby
+
+# $Header: /cvsroot/pgpool/pgpool-II/test/timestamp/run-test,v 1.1 2009/11/10 10:03:10 t-ishii Exp $
+
+#
+# Usage¡§./run-test schedule
+# ignore a line at the beginning of '#'
+#
+
+INPUT_DIRECTORY="input"
+EXPECTED_DIRECTORY="expected"
+RESULT_DIRECTORY="result"
+TEST_PROGRAM="./timestamp-test"
+DIFF_FILE="test.diff"
+
+def escape_string str
+ str.gsub(/([\$\"\\])/) { "\\" + $1 }
+end
+
+if ARGV.size != 1
+ STDERR.puts "run-test schedule_file"
+ exit 1
+end
+
+file = ARGV.shift
+if !(File.exists? file)
+ STDERR.puts "run-test: file does not exist: #{file}"
+ exit 1
+end
+
+if !(File.exists? RESULT_DIRECTORY)
+ Dir.mkdir RESULT_DIRECTORY
+else
+ Dir["#{RESULT_DIRECTORY}/*.out"].each do |f|
+ File.unlink f
+ end
+end
+
+File.unlink DIFF_FILE if File.exists? DIFF_FILE
+
+begin
+ IO.foreach(file) do |testcase|
+ testcase.chomp!
+ if (/^\#/ =~ testcase or testcase == "")
+ next
+ end
+
+ print "testcase #{testcase}:\t"
+ begin
+ IO.foreach("#{INPUT_DIRECTORY}/#{testcase}.sql") do |test_sql|
+ test_sql.chomp!
+ system("#{TEST_PROGRAM} \"#{escape_string(test_sql)}\" >> #{RESULT_DIRECTORY}/#{testcase}.out\n")
+ end
+
+ system("diff -c #{EXPECTED_DIRECTORY}/#{testcase}.out #{RESULT_DIRECTORY}/#{testcase}.out >> #{DIFF_FILE}")
+
+ if ($? == 0)
+ print "OK\n"
+ else
+ print "FAILED\n"
+ end
+ rescue
+ print "FAILED\n"
+ end
+ end
+
+rescue
+ STDERR.puts "NG"
+end
Please sign in to comment.
Something went wrong with that request. Please try again.