Author Topic: QBDbase v2.0 (WIP)  (Read 4861 times)

0 Members and 1 Guest are viewing this topic.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
QBDbase v2.0 (WIP)
« on: October 29, 2018, 03:11:47 am »
Terry decided to redo his sprite library to enhance it with the latest powers/goodness of QB64, and I thought I'd sit down and redo my QBDbase library and address some of the issues or lack of functionality in it, that I was hoping to add at some time..

Below is a little test snippet of the work-in-progress, in case anyone wants to see how this version would differ  from the last one.

Code: QB64: [Select]
  1. CONST True = -1, False = 0
  2. CONST DB_Static = 0, DB_Dynamic = 1
  3. DIM SHARED DB_MaxDataBases AS _UNSIGNED _BYTE
  4.  
  5. TYPE HeaderType
  6.     Offset AS _UNSIGNED LONG 'size of total header plus field data
  7.     NumberOfFields AS _UNSIGNED _BYTE 'number of fields
  8.     Style AS _BYTE '0 for STATIC, 1 for DYNAMIC -- totally changes our data structure/interaction
  9.     NumberOfRecords AS LONG
  10.     LastRecord AS LONG
  11.     MaxNumberofRecords AS LONG
  12.  
  13. REDIM SHARED Header(0) AS HeaderType
  14.  
  15. TYPE FieldType
  16.     Name AS STRING * 20
  17.     Type AS _UNSIGNED _BYTE '1 = (String)/Number, 2 = (Real)/Integer , 4 = (Signed)/Unsigned
  18.     Size AS _UNSIGNED LONG '1 for byte, 2 for integer, 4 for long/single, 8 for int64/float, any value > 0 for strings
  19.  
  20. REDIM SHARED FieldHeader(0, 1 TO 255) AS FieldType '
  21. REDIM SHARED DB_RecordStatus(0, 0) AS _BYTE '1 = Normal, -1 = Deleted, -2 = Hidden, -3 = Password Protected
  22.  
  23. DIM SHARED DatabasesInUse AS _UNSIGNED _BYTE
  24. REDIM SHARED DatabaseHandle(0) AS _BIT 'True/False Flag
  25.  
  26. REDIM SHARED DB_InternalMemory(0, 1 TO 255) AS _MEM
  27.  
  28. '**************** END OF HEADER
  29.  
  30. TYPE foo
  31.     Name AS STRING * 10
  32.     Age AS INTEGER
  33.     Sex AS STRING * 1
  34.  
  35. DIM Customer AS foo 'Our reference variable
  36.  
  37.  
  38. result = DB_SetMaxDatabases&(3) 'only 3 databases total
  39. CDB = DB_CreateDatabase&(100, DB_Static) 'CDB = Customer DataBase; the name I chose for the reference handle
  40. result = DB_AddField&(CDB, "Name", "STRING * 10")
  41. IF result <> 0 THEN PRINT "ERROR ADDING NAME = "; result
  42. result = DB_AddField&(CDB, "Age", "INTEGER")
  43. IF result <> 0 THEN PRINT "ERROR ADDING AGE= "; result
  44. result = DB_AddField&(CDB, "Sex", "STRING * 1")
  45. IF result <> 0 THEN PRINT "ERROR ADDING SEX = "; result
  46. result = DB_LinkToType&(CDB, _OFFSET(Customer)) 'Offset to the reference variable
  47. IF result <> 0 THEN PRINT "ERROR LINKING = "; result
  48.  
  49. PRINT "We're going to add some new records to our test database.  Just hit <ENTER> at the name to quit!"
  50.  
  51.     PRINT
  52.     INPUT "Enter Name =>"; Customer.Name
  53.     IF _TRIM$(Customer.Name) = "" THEN EXIT DO
  54.     INPUT "Enter Age =>"; Customer.Age
  55.     INPUT "Enter Sex =>"; Customer.Sex
  56.     result = DB_AddRecord&(CDB, 0)
  57.     IF result < 0 THEN PRINT "ERROR ADDING RECORD = "; result
  58.  
  59. Customer.Name = "": Customer.Age = 0: Customer.Sex = ""
  60.  
  61.  
  62. PRINT "Record", "Name", "Age", "Sex"
  63.     result = DB_GetRecord(CDB, 0)
  64.     PRINT result, Customer.Name, Customer.Age, Customer.Sex
  65. LOOP UNTIL result = -6 OR _KEYHIT
  66.  
  67.  
  68.  
  69. '**************** START OF SUBS/FUNCTIONS
  70.  
  71. FUNCTION DB_GetRecord& (DB_Handle AS _UNSIGNED _BYTE, DB_Record AS LONG)
  72.     'Error Codes
  73.     '>0 = Proper return tells us the record number recovered
  74.     '-1 = If user passed a 0 value handle
  75.     '-2 = If handle not in use
  76.     '-3 = If handle is larger than max set number of databases
  77.     '-4 = Invalid database type.  Dynamic DBs dont link to user variables
  78.     '-5 = Invalid Record
  79.     '-6 = Reached End of Data; nothing left to read.
  80.     '-7 = Blank Record
  81.     '-8 = Deleted Record
  82.     '-9 = Hidden Record
  83.     '-10 = Password Protected Record
  84.     STATIC DB_RecordOn AS LONG
  85.     IF DB_Handle = 0 THEN DB_GetRecord& = -1: EXIT FUNCTION '-1 if user passed a 0 value handle
  86.     IF DB_Handle > DB_MaxDataBases THEN DB_GetRecord& = -3 '-3 if handle is larger than max set number of databases
  87.     IF DatabaseHandle(DB_Handle) = False THEN DB_GetRecord& = -2: EXIT FUNCTION '-2 if handle not in use
  88.     IF Header(DB_Handle).Style = DB_Dynamic THEN DB_GetRecord& = -4: EXIT FUNCTION '-4 invalid database type.  Static DBs dont link to user variables
  89.  
  90.     IF DB_RecordOn < 1 THEN DB_RecordOn = 1
  91.  
  92.     DIM Temp AS _OFFSET: Temp = DB_InternalMemory(DB_Handle, 2).SIZE
  93.  
  94.     IF DB_Record > 0 THEN
  95.         SELECT CASE DB_RecordStatus(DB_Handle, DB_Record)
  96.             CASE 0, -1, -2, -3 'blank/deleted/hidden/password protected
  97.                 DB_GetRecord& = -7 - DB_RecordStatus(DB_Handle, DB_Record)
  98.                 _MEMFILL DB_InternalMemory(DB_Handle, 2), DB_InternalMemory(DB_Handle, 2).OFFSET, Temp, 0 AS _BYTE 'blank the return field
  99.             CASE 1 'It's all good and readable, no flags to worry about
  100.                 _MEMCOPY DB_InternalMemory(DB_Handle, 1), DB_InternalMemory(DB_Handle, 1).OFFSET + Temp * (DB_Record - 1), Temp TO DB_InternalMemory(DB_Handle, 2), DB_InternalMemory(DB_Handle, 2).OFFSET
  101.                 DB_GetRecord& = DB_Record 'proper return tells us the record number recovered
  102.                 IF DB_RecordOn < Header(DB_Handle).LastRecord THEN DB_RecordOn = DB_Record + 1
  103.         END SELECT
  104.         EXIT FUNCTION
  105.     ELSEIF DB_Record = 0 THEN
  106.         FOR i = DB_RecordOn TO Header(DB_Handle).LastRecord 'look for the next available record we can read
  107.             IF DB_RecordStatus(DB_Handle, i) = 1 THEN
  108.                 _MEMCOPY DB_InternalMemory(DB_Handle, 1), DB_InternalMemory(DB_Handle, 1).OFFSET + Temp * (i - 1), Temp TO DB_InternalMemory(DB_Handle, 2), DB_InternalMemory(DB_Handle, 2).OFFSET
  109.                 DB_GetRecord& = i 'proper return tells us the record number recovered
  110.                 DB_RecordOn = i + 1
  111.                 EXIT FUNCTION
  112.             END IF
  113.         NEXT
  114.     ELSE
  115.         DB_GetRecord& = -5 'Invalid Record (user passed a negative value handle)
  116.     END IF
  117.     DB_GetRecord& = -6 'Reached End of Data
  118.  
  119.  
  120. FUNCTION DB_AddRecord& (DB_Handle AS _UNSIGNED _BYTE, DB_Record AS LONG)
  121.     'Error Codes
  122.     '>0 = Proper return tells us the record number written
  123.     '-1 = If user passed a 0 value handle
  124.     '-2 = If handle not in use
  125.     '-3 = If handle is larger than max set number of databases
  126.     '-4 = Invalid database type.  Dynamic DBs dont link to user variables
  127.     '-5 = Invalid Record
  128.     '-6 = Failure to Write Data.  (All records probably full.)
  129.  
  130.     IF DB_Handle = 0 THEN DB_AddRecord& = -1: EXIT FUNCTION '-1 if user passed a 0 value handle
  131.     IF DB_Handle > DB_MaxDataBases THEN DB_AddRecord& = -3 '-3 if handle is larger than max set number of databases
  132.     IF DatabaseHandle(DB_Handle) = False THEN DB_AddRecord& = -2: EXIT FUNCTION '-2 if handle not in use
  133.     IF Header(DatabasesInUse).Style = DB_Dynamic THEN DB_AddRecord& = -4: EXIT FUNCTION '-4 invalid database type.  Static DBs dont link to user variables
  134.  
  135.     DIM Temp AS _OFFSET: Temp = DB_InternalMemory(DB_Handle, 2).SIZE
  136.  
  137.     IF DB_Record > 0 THEN
  138.         _MEMCOPY DB_InternalMemory(DB_Handle, 2), DB_InternalMemory(DB_Handle, 2).OFFSET, Temp TO DB_InternalMemory(DB_Handle, 1), DB_InternalMemory(DB_Handle, 1).OFFSET + Temp * (DB_Record - 1)
  139.         DB_RecordStatus(DB_Handle, DB_Record) = 1 'no unusal statuses; it's a new, good, visible, valid record
  140.         DB_AddRecord& = DB_Record
  141.     ELSEIF DB_Record = 0 THEN
  142.         FOR i = 1 TO Header(DB_Handle).MaxNumberofRecords
  143.             SELECT CASE DB_RecordStatus(DB_Handle, i)
  144.                 CASE 0, -1 'blank or deleted record
  145.                     _MEMCOPY DB_InternalMemory(DB_Handle, 2), DB_InternalMemory(DB_Handle, 2).OFFSET, Temp TO DB_InternalMemory(DB_Handle, 1), DB_InternalMemory(DB_Handle, 1).OFFSET + Temp * (i - 1)
  146.                     DB_RecordStatus(DB_Handle, i) = 1 'no unusal statuses; it's a new, good, visible, valid record
  147.                     DB_AddRecord& = i
  148.                     IF i > Header(DB_Handle).LastRecord THEN Header(DB_Handle).LastRecord = Header(DB_Handle).LastRecord + 1
  149.                     EXIT FUNCTION
  150.             END SELECT
  151.         NEXT
  152.     ELSE
  153.         DB_AddRecord& = -5 'Invalid Record
  154.     END IF
  155.     DB_AddRecord& = -6 'Failure to Write Data.  (All records probably full.)
  156.  
  157.  
  158.  
  159. FUNCTION DB_LinkToType& (DB_Handle AS _UNSIGNED _BYTE, DB_Offset AS _OFFSET) 'DB_Offset is the offset to the type variable
  160.     IF DB_Handle = 0 THEN DB_LinkToType& = -1: EXIT FUNCTION '-1 if user passed a 0 value handle
  161.     IF DB_Handle > DB_MaxDataBases THEN DB_LinkToType& = -3 '-3 if handle is larger than max set number of databases
  162.     IF DatabaseHandle(DB_Handle) = False THEN DB_LinkToType& = -2: EXIT FUNCTION '-2 if handle not in use
  163.     IF Header(DatabasesInUse).Style = DB_Dynamic THEN DB_LinkToType& = -4: EXIT FUNCTION '-4 invalid database type.  Static DBs dont link to user variables
  164.     FOR i = 1 TO 255
  165.         TotalSize&& = TotalSize&& + FieldHeader(DB_Handle, i).Size
  166.     NEXT
  167.     PRINT TotalSize&&; "SIZE"
  168.  
  169.     DB_InternalMemory(DB_Handle, 1) = _MEMNEW(TotalSize&& * Header(DB_Handle).MaxNumberofRecords) 'first field is the actual data
  170.     _MEMFILL DB_InternalMemory(DB_Handle, 1), DB_InternalMemory(DB_Handle, 1).OFFSET, DB_InternalMemory(DB_Handle, 1).SIZE, 0 AS _BYTE
  171.     DB_InternalMemory(DB_Handle, 2) = _MEM(DB_Offset, TotalSize&&) 'second field links to the refernce variable
  172.  
  173. FUNCTION DB_SetMaxDatabases& (Number AS _UNSIGNED _BYTE)
  174.     IF Number = 0 THEN DB_SetMaxDatabases& = -1 'Can't set zero as a limit
  175.     IF DB_MaxDataBases <> 0 THEN DB_SetMaxDatabases& = -2 'Max has already been set
  176.     DB_MaxDataBases = Number
  177.     REDIM DatabaseHandle(1 TO DB_MaxDataBases) AS _BIT 'True/False Flag
  178.     REDIM Header(1 TO DB_MaxDataBases) AS HeaderType
  179.     REDIM FieldHeader(1 TO DB_MaxDataBases, 0 TO 255) AS FieldType '
  180.     REDIM DB_InternalMemory(1 TO DB_MaxDataBases, 1 TO 255) AS _MEM
  181.     FOR X = 1 TO DB_MaxDataBases
  182.         FOR Y = 1 TO 255
  183.             FieldHeader(X, Y).Type = 255 'If all bits are set, the field is empty
  184.         NEXT
  185.     NEXT
  186.  
  187. FUNCTION DB_CreateDatabase& (DB_SetMaxRecords AS LONG, STYLE AS INTEGER) '0 to 255 return value only
  188.     'Error Codes
  189.     '-1 = User hasn't set DB_SetMaxDatabases yet
  190.     '-2 = Invalid number of max records
  191.     '-3 = Too many databases open
  192.     '-4 = Invalid style set for database
  193.  
  194.     IF DB_MaxDataBases = 0 THEN DB_CreateDatabase& = -1 'Max amount hasn't been set yet
  195.     IF DB_SetMaxRecords <= 0 THEN DB_CreateDatabase& = -2 'Invalid number of max records
  196.     IF STYLE < 0 OR STYLE > 1 THEN DB_CreateDatabase& = -4 'Invalid style set for database
  197.     FOR i = 1 TO DatabasesInUse
  198.         IF DatabaseHandle(i) = False THEN
  199.             DB_CreateDatabase& = i
  200.             Header(i).NumberOfRecords = 0
  201.             Header(i).MaxNumberofRecords = DB_SetMaxRecords
  202.             DatabaseHandle(i) = True
  203.             EXIT FUNCTION
  204.         END IF
  205.     NEXT
  206.     IF DatabasesInUse = DB_MaxDataBases THEN DB_CreateDatabase& = -3: EXIT FUNCTION 'Return -3 if we have too many databases open
  207.     DatabasesInUse = DatabasesInUse + 1
  208.     DB_CreateDatabase& = DatabasesInUse
  209.     Header(DatabasesInUse).NumberOfRecords = 0
  210.     Header(DatabasesInUse).MaxNumberofRecords = DB_SetMaxRecords
  211.     DatabaseHandle(DatabasesInUse) = True
  212.     REDIM DB_RecordStatus(1 TO DB_MaxDataBases, 1 TO DB_SetMaxRecords) AS _BYTE
  213.  
  214. FUNCTION DB_FreeDatabase& (DB_Handle AS _UNSIGNED _BYTE) 'true/false return value
  215.     IF DB_Handle = 0 THEN DB_FreeDatabase& = -1: EXIT FUNCTION '-1 if user passed a 0 value handle
  216.     IF DB_Handle > DB_MaxDataBases THEN DB_FreeDatabase& = -3 '-3 if handle is larger than max set number of databases
  217.     IF DatabaseHandle(DB_Handle) = False THEN DB_FreeDatabase& = -2: EXIT FUNCTION '-2 if handle not in use
  218.     DatabaseHandle(DB_Handle) = False
  219.     IF DB_Handle = DatabaseHandle THEN DatabaseHandle = DatabaseHandle - 1 'clear up a handle if we're freeing the last one
  220.     DB_FreeDatabase& = True
  221.     IF Header(DatabasesInUse).Style = DB_Dynamic THEN
  222.         FOR i = 1 TO 255
  223.             IF FieldHeader(DB_Handle, i).Type <> 255 THEN
  224.                 FieldHeader(DB_Handle, i).Type = 255
  225.                 _MEMFREE DB_InternalMemory(DB_Handle, i)
  226.             END IF
  227.         NEXT
  228.     ELSE
  229.         _MEMFREE DB_InternalMemory(DB_Handle, 1)
  230.     END IF
  231.  
  232. FUNCTION DB_AddField& (DB_Handle AS _UNSIGNED _BYTE, DB_FieldLabel AS STRING * 20, DB_Type AS STRING * 30)
  233.     'Error Return Values:
  234.     '-1 = Handle Value of 0
  235.     '-2 = Handle > DB_MaxDataBases limit
  236.     '-3 = Handle not in use
  237.     '-4 = Too many fields in databse already (255 fields found)
  238.     '-5 = Name contains invalid characters.  Only A-Z (upper or lowercase), 0-9, space, and period allowed
  239.     '-6 = Invalid letters in type
  240.     '-7 = Invalid information in type
  241.     '-8 = Too large a STRING size (0 or blank for variable length, 1 - 255 for fixed length)
  242.     '-9 = Database already has 255 fields; can't add any more.
  243.     IF (DB_Handle = 0) THEN DB_AddField = -1: EXIT FUNCTION
  244.     IF DB_Handle > DB_MaxDataBases THEN DB_AddField = -2 '-2 if handle is larger than max set number of databases
  245.     IF (DatabaseHandle(DB_Handle) = False) THEN DB_AddField = -3: EXIT FUNCTION
  246.     FOR i = 1 TO DB_MaxDataBases
  247.         IF FieldHeader(DB_Handle, i).Type = 255 THEN found = i: EXIT FOR
  248.     NEXT
  249.     IF found = 0 THEN DB_AddField = -4: EXIT FUNCTION '-4 for too many fields
  250.     temp$ = _TRIM$(DB_FieldLabel)
  251.     FOR i = 1 TO LEN(temp$)
  252.         SELECT CASE MID$(temp$, i, 1)
  253.             CASE "a" TO "z", "A" TO "Z", "0" TO "9", ".", " ", CHR$(0)
  254.             CASE ELSE
  255.                 DB_AddField = -5 'invalid characters in field name
  256.                 EXIT FUNCTION
  257.         END SELECT
  258.     NEXT
  259.     t1$ = _TRIM$(UCASE$(DB_Type))
  260.     FOR i = 1 TO LEN(t1$)
  261.         SELECT CASE MID$(t1$, i, 1)
  262.             CASE "A" TO "Z", "0" TO "9" 'acceptable letters
  263.                 t$ = t$ + MID$(t1$, i, 1)
  264.             CASE "_", " ", "*"
  265.             CASE ELSE
  266.                 DB_AddField = -6 'invalid letters in type
  267.                 EXIT FUNCTION
  268.         END SELECT
  269.     NEXT
  270.  
  271.     FOR i = 1 TO Header(DB_Handle).NumberOfFields
  272.         IF FieldHeader(DB_Handle, i).Type = 255 THEN n = Header(DB_Handle).NumberOfFields: EXIT FOR
  273.     NEXT
  274.     IF n = 0 THEN
  275.         IF Header(DB_Handle).NumberOfFields = 255 THEN
  276.             DB_AddField = -9 'Database already has 255 fields; can't add any more.
  277.         ELSE
  278.             n = Header(DB_Handle).NumberOfFields + 1 'prepare to update for a new field, if everything else passes muster
  279.         END IF
  280.     END IF
  281.  
  282.  
  283.     IF LEFT$(t$, 6) = "STRING" THEN
  284.         v% = VAL(MID$(t$, 7))
  285.         IF LEN(_TRIM$(STR$(v%))) + 6 <> LEN(t$) THEN DB_AddField = -7: EXIT FUNCTION 'invalid information in type
  286.         IF v% > 255 THEN DB_AddField = -8 'too large a string size
  287.         Header(DB_Handle).NumberOfFields = n 'add a new field
  288.         FieldHeader(DB_Handle, n).Name = DB_FieldLabel
  289.         FieldHeader(DB_Handle, n).Type = 1
  290.         FieldHeader(DB_Handle, n).Size = v%
  291.     ELSE
  292.         SELECT CASE t$ '1 = (String)/Number, 2 = (Real)/Integer , 4 = (Signed)/Unsigned
  293.             CASE "SINGLE"
  294.                 Header(DB_Handle).NumberOfFields = n 'add a new field
  295.                 FieldHeader(DB_Handle, n).Name = DB_FieldLabel
  296.                 FieldHeader(DB_Handle, n).Type = 2
  297.                 FieldHeader(DB_Handle, n).Size = 4
  298.             CASE "DOUBLE"
  299.                 Header(DB_Handle).NumberOfFields = n 'add a new field
  300.                 FieldHeader(DB_Handle, n).Name = DB_FieldLabel
  301.                 FieldHeader(DB_Handle, n).Type = 2
  302.                 FieldHeader(DB_Handle, n).Size = 8
  303.             CASE "FLOAT"
  304.                 Header(DB_Handle).NumberOfFields = n 'add a new field
  305.                 FieldHeader(DB_Handle, n).Name = DB_FieldLabel
  306.                 FieldHeader(DB_Handle, n).Type = 2
  307.                 FieldHeader(DB_Handle, n).Size = 32
  308.             CASE "UNSIGNEDBYTE"
  309.                 Header(DB_Handle).NumberOfFields = n 'add a new field
  310.                 FieldHeader(DB_Handle, n).Name = DB_FieldLabel
  311.                 FieldHeader(DB_Handle, n).Type = 4
  312.                 FieldHeader(DB_Handle, n).Size = 1
  313.             CASE "UNSIGNEDINTEGER"
  314.                 Header(DB_Handle).NumberOfFields = n 'add a new field
  315.                 FieldHeader(DB_Handle, n).Name = DB_FieldLabel
  316.                 FieldHeader(DB_Handle, n).Type = 4
  317.                 FieldHeader(DB_Handle, n).Size = 2
  318.             CASE "UNSIGNEDLONG"
  319.                 Header(DB_Handle).NumberOfFields = n 'add a new field
  320.                 FieldHeader(DB_Handle, n).Name = DB_FieldLabel
  321.                 FieldHeader(DB_Handle, n).Type = 4
  322.                 FieldHeader(DB_Handle, n).Size = 4
  323.             CASE "UNSIGNEDINTEGER64"
  324.                 Header(DB_Handle).NumberOfFields = n 'add a new field
  325.                 FieldHeader(DB_Handle, n).Name = DB_FieldLabel
  326.                 FieldHeader(DB_Handle, n).Type = 4
  327.                 FieldHeader(DB_Handle, n).Size = 8
  328.             CASE "BYTE"
  329.                 Header(DB_Handle).NumberOfFields = n 'add a new field
  330.                 FieldHeader(DB_Handle, n).Name = DB_FieldLabel
  331.                 FieldHeader(DB_Handle, n).Type = 0
  332.                 FieldHeader(DB_Handle, n).Size = 1
  333.             CASE "INTEGER"
  334.                 Header(DB_Handle).NumberOfFields = n 'add a new field
  335.                 FieldHeader(DB_Handle, n).Name = DB_FieldLabel
  336.                 FieldHeader(DB_Handle, n).Type = 0
  337.                 FieldHeader(DB_Handle, n).Size = 2
  338.             CASE "LONG"
  339.                 Header(DB_Handle).NumberOfFields = n 'add a new field
  340.                 FieldHeader(DB_Handle, n).Name = DB_FieldLabel
  341.                 FieldHeader(DB_Handle, n).Type = 0
  342.                 FieldHeader(DB_Handle, n).Size = 4
  343.             CASE "INTEGER64"
  344.                 Header(DB_Handle).NumberOfFields = n 'add a new field
  345.                 FieldHeader(DB_Handle, n).Name = DB_FieldLabel
  346.                 FieldHeader(DB_Handle, n).Type = 0
  347.                 FieldHeader(DB_Handle, n).Size = 8
  348.             CASE ELSE
  349.                 DB_AddField = -7: EXIT FUNCTION 'invalid information in type
  350.         END SELECT
  351.     END IF
  352.     IF Header(DatabasesInUse).Style = DB_Dynamic THEN
  353.         DB_InternalMemory(DB_Handle, n) = _MEMNEW(FieldHeader(DB_Handle, n).Size * Header(DB_Handle).MaxNumberofRecords)
  354.         _MEMFILL DB_InternalMemory(DB_Handle, n), DB_InternalMemory(DB_Handle, n).OFFSET, DB_InternalMemory(DB_Handle, n).SIZE, 0 AS _BYTE
  355.     END IF
  356.  
  357.  

