Is there such an external?

Having trouble using an XCmd or XFcn? Post your questions here.

Is there such an external?

Postby drpitcairn » Fri Aug 09, 2013 4:18 pm

I have a project in which a large database is searched, all text. If the text were in a book it would be a largish book, so quite a bit of text to search through. We have a working script that allows a user to put in one, two, or more words into a search field and these words are found if they all occur in the same sentence. This sentence then copied and put into a field. So the outcome is a listing of sentences.
It works just fine. The only thing is that it takes some time because the database is large. On my Macbook Pro running 10.6.8 with a SSD, most searches takes several seconds, perhaps 8-10 seconds for some searches.
Would be nice is there was an external that did the same thing but I have not found it looking on line.
Does anyone know if there is such?

Thanks,
Richard
User avatar
drpitcairn
 
Posts: 1023
Joined: Thu Apr 02, 2009 9:39 am
Location: Sedona, Arizona

Re: Is there such an external?

Postby vinnie-bob » Fri Aug 09, 2013 4:52 pm

two things come to mind...

1) Supercard can index things to speed up searches -- that is how the help project gives you such fast results and why it sometimes says it has to generate an index. I haven't ever had the need for this, so you may want to dig around in the documentation or ask Scott.

2) Tomas originally wrote a SQLite external which did quite well in speedily searching the database. I think Alec Hole took this over or was involved in patching it up to get it working with a newer version of SC and Mac OS a while back, and it can be found here:
http://hairyhighlandcow.net/software/SC-Projects.html
The only catch is that you will have to read up on SQLite queries as it requires a specific grammar, but it isn't that hard and there are many tutorials online.
------
vince
------
User avatar
vinnie-bob
 
Posts: 219
Joined: Sun Jul 06, 2008 10:55 am
Location: Des Moines, Iowa, USA

Re: Is there such an external?

Postby drpitcairn » Fri Aug 09, 2013 6:37 pm

Thanks very much for these hints. I will check them both out.
User avatar
drpitcairn
 
Posts: 1023
Joined: Thu Apr 02, 2009 9:39 am
Location: Sedona, Arizona

Re: Is there such an external?

Postby drpitcairn » Fri Aug 09, 2013 6:55 pm

There is nothing in the language guide about index. Well, more accurately, it brings up "don't index" options. Sort of implies that it is done by default. But it also talks about using the Spotlight plugin, so I don't know indexing is simply allowing spotlight searches? Or some other function I can use.
User avatar
drpitcairn
 
Posts: 1023
Joined: Thu Apr 02, 2009 9:39 am
Location: Sedona, Arizona

Re: Is there such an external?

Postby drpitcairn » Fri Aug 09, 2013 6:56 pm

The link for SQlite no longer works. Apparently out of date.
User avatar
drpitcairn
 
Posts: 1023
Joined: Thu Apr 02, 2009 9:39 am
Location: Sedona, Arizona

Re: Is there such an external?

Postby vinnie-bob » Sat Aug 10, 2013 7:18 am

Richard, you are now seeing the potential downside of 3rd party externals. In actuality though, the web site is throwing 404 errors for every link on that page, so it is probably more of a web site issue, although the fact that the last update for the external was 2008, and it is likely that it might be broken again, due to age.

SQLite is available on every Mac using the shell command sqlite3...here's the man page:
Code: Select all
SQLITE3(1)

NAME
       sqlite3 - A command line interface for SQLite version 3

SYNOPSIS
       sqlite3 [options] [databasefile] [SQL]

SUMMARY
       sqlite3  is  a  terminal-based front-end to the SQLite library that can
       evaluate queries interactively and display the results in multiple for-
       mats.  sqlite3 can also be used within shell scripts and other applica-
       tions to provide batch processing features.

