2013/6/24 Albe Laurenz <laurenz.a...@wien.gv.at>

> I'm moving this discussion to -general.
>
Okay, lets continue here.

>
> Dmitriy Igrishin wrote:
> >>> While developing a C++ client library for Postgres I felt lack of extra
> >>> information in command tags in the CommandComplete (B) message [...]
> >>> for the following commands:
>
> >> It seems like bad design to me to keep a list of prepared statements
> >> on the client side when it is already kept on the server side
> >> (accessible with the pg_prepared_statements view).
> >>
> >> What's wrong with the following:
> >> If the user wants to deallocate an individual prepared statement,
> >> just send "DEALLOCATE <statement name>" to the server.  If the
> >> statement does not exist, the server will return an error.
> >> If the user wants to deallocate all statements, just send
> >> "DEALLOCATE ALL".
> >> Why do you need to track prepared statements on the client side?
> >
> >
> > Nothing wrong if the user wants to deal with scary and cumbersome code.
> > As library author, I want to help people make things simpler.
>
> I don't think that anything would change on the user end.
>
But I think so.

>
> > To understand me, please look at the pseudo C++ code below.
> >
> >
> > // A class designed to work with prepared statements
> > class Prepared_statement {
> >
> > public:
> >   // Methods to generate a Bind message, like
> >   Prepared_statement* bind(Position, Value);
> >   // ... and more
> >   // Methods to send Execute message, like
> >   void execute();
> >   void execute_async();
> > };
> >
> > class Connection {
> > public:
> >   // many stuff ...
> >   void close();
> >
> >   Prepared_statement* prepare(Name, Query);
> >   void prepare_async(Statement);
> >
> >   // Make yet another instance of prepared statement.
> >   Prepared_statement* prepared_statement(Name);
> >
> >   // etc.
> > };
> >
> > The Connection class is a factory for Prepared_statement instances.
> > As you can see, the Connection::prepare() returns new instance of
> > *synchronously* prepared statement. Next, the user can bind values
> > and execute the statement, like this:
> >
> > void f(Connection* cn)
> > {
> >   // Prepare unnamed statement and execute it.
> >   cn->prepare("SELECT $1::text")->bind(0, "Albe")->execute();
> >   // Ps: don't worry about absence of delete; We are using smart
> pointers :-)
> > }
> >
> > But there is a another possible case:
> >
> > void f(Connection* cn)
> > {
> >   Prepared_statement* ps = cn->prepare("SELECT $1::text");
> >   cn->close(); // THIS SHOULD invalidate all Prepared_statement
> instances ...
> >   ps->bind(0, "Albe"); // ... to throw the exception here
> > }
>
> Attempting to send a bind message over a closed connection
> will result in a PostgreSQL error.  All you have to do is wrap
> that into an exception of your liking.
>
Okay, thanks for the info.

>
> > Moreover, consider:
> >
> > void f(Connection* cn)
> > {
> >   Prepared_statement* ps1 = cn->prepare("ps1", "SELECT $1::text");
> >
> >   cn->deallocate("ps1"); // THIS invalidates ps1 object...
>
> Shouldn't that be
>   cn->deallocate(ps1);
> without quotes?
>
No, because Connection::deallocate(const string&) considered by me as a
wrapper over
DEALLOCATE SQL command. (As any other SQL command wrapper declared as the
Connection class member.) But it can be overloaded though, but there are
Prepared_statement::deallocate(void) (without arguments) instead.

>
> >   ps1->bind(0, "Albe"); // ... to throw the exception here
> >
> >
> >   Prepared_statement* ps2 = cn->prepare("ps2", "SELECT $1::text");
> >
> >   cn->perform("DEALLOCATE ps2"); // THIS SHOULD ALSO invalidate ps2
> object...
> >   ps2->bind(0, "Albe"); // ... to throw the exception here
> >
> > }
>
> Again, sending a bind message for a deallocated prepared statement
> will cause a PostgreSQL error automatically.
>
Thats great, but there is a some problem -- the *another* statement with
the same
name (and moreover with same parameters!) can be prepared after
deallocating.
And this can result in bugs. So, the client-side allocated "pointer to the
remote
statement" must be invalidated immediatly after deallocating.

>
> > In the latter case when the user deallocates named prepared statement
> directly,
> > the implementation of Connection can invalidates the prepared statement
> (ps2) by
> > analyzing and parsing CommandComplete command tag to get it's name.
> >
> > And please note, that the user can send DEALLOCATE asynchronously. And
> there is
> > only two ways to get the prepared statement (or another session
> object's) name:
> >   1) Parse the SQL command which the user is attempts to send;
> >   2) Just get it from CommandComplete command tag.
> >
> > I beleive that the 1) is a 100% bad idea.
> >
> > PS: this C++11 library is not publicaly available yet, but I hope it
> will this year.
>
> I still think that it is a bad idea to track this on the client side.
>
> What's wrong with throwing an exception when you get a PostgreSQL error?
> If you want to distinguish between certain error conditions,
> you can use the SQLSTATE.  For example, trying to execute a deallocated
> statement would cause SQLSTATE 26000.
>
See above why it make sense.

// Dmitriy.

Reply via email to