Beispiel #1
0
static zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const char *sql, zend_long sql_len)
{
	pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
	PGresult *res;
	zend_long ret = 1;
	ExecStatusType qs;

	if (!(res = PQexec(H->server, sql))) {
		/* fatal error */
		pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
		return -1;
	}
	qs = PQresultStatus(res);
	if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) {
		pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res));
		PQclear(res);
		return -1;
	}
	H->pgoid = PQoidValue(res);
	if (qs == PGRES_COMMAND_OK) {
		ZEND_ATOL(ret, PQcmdTuples(res));
	} else {
		ret = Z_L(0);
	}
	PQclear(res);

	return ret;
}
Beispiel #2
0
    unsigned long psMysqlConnection::Command(const char *sql,...)
    {
        psStopWatch timer;
        csString querystr;
        va_list args;

        va_start(args, sql);
        querystr.FormatV(sql, args);
        va_end(args);

        lastquery = querystr;

        timer.Start();
        PGresult *res = PQexec(conn, querystr.GetData());
        
        if(res && PQresultStatus(res) != PGRES_FATAL_ERROR)
        {
            if(timer.Stop() > 1000)
            {
                csString status;
                status.Format("SQL query %s, has taken %u time to process.\n", querystr.GetData(), timer.Stop());
                if(logcsv)
                    logcsv->Write(CSV_STATUS, status);
            }
            profs.AddSQLTime(querystr, timer.Stop());
            lastRow = PQoidValue(res);
            const char *const RowsStr = PQcmdTuples(res);
            return (unsigned long) (RowsStr[0] ? atoi(RowsStr) : 0);
        }
        else
        {
            PQclear(res);
            return QUERY_FAILED;
        }
    }
Beispiel #3
0
/*
 * call-seq:
 *    res.oid_value() -> Fixnum
 *
 * Returns the +oid+ of the inserted row if applicable,
 * otherwise +nil+.
 */
static VALUE
pgresult_oid_value(VALUE self)
{
	Oid n = PQoidValue(pgresult_get(self));
	if (n == InvalidOid)
		return Qnil;
	else
		return INT2FIX(n);
}
Beispiel #4
0
QVariant QPSQLResult::lastInsertId() const
{
    if (isActive()) {
        Oid id = PQoidValue(d->result);
        if (id != InvalidOid)
            return QVariant(id);
    }
    return QVariant();
}
Beispiel #5
0
/*
 * PrintQueryResults: print out query results as required
 *
 * Note: Utility function for use by SendQuery() only.
 *
 * Returns true if the query executed successfully, false otherwise.
 */
static bool
PrintQueryResults(PGresult *results)
{
	bool		success = false;

	if (!results)
		return false;

	switch (PQresultStatus(results))
	{
		case PGRES_TUPLES_OK:
			success = PrintQueryTuples(results);
			break;

		case PGRES_COMMAND_OK:
			{
				char		buf[10];

				success = true;
				sprintf(buf, "%u", (unsigned int) PQoidValue(results));
				if (!QUIET())
				{
					if (pset.popt.topt.format == PRINT_HTML)
					{
						fputs("<p>", pset.queryFout);
						html_escaped_print(PQcmdStatus(results),
										   pset.queryFout);
						fputs("</p>\n", pset.queryFout);
					}
					else
						fprintf(pset.queryFout, "%s\n", PQcmdStatus(results));
				}
				SetVariable(pset.vars, "LASTOID", buf);
				break;
			}

		case PGRES_EMPTY_QUERY:
			success = true;
			break;

		case PGRES_COPY_OUT:
		case PGRES_COPY_IN:
			/* nothing to do here */
			success = true;
			break;

		default:
			break;
	}

	fflush(pset.queryFout);

	return success;
}
Beispiel #6
0
/* function to call for inserts or updates, returns # of rows affected */
int
pgconn_update(PGConnection *conn, const char *query)
{
   int rowsModified;
   PGresult *res;

   res = PQexec(conn->stream,query);

   if(res == NULL)
     THROW("SQLException", "%s", PQerrorMessage(conn->stream));

   if(PQresultStatus(res) != PGRES_COMMAND_OK)
     THROW("SQLException", "%s", PQresultErrorMessage(res));

   rowsModified = strtol(PQcmdTuples(res), NULL, 10);
   lastOid = PQoidValue(res);
   PQclear(res);

   return rowsModified;
}
Beispiel #7
0
static void
esql_postgresql_res(Esql_Res *res)
{
   Esql_Row *r;
   PGresult *pres;
   int i;

   pres = res->backend.res = PQgetResult(res->e->backend.db);
   EINA_SAFETY_ON_NULL_RETURN(pres);

   switch (PQresultStatus(pres))
     {
      case PGRES_COMMAND_OK:
        {
           const char *a;

           a = PQcmdTuples(pres);
           if (a && (*a))
             res->affected = strtol(a, NULL, 10);
           res->id = PQoidValue(pres);
        }
        return;
      case PGRES_TUPLES_OK:
        break;
      default:
        res->error = PQresultErrorMessage(pres);
        ERR("Error %s:'%s'!", PQresStatus(PQresultStatus(pres)), res->error);
        return;
     }
   res->desc = esql_module_desc_get(PQntuples(pres), (Esql_Module_Setup_Cb)esql_module_setup_cb, res);
   for (i = 0; i < res->row_count; i++)
     {
        r = esql_row_calloc(1);
        EINA_SAFETY_ON_NULL_RETURN(r);
        r->res = res;
        esql_postgresql_row_init(r, i);
        res->rows = eina_inlist_append(res->rows, EINA_INLIST_GET(r));
     }
}
Beispiel #8
0
/*
 * Find the max of the lastOid values returned from the QEs
 */