DESCRIPTION
       To start a sqlite3 interactive session, invoke the sqlite3 command  and
       optionally  provide  the name of a database file.  If the database file
       does not exist, it will be created.  If the database file  does  exist,
       it will be opened.

       For  example, to create a new database file named "mydata.db", create a
       table named "memos" and insert a couple of records into that table:

       $ sqlite3 mydata.db
       SQLite version 3.1.3
       Enter ".help" for instructions
       sqlite> create table memos(text, priority INTEGER);
       sqlite> insert into memos values('deliver project description', 10);
       sqlite> insert into memos values('lunch with Christine', 100);
       sqlite> select * from memos;
       deliver project description|10
       lunch with Christine|100
       sqlite>

       If no database name is supplied, the ATTACH sql command can be used  to
       attach  to  existing  or create new database files.  ATTACH can also be
       used to attach to multiple databases within the same  interactive  ses-
       sion.   This  is  useful for migrating data between databases, possibly
       changing the schema along the way.

       Optionally, a SQL statement or set of SQL statements can be supplied as
       a  single  argument.   Multiple statements should be separated by semi-
       colons.

       For example:

       $ sqlite3 -line mydata.db 'select * from memos where priority > 20;'
           text = lunch with Christine
       priority = 100

   SQLITE META-COMMANDS
       The interactive interpreter offers a set of meta-commands that  can  be
       used to control the output format, examine the currently attached data-
       base files, or perform  administrative  operations  upon  the  attached
       databases (such as rebuilding indices).   Meta-commands are always pre-
       fixed with a dot (.).

       A list of available meta-commands can be viewed at any time by  issuing
       the '.help' command.  For example:

       sqlite> .help
:
     .databases             List names and files of attached databases
       .dump ?TABLE? ...      Dump the database in an SQL text format
       .echo ON|OFF           Turn command echo on or off
       .exit                  Exit this program
       .explain ON|OFF        Turn output mode suitable for EXPLAIN on or off.
       .header(s) ON|OFF      Turn display of headers on or off
       .help                  Show this message
       .import FILE TABLE     Import data from FILE into TABLE
       .indices TABLE         Show names of all indices on TABLE
       .mode MODE ?TABLE?     Set output mode where MODE is one of:
                                csv      Comma-separated values
                                column   Left-aligned columns.  (See .width)
                                html     HTML <table> code
                                insert   SQL insert statements for TABLE
                                line     One value per line
                                list     Values delimited by .separator string
                                tabs     Tab-separated values
                                tcl      TCL list elements
       .nullvalue STRING      Print STRING in place of NULL values
       .output FILENAME       Send output to FILENAME
       .output stdout         Send output to the screen
       .prompt MAIN CONTINUE  Replace the standard prompts
       .quit                  Exit this program
       .read FILENAME         Execute SQL in FILENAME
       .schema ?TABLE?        Show the CREATE statements
       .separator STRING      Change separator used by output mode and .import
       .show                  Show the current values for various settings
       .tables ?PATTERN?      List names of tables matching a LIKE pattern
       .timeout MS            Try opening locked tables for MS milliseconds
       .width NUM NUM ...     Set column widths for "column" mode
       sqlite>

OPTIONS
       sqlite3 has the following options:

       -init file
              Read and execute commands from file , which can contain a mix of
              SQL statements and meta-commands.

       -echo  Print commands before execution.

       -[no]header
              Turn headers on or off.

       -column
              Query results will be displayed in  a  table  like  form,  using
              whitespace characters to separate the columns and align the out-
              put.

       -html  Query results will be output as simple HTML tables.

       -line  Query results will be displayed with one value  per  line,  rows
              separated  by  a  blank  line.   Designed to be easily parsed by
              scripts or other programs

       -list  Query results will  be  displayed  with  the  separator  (|,  by
              default) character between each field value.  The default.

       -separator separator
              Set output field separator.  Default is '|'.

       -nullvalue string
              Set  string used to represent NULL values.  Default is '' (empty
              string).

       -version
              Show SQLite version.

       -help  Show help on options and exit.

INIT FILE
       sqlite3 reads an initialization file to set the  configuration  of  the
       interactive  environment.   Throughout  initialization,  any previously
       specified setting can be overridden.  The sequence of initialization is
       as follows:

       o The default configuration is established as follows:

       mode            = LIST
       separator       = "|"
       main prompt     = "sqlite> "
       continue prompt = "   ...> "

       o  If the file ~/.sqliterc exists, it is processed first.  can be found
       in the user's home directory, it is read and processed.  It should gen-
       erally only contain meta-commands.

       o If the -init option is present, the specified file is processed.

       o All other command line options are processed.

SEE ALSO
       http://www.sqlite.org/
       The sqlite-doc package



--------------------- My NOTES

here's how to do multiple commands using the command line:

G5-QUAD:Desktop vangelon$ sqlite3 mydata.db "create table memos(text,priority INTEGER);insert into memos values('deliver project',10);insert into memos values('lunch with christine',100);"
G5-QUAD:Desktop vangelon$ sqlite3 mydata.db "select * from memos"
deliver project|10
lunch with christine|100



And here is some example code which shows how to import CSV data...not sure who I stole this from, my apologies to the original author (I'd guess it was Dave Suanders...the infamous DCS!).
Code: Select all
Replace delimiters for COMMA with TAB if want to import tab delim data



on mouseup
importCSVtoSQLite3
put result() into cd fld filetext
end mouseup

on importCSVtoSQLite3
put linefeed into lf

put projectpath(this proj) & "sql:" into vp
new folder vp

--get the unix path
--replace with you're favorite mac to posix function
put posixpath(vp) & "" into vu

--get mac path of file to import
answer file "select CSV file to import"
if it = "" then return ""
put it into ipath

--get unix path of file to import
put posixpath(ipath) into iurl

--read the source file
open file ipath
read from file ipath until eof
close file ipath

--get the column count
--watch console for field count error messages
--may need to tweak col count if last item of it  ""
put the number of items in line 1 of it + 1 into xmax

--save source data for import
put vp & "itemp" into temppath
put vu & "itemp" into tempurl
open file temppath
write replace(it,cr,linefeed) to file temppath
close file temppath

--define database file name
put "importtest.db" into dbname

--define table name
put "import table" into tblname

--build simple column name list
repeat with x = 1 to xmax
put "c" & x into item x of colnames
end repeat

--get database schema
put merge("cd '[[vu]]';sqlite3 '[[dbname]]' '.schema'") into cmd
put shell(cmd)& cr into dbschema

--create table if necessary
if merge("create table `[[tblname]]`") is not in dbschema then
put merge("cd '[[vu]]'; sqlite3 '[[dbname]]' 'create table
`[[tblname]]`([[colnames]])'") into
cmd
put shell(cmd) into junk
end if

--make an init file for import
--set field delimiter and import source
put vp & "initImport" into initpath
open file initpath
write merge(".separator ','[[lf]].import 'itemp' `[[tblname]]`") to file
initpath
close file initpath

--import file and get all table records
put merge("cd '[[vu]]';sqlite3 -init 'initImport' '[[dbname]]' 'select * from
`[[tblname]]`'")
into cmd
put shell(cmd) into entireTable

--clean up
delete file temppath
delete file initpath
--done
return entiretable
end importCSVtoSQLite3


And I have posted a simple demo of creating and searching a SQLite database in the Demo area of this board. Available here:
http://forums.supercard.us/viewtopic.php?f=17&t=1848
hth....
------
vince
------
User avatar
vinnie-bob
 
Posts: 219
Joined: Sun Jul 06, 2008 10:55 am
Location: Des Moines, Iowa, USA

Re: Is there such an external?

Postby drpitcairn » Sat Aug 10, 2013 7:54 am

Vince,
Thank you so very much for taking the time to walk me through this. I have downloaded your test file and will study this. Will take me some time but now I have a direction.

With appreciation,
Richard
User avatar
drpitcairn
 
Posts: 1023
Joined: Thu Apr 02, 2009 9:39 am
Location: Sedona, Arizona

Re: Is there such an external?

Postby drpitcairn » Sat Aug 10, 2013 9:23 am

I found this training program on Lynda.com for "SQLite 3 with PHP Essential Training". Think it would be helpful to me to learn how to use this? The part I am not sure about is the "PHP" as I don't know if that is a different way of using it. On the other hand perhaps it doesn't matter, as long as I know how to use SQLite in that environment, I can translate?
User avatar
drpitcairn
 
Posts: 1023
Joined: Thu Apr 02, 2009 9:39 am
Location: Sedona, Arizona

Re: Is there such an external?

Postby HairyHighlandCow » Sun Aug 11, 2013 10:59 am

Hi,
I recently changed my website hosting. I forgot to move across the folder containing my SuperCard externals. I have now uploaded it again so you should be able to download the SQLite external again from this page:

http://hairyhighlandcow.net/software/SC-Projects.html

Unfortunately due to the amount of work I have on at the moment I am unable to provide support for externals. However, there are a lot of resources for the SQLite language on the internet.
Thanks
Alec
Externals, projects and software made with SC:
www.hairyhighlandcow.net/software/SC-projects.html
User avatar
HairyHighlandCow
 
Posts: 242
Joined: Sun Jul 06, 2008 1:45 pm
Location: London, UK

Re: Is there such an external?

Postby drpitcairn » Sun Aug 11, 2013 11:15 am

Thanks Alec. Will check it out.
User avatar
drpitcairn
 
Posts: 1023
Joined: Thu Apr 02, 2009 9:39 am
Location: Sedona, Arizona

Re: Is there such an external?

Postby vinnie-bob » Sun Aug 11, 2013 11:54 am

PHP has to do with website programming...you don't need that. I was talking about free tutorials which teach you how to construct SQLite searches. I believe Tomas' original external had a link to a tutorial site which went over the search/reporting syntax (since you will need to know how to construct these when you use the external), but I am not sure if that link is still there, or whether the tutorial site still exists.

Below are search syntax examples I copied from one of the websites, long ago.

Code: Select all
SELECT column_name(s) FROM table_name
   SELECT LastName,FirstName FROM Persons
   
Select All Columns
To select all columns from the "Persons" table, use a * symbol instead of column names, like this: 
SELECT * FROM Persons


The SELECT DISTINCT Statement
The DISTINCT keyword is used to return only distinct (different) values.
The SELECT statement returns information from table columns. But what if we only want to select distinct elements?
With SQL, all we need to do is to add a DISTINCT keyword to the SELECT statement:

Syntax
SELECT DISTINCT column_name(s) FROM table_name
   SELECT DISTINCT Company FROM Orders
   
   
The WHERE Clause 
To conditionally select data from a table, a WHERE clause can be added to the SELECT statement.

Syntax
SELECT column FROM table WHERE column operator value

With the WHERE clause, the following operators can be used:
Operator      Description
=            Equal
<>            Not equal
>            Greater than
<            Less than
>=            Greater than or equal
<=            Less than or equal
BETWEEN         Between an inclusive range
LIKE         Search for a pattern
IN            If you know the exact value you want to return for at least one                
            of the columns

eg: SELECT * FROM Persons
WHERE City='Sandnes'


The LIKE Condition
The LIKE condition is used to specify a search for a pattern in a column.

Syntax
SELECT column FROM table
WHERE column LIKE pattern

A "%" sign can be used to define wildcards (missing letters in the pattern) both before and after the pattern.

Using LIKE
The following SQL statement will return persons with first names that start with an 'O':
SELECT * FROM Persons
WHERE FirstName LIKE 'O%'

The following SQL statement will return persons with first names that end with an 'a':
SELECT * FROM Persons
WHERE FirstName LIKE '%a'

The following SQL statement will return persons with first names that contain the pattern 'la':
SELECT * FROM Persons
WHERE FirstName LIKE '%la%'


ADDING A ROW
The INSERT INTO Statement
The INSERT INTO statement is used to insert new rows into a table.
Syntax
INSERT INTO table_name
VALUES (value1, value2,....)
You can also specify the columns for which you want to insert data:
INSERT INTO table_name (column1, column2,...)
VALUES (value1, value2,....)

you dont have to put a value in every cell:
INSERT INTO Persons (LastName, Address)
VALUES ('Rasmussen', 'Storgt 67')

would insert into its table like this:

Last      First      Address      City
Angeloni   vince      7272 eagle   Johnsto
Rassmussen            Storgt 67

To add her first name later:

UPDATE Person SET FirstName = 'Nina'
WHERE LastName = 'Rasmussen'

gives:   Rassmussen   Nina   Storgt 67

updating several cells at a time:
UPDATE Person
SET Address = 'Stien 12', City = 'Stavanger'
WHERE LastName = 'Rasmussen'

The DELETE Statement
The DELETE statement is used to delete rows in a table.
Syntax
DELETE FROM table_name
WHERE column_name = some_value
   eg DELETE FROM Person WHERE LastName = 'Rasmussen'
   
Delete All Rows
It is possible to delete all rows in a table without deleting the table. This means that the table structure, attributes, and indexes will be intact:
DELETE FROM table_name
or
DELETE * FROM table_name


The ORDER BY keyword is used to sort the result.

Sort the Rows
The ORDER BY clause is used to sort the rows.
   SELECT Company, OrderNumber FROM Orders
ORDER BY Company

To display the company names in alphabetical order AND the OrderNumber in numerical order:
SELECT Company, OrderNumber FROM Orders
ORDER BY Company, OrderNumber

To display the company names in reverse alphabetical order:
SELECT Company, OrderNumber FROM Orders
ORDER BY Company DESC

To display the company names in reverse alphabetical order AND the OrderNumber in numerical order:
SELECT Company, OrderNumber FROM Orders
ORDER BY Company DESC, OrderNumber ASC


AND & OR
AND and OR join two or more conditions in a WHERE clause.
The AND operator displays a row if ALL conditions listed are true. The OR operator displays a row if ANY of the conditions listed are true.

Use AND to display each person with the first name equal to "Tove", and the last name equal to "Svendson":
SELECT * FROM Persons
WHERE FirstName='Tove'
AND LastName='Svendson'

Use OR to display each person with the first name equal to "Tove", or the last name equal to "Svendson":
SELECT * FROM Persons
WHERE firstname='Tove'
OR lastname='Svendson'

You can also combine AND and OR (use parentheses to form complex expressions):
SELECT * FROM Persons WHERE
(FirstName='Tove' OR FirstName='Stephen')
AND LastName='Svendson'

IN
The IN operator may be used if you know the exact value you want to return for at least one of the columns.
SELECT column_name FROM table_name
WHERE column_name IN (value1,value2,..)
o display the persons with LastName equal to "Hansen" or "Pettersen", use the following SQL:
SELECT * FROM Persons
WHERE LastName IN ('Hansen','Pettersen')

BETWEEN ... AND
The BETWEEN ... AND operator selects a range of data between two values. These values can be numbers, text, or dates.
SELECT column_name FROM table_name
WHERE column_name
BETWEEN value1 AND value2
To display the persons alphabetically between (and including) "Hansen" and exclusive "Pettersen", use the following SQL:
SELECT * FROM Persons WHERE LastName
BETWEEN 'Hansen' AND 'Pettersen'
To display the persons outside the range used in the previous example, use the NOT operator:
SELECT * FROM Persons WHERE LastName
NOT BETWEEN 'Hansen' AND 'Pettersen'

Joins and Keys
Sometimes we have to select data from two or more tables to make our result complete. We have to perform a join.
Tables in a database can be related to each other with keys. A primary key is a column with a unique value for each row. Each primary key value must be unique within the table. The purpose is to bind data together, across tables, without repeating all of the data in every table.
In the "Employees" table below, the "Employee_ID" column is the primary key, meaning that no two rows can have the same Employee_ID. The Employee_ID distinguishes two persons even if they have the same name.
When you look at the example tables below, notice that: 
   •   The "Employee_ID" column is the primary key of the "Employees" table
   •   The "Prod_ID" column is the primary key of the "Orders" table
   •   The "Employee_ID" column in the "Orders" table is used to refer to the persons in the "Employees" table without using their names
Example
Who has ordered a product, and what did they order?
SELECT Employees.Name, Orders.Product
FROM Employees, Orders
WHERE Employees.Employee_ID=Orders.Employee_ID
Example
Who ordered a printer?
SELECT Employees.Name
FROM Employees, Orders
WHERE Employees.Employee_ID=Orders.Employee_ID
AND Orders.Product='Printer'
 
 
 UNION
The UNION command is used to select related information from two tables, much like the JOIN command. However, when using the UNION command all selected columns need to be of the same data type.
Note: With UNION, only distinct values are selected.
SQL Statement 1
UNION
SQL Statement 2
   Example
List all different employee names in Norway and USA:
SELECT E_Name FROM Employees_Norway
UNION
SELECT E_Name FROM Employees_USA

Note: This command cannot be used to list all employees in Norway and USA. In the example above we have two employees with equal names, and only one of them is listed. The UNION command only selects distinct values.

UNION ALL
The UNION ALL command is equal to the UNION command, except that UNION ALL selects all values.
SQL Statement 1
UNION ALL
SQL Statement 2


Using the UNION ALL Command
Example
List all employees in Norway and USA:
SELECT E_Name FROM Employees_Norway
UNION ALL
SELECT E_Name FROM Employees_USA

Create a Database
To create a database:
CREATE DATABASE database_name


Create a Table
To create a table in a database:
CREATE TABLE table_name
(
column_name1 data_type,
column_name2 data_type,
.......
)
Example
This example demonstrates how you can create a table named "Person", with four columns. The column names will be "LastName", "FirstName", "Address", and "Age":
CREATE TABLE Person 
(
LastName varchar,
FirstName varchar,
Address varchar,
Age int
)
This example demonstrates how you can specify a maximum length for some columns:
CREATE TABLE Person 
(
LastName varchar(30),
FirstName varchar,
Address varchar,
Age int(3) 
)
The data type specifies what type of data the column can hold. The table below contains the most common data types in SQL:

Data Type            Description

integer(size)
int(size)
smallint(size)         
tinyint(size)         Hold integers only. The maximum number of digits are
                  specified in parenthesis.


decimal(size,d)
numeric(size,d)         Hold numbers with fractions. The maximum number of digits are specified in "size". The maximum number of digits to the right of the decimal is specified in "d".

char(size)            Holds a fixed length string (can contain letters, numbers, and special characters). The fixed size is specified in parenthesis.

varchar(size)         Holds a variable length string (can contain letters, numbers, and special characters). The maximum size is specified in parenthesis.

date(yyyymmdd)         Holds a date


Create Index
Indices are created in an existing table to locate rows more quickly and efficiently. It is possible to create an index on one or more columns of a table, and each index is given a name. The users cannot see the indexes, they are just used to speed up queries. 
Note: Updating a table containing indexes takes more time than updating a table without, this is because the indexes also need an update. So, it is a good idea to create indexes only on columns that are often used for a search.
A Unique Index
Creates a unique index on a table. A unique index means that two rows cannot have the same index value.
CREATE UNIQUE INDEX index_name
ON table_name (column_name)
The "column_name" specifies the column you want indexed.
A Simple Index
Creates a simple index on a table. When the UNIQUE keyword is omitted, duplicate values are allowed.
CREATE INDEX index_name
ON table_name (column_name)
The "column_name" specifies the column you want indexed.
Example
This example creates a simple index, named "PersonIndex", on the LastName field of the Person table:
CREATE INDEX PersonIndex
ON Person (LastName)
If you want to index the values in a column in descending order, you can add the reserved word DESC after the column name:
CREATE INDEX PersonIndex
ON Person (LastName DESC)
If you want to index more than one column you can list the column names within the parentheses, separated by commas:
CREATE INDEX PersonIndex
ON Person (LastName, FirstName)

Truncate a Table
What if we only want to get rid of the data inside a table, and not the table itself? Use the TRUNCATE TABLE command (deletes only the data inside the table):
TRUNCATE TABLE table_name

ALTER TABLE
The ALTER TABLE statement is used to add or drop columns in an existing table.
ALTER TABLE table_name
ADD column_name datatype
ALTER TABLE table_name
DROP COLUMN column_name
Note: Some database systems don't allow the dropping of a column in a database table (DROP COLUMN column_name).

To add a column named "City" in the "Person" table:
ALTER TABLE Person ADD City varchar(30)


To drop the "Address" column in the "Person" table:
ALTER TABLE Person DROP COLUMN Address

GROUP BY...
GROUP BY... was added to SQL because aggregate functions (like SUM) return the aggregate of all column values every time they are called, and without the GROUP BY function it was impossible to find the sum for each individual group of column values.
The syntax for the GROUP BY function is:
SELECT column,SUM(column) FROM table GROUP BY column

SELECT Company,SUM(Amount) FROM Sales
GROUP BY Company

HAVING...
HAVING... was added to SQL because the WHERE keyword could not be used against aggregate functions (like SUM), and without HAVING... it would be impossible to test for result conditions.
The syntax for the HAVING function is:
SELECT column,SUM(column) FROM table
GROUP BY column
HAVING SUM(column) condition valu

SELECT Company,SUM(Amount) FROM Sales
GROUP BY Company
HAVING SUM(Amount)>10000
------
vince
------
User avatar
vinnie-bob
 
Posts: 219
Joined: Sun Jul 06, 2008 10:55 am
Location: Des Moines, Iowa, USA

Re: Is there such an external?

Postby drpitcairn » Sun Aug 11, 2013 12:51 pm

Helpful. I will see if I can understand it.
User avatar
drpitcairn
 
Posts: 1023
Joined: Thu Apr 02, 2009 9:39 am
Location: Sedona, Arizona


Return to Using Externals

Who is online

Users browsing this forum: No registered users and 1 guest

cron