Introducing the CursorAdapter Class

Size: px
Start display at page:

Download "Introducing the CursorAdapter Class"

Transcription

1 Introducing the CursorAdapter Class By Chuck Urwiler May 20, 2003 he CursorAdapter class is one of the most impressive accomplishments of the VFP 8 development team. It will change the way many developers relate to their various data sources. With the introduction of the CursorAdapter class in VFP 8, the Fox team has finally made a significant change in the way a VFP application accesses data, whether it is native or from a remote data source. Additionally, the setup of CursorAdapter classes will be somewhat familiar to those who are well-versed in the behavior of views and SPT, as well as the alternative data sources of using ADO RecordSets or XML documents. The CursorAdapter class is unique in that it is the first VFP base class to provide conversion between native VFP cursors and ODBC, ADO or XML data sources, all within a single class. In other words, the ability to translate an ODBC data stream, an ADO RecordSet, or an XML document into a VFP cursor is all built into the CursorAdapter class. You can probably tell already that the CursorAdapter is an effective replacement for the local view and remote view technology from earlier versions (Note: neither of these features has been removed from VFP 8). But in some cases, it also replaces SQL Pass Through, and also reduces the need to work directly with ADO and XML in your code. One key advantage to the CursorAdapter is for situations where you need to connect to more than one data source from within the same application. For example, if your application retrieves most of its data from SQL Server, but also needs to work with a handful of XML documents, the CursorAdapter can be used in both cases to make all the data appear to your application as a set of VFP cursors. Another example might be a situation where the data is currently stored in VFP tables, but future plans are to move to a database server, like SQL Server or Oracle. You would build a set of CursorAdapter classes first for VFP and then, if necessary, replace these classes with SQL Server equivalents when necessary. But, since we must walk before we can run, let's take a basic tour through the CursorAdapter class and its different incarnations. After that, it will be easier to devise a strategy for building data classes using the CursorAdapter Class. Your First CursorAdapter Class Like any other class, the best way to learn how to use it is to see how one is created. To keep the complexity low for these first examples, let's start by accessing VFP native tables with a CursorAdapter class. This is very much like using a Local View to retrieve data from VFP tables. Later on in this article, we'll use other CursorAdapter classes to connect to SQL Server data, OLE DB data, and an XML document. First, you have two ways to create a CursorAdapter. You can use the Data Environment builder or you can build the class "by hand" through a program or the class designer. This example will use the Data Environment builder; later examples will be built "by hand." If you're not familiar with the enhancements VFP 8 brings to the Data Environment, you might think that using a builder in the DE to create a CursorAdapter would only be useful within a Form, not a class. However, the DE has been enhanced in VFP 8 so it can be instantiated without the presence of a form! Start by creating a new Data Environment class with the create class command. Be sure to select the Data Environment class in the "Based On" drop down. Name the class detest and store it in a class library called Tests.vcx. Once the class appears in the class designer, right click on the Data Environment and select "Builder" from the drop down. This brings forward the Data Environment builder. 1/15

2 In the data source type drop down, note the available options. Since this first example will connect to native VFP tables, choose Native. Once selected, use the ellipsis button to choose the Northwind database (default location is c:\program files\microsoft visual foxpro 8\samples\northwind\northwind.dbc). Next, click the Cursors page, which is initially empty. Under the list box, choose the New button to create a new CursorAdapter class with the CursorAdapter Builder. Initially, you will see the Properties page, providing options for choosing the name of the class and the alias of the cursor created by the class. Be sure to provide an alias that differs from the table name to avoid confusion with the base table. In this case, use cacustomer as the class name and ccustomer as the alias. You should also check the "Use DataEnvironment data source" option since you want this class to use the same data source as the data environment. Note that you could have a different data source for the CursorAdapter, allowing you to mix data sources between different classes (such as using ODBC for one class and XML for another). To specify how the CursorAdapter will retrieve data from the source, use the Data Access page of the builder. Click the Build button to activate a command builder dialog, where you can select the field(s) of interest for your cursor. For this example, select the Customers table from the Table drop down list, and then select the "Customers.*" option in the list box below. Click the single right-facing arrow to move the selection, and then press OK. This will build the following select command for you: select CUSTOMERS.* from CUSTOMERS If you wish to add filters, joins, or other clauses to the query, you can type them directly into this edit box. However, if you wish to parameterize the query, there are several options, covered later in this article. For now, let's add a WHERE clause so that it looks like the following: select CUSTOMERS.* from CUSTOMERS where companyname like 'C%' This will make it easy to tell the difference between the base table and the resultant cursor, since only a few records match this Where clause. The schema has been built for you in the second edit box. It is usually best to take a minute and verify that the schema matches your expectations before proceeding. The data fetching properties at the bottom of this page relate mostly to how the class deals with remote data fetches, and do not apply when using VFP as a data source. We'll leave these at their defaults and cover them in more detail later. Near the bottom of the data access page is the buffer mode override setting, which allows you to override any associated form's buffer mode setting. Generally, you want to use the optimistic table buffering mode unless you have a specific reason to use the row buffering mode. Set this to Optimistic Table Buffering for this example. Finally, the "Break on error" setting at the bottom of the page controls how errors are handled by the CursorAdapter class. The default setting specifies that the class will trap its own errors and allow you to capture them with the AERROR function. Enabling this setting will cause a VFP error to occur whenever any problems occur within the CursorAdapter class. This means that you will need to use the ON ERROR command or Error event of the class to trap unhandled exceptions. Generally, you will want to leave this setting off so that your code can quietly handle any exceptions that may occur. The final page (labeled "Auto Update") configures how changes are applied to the base table(s) of the class. For the most basic updates, choose the "Auto-update" and "Update all fields" checkboxes. This will direct the CursorAdapter class to automatically build the appropriate Update, Insert or Delete statements for any changes that are made to the data in the cursor. However, you must still choose the primary key field(s) for the cursor, so that these statements know how to uniquely identify the base table records. For this example, the CustomerID field is the primary key, so check the box in the column under the key. 2/15

3 For the time being, leave all of the other settings at their defaults. These settings are explored later on in this article. To finalize your selections in the CursorAdapter builder, click the OK button to return to the DataEnvironment builder. At this point, you should see the cacustomer class listed on the left, and details on the right. If you decide to make modifications to this class, you can return to the DataEnvironment builder at any time, select the desired CursorAdapter class, and click the Builder button. Accessing VFP data At this point, you can try out the Data Environment to see if it retrieves the data specified by the select command in the CursorAdapter. Using the command window, instantiate the DE class and invoke the OpenTables method: lo = NewObject("deTest","Tests.vcx")? lo.opentables() BROWSE When the OpenTables method is fired, the CursorAdapter is instructed to fill its cursor with the results of the Select command that you specified in the builder. When you BROWSE, you will see only the customer records that have a CompanyName which starts with "C" (normally, five records match). One special behavior that comes with the CursorAdapter is that the cursor is coupled to the object instance; therefore, if you destroy the object reference to the CursorAdapter class, you will also lose the cursor and its contents. This means that you'll have to ensure that any CursorAdapter object variables stay within scope for as long as you intend to access the associated cursor. Modifying VFP Data Now, let's see if the cursor allows updates and posts them properly to the base table. Try the following lines of code in the command window: REPLACE contactname WITH 'My Name Here'?TABLEUPDATE() SELECT customers BROWSE Once you browse the Customers alias, you see the base table and should be positioned on the record that you modified. If you didn't move the record pointer before issuing the Replace statement, the record with 'CACTU' as the customer ID was modified. Regardless of which record you modified, this proves that the CursorAdapter is updatable and that the updates are being sent properly to the base table. Under the Hood Let's open the Data Environment class that you just tested to see what the builder did for us. This is not just an exercise it is a great way to learn how to properly configure a CursorAdapter class should you decide to make your own classes outside of the Data Environment. While the Data Environment has a few property changes and a method, we're actually not interested in those changes. Instead, look in the property sheet's object drop-down list and select the cacustomer class to see the settings that are required to make the CursorAdapter class work. Table 1 summarizes the changes made by the builder and what each PEM does. All of the properties that contain "See Init" are populated at run time by the code generated for the Init method. That code is shown in Listing 1. This is probably the most educational place to look after the builder is finished to see how the properties have been set. Note that you can change these values here or through the builder. However, by changing them here, you run the risk of breaking functionality, as your property changes are not verified as they are within the builder. Regardless, you can see in the Init() code how the SelectCmd property is specified, as well as the 3/15

4 KeyFieldList, UpdatableFieldList, and the UpdateNameList. Pay particular attention to the format of the UpdateNameList property this property lists each field from the cursor and its corresponding field (with table name) in the base table. When creating your own CursorAdapter classes from scratch, you may be tempted to leave out the table name in this listing. However, without this exact format, your updates will fail, but without errors. I'll reiterate this point later when creating a class without the builder. Earlier I stated that the CursorAdapter, using the Native data source, is essentially a replacement for a Local View. If you have ever built a local view, you probably can see the similarities: a SQL Select statement is constructed, you specify which fields you wish to be updatable, you specify the field or fields that comprise the primary key, and then let VFP do the rest. Once you retrieve the data in the cursor, you can use TableUpdate() to send the changes back to the base table, and VFP automatically builds the necessary Update, Insert or Delete statements to carry out the modifications. As an example, recall the earlier Replace statement that changed the value of the Contact field in the ccustomer alias. Upon issuing the TableUpdate statement, VFP automatically generates (and submits) the following Update command to attempt the modification: UPDATE customers ; SET CONTACTNAME=ccustomer.contactname ; WHERE ; CUSTOMERID=OLDVAL('customerid','ccustomer'); AND ; CONTACTNAME=OLDVAL('contactname','ccustomer') VFP was able to generate the WHERE clause by referencing the KeyFieldList property of the CursorAdapter as well as parts of the UpdateNameList property. It also takes into account which field was changed and adds in the necessary clauses to ensure that you don't attempt to update a record that has been changed by someone else. Note that this is because we left the WhereType property at its default of "key fields and any modified fields." Handling Errors Obviously, not everything will go as planned when trying to update data from the CursorAdapter. As you well know, TableUpdate can fail for a variety of reasons, such as an update conflict or a record lock. Do you have to do anything special with the CursorAdapter class to detect these problems? The answer is, "it depends." Let's create a simple update problem by locking the record that the CursorAdapter is attempting to update. If the class designer is still open, close it. Then, instantiate the detest class with the NewObject function, just as you did above, and call the OpenTables method. Browse the cursor so that you can see the data, but don't change anything yet. Now open a second instance of VFP 8 so you can lock the record. Execute the following lines in the command window to lock the record that you'll attempt to update: OPEN DATABASE (HOME(2)+"Northwind\northwind.dbc") USE customers LOCATE FOR customerid = 'CACTU'?RLOCK() You'll have to ensure that any CursorAdapter object variables stay within scope for as long as you intend to access the associated cursor. You should get a return value of.t. to show that the record is actually locked by this instance of VFP. Return to the first instance of VFP and attempt the following code from the command window: REPLACE contactname WITH 'updated' SET REPROCESS TO 2 SECONDS 4/15

5 ?TABLEUPDATE() In this case, TableUpdate returns.f., showing that the record lock prevented the update from succeeding. If you issue a call to AERROR() and display the contents of the resultant array, you will see the error message "Record is not locked." This means that you can handle such errors in the same way as if you were working directly with the buffered table and not a cursor. Unfortunately, not all expected errors will behave this way. Of particular note is the Update Conflict, where an update made by one user attempts to overwrite the changes made by another user. To see this in action, issue the following commands in the current instance of VFP (where the CursorAdapter is being used):?tablerevert(.t.) REPLACE contactname WITH 'client 1' Now switch over to the second instance and issue the following commands: CLOSE DATABASES all OPEN DATABASE (HOME(2) + "Northwind\northwind.dbc") USE customers LOCATE FOR customerid = 'CACTU' REPLACE contactname WITH 'client 2' BROWSE Return to the first instance, and attempt to update the changes with TableUpdate:?TABLEUPDATE() In this case, TableUpdate incorrectly returns a.t., leading you to believe that the update was successful! However, it was not, and this can be proven by invoking the CursorRefresh() method of the CursorAdapter, as in the following code:?tablerevert(.t.)?lo.cacustomer.cursorrefresh() The CursorRefresh method tells the CursorAdapter to re-execute the SelectCmd and retrieve the latest data from the base table. Examination of the ContactName field shows that the value was never updated from the CursorAdapter! The easiest way to solve this problem is to take advantage of the AfterUpdate method on the CursorAdapter. This method is invoked after the TableUpdate attempts to save the changes to each record in the result set. Note that this method is invoked only for a change to a current record. If the record is new or the record is deleted, then the AfterInsert or AfterDelete methods would fire. The AfterUpdate method captures several parameters, including the original field state, whether changes were forced, and the text of the commands that were used for the update. The last parameter, lresult, is the most critical for our current topic, as it tells us whether the update was deemed a success by the updating process. The other feature to use to solve the update conflict problem is the system variable _TALLY, which tells how many records were affected by the last operation. Therefore, if lresult is true, but _TALLY is zero, then no records were updated, and you can assume that the problem in this case was an update conflict. In summary, the simple way to solve this problem is to add the following code to the AfterUpdate method on the CursorAdapter class: LPARAMETERS cfldstate, lforce, nupdatetype, cupdateinsertcmd, cdeletecmd, lresult IF lresult AND _TALLY = 0 THEN 5/15

6 ERROR 1585 && update conflict What is interesting here is that you will not see the error message appear on your screen; instead, the message is "trapped" by the TableUpdate call, forcing you to use the AError function to see the cause of the update failure. This occurs because the BreakOnError property was left at its default of False, meaning that errors should not cause a break. If you were to set this property to True, then the "Update Conflict" error message would appear, or if specified, your ON ERROR handler would be triggered. This issue of update conflicts is "by design" for the CursorAdapter since there is no easy way for VFP 8 to automatically detect this problem. Therefore, this code (or similar) will probably end up in your foundation CursorAdapter classes when going against native data sources. CursorAdapter with ODBC Now that you've seen the basics, let's move forward to see how things change when using SQL Server as the back end instead of VFP. We'll start with using the ODBC driver from VFP to access the Northwind database on SQL Server. Also, let's build this CursorAdapter "from scratch" so that every aspect of the class is visible. First, create a new class in the class designer with the following command: CREATE CLASS caodbc OF tests as CursorAdapter You can use TableUpdate() to send the changes to the base table, and VFP automatically builds the necessary Update, Insert or Delete statements to carry out the modifications. The most important property to set at this point is the DataSourceType property. Since we're attempting to connect to SQL Server via ODBC, set this property to ODBC. When set in this fashion, the DataSource property expects a valid connection handle, which can be created through the SQLConnect or SQLConnectString functions. In either case, these functions should be invoked through the Init method of the CursorAdapter class using the following code: LOCAL lcconnstr, lnconn ** string assumes trusted connection (integrated security) lcconnstr = "Driver=SQL Server;Server=(local);DATABASE=Northwind" lnconn = SQLSTRINGCONNECT(lcConnStr) IF lnconn > 0 THEN THIS.DATASOURCE = lnconn ELSE ** unable to connect The connection string assumes that you are using a trusted connection to SQL Server; if you are using SQL Server security instead, add the "uid=" and "pwd=" strings to the connection string to specify the username and password for the connection. In either case, the return value is the connection handle, or a negative value if an error occurred. This connection handle is assigned to the DataSource property so that the CursorAdapter knows where to pass statements. The next step is to build a SelectCmd so that the CursorAdapter knows what data it is acquiring from the data source. The best place to do this is also in the Init method, since the property sheet does have limitations on how long a string you can provide. Add the following line to the Init method, after the code that sets the DataSource property, to retrieve the list of Customers that have a Company Name that starts with "C": This.SelectCmd = "SELECT " + ; "CustomerID, CompanyName, ContactName, " + ; "Address, City, Region, Country " + ; "FROM Customers WHERE CompanyName LIKE 'C%'" Next, you need to tell the CursorAdapter to fill the associated cursor with a call to the CursorFill method. You could leave out this call and invoke it manually from outside of the class, or place it in the 6/15

7 Init method so it automatically fills upon instantiation. Simply call This.CursorFill() after the SelectCmd is populated in the Init method. Finally, you should add a bit of code to the Destroy method of the class, to drop the server connection once the object is removed from memory. Without this code, every new instance will create a new connection to the server, and never release it: IF this.datasource > 0 THEN SQLDISCONNECT(this.DataSource) With these changes, you have a functional CursorAdapter class that produces a non-updatable cursor. Still, it may be a good time to test the class, to ensure that it can be instantiated and that it retrieves data properly, before allowing it to be updatable. Test it with the following code: lo = NEWOBJECT("caODBC","tests") BROWSE Note that you didn't have to invoke an OpenTables method like you did with the Data Environment. This is because you added the CursorFill method directly to the Init method, causing the class to automatically fill the cursor upon instantiation. Updating ODBC Data To make this class updatable, you have to provide correct values for the Tables, KeyFieldList, UpdatableFieldList, and UpdateNameList properties. Also set the AllowInsert, AllowUpdate, and AllowDelete properties to True, to ensure that the automatic updating feature is properly activated. Once again, the best place to make these changes is through code in the Init method. The modified version of the Init method appears in Listing 2. Before closing the class designer, you may also want to change the BufferModeOverride property to "5", "Optimistic table buffering" so automatic updates do not occur when moving the record pointer. To test the updatability of the CursorAdapter, instantiate it, browse the cursor, make a change, and then issue TableUpdate. To ensure the changes were applied, call the CursorRefresh method of the CursorAdapter object and browse again. Handling ODBC Errors As with the native CursorAdapter, most errors are trappable in the "traditional" way test the return value of TableUpdate and, in case of failure, use AError to determine the cause. Unfortunately, the detection of an update conflict is also a problem for the ODBC type CursorAdapter. While the solution for the native CursorAdapter was to raise an error in the AfterUpdate method, this won't be as effective for the ODBC CursorAdapter since we're not expecting VFP errors, but ODBC errors, when an update fails. Therefore, the best answer is to either use a stored procedure (covered later) or add a little more code to the update statement as it is sent to the server. Recall that the solution for the native CursorAdapter was checking _TALLY to see if any records were updated. The solution here for ODBC is similar, but we can't use _TALLY since it isn't reliably correct for remote data. Instead, we can use SQL system function to determine if any records were updated. If you were writing a T-SQL batch of statements to update a record, you might write code similar to the following: --@custid set by earlier code or parameters UPDATE customers SET ContactName WHERE CustomerID AND ContactName 7/15

