Muldis Data Systems
Muldis D Example - CD
OUTDATED

This example code demonstrates a very basic CD database, which consists of a schema definition having relvars to store data and routines to fetch or update that data, and it includes some sample data.

This schema definition, routines, and sample data are heavily based on the contents of DBIx::Class::Manual::Example; they are essentially the same except for optimizations towards perceived better practice for Muldis D.

The database consists of the following:

    - relvar 'artists' with attributes: artist_id, artist_name
    - relvar 'cds'     with attributes: cd_id, artist_id, cd_title
    - relvar 'tracks'  with attributes: track_id, cd_id, track_title

And these rules exist:

    - one artist can have many cds
    - one cd belongs to one artist
    - one cd can have many tracks
    - one track belongs to one cd

In an effort to keep this example more realistic, the code is divided into 4 files:

EXAMPLE CODE

CD_Lib.mdpt

    Muldis_D:PT_STD:ASCII:"http://muldis.com":0;

    package CD_Lib ::= CD_Lib:"http://example.com":0
    {
        using Muldis_D ::= Muldis_D:"http://muldis.com":0;

        searching [Muldis_D,CD_Lib];

    /*************************************************************************/

    "" ::= database_type
    {
        artists : artists;
        cds     : cds;
        tracks  : tracks;
        constraint sc_artist_has_cds;
        constraint sc_cd_has_tracks;
    };

    /*************************************************************************/

    artists ::= relation_type
    {
        artist_id   : Integer;
        artist_name : Text;
        primary_key {artist_id};
        unique_key {artist_name};
    };

    /*************************************************************************/

    cds ::= relation_type
    {
        cd_id     : Integer;
        artist_id : Integer;
        cd_title  : Text;
        primary_key {cd_id};
        unique_key {cd_title};
    };

    sc_artist_has_cds ::= function (Boolean <-- 0|db : "") :
    (
        .db.cds^$artist_id subset_of .db.artists^$artist_id;
    );

    /*************************************************************************/

    tracks ::= relation_type
    {
        track_id    : Integer;
        cd_id       : Integer;
        track_title : Text;
        primary_key {track_id};
        unique_key {track_title};
    };

    sc_cd_has_tracks ::= function (Boolean <-- 0|db : "") :
    (
        .db.tracks^$cd_id subset_of .db.cds^$cd_id;
    );

    /*************************************************************************/

    };
    /* package CD_Lib */

cd_data.mdpt

    Muldis_D:PT_STD:ASCII:"http://muldis.com":0;

    depot_catalog
    {
        using Muldis_D ::= Muldis_D:"http://muldis.com":0;
        using CD_Lib ::= CD_Lib:"http://example.com":0;

        searching [Muldis_D,CD_Lib];

        depot_data_type CD_Lib."";
    };

    depot_data
    :%{
        artists : :@{ artist_id, artist_name },
        cds     : :@{ cd_id, artist_id, cd_title },
        tracks  : :@{ track_id, cd_id, track_title },
    };

