#Revised - 10/05/2003
#Author  - Ray Hoover
#
##f3
\rDriver Info\r

Enter the applicable ODBC Driver followed by ";". This field value
is the first part of your DSN.  

Examples:
	
	MS Access - driver={Microsoft Access Driver (*.mdb)};
	dBase     - driver={Microsoft dbase Driver (*.dbf)};
	MySQL     - driver={MySQL ODBC 3.51 Driver};
##f4
\rDB Info\r

Enter the database path, UID, etc. to be appended to the driver info
to create the complete DSN followed by ";". Values should seperated
with ";" 

	Examples:	

	MS Access - DBQ=C:/fp/mdb/filepro.mdb
	dBase	  - DBQ=c:/fpodbc/fp/mdb/customer.dbf
	MySQL	  - DBI=SQLsample;database=ray;server=localhost
	 	
##f5
\rTable Name\r

Enter the table name within the data source that you want to view and
update.

	Examples:

	The sample MS Access database filepro.mdb contains tables 
	"customer" and "zipcode". Enter customer or zipcode as the 
	table name.
##f6

\rScreen Name\r

Enter the screen name you want to use for this sample record

	Examples:

	mymedia, customer, view

Note: You can define additional screens for your samples as desired,
      but if you don't want to take the time select "view".
##f7
\rSample Key\r

Specify the key to use when accessing canned SQL statements in file
"odbc_samples_SQL". This key allows you to retreive and use SQL 
statements in file odbc_samples_SQL for demonstration and testing
with your own data sources.

This key is only required if you plan to use the SQL sample statements
for your own data sources. 
##f8
\rKey Field\r

Identify the field position in the data source to be used as the 
primary key for accessing the data source. This is noramlly a numeric
field that is unique for each record of the data source. However, the
the only requirement is that the field value in the data source is 
unique so that the correct record is updated.
		 
##fpodbc
@@@ Low-Level,ODBC_CONNECTION,RecordSet
\rLow-Level fileProODBC\r

Provides low-level access to ODBC data sources, filePro now includes ODBC
"objects". These are called ODBC_CONNECTION, and ODBC.

\rThe ODBC_CONNECTION object\r

The first part of creating an ODBC recordset is to establish a connection to
an ODBC server.  This is done by creating an ODBC_CONNECTION object:

	\ihandle = new ODBC_CONNECTION( [CONNECTION_STRING] )\i

where "CONNECTION_STRING" is the connection string for the server. If none is
specified, or an empty value is provided, Windows will ask for the data source
at runtime. Also, if insufficient information is provided in the connection
string, such as not including the password for a password-protected dataset,
Windows will ask for that information at runtime.

