The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

 DBD::RAM - a DBI driver for in-memory data structures

SYNOPSIS

 # This sample creates a database, inserts a record, then reads
 # the record and prints it.  The output is "Hello, new world!"
 #
 use DBI;
 my $dbh = DBI->connect( 'DBI:RAM:' );
 $dbh->func( [<DATA>], 'import' );
 print $dbh->selectrow_array('SELECT col2 FROM table1');
 __END__
 1,"Hello, new world!",sample

 All syntax supported by SQL::Statement and all methods supported
 by DBD::CSV are also supported, see their documentation for 
 details.

DESCRIPTION

 DBD::RAM allows you to import almost any type of Perl data
 structure into an in-memory table and then use DBI and SQL
 to access and modify it.  It also allows direct access to
 almost any kind of flat file, supporting SQL manipulation
 of the file without converting the file out of its native
 format.

 The module allows you to prototype a database without having an
 rdbms system or other database engine and can operate either with 
 or without creating or reading disk files.

 DBD::RAM works with three differnt kinds of tables: tables
 stored only in memory, tables stored in flat files, and tables
 stored in various DBI-accessible databases.  Users may, for
 most purposes, mix and match these differnt kinds of tables
 within a script.

 Currently the following table types are supported:

    FIX   fixed-width record files
    CSV   comma separated values files
    INI   name=value .ini files
    XML   XML files (limited support)
    RAM   in-memory tables including ones created using:
            'ARRAY' array of arrayrefs
            'HASH'  array of hashrefs
            'CSV'   array of comma-separated value strings
            'INI'   array of name=value strings
            'FIX'   array of fixed-width record strings
            'STH'   statement handle from a DBI database
            'USR'   array of user-defined data structures

 With a data type of 'USR', you can pass a pointer to a subroutine
 that parses the data, thus making the module very extendable.

WARNING

 This module is in a rapid development phase and it is likely to
 change quite often for the next few days/weeks.  I will try to keep
 the interface as stable as possible, but if you are going to start
 using this in places where it will be difficult to modify, you might
 want to ask me about the stabilitiy of the features you are using.

INSTALLATION & PREREQUISITES

 This module should work on any any platform that DBI works on.

 You don't need an external SQL engine or a running server, or a
 compiler.  All you need are Perl itself and installed versions of DBI
 and SQL::Statement. If you do *not* also have DBD::CSV installed you
 will need to either install it, or simply copy File.pm into your DBD
 directory.

 For this first release, there is no makefile, just copy RAM.pm
 into your DBD direcotry.

WORKING WITH IN-MEMORY DATABASES

CREATING TABLES

 In-memory tables may be created using standard CREATE/INSERT
 statements, or using the DBD::RAM specific import method:

    $dbh->func( $spec, $data, 'import' );

 The $spec parameter is a hashref containg:

     table_name   a string holding the name of the table
      col_names   a string with column names separated by commas
      data_type   one of: array, hash, etc. see below for full list
        pattern   a string containing an unpack pattern (fixed-width only)
         parser   a pointer to a parsing subroutine (user only)

 The $data parameter is a an arrayref containing an array of the type
 specified in the $spec{data_type} parameter holding the actual table
 data.

 Data types for the data_type parameter currently include: ARRAY, HASH,
 FIX (fixed-width), CSV, INI (name=value), DBI, and USR. See below for
 examples of each of these types.

From an array of CSV strings

  $dbh->func(
    {
      table_name => 'phrases',
      table_type => 'CSV',
      col_names  => 'id,phrase''
    }
    [
      qq{1,"Hello, new world!"},
      qq{2,"Junkity Junkity Junk"},
    ],'import' );

From an array of ARRAYS

 $dbh->func(
     {
       data_type    => 'ARRAY',
         table_name => 'phrases',
         col_names  => 'id,phrase',
     },
     [
       [1,'Hello new world!'],
       [2,'Junkity Junkity Junk'],
     ],
 'import' );

From an array of HASHES

 $dbh->func(
     { table_name => 'phrases',
       col_names  => 'id,phrase',
       data_type  => 'HASH',
     },
     [
       {id=>1,phrase=>'Hello new world!'},
       {id=>2,phrase=>'Junkity Junkity Junk'},
     ],
 'import' );

