You can share StreamBase query tables across modules in a single application. Use either EventFlow or StreamSQL to specify references to a module that holds the table to be shared.
The following sections describe the development of shared query tables within applications.
When sharing query tables within an application, the typical usage model is for one module to hold the actual query table and for other modules to hold a placeholder for the actual query table. Each of the placeholder tables must have exactly the same schema as the actual query table. On the Table Settings tab in EventFlow you specify the actual query table as Shared, and you specify the placeholder table as Placeholder.
You can specify a table to be shared within a multilevel application in the following kinds of modules:
-
The outer module
-
One or more inner modules, that is, modules referred to by the outer module
When a shared query table is held by an inner module, you must complete development of the inner module before you can develop the outer module. That is, the outer module can refer to an inner module holding a shared query table only if the inner module is a functioning, standalone application.
Similarly, an inner module can refer to an outer module holding a shared query table only if the outer module is a functioning, standalone application. You must create, test, and debug the inner and outer modules independently. Then you can complete the outer module by adding the reference to the inner module.
To use EventFlow Editor to specify query tables shared within an application, use module reference operators and the Table Settings tab in the Properties view of the tables. See Using EventFlow to Share Query Tables Within an Application for more information.
In StreamSQL use the APPLY MODULE and CREATE TABLE statements. See Using StreamSQL to Share Tables Within an Application for more information.
This section describes the following scenarios for sharing query tables in an application:
- Shared Table in Inner Module
-
The shared query table exists in an inner module. The outer module has access to the data in the actual query table by means of a query operator in the outer module that is associated with the placeholder, which is also in the outer module.
- Shared Table in Outer Module
-
The shared query table exists in the outer module. A query operator in an inner module is associated with a placeholder in the same inner module.
When you develop each query table you must indicate whether it is to be shared across modules. You can run the modules that share a table independently and the shared tables behave normally.
Note
The rest of this section describes in detail the steps for each scenario using EventFlow Editor. See Using StreamSQL to Share Query Tables Within an Application to develop the same modules using StreamSQL.
In this scenario, the shared table is in the inner module, which must be developed first. When you develop the outer module, you specify a reference to the inner module, which provides access to the data in the shared query table.
The following EventFlow diagram shows a simple application. A query operator (Query1) writes (using the update operation) a query table (QueryTable1) and then emits all input and table fields to the output stream, OS1.
|
To make the query table accessible to other modules within the application, including the outer module:
-
Open the Properties view for QueryTable1.
-
Open the Table Settings tab.
-
Click Shared.
Note that the possible settings are as follows:
-
Clicking Shared specifies that QueryTable1 is to be the actual table holding data and that other tables can have access to it.
-
Clicking Placeholder would indicate that QueryTable1 is not the actual table. Instead it is a table that has access to a shared table. Query operators can act upon the placeholder as if it were the actual table.
-
Clicking Private would make the table available only to the module holding QueryTable1.
The following EventFlow diagram shows the complete outer module.
|
The outer module holds InnerModuleRef1, which is a reference to the inner module, which is already defined and which holds the shared query table, QueryTable1. A metronome operator, at 10 second intervals, initiates a read all rows query (Query2) against QueryTable1, which is the shared query table defined in InnerModuleRef1. Query2 emits tuples containing all table fields to the output stream, OS3.
Note that the data construct port of a query operator is connected to the port of the reference to a module in the same way that a query operator connects to a query table.
To complete the configuration of the Query2 operator in the outer module:
-
Open the Query Settings tab for Query2 in the Properties view.
-
Because the inner module holds only one shared table, QueryTable1, this table is already selected in the Associated Table field.
If, in your application, the inner module holds multiple shared tables, select the table required by the query operator you are defining as the associated table.
In this scenario, both the inner and outer modules must be initially developed as complete, running applications. Then the inner module is referenced in the outer module, so that the query tables in the outer module can be used by the query operators in the inner module.
In the following EventFlow diagram a metronome operator, at 10 second intervals, initiates a read all rows query against QT1 and QT2. Because there are no components in this module that write data into QT1 and QT2, Query5 and Query6 each emit a single tuple with null values in the fields derived from the tables.
|
Another module, that is, an inner module, is designed to be referenced by the outer module to complete this example application and write data into these tables. To accomplish this, specify that QT1 and QT2 in the outer module are shared by selecting Shared in the Table settings tab in the Properties view for each table.
Even though you have not yet developed the inner module, and the inner module is not yet referenced in the application, the outer module can run as a standalone application.
The inner module, which writes data to the tables in the outer module, uses placeholder tables.
The following example shows an application that writes (using the update operation) the same data into two query tables, QueryTable1 and QueryTable2. Each query operator is configured to pass fields from the input stream to its emitted tuple. After writing to the tables, the input data is emitted on the output stream.
|
Because the goal is to substitute the tables from the outer module for the tables in this inner module, the query tables QueryTable1 and QueryTable2 have the same schema and indexing as QT1 and QT2 in the outer module.
To establish the association between the actual tables in the inner module and the tables in the outer module, select Placeholder in the Properties view Table Settings tab of QueryTable1 and QueryTable2.
Because the query operators and query tables are fully defined in the inner module, you can run this module as a standalone application, that is, you can run it without it's being referenced from the outer module. If you run the inner module as a standalone application, the placeholder tables behave as if they are private tables.
The next step is to reference the inner module in the outer module.
To complete the application:
-
Open the application for the outer module while the inner module is still open.
-
Drag and drop the inner module onto the canvas of the outer module.
-
Add an input stream and output stream.
The icon representing the reference to the inner module has a data construct port on the bottom edge, as shown:
This port on the module reference is connected to the data construct port at the top of the query table icon; this connection is similar to the corresponding port on a query operator.
-
Drag an arc from the module reference to each of the shared query tables in the outer module.
At this point the module reference has typecheck errors because StreamBase cannot determine which outer module query table corresponds to each of the inner module query tables.
-
Open the Properties view for InnerModuleRef1.
-
Open the Table Associations tab.
In the listing of table associations, entries QT1 and QT2 are shown in the Application Table column.
-
Select each table in the Module Table column and click the correct table in the Application Table column. For example QueryTable2 should be associated with QT2.
Sharing tables across modules in EventFlow requires multiple steps. At each step, StreamBase Studio attempts to track changes among all the components. During this process, it is possible for shared tables to become unsynchronized during your session, resulting in typecheck errors. This section describes an example of how this problem can occur and how to avoid it.
Here is an example of steps that can cause this problem:
-
Create an inner module like
InnerModule.sbapp, as shown.
This example application holds a query operator and query table to be used as a placeholder.
-
In the Table Settings tab of the Properties view of QueryTable1, set the access level to Placeholder, and save the module as
InnerModule.sbapp. -
Create an outer module,
ShareTableApp.sbapp, which holds a query table with the same schema as QueryTable1, along with an associated query operator. -
Open the Table Settings tab of the Properties view for the new table in the outer module, and set the access level to Private.
-
Integrate the two modules as follows:
-
Drag the inner module from the Modules drawer of the Palette to the canvas of the outer module.
-
Draw any required arcs between components.
-
Connect the bottom port of InnerModuleRef1 to the top port of QueryTable1.
At this point, the module reference icon is red, indicating a typecheck error.
-
-
Click InnerModuleRef1 to open its Properties view. The typecheck error message informs you that the reference refers to an undefined table. This error is to be expected, because you have not finished all the steps to share your table.
-
To share the table, click the Table Associations tab for InnerModuleRef1. The value of the Application Table is None.
-
Instead of None, click the name of the query table in the inner module.
-
Save the outer module.
-
Close the outer module in the EventFlow Editor. Closing the module is important to show the synchronousness problem.
-
The next step to illustrate the problem is to change the established association.
-
Open the inner module, delete the query table (QueryTable1) and its associated query operator (Query1).
-
Reconnect the input stream and the map operator so that the application has no typecheck errors.
-
Save the module.
-
-
Open the outer module. Notice that the module reference is red again. To see why, click the module reference and observe the message in the Typecheck Errors view. It is the same error as previously, about a reference to an undefined table. In this case, unlike the earlier error, the Properties view displays a new message, and a link to resolve the problem:
-
Click Click Here in the Properties view. Notice that in the Table Associations tab, the placeholder table association is removed.
Tables are no longer synchronized if you change a setting in an inner module using EventFlow Editor when the outer module is closed. In this example, StreamBase Studio could have automatically tracked your change to the inner module if you had not closed the outer module in Step 8.
The link shown in Step 12 corrects this kind of error by resynchronizing tables across modules.
This section tells you how to use StreamSQL to develop the applications described in the preceding section about EventFlow. See Using EventFlow to Share Query Tables Within an Application for the same steps using EventFlow.
The overall procedure for writing StreamSQL to share tables across modules is to use CREATE TABLE to create the shared and placeholder tables, and to use APPLY MODULE to integrate the modules and specify the table associations.
In general, when defining shared query tables across modules in StreamSQL you use the same kinds of access level concepts as in EventFlow; there is one difference, which is related to the placeholder access level. See the following list, which shows the access levels for tables specified in EventFlow and the corresponding StreamSQL definitions:
| EventFlow Access Level | StreamSQL | StreamSQL Sample Code |
|---|---|---|
| Private | Specify the name of the table and the schema using the CREATE TABLE statement. |
CREATE TABLE
QueryTable;
|
| Placeholder in the inner module | Specify the name of the table and the schema using CREATE INPUT TABLE. |
CREATE INPUT TABLE
Placeholder_QueryTable_Inner(
..)
;
|
| Placeholder in the outer module |
Specify the name of the table using CREATE INPUT TABLE. NOTE: You can specify just the name of the table without the schema only when defining a placeholder in the outer module. |
CREATE INPUT TABLE
Placeholder_QueryTable_Outer;
|
| Shared | Specify the name of the table and the schema using CREATE OUTPUT TABLE. |
CREATE OUTPUT TABLE
Shared_QueryTable(
..)
;
|
In this StreamSQL example, the actual, or shared, query table exists in an inner module. The outer module has access to the data in the actual query table by means of a placeholder table.
The placeholder table is specified in the outer module using the CREATE INPUT TABLE statement without specifying any schema. (This kind of table, as defined in StreamSQL, is not precisely the same as the kind of placeholder table defined in EventFlow.) The actual query table, that is, the shared table, is specified in the inner module using the CREATE OUTPUT TABLE statement. The outer module holds a reference to the inner module. The module reference is declared using the APPLY MODULE statement, which also specifies that the table in the outer module is a placeholder for the table in the inner module.
In this scenario, you must develop the inner module first. Then, to provide direct access to the shared query table from the outer module, you specify a reference to the inner module from the outer module.
This sample application performs the following operations:
-
Inner module writes data from the input stream to a shared query table.
-
Inner module emits output to OS1, the output stream.
-
Outer module gets input from OS1 and assigns it into the placeholder table.
-
Outer module reads all rows in the placeholder query table every 10 seconds and writes the data to the output stream, OS2.
The following StreamSQL code for the inner module creates the input and output streams, creates the shared query table, and emits to the output stream the data from both the input stream and the shared query table.
-- Inner Module Example 1
CREATE INPUT STREAM InputStream1 (
stock string(4),
volume int,
price int
);
CREATE OUTPUT STREAM OS1;
CREATE OUTPUT MEMORY TABLE QueryTable1 (
sym string(4),
number int,
cost int,
PRIMARY KEY(sym) USING hash
);
REPLACE INTO QueryTable1 (sym, number, cost)
SELECT *
FROM InputStream1
RETURNING InputStream1.stock AS input_stock,
InputStream1.volume AS input_volume,
InputStream1.price AS input_price,
QueryTable1.sym AS table_sym,
QueryTable1.number AS table_number,
QueryTable1.cost AS table_cost
INTO OS1;
The following StreamSQL code specifies the outer module.
-- Outer Module Example 1
CREATE INPUT STREAM InputStream2 (
stock string(4),
volume int,
price int
);
CREATE OUTPUT STREAM OS2;
CREATE OUTPUT STREAM OS3;
CREATE INPUT TABLE InnerModule_QueryTable1;
CREATE METRONOME Metronome1 (time, 10.0);
APPLY MODULE "innerModule.ssql" -- or "innerModule.sbapp"
FROM InputStream1 = InputStream2 INTO OS1 = OS3,
QueryTable1 = InnerModule_QueryTable1;
SELECT InnerModule_QueryTable1.sym
AS table_current_sym,
InnerModule_QueryTable1.number
AS table_current_number,
InnerModule_QueryTable1.cost
AS table_current_cost
FROM Metronome1 OUTER JOIN
InnerModule_QueryTable1
INTO OS2;
The following CREATE INPUT TABLE statement in the outer module creates the placeholder table:
CREATE INPUT TABLE InnerModule_QueryTable1;
The table declaration for the placeholder does not include a description of the table's schema, indexing, or persistence characteristics. Because the shared table has been fully specified in the inner module, its definition cannot be changed from within the outer module.
In the following APPLY MODULE statement, the INTO clause specifies the query table association:
APPLY MODULE "innerModule.ssql" FROM InputStream1 = InputStream2 INTO OS1 = OS3, QueryTable1 = InnerModule_QueryTable1;
Note the order of the entries in the association: the name of a query table in the inner module is on the left and the name of the corresponding query table in the outer module is on the right. This is the same ordering paradigm as the paradigm for the output stream associations—you can think of the shared query table defined in the inner module as equivalent to an output stream from the inner module.
If your design pattern includes an inner module that holds two or more shared query tables, you must specify both of the following in the StreamSQL for the outer module:
-
A separate CREATE INPUT TABLE statement for each placeholder table representing a shared table in the inner module.
-
An INTO clause in the APPLY MODULE statement specifying each query table assignment.
See the following example of the code for an outer module holding two placeholder tables, SharedTables_QueryTable1 and SharedTables_QueryTable2:
CREATE INPUT STREAM IS1 (
stock string(4),
cost int,
volume int
);
CREATE OUTPUT STREAM OS1;
CREATE OUTPUT STREAM OS2;
CREATE OUTPUT STREAM OS3;
CREATE INPUT TABLE SharedTables_QueryTable1;
CREATE INPUT TABLE SharedTables_QueryTable2;
CREATE METRONOME out__Metronome1_1 (time, 10.0);
APPLY MODULE "innerModule.ssql" -- or "innerModule.sbapp"
FROM InputStream1 = IS1
INTO OutputStream1 = OS1,
QueryTable1 = SharedTables_QueryTable1,
QueryTable2 = SharedTables_QueryTable2;
In this scenario, the outer module holds the actual query tables to be shared. The outer module as well as the inner module must each be developed as complete, running applications. Then the inner module is referenced in the outer module, making it possible for the inner module to use the shared query table by means of query operators or other StreamSQL statements.
The outer module specifies a metronome operator, which, at 10 second intervals, initiates a read all rows query against two shared query tables, QT1 and QT2. A query against either of the tables emits a single tuple with null values in the fields derived from the table. The module writes data into QT1 and QT2 as output streams, OS2 and OS3.
--Outer Module Example 2
CREATE OUTPUT STREAM OS2;
CREATE OUTPUT STREAM OS3;
CREATE OUTPUT MEMORY TABLE QT1 (
sym string(4),
shares int,
price int,
PRIMARY KEY(sym) USING hash
);
CREATE OUTPUT MEMORY TABLE QT2 (
sym string(4),
shares int,
price int,
PRIMARY KEY(sym) USING hash
);
CREATE METRONOME Metronome1 (time, 10.0);
SELECT *
FROM Metronome1 OUTER JOIN QT2
INTO OS2;
SELECT Metronome1.time AS input_time,
QT1.sym AS table_sym,
QT1.shares AS table_shares,
QT1.price AS table_price
FROM Metronome1 OUTER JOIN QT1
INTO OS3;
Note that the table declarations include the keyword OUTPUT, which specifies that the table being declared can share its data with other modules; that is, the table data is output from this module.
Even though the inner module has not yet been developed and referenced in the application, this outer module can run as a standalone application.
The inner module uses the update operation to write the same data into two query tables, QueryTable1 and QueryTable2, which are placeholders for the shared tables, QT1 and QT2, respectively. Each of two query operators is configured to pass fields from the input stream to its emitted tuple. After writing to the tables, the input data is emitted on the output stream OS1.
In the StreamSQL representation of this module, the table declarations include the keyword INPUT. This indicates that the table data come from a shared query table implemented in another module. In other words, data in the shared table in the outer module is input into this inner module.
-- Inner Module Example 2
CREATE INPUT STREAM InputStream1 (
stock string(4),
cost int,
volume int
);
CREATE OUTPUT STREAM OS1;
CREATE INPUT MEMORY TABLE QueryTable1 (
sym string(4),
shares int,
price int,
PRIMARY KEY(sym) USING hash
);
CREATE INPUT MEMORY TABLE QueryTable2 (
sym string(4),
shares int,
price int,
PRIMARY KEY(sym) USING hash
);
CREATE STREAM Query1;
REPLACE INTO QueryTable1 (sym, shares, price)
SELECT stock AS sym, volume AS shares, cost AS price
FROM InputStream1
RETURNING InputStream1.stock AS stock,
InputStream1.cost AS cost,
InputStream1.volume AS volume
INTO Query1;
REPLACE INTO QueryTable2 (sym, shares, price)
SELECT stock AS sym, volume AS shares, cost AS price
FROM Query1
RETURNING Query1.stock AS stock,
Query1.cost AS cost,
Query1.volume AS volume
INTO OS1;
Even though this inner module has not been referenced in the outer module, it can run as a standalone application.
After developing the inner and outer modules independently, you can add to the outer module a reference to the inner module.
The following StreamSQL shows the finished application, which includes code for the outer module, the inner module, and code required to integrate the shared query table.
--Final Application Example 2
CREATE INPUT STREAM InputStream2 (
stock string(4), cost int, volume int
);
CREATE OUTPUT STREAM OS2;
CREATE OUTPUT STREAM OS3;
CREATE OUTPUT STREAM OS4;
CREATE OUTPUT MEMORY TABLE QT1 (
sym string(4),
shares int,
price int,
PRIMARY KEY(sym) USING hash
);
CREATE OUTPUT MEMORY TABLE QT2 (
sym string(4),
shares int,
price int,
PRIMARY KEY(sym) USING hash
);
CREATE METRONOME Metronome1 (time, 10.0);
APPLY MODULE "innerModule.ssql" -- or "innerModule.sbapp"
FROM InputStream1 = InputStream2,
QueryTable1 = QT1, QueryTable2 = QT2
INTO OS1 = OS4;
SELECT Metronome1.time AS input_time,
QT2.sym AS table_sym,
QT2.shares AS table_shares,
QT2.price AS table_price
FROM Metronome1 OUTER JOIN QT2
INTO OS2;
SELECT Metronome1.time AS input_time,
QT1.sym AS table_sym,
QT1.shares AS table_shares,
QT1.price AS table_price
FROM Metronome1 OUTER JOIN QT1
INTO OS3;
Note that, in the finished application, the APPLY MODULE statement specifies the inner module, as shown:
APPLY MODULE "innerModule.ssql" -- or "innerModule.sbapp"
The FROM clause includes the following line, which specifies the query table associations:
QueryTable1 = QT1, QueryTable2 = QT2
-
StreamSQL APPLY Statement
-
StreamSQL CREATE TABLE Statement