The return value is a handle to the new object. If the value is less than or
equal to zero, the connection failed.
@@@
\rLow-Level fileProODBC\r (cont'd)

Once the connection has been established, this handle is used to create the
ODBC recordset object.

\rExample connection strings:\r

        \iDriver={Microsoft Access Driver (*.mdb)};DBQ=c:\\mdb\\filepro.mdb;\i
        \iFileDSN=CustomerFileDSN;\i

        \iDriver={Microsoft dBase Driver (*.dbf)};DBQ=c:\\mdb\\customer.dbf;\i
        \iFileDSN=CustomerdbaseDSN;\i

Note that multiple recordsets can be created on a single connection.

@@@ ODBC_objects,Query
\rODBC objects\r

The second part of creating an ODBC recordset is to create the ODBC object.

        \ihandle = new ODBC( connection_handle [ , query ] )\i

where "connection_handle" is the handle of the ODBC_CONNECTION object created
earlier and "query" is the optional SQL query to be used to create the recordset.
The return value is a handle to the new object. If the value is less than or
equal to zero, the recordset creation failed. (Note that, even if you specify
an invalid query, the creation can still succeed. It will simply contain no
data.) If a query is not specified when the object is created, you must
specify it later by using:

	ODBC handle QUERY query [ DYNASET|SNAPSHOT ] [ BOOKMARKS ]

The optional DYNASET and SNAPSHOT flags force those modes to be used for the
recordset.  If neither is specified, the driver's default mode is used.  The
optional BOOKMARKS flag enables bookmark support, if supported by the driver.
@@@
\rODBC objects\r (cont'd)
   
(Note that enabling bookmarks may slow down data retrieval, so dont use it
if you aren't going to be using bookmarks on the recordset.)

A query must be executed prior to accessing any of the records within the
recordset.

Note that you can replace the recordset within an ODBC object by executing
another QUERY command. Doing so will close the existing recordset and create
a new one.

@@@ ODBC_GETFIRST,ODBC_GETLAST,ODBC_GETNEXT,ODBC_GETPREV 
\rMoving around within the recordset\r

Once a recordset has been created, you can move around within it with the
following methods:

	ODBC handle GETFIRST
	ODBC handle GETLAST
	ODBC handle GETPREV
	ODBC handle GETNEXT

These will move the current record to the first, last, previous, and next
records in the set, respectively.Note that you only have sequential access
within recordsets. This is a limitation of ODBC. Note that ODBC does not
include a method of determining the number of records returned within a
recordset. The only way to do so is to GETNEXT through the set, counting
the records, until EOF is reached. If there are no records being modified
on the table by another process, you can run a separate query with "SELECT
COUNT(*)" using the same WHERE clause. (If the table is being modified by
another process, there is no guarantee that both queries will return the
same set of records.)
@@@
\rMoving around within the recordset\r (cont'd)

Note that with some ODBC servers, the GETLAST operation may take a long time
on large recordsets. If you need to access the last record and scroll
backwards through the recordset, it is recommended that you instead use an
ORDER BY clause in descending order, and scroll forwards from the first
record.

@@@ Bookmarks
\rBookmarks\r

Although there is only sequential access through ODBC recordsets, it is
possible (if the server supports it) to "bookmark" your current position and
return to it later.

To retrieve a bookmark to the current position, you use the "BOOKMARK GET"
method:

	ODBC handle BOOKMARK GET bookmark

where "bookmark" is the variable in which to store the bookmark.  This
variable must be at least 16 bytes long, and of type "*". If the bookmark
cannot be made (for example, you didnt specify the BOOKMARKS flag on the
query), the variable will be set to blank.  (ie: bookmark="" will test true.)
In that case, @ODBCERROR[] will contain the error.  If the bookmark succeeds,
the bookmark variable will contain something other than all blanks.

@@@ 
\rBookmarks\r (cont'd)

Example:

        bookmark="" will test false.

The bookmark data is not in a human-readable format.

Once a bookmark has been made, you can return to that position within the
same recordset at any time by using the "BOOKMARK SET" method:

	ODBC handle BOOKMARK SET bookmark

where "bookmark" is the same variable used to GET the bookmark earlier.
Note that there is no guarantee that a bookmark will remain valid for
anything other than the current recordset.  Even executing a REQUERY may
invalidate the bookmark.

Note that there can be multiple bookmarks per recordset.

@@@ REQUERY
\rRefreshing a recordset\r

Not every ODBC data source can return a dynamic data set. Rather, they return
a "snapshot" of the records at the time that the query was executed
(ie: if records are updated, added, or deleted while the recordset is open,
you won't see those changes.)

In order to get an updated snapshot, you need to re-execute the query. Rather
than requiring that your code keep track of the query used, you can simply
"requery" the recordset using the same query as the current set:

        ODBC handle REQUERY

You will be positioned back to the first record, if it exists.

@@@ ODBC_CLOSE
\rClosing a recordset\r

Once you are finished with a recordset, you can close it without deleting
the ODBC object. (This allows future queries to be executed on the same
connection, without the overhead of creating a new object.)

	ODBC handle CLOSE

Once the recordset is closed, no more operations may be performed on it,
aside from DELETE and QUERY.

@@@ Columns,Subscripts,ODBC_TYPE,ODBC_TYPENUM,Class_Members
@@@ ODBC_type_no,SQL_WCHAR,SQL_VARCHAR
\rAccessing columns within the recordset\r

Accessing the columns within a recordset is done with the new @ODBC[] system
array, which is accessed using the syntax:

	@ODBC.handle[subscript]

where "handle" is the handle to the ODBC recordset object, and "subscript" is
the column number or name. As with all system arrays, subscript zero returns
the number of elements within the array, and accessing an out-of-range
subscript returns an empty string. If the number of elements is zero, then
there are no columns available. This could be due to query returning no
records, or positioning the current record to BOF, EOF, or a deleted record.

An enhancement from other system arrays is that "subscript" can be the name
of the column, in addition to the numeric column number.

For example:

        @ODBC.handle["zipcode"]
@@@
\rAccessing columns within the recordset\r (cont'd)

This is especially important when doing "SELECT *" clauses on older ODBC
servers, where the order of the columns cannot be guaranteed. (Or even on
newer servers, where the database can have columns inserted within the
existing structure, thereby changing the column number of the following ones.)
Note that there is currently only read-only access to the recordset.

\rAccessing column information\r

There are "class members" within the @ODBC[] array to access information about
the columns. To return the names of the columns,use the ".NAME" member:

	@ODBC.handle.NAME[column]

Note that expressions and aggregate functions have default names generated by
ODBC.  To explicitly specify a name for the column, you need to append "AS
name" to the expression, such as "SELECT MAX(sales) AS MaxSales"

@@@
\rAccessing columns within the recordset\r (cont'd)

To return the types of the columns (as a server-specific name), use the
".TYPE" member:

        @ODBC.handle.TYPE[column]

To return the types of the columns (as a server-independent number), use the
".TYPENUM" member:

	@ODBC.handle.TYPENUM[column]

        \iODBC type numbers\i

        SQL_GUID            -11
        SQL_WLONGVARCHAR    -10
        SQL_WVARCHAR         -9
        SQL_WCHAR            -8
        SQL_BIT              -7
        SQL_TINYINT          -6
@@@
\rAccessing columns within the recordset\r (cont'd)

        SQL_BIGINT           -5
        SQL_LONGVARBINARY    -4
        SQL_VARBINARY        -3
        SQL_BINARY           -2
        SQL_LONGVARCHAR      -1
        SQL_UNKNOWN_TYPE      0
        SQL_CHAR              1
        SQL_NUMERIC           2
        SQL_DECIMAL           3
        SQL_INTEGER           4
        SQL_SMALLINT          5
        SQL_FLOAT             6
        SQL_REAL              7
        SQL_DOUBLE            8
        SQL_DATE              9
        SQL_TIME             10
        SQL_TIMESTAMP        11
        SQL_VARCHAR          12
@@@
\rAccessing columns within the recordset\r (cont'd)

Note that these members can also use the field name as the subscript.
(Yes, even the ".NAME" member can take a name as its subscript, as redundant
as that may seem.)

@@@ record_state, ODBC_STATE,ODBC_EOF,ODBC_BOF,ODBC_STATE,ODBC_DELETED
\rAccessing the record state\r

To determine the current record state, the following members are defined:

	@ODBC.handle.EOF
	@ODBC.handle.BOF
	@ODBC.handle.DELETED
	@ODBC.handle.STATE

The EOF, BOF, and DELETED members hold whether the current record is
end-of-file, beginning-of-file, or deleted, respectively.  The value is "0"
for false, and "1" for true.

The STATE member holds one of the following:

        0 - The recordset is not open
        1 - The record contains data
        2 - Beginning-of-file
        3 - End-of-file
        4 - Deleted record
@@@ SQL_statements,rawSQL,EXECSQL
\rAccessing raw SQL statements\r

Not every SQL action involves a returned recordset. For example, INSERT INTO
or DELETE clauses. (Actually, SELECT is the only clause that does return a
recordset.) Such statements need to be executed differently than SELECT clauses.

        ODBC_CONNECTION handle EXECSQL sql_statement 
        ODBC handle EXECSQL sql_statement

Although such SQL statements are passed directly to the ODBC server via the
ODBC_CONNECTION object, for convenience you can also use the ODBC recordset
handle, in which case the statement will be passed to the connection object
to which the recordset object is attached.

@@@ ALTER(rawSQL)
\rALTER TABLE Statement\r

Description: Modifies the design of a table after it has been created with
the CREATE statement.

	Syntax:

	ALTER TABLE table ADD COLUMN field type [(size)] 
	ALTER COLUMN field type[(size)] |
	ALTER TABLEDROP COLUMN field


        \iPart\i             \iDescription\i
        table   The name of the table to be altered.
        field   The name of the field to be added to or deleted from table
                or, the name of the field to be altered in table.
        yype    The data type of field.
        size    The field size in characters (Text and Binary fields only).
@@@
\rALTER TABLE Statement\r (cont'd)

Remarks

Using the ALTER TABLE statement you can alter an existing table in several
ways.

	Use ADD COLUMN to add a new field to the table. You specify the field
        name, data type, and (for Text and Binary fields) an optional size.
        For example, the following statement adds a 20-character Text field
        called "Remarks" to the mytest1 table.

		ALTER TABLE mytest1 ADD COLUMN Remarks TEXT(20) 
	
@@@
\rALTER TABLE Statement\r (cont'd)

	Use ALTER COLUMN to change the data type of an existing field. You
        specify the field name, the new data type, and an optional size for
        Text and Binary fields.	For example, the following statement changes
        the data type of a field in the	mytest1 table called "Customer"
        (originally defined as Integer) to a 10-character Text field.

		ALTER TABLE mytest1 ALTER COLUMN Customer TEXT(10) 

	Use DROP COLUMN to delete a field. You specify only the name of the
        field. For example, the following statement drops the column named
        "Remarks" from the table called "mytest1". Note that you can only
        delete one field at a time.

		ALTER TABLE mytest1 DROP COLUMN Remarks 
@@@ CREATE(rawSQL)
\rCREATE TABLE Statement

Description: Creates a new table.
	
 Syntax:
 CREATE TABLE table (field1 type [(size)]  [, field2 type [(size)] [, ...]] )

        \iPart\i             \iDescription\i
        table   The name of the table to be created.
        field1, field2  The name of field or fields to be created in the new
                table. You must create at least one field.
        type    The data type of field in the new table.
        size    The field size in characters (Text and Binary fields only).

Remarks

Use the CREATE TABLE statement to define a new table and its fields and field
constraints. If NOT NULL is specified for a field, then new records are
required to have valid data in that field. You can also use the CREATE INDEX
statement to create a primary key or additional indexes on existing tables.
@@@ DELETE(rawSQL)
\rDELETE Statement\r 

Description: Creates a DELETE query that removes records from one or more of
the tables listed in the FROM clause that satisfy the WHERE clause.

	Syntax:
		DELETE [table.*]
                        FROM table
                        WHERE criteria

        \iPart\i             \iDescription\i

        table    The optional name of the table from which records are deleted.
        table    The name of the table from which records are deleted.
        criteria An expression that determines which records to delete.

@@@ 
\rDELETE Statement\r (cont'd)

Remarks: DELETE is especially useful when you want to delete many records. To
delete an entire table from the database, you can use the DROP statement. If
you use the DROP statement, however, the structure is lost. In contrast, when
you use DELETE, only the data is deleted; the table structure and all of the
table properties, such as field attributes and indexes, remain intact.

A delete query deletes entire records, not just data in specific fields. If
you want to delete values in a specific field, see Update Query to change the
values to NULL.

Notes:

After you remove records using a delete query, you cannot undo the operation.
If you want to know which records were deleted, first examine the results of
a SELECT query that uses the same criteria, and then run the delete query. 

Maintain backup copies of your data at all times. If you delete the wrong
records, you can retrieve them from your backup copies. 
@@@ DROP(rawSQL)
\rDROP Statement\r

Description: Deletes an existing table, procedure, or view from a database,
or deletes an existing index from a table.

Syntax:

 DROP {TABLE table | INDEX index ON table | PROCEDURE procedure | VIEW view}

        \iPart\i             \iDescription\i
	table	  The name of the table to be deleted or the table from which
                  an index is to be deleted.
	procedure The name of the procedure to be deleted.
	view	  The name of the view to be deleted.
	index	  The name of the index to be deleted from table.
@@@ 
\rDROP Statement\r (cont'd)

Remarks

You must close the table before you can delete it or remove an index from it.
You can close a table by using the SELECT statement with another table name or
filePro's ODBC CLOSE function.

You can also use ALTER TABLE to delete an index from a table.
You can use CREATE TABLE to create a table and CREATE INDEX or ALTER TABLE to
create an index. To modify a table, use ALTER TABLE.

@@@ INSERT...INTO(rawSQL)
\rINSERT INTO Statement \r

Description: Adds a record or multiple records to a table. This is referred
to as an append query.

	Syntax:
	
	Multiple-record append query:

	INSERT INTO target [(field1[, field2[, ...]])] [IN externaldatabase] 
		SELECT [source.]field1[, field2[, ...]
		FROM tableexpression

	Single-record append query:

	INSERT INTO target [(field1[, field2[, ...]])]
		VALUES (value1[, value2[, ...])


@@@ 
\rINSERT INTO Statement \r (cont'd)

        \iPart\i             \iDescription\i
        target           The name of the table or query to append records to.
	field1, field2	 Names of the fields to append data to, if following
                         a target argument, or the names of fields to obtain
                         data from, if following a source argument.
	externaldatabase The path to an external database. For a description
        of the path, see the IN clause.
	source		 The name of the table or query to copy records from.
	tableexpression	 The name of the table or tables from which records
                         are inserted. This argument can be a single table
                         name or a compound resulting from an INNER JOING,
                         LEFT JOIN or RIGHT JOIN operation or a saved query.
	value1, value2	 The values to insert into the specific fields of the
                         new record. Each value is inserted into the field that
                         corresponds to the value's position in the list: value1
                         is inserted into field1 of the new record, value2 into
                         field2, and so on. You must separate values with a
                         comma, and enclose text fields in 'quotation' marks.
@@@ 
\rINSERT INTO Statement \r (cont'd)
	
Remarks

You can use the INSERT INTO statement to add a single record to a table using
the single-record append query syntax as shown above. In this case, your code
specifies the name and value for each field of the record. You must specify
each of the fields of the record that a value is to be assigned to and a value
for that field. When you do not specify each field, the default value or NULL
is inserted for missing columns. Records are added to the end of the table.

You can also use INSERT INTO to append a set of records from another table
or query by using the SELECT ... FROM clause as shown above in the multiple-
record append query syntax. In this case, the SELECT clause specifies the
fields to append to the specified target table. INSERT INTO is optional but
when included, precedes the SELECT statement. If your destination table
contains a PRIMARY key, make sure you append unique, non-Null values to
the primary key field or fields; if you do not, the database engine will
not append the records.
@@@ 
\rINSERT INTO Statement \r (cont'd)

If you append records to a table with an AutoNumber field and you want to
renumber the appended records, do not include the AutoNumber field in your
query. Do include the AutoNumber field in the query if you want to retain
the original values from the field. Use the IN clause to append records to
a table in another database.

To create a new table, use the SELECT INTO statement instead to create a
table. Use a SELECT query that uses the same selection criteria as SELECT
INTO or INSERT INTO to mae sure that you are selecting the right records.

An append query copies records from one or more tables to another. The tables
that contain the records you append are not affected by the append query.
Instead of appending existing records from another table, you can specify the
value for each field in a single new record using the VALUES clause. If you
omit the field list, the VALUES clause must include a value for every field
in the table; otherwise, the INSERT operation will fail. Use an additional
INSERT INTO statement with a VALUES clause for each additional record you want
to create.
@@@ SELECT...INTO(rawSQL)
\rSELECT...INTO Statement\r

Description: Creates a new Table from an existing table.

	Syntax:
	
	SELECT field1[, field2[, ...]] INTO newtable [IN externaldatabase]
		FROM source

        \iPart\i             \iDescription\i
        field1, field2   The name of the fields to be copied into the new table.
        newtable         The name of the table to be created. If newtable is
                         the same as the name of an existing table, a trappable
                         error occurs.
        externaldatabase The path to an external database. For a description of
                         the path, see the IN clause.
        source           The name of the existing table from which records are
                         selected. This can be single or multiple tables or
                         query.
@@@ 
\rSELECT...INTO Statement\r (cont'd)

Remarks
You can use make-table queries to archive records, make backup copies of your
tables, or make copies to export to another database or to use as a basis for
reports that display data for a particular time period. For example, you could
produce a Monthly Sales by Region report by running the same make-table query
each month.

Notes 
You may want to define a primary key for the new table. When you create the
table, the fields in the new table inherit the data type and field size of
each field in the query's FROM source, but no other field or table properties
are transferred.

To add data to an existing table, use the INSERT INTO statement instead to
create an append query. To find out which records will be selected before you
run the make-table query, first examine the results of a SELECT statement
that uses the same selection criteria. Make sure that you do not use a table
name that already exists.
@@@ UPDATE(rawSQL)
\rUPDATE Statement\r

Description: Creates an update query that changes values in fields in a
specified table based on specified criteria. 

	Syntax:

	UPDATE table
           SET newvalue
           WHERE criteria;

	Parts - The UPDATE statement has these parts:

        \iPart\i             \iDescription\i
        table    The name of the table containing the data you want to modify.
        newvalue An expression that determines the value to be inserted into
                 a particular field in the updated records.
        criteria An expression that determines which records will be updated.
                 Only records that satisfy the expression are updated.
@@@ 
\rUPDATE Statement\r (cont'd)

Remarks: UPDATE is especially useful when you want to change many records. The
following example will change the Format column values in mytest2 from "Tape"
to "VHS".

        UPDATE mytest2 SET Format='VHS' WHERE Format = 'Tape'

Notes:  UPDATE does not generate a result set. Also, after you update records
        using an update query, you cannot undo the operation. If you want to
        know which records were updated, first examine the results of a SELECT
        query that uses the same criteria, and then run the update query.

        Maintain backup copies of your data at all times. If you update the
        wrong records, you can retrieve them from your backup copies. 
@@@ Errors,@ODBCERROR
\rHandling errors\r

Currently, there really isn't much error handling ability included.
If a query fails, for example if no records are selected due to a WHERE
clause, then @ODBC.handle["0"] will return zero, indicating no data is
available. (Note that this is also true should you scroll to BOF or EOF, or
position to a deleted record.) The system array @ODBCERROR[] will contain the
text of the most recent ODBC failure. Subscript 1 will contain the human-
readable text of the error, and subscript 2 will contain the state information
from which you can programmatically extract the error information. For more
information, see

http://msdn.microsoft.com/library/en-us/vcmfc98/html/_mfc_cdbexception.3a3a.m_strstatenativeorigin.asp

@@@ Example
\rExample\r

The following example opens a connection to a Microsoft Access database, and
allows you to scroll through it. Press "Q" to enter a query. This will display
the first record returned. (Press Enter to clear the listbox.) Pressing "F",
"L", "P", or "N" will scroll to the first, last, previous, or next record,
respectively. Pressing "G" or "S" will get or set the a bookmark, respectively.
Pressing "E" will show the text of the most recent ODBC error.

  Then: end
  Then: declare connection(5,.0,g), recordset(5,.0,g), myquery(200,*,g)
  Then: declare bookmark(16,*,g)
 @once:
  Then: connection = new ODBC_CONNECTION("Driver={Microsoft Access Driver (*.mdb)};DBQ=c:\\mdb\\filepro.mdb;")
    recordset = new ODBC(connection)
  Then: end
showit:
    If: @odbc.recordset["0"] gt "0"
  Then: xx = listbox(@odbc.recordset)
@@@
\rExample\r (cont'd)

    If: not showit
  Then: mesgbox "Record contains no data: " < @odbc.recordset.state
  Then: return
@keyQ :
  Then: input popup myquery "Enter query: " default
    If: myquery = ""
  Then: end
  Then: odbc recordset query myquery
  Then: gosub showit
  Then: end
@keyN :
  Then: odbc recordset getnext ; gosub showit ; end
@keyP :
  Then: odbc recordset getprev ; gosub showit ; end
@keyF :
  Then: odbc recordset getfirst   ; gosub showit ; end
@keyL :
  Then: odbc recordset getlast    ; gosub showit ; end
@@@
\rExample\r (cont'd)

@keyE :
  Then: errorbox @odbcexception["1"] & "\\n---\\n" & @odbcexception["2"]
  Then: end
@keyG :
  Then: odbc recordset bookmark get bookmark
    If: bookmark = ""
  Then: errorbox "Failed to get bookmark" ; end
  Then: mesgbox "Record has been bookmarked" ; end
@keyS :
  Then: odbc recordset bookmark set bookmark ; gosub showit ; end

##sample
@@@ @ONCE
-------------------- ODBC Sample Input Processing Table -----------------
\r@ONCE\r 	Declare variables, define arrays and set default values 

@once:

:'menu choices ; fields array:dim choices(8); dim fields(26)(70):aa:
:'keep track of the current values:dim field_value_current(26)(50):ca:
:'keep track of the updated values:dim field_value_update(26)(50) :fa:
:'odbc table field names:dim field_name(26)(70); dim field_name_short(26)(20):na:
:'default Driver; database:ma(60,,g)      ;mb(60,,g):
:'no of fields ; recordset name:n(3,.0,g) ;rs(50,,g):
:'action field:x(1,allup):
::declare driver_def(60,,g) , database_def(60,,g):
::declare table_def(20,,g):
::declare connection(5,.0,g), recordset(5,.0,g):
::declare mydriver(100,*,g) , myquery(250,*,g):
::driver_def = "driver={Microsoft Access Driver (*.mdb)};":
::end:
@@@ @keyU_process,MENU,Choices
-------------------- ODBC Sample Input Processing Table -----------------
\r@keyU\r	Do setDSN subroutine and define MENU choices

@keyu:
:gosub setDSN:

top::display "1":
:x eq "z":goto shwscrn:
::choices["1"]="Choices":
::choices["2"]="F:First Record in set":
::choices["3"]="N:Next Record in set":
::choices["4"]="P:Previous Record in Set":
::choices["5"]="L:Last Record in Set":
::choices["6"]="S:Sample SQL Statement":
::choices["7"]="U:Update DSN":
::choices["8"]="H:Help":
::menu choices doF,doN,doP,doL,doSQL,doU,doHlp,doExit:
::end:

@@@ @ODBC.hndl.EOF, @ODBC.hndl.BOF,ODBC_hndl_FIRST,ODBC_hndl_LAST
@@@ ODBC_GETNEXT,ODBC_GETPREV,ODBC_GETLAST,ODBC_GETFIRST
-------------------- ODBC Sample Input Processing Table -----------------
\rNavigating the Record Set\r 

doF:
::\iodbc recordset getfirst\i ; gosub showit ; goto top:

doN:
:\i@odbc.recordset.eof\i ne "1":\iodbc recordset getnext\i:
::gosub showit; goto top:

doP:
:\i@odbc.recordset.bof\i ne "1":\iodbc recordset getprev\i:
::gosub showit; goto top:

doL:
::\iodbc recordset getlast\i ; gosub showit ; goto top:

doE:
::errorbox \i@odbcexception["1"]\i & "\\n---\\n" & \i@odbcexception["2"]\i:
::goto top:
@@@ Help,doHelp,screen_setup
-------------------- ODBC Sample Input Processing Table -----------------
\rSetup Screen & Help\r

doHlp::help "fpodbc";goto top:

doU::screen "setup":
::goto top:

	@wef3:3 eq "":3=driver_def   ; screen "setup":
	::end:
	@wef4:4 eq "":4=database_def ; display:
	::end:
	@wef5:5 eq "":5=recordset_def ; display:
	::end:
	@wlf5::gosub setDSN:
	::display;end:
	@wef6::show popup "Available screens are 'mymedia', 'customer' 'view'":
	::end:
	
@@@ SQL_process,@ODBCEXCEPTION,error_trapping,ODBC_connection,EXECSQL
-------------------- ODBC Sample Input Processing Table -----------------
\rSQL Samples Processing\r

doSQL::k(1):
::lookup SQL = sql_statements  k=k    i=A -ng b="(brw=12,9,-1 xkey=x 
	       show=pkeep pop=1 fill=asc,top)[Step   SQL Function:
               Press \\r X \\r to Exit SQL Queries]*1                 *2":

chkSQL:not SQL:end:

selsql:@bk eq "x":goto top:

::popup("9","1") update SQL,"1",3 ; clearp ; 2 = SQL(3){"":

::\iodbc_connection connection execsql 2\i:

odbcEx:  \i@odbcexception["2"]\i ne e:
::errorbox \i@odbcexception["1"]\i & "\\n---\\n" & \i@odbcexception["2"]\i:

::e(50,,g) = \i@odbcexception["2"]\i:
@@@ Subscripts,Query
-------------------- ODBC Sample Input Processing Table -----------------
\rSQL Samples Processing\r (cont'd)

:odbcEx      
:'--- exeception:goto doSQL:
:not odbcEx:\iodbc recordset requery\i;gosub setflds;display:
selset:
:2 eq "SELECT":myquery=2; gosub setRS;gosub showit:
:not selset:msgbox("10","10") "SQL statement successful.\\n\\n"{2; goto doSQL:
::goto top:

showit:
:\i@odbc.recordset["0"] gt "0"\i : n = \i@odbc.recordset("0")\i:
:not showit: msgbox "Recordset contains no data";return:

showit2:6 gt ""         'screen name provided:x = "z":

showit3:not showit2: gosub setflds; xx=listbox(fields,,,"9","2"):
:8 gt ""         'unique field identified:r = field_value_current(8):
::return:
@@@ DSN_setting,recordset,Connecting
-------------------- ODBC Sample Input Processing Table -----------------
\rSetting the DSN\r

setDSN:
:@rn eq "1":database_def = "DBQ="{getenv("PFPROG"){"\\fp\\mdb\\vidmedia.mdb;":
:@rn eq "1":table_def = "mymedia":
:@rn eq "2":database_def = "DBQ="{getenv("PFPROG"){"\\fp\\mdb\\filepro.mdb;":
:@rn eq "2":table_def = "filepro":
:3 eq "" or 4 eq "" or 5 eq "" or 8 eq "":screen "setup":

DSNtest:
:mydriver ne 3{4:mydriver = 3{4 ; connection = \inew ODBC_CONNECTION(mydriver)\i:
::1=mydriver;display:
:DSNtest: recordset = \inew ODBC(connection)\i:

::myquery="select * from"<5:
::gosub setRS:
::return:
@@@ 
-------------------- ODBC Sample Input Processing Table -----------------
\rSetting the DSN\r (cont'd)

setRS:
:rs ne myquery:\iodbc recordset close\i; \irecordset = new ODBC(connection)\i:

::odbc recordset query myquery;rs=myquery:

::odbc \irecordset query myquery\i ; rs=myquery:

:\i@odbc.recordset["0"] gt "0"\i:n = \i@odbc.recordset("0")\i;display:

::return:

@@@ Sample_screens
-------------------- ODBC Sample Input Processing Table -----------------
\rSample Screen Processing\r

shwscrn::gosub setflds;x="";screen (6):
::end:
@wefx::show("24","12")" Press \\rF\\rirst \\rL\\rast \\rN\\rext \\rP\\rrevious \\rH\\relp \\rS\\rQL \\rU\\rpdate \\rX\\r to Exit":
::end:
@wlfx::'recordset navigation:
:@sk eq "CDWN"        'cursor down:x = "N":
:@sk eq "CRUP"        'cursor up:x = "p":
:@sk eq "HOME"        'home key:x = "f":
:x eq "s"             'SQL samples:goto doSQL:
:x eq "h"             'Help:help "sample";x="";pushkey "[ENTR]":
:x eq "X"             'exit:goto top:
:x eq "U"             'update a record in the odbc table:goto updrec:

:x eq "N":\iodbc recordset GETNEXT\i:
:x eq "P":\iodbc recordset GETPREV\i:
:x eq "F":\iodbc recordset GETFIRST\i:
:x eq "L":\iodbc recordset GETLAST\i:
@@@ @ODBC.recs.name,ODBC.recs.type
-------------------- ODBC Sample Input Processing Table -----------------
\rSample Screen Processing\r (cont'd)

::gosub setflds;display;end:

setflds:
::c=""; clear fields 'clears the fields array:

setc:
::c(2,.0)=c+"1"   '--- sets our array counter:
:c gt n or c gt "26":c="";return	'--- end of array fields:

:'field name in recordset   ; field type in recordset
::sa(8) = \i@odbc.recordset.name(c)\i ; sb(10) = \i@odbc.recordset.type(c)\i:

:'field value in recordset:
::sc = \i@odbc.recordset(c)\i:

::fields(c)=sa&":"&sb&sc;goto setc	'--- sets the array field(c):
@@@ Messages
-------------------- ODBC Sample Input Processing Table -----------------
\rMessage subroutine\r

getmsg

::ma = "You have just executed \\n\\rODBC recordset":
::mb = "\\r\\nwhere recordset ="<recordset<"and \\nQuery ="<myquery{" \\n\\n":
::mc = "Invalid Selection":

:x eq "N":m = ma<"GETNEXT"{mb:
:x eq "P":m = ma<"GETPREV"{mb:
:x eq "F":m = ma<"GETFIRST"{mb:
:x eq "L":m = "This may take a while. \\n\\rODBC recordset GETLAST"{mb:
:x ne "N" and x ne "P" and x ne "F" and x ne "L":m = mc:

::return:

@@@ Update,REQUERY
-------------------- ODBC Sample Input Processing Table -----------------
\rUpdating Records\r

doupd:
@SK eq "SAVE" and @sn eq "update"
:gosub pstupd:
::\iODBC recordset REQUERY\i; x="N"   '--- sets us back to 1st record:
::gosub setflds                   '--- refresh the fields:

donext:
@sn eq "update"
:s(10) = field_value_current(8)   '--- get primary key for comparison:
:s ne r                           '--- compare the key
:\iodbc recordset getnext\i;gosub setflds;goto donext:
::screen (6):
::goto shwscrn:

@@@
-------------------- ODBC Sample Input Processing Table -----------------
\rUpdating Records\r (cont'd)

updrec:::
::r(10)=field_value_current(8):
::screen "update",ca:
::end:

pstupd::c="";:
sett::t="Update"<5<"SET":
::c=c+"1":
:c gt n OR c gt "26":c="";  return:

change:
:field_value_current(c) ne field_value_update(c)
::t=t<field_name(c)<"="<"'"{field_value_current(c){"' 
  WHERE "<field_name("1")<"="<field_value_current(8):

:change:\iodbc_connection connection execsql t\i:
::goto sett:
