pdo design

php.internals

Ard Biesheuvel

22 years ago
Guys, I saw the alpha of PDO on pecl. I was wondering if we could discuss the design of it a bit more before putting too much work into it. In particular, the way transactions are represented is not very practical. Generally, databases that support transactions will support multiple concurrent transactions against the same database. Firebird also supports transactions against multiple databases. [which does *not* mean you can join tables from different DBs, it just means ACID over multiple DBs] This means that in some cases you must specify both the connection and the transaction when generating a statement. [I'm not sure if others RDBMSs support this, but it might be something we will see more in the future.] In short: In addition to $db->startTransaction(), maybe we could add PDO::startTransaction($db1 [,$db2 [,$db3]]). In addition to $db->prepare(), maybe we could add a Transaction interface whose instances would allow being called as $trans->prepare($query) and maybe even $trans->prepare($db, $query) Your thoughts please ?
-- Ard

Wez Furlong

22 years ago
Hey Ard, When you say database, do you mean separate database connection, or separate named databases on the same connection? PDO::beginTransaction() initiates a transaction for a given connection ($dbh) in a more or less portable way. You're not always guaranteed to be able to do that; extending it for multiple database handles seems a bit optimistic :) Could you expand on what Firebird does here? Are there other DB's that support this too? --Wez.

George Schlossnagle

22 years ago
On May 22, 2004, at 11:50 AM, Wez Furlong wrote:
> Hey Ard, > > When you say database, do you mean separate database connection, or > separate > named databases on the same connection? > > PDO::beginTransaction() initiates a transaction for a given connection > ($dbh) > in a more or less portable way. You're not always guaranteed to be > able to do > that; extending it for multiple database handles seems a bit > optimistic :) > > Could you expand on what Firebird does here? Are there other DB's that > support this too? >
db4 supports this. George

Ard Biesheuvel

22 years ago
Wez Furlong wrote:
> When you say database, do you mean separate database connection, or separate > named databases on the same connection?
Well, first of all, Firebird does not distinguish between databases and database connections. So connecting to several databases on the same host is basically the same thing as connecting to several databases which are on different hosts. This goes for the multi-DB transactions as well. Every transaction uses 1 or several connections, regardless of which host they're on. Committing multi-DB transactions uses some kind of two-phase locking scheme to ensure atomicity and consistency between all the involved databases.
> PDO::beginTransaction() initiates a transaction for a given connection ($dbh) > in a more or less portable way. You're not always guaranteed to be able to do > that; extending it for multiple database handles seems a bit optimistic :)
Yeah I know, but the current code doesn't even allow multiple transactions against a single database on the same connection, does it ? This is something I'm sure other databases besides Firebird support as well. Adding PDO_Transaction::start($db1[, $db2 ...]) for multiple databases would be nice too, but it's not as important. It's just that I think this might be something that other DBs might already support, or will support in the future.
> Could you expand on what Firebird does here? Are there other DB's that > support this too?
For completeness, I'll add some other features that I think have analogies in other DBs (Oracle, Postgres). [though I'm not saying they should all be supported by PDO] - transaction flags (concurrency, read/write, etc), - executing SQL with placeholders directly, - preparing SQL with placeholders and executing it 1 or several times, - returning information on placeholders in a prepared statement (type), - naming resultsets and using them in 'WHERE CURRENT OF <name>' statements, - [b]lob API, - adding/removing users, stopping/restarting & other maintenance stuff. I'm not saying we should try to squeeze everything into PDO, but we should at least make the API leave some room for it.
-- Ard

Wez Furlong

