Re: [Orclib-users] [Bulk] newbie question on implementing bulk operations
Open source C and C++ library for accessing Oracle Databases
Brought to you by:
vince_del_paris
From: vincent r. <vin...@ya...> - 2012-04-06 17:43:55
|
hi, all string and characters columns are mapped to C null terminated strings. That's why your variable 'a' must be 2 bytes long. On Fri, Apr 6, 2012 at 5:36 PM, <mod...@co...> wrote: > Hi Vincent, > > I think my code below is correct but for reasons not shown, there were > some memory problems. I've got it working now, but in the process I made an > interesting observation that I wanted to share with you. I've got both > methods A and B below working. > > Method A (sql statement) > > OCI_SetFetchSize(st, pts); > OCI_Prepare(st, "select dd1, ii1 from my_table where report_name = > :in_report_name and edge = 'a'"); > OCI_BindString(st, ":in_report_name", report_name, 60); > OCI_Execute(st); > rs = OCI_GetResultset(st); > while (OCI_FetchNext(rs)) { > d1[i] = OCI_GetDouble(rs, 1); > i1[i] = OCI_GetInt(rs, 2); > i++; } > > Method B (stored procedure) > > OCI_SetFetchSize(st, pts); > OCI_Prepare(st, "begin get_data( :in_report_name, 'a', :out_resultset ); > end;"); > OCI_BindString(st, ":in_report_name", report_name, 60); > OCI_BindStatement(st, ":out_resultset", st2); > OCI_Execute(st); > rs = OCI_GetResultset(st2); > while (OCI_FetchNext(rs)) { > d1[i] = OCI_GetDouble(rs, 1); > i1[i] = OCI_GetInt(rs, 2); > i++; } > > One of the ways I got my code working, by trial and error, was to use 'a' > inside the OCI_Prepare statement. Originally, I attempted to bind an edge > parameter ":in_edge" using (Method A): > > char edge[1] = ""; > ... > strncpy(edge, "a", 1); > OCI_Prepare(st, "select dd1, ii1 from my_table where report_name = > :in_report_name and edge = :in_edge"); > OCI_BindString(st, ":in_edge", edge, 1); > > or (Method B): > > char edge[1] = ""; > ... > strncpy(edge, "a", 1); > OCI_Prepare(st, "begin get_data( :in_report_name, :in_edge, :out_resultset > ); end;"); > OCI_BindString(st, ":in_edge", edge, 1); > > However, this did not work, even though I could use a debugger and verify > edge="a" at the OCI_Prepare statement. Interestingly, if I declared the > edge variable as having a length of 2, the code worked. That is, declaring > edge as: > > char edge[2] = ""; > > made the code work. Note the database table's column for "edge" is > declared as char (1 byte). I also tried varchar2 (1 byte) with the same > result. > > Anyway I thought that was interesting, but maybe you already knew. Looking > at this link: > > > http://orclib.sourceforge.net/doc/html/group__g__bind.html#ga49988b9ca8f00b55cc3e62ca2385778b > > in the "OCI_BindString" section, it suggests this string has a "zero null > terminal character" that this second byte may be needed for. If true, > perhaps this means when I declare the string length in C, I should add one > additional byte (e.g. character) for OCILIB to use for this zero null > terminal character. > > Thanks again for all of your wonderful support. OCILIB has really been a > joy to use! > > > > ----- Original Message ----- > From: mod...@co... > To: "vincent rogier" <vin...@ya...> > Cc: orc...@li... > Sent: Thursday, April 5, 2012 2:52:37 PM > Subject: Re: [Orclib-users] [Bulk] newbie question on implementing bulk > operations > > Hi Vincent, I tried to follow this link: > > http://sourceforge.net/projects/orclib/forums/forum/470801/topic/3406215 > > to answer my question below. I *think* the required code (see previous > question below) should look like: > > > OCI_Connection *cn; > OCI_Statement *st; > OCI_Statement *st2; > OCI_Resultset *rs; > > ... > > if (!OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT)) > return EXIT_FAILURE; > > ... > > cn = OCI_ConnectionCreate("<db>", "myschema", "myuser", > OCI_SESSION_DEFAULT); > st = OCI_StatementCreate(cn); > st2 = OCI_StatementCreate(cn); > > OCI_SetFetchSize(st, pts); > OCI_Prepare(st, "begin get_data( :in_report_name, :in_edge, :out_resultset > ); end;"); > OCI_BindString(st, ":in_report_name", report_name, 60); > OCI_BindString(st, ":in_edge", edge, 1); > OCI_BindStatement(st, ":out_resultset", st2); > OCI_Execute(st); > > rs = OCI_GetResultset(st2); > > while (OCI_FetchNext(rs)) > { > d1[i] = OCI_GetDouble(rs, 1); > d2[i] = OCI_GetDouble(rs, 2); > d3[i] = OCI_GetDouble(rs, 3); > i1[i] = OCI_GetInt(rs, 4); > i++; > } > > However, I run into some funny issues when I place it inside my main > program to run it. Here's the structure of OCILIB calls in my program (I > execute 3 processes below): > > > OCI_Connection *cn; > OCI_Statement *st; > OCI_Statement *st2; > OCI_Resultset *rs; > > ... > > if (!OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT)) > return EXIT_FAILURE; > > cn = OCI_ConnectionCreate("<db>", "myschema", "myuser", > OCI_SESSION_DEFAULT); > st = OCI_StatementCreate(cn); > st2 = OCI_StatementCreate(cn); > > OCI_Prepare(st, ...); > OCI_Bindxxxx(st, ...); > OCI_Execute(st, ...); > > ... > > OCI_Prepare(st, ...); /* this is a different prepare stmt than used above > */ > OCI_Bindxxxx(st, ...); > OCI_Execute(st, ...); > > ... > > OCI_SetFetchSize(st, pts); > OCI_Prepare(st, "begin get_data( :in_report_name, :in_edge, :out_resultset > ); end;"); > OCI_BindString(st, ":in_report_name", report_name, 60); > OCI_BindString(st, ":in_edge", edge, 1); > OCI_BindStatement(st, ":out_resultset", st2); > OCI_Execute(st); > > rs = OCI_GetResultset(st2); > > while (OCI_FetchNext(rs)) > { > d1[i] = OCI_GetDouble(rs, 1); > d2[i] = OCI_GetDouble(rs, 2); > d3[i] = OCI_GetDouble(rs, 3); > i1[i] = OCI_GetInt(rs, 4); > i++; > } > > printf("\nd1[0] = %g ", d1[0]); > > ... > > OCI_Cleanup(); > return EXIT_SUCCESS; > > > OCI_Execute executes three times here. > > If I run the program, the first OCI_Execute process runs fine, but the > SECOND OCI_Execute process does not return the correct values from the > database, and the THIRD OCI_Execute process appears not to run (the printf > statement doesn't print anything). > > However, if I comment out the line of code for "OCI_Statement *st2;", and > also comment out the lines of code from "OCI_SetFetchSize(st, pts);" to > "printf(...);" and then run the program, the first two OCI_Execute > processes run fine. > > At this point, if I simply remove the comment from the line "OCI_Statement > *st2;" (keeping the THIRD OCI_Execute related lines of code commented out), > and run the program, the FIRST OCI_Execute process runs fine but the SECOND > OCI_Execute process does not return the correct values from the database. > So, simply having that "OCI_Statement *st2;" line of code there seems to > cause programs, even without any code related to the THIRD OCI_Execute > process. > > Am I arranging the calls correctly here? Do you see anything that looks > wrong? Thanks for any comments. > > > ----- Original Message ----- > From: mod...@co... > To: "vincent rogier" <vin...@ya...> > Cc: orc...@li... > Sent: Thursday, April 5, 2012 10:53:16 AM > Subject: Re: [Orclib-users] [Bulk] newbie question on implementing bulk > operations > > Hi Vincent, if I want to re-write this successful code > > > OCI_SetFetchSize(st, pts); > OCI_Prepare(st, "select dd1, dd2, dd3, ii1 from my_table where report_name > = :in_report_name and edge = 'a'"); > OCI_BindString(st, ":in_report_name", report_name, 60); > OCI_Execute(st); > > rs = OCI_GetResultset(st); > > while (OCI_FetchNext(rs)) > { > d1[i] = OCI_GetDouble(rs, 1); > d2[i] = OCI_GetDouble(rs, 2); > d3[i] = OCI_GetDouble(rs, 3); > i1[i] = OCI_GetInt(rs, 4); > i++; > } > > > to call a stored procedure in the OCI_Prepare statement, how would I do > it? I've tried the following without success: > > > OCI_SetFetchSize(st, pts); > OCI_Prepare(st, "begin get_data( :in_report_name, :in_edge, :out_resultset > ); end;"); > OCI_BindString(st, ":in_report_name", report_name, 60); > OCI_BindString(st, ":in_edge", edge, 1); > OCI_BindRef(st, ":out_resultset", rs); /* I also tried OCI_BindObject, > OCI_BindStatement, etc. */ > OCI_Execute(st); > > rs = OCI_GetResultset(st); > > while (OCI_FetchNext(rs)) > { > d1[i] = OCI_GetDouble(rs, 1); > d2[i] = OCI_GetDouble(rs, 2); > d3[i] = OCI_GetDouble(rs, 3); > i1[i] = OCI_GetInt(rs, 4); > i++; > } > > Thanks for any comments! > > > ----- Original Message ----- > From: "vincent rogier" <vin...@ya...> > To: mod...@co... > Cc: orc...@li... > Sent: Wednesday, April 4, 2012 11:41:54 PM > Subject: Re: [Bulk] [Orclib-users] newbie question on implementing bulk > operations > > Hi, > > the syntax "select .... into ....." is wrong. > > Bulk operation are related to binds variables, not selected columns. > > What you want to do, is "array fetching". You want to fetch more than one > row at a time. No problem.... That what OCILIB already does internally. > When OCILIB fetches data (using OCI_FetchNext() for example), it fetches > by default 20 rows from the DB. Next calls to COI_FetchNext() does not do > servers round trips and local data is returned. Once you've called > OCI_FechNext 20 times, then the next call fetches another 20 rows from DB > and so on. > > The number of rows that OCILIB internally fetches is by default 20 but can > be modified using OCI_SetFetchSize(). > > So, you don't have to provide arrays to OCILIB. You just call > OCI_FetchNext() and OCI_Getxxxxx() to retrieve values. > > If you really want to copy values from OCILIB resultset to your own arrays > you could do like this : > > > OCI_SetFecthSize(pts); > OCI_Prepare(st, "select dd1, dd2, dd3, ii1 into :out_d1, :out_d2, :out_d3, > :out_i1 from my_table where report_name = :in_report_name and edge = 'a'"); > OCI_BindString(st, ":in_report_name", report_name, 60); > OCI_Execute(st); > > rs = OCI_GetResultset(st); > > while (OCI_FetchNext(rs)) > { > out_d1[i] = OCI_GetDouble(rs, 1); > out_d2[i] = OCI_GetDouble(rs, 2); > out_d3[i] = OCI_GetDouble(rs, 3; > out_i1[i] = OCI_GetInt(rs, 4); > > i++; > } > > > > > On Thu, Apr 5, 2012 at 6:48 AM, < mod...@co... > wrote: > > > Hi, I've been successfully using OCILIB to access PL/SQL stored procedures > in Oracle 11.2 database from C program. Now I need to download a lot of > array data, so I thought I'd use bulk operations for a fast download. Since > I know my connection is good, I must be getting the syntax wrong in my > code. I've tried to follow the user manual and any examples I can find > online, but there's nothing covering my exact situation. I was hoping > someone could give me a sanity check on my OCILIB implementation below. I'm > using gcc compiler with -std=C89 switch. > > > OCI_Connection *cn; > OCI_Statement *st; > > char report_name[60]; > double * d1, * d2, * d3; > int * i1; > unsigned int pts = 9; /* pts is actually determined when the program > executes, but I set it to 9 here to simply the code for example only */ > > ... > > /* allocate array memory */ > d1 = malloc( sizeof(double) * pts ); > d2 = malloc( sizeof(double) * pts ); > d3 = malloc( sizeof(double) * pts ); > i1 = malloc( sizeof(int) * pts ); > > ... > > if (!OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT)) > return EXIT_FAILURE; > cn = OCI_ConnectionCreate("<removed>", "myschema", "myuser", > OCI_SESSION_DEFAULT); > st = OCI_StatementCreate(cn); > > ... > > /* database table 'my_table' has columns: > dd1 is binary_double > dd2 is binary_double > dd3 is binary_double > ii1 is number(8,0) > report_name is varchar2(60 bytes) > edge is char(1 byte) */ > OCI_Prepare(st, "select dd1, dd2, dd3, ii1 into :out_d1, :out_d2, :out_d3, > :out_i1 from my_table where report_name = :in_report_name and edge = 'a'"); > OCI_BindString(st, ":in_report_name", report_name, 60); > OCI_BindArraySetSize(st, pts); > OCI_BindArrayOfDoubles(st, ":out_d1", (double*) d1, 0); > OCI_BindArrayOfDoubles(st, ":out_d2", (double*) d2, 0); > OCI_BindArrayOfDoubles(st, ":out_d3", (double*) d3, 0); > OCI_BindArrayOfInts(st, ":out_apron_tie", (int*) i1, 0); > OCI_Execute(st); > > > /* TEST */ > printf("\nd1[0] = %g ", d1[0]); > printf("\nd1[1] = %g ", d1[1]); > > > OCI_Cleanup(); > return EXIT_SUCCESS; > > Thanks for any comments > > > ------------------------------------------------------------------------------ > Better than sec? Nothing is better than sec when it comes to > monitoring Big Data applications. Try Boundary one-second > resolution app monitoring today. Free. > http://p.sf.net/sfu/Boundary-dev2dev > _______________________________________________ > Orclib-users mailing list > Orc...@li... > https://lists.sourceforge.net/lists/listinfo/orclib-users > > > > -- > Vincent Rogier > > > ------------------------------------------------------------------------------ > Better than sec? Nothing is better than sec when it comes to > monitoring Big Data applications. Try Boundary one-second > resolution app monitoring today. Free. > http://p.sf.net/sfu/Boundary-dev2dev > _______________________________________________ > Orclib-users mailing list > Orc...@li... > https://lists.sourceforge.net/lists/listinfo/orclib-users > > > ------------------------------------------------------------------------------ > Better than sec? Nothing is better than sec when it comes to > monitoring Big Data applications. Try Boundary one-second > resolution app monitoring today. Free. > http://p.sf.net/sfu/Boundary-dev2dev > _______________________________________________ > Orclib-users mailing list > Orc...@li... > https://lists.sourceforge.net/lists/listinfo/orclib-users > -- Vincent Rogier |