Recital

Login Register
Recital 10 enhances the way updating tables with indexes is done. The Recital engine will now evaluate each index individually to determine if any changes being made to the row effect it. If this is not the case then the index is flagged not to be updated. This enhancement helps speed up performance of updates and is particularly noticeable in cluster/network installations.
Published in Blogs
Read more...

The Openfiler NAS/SAN Appliance (NSA) is a Storage Management Operating System / NAS Appliance distribution. It is powered by the Linux 2.6 kernel and Open Source applications such as Apache, Samba, LVM2, ext3, Linux NFS and iSCSI Enterprise Target. Openfiler combines these ubiquitous technologies into a small, easy to manage solution fronted by a powerful web-based management interface. Openfiler allows you to build a Network Attached Storage (NAS) and/or Storage Area Network (SAN) appliance, using industry-standard hardware, in less than 10 minutes of installation time.

Building upon the popularity of server virtualization technologies such as VMware, Virtual Iron, and Xen, Openfiler can also be deployed as a virtual machine instance or on  a bare metal machine.

This deployment flexibility of Openfiler ensures that storage administrators are able to make the best use of system performance and storage capacity resources when allocating and managing networked storage in a multi-platform environment.

Openfiler is ideally suited for use with High Availability Recital applications as it incorporates:

  • Heartbeat cluster manager
  • drbd disk replication
  • CIFS
  • NFS
  • Software and hardware RAID
  • FTP
  • rsync
  • HTTP/DAV
  • iSCSI
  • LVM2
  • Multiple NIC bonding for High Availability
  • Powerful web-based GUI

 

Published in Blogs
Read more...

In this article Barry Mavin, CEO and Chief Software Architect for Recital details how to Build C Extension Libraries to use with Recital.

Overview

It is possible to extend the functionaliy of Recital products using "Extension libraries" that can be written in C. These extension libraries, written using the Recital/SDK API, are dynamically loadable from all Recital 9 products. This includes:

  • Recital
  • Recital Server
  • Recital Web

Building C Extension Libraries

You can create C wrappers for virtually any native operating system function and access these from the Recital 4GL. Unlike traditional APIs which only handle the development of C functions that are callable from the 4GL, the Recital/SDK allows you to build Classes that are accessible from all Recital products. e.g. You could create a GUI framework for Linux that handles VFP system classes!

To deploy your C Extension Libraries, copy them to the following location:

Windows:

\Program Files\Recital\extensions

Linux/Unix:

/opt/recital/extensions

Please see the Recital/SDK API Reference documentation for further details.

Sample code

Listed below is the complete example of a C Extension Library.:

////////////////////////////////////////////////////////////////////////////////
#include "mirage_demo.h"  
      
////////////////////////////////////////////////////////////////////////////////
// Declare your functions and classes below as follows:
//
//    Recital Function Name, C Function Name, Type (Function or Class)
//
#define MAX_ELEMENTS    7
static  struct  API_SHARED_FUNCTION_TABLE api_function_table[MAX_ELEMENTS] = {
        {"schar",   "fnSamplesCharacter",   API_FUNCTION},
        {"stype",   "fnSamplesType",           API_FUNCTION},
        {"slog",    "fnSamplesLogical",        API_FUNCTION},
        {"snum",    "fnSamplesNumeric",    API_FUNCTION},
        {"sopen",   "fnSamplesOpen",         API_FUNCTION},
        {"myclass", "clsMyClass",               API_CLASS},
        {NULL,      NULL,                   -1}
};

////////////////////////////////////////////////////////////////////////////////
// Recital API initialization. This should be in only ONE of your C files
// **IT SHOULD NEVER BE EDITED OR REMOVED**  
INIT_API;


///////////////////////////////////////////////////////////////////////
// This is an example of passing a character parameter and returning one.
RECITAL_FUNCTION fnSamplesCharacter(void)
{
    char    *arg1;
    
    if (!_parse_parameters(PCOUNT, "C", &arg1)) { 
        ERROR(-1, "Incorrect parameters");
    }
    
    _retc(arg1);
}


///////////////////////////////////////////////////////////////////////
// This is an example of passing a numeric parameter and returning one.
RECITAL_FUNCTION fnSamplesNumeric(void)
{
    int arg1;
    
    if (!_parse_parameters(PCOUNT, "N", &arg1)) { 
        ERROR(-1, "Incorrect parameters");
    }
    
    _retni(arg1);
}


