1 //-< LOCALCLI.CPP >--------------------------------------------------*--------*
2 // GigaBASE                  Version 1.0         (c) 1999  GARRET    *     ?  *
3 // (Post Relational Database Management System)                      *   /\|  *
4 //                                                                   *  /  \  *
5 //                          Created:     20-Jun-2002  K.A. Knizhnik  * / [] \ *
6 //                          Last update: 20-Jun-2002  K.A. Knizhnik  * GARRET *
7 //-------------------------------------------------------------------*--------*
8 // Implementation of local C interface to database
9 //-------------------------------------------------------------------*--------*
10 
11 #define INSIDE_GIGABASE
12 
13 #include "localcli.h"
14 #include "btree.h"
15 #include "rtree.h"
16 #include "symtab.h"
17 
18 #ifdef SUPPORT_DATA_ENCRYPTION
19 #include "crypt/crypt_file.h"
20 #endif
21 
22 USE_GIGABASE_NAMESPACE
23 
24 #ifdef THROW_EXCEPTION_ON_ERROR
25 #define GB_TRY try {
26 #define GB_CATCH } catch (dbException const&) { return cli_runtime_error; }
27 #else
28 #define GB_TRY
29 #define GB_CATCH
30 #endif
31 
32 
33 dbCLI dbCLI::instance;
34 
cli_open(char const * server_url,int max_connect_attempts,int reconnect_timeout_sec,char_t const * user_name,char_t const * password,int pooled_connection)35 int cli_open(char const*   server_url,
36              int           max_connect_attempts,
37              int           reconnect_timeout_sec,
38              char_t const* user_name,
39              char_t const* password,
40              int           pooled_connection)
41 {
42     return cli_bad_address;
43 }
44 
cli_create(char_t const * databasePath,unsigned transactionCommitDelay,int openAttr,size_t poolSize)45 int cli_create(char_t const* databasePath,
46                unsigned      transactionCommitDelay,
47                int           openAttr,
48                size_t        poolSize)
49 {
50     return dbCLI::instance.create_session(databasePath, transactionCommitDelay, openAttr, poolSize, NULL);
51 }
52 
53 #ifdef SUPPORT_DATA_ENCRYPTION
cli_create_encrypted(char_t const * databasePath,unsigned transactionCommitDelay,int openAttr,size_t poolSize,char const * password)54 int cli_create_encrypted(char_t const* databasePath,
55                          unsigned      transactionCommitDelay,
56                          int           openAttr,
57                          size_t        poolSize,
58                          char const*   password)
59 {
60     return dbCLI::instance.create_session(databasePath, transactionCommitDelay, openAttr, poolSize, password);
61 }
62 #endif
63 
create_session(char_t const * databasePath,time_t transactionCommitDelay,int openAttr,size_t poolSize,char const * password)64 int dbCLI::create_session(char_t const* databasePath,
65                   time_t        transactionCommitDelay,
66                   int           openAttr,
67                   size_t        poolSize,
68                   char const*   password)
69 {
70     dbCriticalSection cs(sessionMutex);
71     dbDatabase* db = NULL;
72     session_desc* s;
73     for (s = active_session_list; s != NULL; s = s->next) {
74         if (STRCMP(s->name, databasePath) == 0) {
75             db = s->db;
76             db->accessCount += 1;
77             break;
78         }
79     }
80     if (db == NULL) {
81         GB_TRY
82         db = new dbDatabase((openAttr & cli_open_multiclient)
83                             ? (openAttr & cli_open_readonly)
84                                ? dbDatabase::dbMulticlientReadOnly : dbDatabase::dbMulticlientReadWrite
85                             : (openAttr & cli_open_readonly)
86                               ? dbDatabase::dbReadOnly : dbDatabase::dbAllAccess,
87                             poolSize);
88         bool success = false;
89         if (password == NULL) {
90             success = db->open(databasePath, transactionCommitDelay,
91                                ((openAttr & cli_open_readonly) ? dbFile::read_only : 0) |
92                                ((openAttr & cli_open_truncate) ? dbFile::truncate : 0) |
93                                ((openAttr & cli_open_no_buffering) ? dbFile::no_buffering : 0));
94 #ifdef SUPPORT_DATA_ENCRYPTION
95         } else {
96             dbCryptFile* file = new dbCryptFile(password);
97             if (file->open(databasePath,
98                           ((openAttr & cli_open_readonly) ? dbFile::read_only : 0) |
99                           ((openAttr & cli_open_truncate) ? dbFile::truncate : 0) |
100                           ((openAttr & cli_open_no_buffering) ? dbFile::no_buffering : 0)) == dbFile::ok)
101             {
102                 success = db->open(file, transactionCommitDelay, true);
103             }
104 #endif
105         }
106         if (!success) {
107             delete db;
108             return cli_database_not_found;
109         }
110         if (openAttr & cli_open_do_not_reuse_oids_on_close) {
111             db->disableOidReuseOnClose();
112         }
113         db->beginTransaction(dbUpdateLock);
114         dbGetTie tie;
115         dbTable* table = (dbTable*)db->getRow(tie, dbMetaTableId);
116         dbTableDescriptor* metatable = new dbTableDescriptor(table);
117         db->linkTable(metatable, dbMetaTableId);
118         oid_t tableId = table->firstRow;
119         while (tableId != 0) {
120             table = (dbTable*)db->getRow(tie, tableId);
121             dbTableDescriptor* desc;
122             for (desc = db->tables; desc != NULL && desc->tableId != tableId; desc = desc->nextDbTable);
123             if (desc == NULL) {
124                 desc = new dbTableDescriptor(table);
125                 db->linkTable(desc, tableId);
126                 desc->setFlags();
127             }
128             tableId = table->next;
129         }
130         if (!db->completeDescriptorsInitialization()) {
131             db->close();
132             delete db;
133             return cli_table_not_found;
134         }
135         db->accessCount = 1;
136         db->commit();
137         GB_CATCH
138     }
139     s = sessions.allocate();
140     s->db = db;
141     s->name = new char_t[STRLEN(databasePath) + 1];
142     STRCPY(s->name, databasePath);
143     s->stmts = NULL;
144     s->next = active_session_list;
145     s->existed_tables = NULL;
146     s->dropped_tables = NULL;
147     active_session_list = s;
148     return s->id;
149 }
150 
151 
cli_close(int session)152 int cli_close(int session)
153 {
154     return dbCLI::instance.close(session);
155 }
156 
close(int session)157 int dbCLI::close(int session)
158 {
159     dbCriticalSection cs(sessionMutex);
160     statement_desc *stmt, *next;
161     session_desc* s = sessions.get(session);
162     if (s == NULL) {
163         return cli_bad_descriptor;
164     }
165     dbCriticalSection cs2(s->mutex);
166     for (stmt = s->stmts; stmt != NULL; stmt = next) {
167         next = stmt->next;
168         release_statement(stmt);
169     }
170     if (--s->db->accessCount == 0) {
171         GB_TRY
172         s->db->close();
173         delete s->db;
174         GB_CATCH
175     }
176     while (s->dropped_tables != NULL) {
177         dbTableDescriptor* next = s->dropped_tables->nextDbTable;
178         delete s->dropped_tables;
179         s->dropped_tables = next;
180     }
181     session_desc** spp;
182     for (spp = &active_session_list; *spp != s; spp = &(*spp)->next);
183     *spp = s->next;
184     delete[] s->name;
185     sessions.free(s);
186 
187     return cli_ok;
188 }
189 
cli_statement(int session,char_t const * sql)190 int cli_statement(int session, char_t const* sql)
191 {
192     return dbCLI::instance.create_statement(session, sql);
193 }
194 
create_statement(int session,char_t const * sql)195 int dbCLI::create_statement(int session, char_t const* sql)
196 {
197     session_desc* s = sessions.get(session);
198     if (s == NULL) {
199         return cli_bad_descriptor;
200     }
201     statement_desc* stmt = statements.allocate();
202     stmt->sql.put(STRLEN(sql)+1);
203     STRCPY(stmt->sql.base(), sql);
204     stmt->columns = NULL;
205     stmt->params = NULL;
206     stmt->session = s;
207     stmt->cursor_type = cli_cursor_view_only;
208     stmt->first_fetch = true;
209     stmt->prepared = false;
210     stmt->n_params = 0;
211     stmt->n_columns = 0;
212     stmt->n_autoincremented_columns = 0;
213     stmt->oid = 0;
214     stmt->updated = false;
215     stmt->record_struct = NULL;
216     stmt->table = NULL;
217     stmt->cursor.db = NULL;
218     {
219         dbCriticalSection cs(s->mutex);
220         stmt->next = s->stmts;
221         s->stmts = stmt;
222     }
223     char_t const* p = sql;
224     parameter_binding** last = &stmt->params;
225     while (*p != '\0') {
226         if (*p == '\'') {
227             do {
228                 do {
229                     p += 1;
230                 } while (*p != '\0' && *p != '\'');
231                 if (*p == '\0') {
232                     *last = NULL;
233                     free_statement(stmt);
234                     return cli_bad_statement;
235                 }
236             } while (*++p == '\'');
237         } else if (*p == '%') {
238             stmt->n_params += 1;
239             char_t const* q = p++;
240             while (ISALNUM(*p) || *p == '_') p += 1;
241             if (*p == '%') {
242                 *last = NULL;
243                 free_statement(stmt);
244                 return cli_bad_statement;
245             }
246             parameter_binding* pb = parameter_allocator.allocate();
247             int len = (int)(p - q);
248             pb->name = new char_t[len+1];
249             memcpy(pb->name, q, len*sizeof(char_t));
250             pb->name[len] = '\0';
251             *last = pb;
252             last = &pb->next;
253             pb->var_ptr = NULL;
254         } else {
255             p += 1;
256         }
257     }
258     *last = NULL;
259     return stmt->id;
260 }
261 
cli_parameter(int statement,char_t const * param_name,int var_type,void * var_ptr)262 int cli_parameter(int           statement,
263                   char_t const* param_name,
264                   int           var_type,
265                   void*         var_ptr)
266 {
267     return dbCLI::instance.bind_parameter(statement, param_name, var_type, var_ptr);
268 }
269 
270 
bind_parameter(int statement,char_t const * param_name,int var_type,void * var_ptr)271 int dbCLI::bind_parameter(int           statement,
272                           char_t const* param_name,
273                           int           var_type,
274                           void*         var_ptr)
275 {
276     if ((((unsigned)var_type > cli_array_of_oid
277           && var_type != cli_array_of_int4
278           && var_type != cli_array_of_int8
279           && var_type != cli_rectangle
280           && var_type != cli_datetime)
281          || var_type == cli_decimal))
282     {
283         return cli_unsupported_type;
284     }
285     statement_desc* s = statements.get(statement);
286     if (s == NULL) {
287         return cli_bad_descriptor;
288     }
289     s->prepared = false;
290     for (parameter_binding* pb = s->params; pb != NULL; pb = pb->next) {
291         if (STRCMP(pb->name, param_name) == 0) {
292             pb->var_ptr  = var_ptr;
293             pb->var_type = var_type;
294             return cli_ok;
295         }
296     }
297     return cli_parameter_not_found;
298 }
299 
cli_column(int statement,char_t const * column_name,int var_type,int * var_len,void * var_ptr)300 int cli_column(int           statement,
301                char_t const* column_name,
302                int           var_type,
303                int*          var_len,
304                void*         var_ptr)
305 {
306     return dbCLI::instance.bind_column(statement, column_name, var_type, var_len, var_ptr);
307 }
308 
bind_column(int statement,char_t const * column_name,int var_type,int * var_len,void * var_ptr)309 int dbCLI::bind_column(int           statement,
310                        char_t const* column_name,
311                        int           var_type,
312                        int*          var_len,
313                        void*         var_ptr)
314 {
315     statement_desc* s = statements.get(statement);
316     if (s == NULL) {
317         return cli_bad_descriptor;
318     }
319     if (var_type == cli_decimal || var_type == cli_cstring || var_type == cli_array_of_decimal
320         || (unsigned)var_type >= cli_unknown)
321     {
322         return cli_unsupported_type;
323     }
324     s->prepared = false;
325     if (var_type == cli_autoincrement) {
326         s->n_autoincremented_columns += 1;
327     }
328     column_binding* cb = column_allocator.allocate();
329     cb->name = new char_t[STRLEN(column_name) + 1];
330     cb->next = s->columns;
331     s->columns = cb;
332     s->n_columns += 1;
333     STRCPY(cb->name, column_name);
334     cb->var_type = var_type;
335     cb->var_len = var_len;
336     cb->var_ptr = var_ptr;
337     cb->set_fnc = NULL;
338     cb->get_fnc = NULL;
339     return cli_ok;
340 }
341 
cli_array_column_ex(int statement,char_t const * column_name,int var_type,void * var_ptr,cli_column_set_ex set,cli_column_get_ex get,void * user_data)342 int cli_array_column_ex(int               statement,
343                         char_t const*     column_name,
344                         int               var_type,
345                         void*             var_ptr,
346                         cli_column_set_ex set,
347                         cli_column_get_ex get,
348                         void*             user_data)
349 {
350     return dbCLI::instance.bind_array_column(statement, column_name, var_type, var_ptr, set, get, user_data);
351 }
352 
353 
cli_array_column(int statement,char_t const * column_name,int var_type,void * var_ptr,cli_column_set set,cli_column_get get)354 int cli_array_column(int            statement,
355                      char_t const*  column_name,
356                      int            var_type,
357                      void*          var_ptr,
358                      cli_column_set set,
359                      cli_column_get get)
360 {
361     return cli_array_column_ex(statement, column_name, var_type, var_ptr,
362                                (cli_column_set_ex)set, (cli_column_get_ex)get, NULL);
363 }
364 
365 
bind_array_column(int statement,char_t const * column_name,int var_type,void * var_ptr,cli_column_set_ex set,cli_column_get_ex get,void * user_data)366 int dbCLI::bind_array_column(int               statement,
367                              char_t const*     column_name,
368                              int               var_type,
369                              void*             var_ptr,
370                              cli_column_set_ex set,
371                              cli_column_get_ex get,
372                              void*             user_data)
373 {
374     statement_desc* s = statements.get(statement);
375     if (s == NULL) {
376         return cli_bad_descriptor;
377     }
378     if (var_type < cli_asciiz || var_type > cli_array_of_string || var_type == cli_array_of_decimal) {
379         return cli_unsupported_type;
380     }
381     s->prepared = false;
382     column_binding* cb = column_allocator.allocate();
383     cb->name = new char_t[STRLEN(column_name) + 1];
384     cb->next = s->columns;
385     s->columns = cb;
386     s->n_columns += 1;
387     STRCPY(cb->name, column_name);
388     cb->var_type = var_type;
389     cb->var_len = NULL;
390     cb->var_ptr = var_ptr;
391     cb->set_fnc = set;
392     cb->get_fnc = get;
393     cb->user_data = user_data;
394     return cli_ok;
395 }
396 
match_columns(char_t const * table_name,statement_desc * stmt)397 int dbCLI::match_columns(char_t const* table_name, statement_desc* stmt)
398 {
399     stmt->table = stmt->session->db->findTable(table_name);
400     if (stmt->table == NULL) {
401         return cli_table_not_found;
402     }
403     for (column_binding* cb = stmt->columns; cb != NULL; cb = cb->next) {
404         cb->field = stmt->table->find(cb->name);
405         if (cb->field == NULL) {
406             return cli_column_not_found;
407         }
408     }
409     return cli_ok;
410 }
411 
cli_fetch(int statement,int for_update)412 int cli_fetch(int statement, int for_update)
413 {
414     cli_cardinality_t n_fetched_records = 0;
415     int rc = dbCLI::instance.fetch(statement, for_update, &n_fetched_records);
416     return rc < 0 ? rc : (int)n_fetched_records;
417 }
418 
cli_fetch_ex(int statement,int for_update,cli_cardinality_t * n_fetched_records)419 int cli_fetch_ex(int statement, int for_update, cli_cardinality_t* n_fetched_records)
420 {
421     return dbCLI::instance.fetch(statement, for_update, n_fetched_records);
422 }
423 
fetch(int statement,int cursor_type,cli_cardinality_t * n_fetched_records)424 int dbCLI::fetch(int statement, int cursor_type, cli_cardinality_t* n_fetched_records)
425 {
426     statement_desc* stmt = statements.get(statement);
427     if (stmt == NULL) {
428         return cli_bad_descriptor;
429     }
430     stmt->cursor_type = cursor_type;
431     stmt->oid = 0;
432     stmt->first_fetch = true;
433     if (!stmt->prepared) {
434         int tkn;
435         sql_scanner scanner(stmt->sql.base());
436         if (scanner.get() != tkn_select) {
437             return cli_bad_statement;
438         }
439         if ((tkn = scanner.get()) == tkn_all) {
440             tkn = scanner.get();
441         }
442         if (tkn == tkn_from && scanner.get() == tkn_ident) {
443             int rc = match_columns(scanner.identifier(), stmt);
444             if (rc != cli_ok) {
445                 return rc;
446             }
447         } else {
448             return cli_bad_statement;
449         }
450         char_t* p = scanner.current_position(), *q = p;
451         parameter_binding* pb = stmt->params;
452         stmt->query.reset();
453         while (*p != '\0') {
454             if (*p == '\'') {
455                 do {
456                     do {
457                         p += 1;
458                     } while (*p != '\0' && *p != '\'');
459                     if (*p == '\0') {
460                         return cli_bad_statement;
461                     }
462                 } while (*++p == '\'');
463             } else if (*p == '%') {
464                 if (p != q) {
465                     *p = '\0';
466                     stmt->query.append(dbQueryElement::qExpression, q);
467                 }
468                 if (pb->var_ptr == NULL) {
469                     return cli_unbound_parameter;
470                 }
471                 switch(pb->var_type) {
472                   case cli_oid:
473                     stmt->query.append(dbQueryElement::qVarReference, pb->var_ptr);
474                     break;
475                   case cli_bool:
476                     stmt->query.append(dbQueryElement::qVarBool, pb->var_ptr);
477                     break;
478                   case cli_int1:
479                     stmt->query.append(dbQueryElement::qVarInt1, pb->var_ptr);
480                     break;
481                   case cli_int2:
482                     stmt->query.append(dbQueryElement::qVarInt2, pb->var_ptr);
483                     break;
484                   case cli_int4:
485                     stmt->query.append(dbQueryElement::qVarInt4, pb->var_ptr);
486                     break;
487                   case cli_int8:
488                     stmt->query.append(dbQueryElement::qVarInt8, pb->var_ptr);
489                     break;
490                   case cli_datetime:
491                     stmt->query.append(sizeof(cli_time_t) == 4
492                                        ? dbQueryElement::qVarInt4 : dbQueryElement::qVarInt8,
493                                        pb->var_ptr);
494                     break;
495                   case cli_real4:
496                     stmt->query.append(dbQueryElement::qVarReal4, pb->var_ptr);
497                     break;
498                   case cli_real8:
499                     stmt->query.append(dbQueryElement::qVarReal8, pb->var_ptr);
500                     break;
501                   case cli_asciiz:
502                     stmt->query.append(dbQueryElement::qVarString, pb->var_ptr);
503                     break;
504                   case cli_pasciiz:
505                     stmt->query.append(dbQueryElement::qVarStringPtr, pb->var_ptr);
506                     break;
507                   case cli_array_of_oid:
508                     stmt->query.append(dbQueryElement::qVarArrayOfRef, pb->var_ptr);
509                     break;
510                   case cli_array_of_int4:
511                     stmt->query.append(dbQueryElement::qVarArrayOfInt4, pb->var_ptr);
512                     break;
513                   case cli_array_of_int8:
514                     stmt->query.append(dbQueryElement::qVarArrayOfInt8, pb->var_ptr);
515                     break;
516                   case cli_rectangle:
517                     stmt->query.append(dbQueryElement::qVarRectangle, pb->var_ptr);
518                     break;
519                   default:
520                     return cli_unsupported_type;
521 
522                 }
523                 while (ISALNUM(*++p));
524                 q = p;
525                 pb = pb->next;
526             } else {
527                 p += 1;
528             }
529         }
530         if (p != q) {
531             stmt->query.append(dbQueryElement::qExpression, q);
532         }
533         stmt->prepared = true;
534     }
535     stmt->cursor.setTable(stmt->table);
536     stmt->cursor.reset();
537     GB_TRY
538         *n_fetched_records = stmt->cursor.select(stmt->query, (dbCursorType)cursor_type);
539         return cli_ok;
540 #ifdef THROW_EXCEPTION_ON_ERROR
541     } catch (dbException const& x) {
542         return (x.getErrCode() == dbDatabase::QueryError)
543             ? cli_bad_statement : cli_runtime_error;
544     }
545 #endif
546 }
547 
548 
549 int dbCLI::fetch_columns(statement_desc* stmt)
550 {
551     stmt->first_fetch = false;
552     if (stmt->cursor.isEmpty()) {
553         return cli_not_found;
554     }
555     dbGetTie tie;
556     GB_TRY
557     stmt->updated = false;
558     if (stmt->record_struct != NULL) {
559         stmt->cursor.fetch();
560         return cli_ok;
561     }
562     char* data = (char*)stmt->session->db->getRow(tie, stmt->cursor.currId);
563     for (column_binding* cb = stmt->columns; cb != NULL; cb = cb->next) {
564         char* src = data + cb->field->dbsOffs;
565         char* dst = (char*)cb->var_ptr;
566         switch (cb->field->type) {
567           case dbField::tpBool:
568             switch (cb->var_type) {
569               case cli_bool:
570                 *(cli_bool_t*)dst = *(bool*)src;
571                 continue;
572               case cli_int1:
573                 *(cli_int1_t*)dst = *(bool*)src ? 1 : 0;
574                 continue;
575               case cli_int2:
576                 *(cli_int2_t*)dst = *(bool*)src ? 1 : 0;
577                 continue;
578               case cli_int4:
579                 *(cli_int4_t*)dst = *(bool*)src ? 1 : 0;
580                 continue;
581               case cli_int8:
582                 *(db_int8*)dst = *(bool*)src ? 1 : 0;
583                 continue;
584               case cli_datetime:
585                 *(cli_time_t*)dst = *(bool*)src ? 1 : 0;
586                 continue;
587               case cli_real4:
588                 *(cli_real4_t*)dst = (cli_real4_t)(*(bool*)src ? 1 : 0);
589                 continue;
590               case cli_real8:
591                 *(cli_real8_t*)dst = *(bool*)src ? 1 : 0;
592                 continue;
593             }
594             break;
595           case dbField::tpInt1:
596             switch (cb->var_type) {
597               case cli_bool:
598                 *(cli_bool_t*)dst = *(int1*)src != 0;
599                 continue;
600               case cli_int1:
601                 *(cli_int1_t*)dst = *(int1*)src;
602                 continue;
603               case cli_int2:
604                 *(cli_int2_t*)dst = *(int1*)src;
605                 continue;
606               case cli_int4:
607                 *(cli_int4_t*)dst = *(int1*)src;
608                 continue;
609               case cli_int8:
610                 *(db_int8*)dst = *(int1*)src;
611                 continue;
612               case cli_datetime:
613                 *(cli_time_t*)dst = *(int1*)src;
614                 continue;
615               case cli_real4:
616                 *(cli_real4_t*)dst = *(int1*)src;
617                 continue;
618               case cli_real8:
619                 *(cli_real8_t*)dst = *(int1*)src;
620                 continue;
621             }
622             break;
623           case dbField::tpInt2:
624             switch (cb->var_type) {
625               case cli_bool:
626                 *(cli_bool_t*)dst = *(int2*)src != 0;
627                 continue;
628               case cli_int1:
629                 *(cli_int1_t*)dst = (int1)*(int2*)src;
630                 continue;
631               case cli_int2:
632                 *(cli_int2_t*)dst = *(int2*)src;
633                 continue;
634               case cli_int4:
635                 *(cli_int4_t*)dst = *(int2*)src;
636                 continue;
637               case cli_int8:
638                 *(db_int8*)dst = *(int2*)src;
639                 continue;
640               case cli_datetime:
641                 *(cli_time_t*)dst = *(int2*)src;
642                 continue;
643               case cli_real4:
644                 *(cli_real4_t*)dst = *(int2*)src;
645                 continue;
646               case cli_real8:
647                 *(cli_real8_t*)dst = *(int2*)src;
648                 continue;
649             }
650             break;
651           case dbField::tpInt4:
652             switch (cb->var_type) {
653               case cli_bool:
654                 *(cli_bool_t*)dst = *(int4*)src != 0;
655                 continue;
656               case cli_int1:
657                 *(cli_int1_t*)dst = (cli_int1_t)*(int4*)src;
658                 continue;
659               case cli_int2:
660                 *(cli_int2_t*)dst = (cli_int2_t)*(int4*)src;
661                 continue;
662               case cli_int4:
663               case cli_autoincrement:
664                 *(cli_int4_t*)dst = *(int4*)src;
665                 continue;
666               case cli_int8:
667                 *(db_int8*)dst = *(int4*)src;
668                 continue;
669               case cli_datetime:
670                 *(cli_time_t*)dst = *(int4*)src;
671                 continue;
672               case cli_real4:
673                 *(cli_real4_t*)dst = (cli_real4_t)*(int4*)src;
674                 continue;
675               case cli_real8:
676                 *(cli_real8_t*)dst = *(int4*)src;
677                 continue;
678             }
679             break;
680           case dbField::tpInt8:
681             switch (cb->var_type) {
682               case cli_bool:
683                 *(cli_bool_t*)dst = *(db_int8*)src != 0;
684                 continue;
685               case cli_int1:
686                 *(cli_int1_t*)dst = (cli_int1_t)*(db_int8*)src;
687                 continue;
688               case cli_int2:
689                 *(cli_int2_t*)dst = (cli_int2_t)*(db_int8*)src;
690                 continue;
691               case cli_int4:
692                 *(cli_int4_t*)dst = (cli_int4_t)*(db_int8*)src;
693                 continue;
694               case cli_int8:
695                 *(db_int8*)dst = *(db_int8*)src;
696                 continue;
697               case cli_datetime:
698                 *(cli_time_t*)dst = (cli_time_t)*(db_int8*)src;
699                 continue;
700               case cli_real4:
701                 *(cli_real4_t*)dst = (cli_real4_t)*(db_int8*)src;
702                 continue;
703               case cli_real8:
704                 *(cli_real8_t*)dst = (cli_real8_t)*(db_int8*)src;
705                 continue;
706             }
707             break;
708           case dbField::tpReal4:
709             switch (cb->var_type) {
710               case cli_bool:
711                 *(cli_bool_t*)dst = *(real4*)src != 0;
712                 continue;
713               case cli_int1:
714                 *(cli_int1_t*)dst = (cli_int1_t)*(real4*)src;
715                 continue;
716               case cli_int2:
717                 *(cli_int2_t*)dst = (cli_int2_t)*(real4*)src;
718                 continue;
719               case cli_int4:
720                 *(cli_int4_t*)dst = (cli_int4_t)*(real4*)src;
721                 continue;
722               case cli_int8:
723                 *(db_int8*)dst = (db_int8)*(real4*)src;
724                 continue;
725               case cli_datetime:
726                 *(cli_time_t*)dst = (cli_time_t)*(real4*)src;
727                 continue;
728               case cli_real4:
729                 *(cli_real4_t*)dst = *(real4*)src;
730                 continue;
731               case cli_real8:
732                 *(cli_real8_t*)dst = *(real4*)src;
733                 continue;
734             }
735             break;
736           case dbField::tpReal8:
737             switch (cb->var_type) {
738               case cli_bool:
739                 *(cli_bool_t*)dst = *(real8*)src != 0;
740                 continue;
741               case cli_int1:
742                 *(cli_int1_t*)dst = (cli_int1_t)*(real8*)src;
743                 continue;
744               case cli_int2:
745                 *(cli_int2_t*)dst = (cli_int2_t)*(real8*)src;
746                 continue;
747               case cli_int4:
748                 *(cli_int4_t*)dst = (cli_int4_t)*(real8*)src;
749                 continue;
750               case cli_int8:
751                 *(db_int8*)dst = (db_int8)*(real8*)src;
752                 continue;
753               case cli_datetime:
754                 *(cli_time_t*)dst = (cli_time_t)*(real8*)src;
755                 continue;
756               case cli_real4:
757                 *(cli_real4_t*)dst = (real4)*(real8*)src;
758                 continue;
759               case cli_real8:
760                 *(cli_real8_t*)dst = *(real8*)src;
761                 continue;
762             }
763             break;
764           case dbField::tpReference:
765             if (cb->var_type == cli_oid) {
766                 *(cli_oid_t*)dst = *(oid_t*)src;
767                 continue;
768             }
769             break;
770           case dbField::tpRectangle:
771             if (cb->var_type == cli_rectangle) {
772                 *(cli_rectangle_t*)dst = *(cli_rectangle_t*)src;
773                 continue;
774             }
775             break;
776           case dbField::tpString:
777             if (cb->var_type == cli_asciiz || cb->var_type == cli_pasciiz) {
778                 if (cb->var_type == cli_pasciiz) {
779                     dst = *(char**)dst;
780                 }
781                 dbVarying* v = (dbVarying*)src;
782                 int size = v->size;
783                 if (cb->set_fnc != NULL) {
784                     dst = (char*)cb->set_fnc(cli_asciiz, dst, size,
785                                              cb->name, stmt->id, data + v->offs, cb->user_data);
786                     if (dst != NULL) {
787                         memcpy(dst, data + v->offs, size*sizeof(char_t));
788                     }
789                 } else {
790                     int n = size;
791                     if (cb->var_len != NULL) {
792                         if (n > *cb->var_len) {
793                             n = *cb->var_len;
794                         }
795                         *cb->var_len = size;
796                     }
797                     memcpy(dst, data + v->offs, n*sizeof(char_t));
798                 }
799                 continue;
800             }
801             break;
802           case dbField::tpArray:
803             if (cb->var_type >= cli_array_of_oid && cb->var_type <= cli_array_of_string && cb->var_type != cli_array_of_decimal)
804             {
805                 dbVarying* v = (dbVarying*)src;
806                 int n = v->size;
807                 src = data + v->offs;
808                 if (cb->set_fnc != NULL) {
809                     dst = (char*)cb->set_fnc(cb->var_type, dst, n,
810                                              cb->name, stmt->id, src, cb->user_data);
811                     if (dst == NULL) {
812                         continue;
813                     }
814                 } else {
815                     int size = n;
816                     if (cb->var_len != NULL) {
817                         if (n > *cb->var_len) {
818                             n = *cb->var_len;
819                         }
820                         *cb->var_len = size;
821                     }
822                 }
823                 switch (cb->field->components->type) {
824                   case dbField::tpBool:
825                     if (cb->var_type == cli_array_of_bool) {
826                         cli_bool_t* dst_elem = (cli_bool_t*)dst;
827                         bool*       src_elem = (bool*)src;
828                         while (--n >= 0) *dst_elem++ = *src_elem++;
829                         continue;
830                     }
831                     break;
832                   case dbField::tpInt1:
833                     if (cb->var_type == cli_array_of_int1) {
834                         cli_int1_t* dst_elem = (cli_int1_t*)dst;
835                         int1*       src_elem = (int1*)src;
836                         while (--n >= 0) *dst_elem++ = *src_elem++;
837                         continue;
838                     }
839                     break;
840                   case dbField::tpInt2:
841                     if (cb->var_type == cli_array_of_int2) {
842                         cli_int2_t* dst_elem = (cli_int2_t*)dst;
843                         int2*       src_elem = (int2*)src;
844                         while (--n >= 0) *dst_elem++ = *src_elem++;
845                         continue;
846                     }
847                     break;
848                   case dbField::tpInt4:
849                     if (cb->var_type == cli_array_of_int4) {
850                         cli_int4_t* dst_elem = (cli_int4_t*)dst;
851                         int4*       src_elem = (int4*)src;
852                         while (--n >= 0) *dst_elem++ = *src_elem++;
853                         continue;
854                     }
855                     break;
856                   case dbField::tpInt8:
857                     if (cb->var_type == cli_array_of_int8) {
858                         cli_int8_t* dst_elem = (cli_int8_t*)dst;
859                         db_int8*    src_elem = (db_int8*)src;
860                         while (--n >= 0) *dst_elem++ = *src_elem++;
861                         continue;
862                     }
863                     break;
864                   case dbField::tpReal4:
865                     if (cb->var_type == cli_array_of_real4) {
866                         cli_real4_t* dst_elem = (cli_real4_t*)dst;
867                         real4*       src_elem = (real4*)src;
868                         while (--n >= 0) *dst_elem++ = *src_elem++;
869                         continue;
870                     }
871                     break;
872                   case dbField::tpReal8:
873                     if (cb->var_type == cli_array_of_real8) {
874                         cli_real8_t* dst_elem = (cli_real8_t*)dst;
875                         real8*       src_elem = (real8*)src;
876                         while (--n >= 0) *dst_elem++ = *src_elem++;
877                         continue;
878                     }
879                     break;
880                   case dbField::tpReference:
881                     if (cb->var_type == cli_array_of_oid) {
882                         cli_oid_t* dst_elem = (cli_oid_t*)dst;
883                         oid_t*     src_elem = (oid_t*)src;
884                         while (--n >= 0) *dst_elem++ = *src_elem++;
885                         continue;
886                     }
887                     break;
888                   case dbField::tpString:
889                     if (cb->var_type == cli_array_of_string) {
890                         char_t** dst_elem = (char_t**)dst;
891                         dbVarying* src_elem = (dbVarying*)src;
892                         while (--n >= 0) {
893                             *dst_elem++ = (char_t*)((char*)src_elem + src_elem->offs);
894                             src_elem += 1;
895                         }
896                         continue;
897                     }
898                 }
899             }
900         }
901         return cli_unsupported_type;
902     }
903     GB_CATCH
904     return cli_ok;
905 }
906 
907 
908 int dbCLI::store_columns(char* data, statement_desc* stmt, bool insert)
909 {
910     for (column_binding* cb = stmt->columns; cb != NULL; cb = cb->next)
911     {
912         char* dst = data + cb->field->appOffs;
913         char* src = (char*)cb->var_ptr;
914         switch (cb->field->type) {
915           case dbField::tpBool:
916             switch (cb->var_type) {
917               case cli_bool:
918                 *(bool*)dst = *(cli_bool_t*)src;
919                 continue;
920               case cli_int1:
921                 *(bool*)dst = *(cli_int1_t*)src != 0;
922                 continue;
923               case cli_int2:
924                 *(bool*)dst = *(cli_int2_t*)src != 0;
925                 continue;
926               case cli_int4:
927                 *(bool*)dst = *(cli_int4_t*)src != 0;
928                 continue;
929               case cli_int8:
930                 *(bool*)dst = *(db_int8*)src != 0;
931                 continue;
932               case cli_datetime:
933                 *(bool*)dst = *(cli_time_t*)src != 0;
934                 continue;
935               case cli_real4:
936                 *(bool*)dst = *(cli_real4_t*)src != 0;
937                 continue;
938               case cli_real8:
939                 *(bool*)dst = *(cli_real8_t*)src != 0;
940                 continue;
941             }
942             break;
943           case dbField::tpInt1:
944             switch (cb->var_type) {
945               case cli_bool:
946                 *(int1*)dst = *(cli_bool_t*)src ? 1 : 0;
947                 continue;
948               case cli_int1:
949                 *(int1*)dst = *(cli_int1_t*)src;
950                 continue;
951               case cli_int2:
952                 *(int1*)dst = (int1)*(cli_int2_t*)src;
953                 continue;
954               case cli_int4:
955                 *(int1*)dst = (int1)*(cli_int4_t*)src;
956                 continue;
957               case cli_int8:
958                 *(int1*)dst = (int1)*(db_int8*)src;
959                 continue;
960               case cli_datetime:
961                 *(int1*)dst = (int1)*(cli_time_t*)src;
962                 continue;
963               case cli_real4:
964                 *(int1*)dst = (int1)*(cli_real4_t*)src;
965                 continue;
966               case cli_real8:
967                 *(int1*)dst = (int1)*(cli_real8_t*)src;
968                 continue;
969             }
970             break;
971           case dbField::tpInt2:
972             switch (cb->var_type) {
973               case cli_bool:
974                 *(int2*)dst = *(cli_bool_t*)src ? 1 : 0;
975                 continue;
976               case cli_int1:
977                 *(int2*)dst = *(cli_int1_t*)src;
978                 continue;
979               case cli_int2:
980                 *(int2*)dst = *(cli_int2_t*)src;
981                 continue;
982               case cli_int4:
983                 *(int2*)dst = (int2)*(cli_int4_t*)src;
984                 continue;
985               case cli_int8:
986                 *(int2*)dst = (int2)*(db_int8*)src;
987                 continue;
988               case cli_datetime:
989                 *(int2*)dst = (int2)*(cli_time_t*)src;
990                 continue;
991               case cli_real4:
992                 *(int2*)dst = (int2)*(cli_real4_t*)src;
993                 continue;
994               case cli_real8:
995                 *(int2*)dst = (int2)*(cli_real8_t*)src;
996                 continue;
997             }
998             break;
999           case dbField::tpInt4:
1000             switch (cb->var_type) {
1001               case cli_bool:
1002                 *(int4*)dst = *(cli_bool_t*)src ? 1 : 0;
1003                 continue;
1004               case cli_int1:
1005                 *(int4*)dst = *(cli_int1_t*)src;
1006                 continue;
1007               case cli_int2:
1008                 *(int4*)dst = *(cli_int2_t*)src;
1009                 continue;
1010               case cli_autoincrement:
1011 #ifdef AUTOINCREMENT_SUPPORT
1012                 if (insert) {
1013                     *(int4*)dst = cb->field->defTable->autoincrementCount+1;
1014                 }
1015 #endif
1016                 continue;
1017               case cli_int4:
1018                 *(int4*)dst = *(cli_int4_t*)src;
1019                 continue;
1020               case cli_int8:
1021                 *(int4*)dst = (int4)*(db_int8*)src;
1022                 continue;
1023               case cli_datetime:
1024                 *(int4*)dst = (int4)*(cli_time_t*)src;
1025                 continue;
1026               case cli_real4:
1027                 *(int4*)dst = (int4)*(cli_real4_t*)src;
1028                 continue;
1029               case cli_real8:
1030                 *(int4*)dst = (int4)*(cli_real8_t*)src;
1031                 continue;
1032             }
1033             break;
1034           case dbField::tpInt8:
1035             switch (cb->var_type) {
1036               case cli_bool:
1037                 *(db_int8*)dst = *(cli_bool_t*)src ? 1 : 0;
1038                 continue;
1039               case cli_int1:
1040                 *(db_int8*)dst = *(cli_int1_t*)src;
1041                 continue;
1042               case cli_int2:
1043                 *(db_int8*)dst = *(cli_int2_t*)src;
1044                 continue;
1045               case cli_int4:
1046                 *(db_int8*)dst = *(cli_int4_t*)src;
1047                 continue;
1048               case cli_int8:
1049                 *(db_int8*)dst = *(db_int8*)src;
1050                 continue;
1051               case cli_datetime:
1052                 *(db_int8*)dst = (db_int8)*(cli_time_t*)src;
1053                 continue;
1054               case cli_real4:
1055                 *(db_int8*)dst = (db_int8)*(cli_real4_t*)src;
1056                 continue;
1057               case cli_real8:
1058                 *(db_int8*)dst = (db_int8)*(cli_real8_t*)src;
1059                 continue;
1060             }
1061             break;
1062           case dbField::tpReal4:
1063             switch (cb->var_type) {
1064               case cli_bool:
1065                 *(real4*)dst = (real4)(*(cli_bool_t*)src ? 1 : 0);
1066                 continue;
1067               case cli_int1:
1068                 *(real4*)dst = *(cli_int1_t*)src;
1069                 continue;
1070               case cli_int2:
1071                 *(real4*)dst = *(cli_int2_t*)src;
1072                 continue;
1073               case cli_int4:
1074                 *(real4*)dst = (real4)(*(cli_int4_t*)src);
1075                 continue;
1076               case cli_int8:
1077                 *(real4*)dst = (real4)*(db_int8*)src;
1078                 continue;
1079               case cli_datetime:
1080                 *(real4*)dst = (real4)*(cli_time_t*)src;
1081                 continue;
1082               case cli_real4:
1083                 *(real4*)dst = *(cli_real4_t*)src;
1084                 continue;
1085               case cli_real8:
1086                 *(real4*)dst = (real4)*(cli_real8_t*)src;
1087                 continue;
1088             }
1089             break;
1090           case dbField::tpReal8:
1091             switch (cb->var_type) {
1092               case cli_bool:
1093                 *(real8*)dst = *(cli_bool_t*)src ? 1 : 0;
1094                 continue;
1095               case cli_int1:
1096                 *(real8*)dst = *(cli_int1_t*)src;
1097                 continue;
1098               case cli_int2:
1099                 *(real8*)dst = *(cli_int2_t*)src;
1100                 continue;
1101               case cli_int4:
1102                 *(real8*)dst = *(cli_int4_t*)src;
1103                 continue;
1104               case cli_int8:
1105                 *(real8*)dst = (real8)*(db_int8*)src;
1106                 continue;
1107               case cli_datetime:
1108                 *(real8*)dst = (real8)*(cli_time_t*)src;
1109                 continue;
1110               case cli_real4:
1111                 *(real8*)dst = *(cli_real4_t*)src;
1112                 continue;
1113               case cli_real8:
1114                 *(real8*)dst = *(cli_real8_t*)src;
1115                 continue;
1116             }
1117             break;
1118           case dbField::tpReference:
1119             if (cb->var_type == cli_oid) {
1120                 *(oid_t*)dst = *(cli_oid_t*)src;
1121                 continue;
1122             }
1123             break;
1124           case dbField::tpRectangle:
1125             if (cb->var_type == cli_rectangle) {
1126                 *(cli_rectangle_t*)dst = *(cli_rectangle_t*)src;
1127                 continue;
1128             }
1129             break;
1130           case dbField::tpString:
1131             if (cb->var_type == cli_pasciiz) {
1132                 *(char**)dst = *(char**)src;
1133                 continue;
1134             } else if (cb->var_type == cli_asciiz) {
1135                 if (cb->get_fnc != NULL) {
1136                     int len;
1137                     src = (char*)cb->get_fnc(cb->var_type, src, &len,
1138                                              cb->name, stmt->id, cb->user_data);
1139                 }
1140                 *(char**)dst = src;
1141                 continue;
1142             }
1143             break;
1144           case dbField::tpArray:
1145             if (cb->var_type >= cli_array_of_oid && cb->var_type <= cli_array_of_string && cb->var_type != cli_array_of_decimal)
1146             {
1147                 int size = 0;
1148                 if (cb->get_fnc != NULL) {
1149                     src = (char*)cb->get_fnc(cb->var_type, src, &size,
1150                                              cb->name, stmt->id, cb->user_data);
1151                 } else {
1152                     if (cb->var_len != NULL) {
1153                         size = *cb->var_len;
1154                     } else {
1155                         return cli_incompatible_type;
1156                     }
1157                 }
1158                 if (cb->var_type == cli_array_of_string) {
1159                     if (cb->field->components->type != dbField::tpString) {
1160                         return cli_incompatible_type;
1161                     }
1162                 } else if ((size_t)sizeof_type[cb->var_type - cli_array_of_oid] != cb->field->components->appSize) {
1163                     return cli_incompatible_type;
1164                 }
1165                 cb->field->arrayAllocator((dbAnyArray*)dst, src, size);
1166                 continue;
1167             }
1168         }
1169         return cli_unsupported_type;
1170     }
1171     return cli_ok;
1172 }
1173 
1174 
1175 
1176 int cli_insert(int statement, cli_oid_t* oid)
1177 {
1178     return dbCLI::instance.insert(statement, oid, false);
1179 }
1180 
1181 int cli_batch_insert(int statement, cli_oid_t* oid)
1182 {
1183     return dbCLI::instance.insert(statement, oid, true);
1184 }
1185 
1186 int dbCLI::insert(int statement, cli_oid_t* oid, bool batch)
1187 {
1188     statement_desc* stmt = statements.get(statement);
1189     if (stmt == NULL) {
1190         return cli_bad_descriptor;
1191     }
1192     if (!stmt->prepared) {
1193         sql_scanner scanner(stmt->sql.base());
1194         if (scanner.get() != tkn_insert
1195             || scanner.get() != tkn_into
1196             || scanner.get() != tkn_ident)
1197         {
1198             return cli_bad_statement;
1199         }
1200         int rc = match_columns(scanner.identifier(), stmt);
1201         if (rc != cli_ok) {
1202             return rc;
1203         }
1204         stmt->prepared = true;
1205     }
1206     dbSmallBuffer<byte> buf(stmt->table->appSize);
1207     byte* obj = buf.base();
1208     memset(obj, 0, stmt->table->appSize);
1209     dbFieldDescriptor *first = stmt->table->columns, *fd = first;
1210     do {
1211         if (fd->appType == dbField::tpString) {
1212             *(char_t**)(obj + fd->appOffs) = STRLITERAL("");
1213         }
1214     } while ((fd = fd->next) != first);
1215 
1216     char* rec = (char*)buf.base();
1217     int rc = store_columns(rec, stmt, true);
1218     if (rc != cli_ok) {
1219         return rc;
1220     }
1221 
1222     dbAnyReference ref;
1223     GB_TRY
1224     if (!stmt->session->db->insertRecord(stmt->table, &ref, rec, batch)) {
1225         stmt->oid = 0;
1226         return cli_not_unique;
1227     }
1228     GB_CATCH
1229     stmt->oid = ref.getOid();
1230     if (oid != NULL) {
1231         *oid = ref.getOid();
1232     }
1233     if (stmt->n_autoincremented_columns > 0) {
1234         for (column_binding* cb = stmt->columns; cb != NULL; cb = cb->next) {
1235             if (cb->var_type == cli_autoincrement) {
1236                 *(cli_int4_t*)cb->var_ptr = *(int4*)(rec + cb->field->appOffs);
1237             }
1238         }
1239     }
1240     return cli_ok;
1241 }
1242 
1243 int cli_update(int statement)
1244 {
1245     return dbCLI::instance.update(statement);
1246 }
1247 
1248 int dbCLI::update(int statement)
1249 {
1250     statement_desc* stmt = statements.get(statement);
1251     if (stmt == NULL) {
1252         return cli_bad_descriptor;
1253     }
1254     if (!stmt->prepared) {
1255         return cli_not_fetched;
1256     }
1257     if (stmt->cursor_type != cli_cursor_for_update) {
1258         return cli_not_update_mode;
1259     }
1260     if (stmt->updated) {
1261         return cli_already_updated;
1262     }
1263     if (stmt->cursor.isEmpty()) {
1264         return cli_not_found;
1265     }
1266     bool succeed;
1267     GB_TRY
1268     if (stmt->record_struct == NULL) {
1269         dbSmallBuffer<byte> buf(stmt->table->appSize);
1270         byte* record = buf.base();
1271         memset(record, 0, stmt->table->appSize);
1272         stmt->cursor.setRecord(record);
1273         stmt->cursor.fetch();
1274 
1275         int rc = store_columns((char*)buf.base(), stmt, false);
1276         if (rc != cli_ok) {
1277             stmt->cursor.setRecord(NULL);
1278             return rc;
1279         }
1280         succeed = stmt->cursor.update();
1281         stmt->cursor.setRecord(NULL);
1282     } else {
1283         succeed = stmt->cursor.update();
1284     }
1285     GB_CATCH
1286     if (succeed) {
1287         stmt->updated = true;
1288         return cli_ok;
1289     } else {
1290         return cli_not_unique;
1291     }
1292 }
1293 
1294 int cli_freeze(int statement)
1295 {
1296     return dbCLI::instance.freeze(statement);
1297 }
1298 
1299 int dbCLI::freeze(int statement)
1300 {
1301     statement_desc* stmt = statements.get(statement);
1302     if (stmt == NULL) {
1303         return cli_bad_descriptor;
1304     }
1305     if (!stmt->prepared) {
1306         return cli_not_fetched;
1307     }
1308     stmt->cursor.freeze();
1309     return cli_ok;
1310 }
1311 
1312 int cli_unfreeze(int statement)
1313 {
1314     return dbCLI::instance.unfreeze(statement);
1315 }
1316 
1317 int dbCLI::unfreeze(int statement)
1318 {
1319     statement_desc* stmt = statements.get(statement);
1320     if (stmt == NULL) {
1321         return cli_bad_descriptor;
1322     }
1323     if (!stmt->prepared) {
1324         return cli_not_fetched;
1325     }
1326     GB_TRY
1327     stmt->cursor.unfreeze();
1328     GB_CATCH
1329     return cli_ok;
1330 }
1331 
1332 int cli_get_first(int statement)
1333 {
1334     return dbCLI::instance.get_first(statement);
1335 }
1336 
1337 int dbCLI::get_first(int statement)
1338 {
1339     statement_desc* stmt = statements.get(statement);
1340     if (stmt == NULL) {
1341         return cli_bad_descriptor;
1342     }
1343     if (!stmt->prepared) {
1344         return cli_not_fetched;
1345     }
1346     if (!stmt->cursor.gotoFirst()) {
1347         return cli_not_found;
1348     }
1349     return fetch_columns(stmt);
1350 }
1351 
1352 int cli_get_last(int statement)
1353 {
1354     return dbCLI::instance.get_last(statement);
1355 }
1356 
1357 int dbCLI::get_last(int statement)
1358 {
1359     statement_desc* stmt = statements.get(statement);
1360     if (stmt == NULL) {
1361         return cli_bad_descriptor;
1362     }
1363     if (!stmt->prepared) {
1364         return cli_not_fetched;
1365     }
1366     if (!stmt->cursor.gotoLast()) {
1367         return cli_not_found;
1368     }
1369     return fetch_columns(stmt);
1370 }
1371 
1372 int cli_remove_current(int statement)
1373 {
1374     return dbCLI::instance.remove_current(statement);
1375 }
1376 
1377 int dbCLI::remove_current(int statement)
1378 {
1379     statement_desc* stmt = statements.get(statement);
1380     if (stmt == NULL) {
1381         return cli_bad_descriptor;
1382     }
1383     if (!stmt->prepared) {
1384         return cli_not_fetched;
1385     }
1386     if (stmt->cursor_type != cli_cursor_for_update) {
1387         return cli_not_update_mode;
1388     }
1389     if (stmt->cursor.isEmpty()) {
1390         return cli_not_found;
1391     }
1392     GB_TRY
1393     stmt->cursor.remove();
1394     GB_CATCH
1395     return cli_ok;
1396 }
1397 
1398 int cli_get_next(int statement)
1399 {
1400     return dbCLI::instance.get_next(statement);
1401 }
1402 
1403 int dbCLI::get_next(int statement)
1404 {
1405     statement_desc* stmt = statements.get(statement);
1406     if (stmt == NULL) {
1407         return cli_bad_descriptor;
1408     }
1409     if (!stmt->prepared) {
1410         return cli_not_fetched;
1411     }
1412     if (!((stmt->first_fetch && stmt->cursor.gotoFirst()) ||
1413           (!stmt->first_fetch && stmt->cursor.moveNext())))
1414     {
1415         return cli_not_found;
1416     }
1417     return fetch_columns(stmt);
1418 }
1419 
1420 int cli_get_prev(int statement)
1421 {
1422     return dbCLI::instance.get_prev(statement);
1423 }
1424 
1425 int dbCLI::get_prev(int statement)
1426 {
1427     statement_desc* stmt = statements.get(statement);
1428     if (stmt == NULL) {
1429         return cli_bad_descriptor;
1430     }
1431     if (!stmt->prepared) {
1432         return cli_not_fetched;
1433     }
1434     if (!((stmt->first_fetch && stmt->cursor.gotoLast()) ||
1435           (!stmt->first_fetch && stmt->cursor.movePrev())))
1436     {
1437         return cli_not_found;
1438     }
1439     return fetch_columns(stmt);
1440 }
1441 
1442 int cli_skip(int statement, int n)
1443 {
1444     return dbCLI::instance.skip(statement, n);
1445 }
1446 
1447 int dbCLI::skip(int statement, int n)
1448 {
1449     statement_desc* stmt = statements.get(statement);
1450     if (stmt == NULL) {
1451         return cli_bad_descriptor;
1452     }
1453     if (!stmt->prepared) {
1454         return cli_not_fetched;
1455     }
1456     if ((n > 0 && !(((stmt->first_fetch && stmt->cursor.gotoFirst() && stmt->cursor.skip(n-1))
1457                      || (!stmt->first_fetch && stmt->cursor.skip(n)))))
1458         || (n < 0 && !(((stmt->first_fetch && stmt->cursor.gotoLast() && stmt->cursor.skip(n+1))
1459                         || (!stmt->first_fetch && stmt->cursor.skip(n))))))
1460     {
1461         return cli_not_found;
1462     }
1463     return fetch_columns(stmt);
1464 }
1465 
1466 int cli_seek(int statement, cli_oid_t oid)
1467 {
1468     return dbCLI::instance.seek(statement, oid);
1469 }
1470 
1471 int dbCLI::seek(int statement, cli_oid_t oid)
1472 {
1473     statement_desc* stmt = statements.get(statement);
1474     if (stmt == NULL) {
1475         return cli_bad_descriptor;
1476     }
1477     if (!stmt->prepared) {
1478         return cli_not_fetched;
1479     }
1480     int pos = stmt->cursor.seek(oid);
1481     if (pos < 0) {
1482         return cli_not_found;
1483     }
1484     int rc = fetch_columns(stmt);
1485     if (rc == cli_ok) {
1486         return pos;
1487     } else {
1488         return rc;
1489     }
1490 }
1491 
1492 cli_oid_t cli_get_oid(int statement)
1493 {
1494     return dbCLI::instance.get_current_oid(statement);
1495 }
1496 
1497 cli_oid_t dbCLI::get_current_oid(int statement)
1498 {
1499     statement_desc* stmt = statements.get(statement);
1500     if (stmt == NULL) {
1501         return (cli_oid_t)cli_bad_descriptor;
1502     }
1503     return stmt->cursor.currId;
1504 }
1505 
1506 
1507 int cli_close_cursor(int statement)
1508 {
1509     return dbCLI::instance.close_cursor(statement);
1510 }
1511 
1512 int dbCLI::close_cursor(int statement)
1513 {
1514     statement_desc* stmt = statements.get(statement);
1515     if (stmt == NULL) {
1516         return cli_bad_descriptor;
1517     }
1518     if (stmt->cursor.db != NULL) {
1519         stmt->cursor.reset();
1520         stmt->cursor.deallocateBitmap();
1521     }
1522     return cli_ok;
1523 }
1524 
1525 int cli_free(int statement)
1526 {
1527     return dbCLI::instance.free_statement(statement);
1528 }
1529 
1530 int dbCLI::free_statement(int statement)
1531 {
1532     statement_desc* stmt = statements.get(statement);
1533     if (stmt == NULL) {
1534         return cli_bad_descriptor;
1535     }
1536     return free_statement(stmt);
1537 }
1538 
1539 int dbCLI::free_statement(statement_desc* stmt)
1540 {
1541     {
1542         dbCriticalSection cs(stmt->session->mutex);
1543         statement_desc *sp, **spp = &stmt->session->stmts;
1544         while ((sp = *spp) != stmt) {
1545             if (sp == NULL) {
1546                 return cli_bad_descriptor;
1547             }
1548             spp = &sp->next;
1549         }
1550         *spp = stmt->next;
1551     }
1552     return release_statement(stmt);
1553 }
1554 
1555 int dbCLI::release_statement(statement_desc* stmt)
1556 {
1557     column_binding *cb, *next_cb;
1558     for (cb = stmt->columns; cb != NULL; cb = next_cb) {
1559         next_cb = cb->next;
1560         delete[] cb->name;
1561         column_allocator.free(cb);
1562     }
1563     parameter_binding *pb, *next_pb;
1564     for (pb = stmt->params; pb != NULL; pb = next_pb) {
1565         next_pb = pb->next;
1566         delete[] pb->name;
1567         parameter_allocator.free(pb);
1568     }
1569     if (stmt->cursor.db != NULL) {
1570         stmt->cursor.reset();
1571         stmt->cursor.deallocateBitmap();
1572     }
1573     statements.free(stmt);
1574     return cli_ok;
1575 }
1576 
1577 
1578 int cli_commit(int session)
1579 {
1580     return dbCLI::instance.commit(session);
1581 }
1582 
1583 int cli_exec_batch(int session)
1584 {
1585     return dbCLI::instance.exec_batch(session);
1586 }
1587 
1588 int dbCLI::exec_batch(int session)
1589 {
1590     session_desc* s = sessions.get(session);
1591     if (s == NULL) {
1592         return cli_bad_descriptor;
1593     }
1594     GB_TRY
1595     s->db->executeBatch();
1596     GB_CATCH
1597     return cli_ok;
1598 }
1599 
1600 int dbCLI::commit(int session)
1601 {
1602     session_desc* s = sessions.get(session);
1603     if (s == NULL) {
1604         return cli_bad_descriptor;
1605     }
1606     while (s->dropped_tables != NULL) {
1607         dbTableDescriptor* next = s->dropped_tables->nextDbTable;
1608         delete s->dropped_tables;
1609         s->dropped_tables = next;
1610     }
1611     GB_TRY
1612     s->db->commit();
1613     GB_CATCH
1614     s->existed_tables = NULL;
1615     return cli_ok;
1616 }
1617 
1618 int cli_precommit(int session)
1619 {
1620     return dbCLI::instance.precommit(session);
1621 }
1622 
1623 int dbCLI::precommit(int session)
1624 {
1625     session_desc* s = sessions.get(session);
1626     if (s == NULL) {
1627         return cli_bad_descriptor;
1628     }
1629     GB_TRY
1630     s->db->precommit();
1631     GB_CATCH
1632     return cli_ok;
1633 }
1634 
1635 int cli_abort(int session)
1636 {
1637     return dbCLI::instance.abort(session);
1638 }
1639 
1640 int dbCLI::abort(int session)
1641 {
1642     session_desc* s = sessions.get(session);
1643     if (s == NULL) {
1644         return cli_bad_descriptor;
1645     }
1646     dbDatabase* db = s->db;
1647     while (s->dropped_tables != NULL) {
1648         dbTableDescriptor* next = s->dropped_tables->nextDbTable;
1649         db->linkTable(s->dropped_tables, s->dropped_tables->tableId);
1650         s->dropped_tables = next;
1651     }
1652     if (s->existed_tables != NULL) {
1653         while (db->tables != s->existed_tables) {
1654             dbTableDescriptor* table = db->tables;
1655             db->unlinkTable(table);
1656             delete table;
1657         }
1658         s->existed_tables = NULL;
1659     }
1660     GB_TRY
1661     s->db->rollback();
1662     GB_CATCH
1663     return cli_ok;
1664 }
1665 
1666 
1667 int cli_remove(int statement)
1668 {
1669     return dbCLI::instance.remove(statement);
1670 }
1671 
1672 int dbCLI::remove(int statement)
1673 {
1674     statement_desc* stmt = statements.get(statement);
1675     if (stmt == NULL || !stmt->prepared) {
1676         return cli_bad_descriptor;
1677     }
1678     if (stmt->cursor_type != cli_cursor_for_update) {
1679         return cli_not_update_mode;
1680     }
1681     if (stmt->cursor.isEmpty()) {
1682         return cli_not_found;
1683     }
1684     GB_TRY
1685     stmt->cursor.removeAllSelected();
1686     GB_CATCH
1687     return cli_ok;
1688 }
1689 
1690 int cli_describe(int session, char_t const* table, cli_field_descriptor** fields)
1691 {
1692     return dbCLI::instance.describe(session, table, fields);
1693 }
1694 
1695 int dbCLI::describe(int session, char_t const* table, cli_field_descriptor** fields)
1696 {
1697     session_desc* s = sessions.get(session);
1698     if (s == NULL) {
1699         return cli_bad_descriptor;
1700     }
1701     dbDatabase* db = s->db;
1702     dbTableDescriptor* desc = db->findTableByName(table);
1703     if (desc == NULL) {
1704         return cli_table_not_found;
1705     } else {
1706         int nColumns = (int)desc->nColumns;
1707         cli_field_descriptor* fp =
1708             (cli_field_descriptor*)malloc(nColumns*sizeof(cli_field_descriptor));
1709         dbFieldDescriptor* fd = desc->columns;
1710         *fields = fp;
1711         for (int i = 0; i < nColumns; i++, fp++) {
1712             fp->type = (cli_var_type)map_type(fd);
1713             fp->name = fd->name;
1714             fp->refTableName =  (fd->type == dbField::tpArray) ? fd->components->refTableName : fd->refTableName;
1715             fp->inverseRefFieldName = fd->inverseRefName;
1716             fp->flags = fd->indexType;
1717             if (fd->bTree != 0) {
1718                 fp->flags |= cli_indexed;
1719                 if (fp->type != cli_rectangle) {
1720                     dbGetTie tie;
1721                     dbBtree* tree = (dbBtree*)db->getRow(tie, fd->bTree);
1722                     if (tree->isCaseInsensitive()) {
1723                         fp->flags |= cli_case_insensitive;
1724                     }
1725                     if (tree->isThick()) {
1726                         fp->flags |= cli_optimize_duplicates;
1727                     }
1728                     if (tree->isUnique()) {
1729                         fp->flags |= cli_unique;
1730                     }
1731                 }
1732             }
1733             if (fd->hashTable != 0) {
1734                 fp->flags |= cli_hashed;
1735             }
1736             fd = fd->next;
1737         }
1738         return nColumns;
1739     }
1740 }
1741 
1742 
1743 int cli_describe_layout(int session, char_t const* table, cli_field_layout** fields, int* rec_size)
1744 {
1745     return dbCLI::instance.describe_layout(session, table, fields, rec_size);
1746 }
1747 
1748 int dbCLI::describe_layout(int session, char_t const* table, cli_field_layout** fields, int* rec_size)
1749 {
1750     session_desc* s = sessions.get(session);
1751     if (s == NULL) {
1752         return cli_bad_descriptor;
1753     }
1754     dbDatabase* db = s->db;
1755     dbTableDescriptor* desc = db->findTableByName(table);
1756     if (desc == NULL) {
1757         return cli_table_not_found;
1758     } else {
1759         int nColumns = (int)desc->nColumns;
1760         cli_field_layout* fp =
1761             (cli_field_layout*)malloc(nColumns*sizeof(cli_field_layout));
1762         dbFieldDescriptor* fd = desc->columns;
1763         *fields = fp;
1764         *rec_size = (int)desc->appSize;
1765         for (int i = 0; i < nColumns; i++, fp++) {
1766             fp->desc.type = (cli_var_type)map_type(fd);
1767             fp->desc.name = fd->name;
1768             fp->desc.refTableName =  (fd->type == dbField::tpArray) ? fd->components->refTableName : fd->refTableName;
1769             fp->desc.inverseRefFieldName = fd->inverseRefName;
1770             fp->desc.flags = fd->indexType;
1771             if (fd->bTree != 0) {
1772                 fp->desc.flags |= cli_indexed;
1773                 if (fp->desc.type != cli_rectangle) {
1774                     dbGetTie tie;
1775                     dbBtree* tree = (dbBtree*)db->getRow(tie, fd->bTree);
1776                     if (tree->isCaseInsensitive()) {
1777                         fp->desc.flags |= cli_case_insensitive;
1778                     }
1779                     if (tree->isThick()) {
1780                         fp->desc.flags |= cli_optimize_duplicates;
1781                     }
1782                     if (tree->isUnique()) {
1783                         fp->desc.flags |= cli_unique;
1784                     }
1785                 }
1786             }
1787             if (fd->hashTable != 0) {
1788                 fp->desc.flags |= cli_hashed;
1789             }
1790             fp->offs = fd->appOffs;
1791             fp->size = (int)fd->appSize;
1792             fd = fd->next;
1793         }
1794         return nColumns;
1795     }
1796 }
1797 
1798 
1799 int cli_show_tables(int session, cli_table_descriptor** tables)
1800 {
1801     return dbCLI::instance.show_tables(session, tables);
1802 }
1803 
1804 int dbCLI::show_tables(int session, cli_table_descriptor** tables)
1805 {
1806     session_desc* s = sessions.get(session);
1807     if (s == NULL) {
1808         return cli_bad_descriptor;
1809     }
1810     dbTableDescriptor* desc;
1811     int nTables = 0;
1812     for (desc = s->db->tables; desc != NULL; desc = desc->nextDbTable) {
1813         if (STRCMP(desc->name, STRLITERAL("Metatable"))) {
1814             nTables += 1;
1815         }
1816     }
1817     if (nTables != 0) {
1818         cli_table_descriptor* td = (cli_table_descriptor*)malloc(nTables*sizeof(cli_table_descriptor));
1819         *tables = td;
1820         for (desc = s->db->tables; desc != NULL; desc = desc->nextDbTable) {
1821             if (STRCMP(desc->name, STRLITERAL("Metatable"))) {
1822                 td->name = desc->name;
1823                 td += 1;
1824             }
1825         }
1826     } else {
1827         *tables = NULL;
1828     }
1829     return nTables;
1830 }
1831 
1832 int cli_get_wrapping_rectangle(int session, char_t const* table, char_t const* field, cli_rectangle_t* rect)
1833 {
1834     return dbCLI::instance.get_wrapping_rectangle(session, table, field, rect);
1835 }
1836 
1837 int dbCLI::get_wrapping_rectangle(int session, char_t const* table, char_t const* field, cli_rectangle_t* rect)
1838 {
1839     session_desc* s = sessions.get(session);
1840     if (s == NULL) {
1841         return cli_bad_descriptor;
1842     }
1843     dbDatabase* db = s->db;
1844     dbTableDescriptor* desc = db->findTableByName(table);
1845     if (desc == NULL) {
1846         return cli_table_not_found;
1847     } else {
1848         dbFieldDescriptor* fd = desc->find(field);
1849         if (fd == NULL || fd->type != dbField::tpRectangle || fd->bTree == 0) {
1850             return cli_column_not_found;
1851         }
1852         dbRtree::cover(db, fd->bTree, *(rectangle*)rect);
1853         return cli_ok;
1854     }
1855 }
1856 
1857 #define MAX_QUERY_IDENTIFIER_LENGTH 256
1858 
1859 int sql_scanner::get()
1860 {
1861     char_t buf[MAX_QUERY_IDENTIFIER_LENGTH];
1862     int i = 0, ch;
1863 
1864     do {
1865         ch = *p++;
1866         if (ch == '\0') {
1867             return tkn_eof;
1868         }
1869     } while (ch > 0 && ch <= 32);
1870 
1871     if (ch == '*') {
1872         return tkn_all;
1873     } else if ((ch >= '0' && ch <= '9') || ch == '+' || ch == '-') {
1874         int const_type = tkn_iconst;
1875         while (true) {
1876             ch = *p++;
1877             if (ch == '.' || ch == 'e' || ch == 'E') {
1878                 const_type = tkn_fconst;
1879             } else if (!((ch >= '0' && ch <= '9') || ch == '+' || ch == '-')) {
1880                 break;
1881             }
1882         }
1883         return const_type;
1884     } else if (ISALNUM(ch) || ch == '$' || ch == '_') {
1885         do {
1886             buf[i++] = ch;
1887             if (i == MAX_QUERY_IDENTIFIER_LENGTH) {
1888                 // Identifier too long
1889                 return tkn_error;
1890             }
1891             ch = *p++;
1892         } while (ch != T_EOF && (ISALNUM(ch) || ch == '$' || ch == '_'));
1893         p -= 1;
1894         buf[i] = '\0';
1895         ident = buf;
1896         return dbSymbolTable::add(ident, tkn_ident);
1897     } else {
1898         // Invalid symbol
1899         return tkn_error;
1900     }
1901 }
1902 
1903 
1904 int cli_create_table(int session, char_t const* tableName, int nColumns,
1905                      cli_field_descriptor* columns)
1906 {
1907     return dbCLI::instance.create_table(session, tableName, nColumns, columns);
1908 }
1909 
1910 int cli_alter_table(int session, char_t const* tableName, int nColumns,
1911                     cli_field_descriptor* columns)
1912 {
1913     return dbCLI::instance.alter_table(session, tableName, nColumns, columns);
1914 }
1915 
1916 
1917 int dbCLI::create_table(int session, char_t const* tableName, int nColumns,
1918                         cli_field_descriptor* columns)
1919 {
1920     session_desc* s = sessions.get(session);
1921     if (s == NULL) {
1922         return cli_bad_descriptor;
1923     }
1924     GB_TRY
1925     s->db->beginTransaction(dbUpdateLock);
1926     if (s->existed_tables == NULL) {
1927         s->existed_tables = s->db->tables;
1928     }
1929     return create_table(s->db, tableName, nColumns, columns);
1930     GB_CATCH
1931 }
1932 
1933 int dbCLI::alter_table(int session, char_t const* tableName, int nColumns,
1934                         cli_field_descriptor* columns)
1935 {
1936     session_desc* s = sessions.get(session);
1937     if (s == NULL) {
1938         return cli_bad_descriptor;
1939     }
1940     GB_TRY
1941     s->db->beginTransaction(dbUpdateLock);
1942     return alter_table(s->db, tableName, nColumns, columns);
1943     GB_CATCH
1944 }
1945 
1946 int dbCLI::calculate_varying_length(char_t const* tableName, int& nFields, cli_field_descriptor* columns)
1947 {
1948     size_t varyingLength = (STRLEN(tableName) + 1)*sizeof(char_t);
1949     for (int i = 0, n = nFields; i < n; i++) {
1950         int type = columns[i].type;
1951         varyingLength += (STRLEN(columns[i].name) + 3)*sizeof(char_t);
1952         if (type == cli_oid || type == cli_array_of_oid) {
1953             varyingLength += STRLEN(columns[i].refTableName)*sizeof(char_t);
1954             if (columns[i].inverseRefFieldName != NULL) {
1955                 varyingLength += STRLEN(columns[i].inverseRefFieldName)*sizeof(char_t);
1956             }
1957         }
1958         switch (type) {
1959           case cli_oid:
1960           case cli_bool:
1961           case cli_int1:
1962           case cli_int2:
1963           case cli_int4:
1964           case cli_autoincrement:
1965           case cli_int8:
1966           case cli_real4:
1967           case cli_real8:
1968           case cli_asciiz:
1969           case cli_pasciiz:
1970           case cli_datetime:
1971           case cli_rectangle:
1972             break;
1973           case cli_array_of_oid:
1974           case cli_array_of_bool:
1975           case cli_array_of_int1:
1976           case cli_array_of_int2:
1977           case cli_array_of_int4:
1978           case cli_array_of_int8:
1979           case cli_array_of_real4:
1980           case cli_array_of_real8:
1981           case cli_array_of_string:
1982             varyingLength += (STRLEN(columns[i].name) + 2 + 3)*sizeof(char_t);
1983             nFields += 1;
1984             break;
1985           case cli_decimal:
1986           case cli_cstring:
1987           case cli_array_of_decimal:
1988           case cli_any:
1989           case cli_unknown:
1990             return cli_unsupported_type;
1991         }
1992     }
1993     return (int)varyingLength;
1994 }
1995 
1996 dbTableDescriptor* dbCLI::create_table_descriptor(dbDatabase*           db,
1997                                                   dbTable*              table,
1998                                                   char_t const*         tableName,
1999                                                   int                   nFields,
2000                                                   int                   nColumns,
2001                                                   cli_field_descriptor* columns)
2002 {
2003     int offs = sizeof(dbTable) + sizeof(dbField)*nFields;
2004     table->name.offs = offs;
2005     table->name.size = (nat4)STRLEN(tableName)+1;
2006     STRCPY((char_t*)((byte*)table + offs), tableName);
2007     offs += table->name.size*sizeof(char_t);
2008     size_t size = sizeof(dbRecord);
2009     table->fields.offs = sizeof(dbTable);
2010     dbField* field = (dbField*)((char*)table + table->fields.offs);
2011     offs -= sizeof(dbTable);
2012 
2013     for (int i = 0; i < nColumns; i++, field += 1, offs -= sizeof(dbField)) {
2014         field->name.offs = offs;
2015         field->name.size = (nat4)STRLEN(columns[i].name) + 1;
2016         STRCPY((char_t*)((char*)field + offs), columns[i].name);
2017         offs += field->name.size*sizeof(char_t);
2018         field->tableName.offs = offs;
2019         int type = columns[i].type;
2020 
2021         if (type == cli_oid || type == cli_array_of_oid) {
2022             if (type == cli_oid) {
2023                 field->tableName.size = (nat4)STRLEN(columns[i].refTableName) + 1;
2024                 STRCPY((char_t*)((byte*)field + offs), columns[i].refTableName);
2025                 offs += field->tableName.size*sizeof(char_t);
2026             } else {
2027                 field->tableName.size = 1;
2028                 *(char_t*)((char*)field + offs) = '\0';
2029                 offs += sizeof(char_t);
2030             }
2031             field->inverse.offs = offs;
2032             if (columns[i].inverseRefFieldName != NULL) {
2033                 field->inverse.size = (nat4)STRLEN(columns[i].inverseRefFieldName) + 1;
2034                 STRCPY((char_t*)((byte*)field + offs), columns[i].inverseRefFieldName);
2035                 offs += field->inverse.size*sizeof(char_t);
2036             } else {
2037                 field->inverse.size = 1;
2038                 *(char_t*)((char*)field + offs) = '\0';
2039                 offs += sizeof(char_t);
2040             }
2041         } else {
2042             field->tableName.size = 1;
2043             *(char_t*)((char*)field + offs) = '\0';
2044             offs += sizeof(char_t);
2045             field->inverse.size = 1;
2046             field->inverse.offs = offs;
2047             *(char_t*)((char*)field + offs) = '\0';
2048             offs += sizeof(char_t);
2049         }
2050         field->bTree = field->hashTable = 0;
2051         field->flags = columns[i].flags;
2052 
2053         switch (type) {
2054           case cli_oid:
2055             field->type = dbField::tpReference;
2056             field->size = sizeof(oid_t);
2057             break;
2058           case cli_bool:
2059             field->type = dbField::tpBool;
2060             field->size = sizeof(bool);
2061             break;
2062           case cli_int1:
2063             field->type = dbField::tpInt1;
2064             field->size = sizeof(int1);
2065             break;
2066           case cli_int2:
2067             field->type = dbField::tpInt2;
2068             field->size = sizeof(int2);
2069             break;
2070           case cli_autoincrement:
2071             field->flags |= AUTOINCREMENT;
2072             // no break
2073           case cli_int4:
2074             field->type = dbField::tpInt4;
2075             field->size = sizeof(int4);
2076             break;
2077           case cli_int8:
2078             field->type = dbField::tpInt8;
2079             field->size = sizeof(db_int8);
2080             break;
2081           case cli_real4:
2082             field->type = dbField::tpReal4;
2083             field->size = sizeof(real4);
2084             break;
2085           case cli_real8:
2086             field->type = dbField::tpReal8;
2087             field->size = sizeof(real8);
2088             break;
2089           case cli_datetime:
2090             field->type = (sizeof(cli_time_t) == 4) ? dbField::tpInt4 : dbField::tpInt8;
2091             field->size = sizeof(cli_time_t);
2092             field->flags |= DB_TIMESTAMP;
2093             break;
2094           case cli_rectangle:
2095             field->type = dbField::tpRectangle;
2096             field->size = sizeof(cli_rectangle_t);
2097             if (db != NULL && (columns[i].flags & (cli_hashed|cli_indexed))) {
2098                 field->bTree = dbRtree::allocate(db);
2099             }
2100             field->offset = (int4)DOALIGN(size, sizeof(cli_coord_t));
2101             size = field->offset + sizeof(cli_rectangle_t);
2102             continue;
2103           case cli_asciiz:
2104           case cli_pasciiz:
2105           case cli_cstring:
2106             field->type = dbField::tpString;
2107             field->size = sizeof(dbVarying);
2108             field->offset = (int4)DOALIGN(size, sizeof(int4));
2109             size = field->offset + sizeof(dbVarying);
2110             if (columns[i].flags & (cli_hashed|cli_indexed)) {
2111                 int flags = 0;
2112                 if (columns[i].flags & cli_case_insensitive) {
2113                     flags |= dbBtree::FLAGS_CASE_INSENSITIVE;
2114                 }
2115                 if (columns[i].flags & cli_optimize_duplicates) {
2116                     flags |= dbBtree::FLAGS_THICK;
2117                 }
2118                 if (columns[i].flags & cli_unique) {
2119                     flags |= dbBtree::FLAGS_UNIQUE;
2120                 }
2121                 if (db != NULL) {
2122                     field->bTree = dbBtree::allocate(db, dbField::tpString, 0, flags);
2123                 }
2124             }
2125             continue;
2126           case cli_array_of_oid:
2127           case cli_array_of_bool:
2128           case cli_array_of_int1:
2129           case cli_array_of_int2:
2130           case cli_array_of_int4:
2131           case cli_array_of_int8:
2132           case cli_array_of_real4:
2133           case cli_array_of_real8:
2134           case cli_array_of_string:
2135             field->type = dbField::tpArray;
2136             field->size = sizeof(dbVarying);
2137             field->offset = (int4)DOALIGN(size, sizeof(int4));
2138             size = field->offset + sizeof(dbVarying);
2139             field += 1;
2140             offs -= sizeof(dbField);
2141             field->name.offs = offs;
2142             field->name.size = (nat4)STRLEN(columns[i].name) + 3;
2143             SPRINTF(SPRINTF_BUFFER((char_t*)((char*)field + offs)), STRLITERAL("%s[]"), columns[i].name);
2144             offs += field->name.size*sizeof(char_t);
2145             field->tableName.offs = offs;
2146             if (type == cli_array_of_oid) {
2147                 field->tableName.size = (nat4)STRLEN(columns[i].refTableName) + 1;
2148                 STRCPY((char_t*)((char*)field + offs), columns[i].refTableName);
2149                 offs += field->tableName.size*sizeof(char_t);
2150             } else {
2151                 field->tableName.size = 1;
2152                 *(char_t*)((char*)field + offs) = '\0';
2153                 offs += sizeof(char_t);
2154             }
2155             field->inverse.offs = offs;
2156             field->inverse.size = 1;
2157             *(char_t*)((char*)field + offs) = '\0';
2158             offs += sizeof(char_t);
2159             field->offset = 0;
2160             field->hashTable = field->bTree = 0;
2161             switch (type) {
2162               case cli_array_of_oid:
2163                 field->type = dbField::tpReference;
2164                 field->size = sizeof(oid_t);
2165                 break;
2166               case cli_array_of_bool:
2167                 field->type = dbField::tpBool;
2168                 field->size = sizeof(bool);
2169                 break;
2170               case cli_array_of_int1:
2171                 field->type = dbField::tpInt1;
2172                 field->size = sizeof(int1);
2173                 break;
2174               case cli_array_of_int2:
2175                 field->type = dbField::tpInt2;
2176                 field->size = sizeof(int2);
2177                 break;
2178               case cli_array_of_int4:
2179                 field->type = dbField::tpInt4;
2180                 field->size = sizeof(int4);
2181                 break;
2182               case cli_array_of_int8:
2183                 field->type = dbField::tpInt8;
2184                 field->size = sizeof(db_int8);
2185                 break;
2186               case cli_array_of_real4:
2187                 field->type = dbField::tpReal4;
2188                 field->size = sizeof(real4);
2189                 break;
2190               case cli_array_of_real8:
2191                 field->type = dbField::tpReal8;
2192                 field->size = sizeof(real8);
2193                 break;
2194               case cli_array_of_string:
2195                 field->type = dbField::tpString;
2196                 field->size = sizeof(dbVarying);
2197                 break;
2198             }
2199             continue;
2200           default:
2201             return NULL;
2202         }
2203         if (columns[i].flags & (cli_hashed|cli_indexed)) {
2204             int flags = 0;
2205             if (columns[i].flags & cli_case_insensitive) {
2206                 flags |= dbBtree::FLAGS_CASE_INSENSITIVE;
2207             }
2208             if (columns[i].flags & cli_optimize_duplicates) {
2209                 flags |= dbBtree::FLAGS_THICK;
2210             }
2211             if (columns[i].flags & cli_unique) {
2212                 flags |= dbBtree::FLAGS_UNIQUE;
2213             }
2214             if (db != NULL) {
2215                 field->bTree = dbBtree::allocate(db, field->type, field->size, flags);
2216             }
2217         }
2218         field->offset = (int4)DOALIGN(size, field->size);
2219         size = field->offset + field->size;
2220     }
2221     table->fields.size = nFields;
2222     table->fixedSize = (nat4)size;
2223     table->nRows = 0;
2224     table->nColumns = nColumns;
2225     table->firstRow = 0;
2226     table->lastRow = 0;
2227 
2228     return new dbTableDescriptor(table);
2229 }
2230 
2231 int dbCLI::create_table(dbDatabase* db, char_t const* tableName, int nColumns,
2232                         cli_field_descriptor* columns)
2233 {
2234     db->modified = true;
2235     if (db->findTableByName(tableName) != NULL) {
2236         return cli_table_already_exists;
2237     }
2238     int nFields = nColumns;
2239     int varyingLength = calculate_varying_length(tableName, nFields, columns);
2240 
2241     db->beginTransaction(dbExclusiveLock);
2242     oid_t oid = db->allocateRow(dbMetaTableId,
2243                                 sizeof(dbTable) + sizeof(dbField)*nFields + varyingLength);
2244     dbPutTie tie;
2245     dbTable* table = (dbTable*)db->putRow(tie, oid);
2246 
2247     dbTableDescriptor* desc = create_table_descriptor(db, table, tableName, nFields, nColumns, columns);
2248     if (desc == NULL) {
2249         return cli_unsupported_type;
2250     }
2251     db->linkTable(desc, oid);
2252     if (!db->completeDescriptorsInitialization()) {
2253         return cli_table_not_found;
2254     }
2255     return cli_ok;
2256 }
2257 
2258 
2259 int dbCLI::alter_table(dbDatabase* db, char_t const* tableName, int nColumns,
2260                        cli_field_descriptor* columns)
2261 {
2262     dbTableDescriptor* oldDesc = db->findTableByName(tableName);
2263     if (oldDesc == NULL) {
2264         return cli_table_not_found;
2265     }
2266     int nFields = nColumns;
2267     int varyingLength = calculate_varying_length(tableName, nFields, columns);
2268 
2269     dbTable* newTable = (dbTable*)new char[sizeof(dbTable) + sizeof(dbField)*nFields + varyingLength];
2270     dbTableDescriptor* newDesc = create_table_descriptor(NULL, newTable, tableName, nFields, nColumns, columns);
2271     delete[] (char*)newTable;
2272     if (newDesc == NULL) {
2273         return cli_unsupported_type;
2274     }
2275     GB_TRY
2276     db->beginTransaction(dbExclusiveLock);
2277     dbGetTie tie;
2278     oid_t tableId = oldDesc->tableId;
2279     dbTable* oldTable = (dbTable*)db->getRow(tie, tableId);
2280     if (!newDesc->equal(oldTable)) {
2281         bool confirmDeleteColumns = db->confirmDeleteColumns;
2282         db->confirmDeleteColumns = true;
2283         db->modified = true;
2284         db->schemeVersion += 1;
2285         db->unlinkTable(oldDesc);
2286         if (oldTable->nRows == 0) {
2287             db->updateTableDescriptor(newDesc, tableId, oldTable);
2288         } else {
2289             db->reformatTable(tableId, newDesc);
2290         }
2291         delete oldDesc;
2292         db->confirmDeleteColumns = confirmDeleteColumns;
2293         db->addIndices(newDesc);
2294         if (!db->completeDescriptorsInitialization()) {
2295             return cli_table_not_found;
2296         }
2297     } else {
2298         delete newDesc;
2299     }
2300     GB_CATCH
2301     return cli_ok;
2302 }
2303 
2304 int cli_drop_table(int session, char_t const* tableName)
2305 {
2306     return dbCLI::instance.drop_table(session, tableName);
2307 }
2308 
2309 
2310 int dbCLI::drop_table(int session, char_t const* tableName)
2311 {
2312     session_desc* s = sessions.get(session);
2313     if (s == NULL) {
2314         return cli_bad_descriptor;
2315     }
2316     dbDatabase* db = s->db;
2317     GB_TRY
2318     db->beginTransaction(dbUpdateLock);
2319     dbTableDescriptor* desc = db->findTableByName(tableName);
2320     if (desc == NULL) {
2321         return cli_table_not_found;
2322     }
2323     db->dropTable(desc);
2324     if (desc == s->existed_tables) {
2325         s->existed_tables = desc->nextDbTable;
2326     }
2327     db->unlinkTable(desc);
2328     desc->nextDbTable = s->dropped_tables;
2329     s->dropped_tables = desc;
2330     GB_CATCH
2331     return cli_ok;
2332 }
2333 
2334 int cli_alter_index(int session, char_t const* tableName, char_t const* fieldName, int newFlags)
2335 {
2336     return dbCLI::instance.alter_index(session, tableName, fieldName, newFlags);
2337 }
2338 
2339 int dbCLI::alter_index(int session, char_t const* tableName, char_t const* fieldName, int newFlags)
2340 {
2341     session_desc* s = sessions.get(session);
2342     if (s == NULL) {
2343         return cli_bad_descriptor;
2344     }
2345     return alter_index(s->db, tableName, fieldName, newFlags);
2346 }
2347 
2348 int dbCLI::alter_index(dbDatabase* db, char_t const* tableName, char_t const* fieldName, int newFlags)
2349 {
2350     GB_TRY
2351     db->beginTransaction(dbUpdateLock);
2352     dbTableDescriptor* desc = db->findTableByName(tableName);
2353     if (desc == NULL) {
2354         return cli_table_not_found;
2355     }
2356     dbFieldDescriptor* fd = desc->find(fieldName);
2357     if (fd == NULL) {
2358         return cli_column_not_found;
2359     }
2360     if (fd->bTree != 0 && (newFlags & (cli_indexed|cli_hashed)) == 0) {
2361         db->dropIndex(fd);
2362         fd->indexType &= ~(INDEXED|OPTIMIZE_DUPLICATES|CASE_INSENSITIVE|UNIQUE);
2363     } else if (fd->bTree == 0 && (newFlags & (cli_indexed|cli_hashed)) != 0) {
2364         fd->indexType |= newFlags & (cli_indexed|cli_hashed|cli_unique|cli_optimize_duplicates|cli_case_insensitive);
2365         db->createIndex(fd);
2366     }
2367     GB_CATCH
2368     return cli_ok;
2369 }
2370 
2371 cli_error_handler cli_set_error_handler(int session, cli_error_handler new_handler, void* context)
2372 {
2373     return dbCLI::instance.set_error_handler(session, new_handler, context);
2374 }
2375 
2376 cli_error_handler dbCLI::set_error_handler(int session, cli_error_handler new_handler, void* context)
2377 {
2378     session_desc* s = sessions.get(session);
2379     if (s == NULL) {
2380         return NULL;
2381     }
2382     return (cli_error_handler)s->db->setErrorHandler(dbDatabase::dbErrorHandler(new_handler), context);
2383 }
2384 
2385 
2386 
2387 int cli_attach(int session)
2388 {
2389     return dbCLI::instance.attach(session);
2390 }
2391 
2392 int dbCLI::attach(int session)
2393 {
2394     session_desc* s = sessions.get(session);
2395     if (s == NULL) {
2396         return cli_bad_descriptor;
2397     }
2398     GB_TRY
2399     s->db->attach();
2400     GB_CATCH
2401     return cli_ok;
2402 }
2403 
2404 int cli_detach(int session, int detach_mode)
2405 {
2406     return dbCLI::instance.detach(session, detach_mode);
2407 }
2408 
2409 int dbCLI::detach(int session, int detach_mode)
2410 {
2411     session_desc* s = sessions.get(session);
2412     if (s == NULL) {
2413         return cli_bad_descriptor;
2414     }
2415     GB_TRY
2416     s->db->detach(detach_mode);
2417     GB_CATCH
2418     return cli_ok;
2419 }
2420 
2421 void cli_free_memory(int, void* ptr)
2422 {
2423     free(ptr);
2424 }
2425 
2426 void cli_set_trace_function(cli_trace_function_t func)
2427 {
2428     dbTraceFunction = func;
2429 }
2430 
2431 
2432 int cli_lock(int session)
2433 {
2434     return dbCLI::instance.lock(session);
2435 }
2436 
2437 int dbCLI::lock(int session)
2438 {
2439     session_desc* s = sessions.get(session);
2440     if (s == NULL) {
2441         return cli_bad_descriptor;
2442     }
2443     GB_TRY
2444     s->db->lock();
2445     GB_CATCH
2446     return cli_ok;
2447 }
2448 
2449 int cli_prepare_query(int session, char_t const* query)
2450 {
2451     return dbCLI::instance.prepare_query(session, query);
2452 }
2453 
2454 int dbCLI::prepare_query(int session, char_t const* query)
2455 {
2456     char_t *p, *q;
2457     int tkn;
2458     session_desc* s = sessions.get(session);
2459     if (s == NULL) {
2460         return cli_bad_descriptor;
2461     }
2462     statement_desc* stmt = statements.allocate();
2463     stmt->columns = NULL;
2464     stmt->params = NULL;
2465     stmt->session = s;
2466     stmt->cursor_type = cli_cursor_view_only;
2467     stmt->first_fetch = true;
2468     stmt->prepared = false;
2469     stmt->n_params = 0;
2470     stmt->n_columns = 0;
2471     stmt->n_autoincremented_columns = 0;
2472     stmt->oid = 0;
2473     stmt->updated = false;
2474     stmt->query.reset();
2475 
2476     stmt->sql.put(STRLEN(query)+1);
2477     p = stmt->sql.base();
2478     STRCPY(p, query);
2479 
2480     sql_scanner scanner(p);
2481     if (scanner.get() != tkn_select) {
2482         statements.free(stmt);
2483         return cli_bad_statement;
2484     }
2485     if ((tkn = scanner.get()) == tkn_all) {
2486         tkn = scanner.get();
2487     }
2488     if (tkn != tkn_from || scanner.get() != tkn_ident) {
2489         statements.free(stmt);
2490         return cli_bad_statement;
2491     }
2492     stmt->table = s->db->findTable(scanner.identifier());
2493     if (stmt->table == NULL) {
2494         statements.free(stmt);
2495         return cli_table_not_found;
2496     }
2497 
2498     p = scanner.current_position();
2499     q = p;
2500     size_t offs = 0;
2501 
2502     while (*p != '\0') {
2503         if (*p == '\'') {
2504             do {
2505                 do {
2506                     p += 1;
2507                 } while (*p != '\0' && *p != '\'');
2508                 if (*p == '\0') {
2509                     statements.free(stmt);
2510                     return cli_bad_statement;
2511                 }
2512             } while (*++p == '\'');
2513         } else if (*p == '%') {
2514             if (p != q) {
2515                 *p = '\0';
2516                 stmt->query.append(dbQueryElement::qExpression, q);
2517             }
2518             switch (*++p) {
2519               case 'd':
2520               case 'i':
2521                 stmt->query.append(dbQueryElement::qVarInt4, (void*)offs);
2522                 offs += sizeof(cli_int4_t);
2523                 break;
2524               case 'f':
2525                 offs = DOALIGN(offs, sizeof(cli_real8_t));
2526                 stmt->query.append(dbQueryElement::qVarReal8, (void*)offs);
2527                 offs += sizeof(cli_real8_t);
2528                 break;
2529               case 'p':
2530                 offs = DOALIGN(offs, sizeof(cli_oid_t));
2531                 stmt->query.append(dbQueryElement::qVarReference, (void*)offs);
2532                 offs += sizeof(cli_oid_t);
2533                 break;
2534               case 'l':
2535               case 'L':
2536                 p += 1;
2537                 if (*p != 'd' && *p != 'i') {
2538                     statements.free(stmt);
2539                     return cli_bad_statement;
2540                 }
2541                 offs = DOALIGN(offs, sizeof(cli_int8_t));
2542                 stmt->query.append(dbQueryElement::qVarInt8, (void*)offs);
2543                 offs += sizeof(cli_int8_t);
2544                 break;
2545               case 's':
2546                 offs = DOALIGN(offs, sizeof(char_t*));
2547                 stmt->query.append(dbQueryElement::qVarStringPtr, (void*)offs);
2548                 offs += sizeof(char_t*);
2549                 break;
2550               case 'R':
2551                 offs = DOALIGN(offs, sizeof(cli_coord_t));
2552                 stmt->query.append(dbQueryElement::qVarRectangle, (void*)offs);
2553                 offs += sizeof(cli_rectangle_t);
2554                 break;
2555               case 't':
2556                 stmt->query.append((sizeof(cli_time_t) == 4) ? dbQueryElement::qVarInt4 : dbQueryElement::qVarInt8,
2557                                    (void*)offs);
2558                 offs += sizeof(cli_time_t);
2559                 break;
2560               default:
2561                 statements.free(stmt);
2562                 return cli_bad_statement;
2563             }
2564             p += 1;
2565             q = p;
2566         } else {
2567             p += 1;
2568         }
2569     }
2570     if (p != q) {
2571         stmt->query.append(dbQueryElement::qExpression, q);
2572     }
2573     stmt->param_size = (int)offs;
2574     {
2575         dbCriticalSection cs(s->mutex);
2576         stmt->next = s->stmts;
2577         s->stmts = stmt;
2578     }
2579     stmt->prepared = true;
2580     return stmt->id;
2581 }
2582 
2583 
2584 int cli_execute_query(int statement, int cursor_type, void* record_struct, ...)
2585 {
2586     va_list args;
2587     va_start(args, record_struct);
2588     int rc = dbCLI::instance.execute_query(statement, cursor_type, record_struct, args);
2589     va_end(args);
2590     return rc;
2591 }
2592 
2593 int dbCLI::execute_query(int statement, int cursor_type, void* record_struct, va_list params)
2594 {
2595     statement_desc* stmt = statements.get(statement);
2596     if (stmt == NULL || !stmt->prepared) {
2597         return cli_bad_descriptor;
2598     }
2599     stmt->cursor_type = cursor_type;
2600     stmt->oid = 0;
2601     stmt->first_fetch = true;
2602     dbSmallBuffer<char> paramBuf(stmt->param_size);
2603     char* paramBase = paramBuf.base();
2604     int offs = 0;
2605     dbQueryElement* elem = stmt->query.elements;
2606     while (elem != NULL) {
2607         switch (elem->type) {
2608           case dbQueryElement::qVarInt4:
2609             *(cli_int4_t*)(paramBase + offs) = va_arg(params, cli_int4_t);
2610             offs += sizeof(cli_int4_t);
2611             break;
2612           case dbQueryElement::qVarInt8:
2613             offs = DOALIGN(offs, sizeof(cli_int8_t));
2614             *(cli_int8_t*)(paramBase + offs) = va_arg(params, cli_int8_t);
2615             offs += sizeof(cli_int8_t);
2616             break;
2617           case dbQueryElement::qVarReal8:
2618             offs = DOALIGN(offs, sizeof(cli_real8_t));
2619             *(cli_real8_t*)(paramBase + offs) = va_arg(params, cli_real8_t);
2620             offs += sizeof(cli_real8_t);
2621             break;
2622           case dbQueryElement::qVarStringPtr:
2623             offs = DOALIGN(offs, sizeof(char_t*));
2624             *(char_t**)(paramBase + offs) = va_arg(params, char_t*);
2625             offs += sizeof(char_t*);
2626             break;
2627            case dbQueryElement::qVarReference:
2628             offs = DOALIGN(offs, sizeof(cli_oid_t));
2629             *(cli_oid_t*)(paramBase + offs) = va_arg(params, cli_oid_t);
2630             offs += sizeof(cli_oid_t);
2631             break;
2632            case dbQueryElement::qVarRectangle:
2633             offs = DOALIGN(offs, sizeof(cli_coord_t));
2634             *(cli_rectangle_t*)(paramBase + offs) = *va_arg(params, cli_rectangle_t*);
2635             offs += sizeof(cli_rectangle_t);
2636             break;
2637           case dbQueryElement::qVarArrayOfRef:
2638           case dbQueryElement::qVarArrayOfInt4:
2639           case dbQueryElement::qVarArrayOfInt8:
2640             offs = DOALIGN(offs, sizeof(dbAnyArray*));
2641             *(dbAnyArray**)(paramBase + offs) = va_arg(params, dbAnyArray*);
2642             offs += sizeof(dbAnyArray*);
2643             break;
2644           case dbQueryElement::qVarArrayOfRefPtr:
2645           case dbQueryElement::qVarArrayOfInt4Ptr:
2646           case dbQueryElement::qVarArrayOfInt8Ptr:
2647             offs = DOALIGN(offs, sizeof(dbAnyArray**));
2648             *(dbAnyArray***)(paramBase + offs) = va_arg(params, dbAnyArray**);
2649             offs += sizeof(dbAnyArray**);
2650             break;
2651           default:
2652             break;
2653         }
2654         elem = elem->next;
2655     }
2656     stmt->record_struct = record_struct;
2657     stmt->cursor.setTable(stmt->table);
2658     stmt->cursor.reset();
2659     stmt->cursor.setRecord(record_struct);
2660 #ifdef THROW_EXCEPTION_ON_ERROR
2661     try {
2662 #endif
2663         return stmt->cursor.select(stmt->query, (dbCursorType)cursor_type, paramBase);
2664 #ifdef THROW_EXCEPTION_ON_ERROR
2665     } catch (dbException const& x) {
2666         return (x.getErrCode() == dbDatabase::QueryError)
2667             ? cli_bad_statement : cli_runtime_error;
2668     }
2669 #endif
2670 }
2671 
2672 int cli_execute_query_ex(int statement, int cursor_type, void* record_struct, int n_params, int* param_types, void** param_values)
2673 {
2674     return dbCLI::instance.execute_query(statement, cursor_type, record_struct, n_params, param_types, param_values);
2675 }
2676 
2677 int dbCLI::execute_query(int statement, int cursor_type, void* record_struct, int n_params, int* param_types, void** param_values)
2678 {
2679     statement_desc* stmt = statements.get(statement);
2680     if (stmt == NULL || !stmt->prepared) {
2681         return cli_bad_descriptor;
2682     }
2683     stmt->cursor_type = cursor_type;
2684     stmt->oid = 0;
2685     stmt->first_fetch = true;
2686     dbSmallBuffer<char> paramBuf(stmt->param_size);
2687     int paramIndex = 0;
2688     char* paramBase = paramBuf.base();
2689     int offs = 0;
2690     dbQueryElement* elem = stmt->query.elements;
2691     while (elem != NULL) {
2692         if (elem->type != dbQueryElement::qExpression) {
2693             if (paramIndex >= n_params) {
2694                 return cli_unbound_parameter;
2695             }
2696             void* val = param_values[paramIndex];
2697             cli_var_type type = (cli_var_type)param_types[paramIndex];
2698             paramIndex += 1;
2699             switch (elem->type) {
2700               case dbQueryElement::qVarInt4:
2701                 switch (type) {
2702                   case cli_int1:
2703                     *(cli_int4_t*)(paramBase + offs) = *(cli_int1_t*)val;
2704                     break;
2705                   case cli_int2:
2706                     *(cli_int4_t*)(paramBase + offs) = *(cli_int2_t*)val;
2707                     break;
2708                   case cli_int4:
2709                     *(cli_int4_t*)(paramBase + offs) = *(cli_int4_t*)val;
2710                     break;
2711                   default:
2712                     return cli_incompatible_type;
2713                 }
2714                 offs += sizeof(cli_int4_t);
2715                 break;
2716               case dbQueryElement::qVarInt8:
2717                 offs = DOALIGN(offs, sizeof(cli_int8_t));
2718                 switch (type) {
2719                   case cli_int1:
2720                     *(cli_int8_t*)(paramBase + offs) = *(cli_int1_t*)val;
2721                     break;
2722                   case cli_int2:
2723                     *(cli_int8_t*)(paramBase + offs) = *(cli_int2_t*)val;
2724                     break;
2725                   case cli_int4:
2726                     *(cli_int8_t*)(paramBase + offs) = *(cli_int4_t*)val;
2727                     break;
2728                   case cli_int8:
2729                     *(cli_int8_t*)(paramBase + offs) = *(cli_int8_t*)val;
2730                     break;
2731                   default:
2732                     return cli_incompatible_type;
2733                 }
2734                 offs += sizeof(cli_int8_t);
2735                 break;
2736               case dbQueryElement::qVarReal8:
2737                 offs = DOALIGN(offs, sizeof(cli_real8_t));
2738                 switch (type) {
2739                   case cli_real4:
2740                     *(cli_real8_t*)(paramBase + offs) = *(cli_real4_t*)val;
2741                     break;
2742                   case cli_real8:
2743                     *(cli_real8_t*)(paramBase + offs) = *(cli_real8_t*)val;
2744                     break;
2745                   default:
2746                     return cli_incompatible_type;
2747                 }
2748                 offs += sizeof(cli_real8_t);
2749                 break;
2750               case dbQueryElement::qVarStringPtr:
2751                 offs = DOALIGN(offs, sizeof(char_t*));
2752                 *(char_t**)(paramBase + offs) = *(char_t**)val;
2753                 offs += sizeof(char_t*);
2754                 break;
2755               case dbQueryElement::qVarReference:
2756                 offs = DOALIGN(offs, sizeof(cli_oid_t));
2757                 *(cli_oid_t*)(paramBase + offs) = *(cli_oid_t*)val;
2758                 offs += sizeof(cli_oid_t);
2759                 break;
2760               case dbQueryElement::qVarRectangle:
2761                 offs = DOALIGN(offs, sizeof(cli_coord_t));
2762                 *(cli_rectangle_t*)(paramBase + offs) = *(cli_rectangle_t*)val;
2763                 offs += sizeof(cli_rectangle_t);
2764                 break;
2765               case dbQueryElement::qVarArrayOfRef:
2766               case dbQueryElement::qVarArrayOfInt4:
2767               case dbQueryElement::qVarArrayOfInt8:
2768                 offs = DOALIGN(offs, sizeof(dbAnyArray*));
2769                 *(dbAnyArray**)(paramBase + offs) = (dbAnyArray*)val;
2770                 offs += sizeof(dbAnyArray*);
2771                 break;
2772               case dbQueryElement::qVarArrayOfRefPtr:
2773               case dbQueryElement::qVarArrayOfInt4Ptr:
2774               case dbQueryElement::qVarArrayOfInt8Ptr:
2775                 offs = DOALIGN(offs, sizeof(dbAnyArray**));
2776                 *(dbAnyArray***)(paramBase + offs) = (dbAnyArray**)val;
2777                 offs += sizeof(dbAnyArray**);
2778                 break;
2779               default:
2780                 return cli_incompatible_type;
2781             }
2782         }
2783         elem = elem->next;
2784     }
2785     if (paramIndex != n_params) {
2786         return cli_unbound_parameter;
2787     }
2788     stmt->record_struct = record_struct;
2789     stmt->cursor.setTable(stmt->table);
2790     stmt->cursor.reset();
2791     stmt->cursor.setRecord((byte*)record_struct);
2792 #ifdef THROW_EXCEPTION_ON_ERROR
2793     try {
2794 #endif
2795         return stmt->cursor.select(stmt->query, (dbCursorType)cursor_type, paramBase);
2796 #ifdef THROW_EXCEPTION_ON_ERROR
2797     } catch (dbException const& x) {
2798         return (x.getErrCode() == dbDatabase::QueryError)
2799             ? cli_bad_statement : cli_runtime_error;
2800     }
2801 #endif
2802 }
2803 
2804 int cli_insert_struct(int session, char_t const* table_name, void* record_struct, cli_oid_t* oid)
2805 {
2806     return dbCLI::instance.insert_struct(session, table_name, record_struct, oid);
2807 }
2808 
2809 int dbCLI::insert_struct(int session, char_t const* table_name, void* record_struct, cli_oid_t* oid)
2810 {
2811     session_desc* s = sessions.get(session);
2812     if (s == NULL) {
2813         return cli_bad_descriptor;
2814     }
2815     dbTableDescriptor* table = s->db->findTableByName(table_name);
2816     if (table == NULL) {
2817         return cli_table_not_found;
2818     }
2819     GB_TRY;
2820     dbAnyReference ref;
2821     if (!s->db->insertRecord(table, &ref, record_struct, false)) {
2822         return cli_not_unique;
2823     }
2824     if (oid != NULL) {
2825         *oid = (cli_oid_t)ref.getOid();
2826     }
2827     GB_CATCH
2828     return cli_ok;
2829 }
2830 
2831 
2832 
2833 int cli_get_field_size(cli_field_descriptor* fields, int field_no)
2834 {
2835     return sizeof_type[fields[field_no].type];
2836 }
2837 
2838 
2839 int cli_get_field_offset(cli_field_descriptor* fields, int field_no)
2840 {
2841     int offs = 0;
2842     int size = 0;
2843     for (int i = 0; i <= field_no; i++) {
2844         size = sizeof_type[fields[i].type];
2845         offs = DOALIGN(offs, alignof_type[fields[i].type]);
2846         offs += size;
2847     }
2848     return offs - size;
2849 }
2850 
2851 void cli_clear_connection_pool()
2852 {
2853 }
2854 
2855 cli_transaction_context_t cli_create_transaction_context()
2856 {
2857     return new dbDatabaseThreadContext();
2858 }
2859 
2860 void cli_remove_transaction_context(cli_transaction_context_t ctx)
2861 {
2862     delete (dbDatabaseThreadContext*)ctx;
2863 }
2864 
2865 int cli_join_transaction(int stmt, cli_transaction_context_t ctx)
2866 {
2867     return dbCLI::instance.join_transaction(stmt, ctx);
2868 }
2869 
2870 int dbCLI::join_transaction(int session, cli_transaction_context_t ctx)
2871 {
2872     session_desc* s = sessions.get(session);
2873     if (s == NULL) {
2874         return cli_bad_descriptor;
2875     }
2876     GB_TRY
2877     s->db->attach((dbDatabaseThreadContext*)ctx);
2878     GB_CATCH
2879     return cli_ok;
2880 }
2881 
2882 int cli_xml_export(int session, FILE* out, char_t const* const* tables, int n_tables, cli_export_method method)
2883 {
2884     return dbCLI::instance.xml_export(session, out, tables, n_tables, method);
2885 }
2886 
2887 int cli_xml_import(int session, FILE* in)
2888 {
2889     return dbCLI::instance.xml_import(session, in);
2890 }
2891 
2892 int dbCLI::xml_export(int session, FILE* out, char_t const* const* tables, int n_tables, cli_export_method method)
2893 {
2894     session_desc* s = sessions.get(session);
2895     if (s == NULL) {
2896         return cli_bad_descriptor;
2897     }
2898     GB_TRY
2899     s->db->exportDatabaseToXml(out, tables, n_tables, (dbDatabase::SelectionMethod)method);
2900     GB_CATCH
2901     return cli_ok;
2902 }
2903 
2904 int dbCLI::xml_import(int session, FILE* in)
2905 {
2906     session_desc* s = sessions.get(session);
2907     if (s == NULL) {
2908         return cli_bad_descriptor;
2909     }
2910     GB_TRY
2911     return s->db->importDatabaseFromXml(in) ? cli_ok : cli_xml_parse_error;
2912     GB_CATCH
2913 }
2914 
2915 int cli_backup(int session, char_t const* file_name, int compactify)
2916 {
2917     return dbCLI::instance.backup(session, file_name, compactify);
2918 }
2919 
2920 int cli_schedule_backup(int session, char_t const* file_name, int period)
2921 {
2922     return dbCLI::instance.schedule_backup(session, file_name, period);
2923 }
2924 
2925 int dbCLI::backup(int session, char_t const* file_name, int compactify)
2926 {
2927     session_desc* s = sessions.get(session);
2928     if (s == NULL) {
2929         return cli_bad_descriptor;
2930     }
2931     GB_TRY
2932     return s->db->backup(file_name, compactify != 0) ? cli_ok : cli_backup_failed;
2933     GB_CATCH
2934 }
2935 
2936 int dbCLI::schedule_backup(int session, char_t const* file_name, int period)
2937 {
2938     session_desc* s = sessions.get(session);
2939     if (s == NULL) {
2940         return cli_bad_descriptor;
2941     }
2942     GB_TRY
2943     s->db->scheduleBackup(file_name, period);
2944     GB_CATCH
2945     return cli_ok;
2946 }
2947 
2948 int cli_get_database_size(int session, cli_nat8_t* size)
2949 {
2950     return dbCLI::instance.get_database_size(session, size);
2951 }
2952 
2953 int dbCLI::get_database_size(int session, cli_nat8_t* size)
2954 {
2955     session_desc* s = sessions.get(session);
2956     if (s == NULL) {
2957         return cli_bad_descriptor;
2958     }
2959     *size = s->db->getDatabaseSize();
2960     return cli_ok;
2961 }
2962 
2963