source: gs2-extensions/tdb/trunk/perllib/dbutil.pm@ 30177

Last change on this file since 30177 was 29316, checked in by jmt12, 10 years ago

Adding in support for databases that merge as a final step

File size: 11.5 KB
Line 
1###########################################################################
2#
3# dbutil.pm -- gateway to utilities for reading/writing to different databases
4#
5# Copyright (C) 2008 DL Consulting Ltd
6#
7# A component of the Greenstone digital library software
8# from the New Zealand Digital Library Project at the
9# University of Waikato, New Zealand.
10#
11# This program is free software; you can redistribute it and/or modify
12# it under the terms of the GNU General Public License as published by
13# the Free Software Foundation; either version 2 of the License, or
14# (at your option) any later version.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License
22# along with this program; if not, write to the Free Software
23# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24#
25###########################################################################
26
27package dbutil;
28
29use strict;
30
31use Symbol qw<qualify>;
32use util;
33
34# /** Dynamic class loading - for use in DBUtils to load various database
35# * drivers, configured in the collect.cfg, at runtime.
36# * @param $class - The class name (including any path) to load
37# * @param rest - any function aliases you want exported
38# */
39sub load_db_driver
40{
41 my $class = shift(@_);
42 (my $file = "$class.pm") =~ s|::|/|g;
43 # - ensure we haven't already loaded this class
44 unless( $INC{$file} )
45 {
46 # - require is fine being assigned at runtime - no need for evil eval
47 #eval
48 #{
49 require $file;
50 #};
51 }
52 # - this is the magic that actually instantiates the class (rubberstamp?)
53 # - we pass @_ to action any function aliases exports requested
54 eval
55 {
56 $class->import(@_);
57 };
58 # - by now the driver file should have been loaded
59 return (defined $INC{$file});
60}
61# /** load_db_driver() **/
62
63# /** Make a function call to a dynamically loaded database driver.
64# * @param $function_name
65# * @param $driver_name
66# * @param <rest> The parameters to be passed to the function called
67# */
68sub call_dynamic_driver_function
69{
70 my $function_name = shift(@_);
71 my $driver_name = shift(@_);
72 my $package_name = 'dbutil::' . $driver_name;
73 # - try to load the requested infodb type
74 if (!&load_db_driver($package_name))
75 {
76 # - try loading the default GDBM driver
77 print STDERR 'Warning! Using default database driver (GDBM) as failed to load configured database driver: ' . $driver_name . "\n";
78 $package_name = 'dbutil::gdbm';
79 if (!&load_db_driver($package_name))
80 {
81 die("Fatal Error! Failed to load default database driver: dbutil::gdbm\n");
82 }
83 }
84 # - make call to the newly created package
85 no strict;
86 # - lets check that the function we are about to call
87 my $symbol = qualify($function_name, $package_name);
88 unless ( defined &{$symbol} )
89 {
90 die ('Error! Function not found: ' . $package_name . '::' . $function_name . "()\n");
91 }
92 return &{$symbol}(@_);
93}
94# /** call_dynamic_driver_function() **/
95
96
97## @function test_dynamic_driver_function()
98#
99# Checks to see if a function in a dynamically loaded driver exists
100#
101sub test_dynamic_driver_function
102{
103 my $function_name = shift(@_);
104 my $driver_name = shift(@_);
105 my $package_name = 'dbutil::' . $driver_name;
106 # - try to load the requested infodb type
107 if (!&load_db_driver($package_name))
108 {
109 # - try loading the default GDBM driver
110 print STDERR 'Warning! Using default database driver (GDBM) as failed to load configured database driver: ' . $driver_name . "\n";
111 $package_name = 'dbutil::gdbm';
112 if (!&load_db_driver($package_name))
113 {
114 die("Fatal Error! Failed to load default database driver: dbutil::gdbm\n");
115 }
116 }
117 # - make call to the newly created package
118 no strict;
119 # - lets check that the function we are about to call
120 my $symbol = qualify($function_name, $package_name);
121 return defined &{$symbol};
122}
123## test_dynamic_driver_function() ##
124
125
126## @function
127#
128sub open_infodb_write_handle
129{
130 my $infodb_type = shift(@_);
131 my $infodb_file_path = shift(@_);
132 # Make a call to the dynamically loaded driver to open the connection.
133 return &dbutil::call_dynamic_driver_function('open_infodb_write_handle', $infodb_type, $infodb_file_path, @_);
134}
135
136
137sub close_infodb_write_handle
138{
139 my $infodb_type = shift(@_);
140 my $infodb_handle = shift(@_);
141 # Dynamic database driver call
142 return &dbutil::call_dynamic_driver_function('close_infodb_write_handle', $infodb_type, $infodb_handle, @_);
143}
144
145
146sub get_default_infodb_type
147{
148 # The default is GDBM so everything works the same for existing collections
149 # To use something else, specify the "infodbtype" in the collection's collect.cfg file
150 return "gdbm";
151}
152
153# /** @function get_infodb_file_path()
154# * Warning! Black magic follows. The first time get_infodb_file_path is
155# * called (presumably from inexport::process_files()) for databases of type
156# * server will actually the Server to be run complete with an initial dummy
157# * listener. This is done so that, in parallel importing, the server will
158# * persist until the top level import.pl (which will be the first that calls
159# * this function) completes and removes the dummy listener. [jmt12]
160sub get_infodb_file_path
161{
162 my $infodb_type = shift(@_);
163 my $collection_name = shift(@_);
164 my $infodb_directory_path = shift(@_);
165
166 #=======================================MSSQL SUPPORT==============================================#
167 # Updated by Jeffrey (2008/08/25 Monday)
168 # After look into the run-time code, it seems we should still create a database file.
169 # Since the run-time code is always try to read a database file, the easiest way here is not
170 # to change the whole structure, but to give whatever the system is looking for.
171 #==================================================================================================#
172 # Added by Jeffrey (2008/08/15 Friday)
173 # No file path required for MS SQL, it is a server-client connection.
174 # At the moment the information is hard coded in dbutil::mssql::open_infodb_write_handle
175 # the this might need some tidy up sometime.
176 #==================================================================================================#
177
178 return &dbutil::call_dynamic_driver_function('get_infodb_file_path', $infodb_type, $collection_name, $infodb_directory_path, @_);
179}
180
181# This function, conceptually, would be better structured if it didn't
182# use return statements, as the database methods it calls do not
183# themselves return anything.
184# Note: if doing this, then the GDBM lines of code should be moved into
185# an 'else' clause
186sub read_infodb_file
187{
188 my $infodb_type = shift(@_);
189 my $infodb_file_path = shift(@_);
190 my $infodb_map = shift(@_);
191
192 return &dbutil::call_dynamic_driver_function('read_infodb_file', $infodb_type, $infodb_file_path, $infodb_map, @_);
193}
194
195sub read_infodb_keys
196{
197 my $infodb_type = shift(@_);
198 my $infodb_file_path = shift(@_);
199 my $infodb_map = shift(@_);
200
201 return &dbutil::call_dynamic_driver_function('read_infodb_keys', $infodb_type, $infodb_file_path, $infodb_map, @_);
202}
203
204
205## @function supportDatestamp
206#
207sub supportsDatestamp
208{
209 my $infodb_type = shift(@_);
210 return &dbutil::test_dynamic_driver_function('supportsDatestamp', $infodb_type);
211}
212## supportsDatestamp() ##
213
214
215## @function supportsMerge
216#
217sub supportsMerge
218{
219 my $infodb_type = shift(@_);
220 return &dbutil::test_dynamic_driver_function('merge_databases', $infodb_type);
221}
222## supportsMerge() ##
223
224
225## @function supportRSS
226#
227sub supportsRSS
228{
229 my $infodb_type = shift(@_);
230 return &dbutil::test_dynamic_driver_function('supportsRSS', $infodb_type);
231}
232## supportsRSS() ##
233
234
235sub write_infodb_entry
236{
237 my $infodb_type = shift(@_);
238 my $infodb_handle = shift(@_);
239 my $infodb_key = shift(@_);
240 my $infodb_map = shift(@_);
241
242 return &dbutil::call_dynamic_driver_function('write_infodb_entry', $infodb_type, $infodb_handle, $infodb_key, $infodb_map, @_);
243}
244
245
246sub write_infodb_rawentry
247{
248 my $infodb_type = shift(@_);
249 my $infodb_handle = shift(@_);
250 my $infodb_key = shift(@_);
251 my $infodb_val = shift(@_);
252
253 return &dbutil::call_dynamic_driver_function('write_infodb_rawentry', $infodb_type, $infodb_handle, $infodb_key, $infodb_val, @_);
254}
255
256
257sub set_infodb_entry
258{
259 my $infodb_type = shift(@_);
260 my $infodb_file_path = shift(@_);
261 my $infodb_key = shift(@_);
262 my $infodb_map = shift(@_);
263
264 return &dbutil::call_dynamic_driver_function('set_infodb_entry', $infodb_type, $infodb_file_path, $infodb_key, $infodb_map, @_);
265}
266
267
268
269sub delete_infodb_entry
270{
271 my $infodb_type = shift(@_);
272 my $infodb_handle = shift(@_);
273 my $infodb_key = shift(@_);
274
275 return &dbutil::call_dynamic_driver_function('delete_infodb_entry', $infodb_type, $infodb_handle, $infodb_key, @_);
276}
277
278sub read_infodb_rawentry
279{
280 my $infodb_type = shift(@_);
281 my $infodb_file_path = shift(@_);
282 my $infodb_key = shift(@_);
283
284 # !! TEMPORARY: Slow and naive implementation that just reads the entire file and picks out the one value
285 # !! This will one day be replaced with database-specific versions that will use dbget etc.
286 my $infodb_map = {};
287 &read_infodb_file($infodb_type, $infodb_file_path, $infodb_map);
288
289 return $infodb_map->{$infodb_key};
290}
291
292
293sub read_infodb_entry
294{
295 my $infodb_type = shift(@_);
296 my $infodb_file_path = shift(@_);
297 my $infodb_key = shift(@_);
298
299 if ($infodb_type eq "sqlite")
300 {
301 require dbutil::sqlite;
302 return &dbutil::sqlite::read_infodb_entry($infodb_file_path, $infodb_key, @_);
303 }
304# elsif ($infodb_type eq "gdbm-txtgz")
305# {
306# require dbutil::gdbmtxtgz;
307# return &dbutil::gdbmtxtgz::read_infodb_entry($infodb_file_path, $infodb_key, @_);
308# }
309# elsif ($infodb_type eq "jdbm")
310# {
311# require dbutil::jdbm;
312# return &dbutil::jdbm::read_infodb_entry($infodb_file_path, $infodb_key, @_);
313# }
314# elsif ($infodb_type eq "mssql")
315# {
316# require dbutil::mssql;
317# return &dbutil::mssql::read_infodb_entry($infodb_file_path, $infodb_key, @_);
318# }
319
320# # Use GDBM if the infodb type is empty or not one of the values above
321# require dbutil::gdbm;
322# return &dbutil::gdbm::read_infodb_entry($infodb_file_path, $infodb_key, @_);
323
324
325 # rawentry above is currently naive implementation
326 my $raw_string = read_infodb_rawentry($infodb_type, $infodb_file_path, $infodb_key);
327 my $infodb_rec = &dbutil::convert_infodb_string_to_hash($raw_string);
328
329 return $infodb_rec;
330}
331
332
333## @function
334#
335sub merge_databases
336{
337 my $infodb_type = shift(@_);
338 # Make a call to the dynamically loaded driver to open the connection.
339 return &dbutil::call_dynamic_driver_function('merge_databases', $infodb_type, @_);
340}
341##
342
343
344# ---- GENERAL FUNCTIONS --------
345
346sub convert_infodb_hash_to_string
347{
348 my $infodb_map = shift(@_);
349
350 my $infodb_entry_value = "";
351 foreach my $infodb_value_key (keys(%$infodb_map))
352 {
353 foreach my $infodb_value (@{$infodb_map->{$infodb_value_key}})
354 {
355 $infodb_entry_value .= "<$infodb_value_key>" . $infodb_value . "\n";
356 }
357 }
358
359 return $infodb_entry_value;
360}
361
362
363sub convert_infodb_string_to_hash
364{
365 my $infodb_entry_value = shift(@_);
366 my $infodb_map = ();
367
368 if (!defined $infodb_entry_value) {
369 print STDERR "Warning: No value to convert into a infodb hashtable\n";
370 }
371 else {
372 while ($infodb_entry_value =~ /^<(.*?)>(.*)$/mg)
373 {
374 my $infodb_value_key = $1;
375 my $infodb_value = $2;
376
377 if (!defined($infodb_map->{$infodb_value_key}))
378 {
379 $infodb_map->{$infodb_value_key} = [ $infodb_value ];
380 }
381 else
382 {
383 push(@{$infodb_map->{$infodb_value_key}}, $infodb_value);
384 }
385 }
386 }
387
388 return $infodb_map;
389}
390
391
3921;
Note: See TracBrowser for help on using the repository browser.