pike.git/lib/modules/Sql.pmod/pgsql.pike:84:
final mapping(string:mapping(string:mixed)) _prepareds=([]);
private int pstmtcount;
private int ptstmtcount; // Periodically one would like to reset these
// but checking when this is safe to do
// probably is more costly than the gain
final int _pportalcount;
private int totalhits;
private int cachedepth=STATEMENTCACHEDEPTH;
private int timeout=QUERYTIMEOUT;
private int portalbuffersize=PORTALBUFFERSIZE;
- private int reconnected; // Number of times the connection was reset
- private int reconnectdelay; // Time to next reconnect
+
#ifdef PG_STATS
private int skippeddescribe; // Number of times we skipped Describe phase
private int portalsopened; // Number of portals opened
private int prepstmtused; // Number of times we used prepared statements
#endif
final int _msgsreceived; // Number of protocol messages received
final int _bytesreceived; // Number of bytes received
private int warningsdropcount; // Number of uncollected warnings
private int warningscollected;
private int invalidatecache;
pike.git/lib/modules/Sql.pmod/pgsql.pike:143:
//!
//! @param database
//! Specifies the database to connect to. Not specifying this is
//! only supported if the PostgreSQL backend has a default database
//! configured. If you do not want to connect to any live database,
//! you can use @expr{"template1"@}.
//!
//! @param options
//! Currently supports at least the following:
//! @mapping
- //! @member int "reconnect"
- //! Set it to zero to disable automatic reconnects upon losing
- //! the connection to the database. Not setting it, or setting
- //! it to one, will cause one timed reconnect to take place.
- //! Setting it to -1 will cause the system to try and reconnect
- //! indefinitely.
+
//! @member int "use_ssl"
//! If the database supports and allows SSL connections, the session
//! will be SSL encrypted, if not, the connection will fallback
//! to plain unencrypted.
//! @member int "force_ssl"
//! If the database supports and allows SSL connections, the session
//! will be SSL encrypted, if not, the connection will abort.
//! @member int "text_query"
//! Send queries to and retrieve results from the database using text
//! instead of the, generally more efficient, default native binary method.
pike.git/lib/modules/Sql.pmod/pgsql.pike:218:
if(!host) host = PGSQL_DEFAULT_HOST;
if(has_value(host,":") && sscanf(host,"%s:%d",host,_port)!=2)
ERROR("Error in parsing the hostname argument\n");
this::_host = host;
if(!_port)
_port = PGSQL_DEFAULT_PORT;
backendreg = .pgsql_util.register_backend();
_shortmux=Thread.Mutex();
- reconnect();
+ PD("Connect\n");
+ waitforauthready = Thread.Condition();
+ qportals=Thread.Queue();
+ _readyforquerycount = 1;
+ qportals->write(1);
+ if (!(c = .pgsql_util.conxion(this, qportals, 0)))
+ ERROR("Couldn't connect to database on %s:%d\n",_host,_port);
+ _runtimeparameter = ([]);
+ _unnamedportalmux = Thread.Mutex();
+ unnamedstatement = Thread.Mutex();
+ readyforquery_cb = connect_cb;
+ _portalsinflight = Thread.ResourceCount();
+ _statementsinflight = Thread.ResourceCount();
+ _wasparallelisable = 0;
}
//! @returns
//! The textual description of the last
//! server-related error. Returns @expr{0@} if no error has occurred
//! yet. It is not cleared upon reading (can be invoked multiple
//! times, will return the same result until a new error occurs).
//!
//! During the execution of a statement, this function accumulates all
//! non-error messages (notices, warnings, etc.). If a statement does not
pike.git/lib/modules/Sql.pmod/pgsql.pike:285:
return 0;
}
//! Check whether the connection is alive.
//!
//! @returns
//! Returns one of the following:
//! @int
//! @value 0
//! Everything ok.
- //! @value 1
- //! The connection has reconnected automatically.
+
//! @value -1
//! The server has gone away, and the connection is dead.
//! @endint
//!
//! @seealso
//! @[is_open()]
/*semi*/final int ping() {
- return is_open() && !catch(c->start()->sendcmd(FLUSHSEND))
- ? !!reconnected : -1;
+ waitauthready();
+ return is_open() && !catch(c->start()->sendcmd(FLUSHSEND)) ? 0 : -1;
}
//! Cancels all currently running queries in this session.
//!
//! @seealso
//! @[reload()], @[resync()]
//!
//! @note
//! This function is PostgreSQL-specific, and thus it is not available
//! through the generic SQL-interface.
pike.git/lib/modules/Sql.pmod/pgsql.pike:416:
//! Total number of prepared statements generated.
//! @member int "portals_opened_count"
//! Total number of portals opened, i.e. number of statements issued
//! to the database.
//! Only available if PG_STATS is compile-time enabled.
//! @member int "bytes_received"
//! Total number of bytes received from the database so far.
//! @member int "messages_received"
//! Total number of messages received from the database (one SQL-statement
//! requires multiple messages to be exchanged).
- //! @member int "reconnect_count"
- //! Number of times the connection to the database has been lost.
+
//! @member int "portals_in_flight"
//! Currently still open portals, i.e. running statements.
//! @endmapping
//!
//! @note
//! This function is PostgreSQL-specific, and thus it is not available
//! through the generic SQL-interface.
/*semi*/final mapping(string:mixed) getstatistics() {
mapping(string:mixed) stats=([
"warnings_dropped":warningsdropcount,
"current_prepared_statements":sizeof(_prepareds),
"current_prepared_statement_hits":totalhits,
"prepared_statement_count":pstmtcount,
#ifdef PG_STATS
"used_prepared_statements":prepstmtused,
"skipped_describe_count":skippeddescribe,
"portals_opened_count":portalsopened,
#endif
"messages_received":_msgsreceived,
"bytes_received":_bytesreceived,
- "reconnect_count":reconnected,
+
]);
return stats;
}
//! @param newdepth
//! Sets the new cachedepth for automatic caching of prepared statements.
//!
//! @returns
//! The previous cachedepth.
//!
pike.git/lib/modules/Sql.pmod/pgsql.pike:530:
int k=(int)offset;
if(k<=0)
return MARKSTART+query+MARKEND;
return MARKSTART+(k>1?query[..k-2]:"")+MARKERROR+query[k-1..]+MARKEND;
}
private void connect_cb() {
PD("%O\n",_runtimeparameter);
}
- private void reconnect_cb() {
- lastmessage+=({sprintf("Reconnected to database %s",host_info())});
- runcallback(backendpid,"_reconnect","");
- }
-
+
private array(string) showbindings(.pgsql_util.sql_result portal) {
array(string) msgs=({});
array from;
if(portal && (from = portal._params)) {
array to,paramValues;
[from,to,paramValues] = from;
if(sizeof(paramValues)) {
string val;
int i;
string fmt=sprintf("%%%ds %%3s %%.61s",max(@map(from,sizeof)));
pike.git/lib/modules/Sql.pmod/pgsql.pike:592:
final void _processloop(.pgsql_util.conxion ci) {
(c=ci)->socket->set_id(procmessage);
cancelsecret=0;
portal=0;
{
Stdio.Buffer plugbuffer=Stdio.Buffer()->add_int32(PG_PROTOCOL(3,0));
if(user)
plugbuffer->add("user\0",user,0);
if(database)
plugbuffer->add("database\0",database,0);
- _options.reconnect=undefinedp(_options.reconnect) || _options.reconnect;
+
foreach(_options-.pgsql_util.censoroptions; string name; mixed value)
plugbuffer->add(name,0,(string)value,0);
plugbuffer->add_int8(0);
PD("%O\n",(string)plugbuffer);
object cs;
if (catch(cs = ci->start())) {
- if(_options.reconnect)
- _connectfail();
- else
+
destruct(waitforauthready);
unnamedstatement=0;
termlock=0;
return;
} else {
CHAIN(cs)->add_hstring(plugbuffer, 4, 4);
cs->sendcmd(SENDOUT);
}
} // Do not flush at this point, PostgreSQL 9.4 disapproves
procmessage();
pike.git/lib/modules/Sql.pmod/pgsql.pike:640: Inside #if defined(PG_DEBUG)
#endif
void showportal(int msgtype) {
if(objectp(portal))
PD("%d<%O %d %c switch portal\n",
ci->socket->query_fd(),portal._portalname,++ci->queueinidx,msgtype);
else if(portal>0)
PD("%d<Sync %d %d %c portal\n",
ci->socket->query_fd(),++ci->queueinidx,portal,msgtype);
};
#endif
+ int msgisfatal(mapping(string:string) msgresponse) {
+ if (!terminating) // Run the callback once per lost connection
+ runcallback(backendpid,"_lost","");
+ return (has_prefix(msgresponse.C, "53")
+ || has_prefix(msgresponse.C, "3D")
+ || has_prefix(msgresponse.C, "57P")) && MAGICTERMINATE;
+ };
for(;;) {
err=catch {
#ifdef PG_DEBUG
if(!portal && datarowdebug) {
PD("%s rows %d\n",datarowdebug,datarowdebugcount);
datarowdebug=0; datarowdebugcount=0;
}
#endif
#ifdef PG_DEBUGMORE
showportalstack("LOOPTOP");
pike.git/lib/modules/Sql.pmod/pgsql.pike:726:
};
PD("Authentication ");
msglen-=4+4;
int authtype, k;
switch(authtype = cr->read_int32()) {
case 0:
PD("Ok\n");
if (SASLcontext) {
PD("Authentication validation still in progress\n");
errtype = PROTOCOLUNSUPPORTED;
- } else {
- .pgsql_util.local_backend->remove_call_out(reconnect);
- reconnectdelay=0;
+ } else
cancelsecret="";
- }
+
break;
case 2:
PD("KerberosV5\n");
errtype=PROTOCOLUNSUPPORTED;
break;
case 3:
PD("ClearTextPassword\n");
authresponse(({pass, 0}));
break;
case 4:
pike.git/lib/modules/Sql.pmod/pgsql.pike:1135:
warningscollected=0;
switch(msgresponse.C) {
case "P0001":
lastmessage=({sprintf("%s: %s",msgresponse.S,msgresponse.M)});
USERERROR(a2nls(lastmessage
+({pinpointerror(portal._query,msgresponse.P)})
+showbindings(portal)));
case "53000":case "53100":case "53200":case "53300":case "53400":
case "57P01":case "57P02":case "57P03":case "57P04":case "3D000":
preplastmessage(msgresponse);
- PD(a2nls(lastmessage));throw(0);
+ PD(a2nls(lastmessage)); throw(msgisfatal(msgresponse));
case "08P01":case "42P05":
errtype=PROTOCOLERROR;
case "XX000":case "42883":case "42P01":
invalidatecache=1;
default:
preplastmessage(msgresponse);
if(msgresponse.D)
lastmessage+=({msgresponse.D});
if(msgresponse.H)
lastmessage+=({msgresponse.H});
pike.git/lib/modules/Sql.pmod/pgsql.pike:1173:
mapping(string:string) msgresponse;
msgresponse=getresponse();
if(clearmessage) {
warningsdropcount+=warningscollected;
clearmessage=warningscollected=0;
lastmessage=({});
}
warningscollected++;
lastmessage=({sprintf("%s %s: %s",
msgresponse.S,msgresponse.C,msgresponse.M)});
- if(has_prefix(msgresponse.C,"53")||has_prefix(msgresponse.C,"57P")) {
+ int val;
+ if (val = msgisfatal(msgresponse)) { // Some warnings are fatal
preplastmessage(msgresponse);
- PD(a2nls(lastmessage));throw(0); // Some warnings are fatal
+ PD(a2nls(lastmessage));throw(val);
}
break;
}
case 'A': {
PD("NotificationResponse\n");
msglen-=4+4;
int pid=cr->read_int32();
string condition,extrainfo=UNDEFINED;
{
array(string) ts=reads();
pike.git/lib/modules/Sql.pmod/pgsql.pike:1275:
break;
}
PD("Closing database processloop %s\n", err ? describe_backtrace(err) : "");
_delayederror=err;
if (objectp(portal)) {
#ifdef PG_DEBUG
showportal(0);
#endif
portal->_purgeportal();
}
- if(!terminating && _options.reconnect)
- _connectfail();
- else
+
destruct(waitforauthready);
termlock=0;
if(err && !stringp(err))
throw(err);
};
- if (err) {
+
unnamedstatement=0;
termlock = 0;
-
+ if (err) {
PD("Terminating processloop due to %s\n", describe_backtrace(err));
}
-
+ _connectfail(err);
}
//! Closes the connection to the database, any running queries are
//! terminated instantly.
//!
//! @note
//! This function is PostgreSQL-specific, and thus it is not available
//! through the generic SQL-interface.
/*semi*/final void close() {
throwdelayederror(this);
pike.git/lib/modules/Sql.pmod/pgsql.pike:1322:
protected void _destruct() {
string errstring;
mixed err = catch(close());
backendreg = 0;
/*
* Flush out any asynchronously reported errors to stderr; because we are
* inside a destructor, throwing an error will not work anymore.
* Warnings will be silently discarded at this point.
*/
- lastmessage = filter(lastmessage, has_prefix, "ERROR ");
+ lastmessage = filter(lastmessage, lambda(string val) {
+ return has_prefix(val, "ERROR ") || has_prefix(val, "FATAL "); });
if (err || (err = catch(errstring = error(1))))
werror(describe_backtrace(err));
else if (errstring && sizeof(errstring))
werror("%s\n", errstring); // Add missing terminating newline
}
final void _connectfail(void|mixed err) {
- PD("Connect failed %O reconnectdelay %d\n",err,reconnectdelay);
- if(!err || reconnectdelay) {
- int tdelay;
- switch(tdelay=reconnectdelay) {
- case 0:
- reconnectdelay=RECONNECTDELAY;
- break;
- default:
- if(err)
- _delayederror=err;
- if (_options.reconnect!=-1) {
+ if (err) {
+ PD("Connect failed %O\n", err);
+ _delayederror = err;
+ }
destruct(waitforauthready);
destruct(c);
- return;
+
}
- reconnectdelay=RECONNECTBACKOFF;
- break;
- }
- Thread.MutexKey lock=_shortmux->lock();
- if(!waitforauthready)
- waitforauthready=Thread.Condition();
- lock=0;
- PD("Schedule reconnect in %ds\n",tdelay);
- _delayederror=0;
- callout(reconnect,tdelay,1);
- } else if(err)
- _delayederror=err;
- }
+
- private int reconnect() {
- int recon=0;
- PD("(Re)connect\n");
- {
- Thread.MutexKey lock=_shortmux->lock();
- if (!waitforauthready)
- waitforauthready=Thread.Condition();
- lock=0;
- }
- if(c) {
- PD("Close old connection\n");
- reconnected++;recon=1;
- #ifdef PG_STATS
- prepstmtused=0;
- #endif
- if (unnamedstatement)
- termlock = unnamedstatement->lock(1);
- catch(c->close());
- unnamedstatement = 0;
- termlock = 0;
- catch(destruct(c));
- PD("Flushing old cache\n");
- foreach(_prepareds;;mapping tp)
- m_delete(tp,"preparedname");
- if(!_options.reconnect)
- ERROR("Lost connection to database %s:%d\n",_host,_port);
- }
- PD("Actually start to connect\n");
- qportals=Thread.Queue();
- _readyforquerycount=1;
- qportals->write(1);
- if (!(c = .pgsql_util.conxion(this, qportals, 0)))
- ERROR("Couldn't connect to database on %s:%d\n",_host,_port);
- _runtimeparameter=([]);
- _unnamedportalmux=Thread.Mutex();
- unnamedstatement=Thread.Mutex();
- readyforquery_cb=recon?reconnect_cb:connect_cb;
- _portalsinflight = Thread.ResourceCount();
- _statementsinflight = Thread.ResourceCount();
- _wasparallelisable = 0;
- return 1;
- }
-
+
//! For PostgreSQL this function performs the same function as @[resync()].
//!
//! @seealso
//! @[resync()], @[cancelquery()]
/*semi*/final void reload() {
resync();
}
private void reset_dbsession() {
big_query("ROLLBACK");
pike.git/lib/modules/Sql.pmod/pgsql.pike:1468:
PD("Statementsinflight: %d Portalsinflight: %d\n",
_statementsinflight, _portalsinflight);
if(!waitforauthready) {
readyforquery_cb=resync_cb;
sendsync();
}
return;
};
PD("%O\n",err);
}
- if(!reconnect()&&sizeof(lastmessage))
+ if(sizeof(lastmessage))
ERROR(a2nls(lastmessage));
}
- //! This function allows you to connect to a database. Due to
- //! restrictions of the Postgres frontend-backend protocol, you always
- //! have to be connected to a database, so in fact this function just
- //! allows you to connect to a different database on the same server.
+ //! Due to restrictions of the Postgres frontend-backend protocol, you always
+ //! already have to be connected to a database.
+ //! To connect to a different database you have to select the right
+ //! database while connecting instead. This function is a no-op when
+ //! specifying the same database, and throws an error otherwise.
//!
- //! @note
- //! This function @b{can@} raise exceptions if something goes wrong
- //! (backend process not running, insufficient privileges...)
- //!
+
//! @seealso
//! @[create()]
/*semi*/final void select_db(string dbname) {
- database=dbname;
- reconnect();
- reconnected=0;
+ if (database != dbname)
+ ERROR("Cannot switch databases from %O to %O"
+ " in an already established connection\n",
+ database, dbname);
}
//! With PostgreSQL you can LISTEN to NOTIFY events.
//! This function allows you to detect and handle such events.
//!
//! @param condition
//! Name of the notification event we're listening
//! to. A special case is the empty string, which matches all events,
//! and can be used as fallback function which is called only when the
//! specific condition is not handled. Another special case is
- //! @expr{"_reconnect"@} which gets called whenever the connection
- //! unexpectedly drops and reconnects to the database.
+ //! @expr{"_lost"@} which gets called whenever the connection
+ //! to the database unexpectedly drops.
//!
//! @param notify_cb
//! Function to be called on receiving a notification-event of
//! condition @ref{condition@}.
//! The callback function is invoked with
//! @expr{void notify_cb(pid,condition,extrainfo, .. args);@}
//! @tt{pid@} is the process id of the database session that originated
//! the event. @tt{condition@} contains the current condition.
//! @tt{extrainfo@} contains optional extra information specified by
//! the database.
pike.git/lib/modules/Sql.pmod/pgsql.pike:1970:
* multistatement text-queries?
* The primary function of this detection is to ensure a SYNC
* right after a COMMIT, and no SYNC after a BEGIN.
*/
int transtype = .pgsql_util.transendprefix->match(q) ? TRANSEND
: .pgsql_util.transbeginprefix->match(q) ? TRANSBEGIN : NOTRANS;
if (transtype != NOTRANS)
tp = .pgsql_util.describenodata; // Description already known
else if (!forcetext && forcecache == 1
|| forcecache && sizeof(q) >= MINPREPARELENGTH) {
- object plugbuffer;
- while(catch(plugbuffer=c->start()))
- reconnect();
+ object plugbuffer = c->start();
if(tp=_prepareds[q]) {
if(tp.preparedname) {
#ifdef PG_STATS
prepstmtused++;
#endif
preparedname=tp.preparedname;
} else if((tstart=tp.trun)
&& tp.tparse*FACTORPLAN>=tstart
&& (undefinedp(_options.cache_autoprepared_statements)
|| _options.cache_autoprepared_statements))