///////////////////////////////////////////////////////////////////////
// This is an example returns the data type of the parameter passed.
RECITAL_FUNCTION fnSamplesType(void)
{
    char    result[10];

    if (PCOUNT != 1) { 
        ERROR(-1, "Incorrect parameters");
    }
    
    switch (_parinfo(1)) {
        case API_CTYPE:
            strcpy(result, "Character");
            break;
        case API_NTYPE:
            strcpy(result, "Numeric");
            break;
        case API_LTYPE:
            strcpy(result, "Logical");
            break;
        case API_DTYPE:
            strcpy(result, "Date");
            break;
        case API_TTYPE:
            strcpy(result, "DateTime");
            break;
        case API_YTYPE:
            strcpy(result, "Currency");
            break;
        case API_ATYPE:
            strcpy(result, "Array");
            break;
        default:
            strcpy(result, "Unkown");
            break;
    }

    _retc(result);
}


///////////////////////////////////////////////////////////////////////
// This is an example returns "True" or False.
RECITAL_FUNCTION  fnSamplesLogical(void)
{
    char    result[10];
    int     arg1;
    
    if (!_parse_parameters(PCOUNT, "L", &arg1)) { 
        ERROR(-1, "Incorrect parameters");
    }
    
    if (arg1) strcpy(result, "True");
    else strcpy(result, "False");

    _retc(result);
}


///////////////////////////////////////////////////////////////////////
// This example opens a table.
RECITAL_FUNCTION fnSamplesOpen(void)
{
    char    *arg1;
    
    if (!_parse_parameters(PCOUNT, "C", &arg1)) { 
        ERROR(-1, "Incorrect parameters");
    }
    
    if (_parinfo(1) == API_CTYPE) {
        _retni(COMMAND(arg1));
    } else {
        _retni(-1);
    }
}

///////////////////////////////////////////////////////////////////////
// Define the MyClass CLASS using the API macros
///////////////////////////////////////////////////////////////////////
RECITAL_EXPORT int DEFINE_CLASS(clsMyClass)
{
    /*-------------------------------------*/ 
    /* Dispatch factory methods and return */
    /*-------------------------------------*/
    DISPATCH_FACTORY();
 
    /*---------------------------------*/
    /* Dispatch constructor and return */ 
    /*---------------------------------*/ 
    DISPATCH_METHOD(clsMyClass, Constructor);

    /*--------------------------------*/
    /* Dispatch destructor and return */
    /*--------------------------------*/
    DISPATCH_METHOD(clsMyClass, Destructor); 

    /*-----------------------------------*/
    /* Dispatch DEFINE method and return */
    /*-----------------------------------*/
    DISPATCH_METHOD(clsMyClass, Define);

    /*------------------------------*/
    /* Dispatch SET or GET PROPERTY */
    /* method for property NumValue */
    /* then return.                 */
    /*------------------------------*/
    DISPATCH_PROPSET(clsMyClass, NumValue);
    DISPATCH_PROPGET(clsMyClass, NumValue); 

    /*------------------------------*/
    /* Dispatch SET or GET PROPERTY */
    /* method for property LogValue */
    /* then return.                 */
    /*------------------------------*/
    DISPATCH_PROPSET(clsMyClass, LogValue);
    DISPATCH_PROPGET(clsMyClass, LogValue);

    /*-------------------------------*/
    /* Dispatch SET or GET PROPERTY  */
    /* method for property DateValue */
    /* then return.                 */
    /*-------------------------------*/
    DISPATCH_PROPSET(clsMyClass, DateValue);
    DISPATCH_PROPGET(clsMyClass, DateValue);

    /*-------------------------------*/
    /* Dispatch SET or GET PROPERTY  */
    /* method for property TimeValue */
    /* then return.                  */
    /*-------------------------------*/
    DISPATCH_PROPSET(clsMyClass, TimeValue);
    DISPATCH_PROPGET(clsMyClass, TimeValue);
 
    /*-------------------------------*/
    /* Dispatch SET or GET PROPERTY  */
    /* method for property CurrValue */
    /* then return.                  */
    /*-------------------------------*/
    DISPATCH_PROPSET(clsMyClass, CurrValue);
    DISPATCH_PROPGET(clsMyClass, CurrValue);

    /*-------------------------------*/
    /* Dispatch SET or GET PROPERTY  */
    /* method for property CharValue */
    /* then return.                  */
    /*-------------------------------*/
    DISPATCH_PROPSET(clsMyClass, CharValue); 
    DISPATCH_PROPGET(clsMyClass, CharValue);

    /*------------------------------*/
    /* Dispatch SET or GET PROPERTY */ 
    /* method for property ObjValue */ 
    /* then return.                 */
    /*------------------------------*/
    DISPATCH_PROPSET(clsMyClass, ObjValue);
    DISPATCH_PROPGET(clsMyClass, ObjValue);

    /*-----------------------------------*/
    /* If message not found return error */
    /*-----------------------------------*/
    OBJECT_RETERROR("Unknown message type");
}