22 years ago
> Yeah I know, but the current code doesn't even allow multiple > transactions against a single database on the same > connection, does it ?
> Adding PDO_Transaction::start($db1[, $db2 ...]) for multiple > databases > would be nice too, but it's not as important. It's just that I think > this might be something that other DBs might already support, or will > support in the future.
PDO tries (a bit) to keep things portable: if it's a method of the PDO or PDOStatement classes, then you can be reasonably sure it's supported for a given driver. That doesn't mean that I want to exclude all non-portable behaviour, I just want to make sure that we don't overload the API too much and make things harder in the future for when those databases grow and add new features. The current transactional behaviour of PDO is this, FYI, a fairly standard semantic: - new connections start in auto-commit mode (ala ODBC) - in auto-commit mode, there is an implicit commit after each query - if you begin a txn in auto-commit mode, auto-commit is disabled until you rollback or commit. - it is an error to begin a new transaction while inside a transaction, since nested transactions are not portable - if a driver does not support transactions, an exception is raised (regardless of your choice of error handling settings) when you attempt to begin a transaction or disable auto-commit mode The PDO philosophy on driver specific behaviour can be generally summed up as: if you can't do it portably, use a driver/db specific API or query. Right now, I think I would prefer to defer making a decision on multi-connection transactions, with a view to implementing it as a separate API (pdo_multi_txn_begin() or something like that) so that it is clear that it operates on separate connections. If it does appear that only firebird supports this, then I would prefer to have that function in the pdo_firebird extension only, at least until other databases appear and we can see how their API operates and find some common ground. Does this cause a big problem for using firebird in PHP? (How often do you open separate connections to separate databases?)
> - transaction flags (concurrency, read/write, etc), > - naming resultsets and using them in 'WHERE CURRENT OF <name>' > statements, > - [b]lob API,
Already planned, along with cursors (and positioned updates using named result sets).
> - executing SQL with placeholders directly, > - preparing SQL with placeholders and executing it 1 or several times,
This is already implemented, although one-shot with placeholders is not yet.
> - returning information on placeholders in a prepared > statement (type),
Just wondering, how often do you need to query the types of placeholders? PDO works in the opposite sense: you tell the database the type you are setting and intending to receive.
> - adding/removing users, stopping/restarting & other > maintenance stuff.
These can (perhaps should) be extension level functions and not methods of the PDO class, unless they happen to be uniform API's across the majority of APIs. --Wez.

Ard Biesheuvel

22 years ago
Wez Furlong wrote:
> PDO tries (a bit) to keep things portable: if it's a method of the PDO or > PDOStatement classes, then you can be reasonably sure it's supported for a > given driver. That doesn't mean that I want to exclude all non-portable > behaviour, I just want to make sure that we don't overload the API too much > and make things harder in the future for when those databases grow and add new > features.
I see, and I understand that it's pointless to add features to a general purpose framework that can only be supported by a single implementation. Still, it would be nice to have a syntax that can basically allow both.
> The current transactional behaviour of PDO is this, FYI, a fairly standard > semantic: > > - new connections start in auto-commit mode (ala ODBC) > - in auto-commit mode, there is an implicit commit after each query
Currently, PHP/Interbase uses one implicit transaction for the entire request. I think that, by the nature of PHP, if transactions can be supported, they should span all the statements executed during a request. [which should still be committed if something goes wrong, but would maintain a consistent view of the database throughout the request]
> - if you begin a txn in auto-commit mode, auto-commit is disabled until you > rollback or commit. > - it is an error to begin a new transaction while inside a transaction, since > nested transactions are not portable
Multiple concurrent transactions are not necessarily nested. It is perfectly legal in many RDBMSs to start two transactions against a database (one could be read-only for instance, or use a different isolation policy)
> - if a driver does not support transactions, an exception is raised > (regardless of your choice of error handling settings) when you attempt to > begin a transaction or disable auto-commit mode
You might turn commit() into a NOP and raise the error on rollback() only, as it's the responsibility of the user to choose a DB that fits his needs. This would enhance portability IMO
> The PDO philosophy on driver specific behaviour can be generally summed up as: > if you can't do it portably, use a driver/db specific API or query.
Yeah, that brings up another point. If you execute 'SET TRANSACTION ...' in Firebird, the result will be a transaction (handle). Do you think this will fit the current scheme ?
> Right now, I think I would prefer to defer making a decision on > multi-connection transactions, with a view to implementing it as a separate > API (pdo_multi_txn_begin() or something like that) so that it is clear that it > operates on separate connections. If it does appear that only firebird > supports this, then I would prefer to have that function in the pdo_firebird > extension only, at least until other databases appear and we can see how their > API operates and find some common ground.
I was merely suggesting we'd look for a way to design the API in such a way that the transaction is not a [singular] property of a connection; if we end up introducing a transaction interface, I would like its dbh property to allow multiple values as well [for the reasons I have pointed out]
> Does this cause a big problem for using firebird in PHP? (How often do you > open separate connections to separate databases?)
You're right, most people will probably never connect to more than one database. It's still a nice feature, though. As far as the transaction are concerned, I think multiple [non-nested] transactions should really be allowed.
>>- executing SQL with placeholders directly, > > This is already implemented, although one-shot with placeholders is not yet.
This feature is not as pointless as some people may think. Even if you execute a query only once, you won't be forced to [add|strip]_slashes() strings or decide whether or not to add quotes.
>>- returning information on placeholders in a prepared >>statement (type), > > Just wondering, how often do you need to query the types of placeholders? > PDO works in the opposite sense: you tell the database the type you are > setting and intending to receive.
Well, Firebird knows the types of its params and fields, so there's no need to specify them when binding params. The opposite would only be of use if you want to analyze an unknown (user-provided) query. [which, I agree, won't happen that often]
> >>- adding/removing users, stopping/restarting & other >>maintenance stuff. > > > These can (perhaps should) be extension level functions and not methods of the > PDO class, unless they happen to be uniform API's across the majority of APIs.
I think most databases allow SQL statements to carry out these tasks, which makes this one a non-issue. Did you have any plans on emulation layers for some of this stuff ? For instance, binding output params is [currently] not supported by PHP/InterBase, but this is something that can easily be handled in a generic way.
-- Ard

Wez Furlong

22 years ago
> Currently, PHP/Interbase uses one implicit transaction for the entire > request. I think that, by the nature of PHP, if transactions can be > supported, they should span all the statements executed during a > request. [which should still be committed if something goes > wrong, but > would maintain a consistent view of the database throughout > the request]
The semantic used by PDO (and the other transactional db's in PHP) is to rollback any pending transaction on rshutdown.
> Multiple concurrent transactions are not necessarily nested. It is > perfectly legal in many RDBMSs to start two transactions against a > database (one could be read-only for instance, or use a different > isolation policy)
You can do this in PDO, but require a separate $dbh for each transaction. PDO doen't separate the concept of a session or environment from a connection to a database. This is perhaps where you are finding the fault. The thing is that not all databases work in this way, so we've gone for the more portable approach again.
> > - if a driver does not support transactions, an exception is raised > > (regardless of your choice of error handling settings) when > you attempt to > > begin a transaction or disable auto-commit mode > > You might turn commit() into a NOP and raise the error on rollback() > only, as it's the responsibility of the user to choose a DB that fits > his needs. This would enhance portability IMO
No, it is important for the script to fail the moment it attempts to use transactions on a non-transactional database, otherwise you risk writing garbage to it, with no way to rollback.
> > The PDO philosophy on driver specific behaviour can be > generally summed up as: > > if you can't do it portably, use a driver/db specific API or query. > > Yeah, that brings up another point. > If you execute 'SET TRANSACTION ...' in Firebird, the result > will be a > transaction (handle). Do you think this will fit the current scheme ?
Database specific stuff again ;) I do intend to allow PDO to return this kind of structured value. Again, I'd like to build up a list of the kind of things that are reasonably portable before going into that aspect in great detail.
> You're right, most people will probably never connect to more > than one > database. It's still a nice feature, though. As far as the > transaction > are concerned, I think multiple [non-nested] transactions > should really > be allowed.
How would that look in the script?
> I think most databases allow SQL statements to carry out these tasks, > which makes this one a non-issue. > > Did you have any plans on emulation layers for some of this > stuff ? For > instance, binding output params is [currently] not supported by > PHP/InterBase, but this is something that can easily be handled in a > generic way.
Courtesy of George, we have emulation of placeholders for input parameters for databases that lack it; it's used by pdo_mysql and pdo_pgsql. We also have generic binding to output columns handled by PDO itself. --Wez.

Ard Biesheuvel