From an array of NAME=VALUE strings

 $dbh->func(
     { table_name => 'phrases',    # ARRAY OF NAME=VALUE PAIRS
       col_names  => 'id,phrase',
       data_type  => 'INI',
     },
     [
       '1=2Hello new world!',
       '2=Junkity Junkity Junk',
     ],
 'import' );

From an array of FIXED-WIDTH RECORD strings

 $dbh->func(
     { table_name => 'phrases',
       col_names  => 'id,phrase',
       data_type  => 'FIX',
       pattern    => 'a1 a20',
     },
     [
       '1Hello new world!    ',
       '2Junkity Junkity Junk',
     ],
 'import' );

 The $spec{pattern} value should be a string describing the fixed-width
 record.  See the Perl documentation on "unpack()" for details.

From another DBI DATABASE

 You can import information from any other DBI accessible database with
 the data_type set to 'sth' in the import() method.  First connect to the
 other database via DBI and get a database handle for it separate from the
 database handle for DBD::RAM.  Then do a prepare and execute to get a
 statement handle for a SELECT statement into that database.  Then pass the
 statement handle to the DBD::RAM import() method which will perform the
 fetch and insert the fetched fields and records into the DBD::RAM table.
 After the import() statement, you can then close the database connection
 to the other database.

 Here's an example using DBD::CSV --

  my $dbh_csv = DBI->connect('DBI:CSV:','','',{RaiseError=>1});
  my $sth_csv = $dbh_csv->prepare("SELECT * FROM mytest_db");
  $sth_csv->execute();
  $dbh->func(
      { table_name => 'phrases',
        col_names  => 'id,phrase',
        data_type  => 'DBI',
      },
      [$sth_csv],
      'import'
  );
  $dbh_csv->disconnect();

From USER-DEFINED DATA STRUCTURES

 $dbh->func(
    { table_name => 'phrases',    # USER DEFINED STRUCTURE
      col_names  => 'id,phrase',
      data_type  => 'USR',
      parser     => sub { split /=/,shift },
    },
    [
        '1=Hello new world!',
        '2=Junkity Junkity Junk',
    ],
 'import' );

 This example shows a way to implement a simple name=value parser.
 The subroutine can be as complex as you like however and could, for
 example, call XML or HTML or other parsers, or do any kind of fetches
 or massaging of data (e.g. put in some LWP calls to websites as part
 of the data massaging).  [Note: the actual name=value implementation
 in the DBD uses a slightly more complex regex to be able to handle equal
 signs in the value.]

 The parsing subroutine must accept a row of data in the user-defined
 format and return it as an array.  Basically, the import() method
 will cycle through the array of data, and for each element in its
 array, it will send that element to your parser subroutine.  The
 parser subroutine should accept an element in that format and return
 an array with the elements of the array in the same order as the
 column names you specified in the import() statement.  In the example
 above, the sub accepts a string and returns and array.

 PLEASE NOTE: If you develop generally useful parser routines that others
 might also be able to use, send them to me and I can encorporate them
 into the DBD itself.

From SQL STATEMENTS

 You may also create tables with standard SQL syntax using CREATE
 TABLE and INSERT statements.  Or you can create a table with the
 import method and later populate it using INSERT statements.  Howver
 the table is created, it can be modified and accessed with all SQL
 syntax supported by SQL::Statement.

USING DEFAULTS FOR QUICK PROTOTYPING

 If no table type is supplied, an in-memory type 'RAM' will be
 assumed.  If no table_name is specified, a numbered table name
 will be supplied (table1, or if that exists table2, etc.).  The
 same also applies to column names (col1, col2, etc.).  If no
 data_type is supplied, CSV will be assumed. If the $spec parameter
 to import is missing, then defaults for all values will be used.
 Thus, the two statements below have the same effect:

    $dbh->func( [
        qq{1,"Hello, new world!"},
        qq{2,"Junkity Junkity Junk"},
        ],'import' );

    $dbh->func(
        {
            table_name => table1,
            table_type => 'CSV',
            col_names  => 'col1,col2'
        }
        [
          qq{1,"Hello, new world!"},
          qq{2,"Junkity Junkity Junk"},
        ],'import' );