8 IF = 0 RAISERROR('Update failed.',16,1) The RaisError T-SQL function causes VFP to receive an ODBC error (number 1526), passing the error message as specified in the first parameter (the other two parameters indicate the severity and state of the error). In this case, RaisError is invoked = 0, meaning that the previous T-SQL statement did not affect any records. Where this all fits into the current discussion is that you can use the BeforeUpdate method of the CursorAdapter to modify the statement that is sent to the server on an update. While the BeforeUpdate method receives five parameters, the last two (cupdateinsertcmd and cdeletecmd) are interesting in that they are passed by reference. This allows you to change the commands before they are sent to the data source. In our case, we'd like to use this method to append the test and subsequent call to RaisError. This can be done with the following code in BeforeUpdate: LPARAMETERS cfldstate, lforce, nupdatetype, ; cupdateinsertcmd, cdeletecmd IF nupdatetype = 1 THEN cupdateinsertcmd = cupdateinsertcmd + ; " = 0 "+ ; "RAISERROR('Update Failed due to update " + ; "conflict.',16,1)" Now, for every row that is sent to the back end, this code will test to see if the row was updated. If not, VFP will receive the error, TableUpdate will fail, and AError will show the usual 1526 error with the message text as specified. There are two problems with this approach. First, this is a specific fix for SQL Server; for other ODBC data sources (such as Oracle), this code will not work. Second, this error message is very generic as it always generates the same VFP error number, and makes proper error handling from VFP a bit difficult. This issue can be mitigated somewhat by creating custom error messages on the SQL Server, each with their own unique error number. Another way to improve upon this solution is to use Stored Procedures to perform the updates instead of letting VFP build and pass an ad-hoc query to the server. Of course, the tradeoff of adopting the stored procedure approach is that you lose the benefit of having VFP automatically handle the updates. Parameterization As a side note, you have now seen one way to parameterize the commands for a CursorAdapter class. In essence, every event that occurs in the class has a set of Before and After methods, such as BeforeUpdate and AfterUpdate. However, there is no BeforeSelect or AfterSelect instead, these are called BeforeCursorFill and AfterCursorFill, since the cursor is filled with the result of the SelectCmd. The BeforeCursorFill method receives three parameters, and expects a Boolean return value. The first parameter, lusecursorschema, specifies whether the CursorSchema property controls the construction of the resultant cursor or not. The second parameter, lnodataonload, is similar to the NODATA clause on views, where the schema is retrieved but no data is actually passed from the data source. For the current discussion, the third parameter, cselectcmd, is of primary interest. It is also passed by reference (like the cupdateinsertcmd parameter of BeforeUpdate) and is initially populated with the current setting of SelectCmd. However, if you change the value of this parameter, it does not change the value of the SelectCmd property; instead, it modifies what is passed to the data source, for as long as the object exists. For example, imagine that you have set a CursorAdapter object's SelectCmd to the following statement: 8/15

9 SELECT CustomerID, CompanyName, ContactName, City, Region, Country FROM Customers Upon calling the CursorFill method of the CursorAdapter, the cselectcmd parameter of the BeforeCursorFill method would contain this value. Now imagine that you have the following code in this method: cselectcmd = cselectcmd + ; " WHERE CompanyName LIKE '" + ; this.ccompanyname + "%'" This would cause the actual Select command to always contain the WHERE clause as specified by the code and the current value of this.ccompanyname (a user-defined property). And since it doesn't modify the original value of SelectCmd, you don't have to include any special coding to ensure that you don't get two WHERE clauses in the submitted select command. Parameterization, Part II If you have used views or SQL Pass Through in the past, then you are probably familiar with parameterization by using the "?" character in front of a variable. As you might suspect, this feature still works in the CursorAdapter. The following example code shows how you can use a parameter in the SelectCmd property of a CursorAdapter: This.SelectCmd = "SELECT * FROM Customers " + ; " WHERE CompanyName like?lcmyvar " lcmyvar = 'C%' This.CursorFill() It is critical to ensure that the variable "lcmyvar" is populated before the CursorFill method is invoked. Otherwise, you are prompted for the value by VFP, something a user should never see. You can also use a property of the CursorAdapter as the parameter, instead of a local variable. The advantage, of course, is that the property will persist as long as the object does, and you could even provide a set of Access/Assign methods to ensure the assigned value meets certain criteria. Using Stored Procedures Above, it was suggested that using stored procedures would be a good way to get around the limitations of handling errors. With that in mind, let's explore the approach of using stored procedures with an ODBCbased CursorAdapter so we can get a feel for how much work is involved in manually handling the updates for a CursorAdapter class. Essentially, this section is all about replacing the automatic generation of Update, Insert, and Delete commands with calls to stored procedures on the data source. This means that you'll be dealing with the UpdateCmd, InsertCmd, and DeleteCmd properties, and assumes that the Northwind database on your SQL Server already has stored procedures in place for performing these functions (they are not provided in the sample database). VFP will automatically convert the ADO RecordSet into a VFP cursor for us, and will also handle the updating. As an example, let's take a look at the complete code for a simplified stored procedure you can use to update the ContactName field in the Customer table for the Northwind database: --T-SQL code, not VFP CREATE PROCEDURE UpdateCustomerContact nchar nvarchar nvarchar (30) ) AS IS NULL RAISERROR('CustomerID is a required parameter',16,1) ELSE UPDATE Customers 9/15

10 SET ContactName WHERE CustomerID AND ContactName To save space, this procedure is lacking the full error handling code that you would normally include. Regardless, there is enough code here to illustrate how to perform an update with the CursorAdapter class. Fortunately, establishing the UpdateCustomerContact procedure as the Update command is as easy as overriding the BeforeUpdate method with the following code: LPARAMETERS cfldstate, lforce, nupdatetype, ; cupdateinsertcmd, cdeletecmd cupdateinsertcmd = ; "EXECUTE UpdateCustomerContact '" + ; EVALUATE(this.Alias+".CustomerID") + "','" +; ALLTRIM(EVALUATE(this.Alias+'.ContactName'))+ ; "','" + ; OLDVAL('contactname',this.Alias)+"'" Here, the code populates the cupdateinsertcmd parameter, in effect overriding the default Update command. I use the Evaluate function so the cursor name will be dynamic, assuming that the cursor name could easily be changed but the code may not. Also, I use the OLDVAL function to retrieve the value the ContactName field had before it was modified. This is critical to the procedure call as it expects the old value in the Where clause, much like the automatically generated Update statement. Remember that the BeforeUpdate method is invoked automatically for us by a TableUpdate call just before the record is actually updated. Therefore, no matter what the current value is for UpdateCmd, this method overrides that to always use the stored procedure. Note that you could also use the parameterization discussed earlier, instead of overriding the BeforeUpdate method. This would still require you to provide the UpdateCmd on the CursorAdapter, but, instead of hard-coding the parameters you would use variables or properties and precede them with question marks. An important point to make here is that the cupdateinsertcmd (or the object's UpdateCmd) cannot return a value. More accurately, if you return a value from the stored procedure, it doesn't have anywhere to "go," and the value is always lost. Therefore, it is critical that you add the appropriate RaisError calls in the stored procedure to have your code respond to any errors that may occur during the update (such as bad parameters or an update conflict). You would catch the error by testing the return value of TableUpdate, calling AError, and then analyzing the error array. Similar code should also be written for the BeforeInsert and BeforeDelete methods, so that they also call stored procedures instead of ad-hoc queries. For the sake of space, I'll leave that code as "an exercise for the reader." CursorAdapter with OLE DB Our next task is to see how to use OLE DB with the CursorAdapter class, and to compare it to how we've used Native and ODBC data sources. OLE DB technology is more capable than ODBC, and may provide access to more types of data sources than ODBC. The CursorAdapter uses OLE DB by hooking into the objects of ADO, which is the standard COM wrapper around the OLE DB technology. VFP will automatically convert the ADO RecordSet into a VFP cursor for us, and will also handle the updating, just as in the previous examples. The first thing to do, of course, is to create a new CursorAdapter class. This time, let's build one through code. Start by creating a new program called caado.prg, and add the following code: 10/15

11 PUBLIC gocaado as CursorAdapter gocaado = CREATEOBJECT('caADO') BROWSE DEFINE CLASS caado AS CursorAdapter oconn = NULL ors = NULL Alias = "ccustado" DataSourceType = "ADO" SelectCmd = "SELECT " + ; "CustomerID, CompanyName, ContactName, "+; "ContactTitle, Address, City, Country "+; "FROM Customers WHERE Customerid LIKE 'C%'" FUNCTION Init() This.DataSource = this.ors This.oRS.ActiveConnection = this.oconn This.CursorFill() ENDFUNC ENDDEFINE In this code, we set the DataSourceType to ADO and add our usual Customers query to the SelectCmd. When the DataSourceType is ADO, then the DataSource property must contain either a valid RecordSet or Command object, depending upon how you want to use the CursorAdapter. If you don't parameterize your query (like the earlier examples through use of the "?" character) then you can use a RecordSet; otherwise, you are forced to use the Command object, simply because that's where ADO has placed the parameters collection. Any parameters in your query are automatically handled by objects in the parameters collection of the command object. In this case, we'll use the RecordSet object, but notice that we must also provide a Connection object. In both cases, I am taking advantage of Access methods to create the references to these objects. Listing 3 shows the code for the Access methods. Both Access methods first check to see if the object has already been created. If not, then they proceed with the object creation. In the case of the RecordSet, you need only to create the object, as the CursorAdapter takes care of the rest. With the Connection object, you must provide the connection string and open the connection, because the CursorAdapter does not open the connection for you. This is because the connection isn't a property of the CursorAdapter, but instead is a property of the RecordSet object. With this code in place, you can run the program and see the resultant cursor. It should look very much like the cursor you retrieved using ODBC, since it contains the data from the same source. Updating with OLE DB Without setting a few more properties, this simple CursorAdapter is not updatable. The following additional code, inserted in the class definition before the Init method, will allow automatic updates to occur: KeyFieldList = "CustomerID" UpdatableFieldList = ; "CompanyName, ContactName, ContactTitle, "+ ; "Address, City, Country" UpdateNameList = ; "CustomerID Customers.CustomerID, " + ; "CompanyName Customers.CompanyName, " + ; "ContactName Customers.ContactName, "+; "ContactTitle Customers.ContactTitle, " + ; "Address Customers.Address, "+; "City Customers.City, Country Customers.Country" Tables = "Customers" However, the RecordSet will be created with its default CursorLocation and CursorType properties. Without changing these properties, the RecordSet is initially read-only; therefore, you will need to modify the ors_access method as follows: 11/15

12 FUNCTION ors_access() as ADODB.RecordSet LOCAL lors as ADODB.RecordSet IF VARTYPE(this.oRS)<>"O" THEN this.ors = NULL lors = NEWOBJECT("ADODB.Recordset") IF VARTYPE(loRS)="O" THEN lors.cursortype= 3 && adopenstatic lors.cursorlocation = 3 && aduseclient lors.locktype= 3 && adlockoptimistic this.ors = lors RETURN this.ors ENDFUNC With these additional settings for the RecordSet, the CursorAdapter can now handle automatic updates. CursorAdapter with XML Last, but not least, let's build a CursorAdapter that uses XML as its data source. This scenario is interesting, since an XML document doesn't normally act as a data source. Also, the CursorAdapter does not automatically build SQL Update, Insert or Delete statements when the data source is set to XML. Therefore, this type of CursorAdapter will require the most coding to retrieve and update data. In this example, I will use the SQLXML feature of SQL Server 2000 to provide an XML document. Also, since SQLXML supports updating via XML, we'll take the time to write the necessary code to perform updates. This assumes that you have configured SQLXML to allow HTTP data access to the Northwind database, and that you are allowing updates to the database with UpdateGrams. In my case, I have set up IIS to use a virtual directory called "nwind" for HTTP access. Therefore, all of my examples will contain URLs that reference to access SQLXML via IIS. Let's start by creating a new program called caxml.prg with the following basic class definition: PUBLIC ocaxml as CursorAdapter SET MULTILOCKS ON && need for table buffering ocaxml = CREATEOBJECT('xcXML') BROWSE NOWAIT DEFINE CLASS xcxml AS CursorAdapter DataSourceType = "XML" Alias = "xmlcursor" UpdateCmdDataSourceType = "XML" InsertCmdDataSourceType = "XML" DeleteCmdDataSourceType = "XML" BufferModeOverride = 5 *custom properties oxmlhttp = NULL oxmldom = NULL cserver = "localhost" cvdir = "nwind" ENDDEFINE Beyond the common DataSourceType and Alias property settings, this is the first time we've seen the xxxcmddatasourcetype properties. Since this is an XML-based CursorAdapter, these properties are not optional if you want it to be updatable. The custom properties oxmlhttp and oxmldom become object references used throughout the class, and will be detailed below. Retrieving XML Data Before thinking about the updatability of the CursorAdapter, let's concentrate on retrieving a document from the SQLXML server. First, since a simple Select command will not work, we have to establish a 12/15

13 custom SelectCmd. This is easily done in the Init method, where we will also invoke the CursorFill method, as follows: FUNCTION INIT() as Boolean LOCAL llretval, lcmsg, laerr[1] this.selectcmd = "this.getxml()" llretval = THIS.CursorFill() IF NOT llretval THEN AERROR(laErr) lcmsg = "Cursor was not filled!" IF NOT EMPTY(laErr[2]) THEN lcmsg = lcmsg + CHR(13) + laerr[2] MESSAGEBOX(lcMsg,16,"XMLCursorAdapter Test") RETURN llretval ENDFUNC This code establishes the SelectCmd as a local method instead of a SQL Select command. While this hasn't been done in the previous examples, this is perfectly legal for any CursorAdapter class, regardless of the type. However, when you use a local method as the SelectCmd, you will have to also provide custom code for your Update, Insert and Delete commands, since VFP won't be able to automatically handle something that is not a SQL Select command. When we invoke CursorFill in the Init(), the GetXML method is called. With the data source set to XML, the GetXML method must return a valid XML document that contains only a single table. If it contains multiple tables, you will get unexpected results. The GetXML method is shown in Listing 4. GetXML starts by getting a reference to an MSXML2.XMLHTTP COM object. This object handles all of the communication across HTTP, including sending the queries to the server and retrieving the results. As you can see, the instantiation of the oxmlhttp object is controlled via the provided Access method, designed to prevent the constant creation and destruction of this COM server. Next, you can see our typical Select statement, except that the LIKE clause is a little different. HTTP requires that we "escape" the percent sign with the hex value of the character, forcing us to expand it to "%25". This value will be "collapsed" to the single percent sign character before SQL Server receives the query. After that, the code sets up the URL with the specified query and sends the URL to SQL Server via HTTP. SQL Server receives the query, processes it, and returns the result as XML because we've included the FOR XML clause on the query. The root element of the XML document is named "results" in this example. As you can see from the query string, this is configurable to your liking. At this point, lcretxml contains an XML stream from the SQL Server. Since the GetXML method was invoked by VFP as the SelectCmd, you can simply return the contents of this variable from the GetXML method and VFP will convert the stream into a VFP cursor. You can test this by executing the caxml program. A browse window should appear with the contents of the returned XML document. Feel free to use the debugger to step through the GetXML method so you can see the XML document in the lcretxml variable before it is converted to a VFP cursor and discarded. Updating XML Data The next step is to determine how to make this cursor updatable so that the changes can be posted back to our SQLXML server. SQLXML can take a special XML document, known as an UpdateGram, and use it to post changes to the database directly. In VFP7, this document could be created by calling the XMLUpdateGram function. With VFP 8 and the CursorAdapter, this is automatically built in with the UpdateGram property. The first step is to set up the updatable properties and establish an Update command. Set up the properties at the top of the class definition and provide the method call for the Update command by 13/15

14 adding a line of code to the Init method of the CursorAdapter. KeyFieldList = 'customerid' Tables = 'customers' UpdatableFieldList = ; "companyname, contactname, contacttitle, "+; "address, city, country " UpdateNameList= ; "customerid customers.customerid, " + ; "companyname customers.companyname, " + ; "contactname customers.contactname, " + ; "contacttitle customers.contacttitle, " + ; "address customers.address, " + ; "city customers.city, country customers.country" FUNCTION INIT() as Boolean LOCAL llretval, lcmsg, laerr[1] this.updatecmd = "this.updatexml()" this.selectcmd = "this.getxml()" ** balance of code skipped... Note that we could have placed the property settings for UpdateCmd and SelectCmd in the list of properties that precede the Init method it works the same either way. Regardless, the first part of this code should be familiar by now, where we set the KeyFieldList, Tables, UpdatableFieldList and UpdateNameList properties. Without these property settings, no UpdateGram can be created. After that, we establish the UpdateXML method as the CursorAdapter's UpdateCmd. There are no parameters passed to the UpdateXML method, however, so all the work of determining the changes must be handled within this method. Also, since an XML-type CursorAdapter has no default update mechanism, you must write the code to post the changes to the XML data source. This is all done in the code for UpdateXML (and oxmldom_access), shown in Listing 5. In this code, we use the XMLHTTP object to post the changes to the server. This is done by loading the contents of the UpdateGram property into an XMLDOM (instantiated by the included Access method) with the LoadXML method, opening a connection to the server, setting the content of the request as XML, and then sending the XMLDOM. Any result is returned via the XMLHTTP object's ResponseText property, which is subsequently loaded in the XMLDOM and analyzed for any error messages. If no errors are detected, the update has succeeded and the procedure ends. However, if there are errors, the text of the error message is parsed and included in a user-defined Error so the TableUpdate function can see the failure. Without this code, your TableUpdate call would always return success, even though there might be a problem. To test this code, execute the caxml program, make a change to one of the fields in the cursor, and then issue TableUpdate from the command window. If TableUpdate succeeds, you should be able to see your change on the server. However, if TableUpdate fails, you will need to use the AError function to retrieve the error message generated by SQL Server. If you are curious about the contents of an UpdateGram, you can step through the UpdateXML method of the class and check the contents of the UpdateGram property once you are inside this method. However, if you're not in one of the data modification methods (as specified in the UpdateCmd, InsertCmd, or DeleteCmd properties), you cannot see the contents of the UpdateGram property. Listing 6 shows the contents of an UpdateGram when the ContactName field has been changed on the Customer record with the ID of 'CACTU'. As you can see, SQLXML can read this document and easily build an Update-SQL statement, which it then posts to the SQL Server. The updg:sync element is closely related to starting a transaction; therefore, if you have multiple tables to update, you could combine them into a single UpdateGram, ensuring they are encapsulated within this element, and they will be wrapped within a transaction. Final Thoughts 14/15

15 In this article, we've covered a lot of ground, showing the four "faces" of the new CursorAdapter class. You've seen how to build the CursorAdapter through the DataEnvironment and CursorAdapter builders, through the visual class designer, and through a PRG. You've also seen the basics of building CursorAdapter classes for native, ODBC, OLE DB or XML data access, and how to make each one of these classes updatable as well. The next step is to think about how to apply these classes to your everyday development efforts. In my opinion, I can see the CursorAdapter class working very well in the UI layer of any application, and also in certain kinds of business objects where there is lots of processing code to implement. The CursorAdapter, as noted earlier, is not a good choice of object for passing data between tiers, as it converts everything into a non-portable VFP cursor. However, in a scenario where a business object uses a CursorAdapter class, it can receive the data from the data source, and then process that data using standard VFP commands and functions, since it is in a VFP cursor. When finished, that data could be converted to a more suitable type for cross-tier marshalling, such as XML. The other advantage of the CursorAdapter is the common OOP interface, regardless of the type of data that it accesses. Even with the XML version, which requires the most coding to make updatable, we still retrieved the data using CursorFill, updated data with TableUpdate, and retrieved errors with AError, as with every other type of CursorAdapter. With a little forethought and planning, you could conceivably build a reusable set of classes, based upon the CursorAdapter, that could then be tweaked for each individual data source. These classes could be reused between applications or mixed within the same application to standardize the way your application handles data. 15/15

SQL passthrough (SPT) functions, such as SQLCONNECT(), SQLEXEC(), and SQLDISCONNECT(), which are also based on ODBC connections.

SQL passthrough (SPT) functions, such as SQLCONNECT(), SQLEXEC(), and SQLDISCONNECT(), which are also based on ODBC connections. Data Strategies in VFP: Introduction By Doug Hennig Overview There are a lot of ways you can access non-vfp data (such as SQL Server) in VFP applications: remote views, SQL passthrough, ADO, XML... This

More information

Instructor: Craig Duckett. Lecture 14: Tuesday, May 15 th, 2018 Stored Procedures (SQL Server) and MySQL

Instructor: Craig Duckett. Lecture 14: Tuesday, May 15 th, 2018 Stored Procedures (SQL Server) and MySQL Instructor: Craig Duckett Lecture 14: Tuesday, May 15 th, 2018 Stored Procedures (SQL Server) and MySQL 1 Assignment 3 is due LECTURE 20, Tuesday, June 5 th Database Presentation is due LECTURE 20, Tuesday,

More information

Much ADO About Something Doug Hennig

Much ADO About Something Doug Hennig Much ADO About Something Doug Hennig The release of the OLE DB provider for VFP means better performance and scalability for applications that need to access VFP data via ADO. However, there are some interesting

More information

CATCH Me if You Can Doug Hennig

CATCH Me if You Can Doug Hennig CATCH Me if You Can Doug Hennig VFP 8 has structured error handling, featuring the new TRY... CATCH... FINALLY... ENDTRY structure. This powerful new feature provides a third layer of error handling and

More information

CaseComplete Roadmap

CaseComplete Roadmap CaseComplete Roadmap Copyright 2004-2014 Serlio Software Development Corporation Contents Get started... 1 Create a project... 1 Set the vision and scope... 1 Brainstorm for primary actors and their goals...

More information

Arduino IDE Friday, 26 October 2018

Arduino IDE Friday, 26 October 2018 Arduino IDE Friday, 26 October 2018 12:38 PM Looking Under The Hood Of The Arduino IDE FIND THE ARDUINO IDE DOWNLOAD First, jump on the internet with your favorite browser, and navigate to www.arduino.cc.

More information

HELPLINE. Dilwyn Jones

HELPLINE. Dilwyn Jones HELPLINE Dilwyn Jones Remember that you can send me your Helpline queries by email to helpline@quanta.org.uk, or by letter to the address inside the front cover. While we do our best to help, we obviously

More information

Session V-STON Stonefield Query: The Next Generation of Reporting

Session V-STON Stonefield Query: The Next Generation of Reporting Session V-STON Stonefield Query: The Next Generation of Reporting Doug Hennig Overview Are you being inundated with requests from the users of your applications to create new reports or tweak existing

More information

Data Handling Issues, Part I Doug Hennig

Data Handling Issues, Part I Doug Hennig Data Handling Issues, Part I Doug Hennig The ability to handle multiple sets of data is a frequent requirement in business applications. So is the management of primary key values for tables. In this first

More information

By default, the 9i instance will follow this sequence to determine its initialization parameters on most operating systems:

By default, the 9i instance will follow this sequence to determine its initialization parameters on most operating systems: ORACLE9I: KISS YOUR INIT.ORA GOODBYE! Dan Norris, norris@celeritas.com, Celeritas Technologies, LLC INTRODUCTION AND OVERVIEW Oracle9i has certainly changed the RDBMS world with all of its new features

More information

How to Improve Your Campaign Conversion Rates

How to Improve Your  Campaign Conversion Rates How to Improve Your Email Campaign Conversion Rates Chris Williams Author of 7 Figure Business Models How to Exponentially Increase Conversion Rates I'm going to teach you my system for optimizing an email

More information

In our first lecture on sets and set theory, we introduced a bunch of new symbols and terminology.

In our first lecture on sets and set theory, we introduced a bunch of new symbols and terminology. Guide to and Hi everybody! In our first lecture on sets and set theory, we introduced a bunch of new symbols and terminology. This guide focuses on two of those symbols: and. These symbols represent concepts

More information

Lab 7 Macros, Modules, Data Access Pages and Internet Summary Macros: How to Create and Run Modules vs. Macros 1. Jumping to Internet

Lab 7 Macros, Modules, Data Access Pages and Internet Summary Macros: How to Create and Run Modules vs. Macros 1. Jumping to Internet Lab 7 Macros, Modules, Data Access Pages and Internet Summary Macros: How to Create and Run Modules vs. Macros 1. Jumping to Internet 1. Macros 1.1 What is a macro? A macro is a set of one or more actions

More information

Azon Master Class. By Ryan Stevenson Guidebook #5 WordPress Usage

Azon Master Class. By Ryan Stevenson   Guidebook #5 WordPress Usage Azon Master Class By Ryan Stevenson https://ryanstevensonplugins.com/ Guidebook #5 WordPress Usage Table of Contents 1. Widget Setup & Usage 2. WordPress Menu System 3. Categories, Posts & Tags 4. WordPress

More information

This lesson is part 5 of 5 in a series. You can go to Invoice, Part 1: Free Shipping if you'd like to start from the beginning.

This lesson is part 5 of 5 in a series. You can go to Invoice, Part 1: Free Shipping if you'd like to start from the beginning. Excel Formulas Invoice, Part 5: Data Validation "Oh, hey. Um we noticed an issue with that new VLOOKUP function you added for the shipping options. If we don't type the exact name of the shipping option,

More information

Oracle SQL. murach s. and PL/SQL TRAINING & REFERENCE. (Chapter 2)

Oracle SQL. murach s. and PL/SQL TRAINING & REFERENCE. (Chapter 2) TRAINING & REFERENCE murach s Oracle SQL and PL/SQL (Chapter 2) works with all versions through 11g Thanks for reviewing this chapter from Murach s Oracle SQL and PL/SQL. To see the expanded table of contents

More information

Taking Control Doug Hennig

Taking Control Doug Hennig Taking Control Doug Hennig This month, Doug Hennig discusses a simple way to make anchoring work the way you expect it to and how to control the appearance and behavior of a report preview window. There

More information

Relevancy Workbench Module. 1.0 Documentation

Relevancy Workbench Module. 1.0 Documentation Relevancy Workbench Module 1.0 Documentation Created: Table of Contents Installing the Relevancy Workbench Module 4 System Requirements 4 Standalone Relevancy Workbench 4 Deploy to a Web Container 4 Relevancy

More information

XML Web Services Basics

XML Web Services Basics MSDN Home XML Web Services Basics Page Options Roger Wolter Microsoft Corporation December 2001 Summary: An overview of the value of XML Web services for developers, with introductions to SOAP, WSDL, and

More information

Multi-User and Data Buffering Issues

Multi-User and Data Buffering Issues Multi-User and Data Buffering Issues Doug Hennig Partner Stonefield Systems Group Inc. 1112 Winnipeg Street, Suite 200 Regina, SK Canada S4R 1J6 Phone: (306) 586-3341 Fax: (306) 586-5080 Email: dhennig@stonefield.com

More information

Integration Services. Creating an ETL Solution with SSIS. Module Overview. Introduction to ETL with SSIS Implementing Data Flow

Integration Services. Creating an ETL Solution with SSIS. Module Overview. Introduction to ETL with SSIS Implementing Data Flow Pipeline Integration Services Creating an ETL Solution with SSIS Module Overview Introduction to ETL with SSIS Implementing Data Flow Lesson 1: Introduction to ETL with SSIS What Is SSIS? SSIS Projects

More information

Password Protect an Access Database

Password Protect an Access Database Access a Password Protected Microsoft Access Database from within Visual Basic 6 Have you ever wanted to password protect an Access Database that is a Data Store (a repository of Data) used in one of your

More information

User's Guide c-treeace SQL Explorer

User's Guide c-treeace SQL Explorer User's Guide c-treeace SQL Explorer Contents 1. c-treeace SQL Explorer... 4 1.1 Database Operations... 5 Add Existing Database... 6 Change Database... 7 Create User... 7 New Database... 8 Refresh... 8

More information

Enterprise Reporting -- APEX

Enterprise Reporting -- APEX Quick Reference Enterprise Reporting -- APEX This Quick Reference Guide documents Oracle Application Express (APEX) as it relates to Enterprise Reporting (ER). This is not an exhaustive APEX documentation

More information

Linked Lists. What is a Linked List?

Linked Lists. What is a Linked List? Linked Lists Along with arrays, linked lists form the basis for pretty much every other data stucture out there. This makes learning and understand linked lists very important. They are also usually the

More information

Xton Access Manager GETTING STARTED GUIDE

Xton Access Manager GETTING STARTED GUIDE Xton Access Manager GETTING STARTED GUIDE XTON TECHNOLOGIES, LLC PHILADELPHIA Copyright 2017. Xton Technologies LLC. Contents Introduction... 2 Technical Support... 2 What is Xton Access Manager?... 3

More information

It Might Be Valid, But It's Still Wrong Paul Maskens and Andy Kramek

It Might Be Valid, But It's Still Wrong Paul Maskens and Andy Kramek Seite 1 von 5 Issue Date: FoxTalk July 2000 It Might Be Valid, But It's Still Wrong Paul Maskens and Andy Kramek This month, Paul Maskens and Andy Kramek discuss the problems of validating data entry.

More information

Naming Things in Adafruit IO

Naming Things in Adafruit IO Naming Things in Adafruit IO Created by Adam Bachman Last updated on 2016-07-27 09:29:53 PM UTC Guide Contents Guide Contents Introduction The Two Feed Identifiers Name Key Aside: Naming things in MQTT

More information

Manual Trigger Sql Server 2008 Inserted Table Examples Insert

Manual Trigger Sql Server 2008 Inserted Table Examples Insert Manual Trigger Sql Server 2008 Inserted Table Examples Insert This tutorial is applicable for all versions of SQL Server i.e. 2005, 2008, 2012, Whenever a row is inserted in the Customers Table, the following

More information

Client Side JavaScript and AJAX

Client Side JavaScript and AJAX Client Side JavaScript and AJAX Client side javascript is JavaScript that runs in the browsers of people using your site. So far all the JavaScript code we've written runs on our node.js server. This is

More information

An Introduction to Stored Procedures in MySQL 5 by Federico Leven6 Apr 2011

An Introduction to Stored Procedures in MySQL 5 by Federico Leven6 Apr 2011 An Introduction to Stored Procedures in MySQL 5 by Federico Leven6 Apr 21 MySQL 5 introduced a plethora of new features - stored procedures being one of the most significant. In this tutorial, we will

More information

Centralized Log Hosting Manual for User

Centralized Log Hosting Manual for User Centralized Log Hosting Manual for User English Version 1.0 Page 1 of 31 Table of Contents 1 WELCOME...3 2 WAYS TO ACCESS CENTRALIZED LOG HOSTING PAGE...4 3 YOUR APPS IN KSC CENTRALIZED LOG HOSTING WEB...5

More information

Oracle Big Data Cloud Service, Oracle Storage Cloud Service, Oracle Database Cloud Service

Oracle Big Data Cloud Service, Oracle Storage Cloud Service, Oracle Database Cloud Service Demo Introduction Keywords: Oracle Big Data Cloud Service, Oracle Storage Cloud Service, Oracle Database Cloud Service Goal of Demo: Oracle Big Data Preparation Cloud Services can ingest data from various

More information

BMC Remedyforce Troubleshooting Document

BMC Remedyforce Troubleshooting Document Troubleshooting Document BMC Remedyforce Troubleshooting Document September 2015 Table of Contents 1.0 Salesforce Apex Governor Limits Overview 2 2.0 SOQL Queries Limits 3 3.0 Triggers and Order of Execution

More information

Instructor: Craig Duckett. Lecture 03: Tuesday, April 3, 2018 SQL Sorting, Aggregates and Joining Tables

Instructor: Craig Duckett. Lecture 03: Tuesday, April 3, 2018 SQL Sorting, Aggregates and Joining Tables Instructor: Craig Duckett Lecture 03: Tuesday, April 3, 2018 SQL Sorting, Aggregates and Joining Tables 1 Assignment 1 is due LECTURE 5, Tuesday, April 10 th, 2018 in StudentTracker by MIDNIGHT MID-TERM

More information

Sql Server Check If Global Temporary Table Exists

Sql Server Check If Global Temporary Table Exists Sql Server Check If Global Temporary Table Exists I am trying to create a temp table from the a select statement so that I can get the schema information from the temp I have yet to see a valid justification

More information

mid=81#15143

mid=81#15143 Posted by joehillen - 06 Aug 2012 22:10 I'm having a terrible time trying to find the Lightworks source code. I was under the impression that Lightworks was open source. Usually that means that it's possible

More information

The Mother of All TreeViews, Part 2 Doug Hennig

The Mother of All TreeViews, Part 2 Doug Hennig The Mother of All TreeViews, Part 2 Doug Hennig Last month, Doug presented a reusable class that encapsulates most of the desired behavior for a TreeView control. He discussed controlling the appearance

More information

USER GUIDE MADCAP DOC-TO-HELP 5. Getting Started

USER GUIDE MADCAP DOC-TO-HELP 5. Getting Started USER GUIDE MADCAP DOC-TO-HELP 5 Getting Started Copyright 2018 MadCap Software. All rights reserved. Information in this document is subject to change without notice. The software described in this document

More information

PL/SQL Block structure

PL/SQL Block structure PL/SQL Introduction Disadvantage of SQL: 1. SQL does t have any procedural capabilities. SQL does t provide the programming technique of conditional checking, looping and branching that is vital for data

More information

CANVAS DEEP INTEGRATION WITH LAUNCHPAD (FOR INSTRUCTORS)

CANVAS DEEP INTEGRATION WITH LAUNCHPAD (FOR INSTRUCTORS) CANVAS DEEP INTEGRATION WITH LAUNCHPAD (FOR INSTRUCTORS) Table of Contents Get started... 3 What are the first steps I should take to make this process go smoothly?... 4 Confirm that the Macmillan Learning

More information

JUnit Test Patterns in Rational XDE

JUnit Test Patterns in Rational XDE Copyright Rational Software 2002 http://www.therationaledge.com/content/oct_02/t_junittestpatternsxde_fh.jsp JUnit Test Patterns in Rational XDE by Frank Hagenson Independent Consultant Northern Ireland

More information

In this chapter, I m going to show you how to create a working

In this chapter, I m going to show you how to create a working Codeless Database Programming In this chapter, I m going to show you how to create a working Visual Basic database program without writing a single line of code. I ll use the ADO Data Control and some

More information

Extending the VFP 9 IDE Doug Hennig

Extending the VFP 9 IDE Doug Hennig Extending the VFP 9 IDE Doug Hennig One of the key themes in VFP 9 is extensibility. You can extend the VFP 9 Report Designer through report events and the reporting engine through the new ReportListener

More information

Customizing Access Parameter Queries

Customizing Access Parameter Queries [Revised and Updated 15 August 2018] Everyone likes parameter queries! The database developer doesn't have to anticipate the user's every requirement, and the user can vary their enquiries without having

More information

Book IX. Developing Applications Rapidly

Book IX. Developing Applications Rapidly Book IX Developing Applications Rapidly Contents at a Glance Chapter 1: Building Master and Detail Pages Chapter 2: Creating Search and Results Pages Chapter 3: Building Record Insert Pages Chapter 4:

More information

Chapter 18 Outputting Data

Chapter 18 Outputting Data Chapter 18: Outputting Data 231 Chapter 18 Outputting Data The main purpose of most business applications is to collect data and produce information. The most common way of returning the information is

More information

SurveyToGo Scripting Best Practices

SurveyToGo Scripting Best Practices www.dooblo.com SurveyToGo Scripting Best Practices Authored by: Ofer Heijmans - Dooblo Revision 1.0, March 2016 Table of Content 1 OVERVIEW... 3 2 VARIABLE NAMES... 3 3 SHORT IDS DISPLAY... 4 4 ANSWER

More information

Module Overview. Instructor Notes (PPT Text)

Module Overview. Instructor Notes (PPT Text) Module 06 - Debugging and Troubleshooting SSIS Packages Page 1 Module Overview 12:55 AM Instructor Notes (PPT Text) As you develop more complex SQL Server Integration Services (SSIS) packages, it is important

More information

Group Leader Quickstart Guide. Original photo by Trey Ratcliff

Group Leader Quickstart Guide. Original photo by Trey Ratcliff Group Leader Quickstart Guide Original photo by Trey Ratcliff In This Guide Group Setup Create a Group Profile Post Types and Restrictions Group Tags Ideal Group Size Group Nickname Default Invitation

More information

Oracle Cloud. Content and Experience Cloud Android Mobile Help E

Oracle Cloud. Content and Experience Cloud Android Mobile Help E Oracle Cloud Content and Experience Cloud Android Mobile Help E82091-01 Februrary 2017 Oracle Cloud Content and Experience Cloud Android Mobile Help, E82091-01 Copyright 2017, Oracle and/or its affiliates.

More information

Persistence Designer User s Guide. Version 3.4

Persistence Designer User s Guide. Version 3.4 Persistence Designer User s Guide Version 3.4 PERSISTENCE DESIGNER... 4 ADDING PERSISTENCE SUPPORT... 5 PERSIST AS COLUMNS OF A TABLE... 6 PERSIST ENTIRE MESSAGE AS XML... 7 DATABASE TABLE DESIGN... 8

More information

Qualtrics Training Guide for Baruch College. Table of Contents. Qualtrics Training Guide. I. Basic Overview

Qualtrics Training Guide for Baruch College. Table of Contents. Qualtrics Training Guide. I. Basic Overview Qualtrics Training Guide for Baruch College Qualtrics Training Guide Qualtrics is a web-based application for creating and administering online surveys and polls. The service is available to all Baruch

More information

Formal Methods of Software Design, Eric Hehner, segment 24 page 1 out of 5

Formal Methods of Software Design, Eric Hehner, segment 24 page 1 out of 5 Formal Methods of Software Design, Eric Hehner, segment 24 page 1 out of 5 [talking head] This lecture we study theory design and implementation. Programmers have two roles to play here. In one role, they

More information

4.6.5 Data Sync User Manual.

4.6.5 Data Sync User Manual. 4.6.5 Data Sync User Manual www.badgepass.com Table of Contents Table of Contents... 2 Configuration Utility... 3 System Settings... 4 Profile Setup... 5 Setting up the Source Data... 6 Source Filters...

More information

Use mail merge to create and print letters and other documents

Use mail merge to create and print letters and other documents Use mail merge to create and print letters and other documents Contents Use mail merge to create and print letters and other documents... 1 Set up the main document... 1 Connect the document to a data

More information

Azon Master Class. By Ryan Stevenson Guidebook #7 Site Construction 2/2

Azon Master Class. By Ryan Stevenson   Guidebook #7 Site Construction 2/2 Azon Master Class By Ryan Stevenson https://ryanstevensonplugins.com/ Guidebook #7 Site Construction 2/2 Table of Contents 1. Creation of Additional Site Pages & Content 2. Custom HTML Menus for Category

More information

SAPtips. Journal. Creating a Well-Developed Master Data Management Solution in BW. August/September 2005 Volume III Issue 4. SAPtips.

SAPtips. Journal. Creating a Well-Developed Master Data Management Solution in BW. August/September 2005 Volume III Issue 4. SAPtips. Page 1 Creating a Well-Developed Master Data Management Solution in BW By Arthur Pat Pesa, arthurpesa, inc Editor Note: Pat Pesa delivers another one-two-three punch with his discussion of developing,

More information

Report Commander 2 User Guide

Report Commander 2 User Guide Report Commander 2 User Guide Report Commander 2.5 Generated 6/26/2017 Copyright 2017 Arcana Development, LLC Note: This document is generated based on the online help. Some content may not display fully

More information

Introduction to Computer Science and Business

Introduction to Computer Science and Business Introduction to Computer Science and Business The Database Programming with PL/SQL course introduces students to the procedural language used to extend SQL in a programatic manner. This course outline

More information

PDFBlaster Installation Overview

PDFBlaster Installation Overview Page 1 of 51 PDFBlaster Installation Overview Step 1 If you downloaded the PDFBlaster setup file (pdfblaster.exe) from the Internet, locate where you downloaded this file and then double-click on it to

More information

Earthwork 3D for Dummies Doing a digitized dirt takeoff calculation the swift and easy way

Earthwork 3D for Dummies Doing a digitized dirt takeoff calculation the swift and easy way Introduction Earthwork 3D for Dummies Doing a digitized dirt takeoff calculation the swift and easy way Getting to know you Earthwork has inherited its layout from its ancestors, Sitework 98 and Edge.

More information

SQL - Tables. SQL - Create a SQL Table. SQL Create Table Query:

SQL - Tables. SQL - Create a SQL Table. SQL Create Table Query: SQL - Tables Data is stored inside SQL tables which are contained within SQL databases. A single database can house hundreds of tables, each playing its own unique role in th+e database schema. While database

More information

Proficy* HMI/SCADA - ifix U SING V ISICONX

Proficy* HMI/SCADA - ifix U SING V ISICONX Proficy* HMI/SCADA - ifix U SING V ISICONX Version 5.5 January 2012 All rights reserved. No part of this publication may be reproduced in any form or by any electronic or mechanical means, including photocopying

More information

Ruby on Rails Welcome. Using the exercise files

Ruby on Rails Welcome. Using the exercise files Ruby on Rails Welcome Welcome to Ruby on Rails Essential Training. In this course, we're going to learn the popular open source web development framework. We will walk through each part of the framework,

More information

8 Querying and Selecting Data

8 Querying and Selecting Data 8 Querying and Selecting Data In this chapter, we will cover the following recipes: ff ff ff ff ff Constructing proper attribute query syntax Creating feature layers and table views Selecting features

More information

One of the fundamental kinds of websites that SharePoint 2010 allows

One of the fundamental kinds of websites that SharePoint 2010 allows Chapter 1 Getting to Know Your Team Site In This Chapter Requesting a new team site and opening it in the browser Participating in a team site Changing your team site s home page One of the fundamental

More information

A New IDE Add-on: FoxTabs Doug Hennig

A New IDE Add-on: FoxTabs Doug Hennig A New IDE Add-on: FoxTabs Doug Hennig FoxTabs provides easy access to all open windows in your VFP IDE. However, not only is it a great tool, it uses some very cool techniques to do its magic, including

More information

CLIENT ONBOARDING PLAN & SCRIPT

CLIENT ONBOARDING PLAN & SCRIPT CLIENT ONBOARDING PLAN & SCRIPT FIRST STEPS Receive Order form from Sales Representative. This may come in the form of a BPQ from client Ensure the client has an account in Reputation Management and in

More information

CONTENTS: While loops Class (static) variables and constants Top Down Programming For loops Nested Loops

CONTENTS: While loops Class (static) variables and constants Top Down Programming For loops Nested Loops COMP-202 Unit 4: Programming with Iterations Doing the same thing again and again and again and again and again and again and again and again and again... CONTENTS: While loops Class (static) variables

More information

Masking Engine User Guide. October, 2017

Masking Engine User Guide. October, 2017 Masking Engine User Guide October, 2017 Masking Engine User Guide You can find the most up-to-date technical documentation at: docs.delphix.com The Delphix Web site also provides the latest product updates.

More information

CLIENT ONBOARDING PLAN & SCRIPT

CLIENT ONBOARDING PLAN & SCRIPT CLIENT ONBOARDING PLAN & SCRIPT FIRST STEPS Receive Order form from Sales Representative. This may come in the form of a BPQ from client Ensure the client has an account in Reputation Management and in

More information

IntelliSense at Runtime Doug Hennig

IntelliSense at Runtime Doug Hennig IntelliSense at Runtime Doug Hennig VFP 9 provides support for IntelliSense at runtime. This month, Doug Hennig examines why this is useful, discusses how to implement it, and extends Favorites for IntelliSense

More information

1Z0-144 Q&As Oracle Database 11g: Program with PL/ SQL

1Z0-144 Q&As Oracle Database 11g: Program with PL/ SQL CertBus.com 1Z0-144 Q&As Oracle Database 11g: Program with PL/ SQL Pass Oracle 1Z0-144 Exam with 100% Guarantee Free Download Real Questions & Answers PDF and VCE file from: 100% Passing Guarantee 100%

More information

6.001 Notes: Section 8.1

6.001 Notes: Section 8.1 6.001 Notes: Section 8.1 Slide 8.1.1 In this lecture we are going to introduce a new data type, specifically to deal with symbols. This may sound a bit odd, but if you step back, you may realize that everything

More information

6.001 Notes: Section 15.1

6.001 Notes: Section 15.1 6.001 Notes: Section 15.1 Slide 15.1.1 Our goal over the next few lectures is to build an interpreter, which in a very basic sense is the ultimate in programming, since doing so will allow us to define

More information

Automatically Generate Xml Schema From Sql Server Table

Automatically Generate Xml Schema From Sql Server Table Automatically Generate Xml Schema From Sql Server Table Working through Chapter 7 of the Querying Microsoft SQL Server 2012 book A table that uses an XML Schema Collection as a column's data type. You

More information

Class #7 Guidebook Page Expansion. By Ryan Stevenson

Class #7 Guidebook Page Expansion. By Ryan Stevenson Class #7 Guidebook Page Expansion By Ryan Stevenson Table of Contents 1. Class Purpose 2. Expansion Overview 3. Structure Changes 4. Traffic Funnel 5. Page Updates 6. Advertising Updates 7. Prepare for

More information

## Version: FoxPro 7.0 ## Figures: ## File for Subscriber Downloads: Publishing Your First Web Service Whil Hentzen

## Version: FoxPro 7.0 ## Figures: ## File for Subscriber Downloads: Publishing Your First Web Service Whil Hentzen ## Version: FoxPro 7.0 ## Figures: ## File for Subscriber Downloads: Publishing Your First Web Service Whil Hentzen Web Services The Buzzword of the 02s! It s nothing really new, however, any more than

More information

As a programmer, you know how easy it can be to get lost in the details

As a programmer, you know how easy it can be to get lost in the details Chapter 1 Congratulations, Your Problem Has Already Been Solved In This Chapter Introducing design patterns Knowing how design patterns can help Extending object-oriented programming Taking a look at some

More information

Tracking changes in Word 2007 Table of Contents

Tracking changes in Word 2007 Table of Contents Tracking changes in Word 2007 Table of Contents TRACK CHANGES: OVERVIEW... 2 UNDERSTANDING THE TRACK CHANGES FEATURE... 2 HOW DID THOSE TRACKED CHANGES AND COMMENTS GET THERE?... 2 WHY MICROSOFT OFFICE

More information

Using Jive for Outlook

Using Jive for Outlook Using Jive for Outlook TOC 2 Contents Using Jive for Outlook...3 Client System Requirements... 3 Getting Started with Jive for Outlook... 3 Jview and View as email...4 Viewing Social Information... 4 Finding

More information

The Definitive Guide to Office 365 External Sharing. An ebook by Sharegate

The Definitive Guide to Office 365 External Sharing. An ebook by Sharegate The Definitive Guide to Office 365 External Sharing An ebook by Sharegate The Definitive Guide to External Sharing In any organization, whether large or small, sharing content with external users is an

More information

Working with the Registry. The Registry class makes it easy. The Registry Structure. January, By Tamar E. Granor

Working with the Registry. The Registry class makes it easy. The Registry Structure. January, By Tamar E. Granor January, 2002 Working with the Registry The Registry class makes it easy By Tamar E. Granor The Windows Registry is full of information about the user, his or her settings, the installed software and the

More information

Importing source database objects from a database

Importing source database objects from a database Importing source database objects from a database We are now at the point where we can finally import our source database objects, source database objects. We ll walk through the process of importing from

More information

Microsoft Access Database How to Import/Link Data

Microsoft Access Database How to Import/Link Data Microsoft Access Database How to Import/Link Data Firstly, I would like to thank you for your interest in this Access database ebook guide; a useful reference guide on how to import/link data into an Access

More information

Teiid Designer User Guide 7.5.0

Teiid Designer User Guide 7.5.0 Teiid Designer User Guide 1 7.5.0 1. Introduction... 1 1.1. What is Teiid Designer?... 1 1.2. Why Use Teiid Designer?... 2 1.3. Metadata Overview... 2 1.3.1. What is Metadata... 2 1.3.2. Editing Metadata

More information

PHP for PL/SQL Developers. Lewis Cunningham JP Morgan Chase

PHP for PL/SQL Developers. Lewis Cunningham JP Morgan Chase PHP for PL/SQL Developers Lewis Cunningham JP Morgan Chase 1 What is PHP? PHP is a HTML pre-processor PHP allows you to generate HTML dynamically PHP is a scripting language usable on the web, the server

More information

TourMaker Reference Manual. Intro

TourMaker Reference Manual. Intro TourMaker Reference Manual Intro Getting Started Tutorial: Edit An Existing Tour Key Features & Tips Tutorial: Create A New Tour Posting A Tour Run Tours From Your Hard Drive Intro The World Wide Web is

More information

Contents. What's New. Version released. Newsletter #31 (May 24, 2008) What's New New version released, version 4.3.3

Contents. What's New. Version released. Newsletter #31 (May 24, 2008) What's New New version released, version 4.3.3 Campground Master Newsletter #31 (May 24, 2008) 1 Newsletter #31 (May 24, 2008) Contents What's New New version released, version 4.3.3 Q & A Retrieving credit card information Guarantee Info missing the

More information

CheckBook Pro 2 Help

CheckBook Pro 2 Help Get started with CheckBook Pro 9 Introduction 9 Create your Accounts document 10 Name your first Account 11 Your Starting Balance 12 Currency 13 We're not done yet! 14 AutoCompletion 15 Descriptions 16

More information

Database to XML Wizard

Database to XML Wizard Database to XML Wizard Jitterbit Connect TM provides a fast, easy route to data transformation. This is made possible through a wizard-based integration tool built directly into Jitterbit. The wizard executes

More information

Oracle Sql Describe Schema Query To Find Table

Oracle Sql Describe Schema Query To Find Table Oracle Sql Describe Schema Query To Find Table And, notably, Oracle still doesn't support the information schema. Views in the /d (object_name) will describe the schema of the table or view. Not sure how.

More information

Enable the Always Offline Mode to Provide Faster Access to Files

Enable the Always Offline Mode to Provide Faster Access to Files Enable the Always Offline Mode to Provide Faster Access to Files 13 out of 16 rated this helpful - Rate this topic Published: April 18, 2012 Updated: July 3, 2013 Applies To: Windows 8, Windows 8.1, Windows

More information

Azon Master Class. By Ryan Stevenson Guidebook #7 Site Construction 2/3

Azon Master Class. By Ryan Stevenson   Guidebook #7 Site Construction 2/3 Azon Master Class By Ryan Stevenson https://ryanstevensonplugins.com/ Guidebook #7 Site Construction 2/3 Table of Contents 1. Creation of Site Pages 2. Category Pages Creation 3. Home Page Creation Creation

More information

Lesson 3 Transcript: Part 1 of 2 - Tools & Scripting

Lesson 3 Transcript: Part 1 of 2 - Tools & Scripting Lesson 3 Transcript: Part 1 of 2 - Tools & Scripting Slide 1: Cover Welcome to lesson 3 of the db2 on Campus lecture series. Today we're going to talk about tools and scripting, and this is part 1 of 2

More information

How To Add Songs To Ipod Without Syncing >>>CLICK HERE<<<

How To Add Songs To Ipod Without Syncing >>>CLICK HERE<<< How To Add Songs To Ipod Without Syncing Whole Library Create a playlist, adding all the songs you want to put onto your ipod, then under the How to add music from ipod to itunes without clearing itunes

More information

What's New. Version 9.2 release. Campground Master Contents 1. Contents. A couple quick reminders:

What's New. Version 9.2 release. Campground Master Contents 1. Contents. A couple quick reminders: Campground Master Contents 1 Contents A couple quick reminders: Make Backups! It's so sad when we hear from someone whose computer has crashed and they have no backup of their data to restore from. It's

More information

COMP-202 Unit 4: Programming with Iterations

COMP-202 Unit 4: Programming with Iterations COMP-202 Unit 4: Programming with Iterations Doing the same thing again and again and again and again and again and again and again and again and again... CONTENTS: While loops Class (static) variables

More information

Active Server Pages Architecture

Active Server Pages Architecture Active Server Pages Architecture Li Yi South Bank University Contents 1. Introduction... 2 1.1 Host-based databases... 2 1.2 Client/server databases... 2 1.3 Web databases... 3 2. Active Server Pages...

More information