Path to this page:
Home » Tips
CSDA
Tips on
Alpha Five &
Product Tips & Tricks
|
|
Last Updated 20 July
2013
Copyright
© 2012, Computer Systems Design
& Associates, All rights reserved
(linking to this page is OK, posting contents of this page in whole or
part is not OK)
|
(add
your thumbs up to
Google if you
like this page) |
|
(add your
facebook like) |
If
any of these tips are particularly useful to you, or make a big
difference in your Alpha Five application, or you believe any
aspect of what is stated is incorrect, we'd love to hear your comments!
Any
database program, Alpha Five included, is very complex in what it does
and how it does it. Database programs push the hardware, software and
networks much harder than almost every other typical programs people
use, like Microsoft Word, Excel or browsers. So, while developing in
Alpha Five is typically much faster, there are
still things that are very useful to know when developing a database
program so that it runs efficiently, with few errors, and is easy to
maintain.
Below
are tips and tricks on a variety of topics (many of which apply to any
database)
that you should be aware of as you develop an application in Alpha
Five.
Re-read each tip very carefully for it's meaning and
guidelines. And if this is just TMI (Too Much Information) for
you to absorb, we provide consulting, mentoring and training to help
you through these issues.
CSDA
Code Utility for Alpha Five specific tips
- Speed up starting an A5
Desktop with a Database shortcut
- Menus are too
complicated or too large
- Customize Menus
for a specific database
- Restoring messed
up button files
- Lost
customized button
- Accurate
Timing of code
- Quick Backup to send
files
- Modeless
Interactive Window
Alpha
Five General Tips & Information
The Short List
-
(What to do
without the details!) Updated
26 Apr 2011
Otherwise, click below:
- Use functions,
Not scripts Updated 28 Mar 2011
- Code
Editor, Function & Variable Limits Updated 1 July 2013
- Choosing
the right
& fast function
- Alpha
Five Data
Dictionary and XBasic Data Types Updated 31 Mar 2011
- Alpha
Five File Types Updated 25 Apr 2012
- Variable
usage,
dimensioning and deletion Updated 07 Nov 2011
- Field
Rule Recalcs Updated 17 Mar 2012
- Delimiter
characters for CSV Updated 28 Mar 2011
- Files
over 2 Gigabytes
- Why
Network
Optimization (shadowing) does not necessarily improve speed Updated 29 Mar 2012
- Lightning
Query Optimization
(LQO)
- Record
Locking 101
(and how to speed up database usage including
Forms/Reports/Appends/Updates) Updated
13 Sep 2011
- Coding
with
XBasic versus Action Scripts Updated
21 Mar 2010
- AEX
Compiled libraries
- Coding Tips Updated
07 Nov 2011
- Network Issues related
to speed Updated 16 Apr 2012
- Speeding up code Updated
12
Mar 2011
- Isolate Code that
depends on tricks Updated 03 Jan 2010
- Protecting your code Updated
09 Jan 2012
- Using Active-X objects tips Updated
27 Feb 2010
- Bitwise Logical operations
(Binary) with numbers Updated 12 Jun 2012
- Alpha Five hardware
suggestions Updated 15 Aug 2011
- Alpha Five Data
& Index Corruptions Updated 26 Aug 2011
- Compatible DBF/Index
Naming usage Updated 09 Nov 2011
- Paths in Alpha Five Updated
25 Apr 2012
- Variable & Code
Naming warnings Updated 04 Feb 2013
- Regular Expressions for Alpha
Five Notes Updated 11 Sep 2011
- UI_Msg_Box Codes Updated
02 Oct 2011
- Good Alpha Five
Built-in Functions Updated 22 Apr 2012
- Data Security Tips Updated
17 Dec 2011
- Alpha
Five/QReportBuilder Version Release Dates Updated 20
Jul 2013
- Using Dates
in a database Updated 06 Apr 2012
- Windows
7 (and Vista) compatibility Updated 09 Feb 2013
- Theory
of Deduping data Updated 11 Apr 2013
- Easier
Customer Support Techniques Updated 01 May 2013
Links
to Other
Alpha Five Tips
- AIMS
Web site - Alpha Five naming conventions
- AlphaToGo
- various Alpha Five Articles
- LearnAlpha.com
- various Alpha Five articles
- Proctor
& Peake - various Alpha Five tips
- Marcel
Kollenaar's tips on using DLL's with Alpha Five
- Dan
Blank's Tips
- QODBC
- QuickBook interface to Alpha Five tip
Alpha
Five Error Reporting
This Alpha Five add-on utility (which also comes with all CSDA
products) is available with
free
usage for all, except it may not be
distributed with any software package or linked to the downloadable
file on the web without permission or
license from CSDA
.
When
you encounter errors, problems or have wishlist items in Alpha
Five, one of the most important thing is to get a
system
snapshot
of the hardware & software and the configurations of
the
operating system & Alpha Five. It is also important to
categorize
the problem or request. If you've used Alpha Five's built-in
bug
reporting email, you will see it reports, at most, the version and
build
numbers, and optionally can attach a copy of the database, a
very
limited ability. The CSDA DiagInfo can be used to send info to
CSDA,
Alpha Software or other support so hopefully they will have a full
picture of your working environment.
Customized versions of CSDA DiagInfo are
available for developers. Additional features are built into
the
commercial version. Reporting info can be added to or deleted
for
individual developer needs.
CSDA Code Utility Tips
To speed up starting,
create a Desktop Restore shortcut for each specific database
you use.
When you start with the A5vX Desktop Restore, or the A5vX Last
Desktop
restore desktop icon shortcuts, it starts a special database and then
starts the actual database. Just start your database, then
select
the CSDA Code Utility "Create Shortcut" button.
Press OK,
and a new A5 Desktop Restore specific to the database will be placed on
your desktop.
Menus are too complicated or
too
large or have features I never use
Simply edit the dialog box menu for the appropriate dialog type
(Rolldown, Rollup, Minimize, Horizontal dock or Vertical dock) and
remove buttons you want. Do this by pressing the
CSDA Code
Utility "Help" button. At the bottom are various
edit
buttons for the dialog boxes. Just press one you want to
edit,
scroll down to the button lines (near the bottom) and "comment out" any
button you don't want to use by simply putting a single quote at the
beginning of the line to eliminate the button. If you made a
mistake, removing the single quote will restore the button.
After
you made desired changes save the file and exit. Then use any
utility's restart buttons to restart the utility with the new menus.
In a similar fashion you can also add buttons (both predefined and ones
that run your own code) to the menus. Just read the comments
at
the top of the button files and copy similar buttons as a guide and
modify.
Can I
customize Menus for a specific database?
Yes, you can. This is sometimes referred to as "skinning".
Simply select the "Edit A5 Buttons Loc" on the Utility's help
button, and when done, save to the database directory as a file name
"databasename.abt" (Be careful, as Notepad will try to save
it to
"databasename.abt.txt". Rename it if this happens).
This
file has lines that point to where the various other button files are
located, and they could be anywhere in the system, but typically should
be in the default Alpha Five "application" folder, or the database
directory.
Restoring Button files
If you have a problem with the CSDA Code Utility button files that are
not operating properly after changing them, you can restore
the
original ones by either
- Reinstall the utility
- Delete the button files (locations are described in the
documentation)
- Edit the files, delete all text, and save. They
will be restored to their original values at the next restart
Lost
customized button
You lost your customized button code for the CSDA Code Utility
after reinstalling or
updating the utility. Because the button files are subject
to
change from version to version (although they haven't in a long time),
the new versions always use the current default definitions.
However, all of
your old button files are renamed with a date at the end of the file
name.
You can either edit the new ones copying in your older changes, copy
the old ones to the newer ones (with the risk of a format change), or
use a button Location file skin to redirect it to some custom named
files that will not be overwritten.
Quick Backup to send files
When you need to send database files to others (or post on the message
board), here is a quick way with the CSDA Code Utility:
- Make sure the Zip
Backup? checkbox is checked, then
- Press Save
DB++ Othr
- Select additional
files that you want included
This will copy your database, dictionaries and web components, compact
(Pack) them, and then zip them together into a file of your choosing.
Accurate Timing of code
To test code speed, using the CSDA Code Utility, select a code editor
tab and press the Code timing
button and that function or script speed will be tested. It
is
generally best not to have any dialog boxes pop up in the code that
require user responses, as the user response time will be included in
the timing results.
If you are on the Interactive Window (IW) tab, just highlight (select)
some code, and press the Code timing button, and that code will be
timed. It is often best to initialize all needed variables in
the
IW, and then test the key code separately.
Results may vary up to
about 3%, depending upon what windows and Alpha is doing at the same
time. If you run the tests at different times where the
Windows
environment and available memory has changed, your results may vary.
Running different tests right after each other yields a
more accurate comparative timing value.
Modeless Interactive Window
Sometimes you need an interactive window in a modeless dialog, that you
can run code without changing the context of the primary window.
By using the Code Edit button to edit any existing code, you
get
a modeless script editor and an interactive window mode on the 2nd tab.
This allows you to probe variables and run test code without
changing the context of the other windows.
Alpha Five General Tips &
Info
The Short List
(What to do without the details!) (
Email
Comments)
Updated
26 Apr 2011
- Use functions, Not scripts
- Choose Alpha Built-in functions that are written in C, vs
XBasic for speed.
- Dimension all variables in code as local where
possible. If
dimensioning Shared, Global or Addin, make sure variable name is
totally unique to your use (and doesn't conflict with other code of
yours or Alpha's built-in Code)
- Don't delete variables other than those created
within the current
code, and make sure the variable being deleted is dimensioned local
- Never do Field Rule Recalcs
- You can use any Delimiter characters you want between ASCII
1 and
ASCII 255, but in general use ones that have few conflicts between
Ascii 3 and Ascii 31, when you still want to use all the printable
characters and common control characters like CR, LF, Tab, Bell, Escape
- Separate memo fields into a linked table with just the link
and memo
field. For large Memo fields getting close to 2
Gigabytes,
place each memo field into a separate linked table.
- Network Optimization (shadowing) does not necessarily
improve
speed. Use if layouts are reloaded many times and
causes the
network to be max'ed out.
- Design your queries to use Lightning Query Optimization
(LQO)
- Never lock your tables longer than required, eliminate
non-running
totals if possible in reports and check off "Don't Lock Table"
in reports unless really required.
- Use the simplest sets needed for a form or report, and turn
off referential integrity and make child read-only if possible
- Never use Wide Area Networks (WAN/Internet) to access
non-client-server
databases. Use a remote viewing program like VNC, PC
Anywhere,
GoToMyPC, TeamViewer or a Citrix Server (which creates virtual local
computers) that operate on a computer that is on a Local Area Network.
- Change Action Scripts to XBasic after perfecting them, to
forestall unexpected changes to their regeneration.
- Isolate Code that depends on code tricks or side-effects
into a
function that can be changed if the trick no longer works in a future
version of Alpha Five.
- No spaces should be used in Database Names, Index Names
& Field Names. Use an Underscore ( _ ) instead of a
space
- To
allow usage of a DBF table by other programs and to minimize a risk of
extended naming of fields and index names being lost (due to data
dictionary corruption), keep all field names and index names to using
10 characters or less
- Don't
have an extraordinate number of files or folders in any one folder (try
to keep it to a reasonable value (say <1000 per folder), except
be
sure to keep all tables, sets and their data dictionary files in the
same folder associated with the application.
- Drop all unused or unneeded tables and sets from
database list on tha Alpha Five Control Panel
Use a function, not
a script (
Email
Comments)
Updated
28 Mar 2011
When you create code in Alpha, particularly using Alpha Five
Genies, or Action Scripts, you are creating code as a
script. It
may be embedded on a form, or available as a Global Script, but the
result is the same.
Move
the code
off the form (layouts)
The 1st point is to change your code to a global script or
function,
rather than embedding it on a form (or other layout - I'll refer to
them all as forms for this tip).
When you
place the code directly on the form, you typically have access to all
the variables and fields
available on the form. In other words it typically "inherits"
the
variable space of the object it is embedded in. In
addition, "parentform"
is an alias to the current form's name. If you
change the
name of the form and use the alias as a pointer in the code, the code
still works. The
code
is being executed in the context of the form, so has access to all of
those properties, as well as all variables that the form has access to.
However, if you move the code off the form and make it a
global script or function, you may lose the context of the form (which
can be totally eliminated by passing the form's pointer).
Recent testing in A5V5 and up seems that this is not the
case.
Parentform and Parent do seem to work in the context of the
function. However local variables on the form are
accessible
only if you pass the pointer to the function, or retrieve the form's
local variable space by using the parentform or parent alias to
retrieve the pointer to the form's local variable space.
You get these advantages with Global Scripts & Functions;
- Reduce
- You can use the code multiple times from many places, say on Form_A
and Form_B
- Reuse
- It is easier to
grab sections of code to reuse in other code without opening up layouts
in design mode and then finding the needed code
- Recycle
- It is easy to export/import the code and bring it to other
applications where you need to do similar code
- Fix Once
- You only have
to fix it in one place if you have to make a change to correct
something, than to search and change multiple forms (or layouts)
- Find
references - It is easier to search the global code
looking for code, and to change all occurrences.
- Faster to edit
- Each
time a correction is required with code embedded in the form,
you have
to change to design mode, open up the object's event code, edit the
code, save the code, save the form, and retest. In the global
code, for most cases, you just need to change the code, resave and
retest.
- Layout
Corruption easier to recover-
If the form or layout ever gets corrupted (doesn't happen for most,
but some do experience this regularly for reasons unknown), the form
will be easier to recreate, as the code is stored elsewhere
- Protection
- Global
code (except action scripts) can be protected easily by passwords and
can be also be encrypted when compiled into an AEX. The
protection for layouts and their code is much less effective.
Using Script_Play_Local("script_name") allows the global script code to
inherit the space from where it is used, so all local variables of the
form are also available (and changeable) within the script, which
simplifies usage for
beginners.
But this can be also be bad -- VERY BAD! Even if you use
SCRIPT_PLAY("script_name"), you still have access to other
global or shared level variable names that have been defined by Alpha,
or yourself, that you did not know
were being used. This can cause your's or Alpha's code portions to
fail, because of different uses of the same name. See this tip
Variable
usage,
dimensioning and deletion.
Use
functions,
Not scripts!
I've seen people use
100's of scripts to accomplish the same as 1 simple function could do.
The problem with the function is that, in general, if you
don't
use a passed pointer (e.g. local_variables() etc.),
the function does not know the context of the code that called it.
No local variables from the calling code (where the function
is referenced) are available.
Normally this is good for debugging, and the context is not needed. If
it does need a context, e.g. it prints a report of an
ID that
is in the
current layout, then you can pass the ID value as a
parameter. If
you need many variables, you can pass a pointer to the local variables,
a pointer to the layout, or to just your own pointer with lots
of property values.
To help isolate
name usage, change the script code to a global function. A global
function can be created as simple as
FUNCTION script_name as V
( )
' original script code here
END FUNCTION
Then just use script_name() instead of SCRIPT_PLAY("script_name")
wherever you need that code to be placed. Also, make sure
each and every variable used is
dimensioned (prior to the code's 1st use) with DIM (or is in the
function parameter definition line).
See this tip for a discussion of
variable dimensioning.
If you do need the context of the form (layout), in a function, pass a
pointer to the context a parameter, such as
FUNCTION script_name as V
(layoutpointer as P)
' in the use of the function, pass the layout pointer, as in
' script_name(parentform.this)
with layoutpointer
' original script code here, has access and can
change all of layout's variables
' (which can be bad, so use this method
less)
end with
' Or prefix layout' variable name with
pointer (better method)
variablename=layoutpointer.layouts_variable_name
END FUNCTION
However, the function templates shown above are really not the best way
to use a function, but will allow you to quickly move your script code
into functions. The next step is eliminate passing the
local_variables() pointer, and instead pass just
the parameters
you need, combined with a returned value. Here is an example;
FUNCTION SumNumbers as N
(Number1 as N, Number2 as N)
' To use this function, use a line similar to
' sum_of_the_numbers = SumNumbers(1.5,
3.4)
' or put any valid numeric value or numeric expression in the
parameters, such as
' sum_of_the_numbers =
SumNumbers(numeric_variable1 *1.5, numeric_variable2)
' All of these are dimensioned in the function definition line, so they
are already DIM'ed
SumNumbers = Number1 + Number2
END FUNCTION
The key advantages to a Function over a script are
- Faster
overhead - The overhead is less for an XBasic function
call by a
factor of 15 over a script play. There is no good reason for
this, and the reverse is
typically
the case in most computer languages, but it happens to be true in Alpha
Five (and could change in the future, but not likely). It
does not matter much, unless your code's main
execution
time is very small compared to the overhead of the
call, and it is used
many times.
- Easier debug
- Variable usage and changes for locally dimensioned variables have
no effect outside the operation of the function. This makes
problems a lot easier to debug.
- Reuse Code
- It is easier to reuse similar code. If parameters
are
used with the function (and normally should be), it can meet the
requirements of many usages of the code, while adapting to the context
of those values
- Easier
Maintenance - By having the same basic code used
repeatedly, it is easier to
maintain and correct bugs in the code, without having to repair all
code in each use.
- Synchronous
Operation (Minimizes timing issues) - Functions tend to
operate in a synchronous
manner, meaning the
main code waits for the function to complete before continuing.
This tends to solve many race and timing problems people
experience (except for User Interface(UI) displays, which are a
different type of timing issue)
Note that any code including functions can start other asynchronous
threads/sessions, that once started, run
independently. The most common way that this is done
is by opening
another
Form or Browse layout.
- Expression
usage - Global functions can be used in expressions,
anywhere within Alpha Five.
- Simplified
calls to code:
In a script call you might do this
variable_A="1"
variable_B=2
script_play_local("script_name)
result=variable_C
In a function, it would look like this
result=Function_Name("1",2)
- Simplifying complex
problems - changes a complex problem to a
simple call, separating the usage from the implementation, E.g. To find
the distance between 2 zip codes can be
simply a function call like
mileage=Global_Distance(zipcode_1,zipcode_2)
simplifying the actual
flow of code and
yielding greater comprehension of what the code is doing. What
the function does inside may be complex, but using it is
simple.
My development library has over 1000 finished functions I have
developed
over time for many purposes, and exactly 1 finished script (the
AUTOEXEC
script). 'Nuff said? :?)
Code
Editor, Function & Variable Limits (
Email
Comments)
Updated
1 July 2013
There is no practical limit to the # of code library objects
(Scripts, Functions, Menus, Toolbars, Classes & Enumerations,
import operations). The only limitation is the maximum size of
any single file (2 GB windows limitation) and the amount of main memory
available to Alpha Five (currently 3 GB max as Alpha Five is a 32 bit
program). The same would also hold for data dictionary items
(Operations, Forms, Browses and other layouts). There are ways to
get around this limitation, but no Alpha Five user has ever gotten
close (including Alpha Software). There is an additional limitation of
any data dictionary memo file (e.g. adm, sem, ddm) or memo fields file
(fpt) of 10000000000 entries, so even with multiple edits or changes
(and it saves every previous copy that was smaller by 512 byte sizes),
exceeding this number is probably not possible. Compacting the
files eliminates all the old entries.
In some versions of
Alpha Five, there is a limit of functions and scripts that can be
exported or imported, but is always at least 1000 objects of the same
type.
Function Parameter Limits
Functions
can have a maximum of 20 parameters - If you need more than this, you
should probably pass some or all of the parameters with a pointer that
has properties, or as an array of values. You can also pass
the local
space variables pointer (Use LOCAL_VARIABLES() to get local pointer in
the function usage - not in it's definition line, where the function is
used). Addin, Global and
Session (session means shared) variables pointers can be obtained while
within the function with ADDIN.VARIABLES(), GLOBAL_VARIABLES() and
SESSION_VARIABLES(), and do not need to be a parameter. To illustrate
some of these:
Example:
dim a1 as n
dim b1 as n
dim c1 as n
dim d1 as n
dim e1 as n
a1=1
b1=2
c1=3
d1=4
e1=YourFunction(5,d1,local_variables())
ui_msg_box("Final Result","YourFunction returns "+ltrim(str(e1)))
end
' Note that the local_variables() used in the function definition line
below
' only is used if parm3 is not specified when the function is
used above.
' So if the 3rd parameter is specified with local_variables(),
' it uses the variable space of where the function is
used.
' If not specified, it uses the local space of the function itself
FUNCTION YourFunction as N (parm1=0 as n,parm2=0 as n,
parm3=local_variables() as p)
' Initial return value
YourFunction=0
' Add in parm1 value
YourFunction=YourFunction+parm1
' Add in parm2 value
YourFunction=YourFunction+parm2
' Add in calling functions b1 value using passed local variable pointer
as prefix pointer
YourFunction=YourFunction+parm3.b1
' another way to access the calling local variable sapce
' Set default pointer space to parm3
' (this pointer space will be tried 1st, then this function's local
variable space,
' then shared variable space, then global
variable space. Anything new DIMed between WITH and END WITH
' will be in the variable space parm3
with parm3
YourFunction=YourFunction+c1
end with
END FUNCTION
Other Function Limits
Length of function definition line is at least 1200 characters but may
be unlimited.
Internal (sometimes referred to as local function) Function names
(functions defined within a global script or global function) should
not begin
with the same characters as the Global Function or Script Name, as
earlier versions of Alpha (version 9 and earlier) gets confused by
them.
Code
Editor Limits
Code Editor can have maximum of 30 functions or scripts open at a time.
Each code editor tab has a limit to it's size, primarily
based upon a size where CRLF count as 1 character. For
typical
code, it is is about the number of short lines/4 for that version.
When you close the code tab and try to reload, the text at the end is
truncated if it exceeds the limit.
A5v5 10915 short lines, 65323
blank lines, 130641 bytes,
1554 Maximum line length
(before trailing comment - may be a compile limitation)
A5v6 50417 short lines, 196120 blank
lines, 392351 bytes
A5v7 50417 short lines, 196120 blank lines, 392351
bytes
A5v8 50417 short lines, 196109 blank lines, 392351
bytes,
No maximum line length (at
least >6500)
A5v9 99472 short lines, 392320
blank lines, 784750 bytes
A5v10 99472 short lines, 392320 blank lines, 784750 bytes
If
you are using an Action Script "Inline XBasic" action line, it's code
may have limitations as well. If you save and close
the
action script for editing, then reopen it, you may find the Inline
XBasic code was truncated.
Variable
Limits
There
is no practical limit to the number of variables and/or properties
(dimensioned at any scope), nor the size of any data.
A
100 Megabyte variable (string or blob) should not be any
problem.
However, should that variable be passed to a function (by value), then
a copy of it's value will be placed on a stack (a temporary
place). And this occurs for each level if
functions are nested and you are passing the parameter. If the value is
returned, another copy is also placed on the stack. If many copies are
made of the large variable, you may get an Alpha Five "OUT OF MEMORY"
error, which has been seen with 350 Megabyte variables.
In
these cases, it may be best to pass the parameter by reference, or
place the variable as a property of a pointer (which can just be the
local_variables() pointer) and pass the pointer. This will
save
memory space and save the time it takes to make a copy (which is not
insignificant for large sizes, say bigger than 1 Megabyte if used a
lot).
Also, for
large character or blob strings, setting them to a length of 0
after usage will return that space to the available
memory afterwards. Deleted variables will also return
the
memory space. This happens to local variables when a script or
function is closed or shared(session) variables when a session
(typically an open layout such as a form or browse) is closed.
Arrays can have a maximum of 127 dimensions (although doubtful
anyone would use more than 2 in a normal basis).
In
practical usage, it seems as when variables typed as a Collection
exceed something
on the order of 5000 elements, there seems to be unexplained crashes in
A5 (tested on A5V8), so beware if you have large collections.
Alpha
Five Data Dictionary and XBasic Data Types
(
Email
Comments)
Updated
31 Mar 2011
Variable types allowed in XBasic Code
- A=Any Type
- B=Blob
- C=Character
- D=Date
- F=Function
- L=Logical
- N=numeric
- P=Pointer
- T=Time
- U=Collection
- Y=Short Time
- Z=Undefined (Null) Type
- K=UUID (A5v9)
- I=I Type (This type is unknown in it's usage)
Types actually stored in a DBF Table Structure
Non-Memo data field types (as stored in the Database structure)
- C=Character
- N=numeric
- L=Logical (width 1)
- D=Date (width 8)
- T=Time (width 17)
- Y=Short Time (width 9)
- E=ExponentNumeric (width 20)
- K=UUID (width 32) A5v9
Memo field data types (as stored in the Database structure)
- M=Memo (width 10)
- M=Bitmap Memo (width 10)
- M=OLE Memo (width 10)
- M=Rich Text Field Memo (width 10)
- M=JPEG image Memo (width 10)
- M=Image File Reference Memo (width 10)
Path is stored in the memo field, but can display on browse as image
without coding
- M=HTML Memo (width 10) A5v9
Memo field data types (as stored in the Data Dictionaries)
- M=Memo (width 10)
- B=Bitmap image Memo (width 10)
- O=OLE Memo (width 10)
- R=Rich Text Field Memo (width 10)
- J=JPEG image Memo (width 10)
- I=Image File Reference Memo (width 10) (Not sure if L and I
are the same or different)
Path is stored in the memo field, but can display on browse as image
without coding
- H=HTML Memo (width 10) A5v9
- L=Image File Reference Memo (width 10) (sometimes referred
to as a LINK field)
Alpha
Five File Types
(
Email
Comments)
Updated
25 Apr 2012
Alpha Fives stores data in the native format of DBase III. It
actually is closer to the FoxPro format for dBase files, but actually
is enhanced beyond that allowing longer field and index names, as well
as supporting more data types than many other dBase compatible
applications.
This can cause problems with other
applications that use the format. They will see only 10
characters for the field names and index names. Unsupported
data
types may either be ignored, interpreted as a different type (typically
character) or cause a program error/crash.
File and record
locking may or may not be compatible with other applications, meaning
other applications should work with a copy of the data, or by opening
the data files exclusively
Alpha Five File type extensions
- ADB
- Stores list of attached tables, or for shadow database, pointers to
the tables/sets used, as well as libraries attached to the
database. The filename is considered the name of the
entire database
- ALB
- A Dbase formatted data file that generally has the same name as the
database. This stores all the global code entries, Import
operations, database settings
- ALM
- This is a DBase memo field that stores binary and text of the code
entries,
Import operations, database settings etc that are referenced in the ALB
- ALX - This is a Dbase index for the ALB data file
- ASI - Stores web application variables
- DBF - Table data stored in a DBase file, could
also be a mapped Table file
- CDX - Index file for DBF type file of same name
- NDX - Older format of index file, Alpha Five can
use/convert this type
- MDX - Older format of file containing multiple
indexes, Alpha Five can use/convert this type
- FPT - Memo field file for DBF type file of same
name if there are any memo fields used by the DBF
- DDD
- Data Dictionary DBase file type for table of same name.
Stores
Layouts (Forms, Browses, Reports, Letters, Mailing Labels), Operations
(except imports), Field Rules, and field and index name extensions
beyond 10 characters and other table specific info
- DDM - Memo Field file for DDD type file of same name
- DDX - Index file for DDD type file of same name
- DBP - Table and field mapping for a Mapped or
Active Link Table file of same name
- SET - Data Dictionary DBase file type for a set definition
of the same name.
Stores
set definition, Layouts (Forms, Browses, Reports, Letters, Mailing
Labels), Operations
(except imports), and other set specific info
- SEM - Memo Field file for SET type file of same name
- SEX - Index file for SET type file of same name
Other file extensions used by Alpha Five (partial list)
- MUF
- temporary Multi-user file, used for networking license count usage.
If A5 has crashed, may be deleted after all users have exited Alpha
Five (A crashed version may leave Alpha Five running - terminate Alpha
Five in task manager). Recreated if deleted
- RMUF - Same as MUF, except for runtime version.
Recreated if deleted
- ASX - A temporary containing the index info for a query of
a set. This file can be deleted, and will be rebuilt as needed
- ADBLOCK - A temporary file created to allow only
one user
to refresh a shadow database at one time. Only used by
some Alpha Five versions.
- MPX - Temporary file containing the index info from a
query. May be deleted
- AEX - Pseudo compiled file of ALB entries
- LOG - Error and transaction log info files
- BML - Bitmap Image Library File , same as a dbase
DBF file holding blob images
- BMT - Bitmap Image Library File , same as a dbase FPT memo
field file holding blob images
- BMX - Bitmap Image Library File , same as a dbase
CDX file holding blob images
- A5W - A5W HTML Pages, may contain XBasic code, Javascript
code, and A5 web components
- A5WCMP - A5 Web Component
- databasename.WCP_Settings - Stores Web Project settings
specific for a profile
- WebPublishHistory - Stores A5 web publish history to
determine what files need to be published later
- A5PKG - A5 package, a format that stores an exported table
or set layout that can be imported to another table or set
- TABBEDUI - Tabbed UI component of an Alpha
Five Web Application
- files starting in $$ or ending in .TMP are temporary files
used by Alpha Five in it's folders and may generally be deleted.
- A5Startup.log records execution time of various processes
during Alpha Five startup (Ver 6 and up). Verision V6 and V7 place it
in the Alpha Five Exe folder, in V8 and up, it stores it in the
application folder.
- databasename.DatabaseExplorer -
- databasename.username.DefWebProject -
- databasename.tablename.history
- databasename.recent_files
- databasename.script_desktop
- databasename.startup -
- databasename.Network_Optimization_Additional_Files - list
of extra files to copy from Master database folder to shadow folder
- TableOrSetName.find_history
- User_accounts - User Accounts File
- a5t - A5 Theme File
- startup.control - # of users allowed if present
Industry Standard file extensions used by Alpha Five (partial
list)
- HTML Pages (*.html)
- HTML Pages (*.htm)
- CSS Style Sheet (*.css)
- Javascript Files (*.js)
- Bitmap Images (*.bmp)
- JPEG Images (*.jpg)
- JPEG Images (*.jpeg)
- GIF Images (*.gif)
- PNG images (*.png)
- ZIP (*.zip) - Zip files
- XML (*.xml) - extended markup language pages
- CHM (*.chm)- Compiled Window help files
- HLP (*.hlp) - Standard Windows help files
Choosing the right Alpha
Five Function (
Email
Comments)
Alpha Five has over 10,000 functions and methods. Methods are
basically functions associated with an object like forms, browses,
email etc, e.g. form.view(form_name), and can be considered to be a
function. How do you
choose one function (or method) over another? The
main
criteria
are
- It has some capability that another one doesn't have, or
- It is clearer as to what you are trying to do so that
others
viewing the code (including yourself 6 months down the road) could
understand it, or
- It is more modifiable for future changes, or
- It is faster
All of the above are important in selection of a function, but the
one that you can have the most control of is speed. No matter how big
databases are, they will grow over
time to a point where speed will become important. No one usually
cares if something takes 2 sec or 5 sec, and sometimes 1 min
or 5
minutes, but running a report/operation/etc. that takes 10 minutes vs 1
hour is significant (especially if you need an update every 15
minutes!). (Note: No matter how long something takes, once it
exceeds a second or 2, you should display the status or at least a
warning that the process will take a time to complete)
In terms of speed, Alpha Five functions (and methods) seem to
fall
into 4 basic groups
- Key Functions - Provided by Alpha, written in C or those
that are
in optimized function libraries that run very fast for what they do
- Key Xbasic Functions - Those written in XBasic code,
but the
majority of the operational code's time is spent using those functions
of #1. They are typically slower than those written using techniques of
#1 and could be user-written. These could also include XBasic
wrapper code (See #4 below for definition) calling Windows
API,
OLE or Active X functions
- Xbasic functions - Those written in XBasic, where most of
the
time is spent in Xbasic code - typically much slower than #1 or #2,
could be
user-written
- Wrapper functions - These are XBasic functions that call
other
functions of type 1, 2 or 3 and just rearrange or simplify the input
parameters and then return values from a function for easier use
How can you tell the difference?
One way is to use the
CSDA Code Utility's
accurate timer to test samples of code or entire functions and scripts.
This utility basically loops code many times to arrive at an accurate
timing value.
Those in #1 or #2 are generally faster by a factor of 3
to 10 than the equivalent in #3 and #4. Type #4 tends to be just a
little
slower than the equivalent of type #3.
Also, those in type #1 and #2 typically have simpler types of
parameters and inputs than those of #3 and #4. Also they may
have
less error checking of parameter values (e.g. a numeric parameter less
than 0 may fail) and less error error trapping than #3 or #4.
Occasionally, Alpha Software changes a function of type #3 or #4 to a
type #1
because they need it for internal uses or because enough developers
seem to need that speed, so newer versions and releases may be faster.
Re-testing is sometimes required to ascertain this. For code
that
operates on multi-Alpha Five versions, you can occasionally benefit by
using version dependent code.
So in general, stick to types #1 and #2 if it makes no difference. Use
type #3 and #4 if speed is not important. If you need a type
#3 or
#4 and speed is important, either
- Write an equivalent function for your use that might be
faster (I've done this many times), or
- Create a function specifically tailored to the usage
without any bells and whistles (features), or
- Embed the equivalent code in each and every place you need
to use that code that needs the speed.
The last affects readability, but can speed things quite a bit,
particularly where the function's main code execution approaches the
speed of the overhead of the function call.
A new feature (Called Code Count) to be released in an upcoming CSDA
Code Utility update will do counts of various scripts and function
usages in your databases, and will indicate if the code is user XBasic,
Alpha Five XBasic or Alpha Five internal code, and whether
there is any documentation on that function, a useful starting
point.
Variable Usage,
Dimensioning & Deletion (
Email
Comments)
Updated
07 November 2011
Alpha Software's webhelp also has some additional information
here:
Variable
Scope
Variable spaces:
A variable space is one or more
variables of any type grouped such that a reference to just the
variable name will use that name in that variable. Alpha has
a
number of built-in variable spaces, which the main ones are Addin,
Global, Shared (sometimes referred to as session), Layout
(which
is just a subset of Shared) and Local.
The
main difference for Shared versus Layout variables is
that Layout
variables are only accessible from code in the current form, and not
embedded forms or other embedded objects. Shared variables
are
accessible to any object within the current layout window.
Variable space persistence:
- Addin variables remain until you close Alpha Five, even if
you change databases
- Global variables remain until you close the current database
- Shared
remain until the session (or thread - means the same in Alpha) closes.
Each Form, Browse or other layout has its own separate session,
sometimes other processes such as emailing, web server may have their
own sessions (threads) as well.
- Local - Valid only till the completion of the current code
piece (script, action script or function).
If
not explicitly DIM'ed or previously used in the current code, Alpha
will always look for a referenced variable name 1st in
the Local space, followed by Shared, then Global for a
matching
variable name, and fail if it doesn't find it. If not
previously
DIM'ed or assigned a value in the current code, then a new
value
will be placed in the local variable space
Basic guideline rules for variable usage are:
- DIM (Dimension) all variables as having the smallest
"scope"
needed, normally local
- Always DIM (dimension) all variables being used before
their
first use in the code.
- Never explicitly Delete any variables, always use another
name if required
- Global
and Shared Variables should be very uniquely named to avoid collisions
with Alpha variable names, e.g. don't use a Global variable named TEMP
- When variables
that are not DIM (dimensioned) are referenced, Alpha
Five searches the session (also referred to as shared) and global
spaces looking for that name. If they do exist as session and/or global
variables, it will use the lowest scope of the variable found (session
is lower than global), and potentially cause
unintended results, and use and change that value at that
scope.
If
the variable name is not DIM'ed prior to when assigned a
value,
Alpha Five creates a local variable automatically with that
name. A local variable is OK, however, if the same name was
previously created or DIM'ed at the shared or global levels prior to
this usage,
it will get the scope of that one, and use it instead, which
can
cause
unintended changes to that scope's variable.
Since
you do not have
access to Alpha Five's code, this makes debugging
a problem very hard, if not close to impossible. Even with your own
code causing the problem, it can be difficult to find. If
Alpha
Five generates an error (e.g. ,
"ERROR: Variable type mismatch") you might be able to fix it, but if
the same data type you may not even realize the answer is wrong because
you are using a variable name shared with some other code!
The
solution is to make sure all variables (local or otherwise)
are DIM'ed before usage at the desired scope before 1st usage.
Note:
Anything
listed here is just one example. The problem exists
everywhere,
100's of cases could be cited, and can fail even with you own
code, if you don't dimension as suggested.
Don't believe it? Try
this in the interactive window!
(A5v6 through A5v10's
December 21, 2009 build, fixed by Alpha Software after that).
- just one simple example out of 100's that are known)
dim shared woy as n
woy=-5
?woy
= -5
?week(date())
= 16
?woy
= 16
Obviously, Alpha
Five's code does
not dimension the "woy" variable name. Here's another example (The
"delete woy" is to clear any previous usage here for illustration)
delete woy
dim shared woy as L
woy=.t.
?woy
= .T.
?week(date())
ERROR: Variable type mismatch
If the variable doesn't exist at a shared or global level,
Alpha
implicitly dimensions it at the local level, and it is gone
after
using week(), as all local variables are after a function
ends.
But since we created it at a shared level, Alpha used that
one
instead.
- Do not change variable types mid-stream (except for
function return values of type "A" if needed). Use a
different variable instead.
- If
you need to Delete a variable, see if there is another
way to use an alternative variable rather than deleting, e.g. use
TempChar, TempNumb, TempLogical etc rather than one Temp variable
- If you must delete a variable for the very odd problem that
might come up, do it to a local variable, not any higher scope. The
delete command ignores the LOCAL scope, and operates as if it is not
there. For DELETE or DELETE LOCAL, it will always delete the
variable at the lowest level that exists. So make sure it
exists
(by knowing your code already created it), before you delete it.
- Select a variable nomenclature ("naming") method that works
for
you. Some things that work for people are placing a "v" at the
beginning of a variable name to indicate variable, and a letter
indicating it's type (e.g. "C", "N", "D", "L", "B", "T", "S" etc) and
scope ("A" -addin, "G" - global, "S" - shared/session, "L" -local) so a
temporary variable might be named vTempGC)
- For debugging purposes, reusing of a variable in different
pieces
of code makes it harder to know how and where it was last modified (but
it can be useful as a method to obscure what you are doing to protect
your code from others- unfortunately you are often the one confused by
it!)
- Arrays can be cleared or resized rather than deleted.
Similar things can also be done to property arrays
- Global variable use should be eliminated or used
only for
special cases requiring them (e.g. database values used
throughout
your application), likewise for addin variables. One very
good technique is to create one global variable pointer, then
create property name values. Use them explicitly as in pointername.propertyname
or using the WITH/END WITH constructs to reference them without the
pointername. This minimizes interfering with other global
variable names used by other code in your application,
or with functions of other Alpha 5 Code libraries. The longer
and
more unique the pointername
name is, the less likely it will be used
elsewhere.
Typically, I'll take the global pointer and copy it to a local pointer
as in
dim global
my_global_ptr as p
dim ptr as p
ptr=my_global_ptr
dim ptr.mypath as c
ptr.mypath=a5.Get_Path()
So use ptr as a local copy of the global variable pointer,
and mypath
is dim'ed as c (and will remain because the global pointer will still
exist to the variable space after the code ends). I use "ptr" locally
as it is shorter and will go away after the code ends.
While
not quite as critical, Session (shared) variables should be
done in a similar fashion to the global variables as Alpha
Five
does use shared variables. Some more common names used by
various
functions and commands are; varC_result, formname, fieldname,
html_text, profile, target, color, pointer_name, prompt_variable,
timer, errortext
- This may or may not be very important,
but consider it
a cardinal rule for debugging. Never change your input
parameters
within a function (unless passed by reference (BYREF), or is a
pointer).
While Alpha allows this, this makes debugging more difficult as
you lose the original calling value when it is modified. And
it
is possible, that if Alpha has an issue with your code, it is more
likely to crash your code (as it might corrupt the XBasic stack).
Worst case scenario is that you must assign the parameter
values
to local variables initially, slowing code down a small
bit. Except for very large strings or blobs (which you could
pass
by
pointer for speed), this is a small price in speed to pay for potential
reliability and debugging ability
- Do
not use system/global variables for Operations like query.filter,
query.expression query.options, and similar before calling
the operation if there is a function that you can pass the
parameters to.
In most cases, this is not an issue if you
don't have that operation running (possibly from another form or
report) between the settings of the variables and the actual operation.
So, the closer the setting of these system values are to the
operation, the better. It is best to set some local variables
and
then set the system/global variables immediately before. E.g.
dim Flags as C
dim Filter_Expression as C
dim Order_Expression as C
dim qry as p
Flags="N"
Filter_Expression="Age > 25"
Order_Expression="LastName+FirstName"
' Do these assignments just before the query_create()
query.options=Flags
query.filter=Filter_Expression
query.order=Order_Expression
qry=tbl.QUERY_CREATE()
' now use the query pointer to get record counts etc.
dim record_count as n
record_count=qry.records_get()
But
if you have an operation that allows parameters, this is a much better
way to do it and does not risk the chance of the variable being
changed. E.g., using the same basic code from above,
dim Flags as C
dim Filter_Expression as C
dim Order_Expression as C
dim qry as p
Flags="N"
Filter_Expression="Age > 25"
Order_Expression="LastName+FirstName"
qry=tbl.QUERY_CREATE(Flags,Filter_Expression,Order_Expression )
' now use the query pointer to get record counts etc.
dim record_count as n
record_count=qry.records_get()
Dimensioning styles
There are 2 styles typically used for dimensioning variables.
They are
- Dimension all variables at the very top of the code.
This
guarantees that all dimensioning occurs before 1st usage.
There
is a very small overhead with dimensioning.
- Dimension all variables just prior to first usage.
This
eliminates unneeded overhead of dimensioning, and makes sections of
code more independent (for copying to other code). However, the
variable may not be dimensioned in the path the code took, so if not
executed prior to the first usage, it is a potential coding
error waiting to happen.
Dimension
as Local
If a variable is not DIM'd, and you don't reference an
explicit variable space
(addin, global, shared and local), the current most limited space (in
Global, Shared or Local) that
is DIM'd is used for references and assignments.
If not DIM'd at any
level, then there is implicit DIM as a local variable (or at the space
as defined by the WITH/END WITH command directive) upon 1st use.
Note
that as of Jan 1, 2010 an explicit "local" DIM, as in "DIM LOCAL
varname AS type" does not work. So to dimension at the local variable
space (or at the space
as defined by the WITH/END WITH command directive), you must do a
"DIM varname AS type".
In a similar way DELETE LOCAL varname does nothing, but DELETE varname
deletes the lowest level
space defined
DIM GLOBAL, DIM SHARED, DELETE GLOBAL and DELETE SHARED do work though
If
you use a property pointer, it will use the scope of that pointer.
So
you could force a local DIM or delete by doing something similar to
(substituting the variable name for varname and the type below);
dim localptr as P
localptr=local_variables()
DIM localptr.varname as type
' for each DIM needed at the local level
DELETE localptr.varname
' for
each DELETE needed at the local level
You could do similar things at other levels with
- SHARED (use session_variables() instead of local_variables)
- GLOBAL (use global_variables() instead of local_variables)
- ADDIN (use addin.variables() instead of local_variables -
notice the period, not underscore),
Cautions
using SCRIPT_PLAY_LOCAL()
Dimensioning variables as local helps eliminate un-intended
side
effects, however, if you create scripts (action or XBasic) and use
SCRIPT_PLAY_LOCAL() to execute it, it operates in the same variable
space as the place it was invoked from.
For your code, you know what variables you are using and can avoid
naming reuse.
However, Alpha Five
uses many cases of
SCRIPT_PLAY_LOCAL() in it's code generated by Genies and Action
Scripts, so there is a potential (although relatively small
chance) of
their being a conflict of names between what you use and what Alpha
generates at the local level.
Xdialog
Variable Spaces (Updated 30 Mar 2010)
XDialogs inherit the local variable space from the code they were
started from. This means any variables dimensioned locally
prior
to starting the XDialog will be available in the XDialog. And
of
course, as before, any references to variables that are not found
dimensioned locally, will be checked in the shared variables and then
the global variables.
Some of the controls in XDialogs, e.g. ListView objects, use an Active
X Control (another program essentially). They are implemented
in
ways that require some variables to be dimensioned at
the shared
(session) level, as those Listview events are running in a different
variable space and must have access and set some values from
the Xdialog variable spaces.
Minimize
Variable Name collisions with code not your own
If you need to prevent conflicts (collisions) with creation
and deletion of variables with the same
name at different scopes (particularly at the shared, global or
addin scopes), by code that is not your own (e.g. Alpha Five
built-in functions, code generated by action scripts and genies) create
a uniquely named variable
space pointer (possibly with a GUID or time stamp in the name), and
reasonably long to be unique to a maximum length of 23 characters
(Alpha can use 24, but has had some places that only saw the 1st 23)
and
then use that pointer to create property values (variables)
for all the ones required at that scope.
That's what is done in the
CSDA Code Utility. Version
2.69 and after, creates 1 global and 1 addin pointers and no
other top level variables in
the entire program. The name is chosen to be long and not common to
eliminate possible conflicts with other programs. Many
variables that are needed by many aspects of the program are stored
under those pointers as properties.
Why you shouldn't delete a
variable, nor have to
The reason for not deleting a variable, and for that matter using a
variable from any other session (also referenced as the words process
or thread in Alpha Five - a layout like a form or browse always runs in
it's own session), is that it has an independent
purpose and timing.
In most cases, when you create a variable, you intend
on using it and for that particular variable type. Its use should
have a determined purpose, and does not need to be redefined to
another type. It would be better to create another variable of
the type
needed, e.g. tmpC, tmpN, tmpL etc. for Character, Numeric, Logical etc.
E.g. two forms create a global variable "tmp". One set's it to "text",
the other deletes it and sets it to the number 2. This will cause an
unintended side effect (read as failure) in the other form. Just
changing the value alone will cause the other form to fail what it was
doing, unless you intend it for use as an inter-thread communications.
In that case usage must done in a way that allows for proper
operation, and that one process does not receive a partial message (A
whole other subject to discuss!)
When a session finishes, all the variables at the session (shared)
scope and
local scope are
released (deleted). Similarly, when a function or
normal
script terminates, the local variables for it are automatically
deleted.
For Global or Addin variables, they persist. Global variables
remain until you close the database, Addin variables remain until you
close Alpha Five (even if you open another database). If you
must delete the global or addin variable, try to do it in the
same
session that created it (and the same applies to variable writes,
although it is generally OK to read from elsewhere). If not,
try
to make sure, by program design, any other session
that
writes or deletes it, is not executing at the same time.
If you need to change variable types, e.g Character and Numeric, the
only real
place I've seen a true need for this, is in a function's
return value
where the return value is type A (can be any type).
E.g. MAX() function works with numbers, characters,
dates, and the return value is the same type as the inputs.
You might start with an initial value of -1 to indicate
error, but for some cases, you might be returning other types. If the
function code works in a way that you only need to set the return
variable once for any particular exit, then you can set it for the 1st
time for that exit path, and not have to delete the previous usage at
all, as there wouldn't be any
Issues
with Action scripts using DELETE
Alpha Five action scripts don't assume any knowledge of other code
that's
been used previously (including the same action script action used on
another line) and thus deletes the variable to make sure there is no
conflict. This carries the risk of deleting another variable at a more
"global" scope that might have an identical name (unlikely for Alpha
script naming, but it could happen) and being currently run.
It
could also delete a variable that you named that had the same name.
Making sure your code dimensions everything as local
eliminates this issue. If a variable can not be
dimensioned as local, make sure it has a unique name.
Special Cases for using DELETE
You might be able to come up with some code examples that do need to
delete variables, but those truly requiring it are going to be very
unique in their purpose.
One example of this is the Code operations of the
CSDA Code Utility.
This has the ability to run other people's code for testing purposes
and can't know what the potential return values types are going to be.
Thus,
there is exactly 1 delete of a variable in the code. And since it's a
"local" variable, the code that created it is the same as the one that
is deleting it, a safe operation.
Referencing
Variables at other levels
Even if variables with the same name are defined at more than 1 level,
you can reference these higher scoped variables, and test for their
presence.
' They can also be
referenced by creating a pointer
dim localptr as p
dim shareptr as p
dim globalptr as p
localptr=local_variables()
shareptr=session_variables()
globalptr=global_variables()
value1=localptr.varname
value2=shareptr.varname
value3=globalptr.varname
' They can also be referenced directly by prefixing the function on the
property name
value1=local_variables().varname
value2=session_variables().varname
value3=global_variables().varname
' Above does not work with A5 directives (commands) like DIM,
DELETE, so do it this way
DIM GLOBAL varname as C
DIM SHARED varname as C
DIM varname as C ' local DIM
' or
DIM globalptr.varname as C
DIM shareptr.varname as C
DIM localptr.varname as C
' for DELETE
DELETE, so do it this way
DELETE GLOBAL varname as C
DELETE SHARED varname as C
DELETE varname ' deletes lowest currently defined
scope of varname
' or
DELETE globalptr.varname
DELETE shareptr.varname
DELETE localptr.varname
If you want to test for a scope of a variable, these examples will
return logical values
logicalvalue1=eval_valid("global_variables().varname")
logicalvalue2=eval_valid("session_variables().varname")
logicalvalue3=eval_valid("local_variables().varname")
Don't use Field Rule recalcs (
Email
Comments)
Updated
17 Mar 2012
The recalc of field rules
applies to all field rules, not just specific ones of interest. It's an
all or nothing proposition (for the most part). In addition, a recalc
requires exclusive use for it to work, so if any one else has the table
open, it will fail
Suppose you have a field rule with a field with a default value of
DATE() holding the record create date (some of these might be
blank from records imported). Another field has a calculated
field of DATE() holding the date record was last modified (by a human,
not a program).
If you do a recalc of the field rules, the blank create dates might be
set to the current date, and the last modified date will be
changed to the current date, not what was necessarily intended.
The field rules are intended as a set of rules to be applied while
entering and changing data by human input (exception probably being
auto-increment). The rules are applied to the record based upon the
current values of the database at the time of entry or change.
If you have any field rule that depends upon date or time, or the
current state of other records at the time, then a recalc of field
rules totally invalidates the conditions that they were when they were
created originally.
If you must perform an operation like the field rules, then run a
global update or other operations that take in consideration of what
must be done to get the correct results.
In the above example you, would not global update the create date, and
may or
may not global update the last modified date, depending upon intended
results.
Also, using variables in a field rule is generally a bad
idea. If
the variable is undefined or uninitialized, the field rule will not
work. The typical way to initialize a variable is
via an
Autoexec script or in a form or browse layout's initialization event
code. This does not happen if you open the table without the
autoexec, or open a form (e.g. the default form) where there is no
initialization event code.
So, to sum up,
- Field rules are primarily for human input to tables/sets
only
- Do not use field rule recalc except to test development
copies
- Recalc can only be done if you have exclusive use of the
table, so if others are using the table, it will fail
- Use update operation or other code for other changes
similar to field rules
- Don't use variables in Field Rules unless they handle the
case when the variable is undefined or uninitialized
If you look at the descriptions of the general method and table method
of recalc and this
link,
it is highly probably that they are using one of those functions to
perform the recalc. Based upon the documentation of the link, it
implies a wide variety of effects besides calc fields. Without testing
intensively, it might be hard to prove that it doesn't affect other
rules, although proving it does affect other rules means finding just 1
or more, which might come out in testing quite quickly.
Based upon the documentation of the functions and my link and that
there seem to be no other functions applicable, I am guessing that
recalc of calc fields is really recalc of field rules. This also agrees
with historical history of the recalc process as well.
Setting create/change dates and times
Setting a creation date/time into a data field is best handled as a
field rule default value that is set "at start of data entry" - that
way no update action will change it.
Setting a last modified (change) date/time into a data field is best
handled with a CanSave Record action (in the field rule table events)
so that no update action will affect it. However, any xbasic
code must specifically update the change date/time field(s)
when needed since the Table CanSave field rule event
isn't triggered when adding/editing records with xbasic. So
the Xbasic code might need to include something like this:
table_pointer =
table.current()
table_pointer.change_dtf = date()
table_pointer.change_tmf = left( time(), 5 )
Delimiter characters
for CSV (character separated values)
(
Email
Comments)
Updated
28 Mar 2011
Normal choices for CSV lists are comma, Carriage Return/Line Feed, or
Tabs. Vertical bar ("|"), space, colon and semi-colons are
also
used often. But whatever character (or character string) you
choose, there is a chance
the user could have text with that character.
But since Alpha can have any value from ascii 1 to ascii 255
(chr(1) -> chr(255)) in a text string, use a chr(x) as the
delimiter, where x is a
value between 1 and 31 with the exceptions of 7 (beep), 8 (backspace),
9 (tab), 10
(line feed), 12 (Form Feed), 13 (Carriage return), and 27 (escape).
Alpha Five also uses values 1 (Start of Header), 2 (Start of Text), and
11 (Vertical Tab)
in some of their code, so avoid these as well.
The
value 28
is a
good choice, as it is not common to find a binary "00011100"
in
text or binary files, but any of the unused values from 17 up to 30
(don't use 31, two many consequtive ones in the binary) are good as
well. In most cases, set a variable name,
such as
chr28 to the value, as in
chr28=CHR(28)
or
CSVchr=chr(28).
Now use the variable name, which allows you to
change the
CSV character to another value quickly without having to edit all
instances where it is used.
If you don't have
characters greater than ASCII 127, then you could also choose ASCII
values from 128 to 255, although these are not as portable in other
languages, so these are not recommended.
To create a string
with your new delimiter, it can sometimes create extra complexity, but
here are some examples with lookups which are generic, but all end up
as the same
return expression.
- lookupc("F",keyvalue,"field1+chr(28)+dtoc(date_field)+chr(28)+trim(Addr_field)",
"database_name","index_name")
- lookupc("F",keyvalue,"field1+chr("+str(asc(CSVchr))+")+dtoc(date_field)+chr("+
str(asc(CSVchr))+")+trim(Addr_field)","database_name","index_name")
- lookupc("F",keyvalue,"field1+\""+CSVchr+"\"+dtoc(date_field)+\""+CSVchr+
"\"+trim(Addr_field)","database_name","index_name")
What to do if your database
files are 2 Gigabytes or larger (
Email
Comments)
If you have a database data file (*.dbf) or memo field (*.fpt) that is
approaching the 2 gigabyte maximum allowed size for these formats, you
must get all files under the 2 Gigabyte limit.
The solution is to segment each memo field of the table into separate
tables with links back to the main table holding all the other
non-memo fields. If you are exceeding 2 Gigabytes with the main dbf
file, then separate fields into more than 1 table with a link.
Alternatively, use an SQL database if you are able to.
Why Network Optimization
(shadowing) does not necessarily improve speed
(
Email
Comments)
Network Optimization (shadowed) makes a copy of all layouts (such as
forms,
browses,
reports), operations (like exports) and user global code, toolbars and
menus to a faster location
(typically a local drive) in attempt to minimize network traffic of
these. Data is still accessed from the "server".
If
the shadowed copy is located on the same drive as the database
application, it serves no purpose, as it still needs to read it from
the same drive (and presumely wouldn't be any faster).
However. only if more than one system's access of layouts, operations
or code
interferes with the network traffic of another, does the network
optimization become useful from a global network performance of all
systems. Any
local
increase in speed is seen only for those applications that
open forms, browses and other layouts, or run Alpha operations
repeatedly, or use global code entries.
A "compiled"
AEX file, stores your code entries, toolbars and menus.
They
can be loaded automatically from the database folder if it has the same
name as the database, or from the addins_installed folder located in
the Alpha Five exe folder, which is generally on the local drive.
It can also be loaded via XBasic commands as well.
In any
of these cases, the compiled code is loaded once, and then all of it's
contents are stored in main memory. If using a shadowed database, it
will be loaded from the shadow directory if it has the same name as the
database. So to minimize network overhead, it is better to be
located on the local drive.
Lightning Query Optimization
(LQO) (
Email
Comments)
Updated
29 Mar 2012
Lightning Query Optimization (LQO) is a process of Alpha Five
that happens automatically whenever you apply any filter or
query
to a DBF table (except for the filter inside an index definition). It
speeds up query time by creating a dynamic range to an index.
Back in Alpha Four, (and actually is still doable in Alpha Five), you
might do a search (query) by setting a range that reduced the number of
records that needed to be searched.
E.g. If you wanted only people whose last name began with S, your range
for an index whose expression was
LastName+FirstName
would be "S" through "Sz". Alpha would only access those "S" records.
In order to be more generic than a range, LQO takes a part of the
expression of the filter (e.g
Between(LastName,"S","Sz)
and looks to see if there is an index that uses the field of that part
of the expression at the beginning of the index expression. It then
tries to internally set a range that restricts the range of records
that need to
be accessed, and then applies the filter to only those records. So if
your filter expression was
(FirstName="John") .and.
Between(LastName,"S","Sz)
then ideally it would search the records only starting with "S".
Unfortunately, LQO isn't super intelligent. You need to help it a bit
to find a matching index. It is best that the beginning part of the
filter expression that you want to match to an index rearrange the
filter terms to either start with a matching equality (or >= or
<=) statement or use a Between() with constants for the 2nd and
3rd
arguments, as in
Between(LastName,"S","Sz)
.and. (FirstName="John")
or
LastName="Smith" .and.
FirstName="John"
So for a last name search, Alpha Five would be just as happy with index
expressions of
LastName
LastName+FirstName
LastName+FirstName+Zipcode
It may not work with things like
trim(lastname)+trim(firstname)
LQO may also not work with query expressions that use expressions that
are complicated inside a between() like
between(trim(lastname),"S","Sz")
between(lastname,BeginValueVariable,EndValueVariable)
But it may work with
between(lastname+firstname,"S","Sz")
Different versions of Alpha Five may be better than others in using LQO
for more complicated expressions, but in generally, make sure it
happens by keeping the expressions simple.
LQO will not help (and could slow it down a bit) if the range of
selected records is a large percentage of total records. This is not
typical, but happens if you create filter and index expressions
incorrectly or reference a large portion of the table.
When you get the LQO right, your speed of accessing records for large
DBF tables will increase a lot. That's the only way to really tell if
LQO
is happening (unfortunately it would be nice if Alpha had an LQO
evaluator which would show you when it would actually happen)
Record Locking 101
(
Email
Comments)
Updated
13 Sept 2011
(and how to speed
up database usage including Forms/Reports/Appends/Updates)
In databases that are not "True" client-server databases, which
includes Alpha Five's DBF file format, as well as Filemaker Pro, and
Microsoft's Access (typical usage) and many others, there is a process
that happens with each access of data that is referred to as
"Locking". Since each client computer is actually writing to a
portion
of a
shared file someplace on a network, this needs to occur so that data is
not used that is only partially changed.
Imagine that data was in the process of being written to the shared
file and you read the data at a point when only part of the
data
had been written. This would give you a mix of the old and
new
data.
To prevent this, writing and reading of a
record's data must be
performed
in "Atomic" operations (one that can not be interrupted during the
process). This includes parent records and every child record
that
is linked.
Locks are performed by the
operating system.
Alpha sends network commands to lock and unlock files and
records, but the operating system actually performs the operation at
the server (where the file is located).
This means that different versions of the operating
system
may do this better than other versions. It is also possible that
default operating system settings may be different, e.g. Windows XP vs
Windows 7, which means that you might get better performance on one
over the other. There may be settings you can adjust
(typically
registry settings), but may not be documented. If you are having a
speed issue in Alpha associated with networking & locks, see
the
tip under speed
here
dealing with opportunistic lock settings.
Each
lock has an associated time that it takes, and that
time can stack up when you consider how many simultaneous file and
record lock requests are being made by users. Since it is atomic, every
lock request by
other users
and processes
must wait
while this happens until it's turn comes up. If
using referential
integrity, Alpha enforces write locks on the children records as well
(referential integrity does not
actually require this child locking, but is the way that it is
implemented in Alpha Five). This atomic
operation generally goes something like this;
- Request and wait for a "file
lock" on all relevant data files
of
the database (typically, data, indexes, memo). Some allow you
to
cancel this step if waiting
- After receiving a file lock, request a
"record lock"
for each
of
those files for the needed records. There
can be read record locks and write record locks for
the
respective needs,
or they may be identical depending on the operating system.
Reads are typically faster than write locks, as the
writes
have to read the original sector(s) on the server hard drive, modify
the data on the record part, and then write back the sectors.
- Release the file lock while keeping the record lock.
The record data can now be used or changed. When done working
with the specific records, the reverse process must be done to release
the locks. For a read, the data is generally read quickly and
released.
For an edit of a record (and its children records for some databases),
it depends on the database program.
Some release
it
after a quick read, then later repeat the process to write the final
changed data. If more than 1 process is editing the record,
and tries to write the same
record, the last values written generally is the one that remains. This
is referred to as optimistic locking.
Others,
such as
Alpha Five, hold the write lock on the record forever until the changed
data is
saved, which means no other process can be editing the same record.
This is referred to as Pessimistic locking. Should the computer that
has the record lock have a program crash, the lock still remains,
meaning no other computer system can edit that record. In
some systems, this lock will timeout, in others it will not.
Alpha Five does not have a timeout, but the operating system might. If
it does not release the lock, rebooting the computer and opening that
database on the computer that crashed will clear the lock.
The release process goes something like this;
- Request and wait for a file lock on all relevant data
files of the database (typically, data, indexes, memo). Some
allow you
to cancel this step if waiting
- After receiving a file lock,
release the record locks.
- Release the file locks
These atomic locks and releases are sent by the operating
system
to the computer where the data is stored. It is fastest if
it's
the local drive (microseconds), a bit longer on a Local Area Network
(LAN), and can be seconds over a Wide Area Network (WAN) (such as the
Internet).
Usage on a WAN will slow a database to being
unusable.
However, there is a solution. Using a remote program
like
PCAnywhere, VNC, or a Citrix server, they move the programs operation
to the LAN or local computer, and essentially just send an image and
allow control of the program remotely over the Internet.
In some cases, the database program may actually hold the file locks
for multiple accesses of record data, say 50 at a time. While
the
file lock is asserted, anything can be done. Then releasing
the
file lock so other processes can use it. This might be done
for
generating a report, or appending data etc. to lessen the impact of
many record accesses, as the lock process has a fairly high overhead
compared to reading and writing records. Some database
programs
may allow you to explicitly control this to "Batch" process
data,
however, Alpha Five does not. In Alpha Five, you can open
tables
with exclusive use if no one else is using it, but this would cause
other processes to fail that tried to share the data.
In SQL and other True client server databases, the same lock
processes basically happens, except now 1
computer controls
the entire database. No file locks or file unlocks are needed, and the
computer manages what amounts to the equivalent of a record lock.
Any requests to read and write data go to the server, who
honors
the request to the data and sends back the data read, or updates the
database with the new data. This gets rid of the time
required to
get and release locks over a network, speeding access to the data.
However this comes at the cost of requiring more work for the server,
that was previously being done at each computer. The server can allow
multiple workstations to open the same record for change, and when the
data is presented back, can manage which of the workstations'
data
will be accepted for changing, and/or flag differences between the
multiple data being written, and manage those differences.
There is one other advantage of a True client server
database. In
a non-True client server database if a workstation crashes during
the lock/unlock process, or, if on a WAN just happens to have a loss of
communication, it can stop every computer using the database.
If it crashes while a record write lock is present, no one
can
change that record.
In most cases, these locks are released
after
the computer is rebooted, and in some systems (but not Alpha Five) may
have some automatic time-out. But in a True client server
database, the client workstation crashing just loses the data it was
working
with, and the server continues normal operation.
Alpha
Five specific locking info tips
Disclaimer: Many
of the items
listed below could change between versions (or even patched updates) of
Alpha Five. They are guidelines and should be verified for
your
usage.
Normally, for most normal record accesses in a shared environment, A5
locks (arbitrate might be a better word here) records (and indexes) on
a record-by-record basis. If a record change causes a change to a
record, or when a new record is entered, A5 performs a very short
indivisible reading/writing of the tables and indexes that for that
short time (typically milliseconds on a LAN), you effectively
have exclusive
control of the database "files" (tables/indexes), but not of the
individual records locked by other users/processes changing another
record.
If the table is being used by another process/user, (in shared mode of
the table) you can perform all
the normal single record type XBasic commands (except writes to records
currently locked for writing).
Same thing is true for the more global operations like append, copy,
crosstab, export, intersect, join, post, subtract, summarize and
update. The most interesting cases most would use are the append and
update. In these, the table's indexes are potentially modified.
Shared
vs Sole
Use during Appends and Updates
A5 has 2 different modes of operation for this kind of case. If the
table is in use by another, it will update the index as each record is
modified. No problem sharing here.
The 2nd way, if it determines it has sole use (still could be in share
mode though), will do the operation and then rebuild the indexes. This
temporarily locks (for what could be a long time, mind you) the tables
from all other users. They will get a locking error with an automatic
retry. As soon as A5 finishes the re-index (and it only rebuilds
indexes that use fields that have been modified in the append or
update), the file locks are released and the other users go on their
merry (although a little miffed for someone locking them out for
potentially a long time!) way.
Why do it this way you say? If the majority of records are going to be
modified, it is much faster to update the indexes at the end of the
operation. This can backfire when the number of changed
records is small compared
to the total size of the table. Assuming you are the sole user, where
the break even point is, I've never actually tested, but I suspect it's
something about where you change about 40% of the records.
To force Appending/Updating to use the shared method, you can
not
count on other users also using the table. To force this,
open
the table in a different session. It is easiest to just open
a
form (which can be hidden) while doing the operation, and
close
the form afterwards.
Some code that does it
frm=form.load("tablename")
update.run_silent("updateoperationname")
frm.close()
Tests on a
35000 record file had an update take about 11 seconds in the above
method, versus 19 seconds for the index rebuilds happening at the end
(index expressions are very complex for this table) when the table is
available exclusively.
Is
the overhead
of an Update operation slower than parsing through the records?
For large numbers of records where either the index rebuilds don't take
long, or using the method shown above to update indexes on the fly,
normally the answer is yes. For under 10 records, it is probably faster
with code, with 10 to 100 records probably being the cross over point.
It's even possible to use both methods, depending upon # of records to
process, if the value changes dynamically.
Force LQO where possible
Depending
upon your options selected in operations, the 1st thing it might do is
perform a query
of the source or destination tables. Make sure your filters will invoke
LQO (Lightning Query Optimization), otherwise you will be doing a slow
query if the query results include almost every record of the
table. Each read of the record for a query requires record
locks.
Accessing less records (by using LQO) will reduce
locking/unlocking requests on the network.
Open
and Close
of Tables
The Opening and closing of a table, either explicitly by code, or
implied by the code being executed is a fairly long process over a
network. (typically 25 milliseconds). For some code, e.g
lookupc(), lookupn() and similar, they actually are much faster if the
table is already open (at least in more recent versions of A5).
This implies that they are using an already opened table, to
save
time. If you are performing a lot of these operations, it is
best
to open the table before the lookups, and close after all are done.
To check which functions might use an already opened table, try the
code with a table open, and without, and measure the speed.
Of course, you should always close tables the code
has opened on the way out of code for cleanliness (and more to not
leave a file open in the operating system - you can't be sure Alpha
will do it for you!)
If
you have opened a file (file, table), Alpha Five will normally close it
when the current code ends. If you want it to stay open even
after the code completes, use the table persist method
Report
Locking
Alpha Five locks reports for the whole process while generating the
report, unless you take specific steps to not have this happen.
A
report that either doesn't check the "Don't Lock Table" box on
the
report properties,
or
uses summary values that are not of the running type, or has widows and
orphan turned on anywhere will lock the
table, sometimes for a relatively long time. Having the "Don't Lock
Tables" unchecked in the report properties (and this is the
default),
using summary values that are not running values, placing summary
values at tops of groups as opposed to bottom, sorting/breaking groups
differently from the sort of the entire report, using widows
or orphans, using page x of y, are just a few things
that slow the application down for everyone, by locking the table(s).
A report needs to be locked when it is required to make multiple passes
through the table for calculations that summarize items. It
guarantees that a pass 1
though the data to get some values needed to format the report is
identical to pass 2 where it actually creates the report.
Some of
these double passes go all the way to the end, some just to
the end of the current page or group.
E.g. to
compute percentages, you need to know the total 1st. Let's
say you don't lock the table. You pre-process the total of some
number. Then as you process each record's value, you divide by that
total times 100 to get the percentage. If some record that is part of
the total is changed, or a new record is added, the actual total might
change, but the total number that A5 computed will not change. It's
possible you could have percentages totaling more or less than 100%
Instead, use calculated fields (on reports) that use functions like
(which I'll call running summary values)
RUN_TOTAL, RUN_AVERAGE, RUN_COUNT, RUN_MAXIMUM, RUN_MINIMUM,
RUN_STDDEV, RUN_VARIANCE
instead of
TOTAL, AVERAGE, COUNT, MAXIMUM, MINIMUM, STDDEV, VARIANCE
PAGE_TOTAL, PAGE_AVERAGE, PAGE_COUNT, PAGE_MAXIMUM, PAGE_MINIMUM
PAGECOUNT
Following the above means that they must also be
used in the footers,
and can't be placed in headers, as it does not exist prior to the
header being generated. Page x of y will also cause 2 passes as
the y can't be determined until
running through the data 1st generating a quasi-internal report.
Group Keep/Balance column height/Keep with detail/Balance column height
(widows) Report Group Properties, Keep together on page (Orphans)
Report Region Properties all force multiple passes and hence locking
through the data.
As you might surmise, the running versions are much faster, and should
always be used unless you must have the summary prior to when all the
records are processed for the printing pass..
Simplify
Sets
Use of a set that is overly complex for forms or reports is
another
bad idea, as it requires unneeded locks. Create a set
tailored
for the usage by the reports. Turn off referential integrity
for
that set and set any children to read-only where possible.
For
Forms, complex sets also pay a high penalty for a set's children
records, so don't add more child tables (particularly 1 to many) than
are needed.
Sometimes it makes sense to copy the needed table records to a local
drive (could also be any place, but the local drive is fastest
normally) and run a report there (this requires a fair amount of
code). The copy goes very fast, and then only the local user
is
impacted by the report (and does not cause any locking of the shared
tables over the network)
The
general rule is never lock tables that need to be shared,
ANYTIME, ANYHOW!
Xbasic
versus Action Script Coding (
Email
Comments)
Updated
21 Mar 2010
One should be familiar with the strengths & weaknesses of any
tool
they use, and XBasic or Action Script coding in Alpha Five is no
different. Below should give you some of the advantages
and disadvantages of Action Scripting and how to "bullet-proof" your
code a bit more.
What
is an
Action Script?
Action Script coding contains individual Action Items. Each
Action Item allows specifying in a menu or dialog driven
sequence,
a number of values and settings that generate an underlying XBasic
code.
The Action item's underlying XBasic code is regenerated each
time you save the Action Script.
Action Script Advantages
- Can be faster to create what you need, especially for the
non-trivial action items.
- Easier for non-programmers or those with less experience to
create something that they couldn't
otherwise create on their own.
- Easy to change a setting and regenerate new code
- There are some techniques and functions buried in Action
Scripting
that are not documented elsewhere, but are worth looking
at and using
them to generate code (especially XDialog)
Action Script Disadvantages
- Many settings or combination of settings mean that all the
possible choices could never be tested in their generation of
XBasic code. This means that uncommon selections may never
been
encountered by anyone, nor validated to it's working.
Here is a thread from the message board that indicates a bug in the
code generation of Action Scripts - Re:
Variable Type "H"
- It is possible to select settings of an action line that
make
no sense, and may generate inoperable Xbasic code. Newer
users
tend to select these out of ignorance or via a "try anything to see if
it works" approach.
- Generates a lot of code to determine the context of the
current
action line, which is repeated for each action line, slowing the code
down
- Many initialization and completion code, e.g. Opening a
table/Closing the table, are repeated for each action line.
This
puts more overhead to the network, Hard drive and other resources, and
slows things down as well.
- When you edit code in XBasic, it's code is always the same
each time. This is not true for action
script code. Each time an action script is opened for editing and
subsequently saved
(whether changed or not), the underlying XBasic code for all action lines
are regenerated.
With a new Alpha Five patch or version number, the same Action Script
settings could potentially generate different code. In most
cases
this is fine, as it should be accomplishing the same as before.
But occasionally, it results in problems even though no real
changes to the code was intended by you. Essentially, this
means
that each time you save in a changed Alpha Five patch or version, you
should retest for proper operation.
- Only so many development dollars can probably go to
correcting
Action Script code generation errors, but the underlying Xbasic code
functions used must work for both Action Script and Xbasic coding.
- Action Scripts are script code, not functions.
See the discussion of the advantages of functions
over scripts
Real-Life
Action
Script example vs XBasic
This is the XBasic code that an action script line to set a form field
to an expression value generates:
'Set 'Value' property of
'TimeStamp' in Form 'TestForm' .
DIM form_name as c
if
is_object(topparent.this) then
form_name = topparent.name()+".this"
else
form_name = ""
end if
DELETE expression_result
expression_result
= eval("now()",form_name)
parentform:TimeStamp.value = expression_result
This is the same exact functional XBasic code without the Action Script
un-needed code
parentform:TimeStamp.value=NOW()
In
the Action Script, the actual code has the disadvantages of
being
slower, more complex, deleting a variable named
"expression_result",
that could have potentially been a global and/or shared variable prior
to
that point that would now not exist. If both global
and
shared levels existed before the delete, now the global will
still
exist. The
"expression_result
= " line will now assign a new value to the
global variable
"expression_result"
since
it was not explicitly dimensioned at a lower level. Either
the
global variable will change it's value, or possibly
cause an execution error as the object type (type T for time here) does
not match the current type of the global variable.
Recommendations
Most users of Alpha Five probably use Action
Scripting more than XBasic coding. It is useful, particularly to
non-programmers, but even
as a 1st step for programmers. However, I would
encourage moving to XBasic coding as your personal abilities
allow.
Use Action Scripting as a tool to learn Xbasic. Use them to
show
the way or at least to point in a direction to go.
Then convert the Action Script to XBasic
code, take out
the extraneous coding and redundant sequences and you almost have
"normal" code. The difference
will be faster, more reliable code, over the long haul.
In the long-term, the more Xbasic you know, the better off you will be.
It will allow you to use the debugger more effectively and generate
faster, cleaner code. Inevitably you will end up needing something
Action Scripts can not address. Action Scripts can do
many
things, but it doesn't do everything that XBasic coding can.
Some
more comments of
Action Script Code Generation
The moment you save an action script being edited (even with no
modifications), the action script dialog settings are evaluated and new
code is generated. So let's say you created in release "X", but are now
editing in release "Y". Saving the Action Script will generate the code
per the new releases' Action Script generating code. So even if you did
not change anything, a small edit of an Action Script line
followed by a save, can result in potentially different (and untested)
code!
A similar thing could occur in the way a function works in an Xbasic
piece of code operates, but at least with XBasic code, your code would
not have changed! Also, it is much less likely that a function would
change in a bad way in what it does, versus the code
that Action
Scripting might generate.
So, one could argue that the prudent thing to do would either change
the code to XBasic so that it will not change, or make sure the
underlying Xbasic code you start with, is the code you end with (except
for your changes) when you edit in a new release.
Despite the surface appearance that they are the same, they are
actually radically different. A function (Alpha's or a user's UDF has
code that is syntactically compiled without errors otherwise
it
would never be able to be used) is tested for what it is.
But Action Scripting can generate myriads of combinations for
the
settings of a single action script line that would generate code that
was never validated. In probably 99.9% of the cases this is
fine
and works as expected. But not always (see this thread
Re:
Variable Type "H" )
Some Action Script lines are very simple and straightforward. These
seldom have issues, as there is almost a 1 to 1 correspondence to the
XBasic code they generate. Others actions generate code from many
selections of menus and settings. These generate a string of code that
needs to be compiled (which Alpha does when you save it) without
errors, and more importantly makes sense for all the settings. While,
in general, each setting (or multiple settings) of an action script
builds a portion of the code, there is no way every combination of
possible codes could ever be fully tested. Some parts can be very
straightforward to generate (and to test - this is why it generally
works) and other parts depend on multiple settings which can be a
nightmare to generate and can create extremely complex code.
In most cases, there is a very specific code generation sequence where
you take setting "A", generate it's code, take setting "B" append it's
generated code and so forth. But then there are cases where "B" implies
what "A" generates, and where it needs to be located in the code, and
so forth. It can get so intertwined, that it's impossible to totally
predict what it will or could generate and these are where the typical
failures tend to be.
AEX
Compiled Librarys (
Email
Comments)
Updated
18 Mar 2013
The
CSDA
Code Utility has an AEX library compiling utility called
LibMake that simplifies the process of making a desired library.
Whether you make an AEX library with LibMake or Alpha
Five's
built in tools, there are items you should know when using AEX's.
What
is an AEX?
The
AEX is a "Compiled" library of your code, toolbars, menus, images, blobs and other
data from your database application that comes from the global items
stored in the database. All of these come from the database
files
ending in ALB, ALX, and ALM. The created file ends in the AEX
extension. The library is useful for protecting your code from
examination and modification, unless you have the original code. This
helps protect your intellectual property. The debugger will not operate
on AEX items, eliminating that as an avenue for looking
at code.
AEX
"Compiled" type
Alpha
Five's "compiled" AEX format is not a truly compiled code, but what is
generally referred to as pseudo-compiled. Portions of the
code
have been translated into internal commands, but essentially
most
of it is still a program's internal representation of your code text,
and still must be processed. when executed. For all intents,
there is no measurable speed
improvements when executing code by using an AEX compiled file.
Alpha Software has experimented with a real compiled
code
version, and it runs much faster than normal Alpha Five code, but there
are many aspects of conversion that don't work or are not compatible,
so it may never be available as a product.
AEX loading speed
When
an AEX file is loaded, that file needs to be read by Alpha Five.
The size of the file, typically is relatively small.
The
largest one ever created is probably about 57 Megabytes and about 7000
entries from Alpha Software (I'm guessing #2 is the CSDA Code Utility
AEX at 3 Megabytes and 13 entries - 500 internal functions don't show).
In
most
cases, it's size is typically very small, and would not have a
particularly large impact to a fast network. The one case
where
it might have a temporary impact is in a large network (100's of
workstations maybe), who all start Alpha Five at approximately the same
time. A similar startup network impact event would
occur if
you used Network Optimize (shadow) and had to refresh the shadow the
copies, all at the same time.
For easier maintenance, it might be be better to leave the developer created AEX files on
the network in the database's main folder, and load them by Xbasic code, for most usages.
AEX
protection of Intellectual Property
An
AEX essentially translates your code to an Alpha Five's "compiled"
internal format. If you use a file "hex" dump utility, you can
normally see most of the expressions and operands of your code used in
your code as ASCII text. However, if you use the code line
OPTION ENCRYPTED_TOKENS
all
of the code after that point in your code will have the ASCII
encrypted so that it is not readable in the AEX. If you wish
to
have portions of the AEX visible, use
OPTION PLAINTEXT_TOKENS
Things
that should be in plain text include Copyrights, Help files and similar
that you want users (and people in a court of law) to be able to see.
If
you use AEX files to distribute your code, and you use the commands
above, there is no reason you need to password protect your original
code, making it easier for development
addin_library.AEX
file
All
of the AEX files from the
Addins_Installed
folder(s), are combined into one large
addin_library.AEX
file at startup of Alpha Five. This combined AEX library is available
to any database you have open with Alpha Five (which may not be what
you intend). Any AEX libraries that have an item in it with the same
name and same type, will result in only one being in the combined AEX.
If they were not identical, this could execute the wrong copy of the
code.
In general, the addin_library.aex file is rebuilt each
time you start Alpha Five, if it doesn't exist or if you have made any changes to the files in
the Addins_Installed folder(s). After that rebuilding of the
addins_library.aex file, the Addins_Installed folder(s) is not used, so
you can change the individual aex files there as needed to update.
If two copies of the same version of Alpha Five are open, you
will not be able to rebuild the file (which is OK, if you have not made
changes to the Addins_Installed folder(s).
Loading
of Other AEX files
If
an AEX file has the same name as the current database and is located in
the database file's (the same folder as the one with extension ADB)
folder, it will be automatically loaded when you start that database.
Other
AEX files that you specify may be loaded automatically from various
settings in Alpha Five. You can also manually
(meaning
XBasic code) load and unload AEX files.
AEX Addins_Installed folder issues
The Addins_installed folder is normally where you place AEX files, that
will automatically be loaded when you start Alpha Five.
In
A5v7 and earlier the addins_installed folder would be located under the
Alpha Five exe folder. Starting in A5v8, an
additional
folder was added in the user's application data folder area, for use in
Windows Vista and later. The AEX files could be located in either
location and work properly. Early patches of A5v8 did not totally work,
but later patches did it correctly. Then in A5v9, the code was changed
in an early patch, and then no other Addins_Installed folder other than
the one in the Alpha Five exe folder will actually work. The Addin
Manager Tool may show the scripts and functions from the other AEX's in
other Addins_Installed folder, but they are not actually loaded. A5v9
and up (at least to A5v11) currently DO NOT WORK FOR THESE OTHER
FOLDERS!
The install of CSDA utilities like the
CSDA
DiagInfo Utility
actually test all the folders during the install process, and will
place the CSDA AEX file only in the best folder that actually works, so
you can test new releases and find out where an AEX actually works. It
will also show you AEX files info that may prove useful for debugging.
Also, Alpha Five rebuilds the
addin_library.AEX
file anytime an AEX file that makes it up changes, but the code doesn't
always catch the changes depending upon the version and patch release.
If you delete the
addin_library.AEX
file, it will be rebuilt the next time Alpha Five is started.
AEX
item name conflicts
If
more than one item has the identical name and type is loaded from more
than one AEX file, only one will be used (probably the last one
loaded).
If you have an
entry in the database with the original code, that will
override
all loaded AEX entries by the same name and type.
So, to
make the AEX be used, you must remove all entries from your database
that you want to use from the AEX. In general, this means
having
one operational database copy with all the code deleted from
it
(and hence uses the AEX version's copy of the code), and another
development copy with all of the original code in it.
AEX Library
number
Depending on whether the AEX libraries are loaded from
Addins_Installed folder(s), or individually, the library
numbers
there were created with may or may not be important. The final library
numbers you use should not conflict with others being used. The
CSDA
DiagInfo Utility
will show which AEX files and numbers are currently in use.
You
should avoid using any Library numbers show below as well.
Alpha Five currently uses the
following library number assignments:
Library # |
Purpose |
0 |
Alpha5.AEX, Startup.AEX |
1 |
System.AEX |
2 |
modern.AEX |
3 |
classic.AEX |
100 |
Temporary for addin_manager explorer, and
default compiled number |
125 |
used in loading aex files |
126 |
used for aex with database name |
127 |
used for
Addin_Library.Aex (which combines all installed library AEX's and
ignores their library number) |
200 |
used by web
server to load AEX libraries to a web page |
60-63 |
for AIMS
products |
64 |
for CSDA Code
Utility and for AlphaSports.AEX |
Coding
tips (
Email
Comments)
Updated
07 November 2011
- Any code (of course, these should all be functions
- See here),
should attempt to leave/restore any values/states/conditions that
existed at the start of the code except for those that are intended to
be changed (normally a function's returned value(s)). All
locally
dimensioned values will be destroyed after a function exits, so you do
not need to "Delete" them. But if you opened a table in the
code,
then it should normally be closed at the end of the code
There are exceptions, e.g. if making multiple calls to a function, you
may want to open the table once, and then close it in the last call to
it (to cause less network overhead and improve speed). The
code
to close it should normally be kept with the code that opened it, but
activated with a flag passed to the code
- Dimension variables location - Either place them all
together
near the top of the code, or place them before their 1st use
as
close as possible to their usage. For the latter, you would
do it
like this
dim x as n
IF a>b
x=a
ELSE
x=b
END IF
not like this
IF a>b
dim x as n
x=a
ELSE
dim x as n
x=b
END IF
- Try to list all side effects of a function in a comment at
top
- Within reason, use resources for the smallest possible time
and
release resources sooner than later. E.g. close table as soon
as
you no longer need it in code. If needing to put a record
in change mode, do as much pre-calculation 1st, enter change
mode, make changes and save.
- In general, evaluation of expressions is very fast compared
to
moving data to variables, so it is often better to have
one long
expression that may repeat calculations, rather than 1st calculating a
sub-expression and assiging it's value to a variable and then using
that variable multiple times in an expression. Strange but true!
- Use
Parenthesis !!! There are times when Alpha Five's expression
evaluator can get confused. Use additional parenthesis around
objects to make sure you get the expression evaluated correctly. There
is no measureable speed loss by using the extra parenthesis and you
will know that the expression is being evaluated exactlly as you
intend. E.g. in the code editor's Interactive Window;
numbervalue1=7
numbervalue2=6
?IF(numbervalue1 .and. 3 < numbervalue2,"True","False")
ERROR: Argument is incorrect data type
?IF(numbervalue1 .and. (3 < numbervalue2),"True","False")
ERROR: Argument is incorrect data type
?IF((numbervalue1 .and. 3) < numbervalue2,"True","False")
= "True"
Network
Issues related to speed (
Email
Comments)
Updated
16 April 2012
The speed of Alpha Five can be effected by various network settings and
how the network is used.
- You have a problem with networking and Alpha Five
- You know what you are doing
- You've tried everything else
Speeding
up code (
Email
Comments)
Updated
12 November 2011
For
the most part, the speed of XBasic code does not change from
one
Alpha Five version to the next. There are a few exceptions to this,
typically when Alpha Software totally rewrites a function, or changes
it to a "compiled" (not an AEX-type compiled) type code. The
following are tips that can speed up an application.
Isolate
Code that depends on tricks (
Email
Comments)
Updated
03 Jan 2010
Any code that uses special tricks or depends on an odd or undocumented
(or less documented - meaning hard to find documentation) way that
Alpha operates, or is Alpha Five version-specific, should
first tried to be avoided.
These
could lead to future support issues with the code.
However, if you still think you need to use this, place the
code
inside a function. It should contain a lot of comments in the
code documenting the trick or usage so that anyone (including
you
in the future) understands what is happening.
By placing it inside a function, you can update it easily as needed in
the future to account for slight variations between Alpha Five versions
or patches.
An example is the code
""+numbervalue
This code converts the numbervalue to a string, keeping all decimals,
but without trailing decimal zeros
and with no leading spaces padding. There are other ways to do it, but
they can
be more complicated. Since the details of the conversion is
not documented (in this case keeping all decimals), it might
change
(although unlikely) in a
future version.
Placing code like this in a function will allow
replacement with a working version should it prove necessary.
Here is a version specific example that returns the current EXE name,
should it be renamed. It is the typical way
many might write
version specific code.
FUNCTION get_exe_name AS C
( )
IF version()>=8
get_exe_name=a5.get_exe_name()
ELSE
get_exe_name="Alpha5.exe"
END IF
END FUNCTION
The above works fine, however, there are times when specific builds
(patches) of Alpha work or don't work. You could process each
case as needed, but this can get to be very hard to check each
build. In this case, and in any cases where you can test the operation
of the "trick", you can dynamically test it and use some alternative
value or method when it doesn't work. For the above, rewrite
it
this way, and now it is not version-specific, but whether the method
works or not-specific.
FUNCTION get_exe_name AS C
( )
IF eval_valid("a5.get_exe_name()")
get_exe_name=a5.get_exe_name()
ELSE
get_exe_name="Alpha5.exe"
END IF
END FUNCTION
If the test requires a lot of time to process, continue storing the
final result or whether the trick works so that you need to only do the
test once
Protecting
Code (
Email
Comments)
Updated
09 Jan 2012
Protecting
your code is important if you want to sell a product. There
are a
variety of ways to protect your code in Alpha Five.
The
protection of code on layouts such as forms, browses and reports is
based on protecting the layout with a password. These
passwords
are relatively easy to break, so I recommend that you not depend on
them. If you do use them, do not use the same password in
code
objects within an Alpha Five application.
Operations (e.g.
Update, Append etc) also have the same password limitations as layouts,
so you are best just converting the operation to an XBasic equivalent,
and then using code techniques described below to protect the code.
To
protect code on layouts, it is better to move the code to Global
Scripts and Functions (although
functions
should be used - see here).
Then your code in the layout amounts to a single line of code
call to the global script or function. This moves the need
for
protection to the global script or function.
In a
similar way, layout calculated fields can utilize global
functions
to hide most of what they are doing, although there is a small speed
penalty
in the overhead of executing the function (which can be a bit higher,
as layout calculated fields may be executed several times to get the
final value, rather than once)
A global script or function can be protected by having the 1st line of
code that looks similar to:
'password your_password
The code debugger will not step
through
password protected code, so that helps.
However, many development projects require exporting
the code and will require re-entry of the password for each and every
code
object being exported, even if previously entered. Also, a global
search of code ignores any code with a password, so passwords get in
the way
of maintenance of code.
The CSDA Code Utility has
replacements for both of these that are more complete and allow for
entering a list of passwords to use for both of these operations.
Action
scripts can actually have their 1st line modified to include the
password line, but each time it is edited, the password line is
removed. There is the
AIMS
Grab Bag
tool from AIMS that will add the password to each Action
Script
after being edited. Alternatively, just convert those Action Scripts
(
recommended
here) you want to protect to an XBasic script and password
protect that code.
Another problem with password protection of code is that
anyone with Alpha Five can compile it, whether or not it has a
password. Unless you follow the technique for AEX files that
follows, much of the text of the code will appear in the binary of an
AEX file
In Alpha Five you
can do a compile (actually a pseudo-compile) of code into a compiled
library (extensions AEX). Like passwords in code, the code debugger
will not step
through compiled code.
Compiling works
on password protected code, creates a small problem. Much of the
text of the code (whether password protected or not), is left visible
in the AEX file. A simple binary hex view or dump program can
see
these text strings. To make sure these strings are encrypted,
and to always protect your code,
use the following line:
OPTION ENCRYPTED_TOKENS
All code that follows the above will not have readable text.
To turn the text strings back on, use:
OPTION PLAINTEXT_TOKENS
You can go freely between the 2 as many times as you wish in your
source code.
In
CSDA code, only those sections that we want visible are readable.
Typically this is built-in help text, copyrights,
and code
date and version numbers.
Another aspect of global code is
that all global functions are accessible, even if they would never be
used by others, or if you don't want them used by others. If
you
make them an internal functions (any function code placed in the a
global function or script's code with a name other than that of the
global code), only that code can access the internal functions, and an
external function of the same name is ignored (giving precedence to the
internal function)
The LIBMAKE() function of the CSDA Code
Utility creates AEX compiled code libraries. Using LIBMAKE(),
you can keep all your internal functions as separate code to
make
it easier to maintain the code (especially when using it in multiple
places). When you compile using LIBMAKE(), it
will compile only the code you want into a specific
library,
and at the same time, include each desired function
as an
internal function to each code that requires it.
For
example, if you look at the viewable functions (using the Alpha Five
Addins Manager) of the CSDA Code Utility, there are only a few
accessible functions, yet there are many internal functions
that
make each of them up, many of them repeated.
The only real
limitation is that the any code object with all included internal
functions not hit certain physical limits that Alpha has for code sizes
(as
discussed here)
When using
variables, it is best to make them local when possible (
see here), which means
that they don't exist after the code closes. Making variables
Global or Shared may create an avenue to which others may be able to
affect your application's operation.
There are additional ways to protect your code from prying
eyes, but you have the basics listed above.
Using
Active-X Objects tips (
Email
Comments)
Updated
27 Feb 2010
Please note the following:
1. Xbasis code using activeX controls can only reference the Active X
properties (variables) with one pointer level (one dot). This
is
due to an issue with the Alpha Five XBasic Interpreter and Visual Basic
Interface. To solve it, set the parent pointers of a variable
to
a temporary variable pointer, and then use that pointer to reference.
E.g.
objectpointer.subpointer.propertyvalue=-1
should be replaced with code similar to this
dim temp_pointer as p
temp_pointer=objectpointer.subpointer
temp_pointer.propertyvalue=-1
2. Active X Arrays referenced from Alpha Five start at base of 1 for
the 1st array element vs the Active X control bases it at a base of 0.
Hence add 1 to the array element when using in Alpha Five
Xbasic.
Also, reference to arrays in XBasic are always addressed with
[ ]
square brackets. E.g. the
lines in visual basic
objectpointer.subpointer(5).propertyvalue=-1
objectpointer.arrayvalue(9)="test
string"
should be replaced with Xbasic code similar to this (taking in to
account both issues)
temp_pointer=objectpointer.subpointer[6]
temp_pointer.propertyvalue=-1
objectpointer.arrayvalue[10]="test
string"
Bitwise
Logical Operations (Binary) with numbers
(
Email
Comments)
Updated
14 June 2012
When interfacing to other programs using Active X controls or similar,
they often use numeric values to act like having many logical flags,
one per binary bit. Each binary bit (base 2 arithmetic) can
have
a value of 1 or 0.
However Alpha Five only operates with decimal numbers in it's
expressions. So any time you have values in other formats,
typically either binary (base 2), octal (base 8) or hexadecimal
(sometimes called just hex - base 16), you must convert them to decimal
number values
So if you had a hexadecimal number 23, typically shown in other
languages and programs as &H23 or 0x23, this is equivalent to
the
following
Value |
Base |
Valid digits
for base |
23 |
hexadecimal |
0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F |
00100011 |
binary |
0,1 |
043 |
octal |
0,1,2,3,4,5,6,7 |
35 |
decimal |
0,1,2,3,4,5,6,7,8,9 |
To convert a hex number from some other language to decimal in Alpha,
Alpha needs for you to give the hex number portion as a string, so for
the
above, you might use
numeric_var=hex_to_dec("23")
' Translate Hex string &H23 to decimal number
Alpha Five does not have any binary or octal functions to convert those
formats.
Once a numeric value, Alpha Five logical operators,
.AND.
.OR.
.XOR.
will all work on each bit of the binary equivalent as if you performed
the logical operation on each bit position of each side.
So
if doing a bitwise .OR. , an example might be
Expression |
Result |
Base |
Valid digits for base |
23 .OR. A1 |
A3 |
hexadecimal |
0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F |
00100011 .OR. 10100001 |
10100011 |
binary |
0,1 |
043 .OR. 121 |
243 |
octal |
0,1,2,3,4,5,6,7 |
35 .OR. 161 |
163 |
decimal |
0,1,2,3,4,5,6,7,8,9 |
Since Alpha Five's expression parser
understands only decimal
numbers, you must convert them all to decimal numbers if
not already in that format, e.g.
hex_to_dec("23") .OR.
hex_to_dec("A1")
or
35 .OR. 161
Later if you need to change it back to hex, use the function
dec_to_hex().
Inverting the Bits
(Bitwise NOT)
As it turns out, to invert the bits (change 1's to 0's and 0's to 1's),
one might expect the .NOT. operator to work on the bits, but it does
NOT
work. The easiest way to invert the bits is to use
.XOR.
with the value on 1 side and a decimal equivalent of all
binary
1's (for the needed width) for the other side. So to invert
hexadecimal 23, for a width of 16 bits, we would use an expression like
hex_to_dec("23") .XOR.
((2^16)-1)
which simplifies to
35 .XOR. 65535
and yields
Expression |
Result |
Base |
Valid digits for base |
23 .XOR. FFFF |
FFDC
|
hexadecimal |
0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F |
00100011 .XOR. 1111111111111111 |
1111111111011100 |
binary |
0,1 |
043 .XOR. 121 |
377,334
|
octal |
0,1,2,3,4,5,6,7 |
35 .XOR. 65535 |
65500 |
decimal |
0,1,2,3,4,5,6,7,8,9 |
Accessing specific Bits
To operate on a particular binary bit in "n" (where n is the bit
position starting at 0 on the right most bit) in a long word of binary
width "w" (typically values, 8, 16, 32, 64 which correspond to 1, 2, 4
or 8 bytes of information), the following expressions should be helpful
numeric_var=numeric_var .OR. (2^n) ' Set a bit at position n in a
binary representation
numeric_var=numeric_var .XOR. (2^n) ' Invert bit at position n in a
binary representation
numeric_var=numeric_var .AND. ((2^w-1) .xor. 2^n) ' Clear bit at
position n in a binary representation
numeric_var=numeric_var*2 'Shift bits left 1 position, and zero-fill
lowest bit
Test Bit n for a 1
IF((numeric_var .AND. (2^n))>0,.T.,.F.) ' True if bit n is set
to a 1, else false
These are not the most efficient ways to do these, as in most cases the
numbers n and w are known at the time of writing the expression, e.g.
2^2 is just the constant 4. And likewise
((2^8-1) .xor. 2^2)
is equal to
255 .xor. 4
which is equal to
251
In addition, you can do multiple bits simultaneously by combining
individual bit expressions together
Bit operations only work for lower 32 bits
The logical operators .AND., .OR. and .XOR. only return the lowest 32
bits of the result. Any bits in higher bit positions
are
effectively set to 0 in Alpha Five Version 8, however in Alpha Five
version 11, any bits past the lower 32 bits seem to result in the top
bot of the 32 lower bits being set. Below is an Interactive
Window
Alpha Five version 8 example.
?(2^33)
= 8589934592
?(2^30)
= 1073741824
?(2^33)+(2^30)
= 9663676416
?((2^33)+(2^30)) .OR. 0
= 1073741824
Also,
when the logical operation results in a value that has the 32nd bit
position set (the 31st position if starting from 0), the result will
return a negative. This can be easily resolved by taking the
absolute of the result. Below is an interactive Window
example.
?(2^31)
=> 2147483648 (decimal)
?(2^31) .OR. 0
= -2147483648 (decimal)
?abs((2^31) .OR. 0)
= 2147483648 (decimal)
Blob bitwise operations
Using Blob functions, there are blob operations OR, AND, XOR
and
NOT that will work on each byte of the blob in a bitwise
manner as
well.
Alpha
Five Hardware Suggestions
(
Email
Comments)
Updated
15 August 2011
Alpha Five utilizes hardware in ways that by careful choice of
the hardware, can have it's speed and reliability increased.
Below are some hardware concepts that apply to Alpha Five's
usage
and guidelines for hardware selection. As technology changes,
the
recommendations are generally for systems that have a good
price/performance ratio.
Desktop
Clients
A
Desktop Client computer runs Windows and the Alpha Five
program,
accesses the Application layouts and code from either the LAN network
or, if Network Optimized (shadowed), a local drive. Most data
is
placed on the Desktop Server so that others can share and
process
records as well. Speed of a Desktop client does not have any
effect on speed of any other client, with 1, maybe 2, exceptions.
Many people use 1 of their Desktop Clients serving the dual
purpose as a Desktop Server, which can cause other desktop Clients to
be much slower if that server is also being used as a client.
Also if the Desktop client
is slow in locking and unlocking on the network files and records, they
will slow the other clients down while performing these.
Each
and every client system needs to be reliable. In fact
they need
to be as reliable as a Web Server! If they have issues
with their memory or other processing data issues, they
can quickly
corrupt indexes and data. And it only takes one bad machine
on a
network to corrupt a shared database.
Most of the times, a machine that might do this is likely to
crash
before causing a corruption, but you never know. If you have
any
kind of irregular
corruption issues of data or indexes, it could be one of your client
systems or the network connection to them. Good Backup procedures
and regular re-indexing (and checking indexes for errors) is a good
procedure to do.
First and foremost, the main component for speed is the networks. For
the desktop, any
problem with the router, network switch, cable, connectors and NIC
(Network Interface Card) can slow the system down, a little, or a lot
for all users!
Using
wireless should always be a last resort choice, as a 1 GB Ethernet runs
much faster and is very cost effective today. Depending upon
your
electrical power in your building, PowerLine adapters (basically they
extend your Ethernet LAN through the AC power in the building, with
speeds up to 500 Mbs - depending on the adapters, although slower
speeds are more typical in many buildings) are often a good choice, and
better than wireless.
The network switches (or routers) and
NIC should also be at least 1 GB Ethernet. There are some
switches
and NIC cards that might be more efficient, but for the most part,
they all operate reasonably well in today's
market. However,
1 bad NIC, or Ethernet cable, or connector or router/switch can slow
the entire network traffic down to a crawl. This happens
because
network packets are sent with errors in them, and have to be re-sent,
possibly many many times, until it is successfully received.
Next,
the speed of the hard drive is important. You could use a
RAID,
but that might be overkill for most needs. However, it does
make
sense to place your application on a different physical drive from
Windows. This makes head seeks on the hard drive independent
for
Windows and for your application. A SSD (solid state drive)
is
good for files that don't change a lot, mostly Windows code and
executables. Using an SSD is even a good place for a shadow
(network optimized) Alpha Five application.
Currently, only
one CPU core (Single threaded) is used by Alpha Five, but it may be
possible to have Windows operate with the other cores. A
reasonably fast CPU is important, but not necessarily critical.
It really depends how much XBasic code is running as to
whether
it will make a big difference, but in general, get a fast CPU, 2 or
more cores, but don't pay a premium price for it.
Memory is
important for Windows operating and efficiently, and Alpha Five can use
it too. I suggest that 2 GB of main memory minimum based on
current memory prices, 1 GB if you are on a very tight budget.
If
possible shoot for 3GB or more. 32 bit versions of Windows have a
usable limit of 3 GB, even if you put more in.
Desktop
Server
A
desktop file server handles locking of a database requests and all the
actual sector read/writes. It does not process any Alpha Five code.
Instead it handles file read/write requests sent from
Client systems, as well as processing all lock/unlock requests (which
are the critical speed item in a non-true client/server application
(e.g. DBF files). Speed of it's NIC card
and how
fast it can process multiple file read/write and locking requests is
important.
CPU
speed is less important than the client, but Hard Drive speed and 2 Gb
or 3 GB minimum memory for caching data is important. This
system
should be very reliable though and have a UPS (Un-interruptible Power
Supply) on it.
WAS
(Web Application Server)
If
using the Web Application Server (WAS), the Internet interface speed
will be your gating factor. Since the WAS computer has to do
everything, make the CPU as fast as your money allows,
but consider a reasonable price/performance point.
Fast
and larger memory (3 GB is probably plenty for most)
& fairly
fast hard drives will minimize latency contributions from those.
Try to put the actual data on a different physical
drive.
If running an SQL database and money allows, place the SQL
server
on a different computer (or assign it to other CPU cores that are not
being used), to allow the fastest processing of SQL requests.
Video card and other devices have no bearing on speed.
This system should be very reliable though, allowing 24x7 operation and
have a UPS (Un-interruptible Power Supply) on it.
Alpha
Five Data & Index Corruptions
(
Email
Comments)
Updated
26 August 2011
In a DBF file structure, which Alpha Five utilizes for DBF
Files and Data Dictionary files, Index corruptions are found sometimes
and less frequently, data corruptions.
The 1st thing people
blame is the software. It is almost never the case.
There
are things you can do in software to affect the efficiency of data (any
data - data, indexes, memo fields etc) reads and writes to the server
computer, and this will decrease the likelihood of a corruption, but it
is not addressing the source of the problem.
It is almost
everytime, the hardware associated with a client computer that is the
cause. Because of the way files and records get locked, and the
fact that the client computer does most of the work (except the
reading/writing of disk drive sector data to the database file, any
corruption of data by
the client computer is generally due to it's memory or
network
usage.
Questionable memory or bad memory timing and memory
setup is one typical cause. If the computer is crashing
windows
on a regular basis, memory or computer bus timing is a key suspect.
The
network path (Network Interface Card (NIC), cables, connectors, swiches
etc) till the common point of the server can also be a
problem. An intermittent Network wire, or one that does not
have a
solid connection (even if it tests good for continuity), can cause
electrical noise in the cable. While networks have error checking in
them, occasionally one bad packet of data that is interpreted as good
will cause a corruption. Maybe it's 1 out of a billion, but
it
doesn't take long to do a billion packets of data. Once an index is
corrupted, it will generally go from bad to worse over time. Rebuilding
indexes can solve an immediate index corruption, but does not remove
the source of the problem.
Multiply the risk by the fact
that it takes only 1 bad client computer sharing a database to bring
down the entire database by corrupting data or indexes.
Despite all this, most systems tend to run extremely reliable.
What
can you do?
First
and foremost, backup regularly, typically once per day. Keep
older backups on a progressively spacing. E.g. 1 backup for
each
day of the week, 1 backup for each week of the month, 1 backup for each
month of the year, and 1 for each year. Except for the yearly
backup, just reuse the oldest for the group, e.g. use monday's backup
and replace the older Monday backup. This process allows you
to
go back to older data before something is corrupted, even if you didn't
detect the corruption for a long time. All backups should
have
error checking built-in to the backup files. ZIP
files and
other compressed files all do this. You want to be sure of
the
validity of the backup to being faithful to the original. For
most smaller businesses, a set of flash pen sticks can serve the backup
needs and allow rotation, and even be easily taken off site in case of
fire or other disaster.
To find the bad hardware, try to eliminate
questionable client computers from accessing the database and see if
the problem goes away. Or replace the client computer, or
anything in it's network path with a new computer or network path items
(wire, connectors, NIC card, switches)
See if there is any common part to all suspected computers (e.g. a
network switch). If so, replace that item.
Sometimes
network sniffers (hardware or software) can identify bad
network
packets. See if you can trace to a client computer that
generates
a lot of errors,
Use
Alpha's built-in index checker to see
if any indexes are corrupted on a regular basis and see how long it is
between corruptions. This will give you a frequency of
failure,
and maybe give a clue to the computer by recent users (particularly big
users)
Rebuild indexes regularly, to eliminate problems in the indexes.
Finally,
you can switch your data to an SQL or True Client Server
database of sometype. While this can be somewhat painful for
conversion, it
eliminates the client computer as a source of corruption, and places
the entire responsibility for data integrity on the server computer.
Compatible
DBF/Index Naming usage (
Email
Comments)
Updated
09 November 2011
If you are using a native Alpha Five Database (*.dbf files),
Alpha Five allows longer field names and longer index names (up to 24
characters) than the standard 10 characters that the format uses.
These extensions are held in the dbf files' associated data
dictionaries, which is an Alpha Five enhancement. Should you
try
to access the data from another dbf compatible program, these
extensions are not available to those programs. They will get
only the 1st 10 characters (possibly modified if not unique).
To
allow easier usage by these compatible programs, and so you don't lose
connections (e.g. in sets) due to losing data dictionaries, keep index
names and field names to 10 characters or less.
Many places
in Alpha Five can not deal with spaces in Database Names, Field Names
or Index Names. It will sometimes convert the spaces to
Underscores ( _ ), but it depends upon where and how it is used, making
it inconsistent from a program perspective. Instead, don't
uses
spaces, and use the underscore instead. So acceptable
characters
for database names, field names and index names are A through Z, 0
through 9 and underscore.
In
Summarize operations, the field names in the summary database seem to
allow up to 32 characters in version 9 and earlier, and more
in
version 11 and up. Version 10 values has not been tested.
It may be that they have extended the fields in recent
versions
of Alpha Five, however, the prudent thing to do is to use shorter field
names in the database you are summarizing.
Alpha
Five Path usage (
Email
Comments)
Updated
25 April 2012
Alpha Five generally stores filename and paths as either
just the filename and the path is assumed to be the current database
path (either the Master or Shadow database path for whichever you are
using), or the full paths and filename. It does not store any
relative paths, unless you create code to do it.
Occasionally,
you may see some Alpha Five configurations and options that allow
setting a path for particular files, e.g. an image path, but these are
the exception.
Alpha Five also allows aliases used in a
filename, where an alias name (surrounded by square brackets (e.g.
[imagepath] ) used in a filepath is replaced by a piece of text that is
stored in the database. This allows other paths to be
replaced,
but beware, that not all file related functions do the alias
replacement.
Using code, you can get the Master or Shadow
database's path, then calculate another path that is relative to that.
You can also obtain Windows and Alpha Five folder paths that
they
use using functions built into Alpha Five, and base your path from
those starting points.
An example of code to always get the
Master database path (including the trailing backslash), even if you
are running the shadowed version is
dim MasterPath as C
MasterPath=IF(a5.Get_Master_Path()=="",a5.Get_Path(),a5.Get_Master_Path())+chr(92)
Variable
& Code Naming warnings (
Email
Comments)
Updated
04 February 2013
Variable & Code (e.g. script, function, classes and
enumeration) names should be 24 characters or less. Part of the reason
is that the ALB, ALM, ALX extension files where the code is stored only
has room for 24 characters in the stored name. So anything
with
the same initial 24 characters will be stored to the same place in the
data dictionary.
In some
older
versions of Alpha Five, 23 characters is a safer choice, as some of
them had issues with the 24th character. They can start with any
character from A to Z or underscore ( _ ), and may use A to Z, 0 to 9
and underscore for the rest of it. Note that this 24
character
limitation is for each part of a pointer variable (between dots), so
the entire length could be much longer. So this is OK
ReallyLongPointerName1.ReallyLongPointerName2.ReallyLongPointerName3.char
If creating a pointer variable name, do not name it any of the following
- XOR - Confused with .XOR.
- AND - Confused with .AND.
- OR - Confused with .OR.
- NOT - Confused with .NOT.
When used in an expression, Alpha Five can't properly evaluate the
result you intended. E.g.
glv=global_variables()
dim glv.xor as p
dim glv.xor.char as c
ERROR: Unexpected text '.xor.'
glv.xor.char="1"
ERROR: Argument is incorrect data type
True and false values of .T. and .F. don't cause problems, but should
probably be avoided to avoid confusion.
Other variable names to stay away from are GLOBAL, SHARED,
SYSTEM, PRIVATE,
BACKGROUND,
PRIVATE, EVENT, PROTECTED, STATIC, CONSTANT. All of these
will
definitely cause problems in DIM statements, and as stated in other
tips, every variable should be DIMed.
Code names should
be 24 characters or less, with the same restrictions as variables.
Because Alpha adds a digit at the end if using multiple copies or
recursive usage (an alias in Alpha Five speak), it is better if the
last character of the name is not a number, so it is not confused.
It is actually possible to create code names that are longer, and Alpha
Five actually does this
In
earlier versions of Alpha Five (Maybe version 9 and earlier, but maybe
version 10 as well), if you use an internal function, do not have it's
leading characters the same as the entire name of the code object.
This causes all sort of odd problems for finding the internal
function, depending upon if it was recently saved or not, etc.
And debugging is an absolute nightmare when this happens. E.g
if
the name of the code object is MyFunction, do not have an internal
function of the name MyFunction_Internal1
Regular
Expression Notes for Alpha Five (
Email
Comments)
Updated
11 September 2011
The Regular Expression engine of Alpha Five is a "Regex Directed"
Engine. This can be proved by trying the following in the
Interactive Window
?regex_grep("regex-directed
not","(regex-directed|regex-directed not)","\1","i")
= "regex-directed"
If the results are "regex-directed not", then it is a text-directed
engine, not considered as adaptable.
The
whitespace "\s." matches a space, tab or or linefeed. A
Carriage
return is ignored. This can be proved by trying the following
in
the
Interactive Window.
?regex_grep("space
"+crlf()+"2tab"+chr(9)+crlf()+"3cr"+chr(13)+"4lf"+chr(10)+"5line
6"+crlf(),"([[:alnum:]]+[\s]*)[[:alnum:]]{1,1}","~\1-","i")
= ~space
-~tab
-~cr-~lf
-~line -
The
dot "." character matches any character. A Carriage return is
ignored. This can be proved by trying the following
in the
Interactive Window.
?regex_grep("space
"+crlf()+"2tab"+chr(9)+crlf()+"3cr"+chr(13)+"4lf"+chr(10)+"5line
6"+crlf(),"([[:alnum:]]{1,1}.*[[:alnum:]]{1,1})","~\1-","i")
= ~space
2tab
3cr4lf
5line 6-
UI_Msg_Box()
Codes (
Email
Comments)
Updated 02
October 2011
Style
- Bits 0->3 values = 0 to 6
- 0 (UI_OK) - "OK" or X button returns 1
- 1 (UI_OK_CANCEL) - "OK,CANCEL" buttons, OK returns 1,
Cancel or X returns 2
- 2 (UI_ABORT_RETRY_IGNORE) - "ABORT,RETRY,IGNORE" buttons,
returns 3,4,5 respectively, no X option
- 3 (UI_YES_NO_CANCEL) - "YES,NO,CANCEL" buttons, returns
6,7,2 respectively, X option returns 2
- 4 (UI_YES_NO) - "YES,NO" buttons, returns 6,7 respectively,
no X option
- 5 (UI_RETRY_CANCEL) - "RETRY,CANCEL" buttons, returns 4,2
respectively, X option returns 2
- 6 (UI_YES_SELECTED) - "CANCEL,TRY AGAIN,CONTINUE" buttons,
returns 2,10,11 respectively, X option returns 2
- 7 thru 15 return 0 with no box
Display symbol - Bits 4->6 values
0 to 4
- 0 (0000xxxx) (UI_NO_SYMBOL) -
- 16 (0001xxxx) (UI_STOP_SYMBOL) (Red
X)
- 32 (0010xxxx) (UI_QUESTION_SYMBOL)
- 48 (0011xxxx) (UI_ATTENTION_SYMBOL)
- 64 (0100xxxx) (UI_INFORMATION_SYMBOL)
Selected Button - Bits 8->9 values
0 to 2
- 0 (xxxxxx00 xxxxxxxx) (UI_FIRST_BUTTON_DEFAULT
- 256 (xxxxxx01 xxxxxxxx) (UI_SECOND_BUTTON_DEFAULT)
- 512 (xxxxxx10 xxxxxxxx) (UI_THIRD_BUTTON_DEFAULT)
Good
Alpha Built-in Functions to use (
Email
Comments)
Updated
22
April 2012
Alpha
Five Functions Converted to "C" code
Newer Alpha Five versions occasionally have functions rewritten
from XBasic to a "C" compiled code. These generally are done to improve
speed in Alpha Five. Here is a list of functions,
and the
first version when they were converted to "C" compiled code.
While they should be 100% equivalent, when the functions are
changed, there is a very small chance of a code bug being introduced.
- A5v10 -
ctime()
- A5v9 - isshorttime()
- A5v9 - istime()
- A5v9 -
comma_to_crlf()
equivalent to alltrim(stritran(text,",",crlf()))
- A5v9 - crlf_to_comma()
equivalent to stritran(alltrim(text),crlf(),",")
- A5v9 - convert_type()
- A5v9 - line_count()
This actually takes 4 times longer in v10, than V8, but v11 seems ok,
reason unknown
w_count(text,crlf())
or *count(text)
each run about 5 times faster than line_count(text)
- A5v9 - remove_blank_lines()
- Improved by about 3x,
word_change("1",text,crlf())
is much faster in A5v8 and earlier, and about the same in A5v9 and up
- A5v9 - word_unique()
- A5v8 - round_up()
- A5v7 -
s_quote()
- A5v6 - urldecode()
- A5v6 - urlencode()
Most
Useful and Efficient Alpha Five Built-In Functions
Below is an edited list of the functions use the most by
CSDA (ones that are specific to CSDA usages such as SIN(), COS(), date
conversions and utility oriented are removed to give a better indicator
of those that a typical user would utilize the most). Note that type C
and M are used the most (and are faster in general), and type S (Xbasic
functions) don't show up as much. The counts give a relative
idea
of frequency of use. Note that the 1st XBasic ("S") function is only
used 245 times by CSDA.
Any function starting with an asterisk (e.g. *count() ) is always a "C"
type code
UI
dialog functions (such as UI_MSG_BOX() ) that are type C, will stop
timing events in XDialog message boxes from occuring until these UI
dialog boxes are closed. Thus it is recommended to replace
them
with a replacement function that uses XDialog if at all possible.
The
functions below should be used as a guide to the best functions to use
for speed and efficiency, and should be studied as a core group of
functions to generally use.
In
general, the ones near the top are faster than ones that are lower in
the list, and should be used in time critical code, and are generally
the fastest functions for their purposes in Alpha Five. This
is
based on
A5v8 - a5v11
Used: 78544 functions
Unique: 3027 unique functions
Counts | Types
=====|=================
- 161 | M - Methods - Generally written in "C" code, but may
be also XBasic
- 329 | C - 'C' code - written in compiled "C" code which is
faster than an equivalent Xbasic Function
- 368 | S - System AEX 'XBasic' code
Count,Type,Code Name,Prototype,Description
==============================================
- 4482,C,Crlf(),C CRLF([N count]),Returns cr-lf(s).
- 3490,C,Word(),C WORD(C character,N word_number[,C
separator[,N num_words]]),Returns a specified word(s) from a string.
- 2875,C,Ut(),C UT(C character),Returns the uppercase trimmed
version of the string supplied.
- 2802,C,Chr(),C CHR(N ascii_value),Converts a numeric
expression to a character expression.
- 2739,C,Str(),C STR(N number[,N length[,N decimal_places[,C
number_format]]]),Converts a number to a character string.
- 2711,C,Alltrim(),C ALLTRIM(C character),Trims leading and
trailing spaces.
- 2636,C,Typeof(),C TYPEOF(A value),Returns the data type of
the the value.
- 2632,C,Ltrim(),C
LTRIM(C character[,C other_chars]),Removes any leading blanks (or
optional other characters) in a character string.
- 2227,C,If(),A IF(L condition,A result_true,A
result_false),Tests an expression and returns one of two expressions.
- 2016,C,Eval(),C
EVAL(C Expression[,C eval_context[,C eval_cursorstate]]),Return the
result of the expression (parses an expression stored in a string, and
evaluates it).
- 1813,C,Substr(),C SUBSTR(C character,N
starting_position[,N number_of_characters]),Returns a substring portion
of a character string.
- 1798,C,Trim(),C TRIM(C character),Removes trailing blanks
from a character string.
- 1517,C,Left(),C LEFT(C text,N Number_of_characters),Returns
a portion of a string, starting with the leftmost character.
- 1395,M,File.Filename_Parse(),
FILE.Filename_Parse(C Filename,C Component),Returns the component(s) of
the filename in the order specified (D=drive,P=Path,N=Name,E=Extension).
- 1303,C,Upper(),C UPPER(C character),Converts a character
string to uppercase.
- 1221,C,Len(),N LEN(C character),Returns the number of
characters in a string.
- 1153,C,Val(),N VAL(C character),Converts a character string
to a numeric value.
- 1113,C,At(),N AT(C search_string,C string[,N
occurrence]),Returns the position where one character string is found
in another.
- 993,C,*Concat(),V
*Concat(C Destination,C Text), Concatonates Text to
Destination
string - Faster than normal Xbasic Concatonation
- 691,C,Ui_Msg_Box(),N
UI_MSG_BOX(C title,C message[,N box_style]),Inform user with a message
box. RECOMMENDATION - Eliminate this and use an XDialog
replacement
- 559,C,Abs(),N ABS(N number),Returns the absolute value of a
number.
- 557,M,File.Exists(), FILE.Exists(C filename),Does
file exist.
- 525,C,Evaluate_Template(),C EVALUATE_TEMPLATE(C template[,C
Macro1[,...C MacroN]]),Evaluate code with optional macros.
- 524,C,Local_Variables(),P
LOCAL_VARIABLES(),Gets the local variable frame, used to pass local
script variables down to other functions.
- 505,C,*For_Each(),C
*FOR_EACH(A tag,A each,C list),Executes the command specified by "each"
on every entry in a crlf delimited "list". "Tag" refers to each entry
in the "list".
- 481,C,Right(),C RIGHT(C,N number_of_characters),Returns a
portion of a string starting with the rightmost character.
- 479,C,Now(),T NOW([L gmt_time]),Return time as type 'Time'.
- 474,C,Strtran(),C
STRTRAN(C character,C substring,[C replacement,[N start_pos,[N
occurrences[,N every]]]]),Replaces each occurrence of a string with
another.
- 406,C,Mod(),N MOD(N numerator,N denominator),Returns the
integer remainder of one number divided by another.
- 393,C,W_Count(),N W_COUNT(C character[,C separator]),Counts
number of words in a character string.
- 390,C,Max(),N MAX(N number_1,N number_2),Returns the larger
of two numbers.
- 377,M,A5.Get_Master_Path(),
A5.Get_Master_Path(),Returns the path of the Master database, or null
if current database is not Network Optimized (shadowed).
- 375,C,Word_Change(),C
WORD_CHANGE(C change_word_format,C text[,C separator[,C pattern[,L
match_pattern]]]),Perform operation across a group of words, if pattern
is specified, only change those words matching the pattern.
- 354,C,Int(),N INT(N number),Returns the integer part of a
number.
- 348,C,Wordat(),N WORDAT(C word,C string[,C
separator]),Returns which word matches (1 based,0 means no match).
- 344,M,Statusbar.Set_Text(), STATUSBAR.Set_Text(C
text),Set the status bar text.
- 310,M,A5.Get_Name(), A5.Get_Name(),Returns the
filename of the current database (.adb file).
- 295,C,Remspecial(),C REMSPECIAL(C),Strips all
non-alphabetic and non-numeric characters and spaces from a string.
- 293,M,Table.Current(), TABLE.Current([N
slot]),Returns the current table.
- 289,C,Min(),N MIN(N number_1,N number_2),Returns the lesser
of two numbers.
- 284,M,A5.Get_Master_Name(),
A5.Get_Master_Name(),Returns the Master database, or null if current
database is not Network Optimized.
- 276,C,Version(),C VERSION([C format]),Returns product
information on the current version.
- 274,C,Stritran(),C
STRITRAN(C character,C substring,[C replacement,[N start_pos,[N
occurrences[,N every]]]]),Replaces each occurrence of a string with
another - compare is case insensitive.
- 270,C,Ui_Modeless_Dlg_Close(),V UI_MODELESS_DLG_CLOSE(C
title),Close a named modal or modeless Xdialog box.
- 264,C,Between(),L BETWEEN(A expr_1,A expr_2,A
expr_3),Returns TRUE if the 1st expr is between the 2nd and 3rd expr,
inclusive.
- 257,C,Lookupc(),C
LOOKUPC(C matchtype,A keyvalue,C result_expression,C tablename,C
tagname),Returns the value of a character expression in an external
table.
- 253,C,Padl(),C PADL(C,N length,C pad_string),Pads the left
side of a string with another string.
- 245,S,Line_Count(),N line_count(C text ),Counts the number
of lines in a CRLF delimited string
- 241,M,A5.Get_Path(), A5.Get_Path(),Returns the
path of the current database (.adb file).
- 221,C,Asc(),N ASC(C character[,L unsigned]),Returns the
ASCII value of a character.
- 219,C,Stuff(),C
STUFF(C character,N start_position,N number_of_characters,C
insert_string),Alters a string by deleting characters and replacing
them with another string.
- 203,C,Ui_Modeless_Dlg_Exist(),L UI_MODELESS_DLG_EXIST(C
title),Returns TRUE if named modal or modeless Xdialog box exists.
- 200,C,Occurs(),N OCCURS(C substring,C character),Returns
the number of times one string is found in another.
- 187,M,A5.Get_Exe_Path(),
A5.Get_Exe_Path(),Returns the path in which the main Alpha Five (or
other product) executable is stored.
- 178,C,Ui_Dlg_Box(),C
UI_DLG_BOX(C title,C dialog_format[,C code]),Generic dialog get -
display a modal XDialog, returns button push, plus valus in
variables.
- 177,M,Table.Open(),
TABLE.OpenMap(C tablename ,C fieldlist [,N mode [,C alias [,C
encryption_key ]]]),Open a table with just the specified fieldnames.
- 176,C,Iif(),A IIF(L condition,A result_true,A
result_false),Tests an expression and returns one of two expressions.
- 173,S,Filename_Relative(),C
filename_relative(C filename [,C reference_path ]),Converts a filename
into a relative filename. If reference_path is blank, assume current
database path.
- 172,S,Filename_Absolute(),C filename_absolute(C
filename [,C reference_path ]),Converts a relative filename into an
absolute filename. If reference_path is blank, assume current database
path.
- 170,M,File.Remove(), FILE.Remove(C
file_name),Remove a file.
- 167,C,Ui_Info(),N UI_INFO(N option),Get information about
the user interface.
- 158,C,Ui_Modeless_Dlg_Box(),V
UI_MODELESS_DLG_BOX(C title,C dialog_format[,C code]),Create a modeless
Xdialog box - optional script to handle button requests is the third
argument.
- 152,C,Ui_Get_File(),C
UI_GET_FILE(C title,C file_types[,C default_choice[,C
exist_flag]]),Prompt user for filename. RECOMMENDATION -
Eliminate
this and use an XDialog replacement
- 147,C,Null_Value(),A NULL_VALUE(),Assign a null value (type
Z) to an undefined or "any" type.
- 140,S,Get_From_File(),C get_from_file(C filename ), Returns
the contents of a file as a text string.
- 132,C,Time(),C
TIME([C time_format[,T time]),Returns the current time on the system
clock in the fomat 'HH:MM:SS', optional format controls other times,
passed in time type can be used in place of the current time.
- 129,C,Obj(),P OBJ(C object_name),Resolves a string into the
object it names.
- 120,C,Regex_Grep(),C
REGEX_GREP( text as c , RegExp as c , Format as c[,options as
c]),Performs a regex grep on text. format takes \0 for all text \N for
tagged expression. options same as regex_match with additional options:
- 117,S,Remove_Blank_Lines(),C remove_blank_lines(C text
),Removes blank lines in a CR-LF delimited list
- 117,C,Transform(),C TRANSFORM(A value,C
formatting_string),Returns a character string that formats a user
defined expression.
- 110,C,Hourglass_Cursor(),V HOURGLASS_CURSOR(L show),Show
hourglass cursor, or restore normal cursor.
- 108,M,Addin.Variables(),
ADDIN.Variables([C variable_frame_context]),Returns a pointer to the
variables defined at the <name> level - optional flag for
'local,shared and global'.
- 107,C,Sleep(),V SLEEP(N Seconds),Sleep for specified number
of seconds.
- 104,C,Error_Script_Get(),C ERROR_SCRIPT_GET([N
SESSION]),Report the script that the error occured in.
- 103,M,A5.Get_Private_Path(),
A5.Get_Private_Path(),Returns the current Private path.
- 102,C,Ctod(),D CTOD(C character),Converts a charcter string
in current format to a date value
- 101,C,Replicate(),C REPLICATE(C substring,N
repeat_value),Duplicates a character a specified number of times.
- 100,C,Sortsubstr(),C
SORTSUBSTR(C string,C delimiter[,C Direction][,C Token type]),Returns a
string of sorted tokens from a character string - token type
'N'-numeric 'X'-length.
- 98,C,Eval_Valid(),L EVAL_VALID(C
Expression[,C eval_context[,N session]]),Return True if the expression
can be parsed (expression is properly formed), session context is
optional.
- 95,C,Ui_Dlg_Event(),C UI_DLG_EVENT(C title,C event[,C
flags]),Invoke an action event in the named Xdialog - flags I)mmediate,
W)ait for input idle.
- 95,C,Error_Code_Get(),N ERROR_CODE_GET(),Retreive the last
error code.
- 93,M,Table.Field_Add(),
TABLE.Field_Add(C Fieldname,C Fieldtype[,N Field_Width[,N
Field_Decimal]]),Define the next field in a table.
- 92,S,Save_To_File(),L save_to_file(C string ,C filename [,L
append [,L silent ]]),Save a string to a file
- 92,C,Chrtran(),C
CHRTRAN(C string_1,C string_2,C string_3),Replaces a portion of one
string with another, limited to 1024 characters
- 88,C,Xbasic_Wait_For_Idle(),V
XBASIC_WAIT_FOR_IDLE([N wait_time]),Run current script asynchronously
(after call) and wait for an idle.
- 86,C,Error_Text_Get(),C ERROR_TEXT_GET([N
error_code]),Retreive the error text for an error code (last error code
if ommitted).
- 84,C,Padr(),C PADR(C,N length,C pad_string),Pads the right
side of a string with another string.
- 80,C,Error_Line_Number_Get(),N ERROR_LINE_NUMBER_GET([N
SESSION]),Report the line that the error occured on.
- 78,M,Table.External_Record_Content_Get(),
TABLE.External_record_content_get(c setname ,c content_expression [,c
order [,c filter [,n level ]]]),method
- 77,S,Prop_Valid(),L prop_valid(P dot_variable ,C property
[,L recursive ]),Tests whether a dot variable has a specified property
- 75,C,Properties_Enum(),C PROPERTIES_ENUM(P variables[,C
variable_enum_flags[,C typemap]]),Returns CR-LF serarated property list.
- 74,C,Rat(),N
RAT(C substring,C string[,N occurance]),Returns the position of one
string found in another, relative from the end of the string.
- 74,C,Atc(),N ATC(C search_string,C string[,N
occurrence]),Returns the position where one string is found within
another.
- 71,M,File.To_Blob(), FILE.To_blob(C
Filename),Reads a file to a in memory blob.
- 67,C,Ui_Modeless_Dlg_Refresh(),V
UI_MODELESS_DLG_REFRESH(C title[,L asynchronous]),Refresh the contents
of the named modal or modeless Xdialog box.
- 65,M,Table.External_Field_Name_Get(),
TABLE.External_field_name_get(c setname [,c format ]),method
- 64,M,File.Copy(),
FILE.copy2(C source_filename ,C dest_filename [,L only_copy_if_newer
]),Same as File.Copy(), but if the dest_filename folder does not exist,
then creates it.
- 63,C,Lower(),C LOWER(C character),Converts a character
string to lowercase.
- 61,C,Quote(),C QUOTE(C string[,C quotechar]),Returns the
quoted value - useful for expressions generating other expressions.
- 57,S,Ui_Get_Text(),C
UI_GET_TEXT(C title,C prompt[,C default_choice[,C
format_string]]),Prompt user for text. RECOMMENDATION -
Eliminate
this and use an XDialog replacement
- 57,C,Filter_String(),C
FILTER_STRING(C string ,C sub_string,[C delimiter[,L exclude]]),Filter
in/out words that contain case insensitive sub_string from a list,
default delimiter is CR-LF.
- 53,C,Dir_Put(),C DIR_PUT(C directory_name),Changes the
current directory (on the specified drive).
- 52,S,A5_Getapplicationdatafolder(),C
A5_GetApplicationDataFolder([L lAllUsers ]), Return the application
data folder where settings should be stored
- 52,M,A5.Active(),
A5.Active([L fullname]),Returns the name of the the current MDI
window that has focus (or had focus prior to an XDialog being
used)
- 52,C,Stritran_Multi(),C
STRITRAN_MULTI(C character,C substrings,C replacements),Replaces
occurrences of strings in one cr-lf list with strings in another cr-lf
list - compare is case insensitive.
- 52,C,Recno(),N RECNO([C tablename]),Returns the record
number of the current record.
- 50,M,Table.Index_Add(),
TABLE.Index_Add(C Tagname,C Order_expression[,C Filter_expression[,C
Index_Type]]),Add the next tag.
- 50,M,A5.Table_Enum(), A5.Table_Enum(C lastname[,N
options]),Returns the name of the Tables in the current
database.
- 45,S,A5_Is_Path_Valid(),L a5_is_path_valid(C path_name
),Tests if a folder name is valid.
- 44,C,Sys_Open(),V SYS_OPEN(C Filename),Open a file using
the files registered open method.
- 43,M,A5.Get_Shared_Path(),
A5.Get_Shared_Path(),Returns the current Shared path.
- 42,C,Sys_Shell(),N SYS_SHELL(C command_line[,N
window_style]),Run a program, returing the window handle of the program
run.
- 41,C,Filter_String_Smatch(),C
FILTER_STRING_SMATCH(C string ,C pattern,[C delimiter[,L
exclude]]),Filter in/out words that match (using smatch) the pattern,
default delimiter is CR-LF.
- 40,C,Round(),N ROUND(N number,N decimal_places),Rounds off
a number to a specified number of decimal places.
- 40,C,Ceiling(),N CEILING(N number),Returns the smallest
integer that is greater than or equal to a number.
- 39,S,Odbc_Dt_To_D(),D
odbc_dt_to_d(C odbc_datetime ),Converts a character field containing a
Date-Time value in format "YYYY-MM-DD HH:MM:SS.SSS" to a date field.
- 39,C,Is_Object(),L IS_OBJECT(P object_pointer | C object
name),Tests whether the object named by the pointer or string exists.
- 38,S,Extract_String(),C
extract_string(C string ,C start_string ,C end_string [,N occurrence[,L
include_tags [,L case_sensitive [,L regex_safe ]]]]),Extracts the nth
occurrences of a sub-string starting with 'start_string' and ending
with 'end_string'.
- 37,S,Word_Number_Get(),N word_number_get(C string ,C
substring ,C delimiter [,L
- match_full_words_only [,L caseSensitive ]]), Returns the
word number of the word in which substring was found.
- 37,M,Statusbar.Enable(),
STATUSBAR.Enable(),Enables the StatusBar window.
- 36,C,Space(),C SPACE(N number_of_spaces),Returns a
character string containing a specified number of spaces.
- 33,S,Char_To_Blob(),B char_to_blob(C str ),
- 33,M,Table.Filename_Get(),
TABLE.Filename_Get([C tablename]),Get the filename of the table, if
name specified, returns full path to provided database name.
- 33,C,W_Upper(),C W_UPPER(C character),Capitalizes the first
character of each word in a character string.
- 31,S,Ui_Get_List2(),C
ui_get_list2(C title ,C default ,C choices [,N style [,L
preserve_selection_order ]]),Prompts user for selection from a CR-LF
delimited list of choices. Style: 1 = single select, 2 = multi-select
(click item to select), 3 = multi-select (Shift+Click, Ctrl+Click and
Drag are supported)
- 28,S,Pos_From_Word_Number(),N
pos_from_word_number(C string ,N word_number [,C separator ]),Returns
the starting character position of word_number in string.
- 28,C,Ui_Yield(),N UI_YIELD(),Let windows process messages.
- 27,M,A5.Get_Exe_Name(), A5.Get_Exe_Name(),Returns
the name of the running executable.
- 27,C,Totime(),C TOTIME(N seconds,N format_code,N
decimal_places),Converts a time value to a formatted time character
string.
- 27,C,Tablecount(),N TABLECOUNT(C tablename,C
filter),Returns the number of matching records in a specified table.
- 26,C,Dir_Get(),C DIR_GET(),Returns the current working
directory
- 25,S,Filename_Decode(),C filename_decode(C filename [,C
reference_path ]),Converts aliased, relative filename to real filename
- 25,C,Ui_Get_Radio(),C
UI_GET_RADIO(C title,C default_choice,C choice1[,C choice
2,...]),Prompt user for selection from radio
button. RECOMMENDATION - Eliminate this and use an XDialog
replacement
- 24,S,Ui_Get_Path(),C
ui_get_path(C title ,C drive_list ,C default ),Prompt user for a file
path. If drive_list is blank, shows all drives. Drive_list is a
semi-colon delimited list of drives. RECOMMENDATION -
Eliminate this and use an XDialog replacement
- 23,S,Dir_Create_Recurse(),L dir_create_recurse(C folder
),Creates a folder, creating as many sub-folders as necessary.
- 23,M,Table.Name_Normalize(),
TABLE.Name_Normalize(C name),Normalize a table name.
- 23,C,Padc(),C PADC(C,N length,C pad_string),Pads both sides
of a string with another string.
- 23,C,Case(),A
CASE(L condition_1,A result_1,[L condition,A result]...),Selects first
TRUE clause and returns the appropriate value.
- 23,C,Addmonths(),D ADDMONTHS(D date,N months),Adds a number
of months to a date.
- 22,S,Crlf_To_Comma(),C crlf_to_comma(C list ),Transforms a
CR-LF delimited list to a comma delimited list
- 22,M,Table.Index_Create_Begin(),
TABLE.Index_Create_Begin(C Tagname,C Order_expression[,C
Filter_expression[,C Index_Type]]),Begin creation of a new index, add
the first tag.
- 22,M,File.Create(), FILE.create_time(C filename
),Returns the date/time at which a file was created
- 22,C,Cyear(),C CYEAR(D date),Converts a date to a character
value of the form 'YYYY'.
- 22,C,Ctodt(),T CTODT(C character),Converts a charcter
string to a time value (date + time).
Data
Security Tips (
Email
Comments)
Updated
17 December 2011
First, don't store any personal data (e.g. Credit Card Numbers, Social
Security Numbers, Birthdates, Maiden Names, etc) in regular text. If
possible, pass the buck. Let someone else (e.g. PayPal, Amazon, Google
etc.) store this information and take the risk if possible.
Second,
IF
you do store personal data, see if the First solution is not better.
Otherwise, encrypt those fields. Someone without the decryption
algorithm is immediately locked out of the data.
Then
- If an application user needs to verify, show a portion (e.g
last 4 digits).
- If an application user needs to change it to a new value,
just overwrite it without viewing the old.
- If an application user needs to view it, Log the viewing,
and show it only while he is physically in the field. Leave the field
or revert back to encrypted view after timeout of no more than 5 minutes
- If
an application user never needs to see it and the application itself
only needs to verify it by comparing it to something previously
entered, then encrypt the entered and compared to stored encrypted
original entered value. Using an MD5 hash or similar will pretty much
guarantee that no user without the original value will be able to
generate the same comparison value. This is absolutely the best way.
The CSDA utility licenses incorporate an MD5 hash of a
license #
with other data.
Any encryption/decryption keys used by the program should be hidden as
best as possible within the program (generally this means encrypting
tokens for an AEX, or a password on XBasic code).
Licensing
your Application Tips (
Email
Comments)
Updated
29 April 2012
The
only way to stop unauthorized usage of a desktop application is to
force it to work at only the current
computer or location. This also assumes also that a person
can't
look at the code or discover the methodology or algorithm you employ
(see
Protecting Code).
There are a number of methods that can be used.
The
simplest way to eliminate business copying is to embed the name/address
of the business owner right into a pop up message, telling people that
it is licensed only for that name/address. This could be encoded into
an encrypted serial #/activation code, so the code would not
have
to be changed. This relies on the idea that a person allowing others to
use his license could more easily be caught, and does not always work.
It also assumes the displayed name is valid, and not a made up name.
Other
ways are tying it to a machine/location specific attribute. Some of
these include the machine name, user name, network name, hard drive
serial #, the MAC address (Network card ID), CPU serial #, Windows
serial #, Alpha Five Serial #, etc, and in some cases, could be to a
hardware key (dongle).
These all have drawbacks whenever a user changes his system
(approximately every 3 years), or updates to a new version of software
(changed serial #). However, a replacement computer can
easily be
set to the same machine name as the computer it
replaced, and
thus use the same licensing keys if based on the machine name.
Activation
Activation requires the user's computer send you some
info about the system, and you generate an encrypted equivalent that
you send back to the user that unlocks the software. This needs to be
done via e-mail, web server, or similar, or by a phone for those who do
not have internet. This is essentially how Alpha Five does it.
Once
you have one or more machine-specific values, the program
can generate an
encrypted software
key (normally should be standard, case-insensitive characters, such as
hexadecimal characters) such that it would be difficult (within reason)
for the user to
ever generate it. Then that software key (with
machine
specific info) is sent it back over the
web, email, or phone. Now you have to generate an
activation
key that is sent back to the user "activate" the program.
This
activation code may have an
expiration date encoded in it if it is a renewable or trial license.
If this is the case, you will also need to validate the
actual
date and compare it to the encoded one in your program. One
way
is by checking on the internet. The computer's
date can be set to earlier values in order to bypass the expiration, so
you need to make sure that the date is never earlier than the highest
date ever seen. However, not all machines are connected to the internet
all the time, so you may want to allow for that contingency.
Whatever the activation code is, create an internal activation key that
is encrypted. You can either hide it
someplace on the system making it difficult to find, or as I do for the
CSDA Code Utility
(http://www.csda1.com/csda_codeutility/CSDA_CodeUtility.html), create a
public key that is almost impossible to generate without knowing the
code.
Duplicate
Usage
Note that if a user duplicates the environment of the licensed
computer (e.g. the
same machine name) it allows the software to be used on that duplicate
machine.
One way to stop a duplicate machine is
to not allow sharing of a table or other common
file/resource that
all computers running the software must be required to use. This really
can't be done, unless the usage of the application requires sharing the
resource. You can't share a table from two
computers in Windows if you have the same machine name, but if they run
two totally different applications or networks, it can't be stopped.
Some
software stops the duplicate by actually having the software
check
in with a web site on a regular basis, sending machine specific data to
validate the license. This could be every time,
however, if you lose your internet connection, it
will fail. So some software are set to stop working
if there
is not valid check in at the website within a certain amount of time.
The problem is that you must have a good validation web server working
100% of the time for as long as you have users still using it (or set
it such that if the web site page is accessible (easy to keep up), but
the server is not, then it also runs.
In terms of basic concepts, that's pretty much it. You have to decide
the level of protection you are looking for.
When I do use an md5 hash, I never hash the plain password. You
should always use a "salt" as part of your hash to protect against
dictionary attacks and other ways to break the passwords. For a
simplified example, instead of md5("MyPassword"), I would do md5("this
is some salt text that only I know" + "MyPassword"). And you would
never want to repeat this algorithm on the client because then your
additional measure is fully exposed and defeated.
Typical passwords today have to
be 6+ in length, contain a digit not as the first or last character,
not
contain their login ID, and a few other rules.
Also look at my tips on
Data Security. While I talk about
data, it also applies to license keys etc. My license keys for my
utilities are MD5 encoded and I use various other techniques to make
them hard to recreate even if looking at the source code. By creating a
license # based on the license # and other machine specific values such
as machine name, Alpha serial #, Alpha version # and types, etc it can
be made pretty hard to copy or share on a network.
Alpha
Five Version Release Dates (
Email
Comments)
Updated
20 July 2013
To give you an idea of when a program was developed,
these are the release dates and build numbers of Alpha 5
through
Alpha 11. Note that the system build #'s do not follow a consistent
pattern, however the Addin Library build # (the 2nd pair of 4 digits)
consistently increases by date and version.
Most recent released version 12 is 1507_4150 dated 20130625
1st released version 12 is 1366_4115 dated 20130421
1st Beta released version 12 after v11 primary development stopped is 1289_4098 dated 20130326
1st beta released version 12 is 1241_4080 dated 20130310
Latest Beta release version 11 after v12 release 3662_4155 dated 20130718
Last released version 11 is 3381_4096 dated 20130322
1st released version 11 is 2199_3810 dated 20111020
(Note: Version 10.5 uses the same license as Version 10)
Last released version 10.5 is 4369_3712 dated 20110611
1st released version 10.5 is 2924_3560 dated 20100610
Last released version 10 is 2814_3539 dated 20100316
1st released version 10 is 2515_3388 dated 20091015
Last released version 9 is 2095_3264 dated 20090310
1st released version 9 is 1389_3163 dated 20080327
Last released version 8 is 1980_3163 dated 20080327
1st released version 8 is 1269_3059 dated 20070228
Last released version 7 is 4130_3033 dated 20060814
1st released version 7 is 3872_3001 dated 20051027
Last released version 6 is 1600_2057 dated 20050711
1st released version 6 is 1530_2001 dated 20040726
Last released version 5 is 1506_1058 dated 20040309
1st released version 5 is 1242_1001 dated 20020905
QReportBuilder
Release Info
Most recent released QReport Builder 2012 is ????
1st released QReport Builder 2012 is ????
Last released QReport Builder 2011 is 4335_3709 dated 20110603
1st released QReport Builder 2011 is 2924_3560 dated 20100610
Last released QReport Builder 2009 is 2193_3268 dated 20090507
1st released QReport Builder 2009 is 1600-3196 dated
200xxxxx???
Last released QReport Builder 2008 is 2029_3183 dated 20080515
1st released QReport Builder 2008 is 1804_3115 dated 20071005
Using
Dates in databases (
Email
Comments)
Updated
06 April 2012
Alpha Five has a very large number of
date
and time conversion
and manipulation functions.
When using DBF files (dBase III combatible), 4 data types are relevant,
which are
- Date (stores just the date, and is a standard DBF type)
Adding and Subtracting a number changes the date by that many days
- Time (which stores Date, Time and
microseconds and is not a standard type)
Adding and Subtracting a number changes the date/time by that many
seconds
- Short Time (which stores just the 24 hour time and is not a
standard type)
Adding and Subtracting a number changes the time by that many
seconds
- Character (which can store a text version of the date
and/or time, and is a standard DBF type).
- Numeric (which can store a number version of 1 portion of
the date or time).
You
should use one of the 1st 3 data types for storage of most
date
and time information. The numeric type is probably never
really useful unless you are just storing the year.
However,
if you ever want to store a partial date, the character data type is
generally best.
An example are credit cards that typically
have expiration dates of just the year and month. You could store it as
a
date type with a day of the last day in the month, and ignore
the
day of the month portion as needed in other expressions.
Alternatively you could store just the year and month as a
character string.
Another example is a birthdate. Some people may
only give some portion of the date, typically either leaving out the
year, or leaving out the day and month. If
you are storing a date that only partial information is available,
store it as a character string, leaving blank the unknown portions. If
you ever obtain the additional info, you can fill it in.
Format
of a Date stored in a character string
When you create a date
in a character string in just about any database (or other places such
as a file name), you should generally store the full
date in the format of YYYYMMDD. If you don't need the days,
then
YYYYMM or for year only, YYYY.
If you want, you can extend the format with time, as in YYYYMMDDHHMMSS
In
any case, make sure you always store the full 4 digit year, otherwise,
you are not Y2K compliant, and there can be issues as to what century
appies to a date.
These concepts applies when using a date in file
naming, data storage (in character form, however Date, and Time
fields already store it this way internally) and usage in index or sort
expressions. Only for display or entering a date and/or time,
should you change the format to a
human-readable format .
The main reason for this it automatically
orders the data by year, then month then day. If used in a
filename, particularly at the beginning, it allows easy sorting by date
by sorting by the filename.
Another
reason is the format is not specific to the United
States
(format mm/dd/yyyy), or most of Europe (format dd-mm-yyyy), but is a
format that everyone can use.
Placing dates in a filename
allows a sequence of similar filenames to be sorted by date.
You
could use the file Create date, or file modify date to sort but both of
these dates can get modified with some copy and archiving
procedures. File modify dates can be changed every time you open it up
in some editors. By placing the date in the filename, it is less likely
the date will change.
If
you go to the Alpha Five control Panel, and select from the menu, View,
Settings..., then go to Preferences, then Advanced followed by Archive
Reports File Name, it will show you one example where Alpha Five allows
setting the format for the date/time portion of a file name. This is
used for saving the results of a layout (form, browse, label, letter,
report) to a file. But this is not used everywhere within
Alpha
Five.
Some examples of good file naming formats with dates in them are:
This case allows sorts by all the files with the same "filename" and
then by date
filename1_20121122.txt
filename1_20121123.txt
filename1_20121124.txt
This
case is just an extension of the above, and allows sorts by all the
files with the same "filename" and then by date, descriptions don't
really matter for the sort
filename1_20121122_description1.txt
filename1_20121123_description2.txt
filename1_20121124_description3.txt
This case allows sorts by all dates 1st, then by filename grouping all
dates together
20121122_filename1.txt
20121123_filename1.txt
20121123_filename2.txt
20121124_filename1.txt
20121124_filename2.txt
Date
Input Format
Certain
date functions use the current character date format of
mm/dd/yyyy
(American) or dd/mm/yyyy (European) or possibly another format which
can depend upon the Windows default date settings, or an Alpha Five
date settings. If you want your XBasic code to be independent
of
this, you must either use code to handle the particular cases, or use a
date function that works independent of this setting
To detect which format you are using, you can use IF expressions
similar to this
?IF(day({01/02/2012})=2,"American","European")
= "American"
?IF(day({01/02/2012})=2,.T.,.F.)
= .T.
Note
that the date constant above never needs to be changed, it is basically
returning whether the 1st or 2nd part of the date constant is being
interpreted as a day of the month within Alpha Five.
Date
constants are evaluated based upon the default setting format, so a
date constant of {03/04/2012} will always yield a character version of
the date with DTOC({03/04/2012}) as "03/04/2012", but which part is the
day or month? You
can't really tell.
Internally the date is stored in
effectively a yyyymmdd format, so once you have a date in a date type
variable, or stored in a date field, it's value will always be that
date value, even if you change the Date default format setting.
The meaning when you 1st created the value will stay, even if
the
default setting format changes.
But in Alpha Five, date type values are
inputted in the current date default setting format. When you specify a
date
constant of {ww/xx/yyyy} where ww and xx will be interpreted
as
a day or month depending on the format. Similarly, if you
input
into a field, the stored date value will depend on the current date
default setting.
If
you specified a date constant in an expression such as
DAY({03/04/2012}), it will return 4 in American format, but a 3 in
European format. Similarly,
MONTH({03/04/2012}), will return
3 in American format, but a 4 in European format. CDATE({03/04/2012})
will also return different results. In America you will get
"20120304" but in European, "20120403". But these different
values are because the date value stored by the date constant
{03/04/2012} was different based upon the settings. It was not the
function, but the conversion from input of the constant based
upon
the format setting to the internal date format.
Had
you instead used CTOD("03/04/2012") to convert the string constant, you
would have found that the date stored would have been March 4th, 2012,
or April 3rd, 2012 depending upon the current date default setting
format.
But once a
date is stored correctly in a date type variable or field, that date is
that date. One of the best ways to store date values that is
format independent is using code similar to this
dim datevalue1 as D
datevalue1=stod("20120304")
Since
STOD() always use a string format of yyyymmdd, the date value is always
what you want. DAY(datevalue1) will always return a 4 in the above code
Note
many Alpha Functions as well as date fields on a layout have a display
format for a date, but this is not the format you actually enter the
date in or specify in a constant. The input format will
always be
the
default date setting format, but once you stop inputting the field (by
leaving focus of the field, or by saving the record), you will see the
display format.
Windows
7 (and Vista) compatibility (
Email
Comments)
Updated
09 February 2013
To install in
Windows 7
A5v10 and a5V11 can just be installed and used
normally. For earlier versions of A5, use the following procedures:
Windows Automated way
- Right click on a5vX shortcut icon and select Troubleshoot
compatibility
- Select "Try recommended settings"
- Then select Start the program... and
then exit A5
- Select Next
- Select Yes, save these settings for this program
- Select Close
Note that in earlier versions of A5, it will suggest
Windows
XP (Service Pack 2), but
Windows
XP (Service Pack 3) is probably better to use.
Windows Manual way
(Faster and easier to do)
- Right click on A5v9, A5v8, a5v7 or a5v6 icon and select Properties
- Go to Compatibility tab
- If you want to use these for only the current user then
change this page, otherwise Select "Change settings for all
users"
- check box Compatibility mode and set
pull down to "Windows XP (Service Pack 3)"
- Optionally, check box "Run this program as an
administrator".
This seems to do nothing in terms of the UAC popup, but may
be
helpful to access some folders that may otherwise be restricted.
Even
though this seems to basically works, some testing of A5v7 runtime
applications required UAC to be totally off for some operations to
work. So if you experience any issues running this way, turn
off
UAC on each system using it. Other versions up through
A5v10.5
may also experience issues along these lines, so try lowering the UAC
level until it works (which may in fact require it to be totally off).
A5v5
The
earliest releases of A5v5
seems to almost work except 4 drivers don't install properly and menus
may not
appear. After installation, install the latest A5v5 patch, and the 4
drivers that have errors will go away. If the menus don't appear, then
you have one of two choices:
- If you have menu and/or toolbar display
issues within A5V5 when you start, you can try this;
- Start Windows 7 Control Panel
- In the top right search box, type "Performance"
- Select "Performance Information and Tools".
- Select "Adjust Visual Effects"
- Choose "Custom"
- Scroll down to the bottom and uncheck "Use visual
styles on windows and buttons"
- Press "Apply" button
Restart Apha 5 Version 5
Note:
The display of windows desktop and other programs will have
a changed look in the buttons and menus as a result of this
setting.
- Install and run a5v5 in a "Windows XP mode"
virtualized window. To learn more how to install Windows XP mode, see
here
Windows
XP mode operation
In
Windows XP
mode
window, A5v5 through A5v11 all install and operate fine, with the
possible exception that A5v7 may have an issue with a
driver.
Theory
of Deduping (
Email
Comments)
Updated
11 Apr 2013
If you have data, it is almost impossible not to have data that
has multiple copies of the same data. This often comes from importing
data that has duplicate data to what you already have, or a 2nd manual
entry of the data, when it is not realized hat a record already exists.
Even
if you have a key like Social Security Number (SSN) that is unique to
each record, occasionally a mistake entering the data, possibly due
to not reading the crawled handwriting, transposing 1 or more
characters, or just a transcription error, may, on occasion result in 2
or more records with the key.
So you may think, I'll just
create a unique key index, so a second copy can't be entered.
Let's suppose that the 1st SSN is typed in incorrectly from
what
it really is. If no one else has that same wrong SSN, it will
accept the record save. Later, another entry from the same person is
now typed in with the correct SSN. The 2nd record will be
saved
as well, creating a 2nd record representing the same person. So it is
very possible, even with a unique key, you can get duplicate records if
there is a typing mistake on the unique key fields of the data. And if
you don't have a really unique field, you may end with,
e.g. multiple John Smiths in the data, where many are really
the
same.
So identifying duplicates is important at entry time,
as well as later. This leaves us with the problem of how to
remove the duplicates, or "Deduping". There is seldom any 1
single method that can allow for deduping, and it is very data
dependent. It depends on what data you have/collect, and how
clean the data is.
E.g. unclean data would have birthdates
in the future, or over 110 years in the past (from when the record was
created assuming it's not a historical list).
So the 1st steps are to clean your data as best as possible.
Some of these include
- All relevent data fields should not have any leading spaces
- All
fields should either be in exactly the same format, e.g. a SSN like
123-45-6789, or alternatively translated to the same format, as in
remspecial(SSN) which would yield 123456789 for the previous example.
Phone Numbers are another example.
- Case should normally be ignored, or translated to the same
case when checking data
- All
addresses should be standardized to the standard format, and have a
full zipcode. In the US, this is an 11 digit zipcode that
indicates the location to a building. There are functions in
later Alpha Five versions to help with the normalization of data by
using the USPS web site to standardize the address.
- You should
have a field that indicates the create date of a record. Date
of
Births are normally never after the create date (in the future) nor
older than 110 years prior to the create date.
- Date
of Deaths, should normally never be before the birth date, nor too far
in the past from today's date if dealing with typical names
- Special characters except commas, spaces and hyphens should
not normally appear in names
- Credit card numbers should all have valid checksums
To
dedup your data records, the best way is to have a variety of fields
that either individually or as a group create different unique
keys of a record. That way, if there is a mistake in a field, or
portion of a field, the likelyhood of a data error will be caught by
one of the groupings of unique data.
More to be added later
Easier Customer
Support Techniques (
Email
Comments)
Updated
01 May 2013
If you have an application that others are using, being able to support
it is a primary concern.
There are things you can change in your application that can make it
easier to support your users.
1. All forms, browses, reports etc should have a code number in some
location (e.g. lower right corner) that is unique to it. That
way when some user says they pressed the print button on a form, you
can get information as to what form.
2. You can add error handling to code to display relevant information,
e.g. script/function name, line number, error #, and maybe
some of the key calling parameters, so the user can report these back
to you. Alternatively, you can write this data to an error
log file that you can then examine after the failure.
3. Somewhat more difficult, you can create a breadcrumb variable that
concatonates the path that the user took to arrive at the button as it
goes from one menu level to another so that you would have a path, then write this to an error log file
4. Create a transaction file that shows the various buttons, forms, and
operations, user, date/time, etc that are being run, and maybe include
error info as well. You may want to limit it to only the last
x number of days or maximum size of the file, throwing away the older
data.