1 /*===========================================================================
2 *
3 *                            PUBLIC DOMAIN NOTICE
4 *               National Center for Biotechnology Information
5 *
6 *  This software/database is a "United States Government Work" under the
7 *  terms of the United States Copyright Act.  It was written as part of
8 *  the author's official duties as a United States Government employee and
9 *  thus cannot be copyrighted.  This software/database is freely available
10 *  to the public for use. The National Library of Medicine and the U.S.
11 *  Government have not placed any restriction on its use or reproduction.
12 *
13 *  Although all reasonable efforts have been taken to ensure the accuracy
14 *  and reliability of the software and data, the NLM and the U.S.
15 *  Government do not and cannot warrant the performance or results that
16 *  may be obtained by using this software or data. The NLM and the U.S.
17 *  Government disclaim all warranties, express or implied, including
18 *  warranties of performance, merchantability or fitness for any particular
19 *  purpose.
20 *
21 *  Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26 
27 #include <vdb/extern.h>
28 
29 #include "database-priv.h"
30 #include "dbmgr-priv.h"
31 #include "schema-priv.h"
32 #include "schema-parse.h"
33 #include "linker-priv.h"
34 
35 #include <kdb/kdb-priv.h>
36 #include <vdb/manager.h>
37 #include <vdb/database.h>
38 #include <kdb/manager.h>
39 #include <kdb/database.h>
40 #include <kdb/meta.h>
41 #include <klib/debug.h>
42 #include <klib/log.h>
43 #include <klib/rc.h>
44 #include <sysalloc.h>
45 
46 #include <stdlib.h>
47 #include <string.h>
48 #include <assert.h>
49 
50 
51 /*--------------------------------------------------------------------------
52  * VDatabase
53  *  opaque connection to a database within file system
54  */
55 
56 
57 /* StoreSchema
58  */
VDatabaseStoreSchema(VDatabase * self)59 rc_t VDatabaseStoreSchema ( VDatabase *self )
60 {
61     /* open schema node */
62     KMDataNode *node;
63     rc_t rc = KMetadataOpenNodeUpdate ( self -> meta, & node, "schema" );
64     if ( rc == 0 )
65     {
66         size_t num_writ;
67         char expr [ 256 ];
68         rc = VSchemaToText ( self -> schema, expr, sizeof expr - 1, & num_writ,
69             "%N%V", self -> sdb -> name, self -> sdb -> version );
70         if ( rc != 0 )
71             LOGERR ( klogInt, rc, "failed to determine database schema" );
72         else
73         {
74             expr [ num_writ ] = 0;
75             rc = KMDataNodeWriteAttr ( node, "name", expr );
76             if ( rc != 0 )
77                 PLOGERR (klogInt, ( klogInt, rc, "failed to write database type '$(expr)'", "expr=%s", expr ));
78             else
79             {
80                 /* truncate existing schema */
81                 rc = KMDataNodeWrite ( node, "", 0 );
82                 if ( rc == 0 )
83                 {
84                     rc = VSchemaDump ( self -> schema, sdmCompact, expr,
85                         ( rc_t ( CC * ) ( void*, const void*, size_t ) ) KMDataNodeAppend, node );
86                 }
87                 if ( rc != 0 )
88                     PLOGERR (klogInt, ( klogInt, rc, "failed to write database schema '$(expr)'", "expr=%s", expr ));
89             }
90         }
91 
92         KMDataNodeRelease ( node );
93     }
94     return rc;
95 }
96 
97 
98 /* OpenUpdate
99  *  finish create operation
100  */
101 static
VDatabaseOpenUpdate(VDatabase * self,const char * decl)102 rc_t VDatabaseOpenUpdate ( VDatabase *self, const char *decl )
103 {
104     /* open metadata */
105     rc_t rc = KDatabaseOpenMetadataUpdate ( self -> kdb, & self -> meta );
106     if ( rc == 0 )
107     {
108         /* fetch stored schema */
109         rc = VDatabaseLoadSchema ( self );
110         if ( rc == 0 )
111         {
112             /* fetch requested schema */
113             const SDatabase *sdb = self -> sdb;
114             if ( decl != NULL && decl [ 0 ] != 0 )
115             {
116                 uint32_t type;
117                 const SNameOverload *name;
118 
119                 if ( self -> dad != NULL )
120                 {
121                     const SDBMember *mbr = SDatabaseFind ( self -> dad -> sdb,
122                         self -> schema, & name, & type, decl, "VDatabaseOpenUpdate" );
123                     if ( mbr == NULL || type != eDBMember )
124                     {
125                         PLOGMSG ( klogWarn, ( klogWarn, "expression '$(expr)' is not a database member",
126                                    "expr=%s", decl ));
127                         sdb = NULL;
128                     }
129                     else
130                     {
131                         sdb = mbr -> db;
132                         assert ( sdb != NULL );
133                     }
134                 }
135                 else
136                 {
137                     sdb = VSchemaFind ( self -> schema,
138                         & name, & type, decl, "VDatabaseOpenUpdate", true );
139                     if ( sdb != NULL && type != eDatabase )
140                     {
141                         PLOGMSG ( klogWarn, ( klogWarn, "expression '$(expr)' is not a database",
142                                    "expr=%s", decl ));
143                         sdb = NULL;
144                     }
145                 }
146             }
147 
148             /* error if the two definitions differ */
149             if ( sdb != NULL && self -> sdb != NULL && sdb != self -> sdb )
150                 rc = RC ( rcVDB, rcDatabase, rcOpening, rcSchema, rcIncorrect );
151             else if ( sdb == NULL && self -> sdb == NULL )
152                 rc = RC ( rcVDB, rcDatabase, rcOpening, rcSchema, rcNotFound );
153             else if ( self -> sdb == NULL )
154             {
155                 /* write schema to metadata */
156                 self -> sdb = sdb;
157                 rc = VDatabaseStoreSchema ( self );
158             }
159             else if ( sdb != NULL )
160             {
161                 /* use latest schema but don't overwrite in metadata */
162                 self -> sdb = sdb;
163             }
164         }
165     }
166 
167     DBGMSG(DBG_VDB, DBG_FLAG(DBG_VDB_VDB), ("VDatabaseOpenUpdate = %d\n", rc));
168 
169     return rc;
170 }
171 
172 
173 /* CreateDB
174  * VCreateDB
175  *  create a new or open an existing database
176  *
177  *  "db" [ OUT ] - return parameter for newly opened database
178  *
179  *  "schema" [ IN ] - schema object containg database
180  *  declaration to be used in creating db [ needed by manager ].
181  *
182  *  "decl" [ IN ] - type and optionally version of db schema
183  *
184  *  "cmode" [ IN ] - creation mode
185  *
186  *  "path" [ IN ] - NUL terminated string in
187  *  wd-native character set giving path to database
188  */
VDBManagerVCreateDB(VDBManager * self,VDatabase ** dbp,const VSchema * schema,const char * decl,KCreateMode cmode,const char * path,va_list args)189 LIB_EXPORT rc_t CC VDBManagerVCreateDB ( VDBManager *self, VDatabase **dbp,
190     const VSchema *schema, const char *decl,
191     KCreateMode cmode, const char *path, va_list args )
192 {
193     rc_t rc;
194 
195     if ( dbp == NULL )
196         rc = RC ( rcVDB, rcMgr, rcCreating, rcParam, rcNull );
197     else
198     {
199         if ( self == NULL )
200             rc = RC ( rcVDB, rcMgr, rcCreating, rcSelf, rcNull );
201         else if ( schema == NULL )
202             rc = RC ( rcVDB, rcMgr, rcOpening, rcSchema, rcNull );
203         else if ( decl == NULL )
204             rc = RC ( rcVDB, rcMgr, rcOpening, rcName, rcNull );
205         else if ( decl [ 0 ] == 0 )
206             rc = RC ( rcVDB, rcMgr, rcOpening, rcName, rcEmpty );
207         else
208         {
209             rc = VDatabaseMake ( dbp, self, NULL, schema );
210             if ( rc == 0 )
211             {
212                 VDatabase *db = * dbp;
213 
214                 rc = KDBManagerVCreateDB ( self -> kmgr, & db -> kdb, cmode, path, args );
215                 if ( rc == 0 )
216                 {
217                     rc = VDatabaseOpenUpdate ( db, decl );
218                     if ( rc == 0 )
219                         return 0;
220 
221                     rc = ResetRCContext ( rc, rcVDB, rcMgr, rcCreating );
222                 }
223 
224                 VDatabaseWhack ( db );
225             }
226         }
227 
228         * dbp = NULL;
229     }
230     return rc;
231 }
232 
VDBManagerCreateDB(VDBManager * self,VDatabase ** db,const VSchema * schema,const char * decl,KCreateMode cmode,const char * path,...)233 LIB_EXPORT rc_t CC VDBManagerCreateDB ( VDBManager *self, VDatabase **db,
234     const VSchema *schema, const char *decl,
235     KCreateMode cmode, const char *path, ... )
236 {
237     rc_t rc;
238     va_list args;
239 
240     va_start ( args, path );
241     rc = VDBManagerVCreateDB ( self, db, schema, decl, cmode, path, args );
242     va_end ( args );
243 
244     return rc;
245 }
246 
VDatabaseVCreateDB(VDatabase * self,VDatabase ** dbp,const char * decl,KCreateMode cmode,const char * name,va_list args)247 LIB_EXPORT rc_t CC VDatabaseVCreateDB ( VDatabase *self, VDatabase **dbp,
248     const char *decl, KCreateMode cmode, const char *name, va_list args )
249 {
250     rc_t rc;
251 
252     if ( dbp == NULL )
253         rc = RC ( rcVDB, rcDatabase, rcCreating, rcParam, rcNull );
254     else
255     {
256         if ( self == NULL )
257             rc = RC ( rcVDB, rcDatabase, rcCreating, rcSelf, rcNull );
258         else if ( decl == NULL )
259             rc = RC ( rcVDB, rcMgr, rcOpening, rcName, rcNull );
260         else if ( decl [ 0 ] == 0 )
261             rc = RC ( rcVDB, rcMgr, rcOpening, rcName, rcEmpty );
262         else if ( self -> read_only )
263             rc = RC ( rcVDB, rcDatabase, rcCreating, rcDatabase, rcReadonly );
264         else
265         {
266             rc = VDatabaseMake ( dbp, self -> mgr, self, self -> schema );
267             if ( rc == 0 )
268             {
269                 VDatabase *db = * dbp;
270 
271                 rc = KDatabaseVCreateDB ( self -> kdb, & db -> kdb, cmode, name, args );
272                 if ( rc == 0 )
273                 {
274                     rc = VDatabaseOpenUpdate ( db, decl );
275                     if ( rc == 0 )
276                         return 0;
277 
278                     rc = ResetRCContext ( rc, rcVDB, rcDatabase, rcCreating );
279                 }
280 
281                 VDatabaseWhack ( db );
282             }
283         }
284 
285         * dbp = NULL;
286     }
287     return rc;
288 }
289 
VDatabaseCreateDB(VDatabase * self,VDatabase ** db,const char * decl,KCreateMode cmode,const char * name,...)290 LIB_EXPORT rc_t CC VDatabaseCreateDB ( VDatabase *self, VDatabase **db,
291     const char *decl, KCreateMode cmode, const char *name, ... )
292 {
293     rc_t rc;
294     va_list args;
295 
296     va_start ( args, name );
297     rc = VDatabaseVCreateDB ( self, db, decl, cmode, name, args );
298     va_end ( args );
299 
300     return rc;
301 }
302 
VDatabaseVDropDB(VDatabase * self,const char * name,va_list args)303 LIB_EXPORT rc_t CC VDatabaseVDropDB ( VDatabase *self,
304                                      const char *name, va_list args)
305 {
306     return KDatabaseVDropDB(self->kdb, name, args);
307 }
308 
VDatabaseDropDB(VDatabase * self,const char * name,...)309 LIB_EXPORT rc_t CC VDatabaseDropDB ( VDatabase *self,
310                                     const char *name, ... )
311 {
312     rc_t rc;
313     va_list args;
314 
315     va_start ( args, name );
316     rc = VDatabaseVDropDB(self, name, args);
317     va_end ( args );
318 
319     return rc;
320 }
321 
VDatabaseVDropTable(VDatabase * self,const char * name,va_list args)322 LIB_EXPORT rc_t CC VDatabaseVDropTable ( VDatabase *self,
323                                     const char *name, va_list args)
324 {
325     return KDatabaseVDropTable(self->kdb, name, args);
326 }
327 
VDatabaseDropTable(VDatabase * self,const char * name,...)328 LIB_EXPORT rc_t CC VDatabaseDropTable ( VDatabase *self,
329                                       const char *name, ... )
330 {
331     rc_t rc;
332     va_list args;
333 
334     va_start ( args, name );
335     rc = VDatabaseVDropTable(self, name, args);
336     va_end ( args );
337 
338     return rc;
339 }
340 
341 
342 /* OpenDBUpdate
343  * VOpenDBUpdate
344  *  open a database for read/write
345  *
346  *  "db" [ OUT ] - return parameter for newly opened database
347  *
348  *  "schema" [ IN, NULL OKAY ] - schema object containg database
349  *  declaration to be used in creating db [ needed by manager ].
350  *
351  *  "path" [ IN ] - NUL terminated string in
352  *  wd-native character set giving path to database
353  */
VDBManagerVOpenDBUpdate(VDBManager * self,VDatabase ** dbp,const VSchema * schema,const char * path,va_list args)354 LIB_EXPORT rc_t CC VDBManagerVOpenDBUpdate ( VDBManager *self, VDatabase **dbp,
355     const VSchema *schema, const char *path, va_list args )
356 {
357     rc_t rc;
358 
359     if ( dbp == NULL )
360         rc = RC ( rcVDB, rcMgr, rcOpening, rcParam, rcNull );
361     else
362     {
363         if ( self == NULL )
364             rc = RC ( rcVDB, rcMgr, rcOpening, rcSelf, rcNull );
365         else
366         {
367             if ( schema == NULL )
368                 schema = self -> schema;
369 
370             rc = VDatabaseMake ( dbp, self, NULL, schema );
371             if ( rc == 0 )
372             {
373                 VDatabase *db = * dbp;
374 
375                 rc = KDBManagerVOpenDBUpdate ( self -> kmgr, & db -> kdb, path, args );
376                 if ( rc == 0 )
377                 {
378                     rc = VDatabaseOpenUpdate ( db, NULL );
379                     if ( rc == 0 )
380                         return 0;
381                 }
382 
383                 VDatabaseWhack ( db );
384             }
385         }
386 
387         * dbp = NULL;
388     }
389     return rc;
390 }
391 
VDBManagerOpenDBUpdate(VDBManager * self,VDatabase ** db,const VSchema * schema,const char * path,...)392 LIB_EXPORT rc_t CC VDBManagerOpenDBUpdate ( VDBManager *self, VDatabase **db,
393     const VSchema *schema, const char *path, ... )
394 {
395     rc_t rc;
396     va_list args;
397 
398     va_start ( args, path );
399     rc = VDBManagerVOpenDBUpdate ( self, db, schema, path, args );
400     va_end ( args );
401 
402     return rc;
403 }
404 
VDatabaseVOpenDBUpdate(VDatabase * self,VDatabase ** dbp,const char * name,va_list args)405 LIB_EXPORT rc_t CC VDatabaseVOpenDBUpdate ( VDatabase *self, VDatabase **dbp,
406     const char *name, va_list args )
407 {
408     rc_t rc;
409 
410     if ( dbp == NULL )
411         rc = RC ( rcVDB, rcDatabase, rcOpening, rcParam, rcNull );
412     else
413     {
414         if ( self == NULL )
415             rc = RC ( rcVDB, rcDatabase, rcOpening, rcSelf, rcNull );
416         else if ( self -> read_only )
417             rc = RC ( rcVDB, rcDatabase, rcOpening, rcDatabase, rcReadonly );
418         else
419         {
420             rc = VDatabaseMake ( dbp, self -> mgr, self, self -> schema );
421             if ( rc == 0 )
422             {
423                 VDatabase *db = * dbp;
424 
425                 rc = KDatabaseVOpenDBUpdate ( self -> kdb, & db -> kdb, name, args );
426                 if ( rc == 0 )
427                 {
428                     rc = VDatabaseOpenUpdate ( db, NULL );
429                     if ( rc == 0 )
430                         return 0;
431                 }
432 
433                 VDatabaseWhack ( db );
434             }
435         }
436 
437         * dbp = NULL;
438     }
439     return rc;
440 }
441 
VDatabaseOpenDBUpdate(VDatabase * self,VDatabase ** db,const char * name,...)442 LIB_EXPORT rc_t CC VDatabaseOpenDBUpdate ( VDatabase *self, VDatabase **db, const char *name, ... )
443 {
444     rc_t rc;
445     va_list args;
446 
447     va_start ( args, name );
448     rc = VDatabaseVOpenDBUpdate ( self, db, name, args );
449     va_end ( args );
450 
451     return rc;
452 }
453 
454 /* Lock
455  *  apply lock
456  *
457  *  if object is already locked, the operation is idempotent
458  *  and returns an rc state of rcLocked
459  *
460  *  "type" [ IN ] - a KDBPathType
461  *  valid values are kptDatabase, kptTable and kptIndex
462  *
463  *  "path" [ IN ] - NUL terminated path
464  */
VDatabaseVLock(VDatabase * self,uint32_t type,const char * name,va_list args)465 LIB_EXPORT rc_t CC VDatabaseVLock ( VDatabase *self, uint32_t type, const char *name, va_list args )
466 {
467     rc_t rc;
468 
469     if ( self == NULL )
470         rc = RC ( rcVDB, rcDatabase, rcLocking, rcSelf, rcNull );
471     else
472         rc = KDatabaseVLock ( self -> kdb, type, name, args );
473 
474     return rc;
475 }
476 
VDatabaseLock(VDatabase * self,uint32_t type,const char * name,...)477 LIB_EXPORT rc_t CC VDatabaseLock ( VDatabase *self, uint32_t type, const char *name, ... )
478 {
479     rc_t rc;
480 
481     va_list args;
482     va_start ( args, name );
483 
484     rc = VDatabaseVLock ( self, type, name, args );
485 
486     va_end ( args );
487 
488     return rc;
489 }
490 
491 /* Unlock
492  *  remove lock
493  *
494  *  if object is already unlocked, the operation is idempotent
495  *  and returns an rc state of rcUnlocked
496  *
497  *  "type" [ IN ] - a KDBPathType
498  *  valid values are kptDatabase, kptTable and kptIndex
499  *
500  *  "path" [ IN ] - NUL terminated path
501  */
VDatabaseVUnlock(VDatabase * self,uint32_t type,const char * name,va_list args)502 LIB_EXPORT rc_t CC VDatabaseVUnlock ( VDatabase *self, uint32_t type, const char *name, va_list args )
503 {
504     rc_t rc;
505 
506     if ( self == NULL )
507         rc = RC ( rcVDB, rcDatabase, rcUnlocking, rcSelf, rcNull );
508     else
509         rc = KDatabaseVUnlock ( self -> kdb, type, name, args );
510 
511     return rc;
512 }
513 
VDatabaseUnlock(VDatabase * self,uint32_t type,const char * name,...)514 LIB_EXPORT rc_t CC VDatabaseUnlock ( VDatabase *self, uint32_t type, const char *name, ... )
515 {
516     rc_t rc;
517 
518     va_list args;
519     va_start ( args, name );
520 
521     rc = VDatabaseVUnlock ( self, type, name, args );
522 
523     va_end ( args );
524 
525     return rc;
526 }
527 
528 
529 /* OpenMetadataUpdate
530  *  opens metadata for update
531  *
532  *  "meta" [ OUT ] - return parameter for metadata
533  */
VDatabaseOpenMetadataUpdate(VDatabase * self,KMetadata ** meta)534 LIB_EXPORT rc_t CC VDatabaseOpenMetadataUpdate ( VDatabase *self, KMetadata **meta )
535 {
536     rc_t rc;
537     if ( meta == NULL )
538         rc = RC ( rcVDB, rcDatabase, rcAccessing, rcParam, rcNull );
539     else
540     {
541         * meta = NULL;
542 
543         if ( self == NULL )
544             rc = RC ( rcVDB, rcDatabase, rcAccessing, rcSelf, rcNull );
545         else
546         {
547             /* we operate under the notion of
548                single-threaded operation, so hand out
549                read or update capable object */
550             rc = KMetadataAddRef ( self -> meta );
551             if ( rc == 0 )
552                 * meta = self -> meta;
553         }
554     }
555 
556     return rc;
557 }
558 
559 
560 /* ColumnCreateParams
561  *  sets the creation parameters for physical columns
562  *
563  *  "cmode" [ IN ] - creation mode
564  *
565  *  "checksum" [ IN ] - the type of checksum information to
566  *  apply when writing blobs
567  *
568  *  "pgsize" [ IN, DEFAULT ZERO ] - size of internal column "pages"
569  *  the default value is indicated by 0 ( zero ).
570  *  NB - CURRENTLY THE ONLY SUPPORTED PAGE SIZE IS 1 ( ONE ) BYTE.
571  */
VDatabaseColumnCreateParams(VDatabase * self,KCreateMode cmode,KChecksum checksum,size_t pgsize)572 LIB_EXPORT rc_t CC VDatabaseColumnCreateParams ( VDatabase *self,
573     KCreateMode cmode, KChecksum checksum, size_t pgsize )
574 {
575     if ( self == NULL )
576         return RC ( rcVDB, rcTable, rcUpdating, rcSelf, rcNull );
577 
578     KDatabaseSetCmode ( self->kdb, cmode );
579     KDatabaseSetChecksum ( self->kdb, checksum );
580 
581     self -> pgsize = pgsize;
582 
583     return 0;
584 }
585 
586 
587 /* OpenManager
588  *  duplicate reference to manager
589  *  NB - returned reference must be released
590  */
VDatabaseOpenManagerUpdate(VDatabase * self,VDBManager ** mgr)591 LIB_EXPORT rc_t CC VDatabaseOpenManagerUpdate ( VDatabase *self, VDBManager **mgr )
592 {
593     rc_t rc;
594 
595     if ( mgr == NULL )
596         rc = RC ( rcVDB, rcDatabase, rcAccessing, rcParam, rcNull );
597     else
598     {
599         if ( self == NULL )
600             rc = RC ( rcVDB, rcDatabase, rcAccessing, rcSelf, rcNull );
601         else
602         {
603             rc = VDBManagerAddRef ( self -> mgr );
604             if ( rc == 0 )
605             {
606                 * mgr = self -> mgr;
607                 return 0;
608             }
609         }
610 
611         * mgr = NULL;
612     }
613 
614     return rc;
615 }
616 
617 
618 /* OpenParent
619  *  duplicate reference to parent database
620  *  NB - returned reference must be released
621  */
VDatabaseOpenParentUpdate(VDatabase * self,VDatabase ** par)622 LIB_EXPORT rc_t CC VDatabaseOpenParentUpdate ( VDatabase *self, VDatabase **par )
623 {
624     rc_t rc;
625 
626     if ( par == NULL )
627         rc = RC ( rcVDB, rcDatabase, rcAccessing, rcParam, rcNull );
628     else
629     {
630         if ( self == NULL )
631             rc = RC ( rcVDB, rcDatabase, rcAccessing, rcSelf, rcNull );
632         else if ( self -> dad != NULL && self -> dad -> read_only )
633             rc = RC ( rcVDB, rcDatabase, rcAccessing, rcDatabase, rcReadonly );
634         else
635         {
636             rc = VDatabaseAddRef ( self -> dad );
637             if ( rc == 0 )
638             {
639                 * par = self -> dad;
640                 return 0;
641             }
642         }
643 
644         * par = NULL;
645     }
646 
647     return rc;
648 }
649 
650 
651 /* OpenKDatabase
652  *  returns a new reference to underlying KDatabase
653  */
VDatabaseOpenKDatabaseUpdate(VDatabase * self,KDatabase ** kdb)654 LIB_EXPORT rc_t CC VDatabaseOpenKDatabaseUpdate ( VDatabase *self, KDatabase **kdb )
655 {
656     rc_t rc;
657 
658     if ( kdb == NULL )
659         rc = RC ( rcVDB, rcDatabase, rcAccessing, rcParam, rcNull );
660     else
661     {
662         if ( self == NULL )
663             rc = RC ( rcVDB, rcDatabase, rcAccessing, rcSelf, rcNull );
664         else if ( self -> read_only )
665             rc = RC ( rcVDB, rcDatabase, rcAccessing, rcDatabase, rcReadonly );
666         else
667         {
668             rc = KDatabaseAddRef ( self -> kdb );
669             if ( rc == 0 )
670             {
671                 * kdb = self -> kdb;
672                 return 0;
673             }
674         }
675 
676         * kdb = NULL;
677     }
678 
679     return rc;
680 }
681