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-11 23:54:08
|
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;
>> }
>>
>
|