////////////////////////////////////////////////////////////////////////////////
// Define METHOD handlers
////////////////////////////////////////////////////////////////////////////////
DEFINE_METHOD(clsMyClass, Constructor) 
{
        struct example_data *objectDataArea;

        /* Allocate memory for objects objectData area */
        objectDataArea = (struct example_data *) 
           malloc(sizeof(struct example_data));
        if (objectDataArea == NULL) return(-1);
    
        /* Assign the default property values */
        strcpy(objectDataArea->prop_charvalue, "Test API object");
        objectDataArea->prop_numvalue = 15.2827;
        objectDataArea->prop_logvalue = 'F';
        strcpy(objectDataArea->prop_datevalue, DATE_DATE());
        strcpy(objectDataArea->prop_timevalue, DATE_DATETIME());
        strcpy(objectDataArea->prop_currvalue, "15.2827");
        strcpy(objectDataArea->object_name, "APIobject");
        objectDataArea->prop_objvalue 
             = OBJECT_NEW(objectDataArea->object_name, "exception", NULL);
    
        /* Set the object objectData area */
        OBJECT_SETDATA((char *)objectDataArea);
    
        return(0);
}
  
DEFINE_METHOD(clsMyClass, Destructor) 
{
        struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();

        if (objectData != NULL) {
            if (objectData->prop_objvalue != NULL) 
              OBJECT_DELETE(objectData->prop_objvalue);
            free(objectData);
            objectData = NULL;
        }
        return(0);
}

DEFINE_METHOD(clsMyClass, Define) 
{
    struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
    struct      API_EXPRESSION result;
    char        buffer[512];
    int         rc;

/* Check the object class */
    OBJECT_GETPROPERTY(objectData->prop_objvalue, "class", buffer);
    rc = OBJECT_GETARG(buffer, &result);
    if (result.errno == 0 && result.type == 'C' 
        && strcmp(result.character, "Exception") == 0) { 
        switch (OBJECT_GETARGC()) {
            case 1: 
                rc = OBJECT_GETPARAMETER(1, &result);
                if (result.errno == 0 && result.type == 'C') {
                    OBJECT_SETARG(buffer, &result);
                    rc = OBJECT_SETPROPERTY(objectData->prop_objvalue,
                         "message", buffer);
                }
                break;
            case 2: 
                rc = OBJECT_GETPARAMETER(2, &result);
                if (result.errno == 0 && result.type == 'N') {
                    OBJECT_SETARG(buffer, &result);
                    rc = OBJECT_SETPROPERTY(objectData->prop_objvalue,
                         "errorno", buffer);
                }
         }
    }

    result.type = 'L';
    result.logical = (rc == 0 ? 'T' : 'F');  
    OBJECT_RETRESULT(&result);
}

////////////////////////////////////////////////////////////////////////////////
// Define GET property handlers
////////////////////////////////////////////////////////////////////////////////
DEFINE_PROPERTYGET(clsMyClass, NumValue) 
{
    struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();

    if (objectData == NULL) return(-1);

    OBJECT_RETPROPERTY('N', objectData->prop_numvalue);
}

DEFINE_PROPERTYGET(clsMyClass, LogValue) 
{
    struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();

    if (objectData == NULL) return(-1);

    OBJECT_RETPROPERTY('L', objectData->prop_logvalue);
}  

DEFINE_PROPERTYGET(clsMyClass, DateValue) 
{
    struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();

    if (objectData == NULL) return(-1);

    OBJECT_RETPROPERTY('D', objectData->prop_datevalue);
}

DEFINE_PROPERTYGET(clsMyClass, TimeValue) 
{
    struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();

    if (objectData == NULL) return(-1);

    OBJECT_RETPROPERTY('T', objectData->prop_timevalue);
}

DEFINE_PROPERTYGET(clsMyClass, CurrValue) 
{
    struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();

    if (objectData == NULL) return(-1);

    OBJECT_RETPROPERTY('Y', objectData->prop_currvalue);
}

DEFINE_PROPERTYGET(clsMyClass, CharValue) 
{
    struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();

    if (objectData == NULL) return(-1);

    OBJECT_RETPROPERTY('C', objectData->prop_charvalue);
}

DEFINE_PROPERTYGET(clsMyClass, ObjValue) 
{
    struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();

    if (objectData == NULL) return(-1);

    OBJECT_RETPROPERTY('O', objectData->prop_objvalue);
} 


////////////////////////////////////////////////////////////////////////////////
// Define SET property handlers
////////////////////////////////////////////////////////////////////////////////
DEFINE_PROPERTYSET(clsMyClass, NumValue) 
{
    struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
    struct API_EXPRESSION result;
    int rc = OBJECT_ERROR;

    OBJECT_GETVALUE(&result);
    if (result.errno == 0 && result.type == 'N') {
        objectData->prop_numvalue = result.number;
        rc = OBJECT_SUCCESS;
    }

    return(rc);
}

DEFINE_PROPERTYSET(clsMyClass, LogValue) 
{
    struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
    struct API_EXPRESSION result;
    int rc = OBJECT_ERROR;

    OBJECT_GETVALUE(&result);
    if (result.errno == 0 && result.type == 'L') {
        objectData->prop_logvalue = result.logical;
        rc = OBJECT_SUCCESS;
    }

    return(rc);
}

DEFINE_PROPERTYSET(clsMyClass, DateValue) 
{
    struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
    struct API_EXPRESSION result;
    int rc = OBJECT_ERROR;
    OBJECT_GETVALUE(&result);
    if (result.errno == 0 && result.type == 'D') {
        strcpy(objectData->prop_datevalue, DATE_DTOS(result.date));
        rc = OBJECT_SUCCESS;
    }

    return(rc);
}

DEFINE_PROPERTYSET(clsMyClass, TimeValue) 
{
    struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
    struct API_EXPRESSION result;
    int rc = OBJECT_ERROR;

    OBJECT_GETVALUE(&result);
    if (result.errno == 0 && result.type == 'T') {
        strcpy(objectData->prop_timevalue, DATE_TTOS(result.datetime));
        rc = OBJECT_SUCCESS;
    }

    return(rc); 
}

DEFINE_PROPERTYSET(clsMyClass, CurrValue) 
{
    struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
    struct API_EXPRESSION result;
    int rc = OBJECT_ERROR;

    OBJECT_GETVALUE(&result);
    if (result.errno == 0 && result.type == 'Y') {
        strcpy(objectData->prop_currvalue, CURR_YTOS(result.currency));
        rc = OBJECT_SUCCESS;
    }

    return(rc);
}

DEFINE_PROPERTYSET(clsMyClass, CharValue)  
{
    struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
    struct API_EXPRESSION result;
    int rc = OBJECT_ERROR;

    OBJECT_GETVALUE(&result);
    if (result.errno == 0 && result.type == 'C') {
        strcpy(objectData->prop_currvalue, result.character);
        rc = OBJECT_SUCCESS;
    }

    return(rc);
}

DEFINE_PROPERTYSET(clsMyClass, ObjValue) 
{
    struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
    OBJECT  objvalue;
    int rc = OBJECT_ERROR;

    if (OBJECT_GETTYPE() == 'O') {
        objvalue = OBJECT_GETOBJECT(); 
        objectData->prop_objvalue = OBJECT_ASSIGN(objvalue, objectData->object_name);
        rc = OBJECT_SUCCESS;
    }

    return(rc);
}
Published in Blogs
Read more...

Recital is a dynamic programming language with an integrated high performance database particularly well suited for the development and deployment of high transaction throughput applications.  Recital 10 further enhances Recital with extensive features and functionality to facilitate its use in fault tolerant high availability systems. Much of the development of Recital 10 was concentrated on performance optimizations and cluster aware functionality to provide an application platform that can be scaled as needed without any application changes. 

Key features of Recital 10 include:

  • Cluster aware database engine that works transparently with drbd, heartbeat, glusterfs and samba
  • High degree of fault tolerance with self healing indexes
  • Massive performance improvements 
  • Extensive internals overall and modernization with superior object-oriented capabilities
  • Chronological data versioning with database timelines
  • SmartQuery caching
  • Database Administration Tools
  • Code and Data Profiling
  • Better integration with unix/linux command shell
  • Incorporates a range of new built-in functions compatible with those in the PHP core libraries
  • Built-in support for outputting data in HTML, XML, and JSON format
  • Seamless SQL command integration into the Recital scripting language
  • Much improved Microsoft FoxPRO language compatibility
  • Numerous extensions and improvements (see below for details)
  • Very large file support (2^63)
Published in Blogs
Read more...
This link provides a decent covering of this topic.
http://www.the-art-of-web.com/css/border-radius/
Published in Blogs
Read more...
Use iptables to restrict access to Recital Web only from localhost.

iptables -I INPUT -j ACCEPT -p tcp --destination-port 8001 -i lo
iptables -A INPUT -j DROP -p tcp --destination-port 8001 -i eth0


Published in Blogs
Read more...
The SET RELATION Recital Navigational Data Command can be used to link two (or more) tables based on the master index key of the child table.  With a relation active, as you move through the parent table, the record pointer also moves in the child table, automatically selecting the first related record or moving to the end of file if no related record exists.    

open database southwind
// open child table
use order_details order orderid in 0
// open parent table
use orders order orderid in 0
set relation to orderid into order_details
do while not eof()
? orders.orderid, order_details.productid
skip
enddo

The code above will display the productid from the first related record, but you will often want to display information from all the related records in the child or detail table as in an SQL Left Outer Join.

open database southwind
select orders.orderid, order_details.productid;
from orders left outer join order_details;
on orders.orderid = order_details.orderid

In this case, we can use a second nested DO WHILE loop, for example:

open database southwind
use order_details order orderid in 0
use orders order orderid in 0
set relation to orderid into order_details
do while not eof()
// Display first or 0 child record
? orders.orderid, order_details.productid
// Display any additional child records
do while not eof(order_details)
? orders.orderid, order_details.productid
skip in order_details
enddo
skip
enddo

Or we can use the SET SKIP command.  The SET SKIP command can be used with DISPLAY, LIST and REPORT and automatically skips through all the related records in the child table.

open database southwind
use order_details order orderid in 0
use orders order orderid in 0
set relation to orderid into order_details
set skip on
set skip to order_details
list orders.orderid, order_details.productid

LIST and DISPLAY offer a number of keyword options to allow you to configure the display output.  REPORT offers full column based report design.
Published in Blogs
Read more...
When using Recital Web you can maintain the exact state of each work area between pages like this.

On exit of an .rsp page.

SAVE DATASESSION TO m_state 
_SESSION["state"] = m_state

On entry to an .rsp page.

IF type( _session["state"] ) != "U" 
    m_state = _session["state"]
    RESTORE DATASESSION FROM m_state
ENDIF
Published in Blogs
Read more...

Opening SSH to the outside world is a security risk. Here is how to restrict SSH access to certain IP addresses on a machine.

  1. Edit the /etc/hosts.allow file to include these lines, assuming your machine is on the 192.168.2.x nonrouting IP block, and you want to enable an external address of 217.40.111.121 IP block: Remember to add the period on the end of each incomplete IP number. If you have another complete IP address or range, add a space and that range on the end.

    sshd,sshdfwd-X11: 192.168.2. 217.40.111.121
  2. Edit your /etc/hosts.deny file to include this line:

    sshd,sshdfwd-X11:ALL 
  3. These lines refuse SSH connections from anyone not in the IP address blocks listed.

Additionally you can restrict SSH access by username.

  1. Edit the /etc/ssh/sshd_config file and add the following lines

    PermitRootLogin no
    AllowUsers      user1 user2 user3 etc
    PasswordAuthentication yes

Now restart the ssh daemon for these changes to take effect

service sshd restart




Published in Blogs
Read more...
Recital 10 enhances the APPEND FROM command. The enhancement was added to the following syntax ;
APPEND FROM  <table-name> 
Before when appending into a shared Recital table each new row was locked along with the table header, then unlocked after it was inserted. This operation has now been enhanced to lock the table once, complete inserting all the rows from the table and then unlock the table. The performance of this operation has been increased by using this method. All the database and table constraints are still enforced.
Published in Blogs
Read more...

Copyright © 2025 Recital Software Inc.

Login

Register

User Registration
or Cancel