22 years ago
Wez Furlong wrote:
> PDO doen't separate the concept of a session or environment from a connection > to a database. This is perhaps where you are finding the fault. The thing is > that not all databases work in this way, so we've gone for the more portable > approach again.
I see that, but my point is that is it not very constructive to code PDO around the assumption that a connection can never have more than one transaction at any point in the future. The same goes for the number of connections involved in a single transaction. Maybe startTransaction could query the specific driver instead of disallowing multiple transactions altogether. I think portability should not limit versatility.
> No, it is important for the script to fail the moment it attempts to use > transactions on a non-transactional database, otherwise you risk writing > garbage to it, with no way to rollback.
I guess that's a matter of taste. There are more uses for transactions than being able to roll them back. I consider it good coding practice to group related updates, and execute them inside a transaction block. But this isn't a big issue.
> Database specific stuff again ;) > I do intend to allow PDO to return this kind of structured value. > Again, I'd like to build up a list of the kind of things that are reasonably > portable before going into that aspect in great detail.
Yeah, I know, but now is our chance to think of a generic way to return other things than statement or result sets from a query execution. I'm not saying each and every driver should support it, I'm just suggesting we shouldn't disallow it altogether.
>>You're right, most people will probably never connect to more >>than one >>database. It's still a nice feature, though. As far as the >>transaction >>are concerned, I think multiple [non-nested] transactions >>should really >>be allowed. > > How would that look in the script?
Transaction parameters can be used to tune the amount of possible concurrency between different requests. Being able to execute one statement inside a read-only transaction will allow the database to optimize its locking behavior. Another point is the visibility of concurrent transactions: monitoring a logging table in a read-only read-committed transaction while executing updates in a concurrent snapshot read/write transaction can be very useful.
> Courtesy of George, we have emulation of placeholders for input parameters for > databases that lack it; it's used by pdo_mysql and pdo_pgsql.
OK, that's nice, but for instance, can we make sure that it's possible that the driver figures out what the param types are ? As Firebird is able to determine this by itself, without the need of specifying it.
-- Ard

George Schlossnagle

22 years ago
On May 23, 2004, at 1:39 PM, Ard Biesheuvel wrote:
> Wez Furlong wrote: >> PDO doen't separate the concept of a session or environment from a >> connection >> to a database. This is perhaps where you are finding the fault. The >> thing is >> that not all databases work in this way, so we've gone for the more >> portable >> approach again. > > I see that, but my point is that is it not very constructive to code > PDO around the assumption that a connection can never have more than > one transaction at any point in the future. The same goes for the > number of connections involved in a single transaction. Maybe > startTransaction could query the specific driver instead of > disallowing multiple transactions altogether. I think portability > should not limit versatility.
I agree with Ard, though I'm uncertain what a good solution is. Certainly plenty of database (Oracle included) have a notion of transactions existing in (possibly concurrent) sessions. Supporting this seems very beneficial. Supporting this without making more crap for the simpler databases is a bit tricky. At first glance I think something like this: $dbh = new PDO(...); $session = $dbh->newSession() // $session is a cloned copy of $dbh in mysql, is a new session in oracle I think some sort of support along these lines is good, though I imagine the above syntax has all sorts of problems.
> >> No, it is important for the script to fail the moment it attempts to >> use >> transactions on a non-transactional database, otherwise you risk >> writing >> garbage to it, with no way to rollback. > > I guess that's a matter of taste. There are more uses for transactions > than being able to roll them back. I consider it good coding practice > to group related updates, and execute them inside a transaction block. > But this isn't a big issue.
I agree with Wez. Implicitly lying about your support for transactions is dangerous, I think.
>>> You're right, most people will probably never connect to more than >>> one database. It's still a nice feature, though. As far as the >>> transaction are concerned, I think multiple [non-nested] >>> transactions should really be allowed. >> How would that look in the script? > > Transaction parameters can be used to tune the amount of possible > concurrency between different requests. Being able to execute one > statement inside a read-only transaction will allow the database > to optimize its locking behavior. Another point is the visibility of > concurrent transactions: monitoring a logging table in a read-only > read-committed transaction while executing updates in a concurrent > snapshot read/write transaction can be very useful.
I agree on multiple transaction support. And why not nested, if the underlying database supports it (db4 does)?
>> Courtesy of George, we have emulation of placeholders for input >> parameters for >> databases that lack it; it's used by pdo_mysql and pdo_pgsql. > > OK, that's nice, but for instance, can we make sure that it's possible > that the driver figures out what the param types are ? As Firebird is > able to determine this by itself, without the need of specifying it.
I don't get this question, can someone reiterate the full context of it for me? George

Ard Biesheuvel

22 years ago
George Schlossnagle wrote:
> I don't get this question, can someone reiterate the full context of it > for me?
For instance, if you prepare the statement "SELECT * FROM <table> WHERE id=?" Firebird can figure out the type of the 'id' param, meaning you don't have to bind it to a certain type, whereas the emulated param binding routines cannot. Additionally, MySQL is a lot more forgiving than Firebird if you put quotes around non-string arguments inside an SQL statement. So we might decide to make the type specification optional, and let it default to 'string'.
-- Ard

George Schlossnagle

22 years ago
On May 23, 2004, at 2:55 PM, Ard Biesheuvel wrote:
> George Schlossnagle wrote: >> I don't get this question, can someone reiterate the full context of >> it for me? > > For instance, if you prepare the statement > > "SELECT * FROM <table> WHERE id=?" > > Firebird can figure out the type of the 'id' param, meaning you don't > have to bind it to a certain type, whereas the emulated param binding > routines cannot. > Additionally, MySQL is a lot more forgiving than Firebird if you put > quotes around non-string arguments inside an SQL statement. So we > might decide to make the type specification optional, and let it > default to 'string'.
If firebird can figure that out, that's great. It should store the column type info internally and use it when bind is called. Mysql doesn't provide this facility, so it has to be more aggressive in it's quoting. These largely seem like driver-internal issues - at least I don't see at all how this should affect the public-facing api. George

Lukas Smith

22 years ago
Ard Biesheuvel wrote:
> Wez Furlong wrote: > You might turn commit() into a NOP and raise the error on rollback() > only, as it's the responsibility of the user to choose a DB that fits > his needs. This would enhance portability IMO
Doesnt sound like a good idea to me. Since the default is auto commit all is well. If you move out of auto commit mode then you do this for the exact reason of potentially wanting to rollback. But anyways maybe I didnt understand what you were trying to say as you only mention commit() but not getting out of auto commit mode.
>> These can (perhaps should) be extension level functions and not >> methods of the >> PDO class, unless they happen to be uniform API's across the majority >> of APIs. > > > I think most databases allow SQL statements to carry out these tasks, > which makes this one a non-issue.
Yup. The main piece of information needed is the version number since sometimes these maintaince query syntaxes change. One thing that you cant query for though which atleast seem RDBMS do is fetch the additional metadata provided inside a result set. Like the table name(s) etc. Would be nice if PDO would provide this information as well. regards, Lukas Smith smith@backendmedia.com _______________________________ BackendMedia www.backendmedia.com berlin@backendmedia.com Linn Zwoch Smith GbR Pariser Str. 44 D-10707 Berlin Tel +49 30 83 22 50 00 Fax +49 30 83 22 50 07

Alan Knowles

22 years ago
Just a thought - feel free to ignore... In dataobjects rather than implement extra methods for transactions, I just hooked into query .. eg. $do->query('BEGIN') - turned off autocommit, and ran begin. $do->query('ROLLBACK') or $do->('COMMIT'); - ranit , then turned autocommit back on It seemed far more sensible than fattening up the API. Regards Alan Wez Furlong wrote:

George Schlossnagle

22 years ago
On May 22, 2004, at 9:12 PM, Alan Knowles wrote:
> Just a thought - feel free to ignore... > In dataobjects rather than implement extra methods for transactions, I > just hooked into query > .. > eg. $do->query('BEGIN') - turned off autocommit, and ran begin. > $do->query('ROLLBACK') or $do->('COMMIT'); - ranit , then turned > autocommit back on > > It seemed far more sensible than fattening up the API.
The problem is that those have different syntaxes in different rdbms systems. George

Alan Knowles

22 years ago
George Schlossnagle wrote:
> > On May 22, 2004, at 9:12 PM, Alan Knowles wrote: > >> Just a thought - feel free to ignore... >> In dataobjects rather than implement extra methods for transactions, >> I just hooked into query >> .. >> eg. $do->query('BEGIN') - turned off autocommit, and ran begin. >> $do->query('ROLLBACK') or $do->('COMMIT'); - ranit , then turned >> autocommit back on >> >> It seemed far more sensible than fattening up the API. > > > The problem is that those have different syntaxes in different rdbms > systems.
thats why it hooks into the query (or exec method, and re-writes it).. In dataobjects it something like this. function query($str) { if ($str == 'BEGIN') { $db->begin(); ... this deals with the different syntaxes.. return; } $db->query($str); } Regards Alan

Andi Gutmans

22 years ago
Wouldn't that be kind of slow because we'd need to check the query for these special cases? Andi At 09:12 AM 5/23/2004 +0800, Alan Knowles wrote:

Lukas Smith

22 years ago
Andi Gutmans wrote:
> Wouldn't that be kind of slow because we'd need to check the query for > these special cases?
> At 09:12 AM 5/23/2004 +0800, Alan Knowles wrote: > >> Just a thought - feel free to ignore... >> In dataobjects rather than implement extra methods for transactions, I >> just hooked into query >> .. >> eg. $do->query('BEGIN') - turned off autocommit, and ran begin. >> $do->query('ROLLBACK') or $do->('COMMIT'); - ranit , then turned >> autocommit back on >> >> It seemed far more sensible than fattening up the API.
I also think that aside from having to parse all queries its opening up a can of worms to force a certain SQL syntax to RDBMS that follow a different syntax. I think that whereever we abstract it must be very clear for the user that its abstraction and therefore not the default behaviour of the RDBMS. regards, Lukas Smith smith@backendmedia.com _______________________________ BackendMedia www.backendmedia.com berlin@backendmedia.com Linn Zwoch Smith GbR Pariser Str. 44 D-10707 Berlin Tel +49 30 83 22 50 00 Fax +49 30 83 22 50 07

Alan Knowles

22 years ago
Andi Gutmans wrote:
> Wouldn't that be kind of slow because we'd need to check the query for > these special cases?
hehe :) - in comparison to getting the data out of the database? Regards alan
> > Andi > > At 09:12 AM 5/23/2004 +0800, Alan Knowles wrote: > >> Just a thought - feel free to ignore... >> In dataobjects rather than implement extra methods for transactions, I >> just hooked into query >> .. >> eg. $do->query('BEGIN') - turned off autocommit, and ran begin. >> $do->query('ROLLBACK') or $do->('COMMIT'); - ranit , then turned >> autocommit back on >> >> It seemed far more sensible than fattening up the API. >> >> Regards >> Alan >> >> >> >> >> Wez Furlong wrote: >> >>> Hey Ard, >>> >>> When you say database, do you mean separate database connection, or >>> separate >>> named databases on the same connection? >>> >>> PDO::beginTransaction() initiates a transaction for a given >>> connection ($dbh) >>> in a more or less portable way. You're not always guaranteed to be >>> able to do >>> that; extending it for multiple database handles seems a bit >>> optimistic :) >>> >>> Could you expand on what Firebird does here? Are there other DB's that >>> support this too? >>> >>> --Wez. >>> >>> >>> >>>> -----Original Message----- >>>> From: Ard Biesheuvel [mailto:a.k.biesheuvel@ewi.tudelft.nl] Sent: 22 >>>> May 2004 16:39 >>>> To: internals@lists.php.net >>>> Cc: helly@php.net; ilia@prohost.org; wez@thebrainroom.net; >>>> george@omniti.com >>>> Subject: pdo design >>>> >>>> Guys, >>>> >>>> I saw the alpha of PDO on pecl. I was wondering if we could discuss >>>> the design of it a bit more before putting too much work into it. >>>> >>>> In particular, the way transactions are represented is not very >>>> practical. Generally, databases that support transactions will >>>> support multiple concurrent transactions against the same database. >>>> Firebird also supports transactions against multiple databases. >>>> [which does *not* mean you can join tables from different DBs, it >>>> just means ACID over multiple DBs] >>>> >>>> This means that in some cases you must specify both the connection >>>> and the transaction when generating a statement. [I'm not sure if >>>> others RDBMSs support this, but it might be something we will see >>>> more in the future.] >>>> >>>> In short: >>>> >>>> In addition to $db->startTransaction(), maybe we could add >>>> PDO::startTransaction($db1 [,$db2 [,$db3]]). >>>> >>>> In addition to $db->prepare(), maybe we could add a Transaction >>>> interface whose instances would allow being called as >>>> >>>> $trans->prepare($query) >>>> >>>> and maybe even >>>> >>>> $trans->prepare($db, $query) >>>> >>>> >>>> Your thoughts please ? >>>> >>>> -- Ard >>>> >>>> >>>> >>>> >>> >>> >> >> -- >> PHP Internals - PHP Runtime Development Mailing List >> To unsubscribe, visit: http://www.php.net/unsub.php > >
-- Can you help out? Need Consulting Services or Know of a Job? http://www.akbkhome.com

Wez Furlong

22 years ago
But you're not getting any data out there ;) It has to be an API due to the requirements of database API's such as Oracle and ODBC. --Wez.