1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "apu.h"
18#include "apr_pools.h"
19#include "apr_dbd.h"
20
21#include <stdio.h>
22
23#define TEST(msg,func)					\
24    printf("======== %s ========\n", msg);		\
25    rv = func(pool, sql, driver);			\
26    if (rv != 0) {					\
27        printf("Error in %s: rc=%d\n\n", msg, rv);	\
28    }							\
29    else {						\
30        printf("%s test successful\n\n", msg);		\
31    }							\
32    fflush(stdout);
33
34static int create_table(apr_pool_t* pool, apr_dbd_t* handle,
35                        const apr_dbd_driver_t* driver)
36{
37    int rv = 0;
38    int nrows;
39    const char *statement = "CREATE TABLE apr_dbd_test ("
40        "col1 varchar(40) not null,"
41        "col2 varchar(40),"
42        "col3 integer)" ;
43    rv = apr_dbd_query(driver, handle, &nrows, statement);
44    return rv;
45}
46static int drop_table(apr_pool_t* pool, apr_dbd_t* handle,
47                      const apr_dbd_driver_t* driver)
48{
49    int rv = 0;
50    int nrows;
51    const char *statement = "DROP TABLE apr_dbd_test" ;
52    rv = apr_dbd_query(driver, handle, &nrows, statement);
53    return rv;
54}
55static int insert_rows(apr_pool_t* pool, apr_dbd_t* handle,
56                       const apr_dbd_driver_t* driver)
57{
58    int i;
59    int rv = 0;
60    int nrows;
61    int nerrors = 0;
62    const char *statement =
63        "INSERT into apr_dbd_test (col1) values ('foo');"
64        "INSERT into apr_dbd_test values ('wibble', 'other', 5);"
65        "INSERT into apr_dbd_test values ('wibble', 'nothing', 5);"
66        "INSERT into apr_dbd_test values ('qwerty', 'foo', 0);"
67        "INSERT into apr_dbd_test values ('asdfgh', 'bar', 1);"
68    ;
69    rv = apr_dbd_query(driver, handle, &nrows, statement);
70    if (rv) {
71        const char* stmt[] = {
72            "INSERT into apr_dbd_test (col1) values ('foo');",
73            "INSERT into apr_dbd_test values ('wibble', 'other', 5);",
74            "INSERT into apr_dbd_test values ('wibble', 'nothing', 5);",
75            "INSERT into apr_dbd_test values ('qwerty', 'foo', 0);",
76            "INSERT into apr_dbd_test values ('asdfgh', 'bar', 1);",
77            NULL
78        };
79        printf("Compound insert failed; trying statements one-by-one\n") ;
80        for (i=0; stmt[i] != NULL; ++i) {
81            statement = stmt[i];
82            rv = apr_dbd_query(driver, handle, &nrows, statement);
83            if (rv) {
84                nerrors++;
85            }
86        }
87        if (nerrors) {
88            printf("%d single inserts failed too.\n", nerrors) ;
89        }
90    }
91    return rv;
92}
93static int invalid_op(apr_pool_t* pool, apr_dbd_t* handle,
94                      const apr_dbd_driver_t* driver)
95{
96    int rv = 0;
97    int nrows;
98    const char *statement = "INSERT into apr_dbd_test1 (col2) values ('foo')" ;
99    rv = apr_dbd_query(driver, handle, &nrows, statement);
100    printf("invalid op returned %d (should be nonzero).  Error msg follows\n", rv);
101    printf("'%s'\n", apr_dbd_error(driver, handle, rv));
102    statement = "INSERT into apr_dbd_test (col1, col2) values ('bar', 'foo')" ;
103    rv = apr_dbd_query(driver, handle, &nrows, statement);
104    printf("valid op returned %d (should be zero; error shouldn't affect subsequent ops)\n", rv);
105    return rv;
106}
107static int select_sequential(apr_pool_t* pool, apr_dbd_t* handle,
108                             const apr_dbd_driver_t* driver)
109{
110    int rv = 0;
111    int i = 0;
112    int n;
113    const char* entry;
114    const char* statement = "SELECT * FROM apr_dbd_test ORDER BY col1, col2";
115    apr_dbd_results_t *res = NULL;
116    apr_dbd_row_t *row = NULL;
117    rv = apr_dbd_select(driver,pool,handle,&res,statement,0);
118    if (rv) {
119        printf("Select failed: %s", apr_dbd_error(driver, handle, rv));
120        return rv;
121    }
122    for (rv = apr_dbd_get_row(driver, pool, res, &row, -1);
123         rv == 0;
124         rv = apr_dbd_get_row(driver, pool, res, &row, -1)) {
125        printf("ROW %d:	", ++i) ;
126        for (n = 0; n < apr_dbd_num_cols(driver, res); ++n) {
127            entry = apr_dbd_get_entry(driver, row, n);
128            if (entry == NULL) {
129                printf("(null)	") ;
130            }
131            else {
132                printf("%s	", entry);
133            }
134        }
135	fputs("\n", stdout);
136    }
137    return (rv == -1) ? 0 : 1;
138}
139static int select_random(apr_pool_t* pool, apr_dbd_t* handle,
140                         const apr_dbd_driver_t* driver)
141{
142    int rv = 0;
143    int n;
144    const char* entry;
145    const char* statement = "SELECT * FROM apr_dbd_test ORDER BY col1, col2";
146    apr_dbd_results_t *res = NULL;
147    apr_dbd_row_t *row = NULL;
148    rv = apr_dbd_select(driver,pool,handle,&res,statement,1);
149    if (rv) {
150        printf("Select failed: %s", apr_dbd_error(driver, handle, rv));
151        return rv;
152    }
153    rv = apr_dbd_get_row(driver, pool, res, &row, 5) ;
154    if (rv) {
155        printf("get_row failed: %s", apr_dbd_error(driver, handle, rv));
156        return rv;
157    }
158    printf("ROW 5:	");
159    for (n = 0; n < apr_dbd_num_cols(driver, res); ++n) {
160        entry = apr_dbd_get_entry(driver, row, n);
161        if (entry == NULL) {
162            printf("(null)	") ;
163        }
164        else {
165            printf("%s	", entry);
166        }
167    }
168    fputs("\n", stdout);
169    rv = apr_dbd_get_row(driver, pool, res, &row, 1) ;
170    if (rv) {
171        printf("get_row failed: %s", apr_dbd_error(driver, handle, rv));
172        return rv;
173    }
174    printf("ROW 1:	");
175    for (n = 0; n < apr_dbd_num_cols(driver, res); ++n) {
176        entry = apr_dbd_get_entry(driver, row, n);
177        if (entry == NULL) {
178            printf("(null)	") ;
179        }
180        else {
181            printf("%s	", entry);
182        }
183    }
184    fputs("\n", stdout);
185    rv = apr_dbd_get_row(driver, pool, res, &row, 11) ;
186    if (rv != -1) {
187        printf("Oops!  get_row out of range but thinks it succeeded!\n%s\n",
188                apr_dbd_error(driver, handle, rv));
189        return -1;
190    }
191    rv = 0;
192
193    return rv;
194}
195static int test_transactions(apr_pool_t* pool, apr_dbd_t* handle,
196                             const apr_dbd_driver_t* driver)
197{
198    int rv = 0;
199    int nrows;
200    apr_dbd_transaction_t *trans = NULL;
201    const char* statement;
202
203    /* trans 1 - error out early */
204    printf("Transaction 1\n");
205    rv = apr_dbd_transaction_start(driver, pool, handle, &trans);
206    if (rv) {
207        printf("Start transaction failed!\n%s\n",
208               apr_dbd_error(driver, handle, rv));
209        return rv;
210    }
211    statement = "UPDATE apr_dbd_test SET col2 = 'failed'";
212    rv = apr_dbd_query(driver, handle, &nrows, statement);
213    if (rv) {
214        printf("Update failed: '%s'\n", apr_dbd_error(driver, handle, rv));
215        apr_dbd_transaction_end(driver, pool, trans);
216        return rv;
217    }
218    printf("%d rows updated\n", nrows);
219
220    statement = "INSERT INTO apr_dbd_test1 (col3) values (3)";
221    rv = apr_dbd_query(driver, handle, &nrows, statement);
222    if (!rv) {
223        printf("Oops, invalid op succeeded but shouldn't!\n");
224    }
225    statement = "INSERT INTO apr_dbd_test values ('zzz', 'aaa', 3)";
226    rv = apr_dbd_query(driver, handle, &nrows, statement);
227    printf("Valid insert returned %d.  Should be nonzero (fail) because transaction is bad\n", rv) ;
228
229    rv = apr_dbd_transaction_end(driver, pool, trans);
230    if (rv) {
231        printf("End transaction failed!\n%s\n",
232               apr_dbd_error(driver, handle, rv));
233        return rv;
234    }
235    printf("Transaction ended (should be rollback) - viewing table\n"
236           "A column of \"failed\" indicates transaction failed (no rollback)\n");
237    select_sequential(pool, handle, driver);
238
239    /* trans 2 - complete successfully */
240    printf("Transaction 2\n");
241    rv = apr_dbd_transaction_start(driver, pool, handle, &trans);
242    if (rv) {
243        printf("Start transaction failed!\n%s\n",
244               apr_dbd_error(driver, handle, rv));
245        return rv;
246    }
247    statement = "UPDATE apr_dbd_test SET col2 = 'success'";
248    rv = apr_dbd_query(driver, handle, &nrows, statement);
249    if (rv) {
250        printf("Update failed: '%s'\n", apr_dbd_error(driver, handle, rv));
251        apr_dbd_transaction_end(driver, pool, trans);
252        return rv;
253    }
254    printf("%d rows updated\n", nrows);
255    statement = "INSERT INTO apr_dbd_test values ('aaa', 'zzz', 3)";
256    rv = apr_dbd_query(driver, handle, &nrows, statement);
257    printf("Valid insert returned %d.  Should be zero (OK)\n", rv) ;
258    rv = apr_dbd_transaction_end(driver, pool, trans);
259    if (rv) {
260        printf("End transaction failed!\n%s\n",
261               apr_dbd_error(driver, handle, rv));
262        return rv;
263    }
264    printf("Transaction ended (should be commit) - viewing table\n");
265    select_sequential(pool, handle, driver);
266    return rv;
267}
268static int test_pselect(apr_pool_t* pool, apr_dbd_t* handle,
269                        const apr_dbd_driver_t* driver)
270{
271    int rv = 0;
272    int i, n;
273    const char *query =
274        "SELECT * FROM apr_dbd_test WHERE col3 <= %s or col1 = 'bar'" ;
275    const char *label = "lowvalues";
276    apr_dbd_prepared_t *statement = NULL;
277    apr_dbd_results_t *res = NULL;
278    apr_dbd_row_t *row = NULL;
279    const char *entry = NULL;
280
281    rv = apr_dbd_prepare(driver, pool, handle, query, label, &statement);
282    if (rv) {
283        printf("Prepare statement failed!\n%s\n",
284               apr_dbd_error(driver, handle, rv));
285        return rv;
286    }
287    rv = apr_dbd_pvselect(driver, pool, handle, &res, statement, 0, "3", NULL);
288    if (rv) {
289        printf("Exec of prepared statement failed!\n%s\n",
290               apr_dbd_error(driver, handle, rv));
291        return rv;
292    }
293    i = 0;
294    printf("Selecting rows where col3 <= 3 and bar row where it's unset.\nShould show four rows.\n");
295    for (rv = apr_dbd_get_row(driver, pool, res, &row, -1);
296         rv == 0;
297         rv = apr_dbd_get_row(driver, pool, res, &row, -1)) {
298        printf("ROW %d:	", ++i) ;
299        for (n = 0; n < apr_dbd_num_cols(driver, res); ++n) {
300            entry = apr_dbd_get_entry(driver, row, n);
301            if (entry == NULL) {
302                printf("(null)	") ;
303            }
304            else {
305                printf("%s	", entry);
306            }
307        }
308	fputs("\n", stdout);
309    }
310    return (rv == -1) ? 0 : 1;
311}
312static int test_pquery(apr_pool_t* pool, apr_dbd_t* handle,
313                       const apr_dbd_driver_t* driver)
314{
315    int rv = 0;
316    const char *query = "INSERT INTO apr_dbd_test VALUES (%s, %s, %d)";
317    apr_dbd_prepared_t *statement = NULL;
318    const char *label = "testpquery";
319    int nrows;
320    apr_dbd_transaction_t *trans =0;
321
322    rv = apr_dbd_prepare(driver, pool, handle, query, label, &statement);
323    /* rv = apr_dbd_prepare(driver, pool, handle, query, NULL, &statement); */
324    if (rv) {
325        printf("Prepare statement failed!\n%s\n",
326               apr_dbd_error(driver, handle, rv));
327        return rv;
328    }
329    apr_dbd_transaction_start(driver, pool, handle, &trans);
330    rv = apr_dbd_pvquery(driver, pool, handle, &nrows, statement,
331                         "prepared", "insert", "2", NULL);
332    apr_dbd_transaction_end(driver, pool, trans);
333    if (rv) {
334        printf("Exec of prepared statement failed!\n%s\n",
335               apr_dbd_error(driver, handle, rv));
336        return rv;
337    }
338    printf("Showing table (should now contain row \"prepared insert 2\")\n");
339    select_sequential(pool, handle, driver);
340    return rv;
341}
342int main(int argc, char** argv)
343{
344    const char *name;
345    const char *params;
346    apr_pool_t *pool = NULL;
347    apr_dbd_t *sql = NULL;
348    const apr_dbd_driver_t *driver = NULL;
349    int rv;
350
351    apr_initialize();
352    apr_pool_create(&pool, NULL);
353
354    if (argc >= 2 && argc <= 3) {
355        name = argv[1];
356        params = ( argc == 3 ) ? argv[2] : "";
357        apr_dbd_init(pool);
358        setbuf(stdout,NULL);
359        rv = apr_dbd_get_driver(pool, name, &driver);
360        switch (rv) {
361        case APR_SUCCESS:
362           printf("Loaded %s driver OK.\n", name);
363           break;
364        case APR_EDSOOPEN:
365           printf("Failed to load driver file apr_dbd_%s.so\n", name);
366           goto finish;
367        case APR_ESYMNOTFOUND:
368           printf("Failed to load driver apr_dbd_%s_driver.\n", name);
369           goto finish;
370        case APR_ENOTIMPL:
371           printf("No driver available for %s.\n", name);
372           goto finish;
373        default:        /* it's a bug if none of the above happen */
374           printf("Internal error loading %s.\n", name);
375           goto finish;
376        }
377        rv = apr_dbd_open(driver, pool, params, &sql);
378        switch (rv) {
379        case APR_SUCCESS:
380           printf("Opened %s[%s] OK\n", name, params);
381           break;
382        case APR_EGENERAL:
383           printf("Failed to open %s[%s]\n", name, params);
384           goto finish;
385        default:        /* it's a bug if none of the above happen */
386           printf("Internal error opening %s[%s]\n", name, params);
387           goto finish;
388        }
389        TEST("create table", create_table);
390        TEST("insert rows", insert_rows);
391        TEST("invalid op", invalid_op);
392        TEST("select random", select_random);
393        TEST("select sequential", select_sequential);
394        TEST("transactions", test_transactions);
395        TEST("prepared select", test_pselect);
396        TEST("prepared query", test_pquery);
397        TEST("drop table", drop_table);
398        apr_dbd_close(driver, sql);
399    }
400    else {
401        fprintf(stderr, "Usage: %s driver-name [params]\n", argv[0]);
402    }
403finish:
404    apr_pool_destroy(pool);
405    apr_terminate();
406    return 0;
407}
408