CD_Insert.mdpt

    Muldis_D:PT_STD:ASCII:"http://muldis.com":0;

    package CD_Install ::= CD_Test:"http://example.com":0
    {
        using Muldis_D ::= Muldis_D:"http://muldis.com":0;
        using CD_Lib ::= CD_Lib:"http://example.com":0;

        searching [Muldis_D,CD_Install,CD_Lib];

        bootstrap ::= stimulus_response_rule
            when loaded invoke main;

    /*************************************************************************/

    main ::= procedure () :
    [
        /* Connect to database "cd_data" in read-write mode.
           The "..." has likes of filename, server name, user, pass, etc. */
        create_depot_mount( name : $db, we_may_update : :$True, details : :@{...} );

        /* Add test data to database "cd_data", as a single atomic transaction.
           This might fail if records with same titles/etc already there. */
        try
            insert_records()
        catch
            insert_failed();

        /* Disconnect database "cd_data" now that we're done with it. */
        drop_depot_mount( name : $db );
    ];

    insert_failed ::= procedure (err : Exception) :
    [
        write_Text_line( 'Failed to insert, maybe because of duplicates.' );
    ];

    /*************************************************************************/

    insert_records ::= recipe () {
        /* We will generate new integer record ids serially starting after
           the maximum ids currently in use per relvar.
           There are, of course, other ways to produce record ids. */
        db ::= .$db;
        src ::= NEW_RECS();

        /* So start by determining the existing maximum ids. */
        max_used_artist_id ::= ?#db.artists ?? [`max`] db.artists^?$artist_id !! 0;
        max_used_cd_id     ::= ?#db.cds     ?? [`max`] db.cds^?$cd_id         !! 0;
        max_used_track_id  ::= ?#db.tracks  ?? [`max`] db.tracks^?$track_id   !! 0;

        /* Now generate new record ids and attach them to records to add.
           The process involves sorting of the new data simply to make it
           fully-deterministic, giving same result on any implementation. */
        src_artists_with_ids ::= rank( src.artists, add : $artist_id,
            by : $artist_name, first : ++max_used_artist_id );
        src_cds_with_ids ::= rank( src.cds, add : $cd_id,
            by : $cd_title, first : ++max_used_cd_id );
        src_tracks_with_ids ::= rank( src.tracks, add : $track_id,
            by : $track_title, first : ++max_used_track_id );

        /* Now substitute parent record ids for parent names or titles. */
        new_artists ::= src_artists_with_ids;
        new_cds     ::= src_cds_with_ids compose new_artists;
        new_tracks  ::= src_tracks_with_ids compose new_cds^-$artist_id;

        /* Now insert the prepared new records into database "cd_data". */
        db.artists :=union new_artists;
        db.cds     :=union new_cds;
        db.tracks  :=union new_tracks;
    };

    /*************************************************************************/

    NEW_RECS ::= constant
    :%{
        artists : :@[ artist_name ] :
        {
            [ 'Michael Jackson' ],
            [ 'Eminem'          ],
        },
        cds : :@[ cd_title, artist_name ] :
        {
            [ 'Thriller'               , 'Michael Jackson' ],
            [ 'Bad'                    , 'Michael Jackson' ],
            [ 'The Marshall Mathers LP', 'Eminem'          ],
        },
        tracks : :@[ track_title, cd_title ] :
        {
            [ 'Beat It'        , 'Thriller'                ],
            [ 'Billie Jean'    , 'Thriller'                ],
            [ 'Dirty Diana'    , 'Bad'                     ],
            [ 'Smooth Criminal', 'Bad'                     ],
            [ 'Leave Me Alone' , 'Bad'                     ],
            [ 'Stan'           , 'The Marshall Mathers LP' ],
            [ 'The Way I Am'   , 'The Marshall Mathers LP' ],
        },
    };

    /*************************************************************************/

    };
    /* package CD_Install */

CD_Test.mdpt

    Muldis_D:PT_STD:ASCII:"http://muldis.com":0;

    package CD_Test ::= CD_Test:"http://example.com":0
    {
        using Muldis_D ::= Muldis_D:"http://muldis.com":0;
        using CD_Lib ::= CD_Lib:"http://example.com":0;

        searching [Muldis_D,CD_Test,CD_Lib];

        bootstrap ::= stimulus_response_rule
            when loaded invoke main;

    /*************************************************************************/

    main ::= procedure () :
    [
        /* Connect to database "cd_data" in read-only mode.
           The "..." has likes of filename, server name, user, pass, etc. */
        create_depot_mount( name : $db, details : :@{...} );

        /* Fetch test data, in a transaction for read-consistency. */
        get_records();

        /* Disconnect database "cd_data" now that we're done with it. */
        drop_depot_mount( name : $db );
    ];

    /*************************************************************************/

    get_records ::= transaction () :
    [
        get_tracks_by_cd( cd_title : 'Bad' );
        get_tracks_by_artist( artist_name : 'Michael Jackson' );

        get_cd_by_track( track_title : 'Stan' );
        get_cds_by_artist( artist_name : 'Michael Jackson' );

        get_artist_by_track( track_title : 'Dirty Diana' );
        get_artist_by_cd( cd_title : 'The Marshall Mathers LP' );
    ];

    /*************************************************************************/

    disp_list ::= procedure (h1 : Text, h2 : Text, list : set of Text) :
    [
        var list_item : Text;
        write_Text_line( h1 ~ ' (' ~ h2 ~ '):' );
        for list_item in list
            write_Text_line( list_item );
        write_Text_line();
    ];

    disp_item ::= procedure (h1 : Text, h2 : Text, item : maybe of Text) :
    [
        write_Text_line( h1 ~ ' (' ~ h2 ~ '):' );
        write_Text_line( item // '' );
        write_Text_line();
    ];

    /*************************************************************************/

    get_tracks_by_cd ::= procedure (cd_title : Text) :
    [
        var track_titles : set of Text;
        track_titles := (.$db.tracks matching
            (.$db.cds matching :%{:cd_title}))^$track_title;
        disp_list( h1 : 'get_tracks_by_cd', h2 : cd_title, list : track_titles );
    ];

    get_tracks_by_artist ::= procedure (artist_name : Text) :
    [
        var track_titles : set of Text;
        track_titles := (.$db.tracks matching (.$db.cds matching
            (.$db.artists matching :%{:artist_name})))^?$track_title;
        disp_list( h1 : 'get_tracks_by_artist', h2 : artist_name, list : track_titles );
    ];

    /*************************************************************************/

    get_cd_by_track ::= procedure (track_title : Text) :
    [
        var cd_title : maybe of Text;
        cd_title := (.$db.cds matching (.$db.tracks matching
            :%{:track_title}))^?$cd_title;
        disp_item( h1 : 'get_cd_by_track', h2 : track_title, item : cd_title );
    ];

    get_cds_by_artist ::= procedure (artist_name : Text) :
    [
        var cd_titles : set of Text;
        cd_titles := (.$db.cds matching (.$db.artists matching
            :%{:artist_name}))^?$cd_title;
        disp_list( h1 : 'get_cds_by_artist', h2 : artist_name, list : cd_titles );
    ];

    /*************************************************************************/

    get_artist_by_track ::= procedure (track_title : Text) :
    [
        var artist_name : maybe of Text;
        artist_name := (.$db.artists matching (.$db.cds matching
            (.$db.tracks matching :%{:track_title})))^?$artist_name;
        disp_item( h1 : 'get_artist_by_track', h2 : track_title, item : artist_name );
    ];

    get_artist_by_cd ::= procedure (cd_title : Text) :
    [
        var artist_name : maybe of Text;
        artist_name := (.$db.artists matching
            (.$db.cds matching :%{:cd_title}))^?$artist_name;
        disp_item( h1 : 'get_artist_by_cd', h2 : cd_title, item : artist_name );
    ];

    /*************************************************************************/

    };
    /* package CD_Test */

EXPECTED OUTPUT

The example should have this output on STDOUT, but perhaps for the exact output order of the list items, which is:

    get_tracks_by_cd(Bad):
    Dirty Diana
    Leave Me Alone
    Smooth Criminal

    get_tracks_by_artist(Michael Jackson):
    Beat it
    Billie Jean
    Dirty Diana
    Leave Me Alone
    Smooth Criminal

    get_cd_by_track(Stan):
    The Marshall Mathers LP

    get_cds_by_artist(Michael Jackson):
    Bad
    Thriller

    get_artist_by_track(Dirty Diana):
    Michael Jackson

    get_artist_by_cd(The Marshall Mathers LP):
    Eminem

AUTHOR

The original document that this current document was translated from, DBIx::Class::Manual::Example, cites as its authors:

sc_ from irc.perl.org#dbix-class
Kieren Diment <kd@totaldatasolution.com>
Nigel Metheringham <nigelm@cpan.org>

This current document was written/derived by:

Darren Duncan <darren@DarrenDuncan.net>

Copyright © 2007-2012, Muldis Data Systems, Inc.
http://www.muldis.com/
MULDIS and MULDIS MULTIVERSE OF DISCOURSE are trademarks of Muldis Data Systems, Inc.

Muldis Data Systems is not affiliated with Muldis Consultants & Engineers (http://www.muldis.nl/), which has its own distinct MULDIS trademark.