WORKING WITH FLAT FILES

 This module now supports working with several different kinds of flat
 files and will soon support many more varieties.  Currently supported are
 fixed-width record files, comma separated values files, name=value ini
 files, and (with limited support) XML files.  See below for details

 To work with these kinds of files, you must first enter the table in a
 catalog specifying the table name, file name, file type, and optionally
 other information.

 Catalogs are created with the $dbh->func('catalog') command.

     $dbh->func([[
                  $table_name,
                  $table_type,
                  $file_name,
                  {optional params}
               ]])

 For example this sets up a catalog with three tables of type CSV, FIX, and
 XML:

    $dbh->func([
        ['my_csv', 'CSV', 'my_db.csv'],
        ['my_xml', 'XML', 'my_db.xml',{col_names=>'idCol,testCol'}],
        ['my_fix', 'FIX', 'my_db.fix',{pattern=>'a1 a25'}],
    ],'catalog' );

 Optional parameters include col_names -- if the column names are not
 specified with this parameter, then the module will look for the column
 names as a comma-separated list on the first line of the file.

 A table only needs to be entered into the catlog once.  After that all
 SQL statements operating on $table_name will actually be carried out on
 $file_name.  Thus, given the example catalog above, 
 "CREATE TABLE my_csv ..." will create a file called 'my_db.csv' and
 "SELECT * FROM my_xml" will open and read data from a file called
 'my_db.xml'.

 In all cases the files will be expected to be located in the directory
 named in the $dbh->{f_dir} parameter (similar to in DBD::CSV).  This
 parameter may be specified in the connect() statement as part of the
 DSN, or may be changed later with $dbh->{f_dir} = $directory, at any
 point later.  If no f_dir is specified, the current working directory
 of the script will be assumed.

CSV FILES

 This works similarly to DBD::CSV (which you may want to use instead
 if you are only working with CSV files).  It supports specifying the
 column names either in the catalog statement, or as the first line of
 the file.  It does not yet support delimiters other than commas or
 end of file characters other than newlines.

FIXED WIDTH RECORD FILES

 Column names may be specified on the first line of the file (as a
 comma separated list), or in the catalog.  A pattern must be specified
 listing the widths of the fields in the catalog.  The pattern should
 be in Perl unpack format e.g. "a2 a7 a14" would indicate a table with
 three columns with widths of 2,7,14 characters.  When data is inserted
 or updated, it will be truncated or padded to fill exactly the amount
 of space alloted to each field.

NAME=VALUE INI FILES

 Column names may be specified on the first line of the file (as a
 comma separated list), or in the catalog.

XML FILES

 Column names *must* be specified in the catalog.  Currently this module
 does not provide full support for XML files.  The feature is included
 here as a "proof of concept" experiment and will be made more robust in
 future releases.  Only a limited subset of XML is currently supported:
 files can contain tags only as specified in the catalog columns list and
 the tags must be in the same order as that list. All tags for a given
 record must occur on the same line.  The parsing routine for the tags
 is very simple minded in this release and is probably easily broken.
 In future releases, XML::Parser will be required and will replace the
 regular expression currently used.

 Here is a sample XML file that would currently work with this module:

    <name>jeff</name><state>oregon</state>
    <name>joe</name><state>new york</state>

USING MULTIPLE TABLES

 A single script can create as many tables as your RAM will support and you
 can have multiple statement handles open to the tables simultaneously. This
 allows you to simulate joins and multi-table operations by iterating over
 several statement handles at once.

TO DO

 Lots of stuff.  An export() method -- dumping the data from in-memory
 tables back into files.  More robust support for XML files.  Support for
 a variety of other easily parsed formats such as Mail files, web logs.
 Support for HTML files with the directory considered as a table, each
 HTML file considered as a record and the filename, <TITLE> tag, and
 <BODY> tags considered as fields.

 Let me know what else...

AUTHOR

 Jeff Zucker <jeff@vpservices.com>

     Copyright (c) 2000 Jeff Zucker. All rights reserved. This program is
     free software; you can redistribute it and/or modify it under the same
     terms as Perl itself as specified in the Perl README file.

     This is alpha software, no warranty of any kind is implied.

SEE ALSO

 DBI, DBD::CSV, SQL::Statement