Oid
cdbdisp_maxLastOid(CdbDispatchResults *results, int sliceIndex)
{
	CdbDispatchResult *dispatchResult;
	CdbDispatchResult *resultEnd = cdbdisp_resultEnd(results, sliceIndex);
	PGresult *pgresult;
	Oid	oid = InvalidOid;

	for (dispatchResult = cdbdisp_resultBegin(results, sliceIndex);
		 dispatchResult < resultEnd; ++dispatchResult)
	{
		pgresult = cdbdisp_getPGresult(dispatchResult, dispatchResult->okindex);
		if (pgresult && !dispatchResult->errcode)
		{
			Oid	tmpoid = PQoidValue(pgresult);

			if (tmpoid > oid)
				oid = tmpoid;
		}
	}

	return oid;
}
Beispiel #9
0
/*
 * PrintQueryStatus: report command status as required
 *
 * Note: Utility function for use by PrintQueryResults() only.
 */
static void
PrintQueryStatus(PGresult *results)
{
	char		buf[16];

	if (!pset.quiet)
	{
		if (pset.popt.topt.format == PRINT_HTML)
		{
			fputs("<p>", pset.queryFout);
			html_escaped_print(PQcmdStatus(results), pset.queryFout);
			fputs("</p>\n", pset.queryFout);
		}
		else
			fprintf(pset.queryFout, "%s\n", PQcmdStatus(results));
	}

	if (pset.logfile)
		fprintf(pset.logfile, "%s\n", PQcmdStatus(results));

	snprintf(buf, sizeof(buf), "%u", (unsigned int) PQoidValue(results));
	SetVariable(pset.vars, "LASTOID", buf);
}
ngx_chain_t *
ngx_postgres_render_rds_header(ngx_http_request_t *r, ngx_pool_t *pool,
    PGresult *res, ngx_int_t col_count, ngx_int_t aff_count)
{
    ngx_chain_t  *cl;
    ngx_buf_t    *b;
    size_t        size;
    char         *errstr;
    size_t        errstr_len;

    dd("entering");

    errstr = PQresultErrorMessage(res);
    errstr_len = ngx_strlen(errstr);

    size = sizeof(uint8_t)        /* endian type */
         + sizeof(uint32_t)       /* format version */
         + sizeof(uint8_t)        /* result type */
         + sizeof(uint16_t)       /* standard error code */
         + sizeof(uint16_t)       /* driver-specific error code */
         + sizeof(uint16_t)       /* driver-specific error string length */
         + (uint16_t) errstr_len  /* driver-specific error string data */
         + sizeof(uint64_t)       /* rows affected */
         + sizeof(uint64_t)       /* insert id */
         + sizeof(uint16_t)       /* column count */
         ;

    b = ngx_create_temp_buf(pool, size);
    if (b == NULL) {
        dd("returning NULL");
        return NULL;
    }

    cl = ngx_alloc_chain_link(pool);
    if (cl == NULL) {
        dd("returning NULL");
        return NULL;
    }

    cl->buf = b;
    b->memory = 1;
    b->tag = r->upstream->output.tag;

    /* fill data */
#if NGX_HAVE_LITTLE_ENDIAN
    *b->last++ = 0;
#else
    *b->last++ = 1;
#endif

    *(uint32_t *) b->last = (uint32_t) resty_dbd_stream_version;
    b->last += sizeof(uint32_t);

    *b->last++ = 0;

    *(uint16_t *) b->last = (uint16_t) 0;
    b->last += sizeof(uint16_t);

    *(uint16_t *) b->last = (uint16_t) PQresultStatus(res);
    b->last += sizeof(uint16_t);

    *(uint16_t *) b->last = (uint16_t) errstr_len;
    b->last += sizeof(uint16_t);

    if (errstr_len) {
        b->last = ngx_copy(b->last, (u_char *) errstr, errstr_len);
    }

    *(uint64_t *) b->last = (uint64_t) aff_count;
    b->last += sizeof(uint64_t);

    *(uint64_t *) b->last = (uint64_t) PQoidValue(res);
    b->last += sizeof(uint64_t);

    *(uint16_t *) b->last = (uint16_t) col_count;
    b->last += sizeof(uint16_t);

    if (b->last != b->end) {
        dd("returning NULL");
        return NULL;
    }

    dd("returning");
    return cl;
}
Beispiel #11
0
/**********************************
 * pg_result
 get information about the results of a query

 syntax:

	pg_result result ?option?

 the options are:

	-status		the status of the result

	-error		the error message, if the status indicates error; otherwise
				an empty string

	-conn		the connection that produced the result

	-oid		if command was an INSERT, the OID of the inserted tuple

	-numTuples	the number of tuples in the query

	-cmdTuples	the number of tuples affected by the query

	-numAttrs	returns the number of attributes returned by the query

	-assign arrayName
				assign the results to an array, using subscripts of the form
				(tupno,attributeName)

	-assignbyidx arrayName ?appendstr?
				assign the results to an array using the first field's value
				as a key.
				All but the first field of each tuple are stored, using
				subscripts of the form (field0value,attributeNameappendstr)

	-getTuple tupleNumber
				returns the values of the tuple in a list

	-tupleArray tupleNumber arrayName
				stores the values of the tuple in array arrayName, indexed
				by the attributes returned

	-attributes
				returns a list of the name/type pairs of the tuple attributes

	-lAttributes
				returns a list of the {name type len} entries of the tuple
				attributes

	-clear		clear the result buffer. Do not reuse after this

 **********************************/
int
Pg_result(ClientData cData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
{
	PGresult   *result;
	const char *opt;
	int			i;
	int			tupno;
	CONST84 char *arrVar;
	char		nameBuffer[256];
	const char *appendstr;

	if (argc < 3 || argc > 5)
	{
		Tcl_AppendResult(interp, "Wrong # of arguments\n", 0);
		goto Pg_result_errReturn;		/* append help info */
	}

	result = PgGetResultId(interp, argv[1]);
	if (result == (PGresult *) NULL)
	{
		Tcl_AppendResult(interp, "\n",
						 argv[1], " is not a valid query result", 0);
		return TCL_ERROR;
	}

	opt = argv[2];

	if (strcmp(opt, "-status") == 0)
	{
		Tcl_AppendResult(interp, PQresStatus(PQresultStatus(result)), 0);
		return TCL_OK;
	}
	else if (strcmp(opt, "-error") == 0)
	{
		Tcl_SetResult(interp, (char *) PQresultErrorMessage(result),
					  TCL_STATIC);
		return TCL_OK;
	}
	else if (strcmp(opt, "-conn") == 0)
		return PgGetConnByResultId(interp, argv[1]);
	else if (strcmp(opt, "-oid") == 0)
	{
		sprintf(interp->result, "%u", PQoidValue(result));
		return TCL_OK;
	}
	else if (strcmp(opt, "-clear") == 0)
	{
		PgDelResultId(interp, argv[1]);
		PQclear(result);
		return TCL_OK;
	}
	else if (strcmp(opt, "-numTuples") == 0)
	{
		sprintf(interp->result, "%d", PQntuples(result));
		return TCL_OK;
	}
	else if (strcmp(opt, "-cmdTuples") == 0)
	{
		sprintf(interp->result, "%s", PQcmdTuples(result));
		return TCL_OK;
	}
	else if (strcmp(opt, "-numAttrs") == 0)
	{
		sprintf(interp->result, "%d", PQnfields(result));
		return TCL_OK;
	}
	else if (strcmp(opt, "-assign") == 0)
	{
		if (argc != 4)
		{
			Tcl_AppendResult(interp, "-assign option must be followed by a variable name", 0);
			return TCL_ERROR;
		}
		arrVar = argv[3];

		/*
		 * this assignment assigns the table of result tuples into a giant
		 * array with the name given in the argument. The indices of the
		 * array are of the form (tupno,attrName). Note we expect field
		 * names not to exceed a few dozen characters, so truncating to
		 * prevent buffer overflow shouldn't be a problem.
		 */
		for (tupno = 0; tupno < PQntuples(result); tupno++)
		{
			for (i = 0; i < PQnfields(result); i++)
			{
				sprintf(nameBuffer, "%d,%.200s", tupno, PQfname(result, i));
				if (Tcl_SetVar2(interp, arrVar, nameBuffer,
#ifdef TCL_ARRAYS
								tcl_value(PQgetvalue(result, tupno, i)),
#else
								PQgetvalue(result, tupno, i),
#endif
								TCL_LEAVE_ERR_MSG) == NULL)
					return TCL_ERROR;
			}
		}
		Tcl_AppendResult(interp, arrVar, 0);
		return TCL_OK;
	}
	else if (strcmp(opt, "-assignbyidx") == 0)
	{
		if (argc != 4 && argc != 5)
		{
			Tcl_AppendResult(interp, "-assignbyidx option requires an array name and optionally an append string", 0);
			return TCL_ERROR;
		}
		arrVar = argv[3];
		appendstr = (argc == 5) ? (const char *) argv[4] : "";

		/*
		 * this assignment assigns the table of result tuples into a giant
		 * array with the name given in the argument.  The indices of the
		 * array are of the form (field0Value,attrNameappendstr). Here, we
		 * still assume PQfname won't exceed 200 characters, but we dare
		 * not make the same assumption about the data in field 0 nor the
		 * append string.
		 */
		for (tupno = 0; tupno < PQntuples(result); tupno++)
		{
			const char *field0 =
#ifdef TCL_ARRAYS
			tcl_value(PQgetvalue(result, tupno, 0));

#else
			PQgetvalue(result, tupno, 0);
#endif
			char	   *workspace = malloc(strlen(field0) + strlen(appendstr) + 210);

			for (i = 1; i < PQnfields(result); i++)
			{
				sprintf(workspace, "%s,%.200s%s", field0, PQfname(result, i),
						appendstr);
				if (Tcl_SetVar2(interp, arrVar, workspace,
#ifdef TCL_ARRAYS
								tcl_value(PQgetvalue(result, tupno, i)),
#else
								PQgetvalue(result, tupno, i),
#endif
								TCL_LEAVE_ERR_MSG) == NULL)
				{
					free(workspace);
					return TCL_ERROR;
				}
			}
			free(workspace);
		}
		Tcl_AppendResult(interp, arrVar, 0);
		return TCL_OK;
	}
	else if (strcmp(opt, "-getTuple") == 0)
	{
		if (argc != 4)
		{
			Tcl_AppendResult(interp, "-getTuple option must be followed by a tuple number", 0);
			return TCL_ERROR;
		}
		tupno = atoi(argv[3]);
		if (tupno < 0 || tupno >= PQntuples(result))
		{
			Tcl_AppendResult(interp, "argument to getTuple cannot exceed number of tuples - 1", 0);
			return TCL_ERROR;
		}
#ifdef TCL_ARRAYS
		for (i = 0; i < PQnfields(result); i++)
			Tcl_AppendElement(interp, tcl_value(PQgetvalue(result, tupno, i)));
#else
		for (i = 0; i < PQnfields(result); i++)
			Tcl_AppendElement(interp, PQgetvalue(result, tupno, i));
#endif
		return TCL_OK;
	}
	else if (strcmp(opt, "-tupleArray") == 0)
	{
		if (argc != 5)
		{
			Tcl_AppendResult(interp, "-tupleArray option must be followed by a tuple number and array name", 0);
			return TCL_ERROR;
		}
		tupno = atoi(argv[3]);
		if (tupno < 0 || tupno >= PQntuples(result))
		{
			Tcl_AppendResult(interp, "argument to tupleArray cannot exceed number of tuples - 1", 0);
			return TCL_ERROR;
		}
		for (i = 0; i < PQnfields(result); i++)
		{
			if (Tcl_SetVar2(interp, argv[4], PQfname(result, i),
#ifdef TCL_ARRAYS
							tcl_value(PQgetvalue(result, tupno, i)),
#else
							PQgetvalue(result, tupno, i),
#endif
							TCL_LEAVE_ERR_MSG) == NULL)
				return TCL_ERROR;
		}
		return TCL_OK;
	}
	else if (strcmp(opt, "-attributes") == 0)
	{
		for (i = 0; i < PQnfields(result); i++)
			Tcl_AppendElement(interp, PQfname(result, i));
		return TCL_OK;
	}
	else if (strcmp(opt, "-lAttributes") == 0)
	{
		for (i = 0; i < PQnfields(result); i++)
		{
			/* start a sublist */
			if (i > 0)
				Tcl_AppendResult(interp, " {", 0);
			else
				Tcl_AppendResult(interp, "{", 0);
			Tcl_AppendElement(interp, PQfname(result, i));
			sprintf(nameBuffer, "%ld", (long) PQftype(result, i));
			Tcl_AppendElement(interp, nameBuffer);
			sprintf(nameBuffer, "%ld", (long) PQfsize(result, i));
			Tcl_AppendElement(interp, nameBuffer);
			/* end the sublist */
			Tcl_AppendResult(interp, "}", 0);
		}
		return TCL_OK;
	}
	else
	{
		Tcl_AppendResult(interp, "Invalid option\n", 0);
		goto Pg_result_errReturn;		/* append help info */
	}


Pg_result_errReturn:
	Tcl_AppendResult(interp,
					 "pg_result result ?option? where option is\n",
					 "\t-status\n",
					 "\t-error\n",
					 "\t-conn\n",
					 "\t-oid\n",
					 "\t-numTuples\n",
					 "\t-cmdTuples\n",
					 "\t-numAttrs\n"
					 "\t-assign arrayVarName\n",
					 "\t-assignbyidx arrayVarName ?appendstr?\n",
					 "\t-getTuple tupleNumber\n",
					 "\t-tupleArray tupleNumber arrayVarName\n",
					 "\t-attributes\n"
					 "\t-lAttributes\n"
					 "\t-clear\n",
					 (char *) 0);
	return TCL_ERROR;


}
Beispiel #12
0
int pgQueryThread::execute()
{
	rowsInserted = -1L;

	if (!conn->conn)
		return(raiseEvent(0));

	wxCharBuffer queryBuf = query.mb_str(*conn->conv);
	if (!queryBuf && !query.IsEmpty())
	{
		conn->SetLastResultError(NULL, _("The query could not be converted to the required encoding."));
		return(raiseEvent(0));
	}

	if (!PQsendQuery(conn->conn, queryBuf))
	{
		conn->SetLastResultError(NULL);
		conn->IsAlive();
		return(raiseEvent(0));
	}
	int resultsRetrieved = 0;
	PGresult *lastResult = 0;
	while (true)
	{
		if (TestDestroy())
		{
			if (rc != -3)
			{
				if (!PQrequestCancel(conn->conn)) // could not abort; abort failed.
					return(raiseEvent(-1));

				rc = -3;
			}
		}
		if (!PQconsumeInput(conn->conn))
			return(raiseEvent(0));
		if (PQisBusy(conn->conn))
		{
			Yield();
			this->Sleep(10);
			continue;
		}

		// If resultToRetrieve is given, the nth result will be returned,
		// otherwise the last result set will be returned.
		// all others are discarded
		PGresult *res = PQgetResult(conn->conn);

		if (!res)
			break;

		resultsRetrieved++;
		if (resultsRetrieved == resultToRetrieve)
		{
			result = res;
			insertedOid = PQoidValue(res);
			if (insertedOid && insertedOid != (OID) - 1)
				appendMessage(wxString::Format(_("Query inserted one row with OID %d.\n"), insertedOid));
			else
				appendMessage(wxString::Format(wxPLURAL("Query result with %d row will be returned.\n", "Query result with %d rows will be returned.\n",
				                                        PQntuples(result))));
			continue;
		}
		if (lastResult)
		{
			if (PQntuples(lastResult))
				appendMessage(wxString::Format(wxPLURAL("Query result with %d row discarded.\n", "Query result with %d rows discarded.\n",
				                                        PQntuples(lastResult))));
			PQclear(lastResult);
		}
		lastResult = res;
	}

	if (!result)
		result = lastResult;

	conn->SetLastResultError(result);

	appendMessage(wxT("\n"));
	rc = PQresultStatus(result);
	insertedOid = PQoidValue(result);
	if (insertedOid == (OID) - 1)
		insertedOid = 0;

	if (rc == PGRES_TUPLES_OK)
	{
		dataSet = new pgSet(result, conn, *conn->conv, conn->needColQuoting);
		dataSet->MoveFirst();
	}
	else if (rc == PGRES_COMMAND_OK)
	{
		char *s = PQcmdTuples(result);
		if (*s)
			rowsInserted = atol(s);
	}
	else if (rc == PGRES_FATAL_ERROR)
	{
		appendMessage(conn->GetLastError() + wxT("\n"));
	}
	return(raiseEvent(1));
}
Beispiel #13
0
int
Pg_execute(ClientData cData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
{
	Pg_ConnectionId *connid;
	PGconn	   *conn;
	PGresult   *result;
	int			i;
	int			tupno;
	int			ntup;
	int			loop_rc;
	CONST84 char *oid_varname = NULL;
	CONST84 char *array_varname = NULL;
	char		buf[64];

	char	   *usage = "Wrong # of arguments\n"
	"pg_execute ?-array arrayname? ?-oid varname? "
	"connection queryString ?loop_body?";

	/*
	 * First we parse the options
	 */
	i = 1;
	while (i < argc)
	{
		if (argv[i][0] != '-')
			break;

		if (strcmp(argv[i], "-array") == 0)
		{
			/*
			 * The rows should appear in an array vs. to single variables
			 */
			i++;
			if (i == argc)
			{
				Tcl_SetResult(interp, usage, TCL_VOLATILE);
				return TCL_ERROR;
			}
			array_varname = argv[i++];
			continue;
		}

		if (strcmp(argv[i], "-oid") == 0)
		{
			/*
			 * We should place PQoidValue() somewhere
			 */
			i++;
			if (i == argc)
			{
				Tcl_SetResult(interp, usage, TCL_VOLATILE);
				return TCL_ERROR;
			}
			oid_varname = argv[i++];
			continue;
		}

		Tcl_AppendResult(interp, "Unknown option '", argv[i], "'", NULL);
		return TCL_ERROR;
	}

	/*
	 * Check that after option parsing at least 'connection' and 'query'
	 * are left
	 */
	if (argc - i < 2)
	{
		Tcl_SetResult(interp, usage, TCL_VOLATILE);
		return TCL_ERROR;
	}

	/*
	 * Get the connection and make sure no COPY command is pending
	 */
	conn = PgGetConnectionId(interp, argv[i++], &connid);
	if (conn == (PGconn *) NULL)
		return TCL_ERROR;

	if (connid->res_copyStatus != RES_COPY_NONE)
	{
		Tcl_SetResult(interp, "Attempt to query while COPY in progress", TCL_STATIC);
		return TCL_ERROR;
	}

	/*
	 * Execute the query
	 */
	result = PQexec(conn, argv[i++]);

	/*
	 * Transfer any notify events from libpq to Tcl event queue.
	 */
	PgNotifyTransferEvents(connid);

	/*
	 * Check for errors
	 */
	if (result == NULL)
	{
		Tcl_SetResult(interp, PQerrorMessage(conn), TCL_VOLATILE);
		return TCL_ERROR;
	}

	/*
	 * Set the oid variable to the returned oid of an INSERT statement if
	 * requested (or 0 if it wasn't an INSERT)
	 */
	if (oid_varname != NULL)
	{
		char		oid_buf[32];

		sprintf(oid_buf, "%u", PQoidValue(result));
		if (Tcl_SetVar(interp, oid_varname, oid_buf,
					   TCL_LEAVE_ERR_MSG) == NULL)
		{
			PQclear(result);
			return TCL_ERROR;
		}
	}

	/*
	 * Decide how to go on based on the result status
	 */
	switch (PQresultStatus(result))
	{
		case PGRES_TUPLES_OK:
			/* fall through if we have tuples */
			break;

		case PGRES_EMPTY_QUERY:
		case PGRES_COMMAND_OK:
		case PGRES_COPY_IN:
		case PGRES_COPY_OUT:
			/* tell the number of affected tuples for non-SELECT queries */
			Tcl_SetResult(interp, PQcmdTuples(result), TCL_VOLATILE);
			PQclear(result);
			return TCL_OK;

		default:
			/* anything else must be an error */
			Tcl_ResetResult(interp);
			Tcl_AppendElement(interp, PQresStatus(PQresultStatus(result)));
			Tcl_AppendElement(interp, PQresultErrorMessage(result));
			PQclear(result);
			return TCL_ERROR;
	}

	/*
	 * We reach here only for queries that returned tuples
	 */
	if (i == argc)
	{
		/*
		 * We don't have a loop body. If we have at least one result row,
		 * we set all the variables to the first one and return.
		 */
		if (PQntuples(result) > 0)
		{
			if (execute_put_values(interp, array_varname, result, 0) != TCL_OK)
			{
				PQclear(result);
				return TCL_ERROR;
			}
		}

		sprintf(buf, "%d", PQntuples(result));
		Tcl_SetResult(interp, buf, TCL_VOLATILE);
		PQclear(result);
		return TCL_OK;
	}

	/*
	 * We have a loop body. For each row in the result set put the values
	 * into the Tcl variables and execute the body.
	 */
	ntup = PQntuples(result);
	for (tupno = 0; tupno < ntup; tupno++)
	{
		if (execute_put_values(interp, array_varname, result, tupno) != TCL_OK)
		{
			PQclear(result);
			return TCL_ERROR;
		}

		loop_rc = Tcl_Eval(interp, argv[i]);

		/* The returncode of the loop body controls the loop execution */
		if (loop_rc == TCL_OK || loop_rc == TCL_CONTINUE)
			/* OK or CONTINUE means start next loop invocation */
			continue;
		if (loop_rc == TCL_RETURN)
		{
			/* RETURN means hand up the given interpreter result */
			PQclear(result);
			return TCL_RETURN;
		}
		if (loop_rc == TCL_BREAK)
			/* BREAK means leave the loop */
			break;

		PQclear(result);
		return TCL_ERROR;
	}

	/*
	 * At the end of the loop we put the number of rows we got into the
	 * interpreter result and clear the result set.
	 */
	sprintf(buf, "%d", ntup);
	Tcl_SetResult(interp, buf, TCL_VOLATILE);
	PQclear(result);
	return TCL_OK;
}
Beispiel #14
0
    Oid oid_value() const {
      Oid o = PQoidValue(res);
      if (o == InvalidOid)
	throw Error::no_insert("", conn.getName());
      return o;
    }
Beispiel #15
0
int pgQueryThread::Execute()
{
	wxMutexLocker lock(m_queriesLock);

	PGresult       *result           = NULL;
	wxMBConv       &conv             = *(m_conn->conv);

	wxString       &query            = m_queries[m_currIndex]->m_query;
	int            &resultToRetrieve = m_queries[m_currIndex]->m_resToRetrieve;
	long           &rowsInserted     = m_queries[m_currIndex]->m_rowsInserted;
	Oid            &insertedOid      = m_queries[m_currIndex]->m_insertedOid;
	// using the alias for the pointer here, in order to save the result back
	// in the pgBatchQuery object
	pgSet         *&dataSet          = m_queries[m_currIndex]->m_resultSet;
	int            &rc               = m_queries[m_currIndex]->m_returnCode;
	pgParamsArray  *params           = m_queries[m_currIndex]->m_params;
	bool            useCallable      = m_queries[m_currIndex]->m_useCallable;
	pgError        &err              = m_queries[m_currIndex]->m_err;

	wxCharBuffer queryBuf = query.mb_str(conv);

	if (PQstatus(m_conn->conn) != CONNECTION_OK)
	{
		rc = pgQueryResultEvent::PGQ_CONN_LOST;
		err.msg_primary = _("Connection to the database server lost");

		return(RaiseEvent(rc));
	}

	if (!queryBuf && !query.IsEmpty())
	{
		rc = pgQueryResultEvent::PGQ_STRING_INVALID;
		m_conn->SetLastResultError(NULL, _("the query could not be converted to the required encoding."));
		err.msg_primary = _("Query string is empty");

		return(RaiseEvent(rc));
	}

	// Honour the parameters (if any)
	if (params && params->GetCount() > 0)
	{
		int    pCount = params->GetCount();
		int    ret    = 0,
		       idx    = 0;

		Oid         *pOids    = (Oid *)malloc(pCount * sizeof(Oid));
		const char **pParams  = (const char **)malloc(pCount * sizeof(const char *));
		int         *pLens    = (int *)malloc(pCount * sizeof(int));
		int         *pFormats = (int *)malloc(pCount * sizeof(int));
		// modes are used only by enterprisedb callable statement
#if defined (__WXMSW__) || (EDB_LIBPQ)
		int         *pModes   = (int *)malloc(pCount * sizeof(int));
#endif

		for (; idx < pCount; idx++)
		{
			pgParam *param = (*params)[idx];

			pOids[idx] = param->m_type;
			pParams[idx] = (const char *)param->m_val;
			pLens[idx] = param->m_len;
			pFormats[idx] = param->GetFormat();
#if defined (__WXMSW__) || (EDB_LIBPQ)
			pModes[idx] = param->m_mode;
#endif
		}

		if (useCallable)
		{
#if defined (__WXMSW__) || (EDB_LIBPQ)
			wxLogInfo(wxString::Format(
			              _("using an enterprisedb callable statement (queryid:%ld, threadid:%ld)"),
			              (long)m_currIndex, (long)GetId()));
			wxString stmt = wxString::Format(wxT("pgQueryThread-%ld-%ld"), this->GetId(), m_currIndex);
			PGresult *res = PQiPrepareOut(m_conn->conn, stmt.mb_str(wxConvUTF8),
			                              queryBuf, pCount, pOids, pModes);

			if( PQresultStatus(res) != PGRES_COMMAND_OK)
			{
				rc = pgQueryResultEvent::PGQ_ERROR_PREPARE_CALLABLE;
				err.SetError(res, &conv);

				PQclear(res);

				goto return_with_error;
			}

			ret = PQiSendQueryPreparedOut(m_conn->conn, stmt.mb_str(wxConvUTF8),
			                              pCount, pParams, pLens, pFormats, 1);

			if (ret != 1)
			{
				rc = pgQueryResultEvent::PGQ_ERROR_EXECUTE_CALLABLE;

				m_conn->SetLastResultError(NULL, _("Failed to run PQsendQuery in pgQueryThread"));
				err.msg_primary = wxString(PQerrorMessage(m_conn->conn), conv);

				PQclear(res);
				res = NULL;

				goto return_with_error;
			}

			PQclear(res);
			res = NULL;
#else
			rc = -1;
			wxASSERT_MSG(false,
			             _("the program execution flow must not reach to this point in pgQueryThread"));

			goto return_with_error;
#endif
		}
		else
		{
			// assumptions: we will need the results in text format only
			ret = PQsendQueryParams(m_conn->conn, queryBuf, pCount, pOids, pParams, pLens, pFormats, 0);

			if (ret != 1)
			{
				rc = pgQueryResultEvent::PGQ_ERROR_SEND_QUERY;

				m_conn->SetLastResultError(NULL,
				                           _("Failed to run PQsendQueryParams in pgQueryThread"));

				err.msg_primary = _("Failed to run PQsendQueryParams in pgQueryThread.\n") +
				                  wxString(PQerrorMessage(m_conn->conn), conv);

				goto return_with_error;
			}
		}
		goto continue_without_error;

return_with_error:
		{
			free(pOids);
			free(pParams);
			free(pLens);
			free(pFormats);
#if defined (__WXMSW__) || (EDB_LIBPQ)
			free(pModes);
#endif
			return (RaiseEvent(rc));
		}
	}
	else
	{
		// use the PQsendQuery api in case, we don't have any parameters to
		// pass to the server
		if (!PQsendQuery(m_conn->conn, queryBuf))
		{
			rc = pgQueryResultEvent::PGQ_ERROR_SEND_QUERY;

			err.msg_primary = _("Failed to run PQsendQueryParams in pgQueryThread.\n") +
			                  wxString(PQerrorMessage(m_conn->conn), conv);

			return(RaiseEvent(rc));
		}
	}

continue_without_error:
	int resultsRetrieved = 0;
	PGresult *lastResult = 0;

	while (true)
	{
		// This is a 'joinable' thread, it is not advisable to call 'delete'
		// function on this.
		// Hence - it does not make sense to use the function 'testdestroy' here.
		// We introduced the 'CancelExecution' function for the same purpose.
		//
		// Also, do not raise event when the query execution is cancelled to
		// avoid the bugs introduced to handle events by the event handler,
		// which is missing or being deleted.
		//
		// It will be responsibility of the compononent, using the object of
		// pgQueryThread, to take the required actions to take care of the
		// issue.
		if (m_cancelled)
		{
			m_conn->CancelExecution();
			rc = pgQueryResultEvent::PGQ_EXECUTION_CANCELLED;

			err.msg_primary = _("Execution Cancelled");

			if (lastResult)
			{
				PQclear(lastResult);
				lastResult = NULL;
			}
			AppendMessage(_("Query-thread execution cancelled...\nthe query is:"));
			AppendMessage(query);

			return rc;
		}

		if ((rc = PQconsumeInput(m_conn->conn)) != 1)
		{
			if (rc == 0)
			{
				err.msg_primary = wxString(PQerrorMessage(m_conn->conn), conv);
			}
			if (PQstatus(m_conn->conn) == CONNECTION_BAD)
			{
				err.msg_primary = _("Connection to the database server lost");
				rc = pgQueryResultEvent::PGQ_CONN_LOST;
			}
			else
			{
				rc = pgQueryResultEvent::PGQ_ERROR_CONSUME_INPUT;
			}

			return(RaiseEvent(rc));
		}

		if (PQisBusy(m_conn->conn))
		{
			Yield();
			this->Sleep(10);

			continue;
		}

		// if resultToRetrieve is given, the nth result will be returned,
		// otherwise the last result set will be returned.
		// all others are discarded
		PGresult *res = PQgetResult(m_conn->conn);

		if (!res)
			break;

		if((PQresultStatus(res) == PGRES_NONFATAL_ERROR) ||
		        (PQresultStatus(res) == PGRES_FATAL_ERROR) ||
		        (PQresultStatus(res) == PGRES_BAD_RESPONSE))
		{
			result = res;
			err.SetError(res, &conv);

			// Wait for the execution to be finished
			// We need to fetch all the results, before sending the error
			// message
			do
			{
				if (PQconsumeInput(m_conn->conn) != 1)
				{
					if (m_cancelled)
					{
						rc = pgQueryResultEvent::PGQ_EXECUTION_CANCELLED;

						// Release the result as the query execution has been cancelled by the
						// user
						if (result)
							PQclear(result);

						return rc;
					}
					goto out_of_consume_input_loop;
				}

				if ((res = PQgetResult(m_conn->conn)) == NULL)
				{
					goto out_of_consume_input_loop;
				}
				// Release the temporary results
				PQclear(res);
				res = NULL;

				if (PQisBusy(m_conn->conn))
				{
					Yield();
					this->Sleep(10);
				}
			}
			while (true);

			break;
		}

#if defined (__WXMSW__) || (EDB_LIBPQ)
		// there should be 2 results in the callable statement - the first is the
		// dummy, the second contains our out params.
		if (useCallable)
		{
			PQclear(res);
			result = PQiGetOutResult(m_conn->conn);
		}
#endif
		if (PQresultStatus(res) == PGRES_COPY_IN)
		{
			rc = PGRES_COPY_IN;
			PQputCopyEnd(m_conn->conn, "not supported by pgadmin");
		}

		if (PQresultStatus(res) == PGRES_COPY_OUT)
		{
			int copyRc;
			char *buf;
			int copyRows = 0;
			int lastCopyRc = 0;

			rc = PGRES_COPY_OUT;

			AppendMessage(_("query returned copy data:\n"));

			while((copyRc = PQgetCopyData(m_conn->conn, &buf, 1)) >= 0)
			{
				if (buf != NULL)
				{
					if (copyRows < 100)
					{
						wxString str(buf, conv);
						wxCriticalSectionLocker cs(m_criticalSection);
						m_queries[m_currIndex]->m_message.Append(str);

					}
					else if (copyRows == 100)
						AppendMessage(_("Query returned more than 100 copy rows, discarding the rest...\n"));

					PQfreemem(buf);
				}
				if (copyRc > 0)
					copyRows++;

				if (m_cancelled)
				{
					m_conn->CancelExecution();
					rc = pgQueryResultEvent::PGQ_EXECUTION_CANCELLED;

					return -1;
				}
				if (lastCopyRc == 0 && copyRc == 0)
				{
					Yield();
					this->Sleep(10);
				}
				if (copyRc == 0)
				{
					if (!PQconsumeInput(m_conn->conn))
					{
						if (PQstatus(m_conn->conn) == CONNECTION_BAD)
						{
							err.msg_primary = _("Connection to the database server lost");
							rc = pgQueryResultEvent::PGQ_CONN_LOST;
						}
						else
						{
							rc = pgQueryResultEvent::PGQ_ERROR_CONSUME_INPUT;

							err.msg_primary = wxString(PQerrorMessage(m_conn->conn), conv);
						}
						return(RaiseEvent(rc));
					}
				}
				lastCopyRc = copyRc;
			}

			res = PQgetResult(m_conn->conn);

			if (!res)
				break;
		}

		resultsRetrieved++;
		if (resultsRetrieved == resultToRetrieve)
		{
			result = res;
			insertedOid = PQoidValue(res);
			if (insertedOid && insertedOid != (Oid) - 1)
				AppendMessage(wxString::Format(_("query inserted one row with oid %d.\n"), insertedOid));
			else
				AppendMessage(wxString::Format(wxPLURAL("query result with %d row will be returned.\n", "query result with %d rows will be returned.\n",
				                                        PQntuples(result)), PQntuples(result)));
			continue;
		}

		if (lastResult)
		{
			if (PQntuples(lastResult))
				AppendMessage(wxString::Format(wxPLURAL("query result with %d row discarded.\n", "query result with %d rows discarded.\n",
				                                        PQntuples(lastResult)), PQntuples(lastResult)));
			PQclear(lastResult);
		}
		lastResult = res;
	}
out_of_consume_input_loop:

	if (!result)
		result = lastResult;

	err.SetError(result, &conv);

	AppendMessage(wxT("\n"));

	rc = PQresultStatus(result);
	if (rc == PGRES_TUPLES_OK)
	{
		dataSet = new pgSet(result, m_conn, conv, m_conn->needColQuoting);
		dataSet->MoveFirst();
	}
	else if (rc == PGRES_COMMAND_OK)
	{
		char *s = PQcmdTuples(result);
		if (*s)
			rowsInserted = atol(s);
	}
	else if (rc == PGRES_FATAL_ERROR ||
	         rc == PGRES_NONFATAL_ERROR ||
	         rc == PGRES_BAD_RESPONSE)
	{
		if (result)
		{
			AppendMessage(wxString(PQresultErrorMessage(result), conv));
			PQclear(result);
			result = NULL;
		}
		else
		{
			AppendMessage(wxString(PQerrorMessage(m_conn->conn), conv));
		}

		return(RaiseEvent(rc));
	}

	insertedOid = PQoidValue(result);
	if (insertedOid == (Oid) - 1)
		insertedOid = 0;

	return(RaiseEvent(1));
}
static int store_pgsql(const char *database, const char *table, va_list ap)
{
	PGresult *result = NULL;
	Oid insertid;
	char sql[256];
	char params[256];
	char vals[256];
	char buf[256];
	int pgresult;
	const char *newparam, *newval;

	if (!table) {
		ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
		return -1;
	}

	/* Get the first parameter and first value in our list of passed paramater/value pairs */
	newparam = va_arg(ap, const char *);
	newval = va_arg(ap, const char *);
	if (!newparam || !newval) {
		ast_log(LOG_WARNING,
				"PostgreSQL RealTime: Realtime storage requires at least 1 parameter and 1 value to store.\n");
		if (pgsqlConn) {
			PQfinish(pgsqlConn);
			pgsqlConn = NULL;
		};
		return -1;
	}

	/* Must connect to the server before anything else, as the escape function requires the connection handle.. */
	ast_mutex_lock(&pgsql_lock);
	if (!pgsql_reconnect(database)) {
		ast_mutex_unlock(&pgsql_lock);
		return -1;
	}

	/* Create the first part of the query using the first parameter/value pairs we just extracted
	   If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
	PQescapeStringConn(pgsqlConn, buf, newparam, sizeof(newparam), &pgresult);
	snprintf(params, sizeof(params), "%s", buf);
	PQescapeStringConn(pgsqlConn, buf, newval, sizeof(newval), &pgresult);
	snprintf(vals, sizeof(vals), "'%s'", buf);
	while ((newparam = va_arg(ap, const char *))) {
		newval = va_arg(ap, const char *);
		PQescapeStringConn(pgsqlConn, buf, newparam, sizeof(newparam), &pgresult);
		snprintf(params + strlen(params), sizeof(params) - strlen(params), ", %s", buf);
		PQescapeStringConn(pgsqlConn, buf, newval, sizeof(newval), &pgresult);
		snprintf(vals + strlen(vals), sizeof(vals) - strlen(vals), ", '%s'", buf);
	}
	va_end(ap);
	snprintf(sql, sizeof(sql), "INSERT INTO (%s) VALUES (%s)", params, vals);

	ast_debug(1, "PostgreSQL RealTime: Insert SQL: %s\n", sql);

	if (!(result = PQexec(pgsqlConn, sql))) {
		ast_log(LOG_WARNING,
				"PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
		ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql);
		ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
		ast_mutex_unlock(&pgsql_lock);
		return -1;
	} else {
		ExecStatusType result_status = PQresultStatus(result);
		if (result_status != PGRES_COMMAND_OK
			&& result_status != PGRES_TUPLES_OK
			&& result_status != PGRES_NONFATAL_ERROR) {
			ast_log(LOG_WARNING,
					"PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
			ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql);
			ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
						PQresultErrorMessage(result), PQresStatus(result_status));
			ast_mutex_unlock(&pgsql_lock);
			return -1;
		}
	}

	insertid = PQoidValue(result);
	ast_mutex_unlock(&pgsql_lock);

	ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s, id: %u\n", table, insertid);

	/* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
	 * An integer greater than zero indicates the number of rows affected
	 * Zero indicates that no records were updated
	 * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
	 */

	if (insertid >= 0)
		return (int) insertid;

	return -1;
}
Beispiel #17
0
long long PostgresqlConnection_lastRowId(T C) {
        assert(C);
        return (long long)PQoidValue(C->res);
}
Beispiel #18
0
static int pgsql_stmt_execute(pdo_stmt_t *stmt)
{
	pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
	pdo_pgsql_db_handle *H = S->H;
	ExecStatusType status;

	/* ensure that we free any previous unfetched results */
	if(S->result) {
		PQclear(S->result);
		S->result = NULL;
	}

	S->current_row = 0;

	if (S->cursor_name) {
		char *q = NULL;

		if (S->is_prepared) {
			spprintf(&q, 0, "CLOSE %s", S->cursor_name);
			S->result = PQexec(H->server, q);
			efree(q);
		}

		spprintf(&q, 0, "DECLARE %s SCROLL CURSOR WITH HOLD FOR %s", S->cursor_name, stmt->active_query_string);
		S->result = PQexec(H->server, q);
		efree(q);

		/* check if declare failed */
		status = PQresultStatus(S->result);
		if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
			pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));
			return 0;
		}

		/* the cursor was declared correctly */
		S->is_prepared = 1;

		/* fetch to be able to get the number of tuples later, but don't advance the cursor pointer */
		spprintf(&q, 0, "FETCH FORWARD 0 FROM %s", S->cursor_name);
		S->result = PQexec(H->server, q);
		efree(q);
	} else if (S->stmt_name) {
		/* using a prepared statement */

		if (!S->is_prepared) {
stmt_retry:
			/* we deferred the prepare until now, because we didn't
			 * know anything about the parameter types; now we do */
			S->result = PQprepare(H->server, S->stmt_name, S->query,
						stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0,
						S->param_types);
			status = PQresultStatus(S->result);
			switch (status) {
				case PGRES_COMMAND_OK:
				case PGRES_TUPLES_OK:
					/* it worked */
					S->is_prepared = 1;
					PQclear(S->result);
					break;
				default: {
					char *sqlstate = pdo_pgsql_sqlstate(S->result);
					/* 42P05 means that the prepared statement already existed. this can happen if you use
					 * a connection pooling software line pgpool which doesn't close the db-connection once
					 * php disconnects. if php dies (no chance to run RSHUTDOWN) during execution it has no
					 * chance to DEALLOCATE the prepared statements it has created. so, if we hit a 42P05 we
					 * deallocate it and retry ONCE (thies 2005.12.15)
					 */
					if (sqlstate && !strcmp(sqlstate, "42P05")) {
						char buf[100]; /* stmt_name == "pdo_crsr_%08x" */
						PGresult *res;
						snprintf(buf, sizeof(buf), "DEALLOCATE %s", S->stmt_name);
						res = PQexec(H->server, buf);
						if (res) {
							PQclear(res);
						}
						goto stmt_retry;
					} else {
						pdo_pgsql_error_stmt(stmt, status, sqlstate);
						return 0;
					}
				}
			}
		}
		S->result = PQexecPrepared(H->server, S->stmt_name,
				stmt->bound_params ?
					zend_hash_num_elements(stmt->bound_params) :
					0,
				(const char**)S->param_values,
				S->param_lengths,
				S->param_formats,
				0);
	} else if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED) {
		/* execute query with parameters */
		S->result = PQexecParams(H->server, S->query,
				stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0,
				S->param_types,
				(const char**)S->param_values,
				S->param_lengths,
				S->param_formats,
				0);
	} else {
		/* execute plain query (with embedded parameters) */
		S->result = PQexec(H->server, stmt->active_query_string);
	}
	status = PQresultStatus(S->result);

	if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
		pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));
		return 0;
	}

	if (!stmt->executed && (!stmt->column_count || S->cols == NULL)) {
		stmt->column_count = (int) PQnfields(S->result);
		S->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column));
	}

	if (status == PGRES_COMMAND_OK) {
		ZEND_ATOL(stmt->row_count, PQcmdTuples(S->result));
		H->pgoid = PQoidValue(S->result);
	} else {
		stmt->row_count = (zend_long)PQntuples(S->result);
	}

	return 1;
}