About This Manual

This manual contains the reference material you need for understanding the language functionality and limitations of Pervasive.SQL V8.

Prev
Check for Revisions 		Next
Who Should Read this Manual


Who Should Read this Manual

This manual provides information for using Pervasive.SQL V8.

This manual assumes you have a general understanding of ODBC architecture and ODBC driver components, and have access to the Microsoft ODBC Software Development Kit.

This document also assumes you have a working understanding of modern database principles and terminology, the C language, and your development environment (compiler and linker).

Pervasive Software would appreciate your comments and suggestions about this manual. As a user of our documentation, you are in a unique position to provide ideas that can have a direct impact on future releases of this and other manuals. If you have comments or suggestions for the product documentation, post your request at http://www.pervasive.com/devtalk or send e-mail to docs@pervasive.com.

Prev
About This Manual 		Up
Check for Revisions 		Next
Manual Organization


Manual Organization

This reference includes the following chapters:

  • Chapter 1 — SQL Overview

    This chapter describes the types of SQL statements you can create using Pervasive.SQL.

  • Chapter 2 — ODBC Engine Reference

    This chapter describes Pervasive.SQL capabilities, characteristics, and conformance to the SQL grammar and ODBC API standards.

  • Chapter 3 — SQL Syntax Reference

    This chapter describes the SQL grammar and syntax supported by Pervasive.SQL.

  • Chapter 4 — Performance Reference

    This chapter explains in detail the optimizations used in the SQL Relational Database Engine (SRDE), and how to best take advantage of them in your SQL statements.

  • Appendix A — Data Types

    This appendix contains tables of supported Data Types.

  • Appendix B — SQL Reserved Words

    This appendix contains the list of supported SQL Keywords.

  • Appendix C — System Tables

    This appendix describes the system tables that comprise the relational data dictionary.

The manual also contains an index.

Prev
Who Should Read this Manual 		Up
Check for Revisions 		Next
Conventions


Conventions

Unless otherwise noted, command syntax, code, and examples use the following conventions:
Case | Commands and reserved words typically appear in uppercase letters. Unless the manual states otherwise, you can enter these items using uppercase, lowercase, or both. For example, you can type MYPROG, myprog, or MYprog.
[ ] | Square brackets enclose optional information, as in [log_name]. If information is not enclosed in square brackets, it is required.
| | A vertical bar indicates a choice of information to enter, as in [file name | @file name].
< > | Angle brackets enclose multiple choices for a required item, as in /D=<5|6|7>.
variable | Words appearing in italics are variables that you must replace with appropriate values, as in file name.
. . . | An ellipsis following information indicates you can repeat the information more than one time, as in [parameter . . . ].
::= | The symbol ::= means one item is defined in terms of another. For example, a::=b means the item a is defined in terms of b.
* | An asterisk is used as a wildcard symbol to indicate a series of APIs with the same prefix.

 Note
Unless otherwise noted, all references in this book to the Pervasive.SQL product refer to the current version, Pervasive.SQL V8.

Prev
Manual Organization 		Up
Check for Revisions 		Next
For More Information


For More Information

For complete information on the ODBC 2.5 specification, see the Microsoft ODBC Programmer's Reference.

Prev
Conventions 		Up
Check for Revisions 		Next
SQL Overview


SQL Overview

An Overview of the Structured Query Language (SQL)

Structured Query Language (SQL) is a database language consisting of English-like statements you can use to perform database operations. Both the American National Standards Institute (ANSI) and IBM have defined standards for SQL. (The IBM standard is the Systems Application Architecture [SAA].) The Pervasive.SQL product implements most of the features of both ANSI SQL and IBM SAA SQL and provides additional extensions that neither standard specifies.

Pervasive.SQL allows you to create different types of SQL statements. The following table lists the types of SQL statements you can create and the tasks you can accomplish using each type of statement:
Table 1-1 SQL Statement Types and Related Tasks
SQL Statement Type | Tasks
Data Definition | Create, modify, and delete tables. Create and delete indexes. Create and delete stored SQL procedures. Create and delete triggers.
Data Manipulation | Retrieve, insert, update, and delete data in tables. Define transactions. Define and delete views. Execute stored SQL procedures. Execute triggers.
Data Control | Enable and disable security for a dictionary. Create and delete users and groups. Grant and revoke table access rights.

The rest of this chapter briefly describes the SQL statements used in each statement category. For detailed information about each statement, refer to SQL Syntax Reference .

The following are the statement category overview sections found in this chapter:

  • Data Definition Statements
  • Data Manipulation Statements
  • Data Control Statements
  • Database Names
Prev
For More Information
Check for Revisions 		Next
Data Definition Statements


Data Definition Statements

Data definition statements let you specify the characteristics of your database. When you execute data definition statements, Pervasive.SQL stores the description of your database in a data dictionary. You must define your database in the dictionary before you can store or retrieve information.

Pervasive.SQL allows you to construct data definition statements to do the following:

  • Create, modify, and delete tables.
  • Create and delete indexes.
  • Create and delete triggers.
  • Create and delete stored procedures.

The following sections briefly describe the SQL statements associated with each of these tasks. For general information about defining the characteristics of your database, refer to the Pervasive.SQL Programmer's Guide, available in the Pervasive.SQL Software Developer Kit (SDK).

Creating, Modifying, and Deleting Tables

You can create, modify, and delete tables from a database by constructing SQL using the following statements:

Table 1-2 Data Definition Statements - Tables
CREATE TABLE | Defines a table and optionally creates the corresponding data file.
ALTER TABLE | Changes a table definition. With an ALTER TABLE statement, you can perform such actions as add a column to the table definition, remove a column from the table definition, change a column's data type or length (or other characteristics), and add or remove a primary key or a foreign key and associate the table definition with an different data file.
DROP TABLE | Deletes a table from the data dictionary and optionally deletes the associated data file from the disk.

Creating and Deleting Indexes

You can create and delete indexes from a database by constructing SQL s using the following statements:

Table 1-3 Data Definition Statements - Indexes
CREATE INDEX | Defines a new index (a named index) for an existing table.
DROP INDEX | Deletes a named index.

Creating and Deleting Triggers

You can create and delete triggers from a database by constructing SQL using the following statements:

Table 1-4 Data Definition Statements - Triggers
CREATE TRIGGER | Defines a trigger for an existing table.
DROP TRIGGER | Deletes a trigger.

Pervasive.SQL provides additional SQL control statements, which you can only use in the body of a trigger. You can use the following statements in triggers:

Table 1-5 Data Definition Statements - Trigger Control
BEFORE | Defines the trigger execution before the INSERT, UPDATE, or DELETE operation.
AFTER | Defines the trigger execution after the INSERT, UPDATE, or DELETE operation.

Creating and Deleting Stored Procedures

A stored procedure consists of statements you can precompile and save in the dictionary. To create and delete stored procedures, construct statements using the following:

Table 1-6 Data Definition Statements - Stored Procedure
CREATE PROCEDURE | Stores a new procedure in the data dictionary.
DROP PROCEDURE | Deletes a stored procedure from the data dictionary.

Pervasive.SQL provides additional SQL control statements, which you can only use in the body of a stored procedure. You can use the following statements in stored procedures:

Table 1-7 Data Definition Statements - Stored Procedure Control
IF...THEN...ELSE | Provides conditional execution based on the truth value of a condition.
LEAVE | Continues execution by leaving a block or loop statement.
LOOP | Repeats the execution of a block of statements.
WHILE | Repeats the execution of a block of statements while a specified condition is true.

Prev
SQL Overview 		Up
Check for Revisions 		Next
Data Manipulation Statements


Data Manipulation Statements

Data manipulation statements let you access and modify the contents of your database. Pervasive.SQL allows you to construct data manipulation statements to do the following:

  • Retrieve data from tables.
  • Modify data in tables.
  • Define transactions.
  • Create and delete views.
  • Execute stored procedures.
  • Execute triggers.

The following sections briefly describe the SQL statements associated with each of these tasks.

Retrieving Data

All statements you use to retrieve information from a database are based on the SELECT statement.

Table 1-8 Data Manipulation Statements - Retrieving Data
SELECT
Retrieves data from one or more tables in the database.

When you create a SELECT statement, you can use various clauses to specify different options. (See the entry for the SELECT statement in "ODBC Engine Reference" on page 2-1 for detailed information about each type of clause.) The types of clauses you use in a SELECT statement are as follows:

Table 1-9 Data Manipulation Statements - Retrieving Data Options
FROM | Specifies the tables or views from which to retrieve data.
WHERE | Defines search criteria that qualify the data a SELECT statement retrieves.
GROUP BY | Combines sets of rows according to the criteria you specify and allows you to determine aggregate values for one or more columns in a group.
HAVING | Allows you to limit a view by specifying criteria that the aggregate values of a group must meet.
ORDER BY | Determines the order in which Pervasive.SQL returns selected rows.

In addition, you can use the UNION keyword to obtain a single result table from multiple SELECT queries.

Modifying Data

You can add, change, or delete data from tables and views by issuing statements such as the following:

Table 1-10 Data Manipulation Statements - Modifying Data
INSERT | Adds rows to one or more tables or a view.
UPDATE | Changes data in a table or a view.
DELETE | Deletes rows from a table or a view.

When you create a DELETE or UPDATE statement, you can use a WHERE clause to define search criteria that restrict the data upon which the statement acts.

Defining Transactions

To update the data in a database, you can issue SQL statements individually or you can define transactions (logical units of related statements). By defining transactions, you can ensure that either all the statements in a unit of work are executed successfully or none are executed. You can use transactions to group statements to ensure the logical integrity of your database.

Pervasive.SQL supports the ODBC API SQLTransact. See the Microsoft ODBC Programmer's Reference for more information.

Creating and Deleting Views

You can create and delete views by constructing SQL using the following statements:

Table 1-11 Data Manipulation Statements - Views
CREATE VIEW | Defines a database view and stores the definition in the dictionary.
DROP VIEW | Deletes a view from the data dictionary.

Executing Stored Procedures

A stored procedure consists of statements you can precompile and save in the dictionary. To execute stored procedures, construct statements using the following:

Table 1-12 Data Manipulation Statements- Stored Procedures
CALL
Recalls a previously compiled procedure and executes it.

Executing Triggers

A trigger consists of statements you can precompile and save in the dictionary. Triggers are executed automatically by the engine when the specified conditions occur.

Prev
Data Definition Statements 		Up
Check for Revisions 		Next
Data Control Statements


Data Control Statements

Data control statements let you define security for your database. When you create a dictionary, no security is defined for it until you explicitly enable security for that dictionary. Pervasive.SQL allows you to construct data control statements to do the following:

  • Enable and disable security.
  • Create and delete users and groups.
  • Grant and revoke rights.

Note
If your Btrieve data files are secured using Btrieve owner names, the Relational Engine will honor them when performing ODBC operations (for example, Read Only access will be permitted with a type 1 or 3 owner name, no access with a type 0 or 2 owner name) if your database is not secured using Relational security. If your database is secured using Relational security, the Relational Engine will enforce access to the database solely based on the defined database user access rights when performing ODBC operations. These rights must be granted with the owner name specified.

The following sections briefly describe the SQL statements associated with each of these tasks.

Enabling and Disabling Security

You can enable or disable security for a database by issuing statements using the following statement:

Table 1-13 Data Control Statements - Security
SET SECURITY
Enables or disables security for the database and sets the Master password.

Creating and Deleting Users and Groups

You can create or delete users and user groups for the database by constructing SQL using the following statements:

Table 1-14 Data Control Statements - Groups and Users
CREATE GROUP | Creates a new group of users.
DROP GROUP | Deletes a group of users.
GRANT LOGIN TO | Creates users and passwords, or adds users to groups.
REVOKE LOGIN FROM | Removes a user from the dictionary.

Granting and Revoking Rights

You can assign or remove rights from users or groups by issuing statements using the following:

Table 1-15 Data Control Statements - Rights
GRANT (access rights) | Grants a specific type of rights to a user or a group. The rights you can grant with a GRANT (access rights) statement are All, Insert, Delete, Alter, Select, Update, and References.
GRANT CREATETAB TO | Grants the right to create tables to a user or a group.
REVOKE (access rights) | Revokes access rights from a user or a group.
REVOKE CREATETAB FROM | Revokes the right to create tables from a user or a group.

Prev
Data Manipulation Statements 		Up
Check for Revisions 		Next
Database Names


Database Names

A database name is a name you associate with the location of a dictionary and its data files; it is also the table qualifier.

Database names are stored in the database names configuration file (DBNAMES.CFG). If you add a primary key, foreign key, or trigger to a table, the database name is also written to the data file associated with the table. Bound named databases also force the database name to be written to the data file for every table in the database.

For more information about bound databases, refer to the Pervasive.SQL Programmer's Guide. For more conceptual and task information regarding database names and concepts, see the following topic in Advanced Operations Guide: "Pervasive.SQL Databases".

Database names must follow these conventions:

  • Begin with a letter.
  • Cannot contain blanks.
  • Cannot contain any of the following characters

    \ / : * ? " < > |

  • Cannot be a reserved keyword.
  • Must not exceed 20 characters.
  • Database names are not case-sensitive.

Prev
Data Control Statements 		Up
Check for Revisions 		Next
ODBC Engine Reference


ODBC Engine Reference

Limits, Connection Strings, and ODBC Conformance

This chapter contains information regarding the limits and conformance of the Pervasive.SQL V8 ODBC interface:

  • Pervasive ODBC Engine Interface Limits
  • Data Source Name Connection String Keywords
  • ODBC API Conformance
  • SQL Grammar Conformance

For detailed information on the ODBC API, SQL grammar, and scalar functions, refer to the Microsoft ODBC Programmer's Reference.

Prev
Database Names
Check for Revisions 		Next
Pervasive ODBC Engine Interface Limits


Pervasive ODBC Engine Interface Limits

The following table shows the limits or conditions that apply to features of the Pervasive ODBC Engine Interface.

Note
All name length limits, such as maximum table name size or maximum view name size, apply only to single-byte character sets. Double-byte character sets are subject to a 50% restriction on the maximum name size. For example, in Japanese, the maximum column name size is 10 characters, not 20 as stated below.

See also "Naming Conventions" in Pervasive.SQL Programmer's Guide (http://www.pervasive.com/library/index.asp?_shownode=GDE), which is part of the Pervasive.SQL Software Developer's Kit (SDK).
Table 2-1 Limits/Conditions of Pervasive.SQL Features
Pervasive.SQLFeature | Limit or Condition
Arguments in a parameter list for a stored procedure | 300
Character string literal | A character in the string may be any ANSI character between 1 and 255 decimal.
Columns in a database | The total number of columns, constraints, and indexes in a database must be <= 65,535. Because a column with the NOT NULL constraint requires an additional field identifier for the constraint itself, the maximum number of NOT NULL columns (assuming no other columns, indexes, or constraints exist) is 32,767.
Columns in a table | 1536
Columns allowed in a trigger or stored procedure | 300
Column name | 20 characters
Column size | 2 GB
Correlation name | limited by memory
Cursor name | 18 characters
Database name | 20 characters
Database sessions | limited by memory
Database tables | 65,534
Group name | 30 characters
Index name | 20 characters
Label name | limited by memory
Length of DBQ entry in the odbc.ini file | 20 characters (name of database)
Number of ANDed predicates | 300. For example, this statement uses two ANDed predicates: SELECT * FROM person WHERE First_Name = 'Janis' AND Last_Name = 'Nipart' AND Perm_Street = '1301 K Street NW.'
Parameter name | 126 characters
Password | 8 characters
Procedure name | 30 characters
Referential integrity (RI) constraint (for example, Xr$Name) | 20 characters
Representation of single quote | Two consecutive single quotes ('')
Result name | limited by memory
Savepoint name | limited by memory
SELECT list columns in a query | 1600
Size of a single term (quoted literal string) in an SQL statement | 14,997, excluding null terminator and quotations (15,000 total)
SQL statement length | 64 KB
SQL statements per session | limited by memory
Stored procedure size | 64 KB
Table name | 20 characters
Table rows | 2 billion
Joined tables per query | limited by memory
Trigger name | 30 characters
User name | 30 characters
Variable name | limited by memory
View name | 20 characters

Prev
ODBC Engine Reference 		Up
Check for Revisions 		Next
Data Source Name Connection String Keywords


Data Source Name Connection String Keywords

A connection string used to connect to a DSN may include any number of driver-defined keywords. Using these keywords, the driver has enough information to connect to the data source. The driver (for example, the Pervasive ODBC Engine Interface or Pervasive ODBC Client Interface) defines which keywords are required to connect to the data source.

Connection strings serve the same purpose in Pervasive.SQL V8 as they did in previous versions. They are used to identify which data source to connect to. The difference now lies in the driver-defined keywords listed in the connection string. Pervasive.SQL 7 used a different set of keywords to identify a data source.

Listed below are the keywords used in Pervasive.SQL V8 connection strings.
Table 2-2 Valid Connection Strings for Client DSNs
DSN | Name of the data source as returned by SQLDataSources or the data sources dialog box of SQLDriverConnect.
DRIVER | The description of the driver as returned by SQLDrivers function. For a Client DSN, it is "Pervasive ODBC Client Interface".
ServerName | The address of the server or host name and the port number where the data resides.
ServerDSN | The name of an Engine data source referenced by this DSN.
TransportHint | Specifies the transport protocols to check for the ServerName. The list of transport protocols is specified in the order in which they should be searched.
PWD | The password corresponding to the user ID.
ArrayFetchOn | Enable array fetching. Array fetching is used to improve performance of data fetching between the client and the server. The default setting is to enable array fetching.
ArrayBufferSize | Size of the array buffer. Values between 1 and 64KB are acceptable. The default setting is 8KB.
UID | A user login ID.

Table 2-3 Valid Connection Strings for Engine DSNs
DSN | Name of the data source as returned by SQLDataSources or the data sources dialog box of SQLDriverConnect.
DRIVER | The description of the driver as returned by SQLDrivers function. For an Engine DSN, it is "Pervasive ODBC Engine Interface".
DBQ | A database name.
UID | A user login ID.
PWD | The password corresponding to the user ID.

Prev
Pervasive ODBC Engine Interface Limits 		Up
Check for Revisions 		Next
ODBC API Conformance


ODBC API Conformance

The Pervasive ODBC Engine Interface fully conforms to the ODBC v2.5 specifications for core grammar API and Level 1 API, and supports most of the Level 2 function calls. The following table lists the ODBC API functions supported by the Pervasive ODBC Engine Interface and the ODBC Conformance level.

Table 2-4 Interface-supported ODBC API Functions
ODBC Function | ODBC Conformance Level
SQLAllocConnect | Core
SQLAllocEnv | Core
SQLAllocStmt | Core
SQLBindCol | Core
SQLBindParameter | Level 1
SQLBrowseConnect | Level 2
SQLCancel | Core
SQLColAttributes | Core
SQLColumns | Level 1
SQLColumnPrivileges | Level 2
SQLConnect | Core
SQLDataSources | Level 2
SQLDescribeCol | Core
SQLDescribeParam | Level 2
SQLDisconnect | Core
SQLDriverConnect | Level 1
SQLDrivers | Level 2
SQLError | Core
SQLExecDirect | Core
SQLExecute | Core
SQLExtendedFetch | Level 2
SQLFetch | Core
SQLForeignKeys | Level 2
SQLFreeConnect | Core
SQLFreeEnv | Core
SQLFreeStmt | Core
SQLGetConnectOption | Level 1
SQLGetCursorName | Core
SQLGetData | Level 1
SQLGetFunctions | Level 1
SQLGetInfo | Level 1
SQLGetStmtOption | Level 1
SQLGetTypeInfo | Level 1
SQLMoreResults | Level 2
SQLNativeSql | Level 2
SQLNumResultCols | Core
SQLNumParams | Level 2
SQLParamData | Level 1
SQLPrepare | Core
SQLPrimaryKeys | Level 2
SQLProcedures | Level 2
SQLProcedureColumns | Level 2
SQLPutData | Level 1
SQLRowCount | Core
SQLSetConnectOption | Level 1
SQLSetCursorName | Core
SQLSetPos | Level 2
SQLSetStmtOption | Level 1
SQLSpecialColumns | Level 1
SQLStatistics | Level 1
SQLTables | Level 1
SQLTablePrivileges | Level 2
SQLTransact | Core

Exceptions to ODBC API Conformance

The following section contains details on the exceptions to ODBC API conformance as specified in Table 2-4 .

SQLMoreResults

The Pervasive ODBC Engine Interface always returns sql_no_data_found for this function. The Pervasive ODBC Engine Interface supports this function, with its return value, due to requirements of Microsoft Access.

SQLSetStmtOption / SQLGetStmtOption

The following table lists the options the Pervasive ODBC Engine Interface supports for SQLSetStmtOption and SQLGetStmtOption:
Table 2-5 Options for SQLSetStmtOption and SQLGetStmtOption
fOption (numerical value) | vParam | Comments
SQL_QUERY_TIMEOUT(0) |  | Supported through SQLSetStmtOption and SQLSetConnectOption. Applies only to SQLExecDirect, SQLExecute, SQLFetch, and SQLExtendedFetch. Does not apply to DDL statements.
SQL_MAX_ROWS(1) |  | Supported according to the Microsoft ODBC Programmer's Reference.
SQL_NOSCAN(2) |  | Supported according to the Microsoft ODBC Programmer's Reference
SQL_MAX_LENGTH(3) |  | Supported according to the Microsoft ODBC Programmer's Reference
SQL_ASYNC_ENABLE(4) |  | Supported according to the Microsoft ODBC Programmer's Reference
SQL_BIND_TYPE(5) |  | Supported according to the Microsoft ODBC Programmer's Reference.
SQL_CURSOR_TYPE(6) |  | Supported according to the Microsoft ODBC Programmer's Reference
SQL_CONCURRENCY(7) |  | Supported according to the Microsoft ODBC Programmer's Reference
SQL_ROWSET_SIZE(9) |  | Supported according to the Microsoft ODBC Programmer's Reference
SQL_RETRIEVE_DATA(11) |  | Supported according to the Microsoft ODBC Programmer's Reference
SQL_USE_BOOKMARK(12) |  | Supported according to the Microsoft ODBC Programmer's Reference
1151 | in the format: Tablename,Password (no space between the Tablename and the Password) | A Pervasive ODBC Engine Interface extension: appends password for table to an internal list in Pervasive ODBC Engine Interface so that the user does not have to be prompted for the password.
1153 | 0 (default) turns off table locking; 1 turns on table locking. | A Pervasive ODBC Engine Interface extension: When vParam is set to 1, all tables used by the hStmt are exclusively locked when a select, update, insert, delete, or create index statement is executed on the hStmt. The tables remain locked until the hStmt is dropped (by calling SQLFreeStmt with the SQL_DROP option) or the option is set to DEFLOCK and the hStmt is re-executed. Locked tables can only be used by the locking hStmt; they cannot be used by any other hStmts.

SQLSetConnectOption and SQLGetConnectOption

The following table lists the options the Pervasive ODBC Engine Interface supports for SQLSetConnectOption and SQLGetConnectOption:
Table 2-6 Options for SQLSetConnectOption and SQLGetConnectOption
fOption (numerical value) | Comments
SQL_QUERY_TIMEOUT(0) | Supported through SQLSetStmtOption and SQLSetConnectOption. Applies only to SQLExecDirect, SQLExecute, SQLFetch, and SQLExtendedFetch. Does not apply to DDL statements.
SQL_MAX_ROWS(1) | Supported according to the Microsoft ODBC Programmer's Reference.
SQL_NOSCAN(2) | Supported according to the Microsoft ODBC Programmer's Reference.
SQL_ASYNC_ENABLE(4) | Supported according to the Microsoft ODBC Programmer's Reference.
SQL_BIND_TYPE(5) | Supported according to the Microsoft ODBC Programmer's Reference.
SQL_CURSOR_TYPE(6) | Supported according to the Microsoft ODBC Programmer's Reference.
SQL_CONCURRENCY(7) | Supported according to the Microsoft ODBC Programmer's Reference.
SQL_ROWSET_SIZE(9) | Supported according to the Microsoft ODBC Programmer's Reference.
SQL_RETRIEVE_DATA(11) | Supported according to the Microsoft ODBC Programmer's Reference.
SQL_USE_BOOKMARKS(12) | Supported according to the Microsoft ODBC Programmer's Reference.
SQL_ACCESS_MODE(101) | Supported according to the Microsoft ODBC Programmer's Reference.
SQL_AUTO(102) | Supported according to the Microsoft ODBC Programmer's Reference.
SQL_TXN_ISOLATION(108) | Supported according to the Microsoft ODBC Programmer's Reference.
SQL_ODBC_CURSORS(110) | Supported according to the Microsoft ODBC Programmer's Reference.

SQLGetTypeInfo

SQLGetTypeInfo generates a list of native data type names (type_name) specified by the Pervasive ODBC Engine Interface. For example, SQL_CHAR is mapped to CHARACTER. Use the names which are returned from this function for the data type names for columns in a CREATE TABLE or ALTER TABLE statement or for parameters for procedures or declared variables in procedures and triggers.

SQLSpecialColumns

The Pervasive ODBC Engine Interface uses unique indexes as the optimal set of columns that uniquely identifies a row in the table. When a new row is inserted, the Pervasive ODBC Engine Interface does not return the values for autoincrement columns. You may determine the value for an autoincrement column through the use of the @@IDENTITY variable. See @@IDENTITY .

SQLGetInfo

The Pervasive ODBC Interface returns identical values for SQL_DRIVER_VER and SQL_DBMS_VER. This version value is returned in the following format:

08.00.0147 010

This value can be interpreted as four components, which are shown in the following table:

Part | Value | Description
08 | Major version | The major version of the Pervasive.SQL database engine.
00 | Minor version | The minor version of the Pervasive.SQL database engine, which is typically updated in a service pack.
0147 | Build number | The build further specifies the release by marking the time at which Pervasive Software generated the release.
010 | Point build | A minor update to the build. The build number and point build uniquely identify a product release created at a specific time.

The following table summarizes the expected format of other values typically returned by SQLGetInfo.

Item | Value
SQL_DRIVER_NAME | W3ODBCCI.DLL
SQL_DRIVER_VER | 08.00.0147 010
SQL_DRIVER_ODBC_VER | 02.50
SQL_DBMS_NAME | Pervasive.SQL
SQL_DBMS_VER | 08.00.0147 010
SQL_ODBC_VER | 03.52.0000

Prev
Data Source Name Connection String Keywords 		Up
Check for Revisions 		Next
SQL Grammar Conformance


SQL Grammar Conformance

The ODBC v2.5 specification provides three levels of SQL grammar conformance: Minimum, Core, and Extended. Each higher level provides more fully-implemented data definition and data manipulation language support. The Pervasive ODBC Engine Interface fully supports the minimum SQL grammar, as well as many core and extended grammar statements. The Pervasive ODBC Engine Interface support for SQL grammar is summarized in the following table.
Table 2-7 SQL Grammar Conformance
SQL Grammar Statement | Minimum | Core | Extended
ALTER TABLE |  |  | ü
CREATE GROUP |  |  | ü
CREATE INDEX |  | ü |
CREATE PROCEDURE |  |  | ü
CREATE TABLE |  |  | ü
CREATE TRIGGER |  |  | ü
CREATE VIEW |  | ü |
DELETE (positional) | ü |  |
DELETE (searched) | ü |  |
DROP GROUP |  |  | ü
DROP INDEX |  | ü |
DROP PROCEDURE |  |  | ü
DROP TABLE | ü |  |
DROP TRIGGER |  |  | ü
DROP VIEW |  | ü |
GRANT |  | ü |
INSERT | ü |  |
JOIN LEFT OUTER (Select) |  |  | ü
REVOKE |  | ü |
SELECT (with into) | ü |  |
- approximate-numeric-literal |  | ü |
- between-predicate |  | ü |
- correlation-name |  | ü |
- date arithmetic |  |  | ü
- date-literal |  |  | ü
- exact-numeric-literal |  | ü |
- extended predicates |  |  | ü
- in-predicate |  | ü |
- set-function |  | ü |
- time-literal |  |  | ü
- timestamp-literal |  |  | ü
Subqueries |  | ü |
UNION |  |  | ü
SET SECURITY |  |  | ü
UPDATE (positional) | ü |  |
UPDATE (searched) | ü |  |

Delimited Identifiers in SQL Statements

Column names and table names can occur as delimited identifiers if they contain non-ODBC standard characters. The delimiter character for delimited identifiers is a double-quote. For example:

SELECT "last-name" FROM "non-standard-tbl"

Prev
ODBC API Conformance 		Up
Check for Revisions 		Next
SQL Syntax Reference


SQL Syntax Reference

A Reference to Pervasive.SQL Supported Syntax

This chapter details the SQL grammar supported by the SRDE:

  • Grammar Statements
  • Grammar Element Definitions
  • Scalar Functions
  • Global Variables
  • Other Characteristics

For detailed information on the ODBC API, ODBC SQL grammar, and scalar functions, refer to the Microsoft ODBC Programmer's Reference.

Prev
SQL Grammar Conformance
Check for Revisions 		Next
Grammar Statements


Grammar Statements

The following pages describe the complete grammar for all supported SQL grammar elements.

Note
You can use the SQL Data Manager provided with the Pervasive Control Center to test most of the SQL examples. Exceptions are noted in the discussion of the grammar elements. Type the SQL statements directly in the query pane. If you wish to enter more than one SQL statement, separate each statement using the pound sign (#). You may change this delimiter character to a semi-colon by selecting Tools 4 Properties from the menu.

Prev
SQL Syntax Reference 		Up
Check for Revisions 		Next
ADD


ADD

Remarks

Use the ADD clause within the ALTER TABLE statement to specify one or more column definitions, column constraints, or table constraints to be added.

See Also

ALTER TABLE

Prev
Grammar Statements 		Up
Check for Revisions 		Next
ALL


ALL

ALL has two forms:

  • ALL (in a query)
  • ALL (SQL Right)

ALL (in a query)

Remarks

When you specify the ALL keyword before a subquery, Pervasive.SQL V8 performs the subquery and uses the result to evaluate the condition in the outer query. If all the rows the subquery returns meet the outer query's condition for a particular row, Pervasive.SQL V8 includes that row in the final result table of the statement.

Generally, you can use the EXISTS or NOT EXISTS keyword instead of the ALL keyword.

Examples

The following SELECT statement compares the ID column from the Person table to the ID columns in the result table of the subquery:

SELECT p.ID, p.Last_Name

FROM Person p

WHERE p.ID <> ALL
(SELECT f.ID FROM Faculty f WHERE f.Dept_Name = 'Chemistry')

If the ID value from Person does not equal to any of the ID values in the subquery result table, Pervasive.SQL includes the row from Person in the final result table of the statement.

See Also

SELECT (with into)

SELECT

UNION

ALL (SQL Right)

This form of ALL is used in a GRANT statement.

When you specify ALL, the INSERT, UPDATE, ALTER, SELECT, DELETE and REFERENCES rights are granted to the specified user or group. In addition, the user or group is granted the CREATE TABLE right for the dictionary.

Example

The following statement grants all these privileges to dannyd for table Class.

GRANT ALL on Class to dannyd

See Also

GRANT

REVOKE

Prev
ADD 		Up
Check for Revisions 		Next
ALTER TABLE


ALTER TABLE

The ALTER TABLE statement modifies a table definition.

Syntax

ALTER TABLE table-name [ IN DICTIONARY ]
    [ USING 'path_name'] [ WITH REPLACE ] alter-option-list

table-name ::= user-defined-name

alter-option-list ::= alter-option

|(alter-option [, alter-option}...)

alter-option ::= ADD [ COLUMN ] column-definition

| ADD table-constraint-definition
| DROP [ COLUMN ] column-name
| DROP CONSTRAINT constraint-name
| DROP PRIMARY KEY
| MODIFY [ COLUMN ] column-definition
| ALTER [ COLUMN ] column-definition

column-definition ::= column-name data-type [ DEFAULT default-value ] [ column-constraint [ column-constraint ]... [CASE | COLLATE collation-name ]

column-name ::= user-defined-name

data-type ::= data-type-name [ (precision [ , scale ] ) ]

precision ::= integer

scale ::= integer

default-value ::= literal

literal ::= 'string'

| number
| { d 'date-literal' }
| { t 'time-literal' }
| { ts 'timestamp-literal' }

column-constraint ::= [ CONSTRAINT constraint-name ] col-constraint

constraint-name ::= user-defined-name

col-constraint ::= NOT NULL

| UNIQUE
| PRIMARY KEY
| REFERENCES table-name [ ( column-name ) ] [ referential-actions ]

referential-actions ::= referential-update-action [ referential-delete-action ]

| referential-delete-action [ referential-update-action ]

referential-update-action ::= ON UPDATE RESTRICT

referential-delete-action ::= ON DELETE CASCADE

| ON DELETE RESTRICT

collation-name ::= 'string' | user-defined-name

table-constraint-definition ::= [ CONSTRAINT constraint-name ] table-constraint

table-constraint ::= UNIQUE (column-name [ , column-name ]... )

| PRIMARY KEY ( column-name [ , column-name ]... )
| FOREIGN KEY ( column-name [ , column-name ] )
REFERENCES table-name
[ ( column-name [ , column-name ]... ) ]
[ referential-actions ]

Remarks

Refer to CREATE TABLE for information pertaining to primary and foreign keys and referential integrity.

Modifying Column Data Types

You cannot change a column with INTEGER data type into one with an IDENTITY data type using an ALTER TABLE statement. If you attempt to do so, you will receive status code -3018.

IN DICTIONARY

The purpose of using this keyword is to notify the SQL Relational Database Engine (SRDE) that you wish to make modifications to the DDFs, while leaving the underlying physical data unchanged. IN DICTIONARY is a very powerful and advanced feature. It should only be used by system administrators or when absolutely necessary. Normally, the SRDE keeps DDFs and data files totally synchronized, but this feature allows users the flexibility to force table dictionary definitions to match an existing data file. This can be useful when you want to create a definition in the dictionary to match an existing data file, or when you want to use a USING clause to change the data file path name for a table.

You cannot use this keyword on a bound database.

IN DICTIONARY is allowed on CREATE and DROP TABLE, in addition to ALTER TABLE. IN DICTIONARY affects dictionary entries only, no matter what CREATE/ALTER options are specified. Since Pervasive.SQL allows multiple options (any combination of ADD, DROP, ADD CONSTRAINT, and so on), IN DICTIONARY is honored under all circumstances to guarantee only the DDFs are affected by the schema changes.

Tables that exist in the DDFs only (the data file does not exist) are called detached entries. These tables are inaccessible via queries or other operations that attempt to open the physical underlying file. For this reason, IN DICTIONARY was added to DROP TABLE, because it is now possible to create detached entries using CREATE TABLE.

The error "Table not found" results if you query a detached table or a table that does not exist. If you determine that a table exists but you receive the "Table not found" error, the error resulted because the data file could not be opened. This indicates a detached table.

You may verify whether a table really exists by using SQLTables or directly querying the Xf$Name column of X$File:

SELECT * FROM X$File WHERE Xf$Name = 'table_name'

It is possible for a detached table to cause confusion, so the IN DICTIONARY feature must be used with extreme care. It is crucial that it should be used to force table definitions to match physical files, not to detach them. Consider the following examples, assuming that the file test123.btr does not exist. (USING is explained below, in the next subtopic.)

CREATE TABLE t1 USING 't1.btr' (c1 INT)
ALTER TABLE t1 IN DICTIONARY USING 'test123.btr'

Or, combining both statements:

CREATE TABLE t1 IN DICTIONARY USING 'test123.btr' (c1 INT)

If you then attempt to SELECT from t1, you receive an error that the table was not found. Confusion can arise, because you just created the table—how can it not be found? Likewise, if you attempt to DROP the table without specifying IN DICTIONARY, you receive the same error. These errors are generated because there is no data file associated with the table.

Whenever you create a relational index definition for an existing Btrieve data file (for example, by issuing an ALTER TABLE statement to add a column definition of type IDENTITY), the SRDE automatically checks the Btrieve indexes defined on the file to determine whether an existing Btrieve index offers the set of parameters requested by the relational index definition. If an existing Btrieve index matches the new definition being created, then an association is created between the relational index definition and the existing Btrieve index. If there is no match, then the SRDE creates a new index definition and, if IN DICTIONARY is not specified, a new index in the data file.

USING

The USING keyword allows you to associate a CREATE TABLE or ALTER TABLE action with a particular data file.

Because Pervasive.SQL requires a Named Database to connect, the path_name provided must always be a simple file name or relative path and file name. Paths are always relative to the first Data Path specified for the Named Database to which you are connected.

The path/file name passed is partially validated when SQLPrepare is called. The following rules must be followed when specifying the path name:

  • The text must be enclosed in single quotes, as shown in the grammar definition.
  • Text must be 1 to 64 characters in length, such that the entry as specified fits in Xf$Loc in X$File. The entry is stored in Xf$Loc exactly as typed (trailing spaces are truncated and ignored).
  • The path must be a simple, relative path. Paths that reference a server or volume are not allowed. For NetWare, a volume-based path (such as SYS:\path\testfile.btr) is not considered a simple, relative path.
  • Relative paths containing a period ('.' - current directory) , double-period ('..' - parent directory), slash '\', or any combination of the three are allowed. The path must contain a file name representing the SQL table name (path_name cannot end in a slash '\' or a directory name). All file names, including those specified with relative paths, are relative to the first Data Path as defined in the Named Database configuration.
  • Root-based relative paths are also allowed. For example, assuming that the first data path is D:\PVSW\DEMODATA, the SRDE interprets the path name in the following statement as D:\TEMP\TEST123.BTR.
    CREATE TABLE t1 USING '\temp\test123.btr' (c1 int)
  • Slash ('\') characters in relative paths may be specified either LINUX style ('/') or in the customary backslash notation ('\'), depending on your preference. You may use a mixture of the two types, if desired. This is a convenience feature since you may know the directory structure scheme, but not necessarily know (or care) what type of server you are connected to. The path is stored in X$File exactly as typed. The SRDE engine converts the slash characters to the appropriate platform type when utilizing the path to open the file. Also, since data files share binary compatibility between all supported platforms, this means that as long as the directory structure is the same between platforms (and path-based file names are specified as relative paths), the database files and DDFs can be moved from one platform to another with no modifications. This makes for a much simpler cross-platform deployment with a standardized database schema.
  • If specifying a relative path, the directory structure in the USING clause must first exist. The SRDE does not create directories to satisfy the path specified in the USING clause.

Include a USING clause to specify the physical location and name of an existing data file to associate with an existing table. A USING clause also allows you to create a new data file at a particular location using an existing dictionary definition. (The string supplied in the USING clause is stored in the Xf$Loc column of the dictionary file X$File.) The original data file must be available when you create the new file since some of the file information must be obtained from the original.

In the DEMODATA sample database, the Person table is associated with the file PERSON.MKD. If you create a new file named PERSON2.MKD, the statement in the following example changes the dictionary definition of the Person table so that the table is associated with the new file.

ALTER TABLE Person IN DICTIONARY USING 'person2.mkd'

You must use either a simple file name or a relative path in the USING clause. If you specify a relative path, Pervasive.SQL interprets it relative to the first data file path associated with the database name.

The USING clause can be specified in addition to any other standard ALTER TABLE option. This means columns can be manipulated in the same statement that specifies the USING path.

If you specify a data file name that differs from the data file name currently used to store the table data, the SRDE creates the new file and copies all of the data from the existing file into the new file. For example, suppose person.mkd is the current data file that holds the data for table Person. You then alter table Person using data file person2.mkd, as shown in the statement above. The contents of person.mkd are copied into person2.mkd. Person2.mkd then becomes the data file associated with table Person and database operations affect person2.mkd. Person.mkd is not deleted, but it is not associated with the database any more.

The reason for copying the data is because Pervasive.SQL allows all other ALTER TABLE options at the same time as USING. The new data file created needs to be fully populated with the existing table's data. The file structure is not simply copied, but instead the entire contents are moved over, similar to a Btrieve BUTIL -CREATE and BUTIL -COPY. This can be helpful for rebuilding an SQL table, or compressing a file that once contained a large number of records but now contains only a few.

Note
ALTER TABLE USING copies the contents of the existing data file into the newly specified data file, leaving the old data file intact but unlinked.

WITH REPLACE

Whenever WITH REPLACE is specified with the USING keyword, Pervasive.SQL automatically overwrites any existing file name with the specified file name. The existing file is always overwritten as long as the operating system allows it.

WITH REPLACE affects only the data file, it never affects the DDFs.

The following rules apply when using WITH REPLACE:

  • WITH REPLACE can only be used with USING.
  • When used with IN DICTIONARY, WITH REPLACE is ignored because IN DICTIONARY specifies that only the DDFs are affected.

Note
No data is lost or discarded if WITH REPLACE is used with ALTER TABLE. The newly created data file, even if overwriting an existing file, still contains all data from the previous file. You cannot lose data by issuing an ALTER TABLE command.

Include WITH REPLACE in a USING clause to instruct Pervasive.SQL to replace an existing file (the file must reside at the location you specified in the USING clause). If you include WITH REPLACE, Pervasive.SQL creates a new file and copies all the data from the existing file into it. If you do not include WITH REPLACE and a file exists at the specified location, Pervasive.SQL returns a status code and does not create the new file. The status code is SRDE error -4940, Btrieve error 59.

MODIFY COLUMN and ALTER COLUMN

The ability to modify the nullability or data type of a column is subject to the following restrictions:

  • The target column cannot have a PRIMARY/FOREIGN KEY constraint defined on it.
  • If converting the old type to the new type causes an overflow (arithmetic or size), the ALTER TABLE operation is aborted.
  • If a nullable column contains NULL values, the column cannot be changed to a non-nullable column.

If you must change the data type of a key column, you can do so by dropping the key, changing the data type, and re-adding the key. Keep in mind that you must ensure that all associated key columns remain synchronized. For example, if you have a primary key in table T1 that is referenced by foreign keys in tables T2 and T3, you must first drop the foreign keys. Then you can drop the primary key. Then you need to change all three columns to the same data type. Finally, you must re-add the primary key and then the foreign keys.

The ANSI standard includes the ALTER keyword. Pervasive.SQL allows both keywords (ALTER and MODIFY) in the ALTER TABLE statement:

ALTER TABLE T1 MODIFY C1 INTEGER
ALTER TABLE T1 ALTER C1 INTEGER
ALTER TABLE T1 MODIFY COLUMN C1 INTEGER
ALTER TABLE T1 ALTER COLUMN C1 INTEGER

Pervasive.SQL allows altering a column to a smaller length if the actual data does not overflow the new, smaller length of the column. This behavior is similar to that of Microsoft SQL Server.

You can add, drop, or modify multiple columns on a single ALTER TABLE statement. Although it simplifies operations, this behavior is not considered ANSI-compliant. The following is a sample multi-column ALTER statement.

ALTER TABLE T1 (ALTER C2 INT, ADD D1 CHAR(20), DROP C4, ALTER C5 LONGVARCHAR, ADD D2 LONGVARCHAR NOT NULL)

You can convert all legacy data types (Pervasive.SQL v7 or earlier) to data types that are natively supported by Pervasive.SQL V8. But the new data types cannot be converted backwards to legacy data types.

To add a LONGVARCHAR/LONGVARBINARY column to a legacy table that contains a NOTE/LVAR column, the NOTE/LVAR column first has to be converted to a LONGVARCHAR or LONGVARBINARY column. After converting the NOTE/LVAR column to LONGVARCHAR/LONGVARBINARY, you can add more LONGVARCHAR/LONGVARBINARY columns to the table. Note that the legacy engine does not work with this new table because the legacy engine can work with only one variable length column per table.

Examples

The following statement adds the Emergency_Phone column to the Person table

ALTER TABLE person add Emergency_Phone NUMERIC(10,0)

The following statement adds two integer columns col1 and col2 to the Class table.

ALTER TABLE class(add col1 INT, add col2 INT)

To drop a column from a table definition, specify the name of the column in a DROP clause. The following statement drops the emergency phone column from the Person table.

ALTER TABLE person drop Emergency_Phone

The following statement drops col1 and col2 from the Class table.

ALTER TABLE class(drop col1, drop col2)

The following statement drops the constraint c1 in the Faculty table.

ALTER TABLE Faculty(drop CONSTRAINT c1)

This example adds an integer column col3 and drops column col2 to the Class table

ALTER TABLE class(add col3 INT, drop col2 )

The following example creates a primary key named c1 on the ID field in the Faculty table. Note that you cannot create a primary key on a Nullable column. Doing so generates the error, "Nullable columns are not allowed in primary keys".

ALTER TABLE Faculty(add CONSTRAINT c1 PRIMARY KEY(ID))

The following example creates a primary key PK_ID in the Faculty table.

ALTER TABLE Faculty(add PRIMARY KEY(ID))

The following example adds the constraint UNIQUE to the columns col1 and col2.

ALTER TABLE Class(add UNIQUE(col1,col2))

The following example drops the primary key in the Faculty table. Because a table can have only one primary key, you cannot add a primary key to a table that already has a primary key defined. To change the primary key of a table, delete the existing key then add the new primary key.

ALTER TABLE Faculty(drop PRIMARY KEY)

Before you can drop a primary key from a parent table, you must drop any corresponding foreign keys from dependent tables.

The following example adds a new foreign key to the Class table. The Faculty column is defined as an index that does not include NULL values. You cannot create a foreign key on a Nullable column.

ALTER TABLE Class ADD CONSTRAINT Teacher FOREIGN KEY (Faculty_ID) REFERENCES Faculty (ID) ON DELETE RESTRICT

In this example, the restrict rule for deletions prevents someone from removing a faculty member from the database without first either changing or deleting all of that faculty's classes.

If you add a foreign key to a table that already contains data, use the Referential Integrity (RI) test to find any data that does not conform to the new referential constraint. The RI test is run from the Check Database wizard in the Pervasive Control Center (PCC).

The following statement shows how to drop the foreign key added in this example. Pervasive.SQL drops the foreign key from the dependent table and eliminates the referential constraints between the dependent table and the parent table.

ALTER TABLE Class DROP CONSTRAINT Teacher

The following example adds a foreign key to the Class table without using the CONSTRAINT clause.

ALTER TABLE Class ADD FOREIGN KEY (Faculty_ID) REFERENCES Faculty (ID) ON DELETE RESTRICT

This creates foreign key FK_Faculty_ID. To drop the foreign key, specify the CONSTRAINT keyword:

ALTER TABLE Class DROP CONSTRAINT FK_Faculty_ID

The following example illustrates multiple adding and dropping of constraints and columns in a table. This statement drops column salary, adds a column col1 of type integer, and drops constraint c1 in the Faculty table.

ALTER TABLE Faculty(DROP salary, ADD col1 INT, DROP CONSTRAINT c1)

The following examples illustrate altering the data type of multiple columns.

ALTER TABLE T1 (ALTER C2 INT, ADD D1 CHAR(20), DROP C4, ALTER C5 LONGVARCHAR, ADD D2 LONGVARCHAR NOT NULL)
ALTER TABLE T2 (ALTER C1 CHAR(50), DROP CONSTRAINT MY_KEY, DROP PRIMARY KEY, ADD MYCOLUMN INT)

The following examples illustrate how the column default and alternate collating sequence can be set or dropped with the ALTER or MODIFY column options.

CREATE TABLE T1 (c1 INT DEFAULT 10, c2 CHAR(10))
ALTER TABLE T1 ALTER c1 INT DEFAULT 20
— resets column c1 default to 20
ALTER TABLE T1 ALTER c1 INT
— drops column c1 default
ALTER TABLE T1 ALTER c2 CHAR(10)
COLLATE 'c:\pvsw\samples\upper.alt'
— on a Windows platform, sets alternate collating sequence on column c2
ALTER TABLE T1 ALTER c2 CHAR(10)
COLLATE '\\Odin\sys:pvsw\samples\upper.alt'
— on NetWare, sets alternate collating sequence on column c2
ALTER TABLE T1 ALTER c2 CHAR(10)
— drops alternate collating sequence on column c2

Upper.alt treats upper and lower case letters the same for sorting. For example, if a database has values abc, ABC, DEF, and Def, inserted in that ordered, the sorting with upper.alt returns as abc, ABC, DEF, and Def. (The values abc and ABC, and the values DEF and Def are considered duplicates and are returned in the order in which they were inserted.) Normal ASCII sorting sequences upper case letters before lower case, such that the sorting would return as ABC, DEF, Def, abc.

Prev
ALL 		Up
Check for Revisions 		Next
ANY


ANY

Remarks

The ANY keyword works similarly to the ALL keyword except that Pervasive.SQL V8 includes the compared row in the result table if the condition is true for any row in the subquery result table.

Examples

The following statement compares the ID column from Person to the ID columns in the result table of the subquery. If the ID value from Person is equal to any of the ID values in the subquery result table, Pervasive.SQL includes the row from Person in the result table of the SELECT statement.

SELECT p.ID, p.Last_Name

FROM Person p

WHERE p.ID = ANY
(SELECT f.ID FROM Faculty f WHERE f.Dept_Name = 'Chemistry')

See Also

SELECT

Prev
ALTER TABLE 		Up
Check for Revisions 		Next
AS


AS

Remarks

Include an AS clause to assign a name to a select term or to a table name. You can use this name elsewhere in the statement to reference the select term. When you use the AS clause on a non-aggregate column, you can reference the name in WHERE, ORDER BY, GROUP BY, and HAVING clauses. When you use the AS clause on an aggregate column, you can reference the name only in an ORDER BY clause.

The name you define must be unique in the SELECT list.

Examples

The AS clause in the following statement instructs Pervasive.SQL to assign the name Total to the select term SUM (Amount_Paid) and order the results by the total for each student:

SELECT Student_ID, SUM (Amount_Paid) AS Total

FROM Billing

GROUP BY Student_ID

ORDER BY Total

The keyword AS is optional when used for table aliases as in this next example. When you use the AS clause on a table name in a FROM clause, you can reference the name in a WHERE, ORDER BY, GROUP BY, and HAVING clause.

SELECT DISTINCT c.Name, p.First_Name, c.Faculty_Id

FROM Person AS p, class AS c

WHERE p.Id = c.Faculty_Id

ORDER BY c.Faculty_Id

You can rewrite this query without using the AS clause in the FROM clause as follows.

SELECT DISTINCT c.Name, p.First_Name, c.Faculty_Id

FROM Person p, class c

WHERE p.Id = c.Faculty_Id

ORDER BY c.Faculty_Id

Once you establish a table alias, do not intermix the table name and its alias in a WHERE clause. The following does not work:

SELECT DISTINCT c.Name, p.First_Name, c.Faculty_Id

FROM Person p, class c

WHERE Person.Id = c.Faculty_Id

ORDER BY c.Faculty_Id

See Also

SELECT

Prev
ANY 		Up
Check for Revisions 		Next
BEGIN [ATOMIC]


BEGIN [ATOMIC]

Remarks

It is often convenient to group individual statements so that they can be treated as a single unit. The BEGIN and END statements are used in compound statements to group the statements into a unit. You can use a compound statement only in the body of a stored procedure or in a trigger declaration.

ATOMIC specifies that the set of statements within the unit either all succeed or all fail. If one condition within the BEGIN ATOMIC . . . END unit is not met, no records are affected. If the condition should affect more than one row, all rows (or none) are affected. For any record to be affected, all the conditions within a BEGIN
ATOMIC . . . END unit must return true.

Examples

In the following example, two UPDATEs are grouped as an ATOMIC unit. The Perm_State column in the Person table is updated only if all of the other conditions are true. That is, a record for Bill Andrew exists with 'TX' as the Perm_State, and a record for Yvette Lopez exists with 'OR' as the Perm_State. If any of these conditions are not true, neither record is updated. Assume the BEGIN . . . END unit is within a procedure.

BEGIN ATOMIC

UPDATE Person set Perm_State = 'MA' where Perm_State = 'TX' AND First_Name = 'Bill' AND Last_Name = 'Andrew';
UPDATE Person SET Perm_State = 'WA' WHERE Perm_State = 'OR' AND First_Name = 'Yvette' AND Last_Name = 'Lopez';

END

See Also

CREATE PROCEDURE

CREATE TRIGGER

Prev
AS 		Up
Check for Revisions 		Next
CALL


CALL

Remarks

Use the CALL statement to invoke a previously created stored procedure.

Examples

The following example calls a procedure without parameters:

CALL NoParms() or CALL NoParms

The following examples call a procedure with parameters:

CALL Parms(vParm1, vParm2)
CALL CheckMax(N.Class_ID)

See Also

CREATE PROCEDURE

CREATE TRIGGER

Prev
BEGIN [ATOMIC] 		Up
Check for Revisions 		Next
CASCADE


CASCADE

Remarks

If you specify CASCADE when creating a Foreign Key, Pervasive.SQL uses the DELETE CASCADE rule. When a user deletes a row in the parent table, Pervasive.SQL V8 deletes the corresponding rows in the dependent table.

See Also

ALTER TABLE

CREATE TABLE

Prev
CALL 		Up
Check for Revisions 		Next
CASE


CASE

Remarks

The CASE keyword causes Pervasive.SQL to ignore case when evaluating restriction clauses involving a string column.

For example, suppose if you have a column called Name that is defined with the CASE attribute. If you insert two rows with Name = 'Smith' and Name = 'SMITH,' a query with a restriction specifying Name = 'smith' correctly returns both rows.

Examples

The following example shows how you add a column to the Student table with the CASE keyword.

ALTER TABLE Student ADD Name char(64) CASE

The following example shows how to modify a column with the CASE keyword.

ALTER TABLE Student MODIFY Name char(64) CASE

See Also

ALTER TABLE

SELECT

Prev
CASCADE 		Up
Check for Revisions 		Next
CLOSE


CLOSE

Remarks

The CLOSE statement closes an open SQL cursor.

The cursor that the cursor name specifies must be open.

This statement is allowed only inside of a stored procedure or a trigger, since cursors and variables are only allowed inside of stored procedures and triggers.

Examples

The following example closes the cursor BTUCursor.

CLOSE BTUCursor;

See Also

OPEN

CREATE PROCEDURE

CREATE TRIGGER

Prev
CASE 		Up
Check for Revisions 		Next
COMMIT


COMMIT

The COMMIT statement signals the end of a logical transaction and converts temporary data into permanent data. The logical transaction begins with START TRANSACTION. COMMIT must always be paired with START TRANSACTION. START TRANSACTION must always be paired with a COMMIT or a ROLLBACK.

Syntax

COMMIT [WORK]

Remarks

COMMIT (and START TRANSACTION) is supported only within stored procedures. You cannot use COMMIT or START TRANSACTION within the SQL Data Manager. (The SQL Data Manager sets AUTOCOMMIT to "on.")

Any committed statements within a stored procedure are controlled by the outermost transaction of the calling ODBC application. This means that, depending on the AUTOCOMMIT mode specified on SQLSetConnectOption, calling the stored procedure externally from an ODBC application performs one of two actions. It either commits automatically (AUTOCOMMIT on, the default) or waits for you to call SQLTransact with SQL_COMMIT or SQL_ROLLBACK (when AUTOCOMMIT is set to off).

You may call multiple START TRANSACTION statements to start the nested transactions, but the outermost COMMIT controls whether any nested committed blocks are committed or rolled back. For example, if transactions are nested five levels, then five COMMIT statements are needed to commit all of the transactions. COMMIT does not release any lock until the outermost transaction is committed.

COMMIT and COMMIT WORK perform the same functionality.

Examples

The following example, within a stored procedure, begins a transaction which updates the Amount_Owed column in the Billing table. This work is committed; another transaction updates the Amount_Paid column and sets it to zero. The final COMMIT WORK statement ends the second transaction.

Statements are delimited with a semi-colon inside stored procedures and triggers.

START TRANSACTION;
UPDATE Billing B

SET Amount_Owed = Amount_Owed - Amount_Paid
WHERE Student_ID IN

(SELECT DISTINCT E.Student_ID
FROM Enrolls E, Billing B
WHERE E.Student_ID = B.Student_ID);

COMMIT WORK;
START TRANSACTION;
UPDATE Billing B

SET Amount_Paid = 0
WHERE Student_ID IN

(SELECT DISTINCT E.Student_ID
FROM Enrolls E, Billing B
WHERE E.Student_ID = B.Student_ID);

COMMIT WORK;

See Also

CREATE PROCEDURE

ROLLBACK

START TRANSACTION
Prev
CLOSE 		Up
Check for Revisions 		Next
CREATE GROUP


CREATE GROUP

The CREATE GROUP statement creates one or more security groups.

Syntax

CREATE GROUP group-name [ , group-name ]...

group-name ::= user-defined-name

Examples

The following example creates a group named pervasive.

CREATE GROUP pervasive

The next example uses a list to create several groups at once.

CREATE GROUP pervasive_dev, pervasive_marketing

See Also

DROP GROUP

GRANT

REVOKE

SET SECURITY

Prev
COMMIT 		Up
Check for Revisions 		Next
CREATE INDEX


CREATE INDEX

Use the CREATE INDEX statement to create a named index for a specified table.

Syntax

CREATE [ UNIQUE | NOT MODIFIABLE ] INDEX index-name [ IN DICTIONARY ] ON table-name [ index-definition ]

index-definition ::= ( index-segment-definition [ , index-segment-definition ]... )

index-segment-definition ::= column-name [ ASC | DESC ]

index-name ::= user-defined-name

Remarks

The maximum column size for VARCHAR and CHAR columns is 255 bytes. VARCHAR columns differ from CHAR columns in that either the length byte (Btrieve lstring) or a zero terminating byte (Btrieve zstring) are reserved, reducing the effective storage by 1 byte. In other words, you can fit 255 useful characters in a CHAR(255) type, but only 254 useful characters in a VARCHAR, after you reserve a byte for the size or null terminator.

An index cannot be created on a VARCHAR or CHAR type column if the column is nullable and at its maximum size. A nullable column is preceded in the data file by a one-byte null indicator. When an index is created on a nullable column, a segmented key is created with a one-byte segment for the null indicator and another segment for the column. The maximum allowed key size is 255 bytes. An index on a nullable VARCHAR or CHAR type column of 255 bytes would require a key of 256 bytes, which exceeds the maximum size allowed for the key.

Whenever you create a relational index definition for an existing data file and the IN DICTIONARY clause is specified, the SRDE automatically checks the Btrieve indexes defined on the file to determine whether an existing Btrieve index offers the set of attributes requested by the relational index definition. If an existing Btrieve index matches the new definition being created, then an association is created between the relational index definition and the existing Btrieve index by assigning the Btrieve index number to the new index definition. If there is no match, then the SRDE assigns the next available index number to the new index definition.

Pervasive.SQL V8 nullable columns: For data files with 4096 byte page size, you are limited to 119 index segments per file. Because each indexed nullable column with true null support requires an index consisting of 2 segments, you cannot have more than 59 indexed nullable columns in a table (or indexed nullable true null fields in a Btrieve file). This limit is smaller for smaller page sizes. Any file created with Pervasive.SQL V8, with file create mode set to 7.x or 8.x, and TRUENULLCREATE set to the default value of On, has true null support. Files created using an earlier file format, or with Pervasive.SQL 7, or with TRUENULLCREATE set to Off, do not have true null support and do not have this limitation.

A UNIQUE segment key guarantees that the combination of the segments for a particular row are unique in the file. It does not guarantee or require that each individual segment is unique.

Note
All data types can be indexed except for the following:
BIT
LONGVARBINARY
LONGVARCHAR
BLOB
CLOB

IN DICTIONARY

The purpose of using this keyword is to notify the SRDE that you wish to make modifications to the DDFs, while leaving the underlying physical data unchanged.

You cannot use this keyword on a bound database.

IN DICTIONARY is a very powerful and advanced feature. It should only be used by system administrators or when absolutely necessary. Normally, the SRDE keeps DDFs and data files perfectly synchronized, but this feature allows users the flexibility to force out-of-sync table dictionary definitions to match an existing data file. This can be useful when you want to create a definition in the dictionary to match an existing data file.

Caution
Modifying a DDF without performing parallel modifications to the underlying data file can cause serious problems.

If you have created a disjointed index, one that exists only in the DDF and not in the data file, and you attempt to drop the index without using IN DICTIONARY, you may encounter Status Code 6 (invalid key number). This error occurs because the database engine attempts to delete the index from the data file, and it is unable to do so because no such index exists in the data file.

You cannot control the number assigned to a Btrieve index (key) if you create the index through SQL. If you use SQL to create indexes on a Btrieve file that has no indexes defined, the first index created is index #0, the second is index #1, and so forth.

If the Btrieve file already has one or more indexes defined, specifying IN DICTIONARY causes the SRDE to check the Btrieve indexes for one that has the required attributes. A description of the new index is inserted into INDEX.DDF. If the SRDE can match an existing Btrieve index, the inserted description includes the index number of the existing index.

If the SRDE cannot match an existing Btrieve index, the smallest unused index number is specified in the description. The description of the new index is inserted into INDEX.DDF. If IN DICTIONARY is not specified, then a Btrieve index is created on the data file using the index number specified in the description.

Since you cannot specify which index number is used to create new indexes in INDEX.DDF, finding out the index number is a manual process. First, issue the command BUTIL -STAT on the underlying Btrieve file to get a listing of the Btrieve indexes. Then you can query X$Index to see which SQL index number(s) will be used next.

    SELECT X$Index.* from X$Index
        WHERE Xi$file in
        (SELECT Xf$Id FROM X$File
            WHERE Xf$Name = 'tablename')

Substitute the name of your database table for tablename.

Examples

The following example creates an index named Dept based on Dept_name in the Faculty table.

CREATE INDEX Dept on Faculty(Dept_Name)

The following example creates a non-modifiable segmented index in the Person table.

CREATE NOT MODIFIABLE INDEX X_Person on Person(ID, Last_Name)

The following examples create a "detached" table, one with no associated data file, then add and drop an index from the table definition. Again, this index is a disjointed index because there is no underlying Btrieve index associated with it.

CREATE TABLE t1 IN DICTIONARY (c1 int, c2 int)
CREATE INDEX idx_1 IN DICTIONARY on t1(c1)
DROP INDEX t1.idx_1 IN DICTIONARY

See Also

DROP INDEX

Prev
CREATE GROUP 		Up
Check for Revisions 		Next
CREATE PROCEDURE


CREATE PROCEDURE

The CREATE PROCEDURE statement creates a new stored procedure. Stored procedures are SQL statements that are pre-defined and saved in the database dictionary.

Syntax

CREATE PROCEDURE procedure-name
( [ parameter [, parameter ]... ] )

RETURNS ( result [ , result ]... ) ]
[ WITH DEFAULT HANDLER ]
as-or-semicolon
proc-stmt

procedure-name ::= user-defined-name

parameter ::= parameter-type-name data-type [ DEFAULT proc-expr | = proc-expr ]

| SQLSTATE

parameter-type-name ::= parameter-name

| parameter-type parameter-name
| parameter-name parameter-type

parameter-type ::= IN | OUT | INOUT | IN_OUT

parameter-name ::= :user-defined-name

proc-expr ::= same as normal expression but does not allow IF expression, or ODBC-style scalar functions

result ::= user-defined-name data-type

as-or-semicolon ::= AS | ;

proc-stmt ::=	  [ label-name : ] BEGIN [ATOMIC] [ proc-stmt [ ; proc-stmt ]... ] END [ label-name ]

| CALL procedure-name ( proc-expr [ , proc-expr ]... )
| CLOSE cursor-name
| DECLARE cursor-name CURSOR FOR select-statement [ FOR UPDATE | FOR READ ONLY ]
| DECLARE variable-name data-type [ DEFAULT proc-expr | = proc-expr ]
| DELETE WHERE CURRENT OF cursor-name
| delete-statement
| FETCH [ fetch-orientation [ FROM ] ] cursor-name [ INTO variable-name [ , variable-name ] ]
| IF proc-search-condition THEN proc-stmt [ ; proc-stmt ]... [ ELSE proc-stmt [ ; proc-stmt ]... ] END IF
| insert-statement
| LEAVE label-name
| [ label-name : ] LOOP proc-stmt [ ; proc-stmt ]... END LOOP [ label-name ]
| OPEN cursor-name
| PRINT proc-expr [ , 'string' ]
— applies only to Windows-based platforms
| RETURN [ proc-expr ]
| transaction-statement
| select-statement-with-into
| select-statement
| SET variable-name = proc-expr
| SIGNAL [ ABORT ] sqlstate-value
| START TRANSACTION
| update-statement
| UPDATE SET column-name = proc-expr [ , column-name = proc-expr ]... WHERE CURRENT OF cursor-name
| [ label-name : ] WHILE proc-search-condition DO [ proc-stmt [ ; proc-stmt ] ]... END WHILE [ label-name ]
| alter-table-statement
| create-index-statement
| create-table-statement
| create-view-statement
| drop-index-statement
| drop-table-statement
| drop-view-statement
| grant-statement
| revoke-statement
| set-statement

transaction-statement ::= commit-statement

| rollback-statement
| release-statement

commit-statement ::= see COMMIT statement
rollback-statement ::= see ROLLBACK statement
release-statement ::= see RELEASE SAVEPOINT statement
create-table-statement ::= see CREATE TABLE statement
alter-table-statement ::= see ALTER TABLE statement
drop-table-statement ::= see DROP TABLE statement
create-index-statement ::= see CREATE INDEX statement
drop-index-statement ::= see DROP INDEX statement
create-view-statement ::= see CREATE VIEW statement
drop-view-statement ::= see DROP VIEW statement
grant-statement ::= see GRANT statement
revoke-statement ::= see REVOKE statement
set-statement ::= see SET DECIMALSEPARATORCOMMA statement

label-name ::= user-defined-name

cursor-name ::= user-defined-name

variable-name ::= user-defined-name

proc-search-condition ::= same as normal search-condition, but does not allow any expression that includes a subquery.

fetch-orientation ::= | NEXT

sqlstate-value ::= 'string'

Remarks

To execute stored procedures, use the CALL statement.

Note that, in a procedure, the name of a variable and the name of a parameter must begin with a colon (:), both in the definition and use of the variable or parameter.

Statements are delimited with a semi-colon inside stored procedures and triggers.

The WITH DEFAULT HANDLER clause, when present, causes the procedure to continue execution when an error occurs. The default behavior (without this clause) is to abort the procedure with SQLSTATE set to the error state generated by the statement.

The use of a StmtLabel at the beginning (and optionally at the end) of an IF statement is an extension to ANSI SQL 3.

The PRINT statement applies only to Windows-based platforms. It is ignored on other operating system platforms.

In SQL Data Manager, the only way to test a stored procedure by using variable parameters is to call the stored procedure from another stored procedure. This technique is shown in the example for pdate (CREATE PROCEDURE pdate();) .

You may use variables as SELECT items only within stored procedures. This technique is shown in the example for varsub1 (CREATE PROCEDURE varsub1 ();) .

Data Type Restrictions

The following data types cannot be passed as parameters or declared as variables in a stored procedure or trigger:
Table 3-1 Data Types Prohibited in Stored Procedures and Triggers
BFLOAT4 | BFLOAT8
NUMERIC | NUMERICSA
NUMERICSTS |

Limits

The following limitations must be observed when creating stored procedures:
Attribute | Limit
Number of columns allowed in a trigger or stored procedure | 300
Number of arguments in a parameter list for a stored procedure | 300
Size of a stored procedure | 64 KB

Examples

The following example creates stored procedure Enrollstudent, which inserts a record into the Enrolls table, given the Student ID and the Class ID.

CREATE PROCEDURE Enrollstudent(in :Stud_id integer, in :Class_Id integer, IN :GPA REAL);

BEGIN

INSERT INTO Enrolls VALUES(:Stud_id, :Class_id, :GPA);

END;

Use the following statement to call the stored procedure.

CALL Enrollstudent(1023456781, 146, 3.2)

Use the following statement to retrieve the newly inserted record.

SELECT * FROM Enrolls WHERE Student_id = 1023456781

The CALL and SELECT statements, respectively, call the procedure by passing arguments, then display the row that was added.

The following procedure reads the Class table, using the classId parameter passed in by the caller and validates that the course enrollment is not already at its limit.

CREATE PROCEDURE Checkmax(in :classid integer);
Begin

DECLARE :numenrolled integer;
DECLARE :maxenrolled integer;
SELECT COUNT(*) INTO :numenrolled  FROM  Enrolls WHERE class_ID = :classid;
SELECT  Max_size INTO  :maxenrolled  FROM  Class WHERE id = :classid;
IF  (:numenrolled > :maxenrolled)  THEN

PRINT  'Enrollment Failed. Number of students enrolled reached maximum allowed for this class' ;

ELSE

PRINT  'Enrollment Possible. Number of students enrolled has not reached maximum allowed for this class';

END  IF;

END;
CALL Checkmax(101)

Note that COUNT(expression) counts all non-NULL values for an expression across a predicate. COUNT(*) counts all values, including NULL values.

The following is an example of using the OUT parameter when creating stored procedures. Calling this procedure returns the number of students into the variable :outval that satisfies the WHERE clause.

CREATE PROCEDURE  PROCOUT (out :outval INTEGER)
AS BEGIN
SELECT COUNT(*) INTO :outval FROM Enrolls WHERE Class_Id = 101;
END;

The following is an example of using the INOUT parameter when creating stored procedures. Calling this procedure requires an INPUT parameter :IOVAL and returns the value of the output in the variable :IOVAL. The procedure sets the value of this variable based on the input and the IF condition.

CREATE PROCEDURE PROCIODATE (inOUT :IOVAL DATE)
AS BEGIN

IF :IOVAL = '1982-03-03'
THEN

SET :IOVAL ='1982-05-05';

ELSE

SET :IOVAL = '1982-03-03';

END IF;

END;

You cannot call the above procedure using a literal value (as in call prociodate('1982-03-03')), because it requires an output parameter. You must first bind the parameter using ODBC calls, or you can test the procedure by creating another procedure to call it, as shown here:

CREATE PROCEDURE pdate();
BEGIN
   DECLARE :a DATE;
   CALL prociodate(:a);
   PRINT :a;
END

CALL pdate

The following example illustrates using the RETURNS clause in a procedure. This sample returns all of the data from the Class table where the Start Date is equal to the date passed in on the CALL statement.

CREATE PROCEDURE DATERETURNPROC(IN :PDATE DATE)
RETURNS(

ID INTEGER,
Name CHAR(7),
Section CHAR(3),
Max_Size USMALLINT,
Start_Date DATE,
Start_Time TIME,
Finish_Time TIME,
Building_Name CHAR(25),
Room_Number UINTEGER,
Faculty_ID UBIGINT
);

BEGIN
SELECT ID, Name, Section, Max_Size, Start_Date, Start_Time, Finish_Time, Building_Name, Room_Number, Faculty_ID FROM CLASS WHERE START_DATE = :PDATE;
END;

CALL DATERETURNPROC('2001-06-05')

Note that the user-defined names in the RETURNS clause do not have to be named identically to the column names that appear in the selection list, as this example shows.

The following example shows the use of the WHERE CURRENT OF clause, which applies to positioned deletes.

CREATE PROCEDURE MyProc(IN :CourseName CHAR(7)) AS

BEGIN

    DECLARE c1 CURSOR FOR SELECT name FROM course
        WHERE name = :CourseName FOR UPDATE;
    OPEN c1;
    FETCH NEXT FROM c1 INTO :CourseName;
    DELETE WHERE CURRENT OF c1;
    CLOSE c1;

END;

CALL MyProc('HIS 305')

(Note that if you use a SELECT inside of a WHERE clause of a DELETE, it is a searched DELETE not a positioned DELETE.)

The following example shows the use of a variable (:i) as a SELECT item. The example assumes that table1 does not already exist. All records in the person table with an ID greater than 950000000 are selected, then inserted into col2 of table1. Col1 contains the value 0, 1, 2, 3, or 4 as defined by the WHILE loop.

CREATE TABLE table1 (col1 CHAR(10), col2 BIGINT);

CREATE PROCEDURE varsub1 ();
BEGIN

DECLARE :i INT;
SET :i = 0;
WHILE :i < 5 DO

BEGIN
  INSERT INTO table1 (col1, col2) SELECT :i , A.ID FROM PERSON A WHERE A.ID > 950000000;
  SET :i = :i + 1;
END;

END WHILE;

END

CALL varsub1
SELECT * FROM table1
    -- returns 110 rows

The following is an example of using ATOMIC, which groups a set of statements so that either all succeed or all fail. ATOMIC can be used only within the body of a stored procedure or trigger.

The first procedure does not specify ATOMIC, the second does.

CREATE TABLE t1 (c1 INTEGER)
CREATE UNIQUE INDEX t1i1 ON t1 (c1)
CREATE PROCEDURE p1 ();

BEGIN

INSERT INTO t1 VALUES (1);
INSERT INTO t1 VALUES (1);

END;

CREATE PROCEDURE p2 ();

BEGIN ATOMIC

INSERT INTO t1 VALUES (2);
INSERT INTO t1 VALUES (2);

END;

CALL p1()
CALL p2()
SELECT * FROM t1

Both procedures return an error because they attempt to insert duplicate values into a unique index.

The result is that t1 contains only one record because the first INSERT statement in procedure p1 succeeds even though the second fails. Likewise, the first INSERT statement in procedure p2 succeeds but the second fails. However, since ATOMIC is in procedure p2, all of the work done inside procedure p2 is rolled back when the error is encountered.

This example uses a stored procedure to create two tables and insert one row of default values into each. It then turns on security and grants permissions to user user1.

CREATE PROCEDURE p1 ();
BEGIN
   CREATE TABLE t1 (c1 INT DEFAULT 10, c2 INT DEFAULT 100);
   CREATE TABLE t2 (c1 INT DEFAULT 1 , c2 INT DEFAULT 2);
   INSERT INTO t1 DEFAULT VALUES;
   INSERT INTO t2 DEFAULT VALUES;
    SET SECURITY = larry;
   GRANT LOGIN TO user1:user1;
   GRANT SELECT ON t1 TO user1;
   GRANT INSERT ON t2 TO user1;
END;

CALL p1
SELECT * FROM t1
    -- returns 10, 100
SELECT * FROM t2
    -- returns 1, 2

This example uses a stored procedure to revoke privileges from user user1, drop the two tables created in Example A, and turn off database security.

CREATE PROCEDURE p3 ();
BEGIN
   REVOKE ALL ON t1 FROM user1;
   REVOKE ALL ON t2 FROM user1;
   DROP TABLE t1;
   DROP TABLE t2;
   SET SECURITY = NULL;
END;

CALL p3
SELECT * FROM t1
    -- returns an error, table not found
SELECT * FROM t2
    -- returns an error, table not found

Cursor loop example

The following example shows how to loop through a cursor.

CREATE TABLE atable (c1 INT, c2 INT)
 INSERT INTO atable VALUES (1,1)
 INSERT INTO atable VALUES (1,2)
 INSERT INTO atable VALUES (2,2)
 INSERT INTO atable VALUES (2,3)
 INSERT INTO atable VALUES (3,3)
 INSERT INTO atable VALUES (3,4)

CREATE PROCEDURE pp ();
 BEGIN
   DECLARE :i INTEGER;
   DECLARE c1Bulk CURSOR FOR SELECT c1 FROM atable ORDER BY c1 FOR UPDATE;
   OPEN c1Bulk;
     BulkLinesLoop:
     LOOP
       FETCH NEXT FROM c1Bulk INTO :i;
        IF SQLSTATE = '02000' THEN
          LEAVE BulkLinesLoop;
        END IF;
       UPDATE  SET c1 = 10 WHERE CURRENT OF c1Bulk;
     END LOOP;
     CLOSE c1Bulk;
 END

CALL pp
    --Succeeds
SELECT * FROM atable
    --Returns 6 rows

Using Stored Procedures

As an example, CALL foo(a, b, c) executes the stored procedure "foo" with parameters a, b, and c. Any of the parameters may be a dynamic parameter ('?'), which is necessary for retrieving the values of output and inout parameters. For example: CALL foo {(?, ?, 'TX')}. The curly braces are optional in your source code.

This is how stored procedures work in the current version of Pervasive.SQL.

  • Triggers (CREATE TRIGGER, DROP TRIGGER) are supported as a form of stored procedure. This support includes tracking dependencies that the trigger has on tables, and procedures, in the database. You cannot use CREATE PROCEDURE or CREATE TRIGGER in the body of a stored procedure or a trigger.
  • CONTAINS, NOT CONTAINS, BEGINS WITH are not supported.
  • There is no support for dynamic SQL statement construction.
  • LOOP: post conditional loops are not supported (REPEAT...UNTIL).
  • ELSEIF: The conditional format uses IF ... THEN ... ELSE. There is no ELSEIF support.
  • CASE: There is no support for CASE in stored procedures.

General Stored Procedure Engine Limitations

You should be aware of the following limitations before using stored procedures.

  • There is no qualifier support in CREATE PROCEDURE or CREATE TRIGGER.
  • Maximum length of a stored procedure variable name is 128 characters.
  • Maximum length of a stored procedure name is 30 characters.
  • Only partial syntactical validation occurs at CREATE PROCEDURE or CREATE TRIGGER time. Column names are not validated until run time.
  • There is currently no support for using subqueries everywhere expressions are used. For example, set :arg = SELECT MIN(sal) FROM emp is not supported. However, you could rewrite this query as SELECT min(sal) INTO :arg FROM emp.
  • Only the default error handler is supported.

Limits to SQL Variables and Parameters

  • Variable names must be preceded with a colon (:). This allows the stored procedure parser to differentiate between variables and column names.
  • Variable names are case insensitive.
  • No session variables are supported. Variables are local to the procedure.

Limits to Cursors

  • Positioned UPDATE does not accept a table name.

Limits when using Long Data

  • When you pass long data as arguments to an imbedded procedure, (that is, a procedure calling another procedure), the data is truncated to 65500 bytes.
  • Long data arguments to and from procedures are limited to a total of 2 MB.

Internally long data may be copied between cursors with no limit on data length. If a long data column is fetched from one statement and inserted into another, no limit is imposed. If, however, more than one destination is required for a single long data variable, only the first destination table receives multiple calls to PutData. The remaining columns are truncated to the first 65500 bytes. This is a limitation of the ODBC GetData mechanism.

See Also

DROP PROCEDURE

Prev
CREATE INDEX 		Up
Check for Revisions 		Next
CREATE TABLE


CREATE TABLE

The CREATE TABLE statement creates a new table in a database.

CREATE TABLE contains functionality that goes beyond minimal or core SQL conformance. CREATE TABLE supports Referential Integrity features. Pervasive.SQL conforms closely to SQL 92 with the exception of ColIDList support.

Syntax

CREATE TABLE table-name [ option ] [ IN DICTIONARY ]
    [ USING 'path_name'] [ WITH REPLACE ]
    ( table-element [ , table-element ]... )

option ::= DCOMPRESS | PAGESIZE = size

table-name ::= user-defined-name

table-element ::= column-definition | table-constraint-definition

column-definition ::= column-name data-type [ DEFAULT default-value ] [ column-constraint [ column-constraint ]... [CASE | COLLATE collation-name ]

column-name ::= user-defined-name

data-type ::= data-type-name [ (precision [ , scale ] ) ]

precision ::= integer

scale ::= integer

default-value ::= literal

literal ::= 'string'

| number
| { d 'date-literal' }
| { t 'time-literal' }
| { ts 'timestamp-literal' }

column-constraint ::= [ CONSTRAINT constraint-name ] col-constraint

constraint-name ::= user-defined-name

col-constraint ::= NOT NULL

| UNIQUE
| PRIMARY KEY
| REFERENCES table-name [ ( column-name ) ] [ referential-actions ]

table-constraint-definition ::= [ CONSTRAINT constraint-name ] table-constraint

table-constraint ::= 	  UNIQUE ( column-name [ , column-name ]... )

| PRIMARY KEY ( column-name [ , column-name ]... )
| FOREIGN KEY ( column-name [ , column-name ] )

  REFERENCES table-name
  [ ( column-name [ , column-name ]... ) ]
  [ referential-actions ]

referential-actions ::= referential-update-action [ referential-delete-action ]

| referential-delete-action [ referential-update-action ]

referential-update-action ::= ON UPDATE RESTRICT

referential-delete-action ::= ON DELETE CASCADE

| ON DELETE RESTRICT

Remarks

Indexes must be created with the CREATE INDEX statement.

Foreign key constraint names must be unique in the dictionary. All other constraint names must be unique within the table in which they reside and must not have the same name as a column.

If the primary key name is omitted, the name of the first column in the key, prefixed by "PK_" is used as the name of the constraint.

If a reference column is not listed, the reference becomes, by default, the primary key of the table referenced. If a PK is unavailable, a "Key not found" error returns. You can avoid this situation by enumerating the target column.

If the foreign key name is omitted, the name of the first column in the key, prefixed by "FK_" is used as the name of the constraint. This is different behavior from previous versions of Pervasive.SQL.

If the UNIQUE key constraint name is omitted, the name of the first column in the constraint, prefixed by "UK_" is used as the name of the constraint.

If the NOT NULL key name is omitted, the name of the first column in the key, prefixed by "NN_" is used as the name of the constraint.

The maximum length of a constraint name is 20 characters. Pervasive.SQL uses the left most 20 characters of the name after the prefix, if any, has been prepended.

A foreign key may reference the primary key of the same table (known as a self-referencing key).

If CREATE TABLE succeeds, the data file name for the created table is xxx.mkd, where xxx is the specified table name. If the table already exists, it is not replaced, and error -1303, "Table already exists" is signalled. The user must drop the table before replacing it.

Limitations on Record Size

The total size of the fixed-length portion of any data record may not exceed 65535 bytes. The fixed-length portion of any data record is made up of the following:

  • all the columns that have a fixed sized (all columns except for LONGVARCHAR and LONGVARBINARY)
  • one byte for each column that allows null values
  • 8 bytes for each variable-length column (column of type LONGVARCHAR or LONGVARBINARY).

If you attempt to create a table that exceeds this limit, or if you attempt modifications that would cause a table to exceed the limit, the SRDE returns Status Code -3016, "The maximum fixed-length rowsize for the table has been exceeded."

To determine the size in bytes of the fixed-length portion of a record before you attempt to create a new table, you can use the following calculation:

(sum of the fixed-length column sizes in bytes) + (number of nullable columns) + ( 8 * number of variable-length columns) = record size in bytes

If you want to determine the size of the fixed-length portion of the record for an existing data file, you can use the BUTIL -STAT command to display a report that includes this information.

Example

Assume you have a table with the following columns defined:
Table 3-2
Type | Number of columns of this type | Nullable?
VARCHAR(216) | 1 | Yes
VARCHAR(213) | 5 | All columns
CHAR(42) | 1494 | All columns

Each VARCHAR has two extra bytes reserved for it. One bite for the preceding NULL indicator and one trailing byte because VARCHAR is implemented as a ZSTRING. Each CHAR has a preceding byte reserved for the NULL indicator.

Therefore, the record size is 1 x 218 + 5 x 215 + 1494 x 43 = 65535 bytes

In this example, you could not add another column of any length without exceeding the fixed-length limit.

Delete Rule

You can include an ON DELETE clause to define the delete rule Pervasive.SQL enforces for an attempt to delete the parent row to which a foreign key value refers. The delete rules you can choose are as follows:

  • If you specify CASCADE, Pervasive.SQL uses the delete cascade rule. When a user deletes a row in the parent table, SRDE deletes the corresponding row in the dependent table.
  • If you specify RESTRICT, Pervasive.SQL enforces the delete restrict rule. A user cannot delete a row in the parent table if a foreign key value refers to it.

If you do not specify a delete rule, Pervasive.SQL applies the restrict rule by default.

Update Rule

Pervasive.SQL enforces the update restrict rule. This rule prevents the addition of a row containing a foreign key value if the parent table does not contain the corresponding primary key value. This rule is enforced whether or not you use the optional ON UPDATE clause, which allows you to specify the update rule explicitly.

IN DICTIONARY

See the discussion of IN DICTIONARY forALTER TABLE .

USING

The USING keyword allows you to associate a CREATE TABLE or ALTER TABLE action with a particular data file.

Because Pervasive.SQL requires a Named Database to connect, the path_name provided must always be a simple file name or relative path and file name. Paths are always relative to the first Data Path specified for the Named Database to which you are connected.

The path/file name passed is partially validated when SQLPrepare is called. The following rules must be followed when specifying the path name:

  • The text must be enclosed in single quotes, as shown in the grammar definition.
  • Text must be 1 to 64 characters in length, such that the entry as specified fits in Xf$Loc in X$File. The entry is stored in Xf$Loc exactly as typed (trailing spaces are truncated and ignored).
  • The path must be a simple, relative path. Paths that reference a server or volume are not allowed. For NetWare, a volume-based path (such as SYS:\path\testfile.btr) is not considered a simple, relative path.
  • Relative paths containing a period ('.' - current directory), double-period ('..' - parent directory), slash '\', or any combination of the three are allowed. The path must contain a file name representing the SQL table name (path_name cannot end in a slash '\' or a directory name). All file names, including those specified with relative paths, are relative to the first Data Path as defined in the Named Database configuration.
  • Root-based relative paths are also allowed. For example, assuming that the first data path is D:\PVSW\DEMODATA, the SRDE interprets the path name in the following statement as D:\TEMP\TEST123.BTR.
    CREATE TABLE t1 USING '\temp\test123.btr' (c1 int)
  • Slash ('\') characters in relative paths may be specified either LINUX style ('/') or in the customary backslash notation ('\'), depending on your preference. You may use a mixture of the two types, if desired. This is a convenience feature since you may know the directory structure scheme, but not necessarily know (or care) what type of server you are connected to. The path is stored in X$File exactly as typed. The SRDE engine converts the slash characters to the appropriate platform type when utilizing the path to open the file. Also, since data files share binary compatibility between all supported platforms, this means that as long as the directory structure is the same between platforms (and path-based file names are specified as relative paths), the database files and DDFs can be moved from one platform to another with no modifications. This makes for a much simpler cross-platform deployment with a standardized database schema.
  • If specifying a relative path, the directory structure in the USING clause must first exist. The SRDE does not create directories to satisfy the path specified in the USING clause.

Include a USING clause to specify the physical location of the data file associated with the table. This is necessary when you are creating a table definition for an existing data file, or when you want to specify explicitly the name or physical location of a new data file.

If you do not include a USING clause, Pervasive.SQL generates a unique file name (based on the table name with the extension .MKD) and creates the data file in the first directory specified in the data file path associated with the database name.

If the USING clause points to an existing data file, the SRDE creates the table in the DDFs and returns SQL_SUCCESS_WITH_INFO. The informational message returned indicates that the dictionary entry now points to an existing data file. If you want CREATE TABLE to return only SQL_SUCCESS, specify IN DICTIONARY on the CREATE statement. If WITH REPLACE is specified (see below), then any existing data file with the same name is destroyed and overwritten with a newly created file.

Note
Pervasive.SQL returns a successful status code if you specify an existing data file.

Whenever you create a relational index definition for an existing data file (for example, by issuing a CREATE TABLE USING statement with a column definition of type IDENTITY), the SRDE automatically checks the Btrieve indexes defined on the file to determine whether an existing Btrieve index offers the set of parameters requested by the relational index definition. If an existing Btrieve index matches the new definition being created, then an association is created between the relational index definition and the existing Btrieve index. If there is no match, then the SRDE creates a new index definition and, if IN DICTIONARY is not specified, a new index in the data file.

WITH REPLACE

Whenever WITH REPLACE is specified with the USING keyword, Pervasive.SQL automatically overwrites any existing file name with the specified file name. The existing file is always overwritten as long as the operating system allows it.

WITH REPLACE affects only the data file, it never affects the DDFs.

The following rules apply when using WITH REPLACE:

  • WITH REPLACE can only be used with USING.
  • When used with IN DICTIONARY, WITH REPLACE is ignored because IN DICTIONARY specifies that only the DDFs are affected.

If you include WITH REPLACE in your CREATE TABLE statement, Pervasive.SQL creates a new data file to replace the existing file (if the file exists at the location you specified in the USING clause). Pervasive.SQL discards any data stored in the original file with the same name. If you do not include WITH REPLACE and a file exists at the specified location, Pervasive.SQL returns a status code and does not create a new file.

WITH REPLACE affects only the data file; it does not affect the table definition in the dictionary.

Note
No data is lost or discarded if WITH REPLACE is used with ALTER TABLE. The newly created data file, even if overwriting an existing file, still contains all data from the previous file. You cannot lose data by issuing an ALTER TABLE command.

DCOMPRESS

The DCOMPRESS option specifies that the data file for the specified table should use data compression to reduce the file size on disk.

The following example creates a table with file compression and page size 1024 bytes:

CREATE TABLE t1 DCOMPRESS PAGESIZE=1024 (c1 INT DEFAULT 10, c2 CHAR(10) DEFAULT 'abc')
PAGESIZE

The PAGESIZE option specifies that the data file for the specified table should use pages of size bytes. The value of size can be any multiple of 512 from 512 to 4096 bytes. Default value is 4096 bytes. Generally speaking, most applications perform best with the default page size.

The following example creates a table with file compression and page size 3584 bytes, specifying creation of the particular data file identified by the relative path, ..\data1.mkd:

CREATE TABLE t1 DCOMPRESS PAGESIZE=3584
USING '..\data1.mkd' (c1 INT DEFAULT 10, c2 CHAR(10) DEFAULT 'abc')

Examples

The following example creates a table named Billing with columns Student_ID, Transaction_Number, Log, Amount_Owed, Amount_Paid, Registrar_ID and Comments, using the specified data types.

CREATE TABLE Billing

(Student_ID  UBIGINT,
Transaction_Number USMALLINT,
Log TIMESTAMP,
Amount_Owed DECIMAL(6,2),
Amount_Paid DECIMAL(6,2),
Registrar_ID DECIMAL(10,0),
Comments LONGVARCHAR)

The following example creates a table named Faculty in the database with columns ID, Dept_Name, Designation, Salary, Building_Name, Room_Number, Rsch_Grant_Amount, and a primary key based on column ID.

CREATE TABLE Faculty

  (ID                   UBIGINT,
   Dept_Name            CHAR(20) CASE,
   Designation          CHAR(10) CASE,
   Salary               CURRENCY,
   Building_Name        CHAR(25) CASE,
   Room_Number          UINTEGER,
   Rsch_Grant_Amount    DOUBLE,

PRIMARY KEY (ID))

The following example is similar to the one just discussed, except the ID column, which is the primary key, is designated as UNIQUE.

CREATE TABLE organizations

(ID UBIGINT UNIQUE,
Name LONGVARCHAR,
Advisor CHAR(30),
Number_of_people INTEGER,
Date_started DATE,
Time_started TIME,
Date_modified TIMESTAMP,
Total_funds DOUBLE,
Budget DECIMAL(2,2),
Avg_funds REAL,
President VARCHAR(20) CASE,
Number_of_executives SMALLINT,
Number_of_meetings TINYINT,
Office UTINYINT,
Active BIT,

PRIMARY KEY(ID))

In the next example, assume that you need a table called StudentAddress to contain students' addresses. You need to alter the Student table's id column to be a primary key and then create a StudentAddress table. (The Student table is part of the DEMODATA sample database.) Four ways are shown how to create the StudentAddress table.

First, make the id column of table Student a primary key.

ALTER TABLE Student ADD PRIMARY KEY (id)

This next statement creates a StudentAddress table to have a foreign key referencing the id column of table Student with the DELETE CASCADE rule. This means that whenever a row is deleted from the Student table (Student is the parent table in this case), all rows in the StudentAddress table with that same id are also deleted.

CREATE TABLE StudentAddress (id UBIGINT REFERENCES Student (id) ON DELETE CASCADE, addr CHAR(128))

This next statement creates a StudentAddress table to have a foreign key referencing the id column of table Student with the DELETE RESTRICT rule. This means that whenever a row is deleted from the Student table and there are rows in the StudentAddress table with that same id, an error occurs. You need to explicitly delete all the rows in StudentAddress with that id before the row in the Student table, the parent table, can be deleted.

CREATE TABLE StudentAddress (id UBIGINT REFERENCES Student (id) ON DELETE RESTRICT, addr CHAR(128))

This next statement creates a StudentAddress table to have a foreign key referencing the id column of table Student with the UPDATE RESTRICT rule. This means that if a row is added to the StudentAddress table that has an id that does not occur in the Student table, an error occurs. In other words, you must have a parent row before you can have foreign keys referring to that row. This is the default behavior of Pervasive.SQL. Moreover, Pervasive.SQL does not support any other UPDATE rules. Thus, whether this rule is stated explicitly or not makes no difference.

CREATE TABLE StudentAddress (id UBIGINT REFERENCES Student (id) ON UPDATE RESTRICT, addr CHAR(128))

This next statement creates a StudentAddress table to have a foreign key referencing the id column of table Student with the DELETE RESTRICT and UPDATE RESTRICT rules. The Pervasive.SQL parser accepts this syntax with RI rules. However, as stated above, the UPDATE RESTRICT rule is redundant since Pervasive.SQL does not behave any other way with respect to UPDATE rules.

CREATE TABLE StudentAddress (id UBIGINT REFERENCES Student (id) ON DELETE RESTRICT, addr CHAR(128))

This next example shows how to use an alternate collating sequence (ACS) when you create a table. The ACS file used is the sample one provided with Pervasive.SQL.

On Windows platforms:

CREATE TABLE t5 (c1 CHAR(20) COLLATE 'c:\pvsw\samples\upper.alt')

On NetWare:

CREATE TABLE t5 (c1 CHAR(20) COLLATE '\\Odin\sys:pvsw\samples\upper.alt')

Upper.alt treats upper and lower case letters the same for sorting. For example, if a database has values abc, ABC, DEF, and Def, inserted in that ordered, the sorting with upper.alt returns as abc, ABC, DEF, and Def. (The values abc and ABC, and the values DEF and Def are considered duplicates and are returned in the order in which they were inserted.) Normal ASCII sorting sequences upper case letters before lower case, such that the sorting would return as ABC, DEF, Def, abc.

See Also

DROP TABLE

Prev
CREATE PROCEDURE 		Up
Check for Revisions 		Next
CREATE TRIGGER


CREATE TRIGGER

The CREATE TRIGGER statement creates a new trigger in a database. Triggers are a type of stored procedure that are automatically executed when data in a table is modified with an INSERT, UPDATE, or DELETE.

Unlike a regular stored procedure, a trigger cannot be executed directly nor can it have parameters. Triggers do not return a result set nor can they be defined on views.

Syntax

CREATE TRIGGER trigger-name before-or-after ins-upd-del ON table-name

[ ORDER number ]
[ REFERENCING referencing-alias ] FOR EACH ROW
[ WHEN proc-search-condition ] proc-stmt

trigger-name ::= user-defined-name

before-or-after ::= BEFORE | AFTER

ins-upd-del ::= INSERT | UPDATE | DELETE

referencing-alias ::= OLD [ AS ] correlation-name [ NEW [ AS ] correlation-name ]

| NEW [ AS ] correlation-name [ OLD [ AS ] correlation-name ]

correlation-name ::= user-defined-name

Remarks

This function is an extension to SQL grammar as documented in the Microsoft ODBC Programmer's Reference and implements a subset of the SQL 3/PSM (Persistent Stored Modules) specification.

Note
In a trigger, the name of a variable must begin with a colon (:).

OLD (OLD correlation-name) and NEW (NEW correlation-name) can be used used inside triggers, not in a regular stored procedure.

In a DELETE or UPDATE trigger, "OLD" or a OLD correlation-name must be prepended to a column name to reference a column in the row of data prior to the update or delete operation.

In an INSERT or UPDATE trigger, "NEW" or a NEW correlation-name must be prepended to a column name to reference a column in the row about to be inserted or updated.

Trigger names must be unique in the dictionary.

Triggers are executed either before or after an UPDATE, INSERT, or DELETE statement is executed, depending on the type of trigger.

Note
CREATE TRIGGER statements are subject to the same length and other limitations as CREATE PROCEDURE. For more information, see Limits and Data Type Restrictions .

Examples

The following example creates a trigger that records any new values inserted into the Tuition table into TuitionIDTable.

CREATE TABLE Tuitionidtable (primary key(id), id ubigint)
CREATE TRIGGER  InsTrig

BEFORE INSERT ON Tuition
REFERENCING NEW AS Indata

FOR EACH ROW
INSERT INTO Tuitionidtable VALUES(Indata.ID);

An INSERT on Tuition calls the trigger.

The following example shows how to keep two tables, A and B, synchronized with triggers. Both tables have the same structure.

CREATE TABLE A (col1 INTEGER, col2 CHAR(10))
CREATE TABLE B (col1 INTEGER, col2 CHAR(10))
CREATE TRIGGER MyInsert

AFTER INSERT ON A FOR EACH ROW
INSERT INTO B VALUES (NEW.col1, NEW.col2);

CREATE TRIGGER MyDelete

AFTER DELETE ON A FOR EACH ROW
DELETE FROM B WHERE B.col1 = OLD.col1 AND B.col2 = OLD.col2;

CREATE TRIGGER MyUpdate

AFTER UPDATE ON A FOR EACH ROW
UPDATE B SET col1 = NEW.col1, col2 = NEW.col2 WHERE B.col1 = OLD.col1 AND B.col2 = OLD.col2;

Note that OLD and NEW in the example keep the tables synchronized only if table A is altered with non-positional SQL statements. If the ODBC SQLSetPos API or a positioned update or delete is used, then the tables stay synchronized only if table A does not contain any duplicate records. An SQL statement cannot be constructed to alter one record but leave another duplicate record unaltered.

See Also

DROP TRIGGER

Prev
CREATE TABLE 		Up
Check for Revisions 		Next
CREATE VIEW


CREATE VIEW

Use the CREATE VIEW statement to define a stored view on the database.

Syntax

CREATE VIEW view-name [ ( column-name [ , column-name ]...) ] AS query-specification

view-name ::= user-defined-name

Remarks

A view is a database object that stores a query and behaves like a table. A view contains a set of columns and rows. Data accessed through a view is stored in one or more tables; the tables are referenced by SELECT statements. Data returned by a view is produced dynamically every time the view is referenced.

The maximum length of a view name is 20 characters. The maximum number of columns in a view is 256. There is a 64KB limit on view definitions.

A grouped view is one that contains the GROUP BY clause and/or an aggregate function in the SELECT list. Grouped views are not allowed in the FROM clause of a SELECT statement with a join (that is, with multiple tables). Grouped views are not allowed in the FROM clause of a SELECT statement with a GROUP BY.

Grouped views may not be used in a subquery.

The WHERE clause against a grouped view is a HAVING clause, and appended to the HAVING clauses of the grouped view.

View definitions cannot contain UNION operators. The operator UNION cannot be applied to any SQL statement that references one or more views.

View definitions cannot contain procedures, nor can they contain an ORDER BY.

Examples

The following statement creates a view named vw_Person, which creates a phone list of all the people enrolled in a university. This view lists the last names, first names and telephone numbers with a heading for each column. The Person table is part of the DEMODATA sample database.

CREATE VIEW vw_Person (lastn,firstn,phone) AS SELECT Last_Name, First_Name,Phone FROM  Person

In a subsequent query on the view, you may use the column headings in your SELECT statement, as shown in the next example.

SELECT lastn, firstn FROM vw_Person

See Also

DROP VIEW

Prev
CREATE TRIGGER 		Up
Check for Revisions 		Next
DECLARE


DECLARE

Remarks

Use the DECLARE statement to define an SQL variable.

This statement is allowed only inside of a stored procedure or a trigger, since cursors and variables are allowed only inside of stored procedures and triggers.

The name of a variable must begin with a colon (:), both in the definition and use of the variable or parameter. A variable must be declared before it can be set to a value with SET VARIABLE.

Use a separate DECLARE statement for each variable (you cannot declare multiple variables with a single statement). Specify a value or values for data types that require a size, precision, or scale, such as CHAR, DECIMAL, NUMERIC, and VARCHAR.

Examples

The following examples show how to declare variables, including ones that require a value for size, precision, or scale.

DECLARE :SaleItem CHAR(15);
DECLARE :CruiseLine CHAR(25) DEFAULT 'Open Seas Tours'
DECLARE :UnitWeight DECIMAL(10,3);
DECLARE :Titration NUMERIC(12,3);
DECLARE :ReasonForReturn VARCHAR(200);
DECLARE :Counter INTEGER = 0;
DECLARE :CurrentCapacity INTEGER = 9;

See Also

CREATE PROCEDURE

CREATE TRIGGER

SET VARIABLE

Prev
CREATE VIEW 		Up
Check for Revisions 		Next
DECLARE CURSOR


DECLARE CURSOR

The DECLARE CURSOR statement defines an SQL cursor.

Syntax

DECLARE cursor-name CURSOR FOR select-statement
  [ FOR UPDATE | FOR READ ONLY ]

Remarks

In Pervasive.SQL V8, this statement is only allowed inside of a stored procedure or a trigger, since cursors and variables are only allowed inside of stored procedures and triggers.

The default behavior for cursors is read-only. Therefore, you must use FOR UPDATE to explicitly designate an update (write or delete).

Examples

The following example creates a cursor that selects values from the Degree, Residency, and Cost_Per_Credit columns in the Tuition table and orders them by ID number.

DECLARE BTUCursor CURSOR

FOR SELECT Degree, Residency, Cost_Per_Credit
FROM Tuition
ORDER BY ID;

The following example uses FOR UPDATE to ensure a delete.

CREATE PROCEDURE MyProc(IN :CourseName CHAR(7)) AS

BEGIN

    DECLARE c1 CURSOR FOR SELECT name FROM course
        WHERE name = :CourseName FOR UPDATE;
    OPEN c1;
    FETCH NEXT FROM c1 INTO :CourseName;
    DELETE WHERE CURRENT OF c1;
    CLOSE c1;

END;

CALL MyProc('HIS 305')

See Also

CREATE PROCEDURE

Cursor loop example

CREATE TRIGGER

Prev
DECLARE 		Up
Check for Revisions 		Next
DELETE (positioned)


DELETE (positioned)

Use the positioned DELETE statement to remove the current row of a view associated with an SQL cursor.

Syntax

DELETE WHERE CURRENT OF cursor-name

Remarks

This statement is allowed in stored procedures, triggers, and at the session level.

Note
Even though positioned DELETE is allowed at the session level, the DECLARE CURSOR statement is not. Use the SQLGetCursorName() API to obtain the cursor name of the active result set.

Examples

The following sequence of statements provide the setting for the positioned DELETE statement. The required statements for the positioned DELETE statement are DECLARE CURSOR, OPEN CURSOR, and FETCH FROM cursorname.

The Modern European History class has been dropped from the schedule, so this example deletes the row for Modern European History (HIS 305) from the Course table in the sample database:

CREATE PROCEDURE DropClass();

DECLARE  :CourseName CHAR(7);
DECLARE  c1 cursor
FOR SELECT name
FROM course
WHERE name = :CourseName;

BEGIN

SET  :CourseName  = 'HIS 305';
OPEN c1;
FETCH NEXT FROM c1 INTO :CourseName;
DELETE WHERE CURRENT OF c1;

END;

See Also

CREATE PROCEDURE

CREATE TRIGGER

Prev
DECLARE CURSOR 		Up
Check for Revisions 		Next
DELETE


DELETE

This statement deletes specified rows from a database table.

Syntax

DELETE FROM table-name [ alias-name ]
[ WHERE search-condition ]

Remarks

INSERT, UPDATE, and DELETE statements behave in an atomic manner. That is, if an insert, update, or delete of more than one row fails, then all insertions, updates, or deletes of previous rows by the same statement are rolled back.

Examples

The following statements deletes the row for first name Ellen from the person table in the sample database.

DELETE FROM person where First_Name = 'Ellen'

The following statement deletes the row for Modern European History (HIS 305) from the course table in the sample database:

DELETE FROM Course WHERE Name = 'HIS 305'
Prev
DELETE (positioned) 		Up
Check for Revisions 		Next
DISTINCT


DISTINCT

Remarks

Include the DISTINCT keyword in your SELECT statement to direct Pervasive.SQL to remove duplicate values from the result. By using DISTINCT, you can retrieve all unique rows that match the SELECT statement's specifications.

The following rules apply to using the DISTINCT keyword:

You can use DISTINCT in any statement that includes subqueries.

The DISTINCT keyword is ignored if the selection list contains an aggregate; the aggregate guarantees that no duplicate rows result.

The following usage of DISTINCT is not allowed:

SELECT DISTINCT column1, DISTINCT column2

Examples

The following statement retrieves all the unique courses taught by Professor Beir (who has a Faculty ID of 111191115):

SELECT DISTINCT c.Name

FROM Course c, class cl

WHERE c.name = cl.name AND cl.faculty_id = '111191115'

See Also

SELECT

Prev
DELETE 		Up
Check for Revisions 		Next
DROP GROUP


DROP GROUP

This statement drops one or more groups in a secured database.

Syntax

DROP GROUP  group-name [ , group-name ]...

Remarks

Separate multiple group names with a comma.

Examples

The following example drops the group pervasive.

DROP GROUP pervasive

The following example uses a list to drop groups.

DROP GROUP pervasive_dev, pervasive_marketing

See Also

CREATE GROUP

Prev
DISTINCT 		Up
Check for Revisions 		Next
DROP INDEX


DROP INDEX

This statement drops a specific index from a designated table.

Syntax

DROP INDEX [ table-name . ] index-name [ IN DICTIONARY ]

Remarks

The IN DICTIONARY keyword allows you to drop an index from a DDF without removing the index from the underlying data file.

IN DICTIONARY is a very powerful and advanced feature. It should only be used by system administrators or when absolutely necessary. Normally, the SRDE keeps DDFs and data files perfectly synchronized, but this feature allows users the flexibility to force out-of-sync table dictionary definitions to match an existing data file. This can be useful when you want to create a definition in the dictionary to match an existing data file.

Caution
Modifying a DDF without performing parallel modifications to the underlying data file can cause serious problems.

For more information on this feature, see the discussion under IN DICTIONARY .

Examples

The following statement drops the named index from the Faculty table.

DROP INDEX Faculty.Dept

The following examples create a "detached" table, one with no associated data file, then add and drop an index from the table definition. The index is a detached index because there is no underlying Btrieve index associated with it.

CREATE TABLE t1 IN DICTIONARY (c1 int, c2 int)
CREATE INDEX idx_1 IN DICTIONARY on t1(c1)
DROP INDEX t1.idx_1 IN DICTIONARY

See Also

CREATE INDEX

Prev
DROP GROUP 		Up
Check for Revisions 		Next
DROP PROCEDURE


DROP PROCEDURE

This statement removes one or more stored procedures from the current database.

Syntax

DROP PROCEDURE procedure-name

Examples

The following statement drops the stored procedure myproc from the dictionary:

DROP PROCEDURE myproc

See Also

CREATE PROCEDURE

Prev
DROP INDEX 		Up
Check for Revisions 		Next
DROP TABLE


DROP TABLE

This statement removes a table from a designated database.

Syntax

DROP TABLE table-name [ IN DICTIONARY ]

Remarks

CASCADE and RESTRICT are not supported.

If any triggers depend on the table, the table is not dropped.

If a transaction is in progress and refers to the table, then an error is signalled and the table is not dropped.

The drop of table fails if other tables depend on the table to be dropped.

If a primary key exists, it is dropped. The user need not drop the primary key before dropping the table. If the primary key of the table is referenced by a constraint belonging to another table, then the table is not dropped and an error is signalled.

If the table has any foreign keys, then those foreign keys are dropped.

If the table has any other constraints (for example, NOT NULL, CHECK, UNIQUE, or NOT MODIFIABLE), then those constraints are dropped when the table is dropped.

IN DICTIONARY

See the discussion of IN DICTIONARY forALTER TABLE .

Examples

The following statement drops the class table definition from the dictionary.

DROP TABLE Class

See Also

ALTER TABLE

CREATE TABLE

Prev
DROP PROCEDURE 		Up
Check for Revisions 		Next
DROP TRIGGER


DROP TRIGGER

This statement removes a trigger from the current database.

Syntax

DROP TRIGGER trigger-name

Examples

The following example drops the trigger named InsTrig.

DROP TRIGGER InsTrig

See Also

CREATE TRIGGER

Prev
DROP TABLE 		Up
Check for Revisions 		Next
DROP VIEW


DROP VIEW

This statement removes a specified view from the database.

Syntax

DROP VIEW view-name

Remarks

[CASCADE | RESTRICT] is not supported.

Examples

The following statement drops the vw_person view definition from the dictionary.

DROP VIEW vw_person

See Also

CREATE VIEW

Prev
DROP TRIGGER 		Up
Check for Revisions 		Next
END


END

Remarks

See the discussion for BEGIN [ATOMIC] .

Prev
DROP VIEW 		Up
Check for Revisions 		Next
EXISTS


EXISTS

Remarks

Use the EXISTS keyword to test whether rows exist in the result of the subquery. For every row the outer query evaluates, Pervasive.SQL V8 tests for the existence of a related row from the subquery. Pervasive.SQL V8 includes in the statement's result table each row from the outer query that corresponds to a related row from the subquery.

In most cases, a subquery with EXISTS can be rewritten to use IN. The SRDE can process the query more efficiently if the query uses IN.

Examples

The following statement returns a list containing only persons who have a 4.0 grade point average:

SELECT * FROM Person p WHERE EXISTS

(SELECT * FROM Enrolls e WHERE e.Student_ID = p.id
AND Grade = 4.0)

This statement can be rewritten to use IN:

SELECT * FROM Person p WHERE p.id IN

(SELECT e.Student_ID FROM Enrolls WHERE Grade = 4.0)

See Also

SELECT

Prev
END 		Up
Check for Revisions 		Next
FETCH


FETCH

Remarks

A FETCH statement positions an SQL cursor on a specified row of a table and retrieves values from that row by placing them into the variables in the target list.

Examples

The FETCH statement in this example retrieves values from cursor c1 into the CourseName variable. The Positioned UPDATE statement in this example updates the row for Modern European History (HIS 305) in the Course table in the DEMODATA sample database:

CREATE PROCEDURE UpdateClass();
BEGIN

DECLARE :CourseName CHAR(7);
DECLARE :OldName CHAR(7);
DECLARE  c1 cursor FOR SELECT name FROM course WHERE name = :CourseName FOR UPDATE;
SET :CourseName = 'HIS 305';
OPEN c1;
FETCH NEXT FROM c1 INTO :OldName;
UPDATE SET name = 'HIS 306' WHERE CURRENT OF c1;

END;

See Also

CREATE PROCEDURE

Prev
EXISTS 		Up
Check for Revisions 		Next
FOREIGN KEY


FOREIGN KEY

Remarks

Include the FOREIGN KEY keywords in the ADD clause to add a foreign key to a table definition.

If you add a foreign key to a table that already contains data, use the Pervasive Control Center utility to find any data that does not conform to the new referential constraint. See the Pervasive.SQL User's Guide for information about this utility.

Note
You must be logged in to the database using a database name before you can add a foreign key or conduct any other referential integrity (RI) operation. Also, when security is enabled, you must have the Reference right on the table to which the foreign key refers before you can add the key.

Include a FOREIGN KEY clause in your CREATE TABLE statement to define a foreign key on a dependent table. In addition to specifying a list of columns for the key, you can define a name for the key.

The columns in the foreign key column may be nullable; however, you should ensure that pseudo-null columns do not exist in a MicroKernel index that does not index pseudo-null values.

The foreign key name must be unique in the dictionary. If you omit the foreign key name, Pervasive.SQL uses the name of the first column in the key as the foreign key name. This can cause a duplicate foreign key name error if your dictionary already contains a foreign key with that name.

When you specify a foreign key, Pervasive.SQL creates an index on the columns that make up the key. This index has the same attributes as the index on the corresponding primary key except that it allows duplicate values. To assign other attributes to the index, create it explicitly using a CREATE INDEX statement. Then, define the foreign key with an ALTER TABLE statement. When you create the index, ensure that it does not allow null values and that its case and collating sequence attributes match those of the index on the corresponding primary key column.

The columns in a foreign key must be the same data types and lengths and in the same order as the referenced columns in the primary key. The only exception is that you can use an integer column in the foreign key to refer to an IDENTITY or SMALLIDENTITY column in the primary key. In this case, the two columns must be the same length.

Pervasive.SQL checks for anomalies in the foreign keys before it creates the table. If it finds conditions that violate previously defined referential integrity (RI) constraints, it generates a status code and does not create the table.

When you define a foreign key, you must include a REFERENCES clause indicating the name of the table that contains the corresponding primary key. The primary key in the parent table must already be defined. In addition, if security is enabled on the database, you must have the Reference right on the table that contains the primary key.

You cannot create a self-referencing foreign key with the CREATE TABLE statement. Use an ALTER TABLE statement to create a foreign key that references the primary key in the same table.

Also, you cannot create a primary key and a foreign key on the same set of columns in a single statement. Therefore, if the primary key of the table you are creating is also a foreign key on another table, you must use an ALTER TABLE statement to create the foreign key.

Examples

The following statement adds a new foreign key to the Class table. (The Faculty column is defined as an index that does not include null values.)

ALTER TABLE Class ADD Constraint Teacher FOREIGN KEY (Faculty_ID)
REFERENCES Faculty ON DELETE RESTRICT

In this example, the restrict rule for deletions prevents someone from removing a faculty member from the database without first either changing or deleting all of that faculty's classes.

See Also

ALTER TABLE

CREATE TABLE

Prev
FETCH 		Up
Check for Revisions 		Next
GRANT


GRANT

This statement creates new user IDs and gives permissions to specific users in a secured database.

Syntax

GRANT CREATETAB TO public-or-user-or-group-name [ , user-or-group-name ]...

GRANT LOGIN TO user-password [ , user-password ]... [ IN GROUP group-name ]

GRANT table-privilege ON [ TABLE ] < * | table-name [ owner-name ]>
TO user-or-group-name [ , user-or-group-name ]...

table-privilege ::=	   ALL

|  SELECT [ ( column-name [ , column-name ]... ) ]
|  UPDATE [ ( column-name [ , column-name ]... ) ]
|  INSERT [ ( column-name [ , column-name ]... ) ]
|  DELETE
|  ALTER
|  REFERENCES

user-password ::= user-name [ : ] password

public-or-user-or-group-name ::= PUBLIC | user-or-group-name

user-or-group-name ::= user-name | group-name

user-name ::= user-defined-name

owner-name ::= user-defined-name

Remarks

CREATETAB and LOGIN arguments are extensions to the core SQL grammar.

Column-level rights are only valid for the SELECT, UPDATE, and INSERT privileges. If you attempt to set column level rights for the DELETE, ALTER or REFERENCES privileges, you will receive status code 8098.

Note
ANSI SQL 3 permits column lists for INSERT, ALTER, REFERENCES, SELECT and UPDATE.

Database-wide Permissions

User permissions can be assigned at the database level, by using the asterisk instead of a table name in a GRANT statement. This feature allows user privileges to be assigned to all tables in a given database.

When you grant database-wide privileges using *, the privileges apply to all tables in a given database except the system tables. Since privileges are additive, these database-level privileges take precedence over any table-level rights

These rights also apply to any tables that are created later by any user having the CREATE TABLE right. If the user creates a table, then full access rights to the table are granted to that user separate from the database rights. If later you revoke database privileges from that user, the user still has rights to the tables they created. The table rights must be revoked separately.

Only the Master user can perform GRANT commands. You cannot use the database-wide (*) syntax when specifying an owner name.

For more information on the default database and how to secure Btrieve files, see the following sections in Advanced Operations Guide:

  • Chapter 1, Pervasive.SQL Databases
  • Chapter 7, Pervasive.SQL Security
Users and Groups

Security is based on the existence of a default user named "Master" who has full access to the database when security is first turned on.

Caution
If you turn on security, be sure to specify a password with a significant length, at least five characters.

The Master user can create groups and other users and define sets of data access privileges for these groups and users.

If you want to grant the same level of access to all users and avoid having to set up individual groups and users, you can grant the desired level of access to PUBLIC. The default group PUBLIC is created when you secure a database and represents any user connecting with or without a password. Any right granted to PUBLIC is granted also to all valid users. No user can have access rights lower than that of PUBLIC.

Note
If you wish to use groups, you must set up the groups before creating users. You cannot add a user to a group after you have already created the user.

You can use the Users node in PCC to perform these tasks. You can also use GRANT and REVOKE statements to perform these tasks.

User name and password must be enclosed in double quotes if they contain spaces or other non-alphanumeric characters.

See Pervasive.SQL User's Guide and Chapter 7, Pervasive.SQL Security in Advanced Operations Guide for further information about users and groups.

Owner Name

An owner name is a password required to gain access to a Btrieve file. There is no relation between an owner name and any system user name or database user name. You should think of an owner name as a simple file password.

If you have a Btrieve owner name set on a file that is a table in a secure ODBC database, the Master user of the ODBC database must use the owner name in any GRANT statement to grant privileges on the given table to any user, including the Master user.

After the GRANT statement containing the owner name has been issued for a given user, that user can access the specified table by logging into the database, without specifying the owner name each time.

If a user tries to access a table through ODBC that has a Btrieve owner name, the access will not be allowed unless the Master user has granted privileges on the table to the user, with the correct owner name in the GRANT statement.

If a table has an owner name with the Read-Only attribute, the Master user automatically has SELECT rights on this table without specifically granting himself/herself the SELECT rights with the owner name.

Examples

A GRANT ALL statement grants the INSERT, UPDATE, ALTER, SELECT, DELETE and REFERENCES rights to the specified user or group. In addition, the user or group is granted the CREATE TABLE right for the dictionary.

The following statement grants all these privileges to dannyd for table Class.

GRANT ALL on Class to dannyd

This statement grants the ALTER privilege to user debieq.

GRANT ALTER on Class TO debieq

The following statement gives INSERT privileges to keithv and miked on table Class.

GRANT INSERT ON Class TO keithv, miked

The following statement grants INSERT privileges on two columns, First_name and Last_name, in the person table to users keithv and brendanb

GRANT INSERT(First_name,last_name) ON Person to keithv,brendanb

The following example grants CREATE TABLE rights to users aideenw and punitas

GRANT  CREATETAB TO aideenw, punitas

This next statement grants login rights to a user named ravi and specifies his password as "password." The semi-colon separating the username and password is required if the password does not start with a letter.

GRANT LOGIN TO ravi:password

The user name and password refer to Pervasive.SQL databases and are not related to user names and passwords set at level of the operating system. Pervasive.SQL user names, groups, and passwords are set through the Pervasive Control Center (PCC).

The following example grants login rights to users named dannyd and travisk and specifies their passwords as "password" and 1234567 respectively.

GRANT LOGIN TO dannyd:password,travisk:1234567

If there are spaces in a name you may use double quotes as in the following example. This statement grants login rights to user named Jerry Gentry and Punita and specifies their password as sun and moon respectively

GRANT LOGIN TO "Jerry Gentry" :sun, Punita:moon

The following example grants the login rights to a user named Jerry Gentry with password 123456 and a user named travisk with password abcdef. It also adds them to the group pervasive_dev

GRANT LOGIN TO "Jerry Gentry" :123456, travisk:abcdef in group pervasive_dev

To grant privileges on a table that has a Btrieve owner name, the Master user has to supply the correct owner name in the GRANT statement.

The following example grants the SELECT rights to the Master user on table T1 that has a Btrieve owner name of "abcd."

GRANT SELECT ON T1 'abcd' TO Master

The Master user has all rights on a table that does not have an owner name. You can set an owner name on a table with the Maintenance utility. The Btrieve owner name is case sensitive.

After the Master user performs the following set of SQL statements, the user "jsmith" has SELECT access to all tables in the current database. The user also has DELETE access to tab1 and UPDATE access to tab2.

GRANT DELETE ON tab1 TO jsmith
GRANT SELECT ON * TO jsmith
GRANT UPDATE ON tab2 TO jsmith

If the following statement is performed later by any user with CREATE TABLE privileges, the user "jsmith" will have SELECT access to the newly created table.

CREATE TABLE tab3 (col1 INT)

See Also

REVOKE

SET SECURITY

CREATE GROUP

DROP GROUP

Prev
FOREIGN KEY 		Up
Check for Revisions 		Next
GROUP BY


GROUP BY

Remarks

In addition to the GROUP BY syntax in a SELECT statement as specified in the Microsoft ODBC Programmer's Reference, the Pervasive.SQL Engine supports an extended GROUP BY syntax that can include vendor strings.

A GROUP BY query returns a result set which contains one row of the select list for every group encountered. (See the Microsoft ODBC Programmer's Reference for the syntax of a select list.)

Examples

The following example uses the course table to produce a list of unique departments:

SELECT Dept_Name from Course GROUP BY Dept_Name

In the next example, the result set contains a list of unique departments and the number of courses in each department:

SELECT Dept_Name, COUNT(*) FROM Course GROUP BY Dept_Name

Note that COUNT(expression) counts all non-NULL values for an expression across a predicate. COUNT(*) counts all values, including NULL values.

The rows operated on by the set function are those rows remaining after the WHERE search condition is applied. In this example, only those rows in the faculty table that have Salary > 80000 are counted:

SELECT COUNT(*) FROM Faculty WHERE Salary > 80000 GROUP BY Dept_Name

The following example shows an extended GROUP BY that includes vendor strings in an escape sequence. The Microsoft Web site provides additional information about escape sequences and vendor strings: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/odbcescape_sequences_in_odbc.asp.

SELECT(--(*vendor(Microsoft), product(ODBC) fn left(at1.col2, 1) *)--) FROM at1 GROUP BY (--(*vendor(Microsoft), product(ODBC) fn left(at1.col2, 1) *)--) ORDER BY (--(*vendor(Microsoft), product(ODBC) fn left(at1.col2, 1) *)--) DESC

See Also

SELECT

GRANT

REVOKE

Prev
GRANT 		Up
Check for Revisions 		Next
HAVING


HAVING

Remarks

Use a HAVING clause in conjunction with a GROUP BY clause within SELECT statements to limit your view to groups whose aggregate values meet specific criteria.

The expressions in a HAVING clause may contain constants, set functions or an exact replica of one of the expressions in the GROUP BY expression list.

The Pervasive.SQL Engine does not support HAVING without GROUP BY.

Examples

The following example returns department names where the count of course names is greater than 5.

SELECT Dept_Name, COUNT(*) FROM Course GROUP BY Dept_Name HAVING COUNT(*) > 5

Note that COUNT(expression) counts all non-NULL values for an expression across a predicate. COUNT(*) counts all values, including NULL values.

The next example returns department name that matches Accounting and has a number of courses greater than 5.

SELECT Dept_Name, COUNT(*) FROM Course GROUP BY Dept_Name HAVING COUNT(*)  > 5 AND Dept_Name = 'Accounting'

See Also

SELECT

Prev
GROUP BY 		Up
Check for Revisions 		Next
IF


IF

Remarks

IF statements provide conditional execution based on the value of a condition. The IF . . . THEN . . . [ELSE . . .] construct controls flow based on which of two statement blocks will be executed.

You may use IF statements in the body of both stored procedures and triggers.

There is no limit to the number of nested IF statements allowed, although the query remains subject to the usual total length limitation and other applicable limitations.

Examples

The following example uses the IF statement to set the variable Negative to either 1 or 0, depending on whether the value of vInteger is positive or negative.

IF (:vInteger < 0) THEN

SET :Negative = '1';
ELSE
SET :Negative = '0';
END IF;

The following example uses the IF statement to test the loop for a defined condition (SQLSTATE = '02000'). If it meets this condition, then the WHILE loop is terminated.

FETCH_LOOP:

WHILE (:counter < :NumRooms) DO

FETCH NEXT FROM cRooms into :CurrentCapacity;
IF (SQLSTATE = '02000') THEN
LEAVE FETCH_LOOP;
END IF;
SET :counter = :counter + 1;
SET :TotalCapacity = :TotalCapacity +
:CurrentCapacity;

END WHILE;

See Also

CREATE PROCEDURE

CREATE TRIGGER

Prev
HAVING 		Up
Check for Revisions 		Next
IN


IN

Remarks

Use the IN operator to test whether the result of the outer query is included in the result of the subquery. The result table for the statement includes only rows the outer query returns that have a related row from the subquery.

Examples

The following example lists the names of all students who have taken Chemistry 408:

SELECT p.First_Name + '  ' +  p.Last_Name

FROM Person p, Enrolls e

WHERE (p.id = e.student_id)
AND (e.class_id IN
(SELECT c.ID FROM Class c WHERE c.Name = 'CHE 408'))

Pervasive.SQL first evaluates the subquery to retrieve the ID for Chemistry 408 from the Class table. It then performs the outer query, restricting the results to only those students who have an entry in the Enrolls table for that course.

Often, you can perform IN queries more efficiently using either the EXISTS keyword or a simple join condition with a restriction clause. Unless the purpose of the query is to determine the existence of a value in a subset of the database, it is more efficient to use the simple join condition because Pervasive.SQL optimizes joins more efficiently than it does subqueries.

See Also

SELECT

Prev
IF 		Up
Check for Revisions 		Next
INSERT


INSERT

This statement inserts column values into one table.

Syntax

INSERT INTO table-name

[ ( column-name [ , column-name ]...) ]
insert-values

insert-values ::=	  values-clause

|  query-specification

values-clause ::= VALUES ( expression [ , expression ]... )
            | DEFAULT VALUES

Remarks

INSERT, UPDATE, and DELETE statements behave in an atomic manner. That is, if an insert, update, or delete of more than one row fails, then all insertions, updates, or deletes of previous rows by the same statement are rolled back.

All data types for data created prior to Pervasive.SQL 2000 (legacy data) report back as nullable. This means that you can INSERT NULL into any legacy column type without pseudo-NULL conversion. The following data types are treated as pseudo-NULL by default:
Date | Decimal | Money | Numeric
NumericSA | NumericSTS | Timestamp |

(Normally, when you convert a legacy column to pseudo-NULL, you lose one of the binary values, forfeiting it for use as a null value, so that you can query the column for NULL. These data types, however, because of their design, have a different, unique internal value for NULL in addition to their normal data range. With these data types, no binary values are lost if they are converted to NULL so there is no harm considering them as pseudo-NULL by default.)

The rest of the data types are considered "legacy nullable," meaning that NULL may be inserted into them. When values are queried, however, the non-NULL binary equivalent is returned. This same binary equivalent must be used in WHERE clauses to retrieve specific values.

The binary equivalents are:

  • 0 for Binary types
  • Empty string from string and BLOB types (legacy types LVAR and NOTE)

CURTIME, CURDATE and NOW variables

You may use the variables CURTIME, CURDATE and NOW in INSERT statements to insert the current local date, time and timestamp values.

CREATE TABLE table1 (col1 DATE)
INSERT INTO table1 VALUES (CURDATE)

In order to insert the current UTC time, date, or timestamp using CURRENT_TIME() or related functions, you must structure your SQL statement as shown:

CREATE TABLE table1 (col1 DATE)
INSERT INTO table1 SELECT CURRENT_DATE()

Using Default Values

The following table describes the interaction between default values and nullable columns:
Table 3-3 Default Values and Nullable Columns
Column Type | Default value used if no literal default value is defined for the column: | Default value if literal value is defined:
Nullable | NULL | As defined
Not NULL | Error—"No default value assigned for column." | As defined
Pre-v2000 Nullable | The pseudo-null value for the column. | As defined

If a statement attempts to insert an explicit NULL value into a NOT NULL column that has a default value defined, the statement fails with an error. The default value is not used in place of the attempted NULL insert.

For any column with a default value defined, the default value may be invoked in an INSERT statement by omitting the column from the insert column list or by using the keyword DEFAULT in place of the insert value.

If all the columns in a table are either nullable or have default values defined, you can insert a record containing all default values by specifying DEFAULT VALUES as the values-clause. If any column is not nullable and no default is defined, or if you want to specify a column list, you cannot use the DEFAULT VALUES clause.

Setting default values for BLOB, CLOB, or BINARY type column definitions is not currently supported.

Long Data

The maximum literal string supported by Pervasive.SQL is 15,000 bytes. You can insert data longer than this through several different methods. If you are using ODBC calls, you can use SQLPutData or bind the data using a '?' placeholder and use SQLBindParameter. Using only direct SQL statements, you can break the insert or update statement into multiple calls. Start with a statement such as this:

update table1 set longfield = '15000 characters' where restriction

Then issue the following statement to add more data:

update table1 set longfield = notefield + '15000 more characters' where restriction

Examples

The following statement adds data to the Course table by directly specifying the values in three VALUES clauses:

INSERT INTO Course(Name, Description, Credit_Hours, Dept_Name)

VALUES ('CHE 308', 'Organic Chemistry II', 4, 'Chemistry')

INSERT INTO Course(Name, Description, Credit_Hours, Dept_Name)

VALUES ('ENG 409', 'Creative Writing II', 3, 'English')

INSERT INTO Course(Name, Description, Credit_Hours, Dept_Name)

VALUES ('MAT 307', 'Probability II', 4, 'Mathematics')

The following INSERT statement uses a SELECT clause to retrieve from the Student table the ID numbers of students who have taken classes.

The statement then inserts the ID numbers into the Billing table.

INSERT INTO Billing (Student_ID)
SELECT ID
FROM Student
WHERE Cumulative_Hours > 0

The following example illustrates the use of the CURTIME, CURDATE and NOW variables to insert the current local date, time and timestamp values inside an INSERT statement.

CREATE TABLE Timetbl (c1 time, c2 date, c3 timestamp)
INSERT INTO Timetbl(c1, c2, c3) values(CURTIME, CURDATE, NOW)

The following examples demonstrate basic usage of default values with INSERT and UPDATE statements.

CREATE TABLE t1 (c1 INT DEFAULT 10, c2 CHAR(10) DEFAULT 'abc')
INSERT INTO t1 DEFAULT VALUES
INSERT INTO t1 (c2) VALUES (DEFAULT)
INSERT INTO t1 VALUES (100, DEFAULT)
INSERT INTO t1 VALUES (DEFAULT, 'bcd')
INSERT INTO t1 VALUES (DEFAULT, DEFAULT)

SELECT * FROM t1

c1 c2

---------- ----------

10 abc

10 abc

100 abc

10 bcd

10         abc

UPDATE t1 SET c1 = DEFAULT WHERE c1 = 100
UPDATE t1 SET c2 = DEFAULT WHERE c2 = 'bcd'
UPDATE t1 SET c1 = DEFAULT, c2 = DEFAULT

SELECT * FROM t1

c1 c2

---------- ----------

10 abc

10 abc

10 abc

10 abc

10         abc

Based on the CREATE TABLE statement immediately above, the following two INSERT statements are equivalent.

INSERT INTO t1 (c1,c2) VALUES (20,DEFAULT)
INSERT INTO t1 (c1) VALUES (20)

The following SQL code shows the use of DEFAULT with multiple UPDATE values.

CREATE TABLE t2 (c1 INT DEFAULT 10,
        c2 INT DEFAULT 20 NOT NULL,
        c3 INT DEFAULT 100 NOT NULL)
INSERT INTO t2 VALUES (1, 1, 1)
INSERT INTO t2 VALUES (2, 2, 2)

SELECT * FROM T2

c1 c2 c3

---------- ---------- ----------

1 1 1

2          2          2

UPDATE t2 SET c1 = DEFAULT,
        c2 = DEFAULT,
        c3 = DEFAULT
WHERE c2 = 2

SELECT * FROM T2

c1 c2 c3

---------- ---------- ----------

1 1 1

10         20         100
Incorrect Examples of DEFAULT

The following examples show possible error conditions because a column is defined as NOT NULL and with no default value defined:

CREATE TABLE t1 (c1 INT DEFAULT 10,
                c2 INT NOT NULL,
                c3 INT DEFAULT 100 NOT NULL)
INSERT INTO t1 DEFAULT VALUES
    -- Error: No default value assigned for column <c2>.
INSERT INTO t1 VALUES (DEFAULT, DEFAULT, 10)
    -- Error: No default value assigned for column <c2>.
INSERT INTO t1 (c1,c2,c3) VALUES (1, DEFAULT, DEFAULT)
    -- Error: No default value assigned for column <c2>.
INSERT INTO t1 (c1,c3) VALUES (1, 10)
    -- Error: Column <c2> not nullable.

The following example shows what occurs when you use INSERT for IDENTITY columns and columns with default values.

CREATE TABLE t (id IDENTITY, c1 INTEGER DEFAULT 100)
INSERT INTO t (id) VALUES (0)
INSERT INTO t VALUES (0,1)
INSERT INTO t VALUES (10,10)
INSERT INTO t VALUES (0,2)
INSERT INTO t (c1) VALUES (3)
SELECT * FROM t

The SELECT shows the table contains the following rows:

1, 100
2, 1
10, 10
11, 2
12, 3

The first row illustrates that if "0" is specified in the values clause for an IDENTITY column, then the value inserted is "1" if the table is empty.

The first row also illustrates that if no value is specified in the values clause for a column with a default value, then the specified default value is inserted.

The second row illustrates that if "0" is specified in the values clause for an IDENTITY column, then the value inserted is one greater than the largest value in the IDENTITY column.

The second row also illustrates that if a value is specified in the values clause for a column with a default value, then the specified value overrides the default value.

The third row illustrates that if a value other than "0" is specified in the values clause for an IDENTITY column, then that value is inserted. If a row already exists that contains the specified value for the IDENTITY column, then the message "The record has a key field containing a duplicate value(Btrieve Error 5)" is returned and the INSERT fails.

The fourth rows shows again that if "0" is specified in the values clause for an IDENTITY column, then the value inserted is one greater than the largest value in the IDENTITY column. This is true even if "gaps" exist between the values (that is, the absence of one or more rows with IDENTITY column values less than the largest value).

The fifth row illustrates that if no value is specified in the values clause for an IDENTITY column, then the value inserted is one greater than the largest value in the IDENTITY column.

See Also

CREATE TABLE

SELECT

Prev
IN 		Up
Check for Revisions 		Next
JOIN


JOIN

You can specify a single table or view, multiple tables, or a single view and multiple tables. When you specify more than one table, the tables are said to be joined.

Syntax

join-definition ::= table-reference INNER JOIN table-reference ON search-condition

| table-reference CROSS JOIN table-reference
| outer-join-definition

outer-join-definition ::= table-reference outer-join-type JOIN table-reference
ON search-condition

outer-join-type ::= LEFT [ OUTER ] | RIGHT [ OUTER ] | FULL [ OUTER ]

The following example illustrates a two-table outer join:

SELECT * FROM Person LEFT OUTER JOIN Faculty ON Person.ID = Faculty.ID

The following example shows an outer join embedded in a vendor string. The "OJ" can be either upper or lower case.

SELECT t1.deptno, ename FROM {OJ emp t2 LEFT OUTER JOIN dept t1 ON t2.deptno=t1.deptno}

The SRDE supports two-table outer joins as specified in the Microsoft ODBC Programmer's Reference.

In addition to simple two-table outer joins, the SRDE supports n-way nested outer joins.

The outer join may or may not be embedded in a vendor string. If a vendor string is used, SRDE strips it off and parses the actual outer join text.

LEFT OUTER

The Pervasive.SQL Engine has implemented LEFT OUTER JOIN using SQL92 (SQL2) as a model. The syntax is a subset of the entire SQL92 syntax which includes cross joins, right outer joins, full outer joins, and inner joins. The TableRefList below occurs after the FROM keyword in a SELECT statement and before any subsequent WHERE, HAVING, and other clauses. Note the recursive nature of TableRef and LeftOuterJoin—a TableRef can be a left outer join that can include TableRefs which, in turn, can be left outer joins and so forth.

TableRefList :

TableRef [, TableRefList]
| TableRef
| OuterJoinVendorString [, TableRefList]

TableRef :

TableName [CorrelationName]
| LeftOuterJoin
| ( LeftOuterJoin )

LeftOuterJoin :
TableRef LEFT OUTER JOIN TableRef ON SearchCond

The search condition (SearchCond) contains join conditions which in their usual form are LT.ColumnName = RT.ColumnName, where LT is left table, RT is right table, and ColumnName represents some column within a given domain. Each predicate in the search condition must contain some non-literal expression.

The implementation of left outer join goes beyond the syntax in the Microsoft ODBC Programmer's Reference.

Vendor Strings

The syntax in the previous section includes but goes beyond the ODBC syntax in the Microsoft ODBC Programmer's Reference. Furthermore, the vendor string escape sequence at the beginning and end of the left outer join does not change the core syntax of the outer join.

The Pervasive.SQL Engine accepts outer join syntax without the vendor strings. However, for applications that want to comply with ODBC across multiple databases, the vendor string construction should be used. Because ODBC vendor string outer joins do not support more than two tables, it may be necessary to use the syntax shown following Table 3-7 .

Examples

The following four tables are used in the examples in this section.

Table 3-4 Emp Table
FirstName | LastName | DeptID | EmpID
Franky | Avalon | D103 | E1
Gordon | Lightfoot | D102 | E2
Lawrence | Welk | D101 | E3
Bruce | Cockburn | D102 | E4

Table 3-5 Dept Table
DeptID | LocID | Name
D101 | L1 | TV
D102 | L2 | Folk

Table 3-6 Addr Table
EmpID | Street
E1 | 101 Mem Lane
E2 | 14 Young St.

Table 3-7 Loc Table
LocID | Name
L1 | PlanetX
L2 | PlanetY

The following example shows a simple two-way Left Outer Join:

SELECT * FROM Emp LEFT OUTER JOIN Dept ON Emp.DeptID = Dept.DeptID

This two-way outer join produces the following result set:

Table 3-8 Two-way Left Outer Join
Emp |  |  |  | Dept |  |
FirstName | LastName | DeptID | EmpID | DeptID | LocID | Name
Franky | Avalon | D103 | E1 | NULL | NULL | NULL
Gordon | Lightfoot | D102 | E2 | D102 | L2 | Folk
Lawrence | Welk | D101 | E3 | D101 | L1 | TV
Bruce | Cockburn | D102 | E4 | D102 | L2 | Folk

Notice the NULL entry for Franky Avalon in the table. That is because no DeptID of D103 was found in the Dept table. In a standard (INNER) join, Franky Avalon would have been dropped from the result set altogether.

Algorithm

The algorithm that the Pervasive.SQL Engine uses for the previous example is as follows:
taking the left table, traverse the right table, and for every case where the ON condition is TRUE for the current right table row, return a result set row composed of the appropriate right table row appended to the current left-table row.

If there is no right table row where the ON condition is TRUE, (it is FALSE for all right table rows given the current left table row), create a row instance of the right table with all column values NULL.

That result set, combined with the current left-table row for each row, is indexed in the returned result set. The algorithm is repeated for every left table row to build the complete result set. In the simple two-way left outer join shown previously, Emp is the left table and Dept is the right table.

Note
Although irrelevant to the algorithm, the appending of the left table to the right table assumes proper projection as specified in the select list of the query. This projection ranges from all columns (for example, SELECT * FROM . . .) to only one column in the result set (for example, SELECT FirstName FROM . . .).

With radiating left outer joins, all other tables are joined onto one central table. In the following example of a three-way radiating left outer join, Emp is the central table and all joins radiate from that table.

SELECT * FROM (Emp LEFT OUTER JOIN Dept ON Emp.DeptID = Dept.DeptID) LEFT OUTER JOIN Addr ON Emp.EmpID = Addr.EmpID

Table 3-9 Three-way Radiating Left Outer Join
Emp |  |  |  | Dept |  |  | Addr
First Name | Last Name | Dept ID | Emp ID | Dept ID | Loc ID | Name | Emp ID | Street
Franky | Avalon | D103 | E1 | NULL | NULL | NULL | E1 | 101 Mem Lane
Gordon | Lightfoot | D102 | E2 | D102 | L2 | Folk | E2 | 14 Young St
Lawrence | Welk | D101 | E3 | D101 | L1 | TV | NULL | NULL
Bruce | Cockburn | D102 | E4 | D101 | L1 | TV | NULL | NULL

In a chaining left outer join, one table is joined to another, and that table, in turn, is joined to another. The following example illustrates a three-way chaining left outer join:

SELECT * FROM (Emp LEFT OUTER JOIN Dept ON Emp.DeptID = Dept.DeptID) LEFT OUTER JOIN Loc ON Dept.LocID = Loc.LocID

Table 3-10 Three-way Chaining Left Outer Join
Emp |  |  |  | Dept |  |  | Loc |
First Name | Last Name | Dept ID | Emp ID | Dept ID | Loc ID | Name | Loc ID | Name
Franky | Avalon | D103 | E1 | NULL | NULL | NULL | NULL | NULL
Gordon | Lightfoot | D102 | E2 | D102 | L2 | Folk | L2 | PlanetY
Lawrence | Welk | D101 | E3 | D101 | L1 | TV | L1 | PlanetX
Bruce | Cockburn | D102 | E4 | D101 | L1 | TV | L1 | PlanetX

This join could also be expressed as:

SELECT * FROM Emp LEFT OUTER JOIN (Dept LEFT OUTER JOIN Loc ON Dept.LocID = Loc.LocID) ON Emp.DeptID = Dept.DeptID

We recommend the first syntax because it lends itself to both the radiating and chaining joins. This second syntax cannot be used for radiating joins because nested left outer join ON conditions cannot reference columns in tables outside their nesting. In other words, in the following query, the reference to Emp.EmpID is illegal:

SELECT * FROM Emp LEFT OUTER JOIN (Dept LEFT OUTER JOIN Addr ON Emp.EmpID = Addr.EmpID) ON Emp.DeptID = Dept.DeptID

The following example shows a three-way radiating left outer join, less optimized:

SELECT * FROM Emp E1 LEFT OUTER JOIN Dept ON E1.DeptID = Dept.DeptID, Emp E2 LEFT OUTER JOIN Addr ON E2.EmpID = Addr.EmpID WHERE E1.EmpID = E2.EmpID

Table 3-11 Three-way Radiating Left Outer Join, Less Optimized
Emp |  |  |  | Dept |  |  | Addr |
First Name | Last Name | Dept ID | Emp ID | Dept ID | Loc ID | Name | Emp ID | Street
Franky | Avalon | D103 | E1 | NULL | NULL | NULL | E1 | 101 Mem Lane
Gordon | Lightfoot | D102 | E2 | D102 | L2 | Folk | E2 | 14 Young St
Lawrence | Welk | D101 | E3 | D101 | L1 | TV | NULL | NULL
Bruce | Cockburn | D102 | E4 | D101 | L1 | TV | NULL | NULL

This query returns the same results as shown in Table 3-10, assuming there are no NULL values for EmpID in Emp and EmpID is a unique valued column. This query, however, is not optimized as well as the one show for Table 3-10 and can be much slower.

See Also

SELECT

Prev
INSERT 		Up
Check for Revisions 		Next
LEAVE


LEAVE

Remarks

A LEAVE statement continues execution by leaving a block or loop statement. You can use LEAVE statements in the body of a stored procedure or a trigger.

Examples

The following example increments the variable vInteger by 1 until it reaches a value of 11, when the loop is ended with a LEAVE statement.

TestLoop:
LOOP

IF (:vInteger > 10) THEN

LEAVE TestLoop;

END IF;
SET :vInteger = :vInteger + 1;

END LOOP;

See Also

IF

LOOP

Prev
JOIN 		Up
Check for Revisions 		Next
LIKE


LIKE

LIKE allows pattern matching within character-based column data.

Syntax

WHERE expr [ NOT ] LIKE value

Remarks

The value on the right side of a LIKE expression must be a simple string constant, the USER keyword, or (outside a stored procedure) a dynamic parameter supplied at runtime, indicated by a question mark "?". Dynamic parameters are not supported within SQL Data Manager, only in application code.

Use the percent sign "%" in the comparison value as many times as desired to match zero or more characters in the column values. Use the underscore "_" to match any one character. If you need to match one of these wildcard symbols as a literal character, use a backslash in front of the symbol to mark the symbol as a literal character.
Table 3-12 Special Characters
Character | Purpose
Percent sign "%" | Wildcard: matches zero or more characters.
Underscore "_" | Wildcard: matches any single character.
Backslash "\" | Flags the following wildcard character as a literal character, indicating that you want to match the actual wildcard character itself. To match a backslash, enter two backslashes. For example, to match "%", the pattern should specify "\%".
Two single quotes "''" | Two single quotes without a space in between them must be used to match a single quote that is part of the result string. For example, if a row in the database contains the value "Jim's house", you can match this pattern by specifying LIKE 'Jim''s house' in the WHERE clause. A double-quote (") is not a special character within the pattern string, and can be used like any letter or digit.

Examples

This example matches all column values that are five characters long and have abc as the middle three characters:

SELECT Building_Name FROM Room WHERE Building_Name LIKE '_abc_'

This example matches all column values that contain a backslash:

SELECT Building_Name FROM Room where Building_Name LIKE '%\\%'

This example matches all column values except those that begin with a percent sign:

SELECT Building_Name FROM Room where Building_Name NOT LIKE '\%%'

This example matches all column values that contain one or more single-quotes:

SELECT Building_Name FROM Room where Building_Name LIKE '%''%'

This example matches all column values where the second character is a double-quote:

SELECT Building_Name FROM Room where Building_Name LIKE '_"%'

This example creates a stored procedure that returns any rows where the Building_Name column contains the characters stored in the input variable :rname and where the Type column contains the characters stored in the input variable :rtype.

CREATE PROCEDURE room_test(IN :rname CHAR(20), IN :rtype CHAR(20))
RETURNS(Building_Name CHAR(25), "Type" CHAR(20));
BEGIN
DECLARE :like1 CHAR(25);
DECLARE :like2 CHAR(25);
SET :like1 = '%' + :rname + '%';
SET :like2 = '%' + :rtype + '%';
SELECT Building_Name, "Type" FROM Room WHERE Building_Name LIKE :like1 AND "Type" LIKE :like2;
END;

Note that the following statement, if placed in the stored procedure above, generates a syntax error because of the expression on the right side of the LIKE operator. The right side must be a simple constant:

Incorrect syntax

SELECT Building_Name, "Type" from Room where Building_Name LIKE '%' + :rname + '%';
Prev
LEAVE 		Up
Check for Revisions 		Next
LOOP


LOOP

Remarks

A LOOP statement repeats the execution of a block of statements.

This statement is only allowed in stored procedures and triggers.

Pervasive.SQL does not support post-conditional loops (REPEAT...UNTIL).

Examples

The following example increments the variable vInteger by 1 until it reaches a value of 11, when the loop is ended.

TestLoop:
LOOP

IF (:vInteger > 10) THEN

LEAVE TestLoop;

END IF;
SET :vInteger = :vInteger + 1;

END LOOP;

See Also

CREATE PROCEDURE

CREATE TRIGGER

IF

Prev
LIKE 		Up
Check for Revisions 		Next
NOT


NOT

Remarks

Using the NOT keyword with the EXISTS keyword allows you to test whether rows do not exist in the result of the subquery. For every row the outer query evaluates, Pervasive.SQL tests for the existence of a related row from the subquery. Pervasive.SQL excludes from the statement's result table each row from the outer query that corresponds to a related row from the subquery.

Including the NOT keyword along with the IN operator allows you to test whether the result of the outer query is not included in the result of the subquery. The result table for the statement includes only rows the outer query returns that do not have a related row from the subquery.

Examples

The following statement returns a list of students who are not enrolled in any classes:

SELECT * FROM Person p WHERE NOT EXISTS

(SELECT * FROM Student s WHERE s.id = p.id
AND Cumulative_Hours = 0)

This statement can be rewritten to include IN:

SELECT * FROM Person p WHERE p.id NOT IN

(SELECT s.id FROM Student s WHERE Cumulative_Hours = 0)

See Also

SELECT

Prev
LOOP 		Up
Check for Revisions 		Next
OPEN


OPEN

Remarks

The OPEN (cursor) statement opens a cursor. A cursor must be defined before it can be opened.

This statement is allowed only inside of a stored procedure or a trigger, since cursors and variables are only allowed inside of stored procedures and triggers.

Examples

The following example opens the declared cursor BTUCursor.

DECLARE BTUCursor CURSOR

FOR SELECT Degree, Residency, Cost_Per_Credit
FROM Tuition
ORDER BY ID;

OPEN BTUCursor;

See Also

CREATE PROCEDURE

CREATE TRIGGER

DECLARE CURSOR

Prev
NOT 		Up
Check for Revisions 		Next
PRIMARY KEY


PRIMARY KEY

Remarks

Include PRIMARY KEY in the ADD clause to add a primary key to a table definition. The primary key is a unique index that does not include null values. When you specify a primary key, Pervasive.SQL creates a unique index with the specified attributes on the defined group of columns.

Because a table can have only one primary key, you cannot add a primary key to a table that already has a primary key defined. To change the primary key of a table, delete the existing key using a DROP clause in an ALTER TABLE statement and add the new primary key.

Note
You must be logged in to the database using a database name before you can add a primary key or conduct any other referential integrity (RI) operation.

Include PRIMARY KEY in the ADD clause with the ALTER TABLE statement to add a primary key to a table definition.

Before adding the primary key, you must ensure that the columns in the primary key column list are defined as NOT NULL. A primary key is a unique index and can be created only on not nullable columns.

If a unique index on not nullable columns already exists, the ADD PRIMARY KEY does not create another unique index. Instead, the existing unique index is promoted to a primary key. For example, the following statements would promote the named index T1_C1C2 to be a primary key.

CREATE TABLE T1 (C1 INT NOT NULL, C2 CHAR(10) NOT NULL)
CREATE UNIQUE INDEX T1_C1C2 ON T1(C1,C2)
ALTER TABLE T1 ADD PRIMARY KEY(C1, C2)

If such a primary key is dropped, the primary key would be switched to a unique index.

ALTER TABLE T1 DROP PRIMARY KEY

If no unique index on not nullable columns exists in the table, ADD PRIMARY KEY creates a unique index on not nullable columns. DROP PRIMARY KEY completely deletes the unique index.

Include a PRIMARY KEY clause with the CREATE TABLE statement to add a primary key to a table definition.

To define referential constraints on your database, you must include a PRIMARY KEY clause to specify the primary key on the parent table. The primary key can consist of one column or multiple columns but can only be defined on columns that are not null. The columns you specify must also appear in the column Definitions list of the CREATE TABLE statement.

When you specify a primary key, Pervasive.SQL creates an index with the specified attributes on the defined group of columns. If the columns are not specifically defined as NOT NULL in the CREATE TABLE statement, the SRDE forces the columns to be not nullable. The SRDE also creates a unique index on the columns.

For example, the following two statements yield the same results:

CREATE TABLE T1 (C1 INT, C2 CHAR(10), PRIMARY KEY(C1,c2))
CREATE TABLE T1 (C1 INT NOT NULL, C2 CHAR(10) NOT NULL, PRIMARY KEY(C1,c2))

Examples

The following statement defines a primary key on a table called Faculty. (The ID column is defined as a unique index that does not include null values.)

ALTER TABLE Faculty ADD PRIMARY KEY (ID)

See Also

ALTER TABLE

CREATE TABLE

Prev
OPEN 		Up
Check for Revisions 		Next
PUBLIC


PUBLIC

Remarks

You can include the PUBLIC keyword in the FROM clause to revoke the Create Table right from all the users to whom the right was not explicitly assigned.

Include a FROM clause to specify the group or user from whom you are revoking rights. You can specify a single name or a list of names, or you can include the PUBLIC keyword to revoke access rights from all users whose rights are not explicitly assigned.

Examples

To assign access rights to all users in the dictionary, include the PUBLIC keyword to grant the rights to the PUBLIC group, as in the following example:

GRANT SELECT ON Course TO PUBLIC

This statement assigns the Select right on the Course table to all users defined in the dictionary. If you later revoke the Select right from the PUBLIC group, only users who are granted the Select right explicitly can access the table.

The following statement includes the PUBLIC keyword to grant the Create Table right to all the users defined in the dictionary:

GRANT CREATETAB TO PUBLIC

See Also

GRANT

REVOKE

Prev
PRIMARY KEY 		Up
Check for Revisions 		Next
PRINT


PRINT

Remarks

Use PRINT to print variable values or constants. The PRINT statement applies only to Windows-based platforms. It is ignored on other operating system platforms.

You can use PRINT only within stored procedures.

Examples

The following example prints the value of the variable :myvar.

PRINT( :myvar );

PRINT 'MYVAR = ' + :myvar;

The following example prints a text string followed by a numeric value. You must convert a number value to a text string to print the value.

PRINT 'Students enrolled in History 101: ' + convert(:int_val, SQL_CHAR);

See Also

CREATE PROCEDURE

Prev
PUBLIC 		Up
Check for Revisions 		Next
RELEASE SAVEPOINT


RELEASE SAVEPOINT

Use the RELEASE SAVEPOINT statement to delete a savepoint.

Syntax

RELEASE SAVEPOINT savepoint-name

savepoint-name ::= user-defined-name

Remarks

RELEASE, ROLLBACK, and SAVEPOINT and are supported at the session level (outside of stored procedures) only if AUTOCOMMIT is off. Otherwise, RELEASE, ROLLBACK, and SAVEPOINT must be used within a stored procedure.

Any committed statements within a stored procedure are controlled by the outermost transaction of the calling ODBC application. This means that, depending on the AUTOCOMMIT mode specified on SQLSetConnectOption, calling the stored procedure externally from an ODBC application performs one of two actions. It either commits automatically (AUTOCOMMIT on, the default) or waits for you to call SQLTransact with SQL_COMMIT or SQL_ROLLBACK (when AUTOCOMMIT is set to off).

Examples

The following example sets a SAVEPOINT then checks a condition to determine whether to ROLLBACK or to RELEASE the SAVEPOINT.

CREATE PROCEDURE Enroll_student( IN :student ubigint, IN :classnum INTEGER);
BEGIN

DECLARE  :CurrentEnrollment INTEGER;
DECLARE  :MaxEnrollment INTEGER;
SAVEPOINT SP1;
INSERT INTO Enrolls VALUES (:student, :classnum, 0.0);
SELECT COUNT(*)  INTO  :CurrentEnrollment FROM Enrolls WHERE class_id = :classnum;
SELECT  Max_size INTO  :MaxEnrollment FROM  Class WHERE  ID = :classnum;
IF :CurrentEnrollment >= :MaxEnrollment
THEN
ROLLBACK TO SAVEPOINT SP1;
ELSE
RELEASE SAVEPOINT SP1;
END IF;

END;

Note that COUNT(expression) counts all non-NULL values for an expression across a predicate. COUNT(*) counts all values, including NULL values.

See Also

CREATE PROCEDURE

ROLLBACK

SAVEPOINT

Prev
PRINT 		Up
Check for Revisions 		Next
RESTRICT


RESTRICT

Remarks

If you specify RESTRICT, Pervasive.SQL enforces the DELETE RESTRICT rule. A user cannot delete a row in the parent table if a foreign key value refers to it.

If you do not specify a delete rule, Pervasive.SQL applies the RESTRICT rule by default.

See Also

ALTER TABLE

Prev
RELEASE SAVEPOINT 		Up
Check for Revisions 		Next
REVOKE


REVOKE

REVOKE deletes user IDs and removes permissions to specific users in a secured database.

Syntax

REVOKE CREATETAB FROM public-or-user-group-name [ , public-or-user-group-name ]...

REVOKE LOGIN FROM user-name [ , user-name ]...

REVOKE table-privilege ON < * | table-name [ owner-name ]>

FROM user-or-group-name [ , user-or-group-name ]...

table-privilege ::=	   ALL

|  SELECT [ ( column-name [ , column-name ]... ) ]
|  UPDATE [ ( column-name [ , column-name ]... ) ]
|  INSERT [ ( column-name [ , column-name ]... ) ]
|  DELETE
|  ALTER
|  REFERENCES

public-or-user-group-name ::= PUBLIC | user-group-name

user-group-name ::= user-name | group-name

group-name ::= user-defined-name

user-name ::= user-defined-name

Examples

The following statement revokes all these privileges from dannyd for table Class.

REVOKE ALL on Class from 'dannyd'

The following statement revokes all privileges from dannyd and travisk for table Class.

REVOKE ALL on Class from 'dannyd', travisk

This statement revokes DELETE privileges from dannyd and travisk for table Class.

REVOKE DELETE on Class from dannyd, travisk

The following example revokes INSERT rights from keithv and miked for table Class.

REVOKE INSERT ON Class from keithv, miked

The following example revokes INSERT rights from keithv and brendanb for table Person and columns First_name and Last_name.

REVOKE INSERT(First_name,Last_name) ON Person from keithv, brendanb

The following statement revokes ALTER rights from dannyd from table Class.

REVOKE ALTER ON Class from dannyd

The following example revokes SELECT rights from dannyd and travisk on table Class.

REVOKE SELECT ON Class from dannyd, travisk

The following statement revokes SELECT rights from dannyd and travisk in table Person for columns First_name and Last_name.

REVOKE SELECT(First_name, Last_name) ON Person from dannyd, travisk

The following example revokes UPDATE rights from dannyd and travisk for table Person.

REVOKE UPDATE ON Person ON dannyd, travisk

See Also

GRANT

Prev
RESTRICT 		Up
Check for Revisions 		Next
ROLLBACK


ROLLBACK

ROLLBACK returns the database to the state it was in before the current transaction began. This statement releases the locks acquired since the last SAVEPOINT or START TRANSACTION.

Syntax

ROLLBACK [ WORK ] [ TO SAVEPOINT savepoint-name ]

Remarks

ROLLBACK, SAVEPOINT, and RELEASE are supported at the session level (outside of stored procedures) only if AUTOCOMMIT is off. Otherwise, ROLLBACK, SAVEPOINT, and RELEASE must be used within a stored procedure.

Any committed statements within a stored procedure are controlled by the outermost transaction of the calling ODBC application. This means that, depending on the AUTOCOMMIT mode specified on SQLSetConnectOption, calling the stored procedure externally from an ODBC application performs one of two actions. It either commits automatically (AUTOCOMMIT on, the default) or waits for you to call SQLTransact with SQL_COMMIT or SQL_ROLLBACK (when AUTOCOMMIT is set to off).

In the case of nested transactions, ROLLBACK rolls back to the nearest START TRANSACTION. For example, if transactions are nested five levels, then five ROLLBACK statements are needed to undo all of the transactions. A transaction is either committed or rolled back, but not both. That is, you cannot roll back a committed transaction.

Examples

The following statement undoes the changes made to the database since the beginning of a transaction.

ROLLBACK WORK

The following statement undoes the changes made to the database since the last savepoint.

ROLLBACK TO SAVEPOINT SP1

See Also

COMMIT

RELEASE SAVEPOINT

SAVEPOINT

Prev
REVOKE 		Up
Check for Revisions 		Next
SAVEPOINT


SAVEPOINT

SAVEPOINT defines a point in a transaction to which you can roll back.

Syntax

SAVEPOINT savepoint-name

savepoint-name ::= user-defined-name

Remarks

ROLLBACK, SAVEPOINT, and RELEASE are supported at the session level (outside of stored procedures) only if AUTOCOMMIT is off. Otherwise, ROLLBACK, SAVEPOINT, and RELEASE must be used within a stored procedure.

Any committed statements within a stored procedure are controlled by the outermost transaction of the calling ODBC application. This means that, depending on the AUTOCOMMIT mode specified on SQLSetConnectOption, calling the stored procedure externally from an ODBC application performs one of two actions. It either commits automatically (AUTOCOMMIT on, the default) or waits for you to call SQLTransact with SQL_COMMIT or SQL_ROLLBACK (when AUTOCOMMIT is set to off).

A SAVEPOINT applies only to the procedure in which it is defined. That is, you cannot reference a SAVEPOINT defined in another procedure.

Examples

The following example sets a SAVEPOINT then checks a condition to determine whether to ROLLBACK or to RELEASE the SAVEPOINT.

CREATE PROCEDURE Enroll_student( in :student ubigint, IN :classnum integer);
BEGIN

DECLARE  :CurrentEnrollment INTEGER;
DECLARE  :MaxEnrollment INTEGER;
SAVEPOINT SP1;
INSERT INTO Enrolls VALUES (:student, :classnum, 0.0);
SELECT COUNT(*)  INTO  :CurrentEnrollment FROM Enrolls WHERE class_id = :classnum;
SELECT  Max_size INTO  :MaxEnrollment FROM  Class WHERE  ID = :classnum;
IF :CurrentEnrollment >= :MaxEnrollment
THEN
ROLLBACK to SAVEPOINT SP1;
ELSE
RELEASE SAVEPOINT SP1;
END IF;

END;

Note that COUNT(expression) counts all non-NULL values for an expression across a predicate. COUNT(*) counts all values, including NULL values.

See Also

COMMIT

CREATE PROCEDURE

RELEASE SAVEPOINT

ROLLBACK

Prev
ROLLBACK 		Up
Check for Revisions 		Next
SELECT (with into)


SELECT (with into)

The SELECT (with INTO) statement allows you to select column values from a specified table to insert into variable names within stored procedures.

Syntax

SELECT [ ALL | DISTINCT ] select-list INTO variable-name [ , variable-name ]...
  FROM table-reference [ , table-reference ]... [ WHERE search-condition ]
  [ GROUP BY expression [ , expression ]...[ HAVING search-condition ] ]

Remarks

The SELECT with the INTO clause is only allowed within stored procedures.

Examples

The following example assigns into variables :x, :y the values of first_name an last_name in the Person table where first name is Bill.

SELECT  first_name, last_name INTO :x, :y from person where first_name = 'Bill'

See Also

CREATE PROCEDURE

Prev
SAVEPOINT 		Up
Check for Revisions 		Next
SELECT


SELECT

Retrieves specified information from a database. A SELECT statement creates a temporary view.

Syntax

query-specification [ [ UNION [ ALL ] query-specification ]...
[ ORDER BY order-by-expression [ , order-by-expression ]... ]

order-by-expression ::= expression [ CASE | COLLATE collation-name ] [ ASC | DESC ]

query-specification ::=	  ( query-specification )

| SELECT [ ALL | DISTINCT ] [ top-clause ] select-list
  FROM table-reference [ , table-reference ]...
  [ WHERE search-condition ]
  [ GROUP BY expression [ , expression ]...

[ HAVING search-condition ] ]

top-clause ::= TOP number

select-list ::= * | select-item [ , select-item ]...

select-item ::= expression [ [ AS ] alias-name ] | table-name . *

table-reference ::= { OJ outer-join-definition }

| [db-name.]table-name [ [ AS ] alias-name ]
| join-definition
| ( join-definition )

join-definition ::= table-reference INNER JOIN table-reference ON search-condition

| table-reference CROSS JOIN table-reference
| outer-join-definition

outer-join-definition ::= table-reference outer-join-type JOIN table-reference
ON search-condition

outer-join-type ::= LEFT [ OUTER ] | RIGHT [ OUTER ] | FULL [ OUTER ]

search-condition ::= search-condition AND search-condition

| search-condition OR search-condition
| NOT search-condition
| ( search-condition )
| predicate

predicate ::= expression [ NOT ] BETWEEN expression AND expression

| expression-or-subquery comparison-operator expression-or-subquery
| expression [ NOT ] IN ( query-specification )
| expression [ NOT ] IN ( value [ , value ]... )
| expression [ NOT ] LIKE value
| expression IS [ NOT ] NULL
| expression comparison-operator ANY ( query-specification )
| expression comparison-operator ALL ( query-specification )
| [ NOT ] EXISTS ( query-specification )

comparison-operator ::= < | > | <= | >= | = | <>

expression-or-subquery ::= expression | ( query-specification )

value ::= literal | USER | ?

expression ::= expression - expression

| expression + expression
| expression * expression
| expression / expression
| ( expression )
| -expression
| +expression
| column-name
| ?
| literal
| set-function
| scalar-function
| { fn scalar-function }
| USER
| IF ( search-condition , expression , expression )
| SQLSTATE
| : user-defined-name
|@:IDENTITY
|@:ROWCOUNT
|@@IDENTITY
|@@ROWCOUNT

set-function ::= COUNT (*)

| COUNT ( [ DISTINCT | ALL ] expression )
| SUM ( [ DISTINCT | ALL ] expression )
| AVG ( [ DISTINCT | ALL ] expression )
| MIN ( [ DISTINCT | ALL ] expression )
| MAX ( [ DISTINCT | ALL ] expression )

scalar-function ::= see Scalar Functions

Remarks

In addition to supporting a GROUP BY on a column-list, as specified in the Microsoft ODBC Programmer's Reference, SRDE has extended the syntax to support a GROUP BY on an expression-list or on any expression in a GROUP BY expression-list. See GROUP BY for more information on GROUP BY extensions. HAVING is not supported without GROUP BY.

Result sets and stored views generated by executing SELECT statements with any of the following characteristics are read-only (they cannot be updated). That is, a positioned UPDATE, a positioned DELETE and an SQLSetPos call to add, alter or delete data is not allowed on the result set or stored view if:

  • SQL_CONCUR_READ_ONLY was specified as the SQL_CONCURRENCY type via SQLSetStmtOption
  • The selection-list contains an aggregate:
    SELECT SUM(c1) FROM t1
  • The selection-list specifies DISTINCT:
    SELECT DISTINCT c1 FROM t1
  • The view contains a GROUP BY clause:
    SELECT SUM(c1), c2 FROM t1 GROUP BY c2
  • The view is a join (references multiple tables):
    SELECT * FROM t1, t2
  • The view uses the UNION operator and UNION ALL is not specified or all SELECT statements do not reference the same table:
    SELECT c1 FROM t1 UNION SELECT c1 FROM t1
    SELECT c1 FROM t1 UNION ALL SELECT c1 FROM t2
    • Note that stored views do not allow the UNION operator.
  • The view contains a subquery that references a table other than the table in the outer query:
    SELECT c1 FROM t1 WHERE c1 IN (SELECT c1 FROM t2)

Dynamic parameters (?) are not supported as SELECT items. You may use dynamic parameters in any SELECT statement if the dynamic parameter is part of the predicate. For example, SELECT * FROM faculty WHERE id = ? is valid because the dynamic parameter is part of the predicate.

Note that the SQLExecDirect statement does not support the use of dynamic parameters. For this reason, you cannot use SQL Data Manager to execute an SQL statement with a dynamic parameter in the predicate. (SQL Data Manager uses SQLExecDirect.)

You may use variables as SELECT items only within stored procedures. See CREATE PROCEDURE .

SUM and DECIMAL Precision

When using the SUM aggregate function to sum a field that is of type DECIMAL, the following rules apply:

The precision of the result is 74, while the scale is dependent on the column definition.

The result may cause an overflow error if a number with precision greater than 74 is calculated (a very large number indeed). If an overflow occurs, no value is returned, and SQLSTATE is set to 22003, indicating a numeric value is out of range.

Examples

This simple SELECT statement retrieves all the data from the Faculty table.

SELECT * FROM Faculty

This statement retrieves the data from the person and the faculty table where the id column in the person table is the same as the id column in the faculty table.

SELECT Person.id, Faculty.salary FROM Person, Faculty WHERE Person.id = Faculty.id

The following example retrieves student_id and sum of the amount_paid where it is greater than or equal to 100 from the billing table. It then groups the records by student_id.

SELECT Student_ID, SUM(Amount_Paid)

FROM Billing
GROUP BY Student_ID
HAVING SUM(Amount_Paid) >=100.00

If the expression is a positive integer literal, then that literal is interpreted as the number of the column in the result set and ordering is done on that column. No ordering is allowed on set functions or an expression that contains a set function.

Subqueries

The following types of subqueries are supported: comparison, quantified, in, exists, and correlated. ORDER BY clauses are not allowed in a subquery clause.

Correlated subquery predicates in the HAVING clause which contain references to grouped columns are not supported.

You may use a subquery on the left-hand side of an expression:

Expr-or-SubQuery CompareOp Expr-or-SubQuery

where Expr is an expression, and CompareOp is one of:
< | > | <= | >= | = | <>
LIKE | IN | NOT IN |  |  |

Examples

Left-hand subquery behavior has been optimized for IN, NOT IN, and =ANY in cases where the subquery is not correlated and any join condition is an outer join. Other conditions may not be optimized. Here is an example of a query that meets these conditions:

SELECT count(*) FROM person WHERE id IN
        (SELECT faculty_id FROM class)

A correlated subquery is a subquery that depends in part on a table or tables in the main query.

approximate-numeric-literal

Examples

SELECT * FROM results WHERE quotient =-4.5E-2

INSERT INTO results (quotient) VALUES (+5E7)

between-predicate

Remarks

The syntax expression1 BETWEEN expression2 and expression3 returns TRUE if expression1 >= expression2 and expression1<= expression3. FALSE is returned if expression1 >= expression3, or is expression1 <= expression2.

Expression2 and expression3 may be dynamic parameters (for example, SELECT * FROM emp WHERE emp_id BETWEEN ? AND ?)

Examples

The next example retrieves the first names from the person table whose ID fall between 10000 and 20000.

SELECT First_name FROM Person WHERE ID BETWEEN 10000 AND 20000

correlation-name

Remarks

Both table and column correlation names are supported.

Examples

The following example selects data from both the person table and the faculty table using the aliases T1 and T2 to differentiate between the two tables.

SELECT * FROM Person T1, Faculty T2 WHERE T1.id = T2.id

The correlation name for a table name can also be specified in using the FROM clause, as seen in the following example.

SELECT a.Name, b.Capacity FROM Class a, Room b

WHERE a.Room_Number = b.Number

exact-numeric-literal

Examples

SELECT car_num, price FROM cars WHERE car_num =49042 AND price=49999.99

in-predicate

Examples

This selects the records from table Person table where the first names are Bill and Roosevelt.

SELECT * FROM  Person WHERE  First_name IN ('Roosevelt', 'Bill')

set-function

Examples

The following example selects the minimum salary from the Faculty table.

SELECT MIN(salary) FROM Faculty

MIN(expression), MAX(expression), SUM(expression), AVG(expression), COUNT(*), and COUNT(expression) are supported.

COUNT(expression) counts all non-NULL values for an expression across a predicate. COUNT(*) counts all values, including NULL values.

The following example counts all the rows in q where a+b does not equal NULL.

SELECT COUNT(a+b) FROM q

date-literal

Remarks

Date constants may be expressed in SQL statements as a character string or embedded in a vendor string. SQL_CHAR and the vendor string representation are treated as a value of type SQL_DATE. This becomes important when conversions are attempted.

The SRDE partially supports extended SQL grammar, as outlined in this function.

Examples

The next two statements return all the classes whose start date is after 1995-06-05.

SELECT * FROM Class WHERE Start_Date > '1995-06-05'

SELECT * FROM Class WHERE Start_Date > {d '1995-06-05'}

The SRDE supports the following date literal format: 'YYYY-MM-DD'.

Dates may be in the range of year 0 to 9999.

time-literal

Examples

The following two statements retrieve records from the class table where the start time for the classes is 14:00:00.

SELECT * FROM Class WHERE Start_time = '14:00:00'

SELECT * FROM Class WHERE Start_time = {t '14:00:00'}

The SRDE supports the following time literal format: 'HH:MM:SS'.

Time constants may be expressed in SQL statements as a character string or embedded in a vendor string. Character string representation is treated as a string of type SQL_CHAR and the vendor string representation as a value of type SQL_TIME.

The SRDE partially supports extended SQL grammar, as outlined in this function.

timestamp-literal

Remarks

Timestamp constants may be expressed in SQL statements as a character string or embedded in a vendor string. The SRDE treats the character string representation as a string of type SQL_CHAR and the vendor string representation as a value of type SQL_TIMESTAMP. The SRDE partially supports extended SQL grammar, as outlined in this function.

Examples

The next two statements retrieve records from the Billing table where the start day and time for the log is 1996-03-28 at 17:40:49.

SELECT * FROM Billing WHERE log = '1996-03-28 17:40:49.0000000'
SELECT * FROM Billing WHERE log = {ts '1996-03-28 17:40:49.0000000'}

The SRDE supports the following time literal format: 'YYYY-MM-DD HH:MM:SS.SSSSSSS'. Note that the SRDE ignores the fractional seconds when processing Timestamp values, but the MKDE does not.

string-literal

Remarks

Literal strings are represented as single-quoted strings. If the string itself contains a single-quote or apostrophe, the character must be preceded by another single-quote.

Examples

SELECT * FROM t1 WHERE c1 = 'Roberta''s Restaurant'
SELECT STREET FROM address WHERE city LIKE 'San%'

date arithmetic

Examples

SELECT * FROM person P, Class C WHERE p.Date_Of_Birth < ' 1973-09-05' and c.Start_date >{d '1995-05-08'} + 30

The SRDE supports adding or subtracting an integer from a date where the integer is the number of days to add or subtract, and the date is embedded in a vendor string. (This is equivalent to executing a convert on the date).

The SRDE also supports subtracting one date from another to yield a number of days.

IF

Remarks

The IF system scalar function provides conditional execution based on the truth value of a condition

Examples

This expression prints the column header as "Prime1" and amount owed as 2000 where the value of the column amount_owed is 2000 or it prints a 0 if the value of the amount_owed column is not equal to 2000.

SELECT Student_ID, Amount_Owed,

IF (Amount_Owed = 2000, Amount_Owed, Convert(0, SQL_DECIMAL)) "Prime1"
FROM Billing

From table Class, the following example prints the value in the Section column if the section is equal to 001, else it prints "xxx" under column header Prime1

Under column header Prime2, it prints the value in the Section column if the value of the section column is equal to 002, or else it prints "yyy."

SELECT ID, Name, Section,

IF (Section = '001', Section, 'xxx') "Prime1",
IF (Section = '002', Section, 'yyy') "Prime2"

FROM Class

You can combine header Prime1 and header Prime2 by using nested IF functions. Under column header Prime, the following query prints the value of the Section column if the value of the Section column is equal to 001 or 002. Otherwise, it print "xxx."

SELECT ID, Name, Section,

IF (Section = '001', Section, IF(Section = '002', Section, 'xxx')) Prime

FROM Class

Multi-database Join

When needed, a database name may be prepended to an aliased table name in the FROM clause, to distinguish among tables from two or more different databases that are used in a join.

All of the specified databases must be serviced by the same database engine. They do not need to reside on the same physical volume. The current database may be secure or non-secure, but all other databases in the join must be non-secure. With regard to Referential Integrity, all RI keys must exist within the same database.

Literal database names are not permitted in the select-list or in the WHERE clause. If you wish to refer to specific columns in the select-list or in the WHERE clause, you must use an alias for each specified table. See examples.

Examples

Assume two separate databases, "accounting" and "customers," exist on the same server. You can join tables from the two databases using table aliases and SQL syntax similar to the following example:

SELECT ord.account, inf.account, ord.balance, inf.address
FROM accounting.orders ord, customers.info inf
WHERE ord.account = inf.account

In this example, the two separate databases are "acctdb" and "loandb." The table aliases are "a" and "b," respectively.

SELECT a.loan_number_a, b.account_no, a.current_bal, b.balance
FROM acctdb.ds500_acct_master b LEFT OUTER JOIN loandb.ml502_loan_master a ON (a.loan_number_a = b.loan_number)
WHERE a.current_bal <> (b.balance * -1)
ORDER BY a.loan_number_a

left outer join

Remarks

The following example shows how to access the "Person" and "Student" tables from the DEMODATA database to obtain the Last Name, First Initial of the First Name and GPA of students. With the LEFT OUTER JOIN, all rows in the "Person" table are fetched (the table to the left of LEFT OUTER JOIN). Since not all people have GPA's, some of the columns have NULL values for the results. This is how outer join works, returning non-matching rows from either table.

Examples

SELECT Last_Name,Left(First_Name,1) AS First_Initial,Cumulative_GPA AS GPA FROM "Person"

LEFT OUTER JOIN "Student" ON Person.ID=Student.ID
ORDER BY Cumulative_GPA DESC, Last_Name

Assume that you want to know everyone with perfectly rounded GPA's and have them all ordered by the length of their last name. Using the MOD statement and the LENGTH scalar function, you can achieve this by adding the following to the query:

WHERE MOD(Cumulative_GPA,1)=0 ORDER BY LENGTH(Last_Name)

right outer join

Remarks

The difference between LEFT and RIGHT OUTER JOIN is that all non matching rows show up for the table defined to the right of RIGHT OUTER JOIN. Change the query for LEFT OUTER JOIN to include a RIGHT OUTER JOIN instead. The difference is that the all non-matching rows from the right table, in this case "Student," show up even if no GPA is present. However, since all rows in the "Student" table have GPA's, all rows are fetched.

Examples

SELECT Last_Name,Left(First_Name,1) AS First_Initial,Cumulative_GPA AS GPA FROM "Person"

RIGHT OUTER JOIN "Student" ON Person.ID=Student.ID
ORDER BY Cumulative_GPA DESC, Last_Name

Cartesian join

Remarks

A Cartesian join is the matrix of all possible combinations of the rows from each of the tables. The number of rows in the Cartesian product equals the number of rows in the first table times the number of rows in the second table.

Examples

Assume you have the following tables in your database:

Table 3-13 Addr Table
EmpID | Street
E1 | 101 Mem Lane
E2 | 14 Young St.

Table 3-14 Loc Table
LocID | Name
L1 | PlanetX
L2 | PlanetY

The following performs a Cartesian JOIN on these tables:

SELECT * FROM Addr,Loc

This results in the following:

Table 3-15 SELECT Statement with Cartesian JOIN
EmpID | Street | LocID | Name
E1 | 101 Mem Lane | L1 | PlanetX
E1 | 101 Mem Lane | L2 | PlanetY
E2 | 14 Young St | L1 | PlanetX
E2 | 14 Young St | L2 | PlanetY

DISTINCT

Remarks

You can use DISTINCT with SUM, AVG, COUNT, MIN, and MAX (but it does not change results with MIN and MAX). DISTINCT eliminates duplicate values before calculating the sum, average or count.

Examples

Suppose you want to know the salaries for different departments including the minimum, maximum and salary, and you want to remove duplicate salaries. The following statement would do this, excluding the computer science department:

SELECT dept_name, MIN(salary), MAX(salary), AVG(DISTINCT salary) FROM faculty WHERE dept_name<>'computer science' GROUP BY dept_name

If you wanted to include duplicate salaries, you would use:

SELECT dept_name, MIN(salary), MAX(salary), AVG(salary) FROM faculty WHERE dept_name<>'computer science' GROUP BY dept_name

TOP

You may limit the number of rows returned by a single SELECT statement by using the keyword TOP in the statement and specifying a value for the maximum number of rows to return.

Remarks

The specified number of rows must be a literal positive number. It is defined as a 32-bit unsigned integer.

If a SELECT statement includes both TOP and an ORDER BY clause, the SRDE generates a temporary table and populates it with the query's entire result set. The rows in the temporary table are ordered as specified by the ORDER BY clause and the TOP number of rows in the ordered result set are returned.

Views that contain a TOP clause may not be joined with other tables or views.

The main difference between TOP and SET ROWCOUNT is that TOP affects only the current statement, while SET ROWCOUNT affects all subsequent statements issued during the current database session.

If both SET ROWCOUNT and TOP are applied to a given query, the query returns a number of rows equal to the lesser of the two values.

Cursor Types and TOP

A SELECT query with a TOP clause used in a cursor implicitly changes the cursor type under several circumstances. When using the table below, remember that any SELECT with ORDER BY on an unindexed column requires a temporary table. If the ORDER BY is on an indexed column, then a temporary table is not required.
Table 3-16 Effect of TOP Clause on Cursor Types
Original Cursor Type | Converted to This Type if SELECT query requires a temporary table: | Converted to This Type if SELECT query does not require a temporary table:
Dynamic | Forward-only | Static
Static | Forward-only | No change
Forward-only | No change | No change

Examples

SELECT TOP 10 * FROM person
        -- returns 10 rows
SET ROWCOUNT = 5;
SELECT TOP 10 * FROM person;
        -- returns 5 rows
SET ROWCOUNT = 12;
SELECT TOP 10 * FROM person ORDER BY id;
        -- returns the first 10 rows of the full list ordered by column id.

The following examples show a variety of behaviors when TOP is used in views, unions, or subqueries.

CREATE VIEW v1 (c1) AS SELECT TOP 10 id FROM person;
CREATE VIEW v2 (d1) AS SELECT TOP 5 c1 FROM v1;

SELECT * FROM v2  -- returns 5 rows
SELECT TOP 10 * FROM v2  -- returns 5 rows
SELECT TOP 0 * FROM v2  -- returns 0 rows
SELECT TOP 2 * FROM v2  -- returns 2 rows

SELECT TOP 10 id FROM person UNION SELECT TOP 13 faculty_id FROM class
         -- returns 14 rows

SELECT TOP 10 id FROM person UNION ALL SELECT TOP 13 faculty_id FROM class
         -- returns 23 rows

SELECT id FROM person WHERE id IN (SELECT TOP 10 faculty_id from class)
        -- returns 4 rows

SELECT id FROM person WHERE id >= any (SELECT TOP 10 faculty_id from class)
        -- returns 1493 rows
Incorrect Examples

The following SELECT statements generate error messages, because the view defined contains a TOP clause, and thus cannot be involved in a join.

CREATE VIEW v1 (c1) AS SELECT TOP 10 id FROM person

SELECT * FROM v1 INNER JOIN person ON v1.c1 = person.id
        -- returns an error message
SELECT * FROM person V v1 ON person.c1 = v1.id
        -- returns an error message
SELECT * FROM person, v1
        -- returns an error message

Example

SELECT USER FROM course
        -- returns 145 instances of Master (the table contains 145 rows)
SELECT DISTINCT USER FROM course
        -- returns 1 instance of Master

See Also

Global Variables

JOIN

SET ROWCOUNT

Prev
SELECT (with into) 		Up
Check for Revisions 		Next
SET DECIMALSEPARATORCOMMA


SET DECIMALSEPARATORCOMMA

The ODBC database engine by default displays decimal data using a period (.) as the separator between ones and tenths (for example, 100.95). The SET DECIMALSEPARATORCOMMA statement allows you to specify that results should be displayed using a comma to separate ones and tenths (for example, 100,95).

As with all SET statements, the effects of this statement apply to the remainder of the current database session, or until another SET DECIMALSEPARATORCOMMA statement is issued.

Syntax

SET DECIMALSEPARATORCOMMA=<ON|OFF>

Remarks

The default value is OFF, meaning that the period is used as the default decimal separator.

In locales where the comma is used as the decimal separator, decimal data can be entered using a comma or a period as the separator (literal values that use the comma as the separator must be enclosed in single quotes, for example: '123,43'). When the data is returned, however (as in the results of a SELECT statement), it is always displayed using a period unless SET DECIMALSEPARATORCOMMA=ON has been specified.

Likewise, if your database contains data that was entered using the period as the decimal separator, you can choose to specify the comma as the separator for output and display by using this statement.

This command affects output and display only. It has no effect on values being inserted, updated, or used in a comparison.

Examples

The following example shows how to insert period-delimited data and the effects of the SET DECIMALSEPARATORCOMMA statement on the SELECT results.

CREATE TABLE t1 (c1 real, c2 real)
INSERT INTO t1 VALUES (102.34, 95.234)
SELECT * FROM t1

    Results: 		c1      c2
            ------- -------
            102.34  95.234

SET DECIMALSEPARATORCOMMA=ON
SELECT * FROM t1

    Results:		c1      c2
            ------- -------
            102,34  95,234

The following example shows how to insert comma-delimited data, and the effects of the SET DECIMALSEPARATORCOMMA statement on the SELECT results.

Note
The comma can only be used as the separator character if the client and/or server operating system locale settings are set to a locale that uses the comma as the separator. For example, if you have U.S. locale settings on both your client and server, you will receive an error if you attempt to run this example.

CREATE TABLE t1 (c1 real, c2 real)
INSERT INTO t1 VALUES ('102,34', '95,234')
SELECT * FROM t1

    Results: 		c1      c2
            ------- -------
            102.34  95.234

SET DECIMALSEPARATORCOMMA=ON
SELECT * FROM t1
    Results:		c1      c2
            ------- -------
            102,34  95,234

See Also

Comma as Decimal Separator

OEM to ANSI Support

Prev
SELECT 		Up
Check for Revisions 		Next
SET OWNER


SET OWNER

The SET OWNER statement allows you to specify one or more Btrieve owner names for use during the current database connection.

Syntax

SET OWNER = [']ownername['] [,[']ownername[']] ...

Remarks

The SET OWNER statement allows you to specify a list of owner names for data files that have owner names assigned through the MicroKernel. The SRDE passes the owner names to the MicroKernel, enabling it to open the data files.

You can specify any number of owner names with a SET OWNER statement. If an owner name begins with a non-alphabetic character, you must enclose the name in single quotes (' ').

The statement is effective for the current connection only. If the current user logs out after issuing a SET OWNER command, the user must re-issue the command the next time the user logs in.

Each SET OWNER statement issued during the same connection re-sets the owner list; that is, only the owner names specified in the most recent SET OWNER statement are passed to the MicroKernel. You cannot "add" owner names to the owner list with subsequent statements.

In a database with security turned Off, this command has the effect of allowing full access to any data file (table) that has a Btrieve owner name the same as any name supplied in the SET OWNER list.

In a database with security turned On, this command has no effect for most users. It only has an effect when it is issued by the Master user. If the Master user has not granted himself permissions yet, issuing this command gives the master user full access to any data file with one of the specified Btrieve owner names. Also, if the Master user chooses to give himself permissions, he does not need to use the owner-name in the GRANT statement, if he has already performed the SET OWNER with the correct owner name for the table he is granting permissions on.

Examples

The following example specifies one owner name.

SET OWNER = '12jerry'

The following example specifies several owner names.

SET OWNER = jimbo, terry, maximus

The following example shows the effects of multiple SET OWNER statements within the same connection.

SET OWNER = jimbo, terry, maximus
    --owner list contains: jimbo,terry,maximus.
SET OWNER = fred, jennie, lucinda
    --owner list now contains: fred,jennie,lucinda. Previous contents are discarded.

The following example demonstrates the usage of SET OWNER for the Master user in a secure database. Assume that security has just been turned on, and no permissions have been granted in the database. The "inventory" data file has the owner name "admin".

To grant himself permissions, the Master user has two options. The Master user can issue a SET OWNER followed by a GRANT without an owner name, or the Master user can issue a GRANT that specifies the owner name. Both methods achieve the same result:

SET OWNER = admin
GRANT ALL ON inventory TO MASTER

or

GRANT ALL ON inventory admin TO MASTER

See Also

GRANT

REVOKE

Prev
SET DECIMALSEPARATORCOMMA 		Up
Check for Revisions 		Next
SET PASSWORD


SET PASSWORD

The SET PASSWORD statement allows the Master user to change the password for a secured database and a normal user to change his or her login password for a database.

Syntax

SET PASSWORD = password

password ::= user-defined-name

Remarks

SET PASSWORD may be issued at any time by either the Master user or a normal user. (SET SECURITY, in contrast, can be issued only by the Master user and the Master user's session must be the only current database connection.)

If issued by the Master user, SET PASSWORD changes the password for the entire database (all DDF files). If issued by a normal user, SET PASSWORD changes the login password for that user only in the user.ddf file.

The Master user or a normal user must be logged in to the database before SET PASSWORD can be issued. Note that "password" is not a reserved keyword. It may be used as a name for a table or column.

The minimum length allowed for a password is one character. The maximum length is eight characters. Only the following characters are valid for use in a password:

  • a through z (lowercase letters)
  • A through Z (uppercase letters)
  • 0 through 9
  • _ (underscore)
  • ^ (caret)
  • ~ (tilde)
  • $ (dollar sign)

A user-defined password is case sensitive. If the password begins with a non-alphabetic character, the password must be enclosed in single quotes.

Examples

The following example shows the Master user enabling security on the database with a password of "bigdog." The Master user then grants login authority to user "user45" with a password of "popcorn," and grants user45 SELECT permissions to table person. The Master user then changes the security password to "tinycat."

SET SECURITY = bigdog
    --this changes the password for all DDFs
GRANT LOGIN TO user45:popcorn
GRANT SELECT ON person TO user45
SET PASSWORD = tinycat
    --this changes the password for all DDFs

The following example assumes that user45 has logged in to the database with a password of "popcorn." User45 changes her password to "tomato." User45 then selects all records in the person table.

SET PASSWORD = tomato
    --this changes only user45's password in user.ddf
SELECT * FROM person
    --user45 still has SELECT rights on table person

See Also

SET SECURITY

Prev
SET OWNER 		Up
Check for Revisions 		Next
SET ROWCOUNT


SET ROWCOUNT

You may limit the number of rows returned by all subsequent SELECT statements within the current session by using the keyword SET ROWCOUNT.

The main difference between SET ROWCOUNT and TOP is that TOP affects only the current statement, while SET ROWCOUNT affects all statements issued during the current database session, until the next SET ROWCOUNT or until the session is terminated.

Syntax

SET ROWCOUNT = number

Remarks

If a SELECT statement subject to a SET ROWCOUNT condition contains an ORDER BY clause and an index cannot be used to optimize on the ORDER BY clause, the SRDE generates a temporary table. The temporary table is populated with the query's entire result set. The rows in the temporary table are ordered as specified by the ORDER BY clause and the ROWCOUNT number of rows in the ordered result set are returned.

You may turn off the ROWCOUNT feature by setting ROWCOUNT to zero:

SET ROWCOUNT = 0

SET ROWCOUNT is ignored when dynamic cursors are used.

If both SET ROWCOUNT and TOP are applied to a given query, the number of rows returned is the lower of the two values.

Examples

Also see the examples for TOP .

SET ROWCOUNT = 10;
SELECT * FROM person;
            -- returns 10 rows

See Also

TOP

Prev
SET PASSWORD 		Up
Check for Revisions 		Next
SET SECURITY


SET SECURITY

The SET SECURITY statement allows you to enable and disable security for the database to which you are currently logged in.

Syntax

SET SECURITY = password
SET SECURITY = NULL

Examples

The following example sets the password as 'mypasswd'.

SET SECURITY = mypasswd

The following example sets the password as 123456.

SET SECURITY = '123456'

The following example disables security.

SET SECURITY = NULL

Remarks

You must be logged in as Master to set security. You can then assign a password by using the SET SECURITY statement. There is no password required to log in as Master initially.

When using SET SECURITY, user name and password are case sensitive. If the password begins with a non-alphabetic character, the password must be enclosed in single quotes.

Only one Master user connection to the database is allowed to set security. You can set security from the Pervasive Control Center (PCC).

Note
The SET SECURITY statement cannot be executed within the SQL Data Manager. An error results if you try. For a database with no security, the SQL Data Manager locks the dictionary files, which prevents you from setting the password. For a secure database, the SQL Data Manager opens a second connection to the database files, which prevents you from disabling security.

The minimum length of a password is one character. The maximum length is eight characters. Only the following characters are valid for use in a password:

  • a through z (lowercase letters)
  • A through Z (uppercase letters)
  • 0 through 9
  • _ (underscore)
  • ^ (caret)
  • ~ (tilde)
  • $ (dollar sign)

A password is case sensitive. If the password begins with a non-alphabetic character, the password must be enclosed in single quotes.

See Also

GRANT

REVOKE

SET PASSWORD

Prev
SET ROWCOUNT 		Up
Check for Revisions 		Next
SET TIME ZONE


SET TIME ZONE

The SET TIME ZONE keyword allows you to specify a current time offset from Coordinated Universal Time (UTC) for your locale, overriding the operating system time zone setting where the database engine is located.

Any SET TIME ZONE statement remains in effect until the end of the current database session, or until another SET TIME ZONE statement is executed.

Caution
You should always use the default behavior unless you have a specific need to override the time zone setting in your operating system. If you are using Pervasive replication or your application has dependencies on the sequential time order of records inserted, use of SET TIME ZONE to modify your time zone offset is not recommended.

Syntax

SET TIME ZONE < displacement | LOCAL >
displacement ::= <+|->hh:mm

Valid range of hh is 00 - 12.

Valid range of mm is 00 - 59.

Either a plus (+) or a minus (-) sign is required as part of the displacement value.

Remarks

Default Behavior—SET TIME ZONE LOCAL is the default behavior, which is the same as not using the SET TIME ZONE command at all. Under the default behavior, the database engine establishes its time zone based on the operating system where it is running. For example, SELECT CURTIME ( ) returns the current local time, while SELECT CURRENT_TIME ( ) returns the current UTC time, both based on local system time and the time zone setting in the operating system.

The LOCAL keyword allows you to restore default behavior after specifying a displacement value, without having to terminate and re-open the database session.

Under default behavior, literal time and date values, such as '1996-03-28' and '17:40:46' are interpreted as current local time/date. In addition, during inserts, TIMESTAMP literal values are interpreted as specifying current local time. TIMESTAMP values are always adjusted and stored internally using UTC time, and converted to local time upon retrieval.
Table 3-17 Time/Date Functions with SET TIME ZONE Default
If no time zone is specified, or TIME ZONE LOCAL is specified ...
CURDATE(), CURTIME(), NOW() | These functions return current local time/date based on system clock.
CURRENT_DATE(), CURRENT_TIME(), CURRENT_TIMESTAMP() | These functions always return current UTC time/date based on system clock and operating system locale setting.

Behavior When Interval Specified—If a valid displacement value is specified, then that value is used instead of the operating system time zone offset when generating values for NOW ( ), CURTIME ( ), or CURDATE ( ). For example, if a displacement of -02:00 is specified, then the local time value of CURDATE() will be calculated by adding -02:00 to the UTC time returned from the operating system.

Under this behavior, time and date literals are interpreted as local time, at their face values. TIMESTAMP literals are interpreted as specifying a time such that if displacement is subtracted from it, the result is UTC. Daylight savings is not a consideration since displacement explicitly takes it into account. TIMESTAMP values are always stored internally using UTC time.
Table 3-18 Time/Date Functions with SET TIME ZONE Specified
If a valid displacement value is specified ...
CURDATE(), CURTIME(), NOW() | These functions return current local time/date values by adding displacement to the current UTC time/date values.
CURRENT_DATE(), CURRENT_TIME(), CURRENT_TIMESTAMP() | These functions always return current UTC time/date based on system clock and operating system locale setting.

To convert a given local time value to UTC, you must subtract your time zone displacement value from the local time value. In other words,

UTC time = local time - time zone displacement

See Table 3-19 for example conversions.
Table 3-19 Local to UTC Conversion Examples
Local Time | Displacement | UTC
10:10:15 Austin, Texas | US Central Standard Time -06:00 | 10:10:15-(-06:00)=16:10:15 UTC
16:10:15 London | Greenwich Mean Time +00:00 | 16:10:15-(+00:00)=16:10:15 UTC
22:10:15 Dhaka | +06:00 | 22:10:15-(+06:00)=16:10:15 UTC

A note about TIMESTAMP data type

Because TIMESTAMP data is always stored as UTC, and literal Timestamp values (including values stored on disk) are always converted to local time when retrieved, the behavior of NOW ( ) and CURRENT_TIMESTAMP( ) values can be confusing. For example, consider the following table, assuming the database engine is located in Central Standard Time, U.S.:
Table 3-20 Timestamp Data Type Examples
Statement | Value
SELECT NOW() | 2001-10-01 12:05:00.000000 displayed.
INSERT INTO t1 (c1) SELECT NOW() | 2001-10-01 18:05:00.000000 stored on disk.
SELECT * from t1 | 2001-10-01 12:05:00.000000 displayed.
SELECT CURRENT_TIMESTAMP() | 2001-10-01 18:05:00.000000 displayed.
INSERT INTO t2 (c1) SELECT CURRENT_TIMESTAMP() | 2001-10-01 18:05:00.000000 stored on disk.
SELECT * from t2 | 2001-10-01 12:05:00.000000 displayed.

It is important to note that the value displayed by a direct SELECT NOW ( ) is not the same as the value stored on disk by the syntax INSERT SELECT NOW ( ). Likewise, note that the display value of SELECT CURRENT_TIMESTAMP( ) is not the same value that you will see if you INSERT the value of CURRENT_TIMESTAMP() then SELECT it, because the literal value stored in the data file is adjusted when it is retrieved.

Examples

In this example, no SET TIME ZONE statement has been issued yet, and the computer on which the database engine is running has its clock set to January 9, 2002, 16:35:03 CST (U.S.). Recall that CURRENT_TIMESTAMP() and the other CURRENT_ functions always return UTC time and/or date based on the system clock and locale settings of the computer where the database engine is running.

SELECT CURRENT_TIMESTAMP(), NOW(),
        CURRENT_TIME(), CURTIME(),
        CURRENT_DATE(), CURDATE()

Results:

2002-01-09 22:35:03.0000000 2002-01-09 16:35:03.0000000
22:35:03 								16:35:03
2002-01-09 								2002-01-09

Note that CST is 6 hours ahead of UTC.

SET TIME ZONE -10:00

Now the same SELECT statement above returns the following:

2002-01-09 22:35:03.0000000 2002-01-09 12:35:03.0000000
22:35:03 								12:35:03
2002-01-09 								2002-01-09

Note that the value of NOW() changed after the SET TIME ZONE statement, but the value of CURRENT_TIMESTAMP() did not.

The following example demonstrates the difference between TIMESTAMP values that are stored as UTC values then converted to local values upon retrieval, and TIME or DATE values that are stored and retrieved at their face value. Assume that the system clock currently shows January 9, 2002, 16:35:03 CST (U.S.). Also assume that no SET TIME ZONE statement has been issued.

CREATE TABLE t1 (c1 TIMESTAMP, c2 TIMESTAMP, c3 TIME, c4 TIME, c5 DATE, c6 DATE)

INSERT INTO T1 SELECT CURRENT_TIMESTAMP(), NOW(), CURRENT_TIME(), CURTIME(), CURRENT_DATE(), CURDATE()

SELECT * FROM T1

Results:

c1                          c2
--------------------------- ---------------------------
2002-01-09 16:35:03.0000000 2002-01-09 16:35:03.0000000

c3       c4       c5         c6
-------- -------- ---------- ----------
22:35:03 16:35:03 2002-01-09 2002-01-09

Observe that NOW() and CURRENT_TIMESTAMP() have different values when displayed to the screen with SELECT NOW(), CURRENT_TIMESTAMP(), but once the literal values are saved to disk, UTC time is stored for both values. Upon retrieval, both values are converted to local time.

By setting the time zone interval to zero, we can view the actual data stored in the file, because it is adjusted by +00:00 upon retrieval:

SET TIME ZONE +00:00
SELECT * FROM T1

Results:

c1                          c2
--------------------------- ---------------------------
2002-01-09 22:35:03.0000000 2002-01-09 22:35:03.0000000

c3       c4       c5         c6
-------- -------- ---------- ----------

22:35:03 16:35:03 2002-01-09 2002-01-09

The following example demonstrates the expected behavior when the local date is different than the UTC date (for example, UTC is past midnight, but local time is not, or the reverse). Assume that the system clock currently shows January 9, 2002, 16:35:03 CST (U.S.).

SET TIME ZONE +10:00
SELECT CURRENT_TIMESTAMP(), NOW(),
        CURRENT_TIME(), CURTIME(),
        CURRENT_DATE(), CURDATE()

Results:

2002-01-09 22:35:03.0000000 2002-01-10 08:35:03.0000000
22:35:03 								08:35:03
2002-01-09 								2002-01-10
INSERT INTO T1 SELECT CURRENT_TIMESTAMP(), NOW(), CURRENT_TIME(), CURTIME(), CURRENT_DATE(), CURDATE()

SELECT * FROM T1

Results:

c1                          c2
--------------------------- ---------------------------
2002-01-10 08:35:03.0000000 2002-01-10 08:35:03.0000000

c3       c4       c5         c6
-------- -------- ---------- ----------
22:59:55 08:59:55 2002-01-09 2002-01-10

As you can see, the UTC time and date returned by CURRENT_DATE() and CURRENT_TIME() are stored as literal values. Since they are not TIMESTAMP values, no adjustment is made to these values when they are retrieved from the database.

See Also

TIMESTAMP data type

Time and Date Functions

Prev
SET SECURITY 		Up
Check for Revisions 		Next
SET TRUENULLCREATE


SET TRUENULLCREATE

The SET TRUENULLCREATE statement turns on or off true NULLs when you create new tables.

Syntax

SET TRUENULLCREATE = < on | off >

Remarks

Pervasive.SQL V8 allows you to set the default format for creation of tables with regard to NULL support. Normally, the product creates new tables using the true NULL data record format, which adds a NULL indicator byte to the beginning of every field. By turning off this engine setting using an SQL statement, you can create new tables that use the legacy NULL data record format that was used in Pervasive.SQL 7.

The creation mode remains in effect until it is changed by issuing the statement again, or until the connection is disconnected. Because this setting is maintained on a per-connection basis, separate database connections can maintain different creation modes, even within the same application. Every connection starts with the setting in default mode, where new tables are created with true NULL support.

This feature does not affect existing tables or available column data types. All tables are created using Pervasive.SQL data types. For example, old data types such as NOTE or LVAR are not available for use regardless of which type of NULL support is selected.

Also see the discussion about nullable data types under INSERT .

This setting can only be toggled using SQL, it cannot be set using the Pervasive Control Center (PCC).

Examples

To toggle the setting and specify that new tables should be created with legacy NULL support, use this SQL statement:

SET TRUENULLCREATE=OFF

To toggle the setting and return the engine to the default, which is table creation with true NULL support, use this SQL statement:

SET TRUENULLCREATE=ON

Prev
SET TIME ZONE 		Up
Check for Revisions 		Next
SET VARIABLE


SET VARIABLE

SET assigns a value to a declared variable.

Syntax

SET variable-name = proc-expr

Remarks

You must declare variables before you can set them. SET is allowed only in stored procedures and triggers.

Examples

The following examples assigns a value of 10 to var1.

SET :var1 = 10;

See Also

CREATE PROCEDURE

DECLARE

Prev
SET TRUENULLCREATE 		Up
Check for Revisions 		Next
SIGNAL


SIGNAL

Remarks

The SIGNAL statement allows you to signal an exception condition or a completion condition other than successful completion.

Signalling an SQLSTATE value causes SQLSTATE to be set to a specific value. This value is then returned to the user, or made available to the calling procedure (through the SQLSTATE value). This value is available to the application calling the procedure.

SIGNAL is available only inside a stored procedure.

Examples

The following example prints the initial SQLSTATE value "00000," then prints "SQLSTATE exception caught" after the signal is raised. The final SQLSTATE value printed is "W9001."
CREATE PROCEDURE GenerateSignal();
BEGIN

SIGNAL 'W9001';

END;

CREATE PROCEDURE TestSignal() WITH DEFAULT HANDLER;
BEGIN

PRINT SQLSTATE;
CALL GenerateSignal();
IF SQLSTATE <> '00000' THEN

PRINT 'SQLSTATE exception caught';

END IF;
PRINT SQLSTATE;

END;

See Also

CREATE PROCEDURE

Prev
SET VARIABLE 		Up
Check for Revisions 		Next
SQLSTATE


SQLSTATE

Remarks

The SQLSTATE value corresponds to a success, warning, or exception condition. The complete list of SQLSTATE values defined by ODBC can be found in the Microsoft ODBC SDK documentation.

When a handler executes, the statements within it affect the SQLSTATE value in the same way as statements in the main body of the compound statement. However, a handler that is intended to take specific action for a specific condition can optionally leave that condition unaffected, by re-assigning that condition at its conclusion. This does not cause the handler to be invoked again; that would cause a loop. Instead, Pervasive.SQL treats the exception condition as an unhandled exception condition, and execution stops.

See Also

CREATE PROCEDURE

SELECT

SIGNAL

Prev
SIGNAL 		Up
Check for Revisions 		Next
START TRANSACTION


START TRANSACTION

START TRANSACTION signals the start of a logical transaction. START TRANSACTION must always be paired with a COMMIT or a ROLLBACK.

Syntax

START TRANSACTION

Remarks

START TRANSACTION is supported only within stored procedures. You cannot use START TRANSACTION within the SQL Data Manager. (The SQL Data Manager sets AUTOCOMMIT to "on.")

This statement is not supported outside of a stored procedure because the ODBC standard specifies that every statement is by default inside a transaction. The ODBC standard does not have an API to start a transaction.

ODBC provides for the application to decide if each SQL statement is in its own transaction or if the application will specify when each transaction is completed. ODBC automatically opens a transaction prior to any statement that is not in a transaction. Thus, with the first statement of a given connection, or with the first statement after a COMMIT or ROLLBACK, ODBC automatically starts a new transaction.

Within the ODBC standard, SQLSetConnectOption is used to specify whether each statement is in its own transaction or the application groups statements within a transaction.

Each statement is in its own transaction if SQLSetConnectOption is called specifying the option SQL_AUTOCOMMIT and the value SQL_AUTOCOMMIT_ON (this is the default). This usage means that a transaction is started at the beginning of executing a statement and the transaction is either automatically committed, if no error occurs, or rolled back, if error occurred, upon completion of statement execution.

The application can group statements in a transaction if SQLSetConnectOption is called specifying the option SQL_AUTOCOMMIT and the value SQL_AUTOCOMMIT_OFF value. This usage means that a transaction is started at the beginning of the first statement executed. The application then decides when and how to end the transaction by calling SQLTransact or executing a 'COMMIT WORK' or 'ROLLBACK WORK' statement. When the application ends one transaction, another transaction is automatically started on execution of the next statement.

Examples

The following example, within a stored procedure, begins a transaction which updates the Amount_Owed column in the Billing table. This work is committed; another transaction updates the Amount_Paid column and sets it to zero. The final COMMIT WORK statement ends the second transaction.

Statements are delimited with a semi-colon inside stored procedures and triggers.

START TRANSACTION;
UPDATE Billing B

SET Amount_Owed = Amount_Owed - Amount_Paid
WHERE Student_ID IN

(SELECT DISTINCT E.Student_ID
FROM Enrolls E, Billing B
WHERE E.Student_ID = B.Student_ID);

COMMIT WORK;
START TRANSACTION;
UPDATE Billing B

SET Amount_Paid = 0
WHERE Student_ID IN

(SELECT DISTINCT E.Student_ID
FROM Enrolls E, Billing B
WHERE E.Student_ID = B.Student_ID);

COMMIT WORK;

See Also

COMMIT

CREATE PROCEDURE

ROLLBACK

Prev
SQLSTATE 		Up
Check for Revisions 		Next
UNION


UNION

Remarks

SELECT statements that use UNION or UNION ALL allow you to obtain a single result table from multiple SELECT queries. UNION queries are suitable for combining similar information contained in more than one data source.

UNION eliminates duplicate rows. UNION ALL preserves duplicate rows. Using the UNION ALL option is recommended unless you require duplicate rows to be removed.

With UNION, the Pervasive.SQL Engine orders the entire result set which, for large tables, can take several minutes. UNION ALL eliminates the need for the sort.

The Pervasive.SQL Engine does not support LONGVARBINARY columns in UNION statements. LONGVARCHAR is limited to 65500 bytes in UNION statements. The operator UNION cannot be applied to any SQL statement that references one or more views.

The two query specifications involved in a union must be compatible. Each query must have the same number of columns and the columns must be of compatible data types.

Examples

The following example lists the ID numbers of each student whose last name begins with 'M' or who has a 4.0 grade point average. The result table does not include duplicate rows.

SELECT Person.ID from Person WHERE Last_name LIKE 'M%'  UNION SELECT Student.ID FROM Student WHERE Cumulative_GPA = 4.0

The next example lists the column id in the person table and the faculty table including duplicate rows.

SELECT person.id FROM person UNION all SELECT faculty.id from faculty

The next example lists the ID numbers of each student whose last name begins with 'M' or who has a 4.0 grade point average. The result table does not include duplicate rows and orders the result set by the first column

SELECT Person.ID FROM Person WHERE Last_name LIKE  'M%'  UNION SELECT Student.ID FROM Student WHERE Cumulative_GPA = 4.0 order by 1

It is common to use the NULL scalar function to allow a UNION select list to have a different number of entries than the parent select list. To do this, you must use the CONVERT function to force the NULL to the correct type.

CREATE TABLE t1 (c1 INTEGER, c2 INTEGER)

INSERT INTO t1 VALUES (1,1)

CREATE TABLE t2 (c1 INTEGER)

INSERT INTO t2 VALUES (2)

select c1, c2 from t1
UNION SELECT c1, convert(null(),sql_integer)FROM t2

See Also

SELECT

Prev
START TRANSACTION 		Up
Check for Revisions 		Next
UNIQUE


UNIQUE

Remarks

To specify that the index not allow duplicate values, include the UNIQUE keyword. If the column or columns that make up the index contains duplicate values when you execute the CREATE INDEX statement with the UNIQUE keyword, Pervasive.SQL returns Status Code 5 and does not create the index.

Note
You should not include the UNIQUE keyword in the list of index attributes following the column name you specify; the preferred syntax is CREATE UNIQUE INDEX.

See Also

ALTER TABLE

CREATE INDEX

CREATE TABLE

Prev
UNION 		Up
Check for Revisions 		Next
UPDATE


UPDATE

The UPDATE statement allows you to modify column values in a database.

Syntax

UPDATE table-name [ alias-name ]

SET column-name = < DEFAULT | expression | subquery >
[ , column-name = < DEFAULT | expression | subquery >]...
[ WHERE search-condition ]

Remarks

INSERT, UPDATE, and DELETE statements behave in an atomic manner. That is, if an insert, update, or delete of more than one row fails, then all insertions, updates, or deletes of previous rows by the same statement are rolled back.

In the SET clause of an UPDATE statement, you may specify a sub-query. This feature allows you to update information in a table based on information in another table or another part of the same table.

You may specify the keyword DEFAULT to set the value to the default value defined for the given column. If no default value is defined, the SRDE uses NULL or pseudo-null value if the column is nullable, otherwise it returns an error. See also, further information on DEFAULT found in INSERT .

The UPDATE statement can update only a single table at a time. UPDATE can relate to other tables via a subquery in the SET clause. This can be a correlated subquery that depends in part on the contents of the table being updated, or it can be a non-correlated subquery that depends only on another table.

Correlated Subquery

UPDATE T1 SET T1.C2 = (SELECT T2.C2 FROM T2 WHERE T2.C1 = T1.C1)

Non-correlated Subquery

UPDATE T1 SET T1.C2 = (SELECT SUM(T2.C2) FROM T2 WHERE T2.C1 = 10)

The same logic is used to process pure SELECT statements and subqueries, so the subquery can consist of any valid SELECT statement. There are no special rules for subqueries.

If SELECT within an UPDATE returns no rows, then the UPDATE inserts NULL. If the given column(s) is/are not nullable, then the UPDATE fails. If select returns more than one row, then UPDATE fails.

An UPDATE statement does not allow the use of join tables in the statement. Instead, use a correlated subquery in the SET clause as follows:

UPDATE T1 SET T1.C2 = (SELECT T2.C2 FROM T2 WHERE T2.C1 = T1.C1)

All data types for data created prior to Pervasive.SQL 2000 (legacy data) report back as nullable. This means that you can UPDATE NULL into any legacy column type without pseudo-NULL conversion. The following data types are treated as pseudo-NULL by default:
Date | Decimal | Money | Numeric
NumericSA | NumericSTS | Timestamp |

(Normally, when you convert a legacy column to pseudo-NULL, you lose one of the binary values, forfeiting it so that you can query the column for NULL. These data types, however, because of their design, have a different, unique internal value for NULL in addition to their normal data range. With these data types, no binary values are lost if they are converted to NULL so there is no harm considering them as pseudo-NULL by default.)

The rest of the data types are considered "legacy nullable," meaning that NULL may be updated into them. When values are queried, however, the non-NULL binary equivalent is returned. This same binary equivalent must be used in WHERE clauses to retrieve specific values.

The binary equivalents are:

  • 0 for Binary types
  • Empty string from string and BLOB types (legacy types LVAR and NOTE)

Note
The maximum length for a single literal string is 15000 bytes. If you need to enter data longer than this, see Long Data for a useful tip.

Examples

The following examples updates the record in the faculty table and sets salary as 95000 for ID 103657107.

UPDATE Faculty SET salary = 95000.00 WHERE ID = 103657107

The following examples show how to use the DEFAULT keyword.

UPDATE t1 SET c2 = DEFAULT WHERE c2 = 'bcd'
UPDATE t1 SET c1 = DEFAULT, c2 = DEFAULT

The following example changes the credit hours for Economics 305 in the course table from 3 to 4.

UPDATE Course SET Credit_Hours = 4 WHERE Name = 'ECO 305'

The following example updates the address for a person in the Person table:

UPDATE Person p

SET p.Street = '123 Lamar',
p.zip = '78758',
p.phone = 5123334444
WHERE p.ID = 131542520

Subquery Example A

Two tables are created and rows are inserted. The first table, t5, is updated with a column value from the second table, t6, in each row where table t5 has the value 2 for column c1. Because there is more than one row in table t6 containing a value of 3 for column c2, the first UPDATE fails because more than one row is returned by the subquery. This result occurs even though the result value is the same in both cases. As shown in the second UPDATE, using the DISTINCT keyword in the subquery eliminates the duplicate results and allows the statement to succeed.

CREATE TABLE t5 (c1 INT, c2 INT)
CREATE TABLE t6 (c1 INT, c2 INT)
INSERT INTO t5(c1, c2) VALUES (1,3)
INSERT INTO t5(c1, c2) VALUES (2,4)

INSERT INTO t6(c1, c2) VALUES (2,3)
INSERT INTO t6(c1, c2) VALUES (1,2)
INSERT INTO t6(c1, c2) VALUES (3,3)
SELECT * FROM t5

Results:

c1         c2
---------- -----
1          3
2			 4

UPDATE t5 SET t5.c1=(SELECT c2 FROM t6 WHERE c2=3) WHERE t5.c1=2 — Note that the query fails

UPDATE t5 SET t5.c1=(SELECT DISTINCT c2 FROM t6 WHERE c2=3) WHERE t5.c1=2 — Note that the query succeeds
SELECT * FROM t5

Results:

c1         c2
---------- -----
1          3
3          4

Subquery Example B

Two tables are created and a variety of valid syntax examples are demonstrated. Note the cases where UPDATE fails because the subquery returns more than one row. Also note that UPDATE succeeds and NULL is inserted if the subquery returns no rows (where NULL values are allowed).

CREATE TABLE T1 (C1 INT, C2 INT)
CREATE TABLE T2 (C1 INT, C2 INT)

INSERT INTO T1 VALUES (1, 0)
INSERT INTO T1 VALUES (2, 0)
INSERT INTO T1 VALUES (3, 0)
INSERT INTO T2 VALUES (1, 100)
INSERT INTO T2 VALUES (2, 200)

UPDATE T1 SET T1.C2 = (SELECT SUM(T2.C2) FROM T2)
UPDATE T1 SET T1.C2 = 0
UPDATE T1 SET T1.C2 = (SELECT T2.C2 FROM T2 WHERE T2.C1 = T1.C1)

UPDATE T1 SET T1.C2 = @@IDENTITY
UPDATE T1 SET T1.C2 = @@ROWCOUNT
UPDATE T1 SET T1.C2 = (SELECT @@IDENTITY)
UPDATE T1 SET T1.C2 = (SELECT @@ROWCOUNT)

UPDATE T1 SET T1.C2 = (SELECT T2.C2 FROM T2) — update fails
INSERT INTO T2 VALUES (1, 150)
INSERT INTO T2 VALUES (2, 250)
UPDATE T1 SET T1.C2 = (SELECT T2.C2 FROM T2 WHERE T2.C1 = T1.C1) — update fails
UPDATE T1 SET T1.C2 = (SELECT T2.C2 FROM T2 WHERE T2.C1 = 5) — Note that the update succeeds, NULL is inserted for all rows of T1.C2
UPDATE T1 SET T1.C2 = (SELECT SUM(T2.C2) FROM T2 WHERE T2.C1 = T1.C1)

See Also

ALTER TABLE

CREATE PROCEDURE

CREATE TRIGGER

GRANT

INSERT

Prev
UNIQUE 		Up
Check for Revisions 		Next
UPDATE (positioned)


UPDATE (positioned)

The positioned UPDATE statement updates the current row of a rowset associated with an SQL cursor.

Syntax

UPDATE [ table-name ] SET column-name = proc-expr [ , column-name = proc-expr ]...
  WHERE CURRENT OF cursor-name

Remarks

This statement is allowed in stored procedures, triggers, and at the session level.

Note
Even though positioned UPDATE is allowed at the session level, the DECLARE CURSOR statement is not. Use the SQLGetCursorName() API to obtain the cursor name of the active result set.

The table-name may be specified in the positioned UPDATE statement only when used at the session level. Table-name cannot be specified with a stored procedure or trigger.

Examples

The following sequence of statements provide the setting for the positioned UPDATE statement. The required statements for a positioned UPDATE are DECLARE CURSOR, OPEN CURSOR, and FETCH FROM cursorname.

The positioned UPDATE statement in this example updates the name of the course HIS 305 to HIS 306.

CREATE PROCEDURE UpdateClass();
BEGIN

DECLARE  :CourseName CHAR(7);
DECLARE  :OldName CHAR(7);
DECLARE  c1 cursor FOR SELECT name FROM course WHERE name = :CourseName FOR UPDATE;
SET  :CourseName  = 'HIS 305';
OPEN c1;
FETCH NEXT FROM c1 INTO :OldName;
UPDATE SET name = 'HIS 306'   WHERE CURRENT OF c1;

END;

See Also

CREATE PROCEDURE

CREATE TRIGGER

Prev
UPDATE 		Up
Check for Revisions 		Next
USER


USER

Remarks

The USER keyword returns the current user name (such as Master) for each row returned by the SELECT restriction.

Example

The following examples return the user name from the course table.

SELECT USER FROM course
        -- returns 145 instances of Master (the table contains 145 rows)
SELECT DISTINCT USER FROM course
        -- returns 1 instance of Master

See Also

SELECT

SET SECURITY

Prev
UPDATE (positioned) 		Up
Check for Revisions 		Next
WHILE


WHILE

Use a WHILE statement is used to control flow. It allows code to be executed repeatedly as long as the WHILE condition is true.

Syntax

[ label-name : ] WHILE proc-search-condition
  DO [ proc-stmt [ ; proc-stmt ] ]... END WHILE [ label-name ]

Remarks

A WHILE statement can have a beginning label (the statement is referred to as a labeled WHILE statement).

Examples

The following example increments the variable vInteger by 1 until it reaches a value of 10, when the loop ends.

WHILE (:vInteger < 10) DO

SET :vInteger = vInteger + 1;

END WHILE

See Also

CREATE PROCEDURE

CREATE TRIGGER

Prev
USER 		Up
Check for Revisions 		Next
Grammar Element Definitions


Grammar Element Definitions

The following is an alphabetical summary of the element definitions used in the grammar syntax section:

alter-option-list ::=alter-option

|(alter-option [, alter-option}...)

alter-option ::= ADD [ COLUMN ] column-definition

| ADD table-constraint-definition
| DROP [ COLUMN ] column-name
| DROP CONSTRAINT constraint-name
| DROP PRIMARY KEY

as-or-semicolon ::= AS | ;

before-or-after ::= BEFORE | AFTER

call-arguments ::= positional-argument [ , positional-argument ]...

col-constraint ::= NOT NULL

| UNIQUE
| PRIMARY KEY
| REFERENCES table-name [ ( column-name ) ] [ referential-actions ]

collation-name ::= 'string' | user-defined-name

column-constraint ::= [ CONSTRAINT constraint-name ] col-constraint

column-definition ::= column-name data-type [ DEFAULT default-value ] [ column-constraint [ column-constraint ]... [CASE | COLLATE collation-name ]

column-name ::= user-defined-name

commit-statement ::= see COMMIT statement

comparison-operator ::= < | > | <= | >= | = | <>

constraint-name ::= user-defined-name

correlation-name ::= user-defined-name

cursor-name ::= user-defined-name

data-type ::= data-type-name [ (precision [ , scale ] ) ]

data-type-name ::= see Pervasive.SQL Supported Data Types

db-name ::= user-defined-name

default-value ::= literal

expression ::= expression - expression

| expression + expression
| expression * expression
| expression / expression
| ( expression )
| -expression
| +expression
| column-name
| ?
| literal
| set-function
| scalar-function
| { fn scalar-function }
| USER
| IF ( search-condition , expression , expression )
| SQLSTATE
| : user-defined-name
|@:IDENTITY
|@:ROWCOUNT
|@@IDENTITY
|@@ROWCOUNT

expression-or-subquery ::= expression | ( query-specification )

fetch-orientation ::= | NEXT

group-name ::= user-defined-name

index-definition ::= ( index-segment-definition [ , index-segment-definition ]... )

index-segment-definition ::= column-name [ ASC | DESC ]

index-name ::= user-defined-name

ins-upd-del ::= INSERT | UPDATE | DELETE

insert-values ::= values-clause

|  query-specification

join-definition ::= table-reference [ INNER ] JOIN table-reference ON search-condition

| table-reference CROSS JOIN table-reference
| outer-join-definition

label-name ::= user-defined-name

literal ::= 'string'

| number
| { d 'date-literal' }
| { t 'time-literal' }
| { ts 'timestamp-literal' }

order-by-expression ::= expression [ CASE | COLLATE collation-name ] [ ASC | DESC ]

outer-join-definition ::= table-reference outer-join-type JOIN table-reference
ON search-condition

outer-join-type ::= LEFT [ OUTER ]| RIGHT [ OUTER ] | FULL [ OUTER ]

parameter ::= parameter-type-name data-type [ DEFAULT proc-expr | = proc-expr ]

| SQLSTATE

parameter-type-name ::= parameter-name

| parameter-type parameter-name
| parameter-name parameter-type

parameter-type ::= IN | OUT | INOUT | IN_OUT

parameter-name ::= [ : ] user-defined-name

password ::= user-defined-name | 'string'

positional-argument ::= expression

precision ::= integer

predicate ::= expression [ NOT ] BETWEEN expression AND expression

| expression comparison-operator expression-or-subquery
| expression [ NOT ] IN ( query-specification )
| expression [ NOT ] IN ( value [ , value ]... )
| expression [ NOT ] LIKE value
| expression IS [ NOT ] NULL
| expression comparison-operator ANY ( query-specification )
| expression comparison-operator ALL ( query-specification )
| EXISTS ( query-specification )

proc-expr ::= same as normal expression but does not allow IF expression, or ODBC-style scalar functions

proc-search-condition ::= same as normal search-condition, but does not allow any expression that includes a subquery.

proc-stmt ::=	  [ label-name : ] BEGIN [ ATOMIC ] [ proc-stmt [ ; proc-stmt ]... ] END [ label-name ]

| CALL procedure-name ( proc-expr [ , proc-expr ]... )
| CLOSE cursor-name
| DECLARE cursor-name CURSOR FOR select-statement [ FOR UPDATE | FOR READ ONLY ]
| DECLARE variable-name data-type [ DEFAULT proc-expr | = proc-expr ]
| DELETE WHERE CURRENT OF cursor-name
| delete-statement
| FETCH [ fetch-orientation [ FROM ] ] cursor-name [ INTO variable-name [ , variable-name ] ]
| IF proc-search-condition THEN proc-stmt [ ; proc-stmt ]... [ ELSE proc-stmt [ ; proc-stmt ]... ] END IF
| insert-statement
| LEAVE label-name
| [ label-name : ] LOOP proc-stmt [ ; proc-stmt ]... END LOOP [ label-name ]
| OPEN cursor-name
| PRINT proc-expr [ , 'string' ]
| RETURN [ proc-expr ]
| transaction-statement
| select-statement-with-into
| select-statement
| SET variable-name = proc-expr
| SIGNAL [ ABORT ] sqlstate-value
| START TRANSACTION
| update-statement
| UPDATE SET column-name = proc-expr [ , column-name = proc-expr ]... WHERE CURRENT OF cursor-name
| [ label-name : ] WHILE proc-search-condition DO [ proc-stmt [ ; proc-stmt ] ]... END WHILE [ label-name ]
| alter-table-statement
| create-index-statement
| create-table-statement
| create-view-statement
| drop-index-statement
| drop-table-statement
| drop-view-statement
| grant-statement
| revoke-statement
| set-statement

procedure-name ::= user-defined-name

public-or-user-group-name ::= PUBLIC | user-group-name

query-specification [ [ UNION [ ALL ] query-specification ]...
[ ORDER BY order-by-expression [ , order-by-expression ]... ]

query-specification ::= ( query-specification )

| SELECT [ ALL | DISTINCT ] select-list [ table-expression ]

referencing-alias ::= OLD [ AS ] correlation-name [ NEW [ AS ] correlation-name ]

| NEW [ AS ] correlation-name [ OLD [ AS ] correlation-name ]

referential-actions ::= referential-update-action [ referential-delete-action ]

| referential-delete-action [ referential-update-action ]

referential-update-action ::= ON UPDATE RESTRICT

referential-delete-action ::= ON DELETE CASCADE

| ON DELETE RESTRICT

release-statement ::= see RELEASE statement

result ::= user-defined-name data-type

rollback-statement ::= see ROLLBACK WORK statement

savepoint-name ::= user-defined-name

scalar-function ::= see Scalar Function list

scale ::= integer

search-condition ::= search-condition AND search-condition

| search-condition OR search-condition
| NOT search-condition
| ( search-condition )
| predicate

select-item ::= expression [ [ AS ] alias-name ] | table-name . *

select-list ::= * | select-item [ , select-item ]...

set-function ::= COUNT (*)

| COUNT ( [ DISTINCT | ALL ] expression )
| SUM ( [ DISTINCT | ALL ] expression )
| AVG ( [ DISTINCT | ALL ] expression )
| MIN ( [ DISTINCT | ALL ] expression )
| MAX ( [ DISTINCT | ALL ] expression )

sqlstate-value ::= 'string'

table-constraint-definition ::= [ CONSTRAINT constraint-name ] table-constraint

table-constraint ::= UNIQUE (column-name [ , column-name ]... )

| PRIMARY KEY ( column-name [ , column-name ]... )
| FOREIGN KEY ( column-name [ , column-name ] )
REFERENCES table-name
[ ( column-name [ , column-name ]... ) ]
[ referential-actions ]

table-element ::= column-definition

| table-constraint-definition

table-expression ::=

FROM table-reference [ , table-reference ]...
[ WHERE search-condition ]
[ GROUP BY expression [ , expression ]...
[ HAVING search-condition ]

table-name ::= user-defined-name

table-privilege ::= ALL

|  SELECT [ ( column-name [ , column-name ]... ) ]
|  UPDATE [ ( column-name [ , column-name ]... ) ]
|  INSERT [ ( column-name [ , column-name ]... ) ]
|  DELETE
|  ALTER
|  REFERENCES

table-reference ::= { OJ outer-join-definition }

| [db-name.]table-name [ [ AS ] alias-name ]
| join-definition
| ( join-definition )

transaction-statement ::= commit-statement

| rollback-statement
| release-statement

trigger-name ::= user-defined-name

user-password ::= user-name [ : ] password

user-group-name ::= user-name | group-name

user-name ::= user-defined-name

value ::= literal | USER | ?

value-list ::= ( value [ , value ]... )

values-clause ::= DEFAULT VALUES | VALUES ( expression [ , expression ]... )

variable-name ::= user-defined-name

view-name ::= user-defined-name

SQL Statement List

SqlStatementList is defined as:

SqlStatementList
Statement ';' | SqlStatementList ';'
Statement ::= StatementLabel ':' Statement
|BEGIN ... END block
|CALL Statement
|CLOSE CURSOR Statement
|COMMIT Statement
|DECLARE CURSOR Statement
|DECLARE Variable Statement
|DELETE Statement
|FETCH Statement
|IF Statement
|INSERT Statement
|LEAVE Statement
|LOOP Statement
|OPEN Statement
|PRINT Statement
|RELEASE SAVEPOINT Statement
|RETURN Statement
|ROLLBACK Statement
|SAVEPOINT Statement
|SELECT Statement
|SET Statement
|SIGNAL Statement
|START TRANSACTION Statement
|UPDATE Statement
|WHILE Statement

Predicate

A predicate is defined as:

Expression CompareOperator Expression
|Expression [ NOT ] BETWEEN Expression AND Expression
|Expression [ NOT ] LIKE StringLiteral
|Expression IS [ NOT ] NULL
|NOT Predicate
|Predicate AND Predicate
|Predicate OR Predicate
|'(' Predicate ')'CompareOperator ::= '=' | '>=' | '>' | '<=' | '<' | '<>'
|[ NOT ] IN value-list

Expression

An expression is defined as:

Number
|StringLiteral
|ColumnName
|VariableName
|NULL
|CONVERT '(' Expression ',' DataType ')'
|'-' Expression
|Expression '+' Expression
|Expression '-' Expression
|Expression '*' Expression
|Expression '/' Expression
|FunctionName '(' [ ExpressionList ] ')'
|'(' Expression')'
|'{' D StringLiteral '}'
|'{' T StringLiteral '}'
|'{' TS StringLiteral '}'
|@:IDENTITY
|@:ROWCOUNT
|@@IDENTITY
|@@ROWCOUNT

An expression list is defined as:

ExpressionList ::= Expression [ , Expression ... ]
Prev
WHILE 		Up
Check for Revisions 		Next
Scalar Functions


Scalar Functions

The SRDE supports ODBC scalar functions which may be included in an SQL statement as a primary expression.

This section lists the scalar functions supported by the SRDE.

String Functions

String functions are used to process and manipulate columns that consist of text information, such as CHAR or LONGVARCHAR data types.

Arguments denoted as string can be the name of column, a string literal, or the result of another scalar function..
Table 3-21 String Functions
Function | Description
ASCII (string) | Returns the ASCII value of the left most character of string
BIT_LENGTH (string) | Returns the length in bits of string
CHAR (code) | Returns the ASCII character corresponding to ASCII value code. The argument must be an integer value.
CHAR_LENGTH (string) | Returns the number of characters in string.
CHARACTER_LENGTH (string) | Same as CHAR_LENGTH.
CONCAT (string1, string2) | Returns a string that results from combining string1 and string2.
LCASE or LOWER (string) | Converts all upper case characters in string to lower case.
LEFT (string, count) | Returns the left most count of characters in string. The value of count is an integer.
LENGTH (string) | Returns the number of characters in string. Trailing blanks and the string termination character are not returned.
LOCATE (string1, string2 [, start ]) | Returns the starting position of the first occurrence of string1 within string2. The search within string2 begins at the first character position unless you specify a starting position (start). The search begins at the starting position you specify. The first character position in string2 is 1. The string1 is not found, the function returns the value zero.
LTRIM (string) | Returns the characters of string with leading blanks removed.
OCTET_LENGTH (string) | Returns the length in bytes of string.
POSITION (string1, string2) | Returns the position of string1 in string2. If string1 does not exist in string2, a zero is returned.
REPLACE (string1, string2, string3) | Searches string1 for occurrences of string2 and replaces each with string3. Returns the result. If no occurrences are found, string1 is returned.
REPLICATE (string, count) | Returns a character string composed of string repeated count times. The value of count is an integer.
RIGHT (string, count) | Returns the right most count of characters in string. The value of count is an integer.
RTRIM (string) | Returns the characters of string with trailing blanks removed.
SPACE (count) | Returns a character string consisting of count spaces.
STUFF (string1, start, length, string2) | Returns a character string where length characters in string1 beginning at position start have been replaced by string2. The values of start and length are integers.
SUBSTRING (string1, start, length) | Returns a character string derived from string1 beginning at the character position specified by start for length characters.
UCASE or UPPER (string) | Converts all lower case characters in string to upper case.

Queries containing a WHERE clause with scalar functions RTRIM or LEFT can be optimized. For example, consider the following query:

SELECT * FROM T1, T2 WHERE T1.C1 = LEFT(T2.C1, 2)

In this case, both sides of the predicate are optimized if T1.C1 and T2.C2 are index columns. The predicate is the complete search condition following the WHERE keyword. Depending on the size of the tables involved in the join, the optimizer chooses the appropriate table to process first.

RTRIM and LEFT cannot be optimized if they are contained in a complex expression on either side of the predicate.

Examples

The following example creates a new table with an integer and a character column. It inserts 4 rows with values for the character column only, then updates the integer column of those rows with the ASCII character code for each character.

CREATE TABLE numchars(num INTEGER,chr CHAR(1) CASE)
INSERT INTO numchars (chr) VALUES('a')
INSERT INTO numchars (chr) VALUES('b')
INSERT INTO numchars (chr) VALUES('A')
INSERT INTO numchars (chr) VALUES('B')
UPDATE numchars SET num=ASCII(chr)
SELECT * FROM numchars

Results of SELECT:

num        chr
---------- ---
97         a
98         b
65         A
66         B

SELECT num FROM numchars WHERE num=ASCII('a')

Results of SELECT:

num
------
97

The following example concatenates the first and last names in the Person table and results in "RooseveltBora".

SELECT  CONCAT(First_name, Last_name) FROM Person WHERE First_name = 'Roosevelt'

The next example changes the case of the first name to lowercase and then to upper case, results in "roosevelt", "ROOSEVELT".

 SELECT LCASE(First_name),UCASE(First_name) FROM Person WHERE First_name = 'Roosevelt'

The following example results in first name trimmed to three characters beginning from left, the length as 9 and locate results 0. This query results in "Roo", 9, 0

SELECT  LEFT(First_name, 3),LENGTH(First_name), LOCATE(First_name, 'a') FROM Person WHERE First_name = 'Roosevelt'

The following example illustrates use of LTRIM and RTRIM functions on strings, results in "Roosevelt", "Roosevelt", "elt".

SELECT LTRIM(First_name),RTRIM(First_name), RIGHT(First_name,3) FROM Person WHERE First_name = 'Roosevelt'

This substring lists up to three characters starting with the second character in the first name as "oos."

SELECT  SUBSTRING(First_name,2, 3) FROM Person WHERE First_name = 'Roosevelt'

SELECT ID,first_name FROM Person WHERE LCASE(First_name) = 'bruce'

Numeric Functions

Numeric functions are used to process and manipulate columns that consist of strictly numeric information, such as decimal and integer values.
Table 3-22 Numeric Functions
Function | Description
ABS (numeric_exp) | Returns the absolute value of numeric_exp.
ACOS (float_exp) | Returns the arc cosine of float_exp as an angle, expressed in radians.
ASIN (float_exp) | Returns the arc sine of float_exp as an angle, expressed in radians.
ATAN (float_exp) | Returns the arc tangent of float_exp as an angle, expressed in radians.
ATAN2 (float_exp1, float_exp2) | Returns the arc tangent of the x and y coordinates, specified by float_exp1 and float_exp2, respectively, as an angle, expressed in radians.
CEILING (numeric_exp) | Returns the smallest integer greater than or equal to numeric_exp.
COS (float_exp) | Returns the cosine of float_exp, where float_exp is an angle expressed in radians.
COT (float_exp) | Returns the cotangent of float_exp, where float_exp is an angle expressed in radians.
DEGREES (numeric_exp) | Returns the number of degrees corresponding to numeric_exp radians.
EXP (float_exp) | Returns the exponential value of float_exp.
FLOOR (numeric_exp) | Returns the largest integer less than or equal to numeric_exp.
LOG (float_exp) | Returns the natural logarithm of float_exp.
LOG10 (float_exp) | Returns the base 10 logarithm of float_exp.
MOD (integer_exp1, integer_exp2) | Returns the remainder (modulus) of integer_exp1 divided by integer_exp2.
PI ( ) | Returns the constant value Pi as a floating point value.
POWER (numeric_exp, integer_exp) | Returns the value of numeric_exp to the power of integer_exp.
RADIANS (numeric_exp) | Returns the number of radians equivalent to numeric_exp degrees.
RAND (integer_exp) | Returns a random floating-point value using integer_exp as the optional seed value.
ROUND (numeric_exp, integer_exp) | Returns numeric_exp rounded to integer_exp places right of the decimal point. If integer_exp is negative, numeric_exp is rounded to |integer_exp| (absolute value of integer_exp) places to the left of the decimal point.
SIGN (numeric_exp) | Returns an indicator of the sign of numeric_exp. If numeric_exp is less than zero, -1 is returned. If numeric_exp equals zero, 0 is returned. If numeric_exp is greater than zero, 1 is returned.
SIN (float_exp) | Returns the sine of float_exp, where float_exp is an angle expressed in radians.
SQRT (float_exp) | Returns the square root of float_exp.
TAN (float_exp) | Returns the tangent of float_exp, where float_exp is an angle expressed in radians.
TRUNCATE (numeric_exp, integer_exp) | Returns numeric_exp truncated to integer_exp places right of the decimal point. If integer_exp is negative, numeric_exp is truncated to |integer_exp| (absolute value) places to the left of the decimal point.

Examples

The following example lists the Modulus of the number and capacity columns in a table named room.

SELECT  MOD(Number, Capacity)  FROM  Room

The following example selects all salaries from a table named Faculty that are evenly divisible by 100.

SELECT Salary FROM Faculty WHERE MOD(Salary, 100) = 0

Time and Date Functions

Date and time functions can be used to generate, process, and manipulate data that consists of date or time data types, such as DATE and TIME.

Inserting Time and Date Function Values

If you wish to insert the value of a function into a table, one method supported by all the time/date functions is to use a SELECT subquery in your INSERT statement, as shown:

INSERT INTO t1 (c1, c2) SELECT CURRENT_DATE(), CURRENT_TIME()

Some functions, such as CURDATE(), CURTIME(), and NOW(), also support direct INSERT, as shown:

INSERT INTO t1 (c1) VALUES (CURDATE())

Table 3-23 Time and Date Functions
Function | Description
CURDATE ( ) | Returns the current local date in this format: 'yyyy-mm-dd'. Uses the local clock time by default. If SET TIME ZONE has been called, then the value of CURDATE() is determined by calculating UTC time/date from the system clock and operating system locale setting, then adding the displacement value specified with SET TIME ZONE.
CURRENT_DATE ( ) | Returns the current UTC date in this format: 'yyyy-mm-dd'
CURTIME ( ) | Returns the current local time in this format: 'hh:mm:ss'. Uses the local clock time by default. If SET TIME ZONE has been called, then the value of CURTIME() is determined by calculating UTC time/date from the system clock and operating system locale setting, then adding the displacement value specified with SET TIME ZONE.
CURRENT_TIME ( ) | Returns the current UTC time in this format: 'hh:mm:ss'
CURRENT_TIMESTAMP( ) | Returns the current UTC date and time as a timestamp value in this format: 'yyyy-mm-dd hh:mm:ss.sssssss'
DAYNAME (date_exp) | Returns a character string containing the data source-specific name of the day (for example, Sunday through Saturday or Sun. through Sat. for a data source that uses English, or Sonntag through Samstag for a data source that uses German) for the day portion of date_exp.
DAYOFMONTH (date_exp) | Returns the day of the month in date_exp as an integer in the range of 1 to 31.
DAYOFYEAR (date_exp) | Returns the day of the year based on the year field in date_exp as an integer value in the range of 1-366.
EXTRACT (extract_field, extract_source) | Returns the extract_field portion of the extract_source. The extract_source argument is a date, time or interval expression. The permitted values of extract_field are: YEAR MONTH DAY HOUR MINUTE SECOND These values are returned from the target expression.
HOUR (time_exp) | Returns the hour as an integer in the rage of 0 to 23.
MINUTE (time_exp) | Returns the minute as an integer in the range 0 to 59.
MONTH (date_exp) | Returns the month as an integer in the range of 1 to 12.
MONTHNAME (date_exp) | Returns a character string containing the data source-specific name of the month (for example, September through December or Sept. through Dec. for a data source that uses English, or Settembre through Dicembre for a data source that uses Italian) for the month portion of date_exp.
NOW ( ) | Returns the current local date and time as a timestamp value in this format: 'yyyy-mm-dd hh:mm:ss.sssssss' Uses the local clock time by default. If SET TIME ZONE has been called, then the value of NOW() is determined by calculating UTC time/date from the system clock and operating system locale setting, then adding the displacement value specified with SET TIME ZONE.
QUARTER (date_exp) | Returns the quarter in date_exp as an integer value in the range of 1- 4, where 1 represents January 1 through March 31.
SECOND (time_exp) | Returns the second as an integer in the range of 0 to 59.
TIMESTAMPADD (interval, integer_exp, timestamp_exp) | Returns the timestamp calculated by adding integer_exp intervals of type interval to timestamp_exp. The allowed values for interval are: SQL_TSI_YEAR SQL_TSI_QUARTER SQL_TSI_MONTH SQL_TSI_WEEK SQL_TSI_DAY SQL_TSI_HOUR SQL_TSI_MINUTE SQL_TSI_SECOND
TIMESTAMPDIFF (interval, timestamp_exp1, timestamp_exp2) | Returns the integer number of intervals of type interval by which timestamp_exp2 is greater than timestamp_exp1. The values allowed for interval are the same as for TIMESTAMPADD
WEEK (date_exp) | Returns the week of the year based on the week field in date_exp as an integer in the range of 1 to 53.
YEAR (date_exp) | Returns the year as an integer value. The range depends on the data source.

Examples

The following example shows how to insert values from time or date functions. Some functions, such as CURDATE(), you can use directly in an INSERT statement. Other functions, however, are not supported in this manner. The method with the widest support is to use INSERT with SELECT. In the example below, the UTC time value returned by CURRENT_TIME() is inserted into table T1:

INSERT INTO T1 (C1) SELECT CURRENT_TIME()

The following example illustrates the use of hour.

SELECT c.Name,c.Credit_Hours FROM Course c WHERE c.Name = ANY (SELECT cl.Name FROM Class cl WHERE c.Name = cl.Name AND c.Credit_Hours >(HOUR (Finish_Time - Start_Time) + 1))

The following is an example of minute.

SELECT minute(log) FROM billing

The following example illustrates the use of second.

SELECT SECOND(log) FROM billing
SELECT log FROM billing where SECOND(log) = 31

The following example illustrates the use of now.

SELECT now() - log FROM billing

The following is a complex example that uses month, day, year, hour and minute.

SELECT Name, Section, MONTH(Start_Date), DAY(Start_Date), YEAR(Start_Date), HOUR(Start_Time),  MINUTE(Start_Time) FROM Class

The following example illustrates use of curdate.

SELECT ID, Name, Section FROM Class WHERE (Start_Date - CURDATE()) <= 2 AND (Start_Date - CURDATE()) >= 0

The next example gives the day of the month and day of the week of the start date of class from the class table.

SELECT  DAYOFMONTH(Start_date), DAYOFWEEK(Start_date)  from Class
SELECT * FROM person WHERE YEAR(Date_Of_Birth) < 1970

System Functions

System functions provide information at a system level.
Table 3-24 System Functions
Function | Description
DATABASE ( ) | Returns the current database name.
USER ( ) | Returns the login name of the current user.

Examples

The following examples show how to obtain the name of the current user and database:

SELECT USER( )
SELECT DATABASE( )

If you want to obtain this information for every record in a table, use the following (the example uses the Person table in DEMODATA):

SELECT USER( ) FROM person
SELECT DATABASE( ) FROM person
SELECT USER( ), DATABASE( ) FROM person

Logical Functions

Logical functions are used to manipulate data based on certain conditions.
Table 3-25 Logical Functions
Function | Description
IF (predicate, expression1, expression2) | Returns expression1 if predicate is true; otherwise, returns expression2.
NULL ( ) | Sets a column as NULL values.
IFNULL (exp, value) | If exp is NULL, value is returned. If exp is not null, exp is returned. The possible data type or types of value must be compatible with the data type of exp.
NULLIF (exp1, exp2) | NULLIF returns exp1 if the two expressions are not equivalent. If the expressions are equivalent, NULLIF returns a NULL value.

Examples

The system scalar functions IF and NULL are SQL extensions.

IF allows you to enter different values depending on whether the condition is true or false. For example, if you want to display a column with logical values as "true" or "false" instead of a binary representation, you would use the following SQL statement:

SELECT IF(logicalcol=1, 'True', 'False')

The system scalar function NULL allows you to set a column as null values. The syntax is:

NULL()

For example, the following SQL statement retrieves null values:

SELECT NULL() FROM person

The following statements demonstrate the IFNULL and NULLIF scalar functions. You use these functions when you want to do certain value substitution based on the presence or absence of NULLs and on equality.

CREATE TABLE Demo (col1 CHAR(3))
INSERT INTO Demo VALUES ('abc')
INSERT INTO Demo VALUES (NULL)
INSERT INTO Demo VALUES ('xyz')

Since the second row contains the NULL value, 'foo' is substituted in its place.

SELECT IFNULL(col1, 'foo') FROM Demo

This results in three rows fetched from one column:

"abc"
"foo"
"xyz"
3 rows fetched from 1 column.

The first row contains 'abc,' which matches the second argument of the following NULLIF call.

SELECT NULLIF(col1, 'abc') FROM Demo

A NULL is returned in its place:

<Null>
<Null>
"xyz"
3 rows fetched from 1 column.

Conversion Function

The conversion function converts an expression to a data type.
Table 3-26 Conversion Function
Function | Description
CONVERT (exp, type ) | Converts exp to the type indicated. The possible types are: SQL_BIGINT SQL_BINARY SQL_BIT SQL_CHAR SQL_DATE SQL_DECIMAL SQL_DOUBLE SQL_FLOAT SQL_INTEGER SQL_LONGVARCHAR SQL_NUMERIC SQL_REAL SQL_SMALLINT SQL_TIME SQL_TIMESTAMP SQL_TINYINT SQL_VARBINARY SQL_VARCHAR SQL_LONGVARBINARY

Examples

SELECT  CONVERT(id , SQL_CHAR), CONVERT( '1995-06-05', SQL_DATE),CONVERT('10:10:10', SQL_TIME), CONVERT('1990-10-10 10:10:10', SQL_TIMESTAMP),CONVERT('1990-10-10', SQL_TIMESTAMP)  FROM  Faculty

SELECT Name FROM Class WHERE Start_date > CONVERT ('1995-05-07', SQL_DATE) + 31
Prev
Grammar Element Definitions 		Up
Check for Revisions 		Next
Global Variables


Global Variables

Pervasive.SQL V8 supports the following global variables:

  • @@IDENTITY
  • @@ROWCOUNT

Either variable can be prefaced with two at signs (@@) or an at sign and a colon (@:). For example, @@IDENTITY and @:IDENTITY are equivalent.

@@IDENTITY and @@ROWCOUNT are global variables per connection. Each database connection has its own @@IDENTITY and @@ROWCOUNT values.

@@IDENTITY

This variable returns the value of the most recently inserted IDENTITY column value (IDENTITY or SMALLIDENTITY). The value is a signed integer value. The initial value is NULL.

This variable can only refer to a single column. If the target table includes more than one IDENTITY column, the value of this variable refers to the IDENTITY column that is the table's primary key. If no such column exists, then the value of this variable refers to the first IDENTITY column in the table.

If the most recent insert was to a table without an IDENTITY column, then the value of @@IDENTITY is set to NULL.

Examples
SELECT @@IDENTITY

Returns NULL if no records have been inserted in the current connection, otherwise returns the IDENTITY column value of the most recently inserted row.

SELECT * FROM T1 WHERE @@IDENTITY = 12

Returns the most recently inserted row if it has an IDENTITY column value of 12. Otherwise, returns no rows.

INSERT INTO T1(C2) VALUES (@@IDENTITY)

Inserts the IDENTITY value of the last row inserted into column C2 of the new row.

UPDATE T1 SET T1.C1 = (SELECT @@IDENTITY) WHERE T1.C1 = @@IDENTITY + 10

Updates column C1 with the IDENTITY value of the last row inserted, if the value of C1 is 10 greater than the IDENTITY column value of the last row inserted.

UPDATE T1 SET T1.C1 = (SELECT NULL FROM T2 WHERE T2.C1 = @@IDENTITY)

Updates column C1 with the value NULL if the value of C1 equals the IDENTITY column value of the last row inserted.

The example below creates a stored procedure and calls it. The procedure sets variable V1 equal to the sum of the input value and the IDENTITY column value of the last row updated. The procedure then deletes rows from the table anywhere column C1 equals V1. The procedure then prints a message stating how many rows were deleted.

CREATE PROCEDURE TEST (IN :P1 INTEGER);

BEGIN
DECLARE :V1 INTERGER;

SET :V1 = :P1 + @@IDENTITY;
DELETE FROM T1 WHERE T1.C1 = :V1;
IF (@@ROWCOUNT = 0) THEN
PRINT 'No row deleted';
ELSE
PRINT CONVERT(@@ROWCOUNT, SQL_CHAR) +
' rows deleted';
END IF;

END;

CALL TEST (@@IDENTITY)

@@ROWCOUNT

This variable returns the number of rows that were affected by the most recent operation in the current connection. The value is an unsigned integer. The initial value is zero.

Grammar

Same as the grammar for @@IDENTITY.

Examples
SELECT @@ROWCOUNT

Returns zero if no records were affected by the previous operation in the current connection, otherwise returns the number of rows affected by the previous operation.

CREATE TABLE T1 (C1 INTEGER, C2 INTEGER)

INSERT INTO T1 (C1, C2) VALUES (100,200)
INSERT INTO T1(C2) VALUES (100, @@ROWCOUNT)
SELECT * FROM T1

SELECT @@ROWCOUNT FROM T1

Results:

2

The first SELECT generates two rows and shows that the value of @@ROWCOUNT was 1 when it was used to insert a row. The second SELECT returns 2 as the value of @@ROWCOUNT, that is, after the first SELECT returned two rows.

Also see the example for @@IDENTITY.

Prev
Scalar Functions 		Up
Check for Revisions 		Next
Other Characteristics


Other Characteristics

This section describes other characteristics of the SQL grammar. It is divided into the following sections:

  • Temporary Files
  • Working with Literal Values
  • Working with NULL Values
  • Working with Binary Data
  • Creating Indexes
  • Closing an Open Table
  • Concurrency
  • Comma as Decimal Separator
  • OEM to ANSI Support

Temporary Files

When the SRDE must generate a temporary table in order to process a given query, it creates the file in a location determined by the following rules:

First, the SRDE uses the path specified in the registry key string value HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBC.INI\PervasiveEngineOptions\TempFileDirectory.

If the registry entry is not defined, then the SRDE uses the path specified in the TMP environment variable.

If the TMP environment variable is not defined, then the SRDE uses the path specified in the TEMP environment variable.

If the TEMP environment variable is not defined, then the SRDE uses the Windows directory for Windows NT/2000/XP otherwise the current directory.

The SRDE deletes all temporary files needed to process a query when it has completed processing the query. If the query is a SELECT statement, then the temporary files exist as long as the result set is active, that is, until the result set is freed by the calling application.

When are Temporary Files Created?

Temporary files are created under the following circumstances:

Forward-only, dynamic, or static cursor

SELECT statements with ORDER BY, GROUP BY, DISTINCT that do not use an index.

SELECT statements with UNION that would generate a temporary table in memory or an OptTech Sort temporary table on disk if temporary table size is greater than 256K.

In addition, for a static cursor, if you have several base tables in the join, the MicroKernel generates a Btrieve temporary file for each table in the join and another temporary file if you use an ORDER BY clause in the query.

Working with Literal Values

Pervasive.SQL supports all of the standard ODBC literal formats. This section provides some examples of the most common literal formats.

  • Date Values
  • Time Values
  • Timestamp Values
  • Date Arithmetic

String Values

String constants may be expressed in SQL statements by surrounding the given string of characters with single quotes. Single quotes contained within the string itself require a preceding single quote as an escape character.

Examples

In the first example, the apostrophe or single quote contained within the string must be escaped by another single quote.

SELECT * FROM t1 WHERE c1 = 'Roberta''s Restaurant'
SELECT STREET FROM address WHERE city LIKE 'san'

Date Values

Date constants may be expressed in SQL statements as a character string or embedded in a vendor string. The first case is treated as data type SQL_CHAR and the vendor string representation is treated as a value of type SQL_DATE. This becomes important when conversions are attempted.

The SRDE partially supports extended SQL grammar, as outlined in this function.

Pervasive.SQL supports the following date literal format: 'YYYY-MM-DD'.

Dates may be in the range of year 0 to 9999.

Examples

The next two statements return all the classes whose start date is after 1995-06-05.

SELECT * FROM Class WHERE Start_Date > '1995-06-05'

SELECT * FROM Class WHERE Start_Date > {d '1995-06-05'}

Time Values

Pervasive.SQL supports the following time literal format: 'HH:MM:SS'.

Time constants may be expressed in SQL statements as a character string or embedded in a vendor string. Character string representation is treated as a string of type SQL_CHAR and the vendor string representation as a value of type SQL_TIME.

Pervasive.SQL partially supports extended SQL grammar, as outlined in this function.

Examples

The following two statements retrieve records from the class table where the start time for the classes is 14:00:00.

SELECT * FROM Class WHERE Start_time = '14:00:00'

SELECT * FROM Class WHERE Start_time = {t '14:00:00'}

Timestamp Values

Timestamp constants may be expressed in SQL statements as a character string or embedded in a vendor string. Pervasive.SQL treats the character string representation as a string of type SQL_CHAR and the vendor string representation as a value of type SQL_TIMESTAMP.

Pervasive.SQL partially supports extended ODBC SQL grammar, as outlined in this function.

Pervasive.SQL supports the following timestamp literal format: 'YYYY-MM-DD HH:MM:SS'

Examples

The next two statements retrieve records from the Billing table where the start day and time for the log is 1996-03-28 at 17:40:49.

SELECT * FROM Billing WHERE log = '1996-03-28 17:40:49'
SELECT * FROM Billing WHERE log = {ts '1996-03-28 17:40:49'}

Date Arithmetic

The SRDE supports adding or subtracting an integer from a date where the integer is the number of days to add or subtract, and the date is embedded in a vendor string. (This is equivalent to executing a convert on the date).

The SRDE also supports subtracting one date from another to yield a number of days.

Examples

SELECT * FROM person P, Class C WHERE p.Date_Of_Birth < ' 1973-09-05' and c.Start_date >{d '1995-05-08'} + 30

Working with NULL Values

The SRDE interprets a NULL as an unknown value. Thus, if you try to compare two NULL values, they will compare as not equal.

An expression that evaluates to WHERE NULL=NULL returns FALSE.

Working with Binary Data

Consider the following scenario: you insert the literal value '1' into a BINARY(4) column named c1, in table t1. Next, you enter the statement, SELECT * FROM t1 WHERE c1='1'.

The engine can retrieve data using the same binary format as was used to input the data. That is, the SELECT example above works properly and returns the value, 0x01000000, even though there isn't a literal match.

Note
The engine always adds a zero ('0') to the front of odd-digit binary values that are inserted. For example, if you insert the value '010', then the value '0x00100000' is stored in the data file.

Do not use the prefix 0x to denote binary constants. Binary constants are a string of hexadecimal numbers enclosed by single quotation marks. The SRDE adds prefix 0x to the results of a SELECT statement.

This behavior is the same as SQL Server.

Creating Indexes

The maximum column size for VARCHAR columns is 254 bytes if the column does not allow Null values and 253 bytes if the column is nullable.

The maximum column size for CHAR columns is 255 bytes if the column does not allow Null values and 254 bytes if the column is nullable.

The maximum Btrieve key size is 255. When a column is nullable and indexed a segmented key is created with 1 byte for the null indicator and a maximum 254 bytes from the column indexed. VARCHAR columns differ from CHAR columns in that either length byte (Btrieve lstring) or a zero terminating byte (Btrieve zstring) are reserved, reducing the effective storage by 1 byte.

Closing an Open Table

Calling SQLFreeStmt with the SQL_CLOSE option changes the SQLState but does not close the open tables used by the hStmt. To close the tables currently used by hStmt, SQLFreeStmt must be called with the SQL_DROP option.

In the following example, the Emp and Dept tables remain open:

SQLPrepare(hStmt, "SELECT * FROM Emp, Dept", SQL_NTS)
SQLExecute(hStmt)
SQLFetch until SQL_No_Data_Found
SQLFreeStmt(hStmt, SQL_CLOSE)

When SQLPrepare is subsequently called on the hStmt, the tables used in the previous statement are closed. For example, when the following call is made, both the Emp and Dept tables are closed by the SRDE:

SQLPrepare(hStmt, "SELECT * FROM Customer",SQL_NTS)

The following call would then close the table Customer:

SQLFreeStmt(hStmt, SQL_DROP)

Concurrency

The timeliness of data, dynamic or snapshot, is determined by whether or not execution of a query results in a sort. Queries with DISTINCT, GROUP BY, or ORDER BY result in a temporary sort by SRDE, unless an index exists that satisfies the required ordering.

For those queries which do not result in a temporary sort by SRDE, the data fetched is from the data files. For those queries that result in a temporary sort by SRDE, the data fetched is from a temporary table. The temporary table is built from the required data in the original data file at SQLExecute time.

Note
For some sort operations (for example, SELECT statements where long data columns are included in the select-list, or SELECT statements with GROUP BY), SRDE may use bookmarks which SRDE assumes are persistent within a SELECT statement. The situation may arise whereby another application updates or deletes the row that a bookmark references.

To avoid this situation, an application may set an exclusive lock on the table being sorted through a call to SQLSetStmtOption, with fOption = 1153 and vParam = 1.

Comma as Decimal Separator

Many locales use a comma to separate whole numbers from fractional numbers within a floating point numeric field. For example, these locales would use 1,5 instead of 1.5 to represent the number one-and-one-half.

Starting with Pervasive.SQL V8, the SRDE can support both the period "." and the comma "," as decimal separators. The SRDE accepts input values using the period or the comma, based on the regional settings for the operating system. By default, the database engine displays values using the period.

Note
When the decimal separator is not a period, numbers appearing in SQL statements must be enclosed in quotes.

For output and display only, the session-level command SET DECIMALSEPARATORCOMMA can be used to specify output (for example, SELECT results) that uses the comma as the decimal separator. This command has no effect on data entry or storage.

Client/Server Considerations

Support for the comma as decimal separator is based on the locale setting in the operating system. Both the client operating system and the server operating system have a locale setting. The expected behavior varies according to both settings.

  • If either the server or client locale setting uses the comma as decimal separator, then the SRDE accepts both period-separated values and quoted comma-separated values.
  • If neither the server nor the client locale setting uses the comma decimal separator, then the SRDE does not accept comma-separated values.

Changing the Locale Setting

Decimal separator information can only be retrieved or changed for a Win32 machine (Windows 95/98/NT/2000).

The decimal setting for NetWare and Linux cannot be configured, and it is set to a period. If you have a NetWare or Linux server engine and you want to use the comma as decimal separator, you must ensure that all your client computers are set to a locale that uses the decimal separator.

To view or change your locale setting

  1. From the Start menu, choose Settings | Control Panel.
  2. In the Control Panel window, double-click Regional Settings.
  3. On the Regional Settings tab, select the desired country.
  4. You must stop and restart the Pervasive.SQL services.

Examples

Example A - Server locale uses the comma for decimal separator

Client's locale uses comma "," as decimal separator:

CREATE TABLE t1 (c1 DECIMAL(10,3), c2 DOUBLE)
INSERT INTO t1 VALUES (10.123, 1.232)
INSERT INTO t1 VALUES ('10,123', '1.232')
SELECT * FROM t1 WHERE c1 = 10.123
SELECT * FROM t1 FROM c1 = '10,123'

  The above two SELECT statements, if executed from the client, return:

10.123, 1.232
10.123, 1.232

SET DECIMALSEPARATORCOMMA=ON
SELECT * FROM t1 FROM c1 = '10,123'

  The above SELECT statement, if executed from the client after setting the decimal separator for display, returns:

10,123, 1,232

Client's locale uses period "." as decimal separator, and these statements are issued from a new connection (meaning default behavior for SET DECIMALSEPARATORCOMMA):

CREATE TABLE t1 (c1 DECIMAL(10,3), c2 DOUBLE)
INSERT INTO t1 VALUES (10.123, 1.232)
INSERT INTO t1 VALUES ('10,123', '1.232')
SELECT * FROM t1 WHERE c1 = 10.123
SELECT * FROM t1 WHERE c1 = '10,123'

  The above two SELECT statements, if executed from the client, return:

10.123, 1.232
10.123, 1.232
Example B - Server locale uses the period for decimal separator

Client's locale uses comma "," as decimal separator:

Same as client using comma "," in Example A.

Client's locale uses period "." as decimal separator:

CREATE TABLE t1 (c1 DECIMAL(10,3), c2 DOUBLE)
INSERT INTO t1 VALUES (10.123, 1.232)
INSERT INTO t1 VALUES ('10,123', '1,232')
-- error in assignment
SELECT * FROM t1 WHERE c1 = 10.123
SELECT * FROM t1 WHERE c1 = '10,123'
-- error in assignment

  The first select statement above, if executed from the client, returns:

10.123, 1.232

SET DECIMALSEPARATORCOMMA=ON
SELECT * FROM t1 FROM c1 = 10.123

  The above SELECT statement, if executed after setting the decimal separator for display, returns:

10,123, 1,232

OEM to ANSI Support

Applications can now store or retrieve character data in the OEM character set using Pervasive.SQL, while allowing the data to be manipulated and displayed using the ANSI Windows character set. The Pervasive ODBC driver translation DLL can perform all necessary translations between the two character sets. This feature can be turned on or off for each DSN. To access the switch, click Options... on the Pervasive ODBC DSN Setup dialog box.

The Pervasive Control Center (PCC) and the SQL Data Manager (SQL DM) are fully OEM-character aware if you use extended ASCII characters for column or table names. Any character data that is passed to and from the database is correctly translated between the OEM and ANSI character sets.

If your application connects to the data source using SQLDriverConnect, you can also specify the translation DLL using the connection string option TRANSLATIONDLL=path_and_DLL_name. The translation DLL name for Pervasive is W32BTXLT.DLL.

NOTE: The OEM to ANSI translation option is available only for client or local engine DSNs.

Prev
Global Variables 		Up
Check for Revisions 		Next
Performance Reference


Performance Reference

An Examination of the Performance Features in the SRDE

The purpose of this chapter is to explain in detail the optimizations used in the SQL Relational Database Engine (SRDE), and how to best take advantage of them in your SQL statements.

This chapter contains complex technical material and is intended for expert-level SQL users. For definitions of the technical terms used in this chapter, see Terminology .

  • Optimizations at a Glance
  • Restriction Analysis
  • Restriction Optimization
  • Push-Down Filters
  • Efficient Use of Indexes
  • Temporary Table Performance
  • Row Pre-fetch
  • Terminology

Prev
Other Characteristics
Check for Revisions 		Next
Optimizations at a Glance


Optimizations at a Glance

To help you understand the overall differences between this release and the preceding release, this section summarizes the optimizations that have been added in this release.

Table 4-1 shows a summary comparison of optimization behaviors in Pervasive.SQL 2000i and Pervasive.SQL V8.

Table 4-1 Optimization Behaviors by Product Version

Supported by Version
Type of Optimization
2000i
8.x
Restriction Optimizations

  • Single Predicate Optimization

ü

ü

  • Closed Range Optimization

ü

ü

  • Modified Disjunct Optimization

ü

  • Conjunct Optimization

ü

ü

  • Disjunctive Normal Form Optimization

ü

ü

  • Modified Conjunctive Normal Form Optimization

ü

  • Closing Open Ended Ranges through Modified CNF Optimization

ü

  • Single Join Condition Optimization

ü

ü

  • Conjunct with Join Conditions Optimization
Partial

ü

  • Modified Conjunctive Normal Form with Join Conditions Optimization

ü

  • Closing Join Condition Open Ended Ranges through Modified CNF Optimization

ü

  • Multi-Index Modified Disjunct Optimization

ü

Push-Down Filters

ü

Efficient Use of Indexes

  • DISTINCT in Aggregate Functions

ü

  • DISTINCT Preceding Selection-list

ü

  • Relaxed Index Segment Order Sensitivity
Partial

ü

  • Relaxed Segment Ascending Attribute Sensitivity
Partial

ü

Temporary Table Performance

ü

Row Pre-fetch

ü

Prev
Performance Reference 		Up
Check for Revisions 		Next
Restriction Analysis


Restriction Analysis

This section explains one method that the SRDE uses to analyze and optimize on a Restriction. For definitions of the technical terms used in this chapter, see Terminology .

Modified CNF Conversion

During SQL statement execution, the SRDE attempts to convert the restriction into Modified Conjunctive Normal Form (Modified CNF). Conversion to modified CNF is a method for placing Boolean expressions in a uniform structure to facilitate restriction analysis for possible query processing optimizations. If the restriction can be converted to modified CNF, the SRDE can methodically and thoroughly analyze the query for possible optimizations that make efficient use of available Indexes. If the SRDE is unable to convert the restriction to modified CNF, it still analyzes the restriction for possible optimizations. In this case, however, the SRDE is often unable to make use of the available indexes as effectively as it would for restrictions that either are already in modified CNF or can be converted internally to modified CNF.

Restrictions that Cannot be Converted

The SRDE is unable to convert some restrictions into modified CNF depending on the contents of the restriction. A restriction is not converted to modified CNF if any of the following conditions is true:

  • The restriction contains a subquery.
  • The restriction contains a NOT operator.
  • The restriction contains a dynamic parameter (a dynamic parameter is a question mark ("?") in the SQL statement, which will be prompted for when the statement is executed).

Conditions Under Which Conversion is Avoided

There are some cases in which the SRDE may be capable of converting a restriction into modified CNF but will not do so. The SRDE chooses not to convert a restriction to modified CNF in cases where it has determined that the restriction is more likely to benefit from optimizations that can be applied to its original form than from optimizations that could be applied after modified CNF conversion.

A restriction is not converted to modified CNF if either of the following conditions is true:

  • The restriction is in Disjunctive Normal Form (DNF) and all Predicates involve only the equal (=), LIKE or IN comparison operators.

    For example, the SRDE does not convert the following restriction to modified CNF:

    (c1 = 1 AND c2 = 1) OR (c1 = 1 AND c2 = 2) OR (c1 = 2)
  • The restriction meets all of the following conditions:
    • It contains an expression in Disjunctive Normal Form (DNF) that is AND connected to the rest of the restriction
    • The specified DNF expression contains only Predicates that involve the equal (=), LIKE or IN comparison operator
    • The predicates in identical positions in each Conjunct in the DNF expression reference the same column.

      For example, a Restriction that contains the following Expression will not be converted to modified CNF:

      (c1 = 1 AND c2 = 1) OR (c1 = 1 AND c2 = 2)
Prev
Optimizations at a Glance 		Up
Check for Revisions 		Next
Restriction Optimization


Restriction Optimization

This section provides a detailed description of the primary techniques employed by the SRDE to make use of expressions in a restriction for optimization purposes. The types of optimizations performed by the SRDE are described below in order from the simplest to the most complex.

A clear understanding of optimization techniques used by the SRDE may aid you in structuring queries to achieve optimal performance from the SRDE. In addition, by understanding how the SRDE uses indexes to optimize queries, you can determine how to construct indexes that provide the best performance for a given set of typical uses.

For the sake of simplicity, the descriptions below initially address expressions that reference columns from only a single table. Optimizations making use of join conditions, in which predicates compare columns from two different tables, are described following the single table optimizations.

For definitions of the technical terms used in this chapter, see Terminology .

Single Predicate Optimization

The simplest form of Restriction optimization involves the use of a single Predicate. A predicate can be used for optimization if it meets all of the following conditions:

  • the predicate is joined to the rest of the restriction by the AND operator
  • one operand of the predicate consists of a column reference which is a leading segment of an index and the other operand consists of an expression that does not contain a column reference (that is, the other operand contains only a literal value or dynamic parameter)
  • the comparison operator is one of: <, <=, =, >=, >, LIKE, or IN.

For example, suppose an index exists with the first segment on column c1. The following predicates can be used for optimization:

c1 = 1
c1 IN (1,2)
c1 > 1

Past Behavior

Pervasive.SQL 2000i also supports single predicate optimization.

Closed Range Optimization

A Closed Range can be used for optimization if it satisfies all the requirements for Single Predicate Optimization.

For example, suppose an index exists with the first segment on column c1. The following closed range can be used for optimization:

c1 >= 1 AND c1 < 10

Past Behavior

Pervasive.SQL 2000i also supports closed range optimization.

Modified Disjunct Optimization

A Modified Disjunct can be used for optimization if it satisfies all of the following conditions:

  • it is joined to the rest of the Restriction by the AND operator
  • each Predicate and Closed Range in the disjunct satisfies the requirements for Single Predicate Optimization and Closed Range Optimization
  • each predicate or closed range references the same column as the others.

For example, suppose an index exists with the first segment on column c1. The following modified disjunct can be used for optimization:

c1 = 1 OR (c1 > 5 AND c1 < 10) OR c1 > 20

The following modified disjunct cannot be used for this type of optimization because the same column is not referenced in all predicates and closed ranges:

c1 = 1 OR (c1 > 5 AND c1 < 10) OR c2 = 1

Past Behavior

Pervasive.SQL 2000i does not support modified disjunct optimization.

Pervasive.SQL 2000i does support disjunct optimization, if the disjunct consists only of predicates (no closed ranges). Furthermore all of the predicates must contain the equal (=) comparison operation.

Conjunct Optimization

A Conjunct can be used for optimization if it satisfies all of the following conditions:

  • it is joined to the rest of the restriction by the AND operator
  • each Predicate in the conjunct satisfies the requirements for Single Predicate Optimization
  • each predicate optimizes on the leading segments of an index with only one predicate for each leading segment (that is, there are not two different predicates that use the same set of leading segments)
  • all predicates, except for the predicate referencing the last segment used for optimization, use the equal (=) comparison operator.

For example, suppose an index exists with the first three segments on columns c1, c2 and c3, in that order. The following conjuncts can be used for optimization:

c1 = 1 AND c2 = 1 AND c3 = 1
c1 = 1 AND c2 = 1 AND c3 >= 1
c1 = 1 AND c2 > 1

The order of the predicates does not matter. For example, the following conjunct can be used for optimization:

c2 = 1 AND c3 = 1 AND c1 = 1

The following conjunct cannot be used for optimization because the second segment of the index is skipped (there is no reference to column c2):

c1 = 1 AND c3 = 1

In this case, the single predicate, c1 = 1, can still be used for optimization.

Past Behavior

Pervasive.SQL 2000i also supports conjunct optimization.

Disjunctive Normal Form Optimization

An expression in Disjunctive Normal Form (DNF) can be used for optimization if it satisfies all of the following conditions:

  • it is joined to the rest of the restriction by the AND operator
  • each of the conjuncts in the expression satisfies the requirements for Conjunct Optimization with the additional limitation that all the predicates must contain the equal (=) comparison operator
  • all the conjuncts must use the same index and the same number of segments for optimization.

The SRDE does not convert restrictions that are originally in DNF into modified CNF, because it can optimize on DNF.

For example, suppose an index exists with the first three segments on columns c1, c2 and c3, in that order. The following expression in DNF can be used for optimization:

(c1 = 1 AND c2 = 1 AND c3 = 1) OR (c1 = 1 AND c2 = 1 AND c3 = 2) OR (c1 = 2 AND c2 = 2 AND c3 = 2)

The following expression in DNF cannot be used for optimization because both conjuncts do not reference the same number of segments:

(c1 = 1 AND c2 = 1 AND c3 = 1) OR (c1 = 1 AND c2 = 2)

Past Behavior

Pervasive.SQL 2000i also supports DNF optimization.

Modified Conjunctive Normal Form Optimization

An expression in Modified Conjunctive Normal Form (Modified CNF) can be used for optimization if it satisfies all of the following conditions:

  • it is joined to the rest of the restriction by the AND operator
  • each Modified Disjunct satisfies the requirements for Modified Disjunct Optimization except that each modified disjunct must reference a different index segment which together make up the Leading Segments (that is, taking all the disjuncts together, no segments can be skipped)
  • all the modified disjuncts except for the one that references the last segment must contain at least one predicate that contains the equals (=) comparison operator.

Modified CNF optimization is similar to DNF optimization but allows combinations of predicates involving different comparison operations not supported by DNF optimization.

For example, suppose an index exists with the first three segments on columns c1, c2 and c3, in that order. The following expression in modified CNF can be used for optimization:

(c1 = 1 OR c1 = 2) AND (c2 = 1 OR (c2 > 2 AND c2 < 5)) AND (c3 > 1)

It may be easier to understand how the SRDE uses this expression for optimization by looking at the equivalent expression in modified DNF:

(c1 = 1 AND c2 = 1 AND c3 > 1) OR (c1 = 1 AND (c2 > 2 AND c2 < 5) AND c3 > 1) OR (c1 = 2 AND c2 = 1 AND c3 > 1) OR (c1 = 2 AND (c2 > 2 AND c2 < 5) AND c3 > 1)

Past Behavior

Pervasive.SQL 2000i does not support modified CNF optimization.

Closing Open Ended Ranges through Modified CNF Optimization

Two Modified Disjuncts can be combined to form one or more Closed Ranges if the following conditions are satisfied:

  • both modified disjuncts satisfy the requirements for Modified Disjunct Optimization
  • both modified disjuncts use the same segment in the same index
  • both modified disjuncts contain open ended ranges that can be combined to form one or more closed ranges.

For example, suppose an index exists with the first segment on column c1. The following expression in modified CNF can be used for optimization:

(c1 = 1 OR c1 > 2) AND (c1 < 5 OR c1 = 10)

It may be easier to understand how the SRDE uses this expression for optimization by looking at an equivalent expression which is simply a modified disjunct:

c1 = 1 OR (c1 > 2 AND c1 < 5) OR c1 = 10

Past Behavior

Pervasive.SQL 2000i does not support this type of optimization.

Single Join Condition Optimization

The simplest form of optimization involving two tables makes use of a single Join Condition. Single join condition optimization is similar to Single Predicate Optimization. A join condition can be used for optimization if it satisfies the requirements for single predicate optimization. The table that will be optimized through the use of the join condition will be processed after the other table referenced in the join condition. The table optimized through the use of the join condition uses an optimization value retrieved from a row in the other table referenced in the join condition.

For example, suppose an index exists on table t1 with the first segment on column c1. The following join conditions can be used for optimization:

t1.c1 = t2.c1
t1.c1 > t2.c2

During optimization, a row is retrieved from table t2. From this row, the value of column c2 is used to optimize on table t1 according to the join condition.

If, instead of an index on t1.c1, there is an index on t2.c1, then t1.c1=t2.c1 could be used to optimize on table t2. In this case, table t1 would be processed first and the value for t1.c1 would be used to optimize on table t2 according to the join condition.

In the case that there is an index on t1.c1 as well as an index on t2.c1, the SRDE query optimizer examines the size of both tables as well as the characteristics of the two indexes and chooses the table to optimize that will provide the best overall query performance.

Past Behavior

Pervasive.SQL 2000i also supports single join condition optimization.

Conjunct with Join Conditions Optimization

A Conjunct that consists of a mixture of join conditions and other Predicates can be used for optimization if it satisfies all of the following conditions:

  • all the join conditions compare columns from the same two tables
  • the conjunct satisfies the requirements for regular Conjunct Optimization for one of the two tables.

The table that will be optimized through the use of the conjunct will be processed after the other table referenced.

For example, suppose an index exists on table t1 with the first three segments on columns c1, c2 and c3, in that order. The following conjuncts can be used for optimization:

t1.c1 = t2.c1 AND t1.c2 = t2.c2 AND t1.c3 = t2.c3
t1.c1 = t2.c1 AND t1.c2 > t2.c2
t1.c1 = t2.c1 AND t1.c2 = 1
t1.c1 = 1 AND t1.c2 = t2.c2

Past Behavior

Pervasive.SQL 2000i also supports this type of optimization except that Pervasive.SQL 2000i has the additional requirements that the conjunct must contain only join conditions, no other predicates, and all the join conditions must contain the equal (=) comparison operation. Thus, to optimize the examples above, Pervasive.SQL 2000i would be able to use the entire first example, only the first join condition in the second example, only the join condition in the third example, and none of the fourth example.

Modified Conjunctive Normal Form with Join Conditions Optimization

An Expression in Modified Conjunctive Normal Form (Modified CNF) that contains join conditions can be used for optimization if it satisfies all the following conditions:

  • it satisfies the conditions for Modified Conjunctive Normal Form Optimization
  • in addition, all disjuncts but the disjunct optimizing on the last portion of the leading segment being used must contain only a single join condition or a single predicate and at least one of these is a single join condition.

For example, suppose an index exists on table t1 with the first three segments on columns c1, c2 and c3, in that order. The following expressions in modified CNF can be used for optimization:

(t1.c1 = t2.c1) AND (t1.c2 = t2.c2 OR
(t1.c2 > 2 AND t1.c2 < 5))

(t1.c1 = 1) AND (t1.c2 = t2.c2) AND
(t1.c3 > 2 AND t1.c3 < 5)

Past Behavior

Pervasive.SQL 2000i does not support this type of optimization.

Closing Join Condition Open Ended Ranges through Modified CNF Optimization

This type of optimization is exactly like Closing Open Ended Ranges through Modified CNF Optimization except that the range being closed may be a Join Condition.

For example, suppose an index exists on table t1 with the first two segments on columns c1 and c2, in that order. The following expressions in modified CNF can be used for optimization:

(t1.c1 > t2.c1) AND (t1.c1 < t2.c2 OR t1.c1 = 10)

(t1.c1 = t2.c1) AND (t1.c2 > t2.c2) AND (t1.c2 < 10 OR t1.c2 = 100)

Past Behavior

Pervasive.SQL 2000i does not support this type of optimization.

Multi-Index Modified Disjunct Optimization

A Modified Disjunct can be used for optimization through the use of more than one index if it satisfies all of the following conditions:

  • is joined to the rest of the restriction by the AND operator
  • each Predicate and Closed Range in the disjunct satisfies the requirements for Single Predicate Optimization or Closed Range Optimization, respectively
  • each predicate or closed range references a column that is the first segment in an index. If all predicates and closed ranges reference the same column, then this scenario is simply Modified Disjunct Optimization, as described previously.

For example, suppose an index exists with the first segment on column c1 and another index exists with the first segment on column c2. The following modified disjunct can be used for optimization:

c1 = 1 OR (c1 > 5 AND c1 < 10) OR c2 = 1

Past Behavior

Pervasive.SQL 2000i does not support this type of optimization.

Prev
Restriction Analysis 		Up
Check for Revisions 		Next
Push-Down Filters


Push-Down Filters

Push-down filters are strictly an internal optimization technique. By taking advantage of the high speed filtering capabilities of the MicroKernel Database Engine (MKDE), the SRDE can efficiently identify certain rows to be rejected from the result set depending on characteristics of the restriction. Because rows are rejected from the result set before they are returned to the SRDE, the SRDE has to analyze fewer rows and completes the operation faster than it would without push-down filters.

The SRDE can use an expression or combination of expressions as a push-down filter if the following conditions are satisfied:

  • A Predicate can be used in a push-down filter if it is joined to the rest of the Restriction by the AND operator.
  • A Predicate can be used in a push-down filter if one operand consists of a column reference and the other operand consists of either a literal value or a dynamic parameter ("?"). Also, the referenced column must not be of one of the following data types: bit, float, double, real, longvarchar, longvarbinary, or binary.
  • A Predicate can be used in a push-down filter if the comparison operator is one of the following: <, <=, =, >=, >, or <>.
  • A Disjunct can be used in a push-down filter if it is joined to the rest of the restriction by the AND operator and all the predicates within the disjunct satisfy the requirements for a predicate to be used in a push-down filter, except for the condition that the predicate must be joined to the rest of the restriction by an AND operator. Only one disjunct may be included in the push-down filter.
  • A push-down filter may combine a single disjunct with other predicates that satisfy the requirements for a predicate to be used in a push-down filter.

For definitions of the technical terms used in this chapter, see Terminology .

Past Behavior

Pervasive.SQL 2000i does not support this type of optimization.

Prev
Restriction Optimization 		Up
Check for Revisions 		Next
Efficient Use of Indexes


Efficient Use of Indexes

Several improvements have been made in this release involving the use of indexes to optimize on query characteristics other than the Restriction, such as a DISTINCT or ORDER BY clause. In some cases, indexes were already being used for optimization purposes but are used more efficiently in this release.

For definitions of the technical terms used in this chapter, see Terminology .

DISTINCT in Aggregate Functions

An index can be used to reduce the number of rows retrieved for queries with a selection-list that consists of an Aggregate Function containing the DISTINCT keyword. To be eligible for this type of optimization, the expression on which the DISTINCT keyword operates must consist of a single column reference. Furthermore, the column must be the leading segment of an index.

For example, suppose an index exists on table t1 with the first segment on column c1. The index can be used to avoid retrieving rows with duplicate values of column c1:

SELECT COUNT(DISTINCT c1) FROM t1 WHERE c2 = 1

Past Behavior

Pervasive.SQL 2000i does not support this type of optimization. Pervasive.SQL 2000i retrieves every row that satisfies the restriction and then rejects the rows with duplicate values.

DISTINCT Preceding Selection-list

An index can be used to reduce the number of rows retrieved for some queries with the DISTINCT keyword preceding the selection-list. To be eligible for this type of optimization, the selection-list must consist only of column references (no complex expressions such as arithmetic expressions or scalar functions), and the referenced columns must be the leading segments of a single index.

For example, suppose an index exists on table t1 with the first three segments on columns c1, c2 and c3, in any order. The index can be used to avoid retrieving rows with duplicate values for the selection-list items:

SELECT DISTINCT c1, c2, c3 FROM t1 WHERE c2 = 1

Past Behavior

Pervasive.SQL 2000i does not support this type of optimization. Pervasive.SQL 2000i retrieves every row that satisfies the restriction and then rejects the rows with duplicate values.

Relaxed Index Segment Order Sensitivity

Whether an index can be used to optimize on an ORDER BY clause depends on the order in which the columns appear as segments in the index. Specifically, to be eligible for this type of optimization, the columns in the ORDER BY clause must make up the leading segments of an index, and the columns must appear in the ORDER BY clause in the same order as they appear as segments in the index.

In contrast, an index can be used to optimize on a DISTINCT preceding a selection-list or on a GROUP BY clause as long as the selection-list or GROUP BY clause consists of columns that are the leading segments of the index. This statement is true regardless of the order in which the columns appear as segments in the index.

For example, suppose an index exists on table t1 with the first three segments on columns c1, c2 and c3, in any order. The index can be used to optimize on the DISTINCT in the following queries:

SELECT DISTINCT c1, c2, c3 FROM t1
SELECT DISTINCT c2, c3, c1 FROM t1 WHERE c3 > 1

The index can be used to optimize on the GROUP BY in the following queries:

SELECT c1, c2, c3, count(*) FROM t1 GROUP BY c2, c1, c3
SELECT c2, c3, c1, count(*) FROM t1 GROUP BY c3, c2, c1

For the index to be used to optimize on the ORDER BY, however, the index segments must be in the order of c2, c1, c3:

SELECT c1, c2, c3 FROM t1 ORDER BY c2, c1, c3

Past Behavior

Pervasive.SQL 2000i does not fully support this type of optimization.

Pervasive.SQL 2000i uses an index to optimize on a DISTINCT preceding a selection-list or a GROUP BY only if the columns appear in the selection-list or GROUP BY clause in the same order as they appear as columns in the index.

Relaxed Segment Ascending Attribute Sensitivity

Whether an index can be used to optimize on an ORDER BY clause depends on several conditions.

Specifically, an index can be used for optimization of ORDER BY if all of the following conditions are satisfied:

  • the DESC keyword follows the column in the ORDER BY clause
  • the corresponding index segment is defined as descending
  • the specified column is not nullable.

In addition, an index can be used for optimization of ORDER BY if all of the following converse conditions are satisfied (note that nullable columns are allowed for ascending ORDER BY):

  • the ASC keyword or neither ASC nor DESC follows the column in the ORDER BY statement
  • the corresponding index segment is defined as ascending.

As well, an index can be used for optimization of ORDER BY if the ascending/descending attributes of all the involved segments are the exact opposite of each ASC or DESC keyword specified in the ORDER BY. Again, the segments defined as descending can only be used if the associated columns are not nullable.

Indexes can be used for any of the restriction optimizations, optimization on a DISTINCT, or optimization on a GROUP BY clause, regardless of the ascending/descending attribute of any of the segments.

For example, suppose an index exists on table t1 with the first two segments on columns c1 and c2, in that order, and both segments are ascending. The index can be used to optimize on the following queries:

SELECT c1, c2, c3 FROM t1 ORDER BY c1, c2
SELECT c1, c2, c3 FROM t1 ORDER BY c1 DESC, c2 DESC
SELECT DISTINCT c1, c2 FROM t1
SELECT DISTINCT c2, c1 FROM t1
SELECT * FROM t1 WHERE c1 = 1

Suppose an index exists on table t1 with the first two segments on columns c1 and c2, in that order, with the segment on c1 defined as ascending and the segment on c2 defined as descending. Suppose also that c2 is nullable. The second segment cannot be used to optimize on ORDER BY because the column is both descending and nullable. The index can be used to optimize on the following queries:

SELECT c1, c2, c3 FROM t1 ORDER BY c1
SELECT c1, c2, c3 FROM t1 ORDER BY c1 DESC
SELECT DISTINCT c1, c2 FROM t1
SELECT DISTINCT c2, c1 FROM t1
SELECT * FROM t1 WHERE c1 = 1

If column c2 is not nullable, then the index can also be used to optimize on the following queries:

SELECT c1, c2, c3 FROM t1 ORDER BY c1, c2 DESC
SELECT c1, c2, c3 FROM t1 ORDER BY c1 DESC, c2

Past Behavior

Pervasive.SQL 2000i does not fully support this type of optimization.

Pervasive.SQL 2000i can only use an index if all the segments are ascending.

Search Update Optimization

You may take advantage of search optimization when you update a leading segment index by using the same index in the WHERE clause for the search. The SRDE uses one session (client ID) for the UPDATE and another session for the search.

The following statements benefit from search optimization.

CREATE TABLE t1 (c1 INT)
CREATE INDEX t1_c1 ON t1(c1)
INSERT INTO t1 VALUES(1)
INSERT INTO t1 VALUES(1)
INSERT INTO t1 VALUES(9)
INSERT INTO t1 VALUES(10)
INSERT INTO t1 VALUES(10)
UPDATE t1 SET c1 = 2 WHERE c1 = 10
UPDATE t1 SET c1 = c1 + 1 WHERE c1 >= 1

Past Behavior

Pervasive.SQL 2000i does not have search optimization. The same session (client ID) is used for both the search and UPDATE. The updates are seen immediately by the search. Therefore, the SRDE would not optimize on the index but scan the table instead.

Prev
Push-Down Filters 		Up
Check for Revisions 		Next
Temporary Table Performance


Temporary Table Performance

Performance improvements have been made to the implementation of temporary sort tables in this release. To process certain queries, the SRDE must generate temporary tables for internal use. The performance for many of these queries has been improved.

In general, the SRDE generates at least one temporary table to process a given query if any of the following conditions is true:

  • The DISTINCT keyword precedes the selection list and the items in the selection list are not columns that are the leading segments of an index.

    For example, a temporary table is generated to process the following query unless an index exists with columns c1 and c2 as leading segments:

    SELECT DISTINCT c1, c2 FROM t1
  • A GROUP BY clause exists, and the items in the GROUP BY clause are not columns that are the leading segments of an index.

    For example, a temporary table is generated to process the following query unless an index exists with columns c1 and c2 as leading segments:

    SELECT c1, c2, COUNT(*) FROM t1 GROUP BY c1, c2
  • A static cursor is being used.

    For example, a temporary table is generated if an application calls the ODBC API SQLSetStmtOption specifying the SQL_CURSOR_TYPE option and the SQL_CURSOR_STATIC value prior to creation of the result set.

  • The result set includes bookmarks.

    For example, if the ODBC API SQLSetStmtOption is called specifying the SQL_USE_BOOKMARKS option and the SQL_UB_ON value prior to generating the result set.

  • A query contains a non-correlated subquery to the right of the IN or =ANY keywords.

    For example:

    SELECT c1 FROM t1 WHERE c2 IN (SELECT c2 FROM t2)

Prev
Efficient Use of Indexes 		Up
Check for Revisions 		Next
Row Pre-fetch


Row Pre-fetch

For definitions of the technical terms used in this chapter, see Terminology .

Under certain circumstances, upon execution of a SELECT statement, this release of the database engine attempts to pre-fetch to the client the first two rows of the resulting rowset. This pre-fetch greatly improves the performance of fetching data from result sets that consist of zero or one row.

Pre-fetching rows can be a costly waste of time if the result set consists of more than one row and the first data retrieval operation requests a row other than the first row in the result set, such as the last row. Therefore, pre-fetching is limited to a maximum of two rows with the goal of improving performance for the cases that would benefit most while avoiding cases where pre-fetching would not provide significant benefits.

Pre-fetching occurs only if Array Fetch is enabled in the client DSN setup options. Array fetching is similar to pre-fetching except that an array fetch does not occur until the first explicit data retrieval operation is performed. This difference exists because the first explicit data retrieval operation may provide enough information to allow the SRDE to extrapolate how the rest of the result set will be retrieved. For example, if the first data retrieval operation is a call to the ODBC API SQLFetch, then the SRDE can assume with complete certainty that the entire result set will be retrieved one record at a time in the forward direction only. This assumption can be made because, according to the ODBC specification, an SQLFetch entails that the rest of the result set will be retrieved via SQLFetch as well. On the other hand, if a SQLExtendedFetch call is made, and the row set size is greater than one, then the client assumes that the developer-specified rowset size is optimal, and it does not override that setting with the array fetch.

Pre-fetching occurs only when all of the following conditions are satisfied:

  • Array fetch is enabled.
  • The result set does not include large variable length data; for example, the selection list does not contain a column of type LONGVARCHAR or LONGVARBINARY.
  • The result set does not include bookmarks.

    For example, pre-fetching does not occur if the ODBC API SQLSetStmtOption is called prior to generating the result set, specifying the SQL_USE_BOOKMARKS option and the SQL_UB_ON value.

  • A cursor with read-only concurrency is being used.

    For example, pre-fetching does not occur if the ODBC API SQLSetStmtOption is called, specifying the SQL_CONCURRENCY option and any value other than SQL_CONCUR_READ_ONLY, prior to generating the result set. By default, concurrency is read-only.

Prev
Temporary Table Performance 		Up
Check for Revisions 		Next
Terminology


Terminology

This section provides definitions and examples designed to help you understand the complex technical material in this chapter.

Aggregate Function

An aggregate function uses a group of values in the SELECT or HAVING clause of a query to produce a single value. Aggregate functions include: COUNT, AVG, SUM, MAX, MIN, and DISTINCT.

Closed Range

A closed range is a pair of Open-Ended Ranges joined by an AND operator. Both open-ended ranges must reference the same column and one must contain the < or <= comparison operator and the other must contain the >= or > comparison operator. A BETWEEN clause also defines a closed range.

For example, the following expressions are closed ranges:

c1 > 1 AND c1 <= 10
c1 BETWEEN 1 AND 10

Conjunct

A conjunct is an expression in which two or more Predicates are joined by AND operators. For example, the following Restrictions are conjuncts:

c1=2 AND c2<5
c1>2 AND c1<5 AND c2= 'abc'

Conjunctive Normal Form (CNF)

An Expression is in Conjunctive Normal Form if it contains two or more Disjuncts joined by AND operators. For example, the following expressions are in CNF:

c1 = 2 AND c2 < 5
(c1 = 2 OR c1 = 5) AND (c2 < 5 OR c2 > 20) AND (c3 = 'abc' OR c3 = 'efg')

Disjunct

A disjunct is an Expression in which two or more Predicates are joined by OR operators. For example, the following expressions are disjuncts:

c1 = 2 OR c2 = 5
c1 = 2 OR c1 > 5 OR c2 = 'abc'

Disjunctive Normal Form (DNF)

An Expression is in disjunctive normal form if it contains two or more Conjuncts joined by OR operators. For example, the following expressions are in DNF:

c1 = 2 OR c2 < 5
(c1 = 2 AND c2 = 5) OR (c2 > 5 AND c2 < 10) OR c3 = 'abc'

Expression

An expression consists of any Boolean algebra allowed in a Restriction. An entire restriction or any part of the restriction that includes at least one or more complete Predicates is an expression.

Index

An index is a construct associated with one or more columns in a table that allows the SRDE to perform efficient searches and sorts. The SRDE can make use of indexes to improve search performance by reading only specific rows that will satisfy the search conditions rather than by examining all the rows in the table. The SRDE can make use of indexes to retrieve rows in the order specified by an SQL query rather than having to use inefficient techniques to order the rows after retrieving them.

Join Condition

A join condition is a Predicate that compares a column in one table to a column in another table using any of the comparison operators: <, <=, =, >=, >.

For example, the following predicates are join conditions:

t1.c1 = t2.c1
t1.c1 > t2.c2

Leading Segments

A group of index segments are leading segments if they consist of the first n columns in an Index, where n is any number up to and including the total number of segments in the index. For example, if an index is defined with segments on columns c1, c2, and c4, then c1 is a leading segment, c1 and c2 together are leading segments, and all three together are leading segments. c2 alone is not a leading segment, because the segment c1 precedes c2 and is excluded. Columns c1 and c4 together are not leading segments, because c2 precedes c4 and is excluded.

Modified Conjunctive Normal Form (Modified CNF)

An Expression in Modified Conjunctive Normal Form is like an expression in Conjunctive Normal Form (CNF) except that each Disjunct may contain Closed Ranges as well as Predicates.

For example, the following expressions are in Modified CNF:

c1 = 2 AND c2 < 5
(c1 = 2 OR (c1 > 4 AND c1 < 6) OR c1 = 10) AND (c2 = 1 OR c3 = 'efg')

Modified Disjunct

A modified disjunct is like a Disjunct except that it may contain Closed Ranges as well as Predicates.

For example, the following expressions are modified disjuncts:

c1 = 2 OR (c1 > 4 AND c1 < 5)
(c1 = 2 OR (c1 > 4 AND c1 < 5)) OR c2 = 'abc'

Open-Ended Range

An open-ended range is a Predicate that contains any of the following comparison operators: <, <=, >= or >. Furthermore, one of the predicate's operands must consist entirely of a single column and the other operand must consist entirely of either a single column from another table or a literal.

For example, the following expressions are open ended ranges:

c1 > 1
c1 <= 10
t1.c1 > t2.c1

Predicate

A predicate is a Boolean expression that does not include any AND or OR Boolean operators (with the exception of a BETWEEN predicate).

For example, the following expressions are predicates:

(c1 = 1)
(c1 LIKE 'abc')
(c1 BETWEEN 1 AND 2)

The following examples are not predicates:

(c1 > 1 AND c1 < 5)
(c1 = 1 OR c1 = 2)

Restriction

A restriction is the entire WHERE clause of an SQL query.

Prev
Row Pre-fetch 		Up
Check for Revisions 		Next
Data Types


Data Types

Pervasive.SQL Supported Data Types

This appendix describes the data types and data type mappings offered by Pervasive.SQL through the ODBC interface and the Btrieve interface.

  • Pervasive.SQL Supported Data Types
  • Notes on Data Types
  • Legacy Data Types
  • Btrieve Key Data Types
Prev
Terminology
Check for Revisions 		Next
Pervasive.SQL Supported Data Types


Pervasive.SQL Supported Data Types

The following table shows information about the transactional and relational data types supported by Pervasive.SQL. The SRDE converts the relational data types to ODBC default types unless another data type conversion is specified when SQLGetData or SQLBindCol is called. (For a discussion of data type conversions, see the data types appendix in the Microsoft ODBC Programmer's Reference.). Items that are not applicable are designated "n/a."
Table A-1 Pervasive.SQL Data Types and Equivalent ODBC Data Types
Transactional Type (Size) | Relational Type | ODBC Type (code) | Type Code | Size (bytes) | Create/Add Parameters | Notes
AUTOINC(2) | SMALLIDENTITY | SQL_SMALLINT(5) | 15 | 2 |  |
AUTOINC(4) | IDENTITY | SQL_INTEGER(4) | 15 | 4 |  |
BFLOAT(4) | BFLOAT4 | SQL_REAL(7) | 9 | 4 | p2 | n4
BFLOAT(8) | BFLOAT8 | SQL_DOUBLE(8) | 9 | 8 | p2 | n4
BLOB | LONGVARBINARY | SQL_LONGVARBINARY (-4) | 22 | n/a | p2 | n2, n3, n6
CHAR | BINARY | SQL_BINARY(-2) | 0 | 1–255 | p1 | n2, n3
CHAR | CHAR | SQL_CHAR(1) | 0 | 1–255 | p1 | n1
CLOB | LONGVARCHAR | SQL_LONGVARCHAR (-1) | 21 | n/a | p4 | n5, n6
CURRENCY | CURRENCY | SQL_DECIMAL(3) | 19 | 8 | p2 |
DATE | DATE | SQL_DATE(9) | 3 | 4 | p2 |
DECIMAL | DECIMAL | SQL_DECIMAL(3) | 5 | 1–64 | p3 |
FLOAT(4) | REAL | SQL_REAL(7) | 2 | 4 | p2 |
FLOAT(8) | DOUBLE | SQL_DOUBLE(8) | 2 | 8 | p2 |
INTEGER(1) | TINYINT | SQL_TINYINT(-6) | 1 | 1 | p2 |
INTEGER(2) | SMALLINT | SQL_SMALLINT(5) | 1 | 2 | P2 |
INTEGER(4) | INTEGER | SQL_INTEGER(4) | 1 | 4 | p2 |
INTEGER(8) | BIGINT | SQL_DECIMAL(3) | 1 | 8 | p2 |
NUMERIC | NUMERIC | SQL_NUMERIC(2) | 8 | 1–15 | p3 | n4
NUMERICSA | NUMERICSA | SQL_NUMERIC(2) | 18 | 1–15 | p3 | n4
NUMERICSTS | NUMERICSTS | SQL_NUMERIC(2) | 17 | 1–14 | p3 | n4
TIME | TIME | SQL_TIME(10) | 4 | 4 | p2 |
TIMESTAMP | TIMESTAMP | SQL_TIMESTAMP(11) | 20 | 8 | p2 |
UNSIGNED(1) | UTINYINT | SQL_TINYINT(-6) | 14 | 1 | p2 |
UNSIGNED(2) | USMALLINT | SQL_SMALLINT(5) | 14 | 2 | p2 |
UNSIGNED(4) | UINTEGER | SQL_INTEGER(4) | 14 | 4 | p2 |
UNSIGNED(8) | UBIGINT | SQL_DECIMAL(3) | 14 | 8 | p2 |
WSTRING | n/a | n/a | 25 | n/a |  | n7
WSZSTRING | n/a | n/a | 26 | n/a |  | n7
ZSTRING | VARCHAR | SQL_VARCHAR(12) | 11 | 1–254 | p1 | n5
none | BIT | SQL_BIT(-7) | 16 | 1 bit |  | n6

Add/Create Parameters

p1 – SIZE, NOT NULL, CASE

p2 – NOT NULL

p3 – PRECISION (1-15), SCALE (0-15), NOT NULL

p4 – NOT NULL, CASE

Notes

n1 – padded with spaces

n2 – flag set in FIELD.DDF to tell SQL to use binary

n3 – padded with binary zeros

n4 – cannot be used as variable or in stored procedures

n5 – not padded

n6 – cannot be indexed

n7 – transactional only

Data Type Ranges

The following table lists the value range for the Pervasive.SQL data types.
Table A-2 Pervasive.SQL Data Type Ranges
Relational Data Type | Valid Value Range
BFLOAT4 | -2.93873588e-39 – 1.70141173e+38
BFLOAT8 | -2.93873588e-39 – 1.70141183e+38
BIGINT | -9223372036854775808 – 9223372036854775808
BINARY | range not applicable
BIT | 0 or 1
CHAR | range not applicable
CURRENCY | -922337203685477.5808 – 922337203685477.5807
DATE | 01-01-0001 – 12-31-9999
DECIMAL | depends on the length and number of decimal places
DOUBLE | -2.2250738585072e-308 – 1.79769313486232e+308
FLOAT (4) | -2.2250738585072e-308 – 1.79769313486232e+308
FLOAT (8) | -1.17549e-38 – 3.402823e+38
IDENTITY | 1 – 2147483647
INTEGER | -2147483648 – 2147483647
LONGVARBINARY | range not applicable
LONGVARCHAR | range not applicable
MONEY | -99999999999999999.99 – 99999999999999999.99
NUMERIC | depends on the length and number of decimal places
NUMERICSA | depends on the length and number of decimal places
NUMERICSTS | depends on the length and number of decimal places
REAL | -1.17549e-38 – 3.402823e+38
SMALLIDENTITY | 1 – 32767
SMALLINT | -32768 – 32767
TIME | 00:00:00 – 23:59:59
TIMESTAMP | 0001-01-01 00:00:00.0000000 – 9999-12-31 23:59:59.9999999 UTC
TINYINT | -128 – 127
UBIGINT | 0 – 18446744073709551615
UINTEGER | 0 – 4294967295
USMALLINT | 0 – 65535
UTINYINT | 0 – 255
VARCHAR | range not applicable

Prev
Data Types 		Up
Check for Revisions 		Next
Notes on Data Types


Notes on Data Types

This section covers various behaviors and key information regarding the available data types.

CHAR, VARCHAR, and LONGVARCHAR

  • CHAR columns are padded with blanks to "fill" the columns
  • VARCHAR/LONGVARCHAR are not padded with blanks to "fill" the columns. The significant data is terminated with a NULL character.
  • In all cases the trailing blanks are NOT significant in comparison operations (LIKE and =). However, in the LIKE case, if a space is explicitly entered in the query (like 'abc %'), the space before the wildcard does matter. In this example you are looking for 'abc<space><any other character>'

See also Limitations on LONGVARCHAR and LONGVARBINARY .

BINARY and LONGVARBINARY

  • BINARY columns are padded with binary zeros to "fill" the columns
  • LONGVARBINARY are NOT padded with blanks to "fill" the columns.
  • The current engine does not compare LONGVARBINARY. The current engine is able to compare fixed length BINARY data.

Pervasive.SQL now supports multiple LONGVARCHAR and LONGVARBINARY columns per table. The data is stored according to the offset in the variable length portion of the record. The variable length portion of data can vary from the column order of the data depending on how the data is manipulated. Consider the following example.

CREATE TABLE BlobDataTest
(

Nbr   UINT,          // Fixed record (Type 14)
Clob1 LONGVARCHAR,   // Fixed record (Type 21)
Clob2 LONGVARCHAR,   // Fixed record (Type 21)
Blob1 LONGVARBINARY, // Fixed record (Type 21)

)

On disk, the physical record would normally look like this:

[Fixed Data (Nbr, Clob1header, Clob2header, Blob1header)][ClobData1][ClobData2][BlobData1]

Now alter column Nbr to a LONGVARCHAR column:

ALTER TABLE BlobDataTest ALTER Nbr LONGVARCHAR

On disk, the physical record now looks like this:

[Fixed Data (Nbrheader, Clob1header, Clob2header, Blob1header)][ClobData1][ClobData2][BlobData1]
[NbrClobData]

As you can see, the variable length portion of the data is not in the column order for the existing data.

For newly inserted records, however, the variable length portion of the data is in the column order for the existing data.

[Fixed Data (Nbrheader, Clob1header, Clob2header, Blob1header)][NbrClobData][ClobData1][ClobData2]
[BlobData1]

See also Limitations on LONGVARCHAR and LONGVARBINARY .

Limitations on LONGVARCHAR and LONGVARBINARY

The following limitations apply to the LONGVARCHAR and LONGVARBINARY data types:

  • The LIKE predicate operates on the first 65500 characters of the column data.
  • All other predicates operate on the first 256 characters of the column data.
  • SELECT statements with GROUP BY, DISTINCT, and ORDER BY return all the data but only order on the first 256 characters of the column data.
  • In a single call to SQLGetData, the maximum number of characters returned by Pervasive.SQL for a LONGVARCHAR or LONGVARBINARY columns is 65500. Multiple calls must be made to SQLGetData to retrieve column data over 65500 characters.
  • Though the maximum amount of data that can be inserted into a LONGVARCHAR/LONGVARBINARY column is 2GB, using a literal in an INSERT statement reduces this amount to 15000 characters. You can insert more than 15000 characters by using a parameterized insert.

Comparison of Floats

Pervasive ODBC Engine Interface compares floating point numbers in comparison predicates using an almost equals algorithm. For example, 12.203 = 12.20300000000001, and 12.203 is >= 12.20300000000001. The epsilon value defined as dbl epsilon is (.2204460492503131e-016). This feature works for large numbers, but > and < will not be detected for small numbers; small numbers will be detected as equal.

Note
If you require precision to many decimal places, use the Decimal data type instead of the Real or Float data type.

Here is the comparison routine that Pervasive ODBC Engine Interface uses for the sql_double data type (which maps to the C double type). For the sql_real data type (which maps to the C float type), Pervasive ODBC Engine Interface uses flt_epsilon, which is (.2204460492503131e-016).

SHORT sCnvDblCmp(
DOUBLE  d1,
DOUBLE  d2)
{
if (d1 == d2)
   return 0;
if (d1 > d2)
   {
   if (d1 > d2 + DBL_EPSILON)
      return(1);
   }
else
   {
   if (d2 > d1 + DBL_EPSILON)
      return(-1);
   }

return(0);
}

Representation of Infinity

When Pervasive.SQL is required by an application to represent infinity, it can do so in either a 4-byte (C float type) or 8-byte (C double type) form, and in either a hexadecimal or character representation, as shown in the following table:
Table A-3 Infinity Representation
Value | Float Hexadecimal | Float Character | Double Hexadecimal | Double Character
Maximum Positive |  |  | 0x7FEFFFFFFFFFFFFF |
Maximum Negative |  |  | 0xFFEFFFFFFFFFFFFF |
Infinity Positive | 0x7F800000 | 1E999 | 0x7FF0000000000000 | 1E999
Infinity Negative | 0xFF800000 | -1E999 | 0xFFF0000000000000 | -1E999

Prev
Pervasive.SQL Supported Data Types 		Up
Check for Revisions 		Next
Legacy Data Types


Legacy Data Types

Some older (legacy) data types are not supported by the current release of Pervasive.SQL. The following table shows the new data type to use in place of the legacy data type.
Table A-4 Data Types Used to Replace Legacy Data Types
Legacy Type | Type Code | Replaced by | Type Code
LOGICAL(1) | 7 | UTINYINT | 14
LOGICAL(2) | 7 | USMALLINT | 14
LSTRING | 10 | VARCHAR | 11
LVAR | 13 | LONGVARCHAR | 21
MONEY | 6 | DECIMAL | 5
NOTE | 12 | LONGVARCHAR | 21

Prev
Notes on Data Types 		Up
Check for Revisions 		Next
Btrieve Key Data Types


Btrieve Key Data Types

This section discusses the Btrieve data types that can be indexed (key types). Internally, the MicroKernel compares string keys on a byte-by-byte basis, from left to right. By default, the MicroKernel sorts string keys according to their ASCII value. You may, however, define string keys to be case insensitive or to use an alternate collating sequence (ACS).

The MicroKernel compares unsigned binary keys one word at a time. It compares these keys from right to left because the Intel 8086 family of processors reverses the high and low bytes in an integer.

If a particular data type is available in more than one size (for example, both 4- and 8-byte FLOAT values are allowed), the Key Length parameter (used in the creation of a new key) defines the size that will be expected for all values of that particular key. Any attempt to define a key using a Key Length that is not allowed results in a Status 29 (Invalid Key Length).

The following table lists the key types and their associated codes. Following the table is a discussion of the internal storage formats for each key type.
Table A-5 Btrieve Key Data Types and Type Codes
Data Type | Type Code
AUTOINC | 15
BFLOAT | 9
BLOB | 22
CHAR | 0
CLOB | 21
CURRENCY | 19
DATE | 3
DECIMAL | 5
FLOAT | 2
INTEGER | 1
LOGICAL | 7
LSTRING | 10
MONEY | 6
NUMERIC | 8
NUMERICSA | 18
NUMERICSTS | 17
TIME | 4
TIMESTAMP | 20
UNSIGNED | 14
WSTRING | 25
WZSTRING | 26
ZSTRING | 11

AUTOINC

The AUTOINC key type is a signed Intel integer that can be either two or four bytes long. Internally, AUTOINC keys are stored in Intel binary integer format, with the high-order and low-order bytes reversed within a word. The MicroKernel sorts AUTOINC keys by their absolute (positive) values, comparing the values stored in different records a word at a time from right to left. AUTOINC keys may be used to automatically assign the next highest value when a record is inserted into a file. Because the values are sorted by absolute value, the number of possible records is roughly half what you would expect given that the datatype is signed.

Values that have been deleted from the file are not re-used automatically. If you indicate that you want the database engine to assign the next value by entering a zero (0) value in an insert or update, the database simply finds the highest number, adds 1, and inserts the resulting value.

You can initialize the value of a field in all or some records to zero and later add an index of type AUTOINC. This feature allows you to prepare for an AUTOINC key without actually building the index until it is needed.

When you add the index, the MicroKernel changes the zero values in each field appropriately, beginning its numbering with a value equal to the greatest value currently defined in the field, plus one. If nonzero values exist in the field, the MicroKernel does not alter them. However, the MicroKernel returns an error status code if nonzero duplicate values exist in the field.

The MicroKernel maintains the highest previously used auto-increment value associated with each open file containing an AUTOINC key. This value is established and increments only when an INSERT operation occurs for a record with ASCII zeros in the AUTOINC field. The value is used by all clients so that concurrent changes can take place, taking advantage of Key Page Concurrency.

The next AUTOINC value for a file is raised whenever any INSERT occurs that uses the previous AUTOINC value. This happens whether or not the INSERT is in a transaction or the change is committed.

However, this value may be lowered during an INSERT if all of the following are true:

  • The highest AUTOINC value found in the key is lower than the next AUTOINC value for the file
  • No other client has a pending transaction affecting the page that contains the highest AUTOINC value
  • The key page containing the highest AUTOINC value is not already pending by the client doing the INSERT.

In other words, only the first INSERT within a transaction can lower the next available AUTOINC value. After that, the next available AUTOINC value just keeps increasing.

An example helps clarify how an AUTOINC value may be lowered. Assume an autoincrement file exists with records 1, 2, 3 and 4. The next available AUTOINC value is 5.

Client1 begins a transaction and inserts two new records, raising the next available AUTOINC value to 7. (Client1 gets values 5 and 6). Client2 begins a transaction and also inserts two new records. This raises the next available AUTOINC value to 9. (Client 2 gets values 7 and 8).

Client1 the deletes records 4, 5, and 6. The next AUTOINC value remains the same since it is adjusted only on INSERTS. Client1 then commits. The committed version of the file now contains records 1, 2, and 3.

For Client2, the file contains records 1, 2, 3, 7, and 8. Client2 then inserts another record, which becomes record 9. The next available AUTOINC value is raised to 10. Client2 deletes records 3, 7, 8, and 9. For Client2, the file now contains only the committed records 1 and 2.

Next Client2 inserts another record, which becomes record 10. The next available AUTOINC value is raised to 11. The next autoincrement value is not lowered to 3 since the page containing the change has other changes pending on it.

Client2 then aborts the transaction. The committed version of the file now contains records 1, 2, and 3, but the next available AUTOINC value is still 11.

If either client inserts another record, whether in a transaction or not, the next available AUTOINC value will be lowered to 4. This occurs because all of the conditions required for lowering the value are true.

If a resulting auto-increment value is out of range, a Status Code 5 results. The database engine does not attempt to "wrap" the values and start again from zero. You may, however, insert unused values directly if you know of gaps in the autoincrement sequence where a previously inserted value has been deleted.

Restrictions

The following restrictions apply to AUTOINC keys:

  • The key must be defined as unique.
  • The key cannot be segmented. However, an AUTOINC key can be included as an integer segment of another key, as long as the AUTOINC key has been defined as a separate, single key first, and the AUTOINC key number is lower than the segmented key number.
  • The key cannot overlap another key.
  • All keys must be ascending.

The MicroKernel treats AUTOINC key values as follows when you insert records into a file:

  • If you specify a value of binary 0 for the AUTOINC key, the MicroKernel assigns a value to the key based on the following criteria:
    • If you are inserting the first record in the file, the MicroKernel assigns the value of 1 to the AUTOINC key.
    • If records already exist in the file, the MicroKernel assigns the key a value that is one number higher than the highest existing absolute value in the file.
  • If you specify a nonzero value for the AUTOINC key, the MicroKernel inserts the record into the file and uses the specified value as the key value. If a record containing that value already exists in the file, the MicroKernel returns an error status code, and does not insert the record.

It is a good idea not to insert negative numbers directly into a table using IDENTITY, because any attempt by the SRDE to optimize on the index will return unexpected results.

BFLOAT

The BFLOAT key type is a single or double-precision real number. A single-precision real number is stored with a 23-bit mantissa, an 8-bit exponent biased by 128, and a sign bit. The internal layout for a 4-byte float is as follows:

The representation of a double-precision real number is the same as that for a single-precision real number, except that the mantissa is 55 bits instead of 23 bits. The least significant 32 bits are stored in bytes 0 through 3.

The BFLOAT type is commonly used in legacy BASIC applications. Microsoft refers to this data type as MBF (Microsoft Binary Format), and no longer supports this type in the Visual Basic environment.

BLOB

The Binary Large Object (BLOB) type provides support for binary data fields up to 2 GB in size. This type consists of 2 parts:

  • an 8-byte header in the fixed-length portion of the record. The header contains a 4-byte integer that identifies the beginning of the data in the variable-length portion of the record, and a 4-byte integer that specifies the size of the field.
  • the binary data itself within the variable-length portion of the record. The size of all BLOB and CLOB fields must sum to 2 GB or less, because the offset pointer into the variable-length portion of the record is limited to 2 GB maximum offset. To store the maximum BLOB size of 2 GB, you may have only 1 BLOB or CLOB field defined in the record.

For additional information, see BINARY and LONGVARBINARY and Limitations on LONGVARCHAR and LONGVARBINARY .

CHAR

Note
In previous versions of Pervasive.SQL, this data type was referred to as STRING

The CHAR key type is a sequence of characters ordered from left to right. Each character is represented in ASCII format in a single byte, except when the MicroKernel is determining whether a key value is null.

CLOB

The Character Large Object (CLOB) type provides support for character data fields up to 2 GB in size. This type consists of 2 parts:

  • an 8-byte header in the fixed-length portion of the record. The header contains a 4-byte integer that identifies the beginning of the data in the variable-length portion of the record, and a 4-byte integer that specifies the size of the field.
  • the character data itself within the variable-length portion of the record. The size of all BLOB and CLOB fields must sum to 2 GB or less, because the offset pointer into the variable-length portion of the record is limited to 2 GB maximum offset. To store the maximum BLOB size of 2 GB, you may have only 1 BLOB or CLOB field defined in the record.

For additional information, see CHAR, VARCHAR, and LONGVARCHAR and Limitations on LONGVARCHAR and LONGVARBINARY .

CURRENCY

The CURRENCY key type represents an 8-byte signed quantity, sorted and stored in Intel binary integer format; therefore, its internal representation is the same as an 8-byte INTEGER data type. The CURRENCY data type has an implied four digit scale of decimal places, which represents the fractional component of the currency data value.

DATE

The DATE key type is stored internally as a 4-byte value. The day and the month are each stored in 1-byte binary format. The year is a 2-byte binary number that represents the entire year value. The MicroKernel places the day into the first byte, the month into the second byte, and the year into a two-byte word following the month.

An example of C structure used for date fields would be:

TYPE dateField {

char day;

char month;

integer year;

}

The year portion of a date field is expected to be set to the integer representation of the entire year. For example, 2,001 for the year 2001.

DECIMAL

The DECIMAL key type is stored internally as a packed decimal number with two decimal digits per byte. The internal representation for an n-byte DECIMAL field is as follows:

The sign nibble is either 0xF or 0xC for positive numbers and 0xD for negative numbers. Pervasive ODBC and Pervasive ActiveX controls always use 0xF for the positive sign nibble. The decimal point is implied; no decimal point is stored in the DECIMAL field. Your application is responsible for tracking the location of the decimal point for the value in a DECIMAL field. All the values for a DECIMAL key type must have the same number of decimal places in order for the MicroKernel to collate the key correctly. The DECIMAL type is commonly used in COBOL applications.

An eight-byte decimal can hold 15 digits plus the sign. A ten-byte decimal can hold 19 digits plus the sign. The decimal value is expected to be left-padded with zeros.

FLOAT

Caution
Precision beyond that supported by the C-language definitions for the FLOAT (4-byte) or DOUBLE (8-byte) data type will be lost. If you require precision to many decimal points, consider using the DECIMAL type.

The FLOAT key type is consistent with the IEEE standard for single and double-precision real numbers. The internal format for a 4-byte FLOAT consists of a 23-bit mantissa, an 8-bit exponent biased by 127, and a sign bit, as follows:

A FLOAT key with 8 bytes has a 52-bit mantissa, an 11-bit exponent biased by 1023, and a sign bit. The internal format is as follows:

INTEGER

The INTEGER key type is a signed whole number and can contain any number of digits. Internally, INTEGER fields are stored in Intel binary integer format, with the high-order and low-order bytes reversed within a word. The MicroKernel evaluates the key from right to left. The sign must be stored in the high bit of the rightmost byte. The INTEGER type is supported by most development environments.
Table A-6 INTEGER Key Type
Length in Bytes | Value Ranges
1 | 0 – 255
2 | -32768 – 32767
4 | -2147483648 – 2147483647
8 | -9223372036854775808 – 9223372036854775807

LOGICAL

The LOGICAL key type is stored as a 1 or 2-byte value. The MicroKernel collates LOGICAL key types as strings. Doing so allows your application to determine the stored values that represent true or false.

LSTRING

The LSTRING key type has the same characteristics as a regular STRING type, except that the first byte of the string contains the binary representation of the string's length. The LSTRING key type is limited to a maximum size of 255 bytes. The length stored in byte 0 of an LSTRING key determines the number of significant bytes. The MicroKernel ignores any values beyond the specified length of the string when sorting values. The LSTRING type is commonly used in Pascal applications.

MONEY

The MONEY key type has the same internal representation as the DECIMAL type, with an implied two decimal places.

NUMERIC

NUMERIC values are stored as ASCII strings, right justified with leading zeros. Each digit occupies one byte internally. The rightmost byte of the number includes an embedded sign with an EBCDIC value. Table A-7 indicates how the rightmost digit is represented when it contains an embedded sign for positive and negative numbers.
Table A-7 Rightmost Digit with Embedded Sign
Digit | Positive | Negative
1 | A | J
2 | B | K
3 | C | L
4 | D | M
5 | E | N
6 | F | O
7 | G | P
8 | H | Q
9 | I | R
0 | { | }

For positive numbers, the rightmost digit can be represented by 1 through 0 instead of A through {. The MicroKernel processes positive numbers represented either way. The NUMERIC type is commonly used in COBOL applications.

NUMERICSA

The NUMERICSA key type (sometimes called NUMERIC SIGNED ASCII) is a COBOL data type that is the same as the NUMERIC data type, except that the embedded sign has an ASCII value instead of an EBCDIC value.

NUMERICSTS

The NUMERICSTS key type (sometimes called SIGN TRAILING SEPARATE) is a COBOL data type that has values resembling those of the NUMERIC data type. NUMERICSTS values are stored as ASCII strings and right justified with leading zeros. However, the rightmost byte of a NUMERICSTS string is either "+" (ASCII 0x2B) or "-" (ASCII 0x2D). This differs from NUMERIC values that embed the sign in the rightmost byte along with the value of that byte.

REAL

The REAL type is defined as a 4-byte FLOAT.

TIME

The TIME key type is stored internally as a 4-byte value. Hundredths of a second, second, minute, and hour values are each stored in 1-byte binary format. The MicroKernel places the hundredths of a second value into the first byte, followed respectively by the second, minute, and hour values.

TIMESTAMP

The TIMESTAMP data type represents a time and date value. In SQL applications, use this data type to stamp a record with the time and date of the last update to the record.

Note
According to the ODBC standard, scalar functions such as CURRENT_TIME() or NOW() ignore the portion of the data type that represents fractional seconds. For this reason, TIMESTAMP values generated from scalar functions always have zero fractional seconds. For example: 2002-06-11 16:15:50.0000000. If you require your TIMESTAMP values to have significance in fractions of a second, do not use scalar functions to generate values to be inserted or updated.

TIMESTAMP is intended to cover time and data values made up of the following components: year, month, day, hour, minute, and second. The following table indicates the valid values of each of these components:
Table A-8 TIMESTAMP Data Type
YEAR | 0001 to 9999
MONTH | 01 to 12
DAY | 01 to 31, constrained by the value of MONTH and YEAR in the Gregorian calendar.
HOUR | 00 to 23
MINUTE | 00 to 59
SECOND | 00 to 59

The value is stored internally as a TIMESTAMP MicroKernel key, an 8-byte long field that contains the complete date and time value converted to the fractions of seconds that timestamp precision specifies. For example, it is converted to microseconds when timestamp precision is 6, and it is converted to milliseconds when timestamp precision is 3. You provide the value of a TIMESTAMP in local time and SRDE converts it to Coordinated Universal Time (UTC), formerly Greenwich Mean Time (GMT), before storing it in a MicroKernel record. When you request the value of a TIMESTAMP, SRDE converts it from UTC to local time before returning the data.

Caution
It is critical that you correctly set time zone information on the computer where the database engine runs. If you move across time zones or change time zone information, the returned data will change when it is converted from UTC to local time. The local time/UTC conversions occur in SRDE using the time zone information where SRDE is running. The time zone information for sessions that are in different time zones than SRDE engine are not used in the local time/UTC conversions.

Caution Because timestamp data is converted to UTC before it is stored, the TIMESTAMP type is inappropriate for use with local time and local date data that reference events external to the database itself, particularly in time zones where seasonal time changes take place (such as Daylight Savings Time in the United States).

For example, assume it is October 15, and you enter a timestamp value to track an appointment on November 15 at 10 a.m. Assume you are in the U. S. Central Time Zone. When the SRDE stores the value, it converts it to UTC using current local time information (UTC-5 hours for CDT). So it stores the hour value 15. Assume, on November 1, you check the time of your appointment. Your computer is now in Standard Time, because of the switch that occurred on the last Sunday of October, so the conversion is (UTC-6 hours). When you extract the appointment time, it will show 9 a.m. local time (15 UTC - 6 CST), which is not the correct appointment time.

The same type of issue will occur if a database engine is moved from one time zone to another.

Because the SRDE does not convert DATE and TIME values to UTC, you should almost always use DATE and TIME columns to record external data. The only reason to use a TIMESTAMP column is a need for the specific ability to determine the sequential time order of records entered into the database.

UNSIGNED

The MicroKernel sorts UNSIGNED keys as unsigned INTEGER keys. An UNSIGNED key could contain any even number of bytes. The MicroKernel compares UNSIGNED keys from right to left.

An UNSIGNED key is sorted in the same manner as an INTEGER key. The differences between an UNSIGNED key and an INTEGER key are that an INTEGER has a sign bit, while an UNSIGNED type does not, and an UNSIGNED key can be longer than 4 bytes.

WSTRING

WSTRING is a Unicode string that is not null-terminated. The length of the string is determined by the field length.

WZSTRING

WZSTRING is a Unicode string that is double null-terminated. The length of this string is determined by the position of the Unicode NULL (two null bytes) within the field. This corresponds to the ZSTRING type supported in Btrieve.

ZSTRING

The ZSTRING key type corresponds to a C string. It has the same characteristics as a regular string type except that a ZSTRING type is terminated by a binary 0. The MicroKernel ignores any values beyond the first binary 0 it encounters in the ZSTRING, except when the MicroKernel is determining whether a key value is null.

The maximum length of a ZSTRING type is 255 characters, including the null terminator character. If used as a key for a nullable column, only the first 254 bytes of the string are used in the key. This minor limitation occurs because the key is limited to 255 bytes total length, and one byte is occupied by the null indicator for the column, leaving only 254 bytes for the key value.

Prev
Legacy Data Types 		Up
Check for Revisions 		Next
SQL Reserved Words


SQL Reserved Words

Supported Pervasive.SQL Reserved Words

Reserved words are SQL keywords and other symbols that have special meanings when they are processed by the SRDE. Reserved words are not recommended for use as database, table, column, variable or other object names. If a reserved word is used as an object name, it must be enclosed in double-quotes to notify the SRDE that the word is not being used as a keyword in the given context.

You can avoid having to worry about reserved words by always enclosing user-defined object names in double-quotes.

This appendix contains the following topic:

  • Reserved Words
  • Words to Avoid
Prev
Btrieve Key Data Types
Check for Revisions 		Next
Reserved Words


Reserved Words

Each of the symbols or words listed below has a special meaning when processed by the SRDE, unless it is delimited by double-quotes. Using one of these words as a table or object name without double quotes will cause an error.

See also the next section, Words to Avoid .

Symbols
#
;
:
@
A
ABORT | ABSOLUTE | ACCELERATED
ACTION | ADD | AFTER
ALL | ALTER | AND
ANY | AS | ASC
ATOMIC | AUTHORIZATION | AVG
B
BEFORE | BEGIN/BEGINS | BETWEEN
BINARYNULL | BIND | BLANK
BY |  |
C
CALL | CASCADE | CASE
CHAIN | CHARACTER | CHECK
CLOSE | COBOL | COLLATE
COLUMN | COMMIT | COMMITTED
CONDITION | CONSTRAINT | CONTINUE
CONVERT | COUNT | CREATE
CREATETAB | CROSS | CS
CURDATE | CURRENT | CURSOR
CURTIME |  |
D
D | DATABASE | DCOMPRESS
DECIMALSEPARATORCOMMA | DECLARE | DEFAULT
DELETE | DESC | DIAGNOSTICS
DICTIONARY | DISTINCT | DO
DROP |  |
E
EACH | ELSE | ELSEIF
END | ESCAPE | EX
EXCLUSIVE | EXEC | EXECUTE
EXISTS | EXTERNAL |
F
FETCH | FIRST | FN
FOR | FOREIGN | FROM
FULL |  |
G
GRANT
GROUP

H
HANDLER
HAVING
HOUR
I
IDENTITY | IF | IN
INCLUDE | INDEX | INNER
INOUT | INSERT | INTERNAL
INTO | IS | ISOLATION
J

JOIN

K

KEY

L
LANGUAGE | LAST | LEAVE
LEFT | LEGACYOWNERNAME | LENGTH
LEVEL | LIKE | LOGICAL
LOGIN | LOOP | LOWER
M
MAX | MILLISECOND | MIN
MINUTE | MODIFIABLE | MODIFY
MODULE | MONEY | MONTH
N
NEW | NEXT | NO
NORMAL | NOT | NOTFOUND
NOW | NULL |
O
OF | OFF | OLD
ON | ONLY | OPEN
OPENMODE | OPTION | OR
ORDER | OUT | OUTER
OWNER | OWNERACCESS |
P
PAGESIZE | PREALLOCATE | PREPARE
PRIMARY | PRINT | PRIOR
PRIVILEGES | PROCEDURE | PUBLIC
R
RANGE | READ | READONLY
REFERENCES | REFERENCING | RELATIVE
RELEASE | REPEAT | REPEATABLE
REPLACE | REPLICATION | RESIGNAL
RESTRICT | RETURN | RETURNS
REVOKE | RIGHT | ROLLBACK
ROW | ROWCOUNT |
S
SAVEPOINT | SCHEMA | SCROLL
SECURITY | SEG | SELECT
SERIALIZABLE | SET | SIGNAL
SIZE | SOME | SQLERROR
SQLEXCEPTIONS | SQLWARNINGS | SQLSTATE
SSP_EXPR | SSP_PRED | START
STATEMENT | STRINGNULL | SUM
SVBEGIN | SVEND |
T
T | TABLE | THEN
THRESHOLD | TO | TOP
TRANSACTION | TRIGGER | TRIGGERSTAMPMISC
TRUENULLCREATE | TS | TYPE
U
UNCOMMITTED | UNDO | UNION
UNIQUE | UNTIL | UPDATABLE
UPDATE | UPPER | USER
USING |  |
V
VALUE, VALUES
VERIFY
VIEW
W
WHEN | WHERE | WHILE
WITH | WORK | WRITE
Y

YEAR

Z

ZSTRING

Prev
SQL Reserved Words 		Up
Check for Revisions 		Next
Words to Avoid


Words to Avoid

The following table lists common keywords from the SQL-92 and SQL-99 ANSI standards. While Pervasive.SQL may not support or offer all of these keywords, you should avoid using these words as names for tables, columns, or other objects, if you do not use double-quotes around them. Pervasive reserves the right to add support for any of these keywords as well as any future ANSI SQL keywords in future releases.

If you use double-quotes to delimit all table, column, and user-defined object names, you do not need to worry about possible future conflicts with reserved words.

See also the previous section, Reserved Words .
Table B-1 Words to Avoid as User-Defined Names
ABSOLUTE | ACTION | ADD
ALL | ALLOCATE | ALTER
AND | ANY | ARE
AS | ASC | ASSERTION
AT | AUTHORIZATION | AVG
BEGIN | BETWEEN | BIT
BIT_LENGTH | BOTH | BY
CASCADE | CASCADED | CASE
CAST | CATALOG | CHAR
CHARACTER | CHAR_LENGTH | CHARACTER_LENGTH
CHECK | CLOSE | COALESCE
COLLATE | COLLATION | COLUMN
COMMIT | CONNECT | CONNECTION
CONSTRAINT | CONSTRAINTS | CONTINUE
CONVERT | CORRESPONDING | COUNT
CREATE | CROSS | CURRENT
CURRENT_DATE | CURRENT_TIME | CURRENT_TIMESTAMP
CURRENT_USER | CURSOR | DATE
DAY | DEALLOCATE | DEC
DECIMAL | DECLARE | DEFAULT
DEFERRABLE | DEFERRED | DELETE
DESC | DESCRIBE | DESCRIPTOR
DIAGNOSTICS | DISCONNECT | DISTINCT
DOMAIN | DOUBLE | DROP
ELSE | END | END-EXEC
ESCAPE | EXCEPT | EXCEPTION
EXEC | EXECUTE | EXISTS
EXTERNAL | EXTRACT | FALSE
FETCH | FIRST | FLOAT
FOR | FOREIGN | FOUND
FROM | FULL | GET
GLOBAL | GO | GOTO
GRANT | GROUP | HAVING
HOUR | IDENTITY | IMMEDIATE
IN | INDICATOR | INITIALLY
INNER | INPUT | INSENSITIVE
INSERT | INT | INTEGER
INTERSECT | INTERVAL | INTO
IS | ISOLATION | JOIN
KEY | LANGUAGE | LAST
LEADING | LEFT | LEVEL
LIKE | LOCAL | LOWER
MASK | MATCH | MAX
MIN | MINUTE | MODULE
MONTH | NAMES | NATIONAL
NATURAL | NCHAR | NEXT
NO | NOT | NULL
NULLIF | NUMERIC | OCTET_LENGTH
OF | ON | ONLY
OPEN | OPTION | OR
ORDER | OUTER | OUTPUT
OVERLAPS | PAD | PARTIAL
POSITION | PRECISION | PREPARE
PRESERVE | PRIMARY | PRIOR
PRIVILEGES | PROCEDURE | PUBLIC
READ | REAL | REFERENCES
RELATIVE | RESTRICT | REVOKE
RIGHT | ROLLBACK | ROWS
SCHEMA | SCROLL | SECOND
SECTION | SELECT | SESSION
SESSION_USER | SET | SIZE
SMALLINT | SOME | SPACE
SQL | SQLCODE | SQLERROR
SQLSTATE | SUBSTRING | SUM
SYSTEM_USER | TABLE | TEMPORARY
THEN | TIME | TIMESTAMP
TIMEZONE_HOUR | TIMEZONE_MINUTE | TO
TRAILING | TRANSACTION | TRANSLATE
TRANSLATION | TRIM | TRUE
UNION | UNIQUE | UNKNOWN
UPDATE | UPPER | USAGE
USER | USING | VALUE
VALUES | VARCHAR | VARYING
VIEW | WHEN | WHENEVER
WHERE | WITH | WORK
WRITE | YEAR | ZONE

Prev
Reserved Words 		Up
Check for Revisions 		Next
System Tables


System Tables

Pervasive.SQL System Tables Reference

This appendix describes the Pervasive.SQL system tables. For each system table, the following table indicates the name of the associated file and briefly describes the system table's contents.

Note
Some data in the system tables cannot be displayed. User passwords, for example, display in encrypted form.

Table C-1 System Tables
System Table | Dictionary File | Contents
X$File | FILE.DDF | Names and locations of the tables in your database.
X$Field | FIELD.DDF | Column and named index definitions.
X$Index | INDEX.DDF | Index definitions.
X$Attrib | ATTRIB.DDF | Column attributes definitions.
X$View | VIEW.DDF | View definitions.
X$Proc | PROC.DDF | Stored procedure definitions.
X$User | USER.DDF | User names, group names, and passwords.
X$Rights | RIGHTS.DDF | User and group access rights definitions.
X$Relate | RELATE.DDF | Referential integrity (RI) information.
X$Trigger | TRIGGER.DDF | Trigger information.
X$Depend | DEPEND.DDF | Trigger dependencies such as tables, views, and procedures.

When you issue a CREATE DICTIONARY statement, Pervasive.SQL creates the X$File, X$Field, and X$Index system tables and the associated dictionary files. Pervasive.SQL creates the other system tables as follows:

  • X$Attrib—When you define column attributes, Pervasive.SQL creates this table and stores the definitions.
  • X$View—When you define views, Pervasive.SQL creates this table and stores the definitions.
  • X$Proc—When you define stored procedures, Pervasive.SQL creates this table and stores the definitions.
  • X$User and X$Rights—When you set up data security on the database, Pervasive.SQL creates these two tables. In X$User, Pervasive.SQL stores information about user names, group names, and passwords. In X$Rights, Pervasive.SQL stores information about the access rights assigned to users and groups. Pervasive.SQL does not delete these two tables when you disable security. If you enable security again for this database, the users and rights are still defined as before.
  • X$Relate—When you define RI constraints for the database, Pervasive.SQL creates this table and stores information about foreign key references.
  • X$Trigger and X$Depend—When you define triggers for tables in the database, Pervasive.SQL creates these two tables. In X$Trigger, Pervasive.SQL stores information about the triggers. In X$Depend, Pervasive.SQL stores information about the trigger dependencies.

Because the system tables are part of the database, you can query them to retrieve information about the database. However, to update the system tables, you must use data definition statements. You cannot update them with data manipulation statements as you would standard data tables; this may corrupt the dictionary.

Prev
Words to Avoid
Check for Revisions 		Next
Installing System Tables and Data Dictionary Files


Installing System Tables and Data Dictionary Files

The system tables included with Pervasive.SQL contain the data dictionary files for the sample database. When you install Pervasive.SQL, you can copy this data dictionary to the appropriate device on your system and log in to the sample database. After logging in, you can create a new data dictionary in another directory of your choice. Alternatively, you can create a new data dictionary using the Configuration utility in the Pervasive Control Center (PCC) to define a bound or unbound named database.

Prev
System Tables 		Up
Check for Revisions 		Next
X$File


X$File

The X$File system table is associated with the file FILE.DDF. For each table defined in the database, X$File contains the table name, the location of the associated table, and a unique internal ID number that Pervasive.SQL assigns. The structure of X$File is as follows:
Table C-2 X$File System Table Structure
Column Name | Type | Size | Case Insensitive | Description
Xf$Id | USMALLINT | 2 | N/A | Internal ID Pervasive.SQL assigns.
Xf$Name | CHAR | 20 | Yes | Table name.
Xf$Loc | CHAR | 64 | No | File location (path name).
Xf$Flags | UTINYINT | 1 | N/A | File flags. If bit 4 = 1, the file is a dictionary file. If bit 4 = 0, the file is user-defined. If bit 6=1, the table supports true nullable columns.
Xf$Reserved | CHAR | 10 | No | Reserved.

Two indexes are defined for the X$File table.
Table C-3 X$File System Table Index Definitions
Index Number | Segment Number | Column Name | Duplicates | Case Insensitive | Segmented
0 | 0 | Xf$Id | No | N/A | No
1 | 0 | Xf$Name | No | Yes | No

Prev
Installing System Tables and Data Dictionary Files 		Up
Check for Revisions 		Next
X$Field


X$Field

The X$Field system table is associated with the file FIELD.DDF. X$Field contains information about all the columns and named indexes defined in the database. The structure of X$Field is as follows:
Table C-4 X$Field System Table Structure
Column Name | Type | Size | Case Insensitive | Description
Xe$Id | USMALLINT | 2 | N/A | Internal ID Pervasive.SQL assigns, unique for each field in the database.
Xe$File | USMALLINT | 2 | N/A | ID of table to which this column or named index belongs. It corresponds to Xf$Id in X$File.
Xe$Name | CHAR | 20 | Yes | Column name or index name.
Xe$DataType | UTINYINT | 1 | N/A | Control field - Column data type (range 0–26). If value is 227, it represents a constraint name. If value is 255, it represents an index name.
Xe$Offset | USMALLINT | 2 | N/A | Column offset in table; index number if named index. Offsets are zero-relative. Index Number corresponds to the value stored in the Xi$Number column in the X$Index system table. Segment Number corresponds to the value stored in the Xi$Part column in the X$Index system table.
Xe$Size | USMALLINT | 2 | N/A | Column size, representing the internal storage, in bytes, required for the field.
Xe$Dec | UTINYINT | 1 | N/A | Column decimal place (for DECIMAL, NUMERIC, NUMERICSA, NUMERICSTS, MONEY, or CURRENCY types). Relative bit positions for contiguous bit columns. Fractional seconds for TIMESTAMP data type.
Xe$Flags | USMALLINT | 2 | N/A | Flags word. Bit 0 is the case flag for string data types. If bit 0 = 1, the field is case insensitive. If bit 2 = 1, the field allows null values. Bit 3 of Xe$flag is used to differentiate a Pervasive.SQL v7 1-byte TINYINT (B_TYPE_INTEGER unsigned) from SRDE's 1-byte TINYINT (B_TYPE_INTEGER, but signed). If bit 3 = 1 and Xe$datatype = 1 and Xe$size =1, then it means that TINYINT column is created by the SRDE and is a signed 1-byte TINYINT. If bit 3 = 0 and Xe$datatype = 1 and xe$size = 1 then it means that TINYINT column is created by the legacy SQL engine and is an unsigned 1-byte TINYINT. If bit 12 = 1, the field is interpreted as binary.

The column Xe$File corresponds to the column Xf$Id in the X$File system table and is the link between the tables and the columns they contain. So, for example, the following query shows you all of the field definitions in order for the Billing table.

SELECT "X$Field".*

FROM X$File,X$Field
WHERE Xf$Id=Xe$File AND Xf$Name = 'Billing'

ORDER BY Xe$Offset

The integer values in column Xe$DataType are codes that represent the Pervasive.SQL data types. See Pervasive.SQL Supported Data Types for the codes.

Five indexes are defined for the X$Field table, as follows:
Table C-5 X$Field System Table Index Definitions
Index Number | Segment Number | Column Name | Duplicates | Case Insensitive | Segmented
0 | 0 | Xe$Id | No | N/A | No
1 | 0 | Xe$File | Yes | N/A | No
2 | 0 | Xe$Name | Yes | Yes | No
3 | 0 | Xe$File | No | N/A | Yes
3 | 1 | Xe$Name | No | Yes | No
4 | 0 | Xe$File | Yes | N/A | Yes
4 | 1 | Xe$Offset | Yes | N/A | Yes
4 | 2 | Xe$Dec | Yes | N/A | No

Prev
X$File 		Up
Check for Revisions 		Next
X$Index


X$Index

The X$Index system table is associated with the file INDEX.DDF. X$Index contains information about all the indexes defined on the tables in the database. The structure of X$Index is as follows:
Table C-6 X$Index System Table Structure
Column Name | Type | Size | Case Insensitive | Description
Xi$File | USMALLINT | 2 | N/A | Unique ID of the table to which the index belongs. It corresponds to Xf$Id in X$File.
Xi$Field | USMALLINT | 2 | N/A | Unique ID of the index column. It corresponds to Xe$Id in X$Field.
Xi$Number | USMALLINT | 2 | N/A | Index number (range 0–119).
Xi$Part | USMALLINT | 2 | N/A | Segment number (range 0–119).
Xi$Flags | USMALLINT | 2 | N/A | Index attribute flags.

The Xi$File column corresponds to the Xf$Id column in the X$File system table. The Xi$Field column corresponds to the Xe$Id column in the X$Field system table. Thus, an index segment entry is linked to a file and to a field.

The Xi$Flags column contains integer values that define the index attributes. The following table describes how Pervasive.SQL interprets each bit position when the bit has the binary value of 1. Bit position 0 is the rightmost bit in the integer.
Table C-7 X$Index System Table Index Definitions
Bit Position | Decimal Equivalent | Description
0 | 1 | Index allows duplicates.
1 | 2 | Index is modifiable.
2 | 4 | Indicates an alternate collating sequence.
3 | 8 | Null values are not indexed (refers to Btrieve NULLs, not SQL true NULLS).
4 | 16 | Another segment is concatenated to this one in the index.
5 | 32 | Index is case-insensitive.
6 | 64 | Index is collated in descending order.
7 | 128 | Index is a named index.
8 | 256 | Index is a Btrieve extended key type.
13 | 8192 | Index is a foreign key.
14 | 16384 | Index is a primary key.

The value in the Xi$Flags column for a particular index is the sum of the decimal values that correspond to that index's attributes. Three indexes are defined for the X$Index table, as follows:
Table C-8 X$Index System Table Index Definitions
Index Number | Segment Number | Column Name | Duplicates | Case Insensitive | Segmented
0 | 0 | Xi$File | Yes | N/A | No
1 | 0 | Xi$Field | Yes | N/A | No
2 | 0 | Xi$File | No | N/A | Yes
2 | 1 | Xi$Number | No | N/A | Yes
2 | 2 | Xi$Part | No | N/A | No

Index Number corresponds to the value stored in the Xi$Number column in the X$Index system table. Segment Number corresponds to the value stored in the Xi$Part column in the X$Index system table.

To see the information about the index segments defined for the Billing table, for example, issue the following query:

SELECT Xe$Name,Xe$Offset, "X$Index".*

FROM X$File,X$Index,X$Field
WHERE Xf$Id=Xi$File and Xi$Field=Xe$Id and Xf$Name = 'Billing'

ORDER BY Xi$Number,Xi$Part

Prev
X$Field 		Up
Check for Revisions 		Next
X$Attrib


X$Attrib

The X$Attrib system table is associated with the file ATTRIB.DDF. X$Attrib contains information about the column attributes of each column in the database; there is an entry for each column attribute you define. The structure of X$Attrib is as follows:
Table C-9 X$Attrib System Table Structure
Column Name | Type | Size | Case Insensitive | Description
Xa$Id | USMALLINT | 2 | N/A | Corresponds to Xe$Id in X$Field.
Xa$Type | CHAR | 1 | No | D for default
Xa$ASize | USMALLINT | 2 | N/A | Length of text in Xa$Attrib.
Xa$Attrs | LONGVARCHAR (NOTE) | <=2048 | N/A | Text that defines the column attribute.

When you define multiple attributes for a single column, the X$Attrib system table contains multiple entries for that column ID — one for each attribute you define. If you do not define column attributes for a particular column, that column has no entry in the X$Attrib table. The text in the Xa$Attrs column appears exactly as you define it with Pervasive.SQL. One index is defined for the X$Attrib table, as follows:
Table C-10 X$Attrib System Table Index Definitions
Index Number | Segment Number | Column Name | Duplicates | Case Insensitive | Segmented
0 | 0 | Xa$Id | No | N/A | Yes
0 | 1 | Xa$Type | No | No | No

Index Number corresponds to the value stored in the Xi$Number column in the X$Index system table. Segment Number corresponds to the value stored in the Xi$Part column in the X$Index system table.

Prev
X$Index 		Up
Check for Revisions 		Next
X$View


X$View

The X$View system table is associated with the file VIEW.DDF. X$View contains view definitions, including information about joined tables and the restriction conditions that define views. You can query the X$View table to retrieve the names of the views that are defined in the dictionary.

The first column of the X$View table contains the view name; the second and third columns describe the information found in the LVAR column, Xv$Misc. The structure of X$View is as follows:
Table C-11 X$View System Table Structure
Column Name | Type | Size | Case Insensitive | Description
Xv$Name | CHAR | 20 | Yes | View name.
Xv$Ver | UTINYINT | 1 | N/A | Version ID.
Xv$Id | UTINYINT | 1 | N/A | Sequence number.
Xv$Misc | LONGVARCHAR (LVAR) | <=2000 | N/A | Pervasive.SQL internal definitions.

Two indexes are defined for the X$View table, as follows:
Table C-12 X$View System Table Index Definitions
Index Number | Segment Number | Column Name | Duplicates | Case Insensitive | Segmented
0 | 0 | Xv$Name | Yes | Yes | No
1 | 0 | Xv$Name | No | Yes | Yes
1 | 1 | Xv$Ver | No | N/A | Yes
1 | 2 | Xv$Id | No | N/A | No

Index Number corresponds to the value stored in the Xi$Number column in the X$Index system table. Segment Number corresponds to the value stored in the Xi$Part column in the X$Index system table.

Prev
X$Attrib 		Up
Check for Revisions 		Next
X$Proc


X$Proc

The X$Proc system table is associated with the file PROC.DDF. X$Proc contains the compiled structure information for every stored procedure defined. The structure of X$Proc is as follows:
Table C-13 X$Proc System Table Structure
Column Name | Type | Size | Case Insensitive | Description
Xp$Name | CHAR | 30 | Yes | Stored procedure name.
Xp$Ver | UTINYINT | 1 | N/A | Version ID.
Xp$Id | USMALLINT | 2 | N/A | 0-based Sequence Number.
Xp$Flags | UTINYINT | 1 | N/A | 1 for stored statement, 2 for stored procedure or 3 for external procedure.
Xp$Misc | LONGVARCHAR (LVAR) | <=990 | N/A | Internal representation of stored procedure.

 Note
Stored procedures and external procedures were supported in versions prior versions of Pervasive.SQL. Only stored procedures are supported in Pervasive.SQL V8.

One index is defined for the X$Proc table, as follows:
Table C-14 X$Proc System Table Index Definitions
Index Number | Segment Number | Column Name | Duplicates | Case Insensitive | Segmented
0 | 0 | Xp$Name | No | Yes | Yes
0 | 1 | Xp$Id | No | N/A | No

Index Number corresponds to the value stored in the Xi$Number column in the X$Index system table. Segment Number corresponds to the value stored in the Xi$Part column in the X$Index system table.

Prev
X$View 		Up
Check for Revisions 		Next
X$User


X$User

The X$User system table is associated with the file USER.DDF. X$User contains the name and password of each user and the name of each user group. Pervasive.SQL uses this table only when you enable the security option. The structure of X$User is as follows:
Table C-15 X$User System Table Structure
Column Name | Type | Size | Case Insensitive | Description
Xu$Id | USMALLINT | 2 | N/A | Internal ID assigned to the user or group.
Xu$Name | CHAR | 30 | Yes | User or group name.
Xu$Password | CHAR | 9 | No | User password (encrypted)
Xu$Flags | USMALLINT | 2 | N/A | User or group flags.

 Note
For any row in the X$User system table that describes a group, the column value for Xu$Password is NULL.

The Xu$Flags column contains integer values whose rightmost 8 bits define the user or group attributes. The following table describes how Pervasive.SQL interprets each bit position when the bit has the binary value of 1. Bit position 0 is the rightmost bit in the integer.
Table C-16 Xu$Flags System Table Bit Position Definitions
Bit Position | Decimal Equivalent | Description
0 | 1 | Reserved.
1 | 2 | Reserved.
2 | 4 | Reserved.
3 | 8 | Reserved.
4 | 16 | Reserved.
5 | 32 | Reserved.
6 | 64 | Name is a group name.
7 | 128 | User or group has the right to define tables in the dictionary.

The value in the Xu$Flags column for a particular user or group is the sum of the decimal values corresponding to the attributes that apply to the user or group.

Two indexes are defined for the X$User table, as follows:
Table C-17 X$User System Table Index Definitions
Index Number | Segment Number | Column Name | Duplicates | Case Insensitive | Segmented
0 | 0 | Xu$Id | Yes | N/A | No
1 | 0 | Xu$Name | No | Yes | No

Index Number corresponds to the value stored in the Xi$Number column in the X$Index system table. Segment Number corresponds to the value stored in the Xi$Part column in the X$Index system table.

Prev
X$Proc 		Up
Check for Revisions 		Next
X$Rights


X$Rights

The X$Rights system table is associated with the file RIGHTS.DDF. X$Rights contains access rights information for each user. Pervasive.SQL uses this table only when you enable the security option. The structure of X$Rights is as follows:
Table C-18 X$Rights System Table Structure
Column Name | Type | Size | Case Insensitive | Description
Xr$User | USMALLINT | 2 | N/A | User ID
Xr$Table | USMALLINT | 2 | N/A | Table ID
Xr$Column | USMALLINT | 2 | N/A | Column ID
Xr$Rights | UTINYINT | 1 | N/A | Table or column rights flag

The Xr$User column corresponds to the Xu$Id column in the X$User table. The Xr$Table column corresponds to the Xf$Id column in the X$File table. The Xr$Column column corresponds to the Xe$Id column in the X$Field table.

Note
For any row in the system table that describes table rights, the value for Xr$Column is null.

The Xr$Rights column contains integer values whose rightmost 8 bits define the users' access rights. The following table describes how Pervasive.SQL interprets the value. Values from this table may be combined into a single Xr$Rights value.

Table C-19 Xr$Rights System Table Bit Position Definitions
Hex Value | Decimal Equivalent | Description
1 | 1 | Reorganization in progress.
0x90 | 144 | References rights to table.
0xA0 | 160 | Alter Table rights.
0x40 | 64 | Select rights to table or column.
0x82 | 130 | Update rights to table or column.
0x84 | 132 | Insert rights to table or column.
0x88 | 136 | Delete rights to table or column.

A decimal equivalent of 0 implies no rights.

The value in the Xr$Rights column for a particular user is the bit-wise intersection of the hex values corresponding to the access rights that apply to the user. It is not the sum of the decimal values.

For example, the value in Xr$Rights for a user with all rights assigned would be represented as follows:

144 | 160 | 64 | 130 | 132 | 136 = 254

Three indexes are defined for the X$Rights table, as follows:
Table C-20 X$Rights System Table Index Definitions
Index Number | Segment Number | Column Name | Duplicates | Case Insensitive | Segmented
0 | 0 | Xr$User | Yes | N/A | No
1 | 0 | Xr$User | No | N/A | Yes
1 | 1 | Xr$Table | No | N/A | Yes
1 | 2 | Xr$Column | No | N/A | No
2 | 0 | Xr$Table | Yes | N/A | Yes
2 | 1 | Xr$Column | Yes | N/A | No

Prev
X$User 		Up
Check for Revisions 		Next
X$Relate


X$Relate

The X$Relate system table is associated with the file RELATE.DDF. X$Relate contains information about the referential integrity (RI) constraints defined on the database. X$Relate is automatically created when the first foreign key is created, since this results in a relationship being defined.

The structure of X$Relate is as follows:
Table C-21 X$Relate System Table Structure
Column Name | Type | Size | Case Insensitive | Description
Xr$PId | USMALLINT | 2 | N/A | Primary table ID.
Xr$PIndex | USMALLINT | 2 | N/A | Index number of primary key in primary table.
Xr$FId | USMALLINT | 2 | N/A | Dependent table ID.
Xr$FIndex | USMALLINT | 2 | N/A | Index number of foreign key in dependent table.
Xr$Name | CHAR | 20 | Yes | Foreign key name.
Xr$UpdateRule | UTINYINT | 1 | N/A | 1 for restrict.
Xr$DeleteRule | UTINYINT | 1 | N/A | 1 for restrict, 2 for cascade.
Xr$Reserved | CHAR | 30 | No | Reserved.

Five indexes are defined for the X$Relate table, as follows:
Table C-22 X$Relate System Table Index Definitions
Index Number | Segment Number | Column Name | Duplicates | Case Insensitive | Segmented
0 | 0 | Xr$PId | Yes | N/A | No
1 | 0 | Xr$FId | Yes | N/A | No
2 | 0 | Xr$Name | No | Yes | No
3 | 0 | Xr$Pld | No | N/A | Yes
3 | 1 | Xr$Name | No | Yes | No
4 | 0 | Xr$Fld | No | N/A | Yes
4 | 1 | Xr$Name | No | Yes | No

Index Number corresponds to the value stored in the Xi$Number column in the X$Index system table. Segment Number corresponds to the value stored in the Xi$Part column in the X$Index system table.

Prev
X$Rights 		Up
Check for Revisions 		Next
X$Trigger


X$Trigger

The X$Trigger system table is associated with the file TRIGGER.DDF. X$Trigger contains information about the triggers defined for the database. The structure of X$Trigger is as follows:
Table C-23 X$Trigger System Table Structure
Column Name | Type | Size | Case Insensitive | Description
Xt$Name | CHAR | 30 | Yes | Trigger name.
Xt$Version | USMALLINT | 2 | N/A | Trigger version. A 4 indicates Scalable SQL v4.
Xt$File | USMALLINT | 2 | N/A | File on which trigger is defined. Corresponds to Xf$Id in X$File.
Xt$Event | UNSIGNED | 1 | N/A | 0 for INSERT, 1 for DELETE, 2 for UPDATE.
Xt$ActionTime | UTINYINT | 1 | N/A | 0 for BEFORE, 1 for AFTER.
Xt$ForEach | UTINYINT | 1 | N/A | 0 for ROW (default), 1 for STATEMENT.
Xt$Order | USMALLINT | 2 | N/A | Order of execution of trigger.
Xt$Sequence | USMALLINT | 2 | N/A | 0-based sequence number.
Xt$Misc | LONGVARCHAR (LVAR) | <=4054 | N/A | Internal representation of trigger.

A trigger that is long enough may require multiple entries in Trigger.DDF. Each entry has the same trigger name in the Xt$Name field, and is used in the order specified by the Xt$Sequence field.

Three indexes are defined for the X$Trigger table, as follows:
Table C-24 X$Trigger System Table Index Definitions
Index Number | Segment Number | Column Name | Duplicates | Case Insensitive | Segmented
0 | 0 | Xt$Name | No | Yes | Yes
0 | 1 | Xt$Sequence | No | N/A | No
1 | 0 | Xt$File | No | N/A | Yes
1 | 1 | Xt$Name | No | Yes | Yes
1 | 2 | Xt$Sequence | No | N/A | No
2 | 0 | Xt$File | Yes | N/A | Yes
2 | 1 | Xt$Event | Yes | N/A | Yes
2 | 2 | Xt$ActionTime | Yes | N/A | Yes
2 | 3 | Xt$ForEach | Yes | N/A | Yes
2 | 4 | Xt$Order | Yes | N/A | Yes
2 | 5 | Xt$Sequence | Yes | N/A | No

Index Number corresponds to the value stored in the Xi$Number column in the X$Index system table. Segment Number corresponds to the value stored in the Xi$Part column in the X$Index system table.

Prev
X$Relate 		Up
Check for Revisions 		Next
X$Depend


X$Depend

The X$Depend system table is associated with the file DEPEND.DDF. X$Depend contains information about trigger dependencies such as tables, views, and procedures. The structure of X$Depend is as follows:
Table C-25 X$Depend System Table Structure
Column Name | Type | Size | Case Insensitive | Description
Xd$Trigger | CHAR | 30 | Yes | Name of trigger. It corresponds to Xt$Name in X$Trigger.
Xd$DependType | UNSIGNED | 1 | N/A | 1 for Table, 2 for View, 3 for Procedure.
Xd$DependName | CHAR | 30 | Yes | Name of dependency with which the trigger is associated. It corresponds to either Xf$Name in X$File, Xv$Name in X$View, or Xp$Name in X$Proc.

Two indexes are defined for the X$Depend table, as follows:
Table C-26 X$Depend System Table Index Definitions
Index Number | Segment Number | Column Name | Dupli-cates | Case Insensitive | Segmented
0 | 0 | Xd$Trigger | No | Yes | Yes
0 | 1 | Xd$DependType | No | N/A | Yes
0 | 2 | Xd$DependName | No | Yes | No
1 | 0 | Xd$DependType | Yes | N/A | Yes
1 | 1 | Xd$DependName | Yes | Yes | No

Index Number corresponds to the value stored in the Xi$Number column in the X$Index system table. Segment Number corresponds to the value stored in the Xi$Part column in the X$Index system table.

Prev
X$Trigger 		Up
Check for Revisions 		Next