Re: [Orclib-users] Crash when using BindArray* and Bind* functions together
Open source C and C++ library for accessing Oracle Databases
Brought to you by:
vince_del_paris
|
From: Ivan C. <ich...@qu...> - 2015-02-12 07:51:43
|
Thanks, Vincent! So it is obvious I have to invent my own simple escaping
function :)
Ivan.
On Wed, Feb 11, 2015 at 11:42 PM, vincent rogier <vin...@gm...>
wrote:
> Hi,
>
> Oracle OCI requires all binds to be arrays when performing bulk inserts
> but does not prevent mixing single and array binds. It only complains when
> statement is executed by crashing.
> OCILIB is built on top of OCI.
> I could check any violation of that OCI paradigm. But then I should check
> plenty of other ones that may also change depending on oracle version. Thus
> it could lead to an important library code complexification. It may also
> require runtime overhead checks that require logic dependent on the sql
> statement type...
> I will check this at least for that paradigm and see if i can prevent any
> mix for bulks without performance or code complexification issues.
> But keep in mind that what you want to do (mix single bind and array bind
> for bulk inserts) is not possible with oracle !
>
> Regards,
>
> Vincent
> Le 12 févr. 2015 00:53, "Ivan Chernetsky" <ich...@qu...> a
> écrit :
>
> Hi Vincent,
>>
>> The fact that library allows to use BindInt and BindArrayOfInts with the
>> same prepared statement implies that
>>
>> 1. Either it will use the same int that was bind to :single for each
>> element that comes from :array.
>> 2. Or it uses only the first element of :array along with :single value.
>>
>> To crash is not among good options. So one of those functions either
>> should return an error code, or the library should do either 1 or 2.
>>
>> For instance, it's quite common when where is a need for an insert like
>> "INSERT INTO t (n1, n2) VALUES (:single, :array)", and data for :single and
>> :array comes from a user, and by using both BindInt and BindArrayOfInts I
>> can be sure that SQL injection cannot happen. Theoretically it can be done
>> with OCI_PrepareFmt and the like, and it works fine, if it's me who
>> provides a value for single variable. If single[] = "va'lue", PrepareFmt
>> cannot escape it and just return an error code.
>>
>> In C, int array[N] = {0} means array is to be initialized with N zeros.
>> int array[N] = {1} means array is to be initialized with 1 and (N-1) zeros.
>>
>> #include <ocilib.h>
>>
>> #define N 100000
>>
>> int main(void)
>> {
>> OCI_Connection *cn = NULL; OCI_Statement *st = NULL;
>> int i = 0, array[N] = {0}; char single[] = "value";
>> const char *host = "db", *user = "user", *password = "pass";
>>
>> for (i = 0; i < N; i++) array[i] = i;
>>
>> if (!OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT)) return EXIT_FAILURE;
>> cn = OCI_ConnectionCreate(host, user, password, OCI_SESSION_DEFAULT);
>> if (cn == NULL) return EXIT_FAILURE;
>>
>> st = OCI_StatementCreate(cn);
>> if (st == NULL) return EXIT_FAILURE;
>>
>> OCI_Prepare(st, "DROP TABLE ns");
>> OCI_Execute(st);
>> OCI_Commit(cn);
>>
>> OCI_Prepare(st, "CREATE TABLE ns (s VARCHAR2(64), n NUMBER)");
>> OCI_Execute(st);
>> OCI_Commit(cn);
>>
>> if (!OCI_PrepareFmt(st, "INSERT INTO ns (s, n) VALUES (%s, :array)",
>> single))
>> return EXIT_FAILURE;
>>
>> if (!OCI_BindArraySetSize(st, N)) return EXIT_FAILURE;
>> if (!OCI_BindArrayOfInts(st, ":array", array, N)) return EXIT_FAILURE;
>>
>> if (!OCI_Execute(st)) return EXIT_FAILURE;
>> printf("rows affected: %u\n", OCI_GetAffectedRows(st));
>>
>> if (!OCI_Commit(cn)) return EXIT_FAILURE;
>>
>> OCI_Cleanup();
>> return EXIT_SUCCESS;
>> }
>>
>> On Wed, Feb 11, 2015 at 2:06 PM, vincent rogier <vin...@gm...>
>> wrote:
>>
>>> Hi,
>>>
>>> When performing bulk insert, all binding host variables must be arrays.
>>> you cannot bind an int to n1 and an array to n2.
>>> You need to bind array for both n1 and n2.
>>> That's the concept of bulk inserts !
>>>
>>> Look at your array variable "array". you declare it of size N but you
>>> only initialize the first element !!! Other elements have non defined
>>> values !
>>>
>>> Regards,
>>>
>>> Vincent
>>>
>>> On Wed, Feb 11, 2015 at 9:26 PM, Ivan Chernetsky <ich...@qu...
>>> > wrote:
>>>
>>>> Hello everybody,
>>>>
>>>> With N sufficiently large (on my machine 100000 is large enough), the
>>>> below program segfaults. With N sufficiently small (on my machine 1000 is
>>>> small enough) trash values get written into ns table. It is not allowed to
>>>> use BindInt with BindArrayOfInts with the same statement, by design, is it?
>>>> If so, BindInt should return some error code, instead of silently allowing
>>>> it. Again, if so, what is the proper way of binding single variables and
>>>> arrays?
>>>>
>>>> #include <ocilib.h>
>>>>
>>>> #define N 100000
>>>>
>>>> int main(void)
>>>> {
>>>> OCI_Connection *cn = NULL; OCI_Statement *st = NULL;
>>>> int single = 0, array[N] = {1};
>>>> const char *host = "db", *user = "user", *password = "pass";
>>>>
>>>> if (!OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT))
>>>> return EXIT_FAILURE;
>>>> cn = OCI_ConnectionCreate(host, user, password,
>>>> OCI_SESSION_DEFAULT);
>>>> if (cn == NULL)
>>>> return EXIT_FAILURE;
>>>>
>>>> st = OCI_StatementCreate(cn);
>>>> if (st == NULL)
>>>> return EXIT_FAILURE;
>>>>
>>>> OCI_Prepare(st, "DROP TABLE ns");
>>>> OCI_Execute(st);
>>>> OCI_Commit(cn);
>>>>
>>>> if (!OCI_Prepare(st, "CREATE TABLE ns (n1 NUMBER, n2 NUMBER)"))
>>>> return EXIT_FAILURE;
>>>> if (!OCI_Execute(st))
>>>> return EXIT_FAILURE;
>>>> if (!OCI_Commit(cn))
>>>> return EXIT_FAILURE;
>>>>
>>>> if (!OCI_Prepare(st, "INSERT INTO ns (n1, n2) VALUES (:single,
>>>> :array)"))
>>>> return EXIT_FAILURE;
>>>>
>>>> if (!OCI_BindArraySetSize(st, N))
>>>> return EXIT_FAILURE;
>>>> if (!OCI_BindInt(st, ":single", &single))
>>>> return EXIT_FAILURE;
>>>> if (!OCI_BindArrayOfInts(st, ":array", array, N))
>>>> return EXIT_FAILURE;
>>>>
>>>> if (!OCI_Execute(st))
>>>> return EXIT_FAILURE;
>>>> printf("rows affected: %u\n", OCI_GetAffectedRows(st));
>>>>
>>>> if (!OCI_Commit(cn))
>>>> return EXIT_FAILURE;
>>>>
>>>> OCI_Cleanup();
>>>> return EXIT_SUCCESS;
>>>> }
>>>>
>>>
|