Now, if I were to remove out the lines which would become the actual library, what the user supplied code would look like is the following (which might help highlight how things are different):

Code: QB64: [Select]
  1. TYPE foo
  2.     Name AS STRING * 10
  3.     Age AS INTEGER
  4.     Sex AS STRING * 1
  5.  
  6. DIM Customer AS foo 'Our reference variable
  7.  
  8.  
  9. result = DB_SetMaxDatabases&(3) 'only 3 databases total
  10. CDB = DB_CreateDatabase&(100, DB_Static) 'CDB = Customer DataBase; the name I chose for the reference handle
  11. result = DB_AddField&(CDB, "Name", "STRING * 10")
  12. IF result <> 0 THEN PRINT "ERROR ADDING NAME = "; result
  13. result = DB_AddField&(CDB, "Age", "INTEGER")
  14. IF result <> 0 THEN PRINT "ERROR ADDING AGE= "; result
  15. result = DB_AddField&(CDB, "Sex", "STRING * 1")
  16. IF result <> 0 THEN PRINT "ERROR ADDING SEX = "; result
  17. result = DB_LinkToType&(CDB, _OFFSET(Customer)) 'Offset to the reference variable
  18. IF result <> 0 THEN PRINT "ERROR LINKING = "; result
  19.  
  20. PRINT "We're going to add some new records to our test database.  Just hit <ENTER> at the name to quit!"
  21.  
  22.     PRINT
  23.     INPUT "Enter Name =>"; Customer.Name
  24.     IF _TRIM$(Customer.Name) = "" THEN EXIT DO
  25.     INPUT "Enter Age =>"; Customer.Age
  26.     INPUT "Enter Sex =>"; Customer.Sex
  27.     result = DB_AddRecord&(CDB, 0)
  28.     IF result < 0 THEN PRINT "ERROR ADDING RECORD = "; result
  29.  
  30. Customer.Name = "": Customer.Age = 0: Customer.Sex = ""
  31.  
  32.  
  33. PRINT "Record", "Name", "Age", "Sex"
  34.     result = DB_GetRecord(CDB, 0)
  35.     PRINT result, Customer.Name, Customer.Age, Customer.Sex
  36. LOOP UNTIL result = -6 OR _KEYHIT
  37.  
  38.  
  39.  

First note of difference -- EVERYTHING is basically a FUNCTION.   The reason for this is rather simple:  this library is going to work with various mem blocks (and most with $CHECKING:OFF once it gets finished), so I wanted it to be able to return as much ERROR information as possible back to the end user. 

For example, when attempting to add a field, we could get back any of the following error codes:
    'Error Return Values:
    '-1 = Handle Value of 0
    '-2 = Handle > DB_MaxDataBases limit
    '-3 = Handle not in use
    '-4 = Too many fields in database already (255 fields found)
    '-5 = Name contains invalid characters.  Only A-Z (upper or lowercase), 0-9, space, and period allowed
    '-6 = Invalid letters in type
    '-7 = Invalid information in type
    '-8 = Too large a STRING size (0 or blank for variable length, 1 - 255 for fixed length)
    '-9 = Database already has 255 fields; can't add any more.

I'm trying to make this fairly decent at error catching and returning helpful, decipherable error messages for us.
 
*********************

Next thing you might notice is that we now can add Fields to our database, and Link to a user-defined type. (Note, linking is for Static databases only, but I'll explain the difference between what this library calls 'Static' and 'Dynamic' later.)

If you  look at our user defined TYPE, what we see is:
Code: QB64: [Select]
  1. TYPE foo
  2.     name AS STRING * 10
  3.     age AS INTEGER
  4.     sex AS STRING * 1
  5.  
  6. DIM Customer AS foo 'Our reference variable

And the way we associate these fields with our database is rather simple:
Code: QB64: [Select]
  1. result = DB_AddField&(CDB, "Name", "STRING * 10")
  2. result = DB_AddField&(CDB, "Age", "INTEGER")
  3. result = DB_AddField&(CDB, "Sex", "STRING * 1")
  4. result = DB_LinkToType&(CDB, _OFFSET(Customer)) 'Offset to the reference variable

Then, when it's time to add an actual record to the database, we do so just by using the same variables in our code as normal:

Code: QB64: [Select]
  1.     PRINT
  2.     INPUT "Enter Name =>"; Customer.name
  3.     IF _TRIM$(Customer.name) = "" THEN EXIT DO
  4.     INPUT "Enter Age =>"; Customer.age
  5.     INPUT "Enter Sex =>"; Customer.sex
  6.     result = DB_AddRecord&(CDB, 0)

All-in-all, it's more-or-less a complete revisioning of the QBDbase system, and by using what I learned while making the last version, this one should be much simpler for the end-user to implement and make use of.  (And a LOT more flexible, when all is said and done.)

Anywho....  It's just a little taste of what I'm playing around with, but there's no need to get too excited and hold your breath for a finished product; my precious for programming time is limited a lot anymore, and I'm not even going to attempt to put a date on when this little revision might be finished.  It'll get done when it gets done, is the best I'm going to offer.  :P
« Last Edit: October 29, 2018, 09:36:51 am by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline TerryRitchie

  • Seasoned Forum Regular
  • Posts: 495
  • Semper Fidelis
    • View Profile
Re: QBDbase v2.0 (WIP)
« Reply #1 on: October 29, 2018, 04:16:14 am »
Excellent news!
In order to understand recursion, one must first understand recursion.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: QBDbase v2.0 (WIP)
« Reply #2 on: October 29, 2018, 09:41:23 am »
Updated the demo.  You can now Create the database, Add Records, and GetRecords....

Looking at the actual program code (not the part which will become the library files), it seems a lot more complicated now, but it's not!  The majority of the new code is just adding lines so the program will print out a message in case any errors occur while running.  :P
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline luke

  • Administrator
  • Seasoned Forum Regular
  • Posts: 324
    • View Profile
Re: QBDbase v2.0 (WIP)
« Reply #3 on: October 29, 2018, 11:20:21 am »
Suggestion: use the built in ERROR command to throw fatal errors, especially those that indicate a programming error. It'll keep the caller code cleaner and ensure mistakes don't go unnoticed.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: QBDbase v2.0 (WIP)
« Reply #4 on: October 29, 2018, 12:02:26 pm »
Suggestion: use the built in ERROR command to throw fatal errors, especially those that indicate a programming error. It'll keep the caller code cleaner and ensure mistakes don't go unnoticed.

Before I finish, I plan to set a flag so a person can choose between internal and external error messages.  Internal would basically be what we have now: the function returns a result and the programmer can write their own method for dealing with the issue.  External errors give us a pop up message and a choice "halt execution or resume", like we often see in QB64 errors such as "subscript out of range".

Part of the idea behind this database is to allow the end USER to add/remove fields, not just the programmer, and external errors may not be the desired route to go for simple cases of user typos and such.  (Like a user defining a field "STRIG * 10"... We can catch that and program for them to retype the entry without just popping up an error message.)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
    • View Profile
Re: QBDbase v2.0 (WIP)
« Reply #5 on: October 29, 2018, 05:33:53 pm »
Hi Steve
great project !
Good evolution of your library!

about
Quote
First note of difference -- EVERYTHING is basically a FUNCTION

going from Qbasic toward C/C++ and passing throug Pascal/TurboPascal I learnt how a SUBroutine can be useful mimed by a Function that return an OK_feedback value while no SUB can mimy a Function. So the simple block of code separated has its evolution in the simple block separated returning a feedback message. And IMHO this become the basis for event driven coding and the oriented object programming.
Programming isn't difficult, only it's  consuming time and coffee