diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index e02b0c8..1f5d50b 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1284,6 +1284,22 @@ include_dir 'conf.d'
+
+ allow_multiple_queries (boolean)
+
+ allow_multiple_queries configuration parameter
+
+
+
+
+ When this parameter is on, the PostgreSQL> server
+ allow multiple SQL commands without being a transaction block in the given string
+ in simple query protocol. setting this parameter off is use for providing
+ an additional defense against SQL-injection attacks.
+
+
+
+
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 4bc5bf3..de48b0c 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -2160,6 +2160,14 @@ PGresult *PQexec(PGconn *conn, const char *command);
of the last command executed from the string. Should one of the
commands fail, processing of the string stops with it and the returned
PGresult describes the error condition.
+
+
+ The server may be configure to disallow multiple sql commands without being
+ a transaction block to add an extra defense against SQL-injection attacks
+ so it is always a good practice to add BEGIN/COMMIT
+ commands for multiple sql commands
+
+
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 75c2d9a..3e1a2fc 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -96,7 +96,7 @@ int max_stack_depth = 100;
/* wait N seconds to allow attach from a debugger */
int PostAuthDelay = 0;
-
+bool allow_multiple_queries = true;
/* ----------------
* private variables
@@ -185,6 +185,8 @@ static void finish_xact_command(void);
static bool IsTransactionExitStmt(Node *parsetree);
static bool IsTransactionExitStmtList(List *pstmts);
static bool IsTransactionStmtList(List *pstmts);
+static bool IsTransactionStartStmt(Node *parsetree);
+static bool IsTransactionEndStmt(Node *parsetree);
static void drop_unnamed_stmt(void);
static void SigHupHandler(SIGNAL_ARGS);
static void log_disconnections(int code, Datum arg);
@@ -937,6 +939,25 @@ exec_simple_query(const char *query_string)
*/
parsetree_list = pg_parse_query(query_string);
+ if (!allow_multiple_queries && (list_length(parsetree_list) > 1))
+ {
+ /*
+ * we use head and tail cell for checking weather it is a transaction
+ * block or not
+ */
+ RawStmt *parsetreehead = lfirst_node(RawStmt, list_head(parsetree_list));
+ RawStmt *parsetreetail = lfirst_node(RawStmt, list_tail(parsetree_list));
+
+ /*
+ * We only allow a single user query or a transaction block per call .
+ * This is provide an additional defense against SQL-injection attacks.
+ */
+
+ if (!IsTransactionStartStmt(parsetreehead->stmt) || !IsTransactionEndStmt(parsetreetail->stmt) )
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR), errmsg("cannot execute multiple commands unless it is a transaction block")));
+ }
+
/* Log immediately if dictated by log_statement */
if (check_log_statement(parsetree_list))
{
@@ -2532,6 +2553,44 @@ IsTransactionStmtList(List *pstmts)
return false;
}
+/*
+ *Convenience routines for checking whether a statement is transaction Start statment.
+ */
+
+/* Test a bare parsetree */
+static bool
+IsTransactionStartStmt(Node *parsetree)
+{
+ if (parsetree && IsA(parsetree, TransactionStmt))
+ {
+ TransactionStmt *stmt = (TransactionStmt *) parsetree;
+
+ if (stmt->kind == TRANS_STMT_BEGIN ||
+ stmt->kind == TRANS_STMT_START)
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Convenience routines for checking whether a statement is transaction end statment.
+ */
+
+/* Test a bare parsetree */
+static bool
+IsTransactionEndStmt(Node *parsetree)
+{
+ if (parsetree && IsA(parsetree, TransactionStmt))
+ {
+ TransactionStmt *stmt = (TransactionStmt *) parsetree;
+
+ if (stmt->kind == TRANS_STMT_COMMIT||
+ stmt->kind == TRANS_STMT_ROLLBACK)
+ return true;
+ }
+ return false;
+}
+
/* Release any existing unnamed prepared statement */
static void
drop_unnamed_stmt(void)
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a414fb2..0aada36 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -1670,6 +1670,16 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
+ {
+ {"allow_multiple_queries", PGC_SUSET, CLIENT_CONN_OTHER,
+ gettext_noop("Allow multiple queries per query string."),
+ NULL
+ },
+ &allow_multiple_queries,
+ true,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index f1a34a1..e11ea23 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -47,6 +47,8 @@ typedef enum
extern int log_statement;
+extern bool allow_multiple_queries;
+
extern List *pg_parse_query(const char *query_string);
extern List *pg_analyze_and_rewrite(RawStmt *parsetree,
const char *query_string,