Re: Clarification sought on execute_array
John Scoles <[email protected]>
| Newsgroups | gmane.comp.lang.perl.modules.dbi.sybase.devel |
|---|---|
| Message-ID | <[email protected]> |
On 19/01/2011 5:25 PM, Martin J. Evans wrote: Great stuff Martin I am sure we can clear this up. One thing I did notice was you never set the 'ora_array_chunk_size' which is defaulting to 1000 I wonder if running this with this value set to say 2 or 3 will see a change in behaviour. No doubt there is some thing awry in there That we will have to get to the bottom of. Cheers John > On 19/01/2011 11:47, Tim Bunce wrote: >> I'm sorry to arrive here late. Seems I've been missing all the fun! >> > Thought you were in Paris so I didn't expect you to see this. It has > not been a lot of fun ;-) > >> Some observations: >> >> - I think it's reasonable for execute_array() and execute_for_fetch() >> to return an error (ie err() true and so trigger RaiseError etc.) >> if execution of any of the tuples encountered an error. >> > The way I see it is that if some of the rows in the batch end up in > the table and some don't I'd expect a warning. However, if 1 or more > rows fail and no rows end up in the table I don't see any success so > I'd expect an error. In my example code, 1 row fails but no rows end > up successful so it is the latter. > > It may be a change/bug in the Oracle libraries as I thought when you > execute a batch all the rows are executed no matter if some fail and > all successful ones end up in the table. > >> - That'll need a spec clarification and a clear warning in Changes. >> >> - We should avoid breaking existing batch apps that use DBD::Oracle. >> > Agreed, but we've not got any working examples of anyone using > execute_array other than the one which started this off (and mine) > which was DBIx::Class and that was broken by DBD::Oracle/Oracle's > current behaviour. The big problem with the DBIx::Class code was > checking $sth->err which was 0 in this case because 0 indicates a > warning. However, DBD::Oracle contradicted itself since execute_array > returned undef (error) but then set a warning on err. > >> - I'm hopeful that the above change wouldn't. (John?) >> > So am I. If we could get the examples John has seen via his > DBD::Oracle maintainership or via Pythian customers I would be happy > to create test cases. We are running blind at the moment as we've not > got those solid examples of supposedly working code. > >> - We should review other database APIs that provide batch execution >> in order to spec a reasonable common subset behaviour for the DBI. >> > Obviously ODBC and JDBC do batched statements. I might provide a JDBC > example but for now I've done an ODBC example (slightly more familiar > to me) - see below. > >> - Clearly we should bring DBD::Oracle, the DBI default behaviour, and >> the >> DBI spec into agreement with each other. >> > Exactly. This is really my main point. As it stands (and given it is > not a bug in Oracle) I see a nightmare for anyone trying to use > execute_array in a database neutral way as with DBI, all successful > rows are inserted and we know which ones failed and with DBD::Oracle > no rows are inserted (including the ones where there is no error) and > you cannot commit the good ones and it is difficult to know (if not > impossible) what really happened. This is not a dig at anyone in > particular as I added the array context execute_array to > DBI/DBD::Oracle but this is also why I suspect something has changed > in DBD::Oracle/Oracle. > >> - We *really* need a way to share tests across drivers. >> Perhaps something like a separate DBI::TestSuite distro that the >> DBI >> and drivers could have as a prerequisite. That would contain >> tests in >> modules. The DBI and DBDs would have a test file that uses the >> DBI::TestSuite module and calls a function that runs the tests. >> This issue could provide the first test. >> >> Tim. > > I agree and I seem to remember a project to do something like this - > was it perhaps a google summer of code suggestion? But it is pretty > difficult and I think that puts a lot of people off. I briefly looked > at Test::Database so I could get more realistic test results for > DBD::ODBC but I ran in to a load of problems as Test::Database needs > some DBD methods writing and expects to be able to create a database > and in ODBC (via dozens of ODBC drivers) there is not single way to do > this. The gain was just not worth the pain for me. I'd be happy to > help someone do this but only in a minor way as right now I cannot > find the time to satisfy even half of my OS commitments (as an > example, I REALLY want to be able to set handle attributes on methods > in DBI [post from a week back] but I just cannot find time to do it - > something else is always cropping up). > > Attached is a very rough and ready bit of C code (with little error > checking) that does batch inserts. You run it with something like: > > ./a.out 'DSN=mydsn;UID=username;PWD=password' > > and it does batch inserts into a table called xtest that is defined as: > > create table xtest(ky integer primary key, txt varchar(20)) > > It has 4 tests: > > 1. insert a batch successfully reading the parameter status array > (ArrayTupleStatus) to see what worked (autocommit) > 2. insert a batch where 2 rows cannot be inserted because of a > duplicate key and with a parameter status array (autocommit) > 3. insert a batch where 2 rows cannot be inserted because of a > duplicate key and without a parameter status array (autocommit) > 4. insert a batch where 2 rows cannot be inserted with an explicit txn > and with a parameter status array > > The code was run against the Easysoft ODBC Driver for MS SQL Server > but the same results are obtained when using the MS SQL Server driver > on Windows. No other ODBC attributes were changed (other than the ones > mentioned). > > What it tells us is: > > o SQL_SUCCESS is always returned when all the rows are inserted > o SQL_SUCCESS_WITH_INFO is returned if some of the rows were > successful but some were not > o it does not matter whether we provide a parameter status array > (ArrayTupleStatus) or not - the 2 results above stand i.e. even if the > ODBC driver cannot tell you which ones failed (because you did not > give a parameter status array) it still does the successful rows and > only returns SQL_SUCCESS_WITH_INFO if some failed. > o AutoCommit makes no difference - i.e., if auto commit is on or off > the end result is the same IF we commit afterwards. > > The output from running the code is below. I apologise for the length > of the C code but this is just another example of what you can do in C > code you can do in 1/10 (or thereabouts) of the code in Perl. > > $ ./a.out 'DSN=baugi;UID=sa;PWD=easysoft' > Successful batch AUTOCOMMIT with PARAMSTATUSARRAY > "delete from "xtest"" > Setting bind by column > Setting Parameter Status Array Ptr > Setting Parameters Processed Ptr > Setting PARAMSETSIZE to 10 > "insert into "xtest" (ky,txt) values(?,?)" > Inserting rows into table > SQLExecute=SUCCESS > RowCount=10 > Param Status Array 0 = 0 > Param Status Array 1 = 0 > Param Status Array 2 = 0 > Param Status Array 3 = 0 > Param Status Array 4 = 0 > Param Status Array 5 = 0 > Param Status Array 6 = 0 > Param Status Array 7 = 0 > Param Status Array 8 = 0 > Param Status Array 9 = 0 > Params processed = 10 > . > Resetting parameters > Closing statement > Clearing Parameter Status Array Ptr > Clearing Parameters Processed Ptr > 0, this is row 0 > 1, this is row 1 > 2, this is row 2 > 3, this is row 3 > 4, this is row 4 > 5, this is row 5 > 6, this is row 6 > 7, this is row 7 > 8, this is row 8 > 9, this is row 9 > Dropping Statement > Partially successful batch AUTOCOMMIT with PARAMSTATUSARRAY > "delete from "xtest"" > Setting bind by column > Setting Parameter Status Array Ptr > Setting Parameters Processed Ptr > Setting PARAMSETSIZE to 10 > "insert into "xtest" (ky,txt) values(?,?)" > Inserting rows into table > SQLExecute=SQL_SUCCESS_WITH_INFO > ** Error from SQLExecute ** > 4 diagnostics found > ** error: 23000:1:2627:[Easysoft][SQL Server Driver 10.0][SQL > Server]Violation o > f PRIMARY KEY constraint 'PK__xtest__3213D96D46D27B73'. Cannot insert > duplicate > key in object 'dbo.xtest'. ** > > ** error: 23000:2:2627:[Easysoft][SQL Server Driver 10.0][SQL > Server]Violation o > f PRIMARY KEY constraint 'PK__xtest__3213D96D46D27B73'. Cannot insert > duplicate > key in object 'dbo.xtest'. ** > > ** error: 01000:3:3621:[Easysoft][SQL Server Driver 10.0][SQL > Server]The stateme > nt has been terminated. ** > > ** error: 01000:4:3621:[Easysoft][SQL Server Driver 10.0][SQL > Server]The stateme > nt has been terminated. ** > > ** RowCount=8, expected 10 ** > Param Status Array 0 = 0 > Param Status Array 1 = 0 > ** Row 3 not executed, status=5** > ** Row 4 not executed, status=5** > Param Status Array 4 = 0 > Param Status Array 5 = 0 > Param Status Array 6 = 0 > Param Status Array 7 = 0 > Param Status Array 8 = 0 > Param Status Array 9 = 0 > Params processed = 10 > . > Resetting parameters > Closing statement > Clearing Parameter Status Array Ptr > Clearing Parameters Processed Ptr > 0, this is row 0 > 1, this is row 1 > 4, this is row 4 > 5, this is row 5 > 6, this is row 6 > 7, this is row 7 > 8, this is row 8 > 9, this is row 9 > Dropping Statement > Partially successful batch AUTOCOMMIT without PARAMSTATUSARRAY > "delete from "xtest"" > Setting bind by column > Setting Parameters Processed Ptr > Setting PARAMSETSIZE to 10 > "insert into "xtest" (ky,txt) values(?,?)" > Inserting rows into table > SQLExecute=SQL_SUCCESS_WITH_INFO > ** Error from SQLExecute ** > 4 diagnostics found > ** error: 23000:1:2627:[Easysoft][SQL Server Driver 10.0][SQL > Server]Violation o > f PRIMARY KEY constraint 'PK__xtest__3213D96D46D27B73'. Cannot insert > duplicate > key in object 'dbo.xtest'. ** > > ** error: 23000:2:2627:[Easysoft][SQL Server Driver 10.0][SQL > Server]Violation o > f PRIMARY KEY constraint 'PK__xtest__3213D96D46D27B73'. Cannot insert > duplicate > key in object 'dbo.xtest'. ** > > ** error: 01000:3:3621:[Easysoft][SQL Server Driver 10.0][SQL > Server]The stateme > nt has been terminated. ** > > ** error: 01000:4:3621:[Easysoft][SQL Server Driver 10.0][SQL > Server]The stateme > nt has been terminated. ** > > ** RowCount=8, expected 10 ** > Params processed = 10 > . > Resetting parameters > Closing statement > Clearing Parameter Status Array Ptr > Clearing Parameters Processed Ptr > 0, this is row 0 > 1, this is row 1 > 4, this is row 4 > 5, this is row 5 > 6, this is row 6 > 7, this is row 7 > 8, this is row 8 > 9, this is row 9 > Dropping Statement > Partially successful batch no AUTOCOMMIT WITH PARAMSTATUSARRAY > "delete from "xtest"" > Setting bind by column > Setting Parameter Status Array Ptr > Setting Parameters Processed Ptr > Setting PARAMSETSIZE to 10 > "insert into "xtest" (ky,txt) values(?,?)" > Inserting rows into table > SQLExecute=SQL_SUCCESS_WITH_INFO > ** Error from SQLExecute ** > 4 diagnostics found > ** error: 23000:1:2627:[Easysoft][SQL Server Driver 10.0][SQL > Server]Violation o > f PRIMARY KEY constraint 'PK__xtest__3213D96D46D27B73'. Cannot insert > duplicate > key in object 'dbo.xtest'. ** > > ** error: 23000:2:2627:[Easysoft][SQL Server Driver 10.0][SQL > Server]Violation o > f PRIMARY KEY constraint 'PK__xtest__3213D96D46D27B73'. Cannot insert > duplicate > key in object 'dbo.xtest'. ** > > ** error: 01000:3:3621:[Easysoft][SQL Server Driver 10.0][SQL > Server]The stateme > nt has been terminated. ** > > ** error: 01000:4:3621:[Easysoft][SQL Server Driver 10.0][SQL > Server]The stateme > nt has been terminated. ** > > ** RowCount=8, expected 10 ** > Param Status Array 0 = 0 > Param Status Array 1 = 0 > ** Row 3 not executed, status=5** > ** Row 4 not executed, status=5** > Param Status Array 4 = 0 > Param Status Array 5 = 0 > Param Status Array 6 = 0 > Param Status Array 7 = 0 > Param Status Array 8 = 0 > Param Status Array 9 = 0 > Params processed = 10 > . > Resetting parameters > Closing statement > Clearing Parameter Status Array Ptr > Clearing Parameters Processed Ptr > 0, this is row 0 > 1, this is row 1 > 4, this is row 4 > 5, this is row 5 > 6, this is row 6 > 7, this is row 7 > 8, this is row 8 > 9, this is row 9 > Dropping Statement > > I'm not in a rush to provide a JDBC example as my experience is that > it will be pretty similar - I might if pushed hard. > > What is not clear to me is what effect oci_mode = OCI_BATCH_ERRORS is > supposed to have. Also my current Oracle example seems to return a > SUCCESS_WITH_INFO when OCIExecute is called even though no rows are > committed. John seems to have a recollection that you can commit the > successful rows but I cannot duplicate it. We need real example usage > of execute_array for DBD::Oracle which worked as a test case. > > Martin